Browse Source

Merge branch 'ticket14710_squashed'

Nick Mathewson 9 years ago
parent
commit
30e933b136
6 changed files with 274 additions and 43 deletions
  1. 10 0
      changes/ticket14710
  2. 61 38
      src/common/address.c
  3. 3 0
      src/common/address.h
  4. 18 2
      src/common/compat.c
  5. 9 1
      src/common/compat.h
  6. 173 2
      src/test/test_address.c

+ 10 - 0
changes/ticket14710

@@ -0,0 +1,10 @@
+  o Code simplification and refactoring:
+    - Move the hacky fallback code out of get_interface_address6() 
+      into separate function and get it covered with unit-tests. Resolves
+      ticket 14710.
+
+  o Minor bugfixes:
+    - When attempting to use fallback technique for network interface
+      lookup, disregard loopback and multicast addresses since they are
+      unsuitable for public communications.
+

+ 61 - 38
src/common/address.c

@@ -1504,47 +1504,22 @@ tor_addr_is_multicast(const tor_addr_t *a)
   return 0;
 }
 
-/** Set *<b>addr</b> to the IP address (if any) of whatever interface
- * connects to the Internet.  This address should only be used in checking
- * whether our address has changed.  Return 0 on success, -1 on failure.
+/** Attempt to retrieve IP address of current host by utilizing some
+ * UDP socket trickery. Only look for address of given <b>family</b>.
+ * Set result to *<b>addr</b>. Return 0 on success, -1 on failure.
  */
-MOCK_IMPL(int,
-get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
+STATIC int
+get_interface_address6_via_udp_socket_hack(int severity,
+                                           sa_family_t family,
+                                           tor_addr_t *addr)
 {
-  /* XXX really, this function should yield a smartlist of addresses. */
-  smartlist_t *addrs;
-  int sock=-1, r=-1;
   struct sockaddr_storage my_addr, target_addr;
+  int sock=-1, r=-1;
   socklen_t addr_len;
-  tor_assert(addr);
-
-  /* Try to do this the smart way if possible. */
-  if ((addrs = get_interface_addresses_raw(severity))) {
-    int rv = -1;
-    SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
-      if (family != AF_UNSPEC && family != tor_addr_family(a))
-        continue;
-      if (tor_addr_is_loopback(a) ||
-          tor_addr_is_multicast(a))
-        continue;
 
-      tor_addr_copy(addr, a);
-      rv = 0;
-
-      /* If we found a non-internal address, declare success.  Otherwise,
-       * keep looking. */
-      if (!tor_addr_is_internal(a, 0))
-        break;
-    } SMARTLIST_FOREACH_END(a);
-
-    SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a));
-    smartlist_free(addrs);
-    return rv;
-  }
-
-  /* Okay, the smart way is out. */
   memset(addr, 0, sizeof(tor_addr_t));
   memset(&target_addr, 0, sizeof(target_addr));
+
   /* Don't worry: no packets are sent. We just need to use a real address
    * on the actual Internet. */
   if (family == AF_INET6) {
@@ -1566,6 +1541,7 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
   } else {
     return -1;
   }
+
   if (sock < 0) {
     int e = tor_socket_errno(-1);
     log_fn(severity, LD_NET, "unable to create socket: %s",
@@ -1573,27 +1549,74 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
     goto err;
   }
 
-  if (connect(sock,(struct sockaddr *)&target_addr, addr_len) < 0) {
+  if (tor_connect_socket(sock,(struct sockaddr *)&target_addr,
+                         addr_len) < 0) {
     int e = tor_socket_errno(sock);
     log_fn(severity, LD_NET, "connect() failed: %s", tor_socket_strerror(e));
     goto err;
   }
 
-  if (getsockname(sock,(struct sockaddr*)&my_addr, &addr_len)) {
+  if (tor_getsockname(sock,(struct sockaddr*)&my_addr, &addr_len)) {
     int e = tor_socket_errno(sock);
     log_fn(severity, LD_NET, "getsockname() to determine interface failed: %s",
            tor_socket_strerror(e));
     goto err;
   }
 
-  tor_addr_from_sockaddr(addr, (struct sockaddr*)&my_addr, NULL);
-  r=0;
+ if (tor_addr_from_sockaddr(addr, (struct sockaddr*)&my_addr, NULL) == 0) {
+    if (tor_addr_is_loopback(addr) || tor_addr_is_multicast(addr)) {
+      log_fn(severity, LD_NET, "Address that we determined via UDP socket"
+                               " magic is unsuitable for public comms.");
+    } else {
+      r=0;
+    }
+ }
+
  err:
   if (sock >= 0)
     tor_close_socket(sock);
   return r;
 }
 
+/** Set *<b>addr</b> to the IP address (if any) of whatever interface
+ * connects to the Internet.  This address should only be used in checking
+ * whether our address has changed.  Return 0 on success, -1 on failure.
+ */
+MOCK_IMPL(int,
+get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
+{
+  /* XXX really, this function should yield a smartlist of addresses. */
+  smartlist_t *addrs;
+  tor_assert(addr);
+
+  /* Try to do this the smart way if possible. */
+  if ((addrs = get_interface_addresses_raw(severity))) {
+    int rv = -1;
+    SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
+      if (family != AF_UNSPEC && family != tor_addr_family(a))
+        continue;
+      if (tor_addr_is_loopback(a) ||
+          tor_addr_is_multicast(a))
+        continue;
+
+      tor_addr_copy(addr, a);
+      rv = 0;
+
+      /* If we found a non-internal address, declare success.  Otherwise,
+       * keep looking. */
+      if (!tor_addr_is_internal(a, 0))
+        break;
+    } SMARTLIST_FOREACH_END(a);
+
+    SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a));
+    smartlist_free(addrs);
+    return rv;
+  }
+
+  /* Okay, the smart way is out. */
+  return get_interface_address6_via_udp_socket_hack(severity,family,addr);
+}
+
 /* ======
  * IPv4 helpers
  * XXXX024 IPv6 deprecate some of these.

+ 3 - 0
src/common/address.h

@@ -274,6 +274,9 @@ tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port);
 
 #ifdef ADDRESS_PRIVATE
 STATIC smartlist_t *get_interface_addresses_raw(int severity);
+STATIC int get_interface_address6_via_udp_socket_hack(int severity,
+                                                      sa_family_t family,
+                                                      tor_addr_t *addr);
 
 #ifdef HAVE_IFADDRS_TO_SMARTLIST
 STATIC smartlist_t *ifaddrs_to_smartlist(const struct ifaddrs *ifa);

+ 18 - 2
src/common/compat.c

@@ -1156,12 +1156,20 @@ mark_socket_open(tor_socket_t s)
 /** @} */
 
 /** As socket(), but counts the number of open sockets. */
-tor_socket_t
-tor_open_socket(int domain, int type, int protocol)
+MOCK_IMPL(tor_socket_t,
+tor_open_socket,(int domain, int type, int protocol))
 {
   return tor_open_socket_with_extensions(domain, type, protocol, 1, 0);
 }
 
+/** Mockable wrapper for connect(). */
+MOCK_IMPL(tor_socket_t,
+tor_connect_socket,(tor_socket_t socket,const struct sockaddr *address,
+                     socklen_t address_len))
+{
+  return connect(socket,address,address_len);
+}
+
 /** As socket(), but creates a nonblocking socket and
  * counts the number of open sockets. */
 tor_socket_t
@@ -1308,6 +1316,14 @@ get_n_open_sockets(void)
   return n;
 }
 
+/** Mockable wrapper for getsockname(). */
+MOCK_IMPL(int,
+tor_getsockname,(tor_socket_t socket, struct sockaddr *address,
+                 socklen_t *address_len))
+{
+   return getsockname(socket, address, address_len);
+}
+
 /** Turn <b>socket</b> into a nonblocking socket. Return 0 on success, -1
  * on failure.
  */

+ 9 - 1
src/common/compat.h

@@ -463,7 +463,8 @@ int tor_close_socket(tor_socket_t s);
 tor_socket_t tor_open_socket_with_extensions(
                                            int domain, int type, int protocol,
                                            int cloexec, int nonblock);
-tor_socket_t tor_open_socket(int domain, int type, int protocol);
+MOCK_DECL(tor_socket_t,
+tor_open_socket,(int domain, int type, int protocol));
 tor_socket_t tor_open_socket_nonblocking(int domain, int type, int protocol);
 tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr,
                                   socklen_t *len);
@@ -474,8 +475,15 @@ tor_socket_t tor_accept_socket_with_extensions(tor_socket_t sockfd,
                                                struct sockaddr *addr,
                                                socklen_t *len,
                                                int cloexec, int nonblock);
+MOCK_DECL(tor_socket_t,
+tor_connect_socket,(tor_socket_t socket,const struct sockaddr *address,
+                    socklen_t address_len));
 int get_n_open_sockets(void);
 
+MOCK_DECL(int,
+tor_getsockname,(tor_socket_t socket, struct sockaddr *address,
+                 socklen_t *address_len));
+
 #define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags)
 #define tor_socket_recv(s, buf, len, flags) recv(s, buf, len, flags)
 

+ 173 - 2
src/test/test_address.c

@@ -130,8 +130,8 @@ test_address_ifaddrs_to_smartlist(void *arg)
    ipv6_sockaddr = tor_malloc(sizeof(struct sockaddr_in6));
    ipv6_sockaddr->sin6_family = AF_INET6;
    ipv6_sockaddr->sin6_port = 0;
-   inet_pton(AF_INET6, "2001:db8:8714:3a90::12",
-             &(ipv6_sockaddr->sin6_addr));
+   tor_inet_pton(AF_INET6, "2001:db8:8714:3a90::12",
+                 &(ipv6_sockaddr->sin6_addr));
 
    ifa = tor_malloc(sizeof(struct ifaddrs));
    ifa_ipv4 = tor_malloc(sizeof(struct ifaddrs));
@@ -452,10 +452,181 @@ test_address_get_if_addrs_ioctl(void *arg)
 
 #endif
 
+#define FAKE_SOCKET_FD (42)
+
+tor_socket_t
+fake_open_socket(int domain, int type, int protocol)
+{
+  (void)domain;
+  (void)type;
+  (void)protocol;
+
+  return FAKE_SOCKET_FD;
+}
+
+static int last_connected_socket_fd = 0;
+
+static int connect_retval = 0;
+
+tor_socket_t
+pretend_to_connect(tor_socket_t socket, const struct sockaddr *address,
+                   socklen_t address_len)
+{
+  (void)address;
+  (void)address_len;
+
+  last_connected_socket_fd = socket;
+
+  return connect_retval;
+}
+
+static struct sockaddr *mock_addr = NULL;
+
+int
+fake_getsockname(tor_socket_t socket, struct sockaddr *address,
+                 socklen_t *address_len)
+{
+  socklen_t bytes_to_copy = 0;
+
+  if (!mock_addr)
+    return -1;
+
+  if (mock_addr->sa_family == AF_INET) {
+    bytes_to_copy = sizeof(struct sockaddr_in);
+  } else if (mock_addr->sa_family == AF_INET6) {
+    bytes_to_copy = sizeof(struct sockaddr_in6);
+  } else {
+    return -1;
+  }
+
+  if (*address_len < bytes_to_copy) {
+    return -1;
+  }
+
+  memcpy(address,mock_addr,bytes_to_copy);
+  *address_len = bytes_to_copy;
+
+  return 0;
+}
+
+static void
+test_address_udp_socket_trick_whitebox(void *arg)
+{
+  int hack_retval;
+  tor_addr_t *addr_from_hack = tor_malloc_zero(sizeof(tor_addr_t));
+  struct sockaddr_in6 *mock_addr6;
+  struct sockaddr_in6 *ipv6_to_check =
+  tor_malloc_zero(sizeof(struct sockaddr_in6));
+
+  (void)arg;
+
+  MOCK(tor_open_socket,fake_open_socket);
+  MOCK(tor_connect_socket,pretend_to_connect);
+  MOCK(tor_getsockname,fake_getsockname);
+
+  mock_addr = tor_malloc_zero(sizeof(struct sockaddr_storage));
+  sockaddr_in_from_string("23.32.246.118",(struct sockaddr_in *)mock_addr);
+
+  hack_retval =
+  get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
+                                             AF_INET, addr_from_hack);
+
+  tt_int_op(hack_retval,==,0);
+  tt_assert(tor_addr_eq_ipv4h(addr_from_hack, 0x1720f676));
+
+  /* Now, lets do an IPv6 case. */
+  memset(mock_addr,0,sizeof(struct sockaddr_storage));
+
+  mock_addr6 = (struct sockaddr_in6 *)mock_addr;
+  mock_addr6->sin6_family = AF_INET6;
+  mock_addr6->sin6_port = 0;
+  tor_inet_pton(AF_INET6,"2001:cdba::3257:9652",&(mock_addr6->sin6_addr));
+
+  hack_retval =
+  get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
+                                             AF_INET6, addr_from_hack);
+
+  tt_int_op(hack_retval,==,0);
+
+  tor_addr_to_sockaddr(addr_from_hack,0,(struct sockaddr *)ipv6_to_check,
+                       sizeof(struct sockaddr_in6));
+
+  tt_assert(sockaddr_in6_are_equal(mock_addr6,ipv6_to_check));
+
+  UNMOCK(tor_open_socket);
+  UNMOCK(tor_connect_socket);
+  UNMOCK(tor_getsockname);
+
+  done:
+  tor_free(ipv6_to_check);
+  tor_free(mock_addr);
+  tor_free(addr_from_hack);
+  return;
+}
+
+static void
+test_address_udp_socket_trick_blackbox(void *arg)
+{
+  /* We want get_interface_address6_via_udp_socket_hack() to yield
+   * the same valid address that get_interface_address6() returns.
+   * If the latter is unable to find a valid address, we want
+   * _hack() to fail and return-1.
+   *
+   * Furthermore, we want _hack() never to crash, even if
+   * get_interface_addresses_raw() is returning NULL.
+   */
+
+  tor_addr_t addr4;
+  tor_addr_t addr4_to_check;
+  tor_addr_t addr6;
+  tor_addr_t addr6_to_check;
+  int retval, retval_reference;
+
+  (void)arg;
+
+  retval_reference = get_interface_address6(LOG_DEBUG,AF_INET,&addr4);
+  retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
+                                                      AF_INET,
+                                                      &addr4_to_check);
+
+  tt_int_op(retval,==,retval_reference);
+  tt_assert( (retval == -1 && retval_reference == -1) ||
+             (tor_addr_compare(&addr4,&addr4_to_check,CMP_EXACT) == 0) );
+
+  //[XXX: Skipping the AF_INET6 case because bug #12377 makes it fail.]
+  (void)addr6_to_check;
+  (void)addr6;
+#if 0
+  retval_reference = get_interface_address6(LOG_DEBUG,AF_INET6,&addr6);
+  retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
+                                                      AF_INET6,
+                                                      &addr6_to_check);
+
+  tt_int_op(retval,==,retval_reference);
+  tt_assert( (retval == -1 && retval_reference == -1) ||
+             (tor_addr_compare(&addr6,&addr6_to_check,CMP_EXACT) == 0) );
+
+#endif
+
+  /* When family is neither AF_INET nor AF_INET6, we want _hack to
+   * fail and return -1.
+   */
+
+  retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
+                                                      AF_CCITT,&addr4);
+
+  tt_assert(retval == -1);
+
+  done:
+  return;
+}
+
 #define ADDRESS_TEST(name, flags) \
   { #name, test_address_ ## name, flags, NULL, NULL }
 
 struct testcase_t address_tests[] = {
+  ADDRESS_TEST(udp_socket_trick_whitebox, TT_FORK),
+  ADDRESS_TEST(udp_socket_trick_blackbox, TT_FORK),
 #ifdef HAVE_IFADDRS_TO_SMARTLIST
   ADDRESS_TEST(get_if_addrs_ifaddrs, TT_FORK),
   ADDRESS_TEST(ifaddrs_to_smartlist, 0),