Browse Source

Refactor code that looks up addresses from interfaces

Now the code has separate implementation and examination functions,
uses smartlists sanely, and has relatively decent test coverage.
rl1987 10 years ago
parent
commit
3966145dff
6 changed files with 697 additions and 64 deletions
  1. 11 0
      changes/ticket12376_part2
  2. 163 64
      src/common/address.c
  3. 53 0
      src/common/address.h
  4. 1 0
      src/test/include.am
  5. 2 0
      src/test/test.c
  6. 467 0
      src/test/test_address.c

+ 11 - 0
changes/ticket12376_part2

@@ -0,0 +1,11 @@
+  o Major refactoring:
+    - Refactor the get_interface_addresses_raw() Doom-function into
+      multiple smaller and easier to understand subfunctions. Cover the
+      resulting subfunctions with unit-tests. Fixes a significant portion 
+      of issue 12376.
+
+  o Minor bugfixes:
+    - Fix the ioctl()-based network interface lookup code so that it will
+      work on systems that have variable-length struct ifreq, for example
+      Mac OS X.
+

+ 163 - 64
src/common/address.c

@@ -8,24 +8,26 @@
  * \brief Functions to use and manipulate the tor_addr_t structure.
  **/
 
-#include "orconfig.h"
-#include "compat.h"
-#include "util.h"
-#include "address.h"
-#include "torlog.h"
-#include "container.h"
-#include "sandbox.h"
+#define ADDRESS_PRIVATE
 
 #ifdef _WIN32
-#include <process.h>
-#include <windows.h>
-#include <winsock2.h>
 /* For access to structs needed by GetAdaptersAddresses */
 #undef _WIN32_WINNT
 #define _WIN32_WINNT 0x0501
+#include <process.h>
+#include <winsock2.h>
+#include <windows.h>
 #include <iphlpapi.h>
 #endif
 
+#include "orconfig.h"
+#include "compat.h"
+#include "util.h"
+#include "address.h"
+#include "torlog.h"
+#include "container.h"
+#include "sandbox.h"
+
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
@@ -122,7 +124,7 @@ tor_addr_to_sockaddr(const tor_addr_t *a,
 }
 
 /** Set the tor_addr_t in <b>a</b> to contain the socket address contained in
- * <b>sa</b>. */
+ * <b>sa</b>. Return 0 on success and -1 on failure. */
 int
 tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa,
                        uint16_t *port_out)
@@ -1192,26 +1194,17 @@ typedef ULONG (WINAPI *GetAdaptersAddresses_fn_t)(
               ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG);
 #endif
 
-/** Try to ask our network interfaces what addresses they are bound to.
- * Return a new smartlist of tor_addr_t on success, and NULL on failure.
- * (An empty smartlist indicates that we successfully learned that we have no
- * addresses.)  Log failure messages at <b>severity</b>. */
-static smartlist_t *
-get_interface_addresses_raw(int severity)
+#ifdef HAVE_IFADDRS_TO_SMARTLIST
+/*
+ * Convert a linked list consisting of <b>ifaddrs</b> structures
+ * into smartlist of <b>tor_addr_t</b> structures.
+ */
+STATIC smartlist_t *
+ifaddrs_to_smartlist(const struct ifaddrs *ifa)
 {
-#if defined(HAVE_GETIFADDRS)
-  /* Most free Unixy systems provide getifaddrs, which gives us a linked list
-   * of struct ifaddrs. */
-  struct ifaddrs *ifa = NULL;
+  smartlist_t *result = smartlist_new();
   const struct ifaddrs *i;
-  smartlist_t *result;
-  if (getifaddrs(&ifa) < 0) {
-    log_fn(severity, LD_NET, "Unable to call getifaddrs(): %s",
-           strerror(errno));
-    return NULL;
-  }
 
-  result = smartlist_new();
   for (i = ifa; i; i = i->ifa_next) {
     tor_addr_t tmp;
     if ((i->ifa_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING))
@@ -1226,9 +1219,72 @@ get_interface_addresses_raw(int severity)
     smartlist_add(result, tor_memdup(&tmp, sizeof(tmp)));
   }
 
+  return result;
+}
+
+/** Use getiffaddrs() function to get list of current machine
+ * network interface addresses. Represent the result by smartlist of
+ * <b>tor_addr_t</b> structures.
+ */
+STATIC smartlist_t *
+get_interface_addresses_ifaddrs(int severity)
+{
+
+  /* Most free Unixy systems provide getifaddrs, which gives us a linked list
+   * of struct ifaddrs. */
+  struct ifaddrs *ifa = NULL;
+  smartlist_t *result;
+  if (getifaddrs(&ifa) < 0) {
+    log_fn(severity, LD_NET, "Unable to call getifaddrs(): %s",
+           strerror(errno));
+    return NULL;
+  }
+
+  result = ifaddrs_to_smartlist(ifa);
+
   freeifaddrs(ifa);
+
+  return result;
+}
+#endif
+
+#ifdef HAVE_IP_ADAPTER_TO_SMARTLIST
+
+/** Convert a Windows-specific <b>addresses</b> linked list into smartlist
+ * of <b>tor_addr_t</b> structures.
+ */
+
+STATIC smartlist_t *
+ip_adapter_addresses_to_smartlist(const IP_ADAPTER_ADDRESSES *addresses)
+{
+  smartlist_t *result = smartlist_new();
+  const IP_ADAPTER_ADDRESSES *address;
+
+  for (address = addresses; address; address = address->Next) {
+    const IP_ADAPTER_UNICAST_ADDRESS *a;
+    for (a = address->FirstUnicastAddress; a; a = a->Next) {
+      /* Yes, it's a linked list inside a linked list */
+      const struct sockaddr *sa = a->Address.lpSockaddr;
+      tor_addr_t tmp;
+      if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
+        continue;
+      if (tor_addr_from_sockaddr(&tmp, sa, NULL) < 0)
+        continue;
+      smartlist_add(result, tor_memdup(&tmp, sizeof(tmp)));
+    }
+  }
+
   return result;
-#elif defined(_WIN32)
+}
+
+/** Windows only: use GetAdaptersInfo() function to retrieve network interface
+ * addresses of current machine and return them to caller as smartlist of
+ * <b>tor_addr_t</b>  structures.
+ */
+STATIC smartlist_t *
+get_interface_addresses_win32(int severity)
+{
+
   /* Windows XP began to provide GetAdaptersAddresses. Windows 2000 had a
      "GetAdaptersInfo", but that's deprecated; let's just try
      GetAdaptersAddresses and fall back to connect+getsockname.
@@ -1237,7 +1293,7 @@ get_interface_addresses_raw(int severity)
   smartlist_t *result = NULL;
   GetAdaptersAddresses_fn_t fn;
   ULONG size, res;
-  IP_ADAPTER_ADDRESSES *addresses = NULL, *address;
+  IP_ADAPTER_ADDRESSES *addresses = NULL;
 
   (void) severity;
 
@@ -1272,63 +1328,106 @@ get_interface_addresses_raw(int severity)
     goto done;
   }
 
-  result = smartlist_new();
-  for (address = addresses; address; address = address->Next) {
-    IP_ADAPTER_UNICAST_ADDRESS *a;
-    for (a = address->FirstUnicastAddress; a; a = a->Next) {
-      /* Yes, it's a linked list inside a linked list */
-      struct sockaddr *sa = a->Address.lpSockaddr;
-      tor_addr_t tmp;
-      if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
-        continue;
-      if (tor_addr_from_sockaddr(&tmp, sa, NULL) < 0)
-        continue;
-      smartlist_add(result, tor_memdup(&tmp, sizeof(tmp)));
-    }
-  }
+  result = ip_adapter_addresses_to_smartlist(addresses);
 
  done:
   if (lib)
     FreeLibrary(lib);
   tor_free(addresses);
   return result;
-#elif defined(SIOCGIFCONF) && defined(HAVE_IOCTL)
+}
+
+#endif
+
+#ifdef HAVE_IFCONF_TO_SMARTLIST
+
+/* This is defined on Mac OS X */
+#ifndef _SIZEOF_ADDR_IFREQ
+#define _SIZEOF_ADDR_IFREQ sizeof
+#endif
+
+/** Convert <b>*ifr</b>, an ifreq structure array of size <b>buflen</b>
+ * into smartlist of <b>tor_addr_t</b> structures.
+ */
+STATIC smartlist_t *
+ifreq_to_smartlist(const struct ifreq *ifr, size_t buflen)
+{
+  smartlist_t *result = smartlist_new();
+
+  struct ifreq *r = (struct ifreq *)ifr;
+
+  while ((char *)r < (char *)ifr+buflen) {
+    const struct sockaddr *sa = &r->ifr_addr;
+    tor_addr_t tmp;
+    int valid_sa_family = (sa->sa_family == AF_INET ||
+                           sa->sa_family == AF_INET6);
+
+    int conversion_success = (tor_addr_from_sockaddr(&tmp, sa, NULL) == 0);
+
+    if (valid_sa_family && conversion_success)
+      smartlist_add(result, tor_memdup(&tmp, sizeof(tmp)));
+
+    r = (struct ifreq *)((char *)r + _SIZEOF_ADDR_IFREQ(*r));
+  }
+
+  return result;
+}
+
+/** Use ioctl(.,SIOCGIFCONF,.) to get a list of current machine
+ * network interface addresses. Represent the result by smartlist of
+ * <b>tor_addr_t</b> structures.
+ */
+STATIC smartlist_t *
+get_interface_addresses_ioctl(int severity)
+{
   /* Some older unixy systems make us use ioctl(SIOCGIFCONF) */
   struct ifconf ifc;
-  int fd, i, sz, n;
+  int fd, sz;
+  void *databuf = NULL;
   smartlist_t *result = NULL;
+
   /* This interface, AFAICT, only supports AF_INET addresses */
   fd = socket(AF_INET, SOCK_DGRAM, 0);
   if (fd < 0) {
     tor_log(severity, LD_NET, "socket failed: %s", strerror(errno));
     goto done;
   }
+
   /* Guess how much space we need. */
-  ifc.ifc_len = sz = 15*1024;
-  ifc.ifc_ifcu.ifcu_req = tor_malloc(sz);
+  ifc.ifc_len = sz = 4096;
+  databuf = tor_malloc_zero(sz);
+  ifc.ifc_buf = databuf;
+
   if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
     tor_log(severity, LD_NET, "ioctl failed: %s", strerror(errno));
     close(fd);
     goto done;
   }
-  close(fd);
-  result = smartlist_new();
-  if (ifc.ifc_len < sz)
-    sz = ifc.ifc_len;
-  n = sz / sizeof(struct ifreq);
-  for (i = 0; i < n ; ++i) {
-    struct ifreq *r = &ifc.ifc_ifcu.ifcu_req[i];
-    struct sockaddr *sa = &r->ifr_addr;
-    tor_addr_t tmp;
-    if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
-      continue; /* should be impossible */
-    if (tor_addr_from_sockaddr(&tmp, sa, NULL) < 0)
-      continue;
-    smartlist_add(result, tor_memdup(&tmp, sizeof(tmp)));
-  }
+
+  result = ifreq_to_smartlist(databuf, ifc.ifc_len);
+
  done:
-  tor_free(ifc.ifc_ifcu.ifcu_req);
+  close(fd);
+  tor_free(databuf);
   return result;
+}
+#endif
+
+/** Try to ask our network interfaces what addresses they are bound to.
+ * Return a new smartlist of tor_addr_t on success, and NULL on failure.
+ * (An empty smartlist indicates that we successfully learned that we have no
+ * addresses.)  Log failure messages at <b>severity</b>. */
+STATIC smartlist_t *
+get_interface_addresses_raw(int severity)
+{
+#if defined(HAVE_IFADDRS_TO_SMARTLIST)
+  return get_interface_addresses_ifaddrs(severity);
+#endif
+#if defined(HAVE_IP_ADAPTER_TO_SMARTLIST)
+  return get_interface_addresses_win32(severity);
+#endif
+#if defined(HAVE_IFCONF_TO_SMARTLIST)
+  return get_interface_addresses_ioctl(severity);
 #else
   (void) severity;
   return NULL;

+ 53 - 0
src/common/address.h

@@ -11,10 +11,41 @@
 #ifndef TOR_ADDRESS_H
 #define TOR_ADDRESS_H
 
+//#include <sys/sockio.h>
 #include "orconfig.h"
 #include "torint.h"
 #include "compat.h"
 
+#ifdef ADDRESS_PRIVATE
+
+#if defined(HAVE_SYS_IOCTL_H)
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_GETIFADDRS
+#define HAVE_IFADDRS_TO_SMARTLIST
+#endif
+
+#ifdef _WIN32
+#define HAVE_IP_ADAPTER_TO_SMARTLIST
+#endif
+
+#if defined(SIOCGIFCONF) && defined(HAVE_IOCTL)
+#define HAVE_IFCONF_TO_SMARTLIST
+#endif
+
+#if defined(HAVE_NET_IF_H)
+#include <net/if.h> // for struct ifconf
+#endif
+
+#if defined(HAVE_IFADDRS_TO_SMARTLIST)
+#include <ifaddrs.h>
+#endif
+
+// TODO win32 specific includes
+#include "container.h"
+#endif // ADDRESS_PRIVATE
+
 /** The number of bits from an address to consider while doing a masked
  * comparison. */
 typedef uint8_t maskbits_t;
@@ -229,5 +260,27 @@ int get_interface_address(int severity, uint32_t *addr);
 
 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);
+
+#ifdef HAVE_IFADDRS_TO_SMARTLIST
+STATIC smartlist_t *ifaddrs_to_smartlist(const struct ifaddrs *ifa);
+STATIC smartlist_t *get_interface_addresses_ifaddrs(int severity);
+#endif
+
+#ifdef HAVE_IP_ADAPTER_TO_SMARTLIST
+STATIC smartlist_t *ip_adapter_addresses_to_smartlist(
+                                        const IP_ADAPTER_ADDRESSES *addresses);
+STATIC smartlist_t *get_interface_addresses_win32(int severity);
+#endif
+
+#ifdef HAVE_IFCONF_TO_SMARTLIST
+STATIC smartlist_t *ifreq_to_smartlist(const struct ifreq *ifr,
+                                       size_t buflen);
+STATIC smartlist_t *get_interface_addresses_ioctl(int severity);
+#endif
+
+#endif // ADDRESS_PRIVATE
+
 #endif
 

+ 1 - 0
src/test/include.am

@@ -45,6 +45,7 @@ src_test_test_SOURCES = \
 	src/test/test_nodelist.c \
 	src/test/test_policy.c \
 	src/test/test_status.c \
+        src/test/test_address.c \
 	src/ext/tinytest.c
 
 src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)

+ 2 - 0
src/test/test.c

@@ -1287,6 +1287,7 @@ static struct testcase_t test_array[] = {
 };
 
 extern struct testcase_t addr_tests[];
+extern struct testcase_t address_tests[];
 extern struct testcase_t buffer_tests[];
 extern struct testcase_t crypto_tests[];
 extern struct testcase_t container_tests[];
@@ -1319,6 +1320,7 @@ static struct testgroup_t testgroups[] = {
   { "buffer/", buffer_tests },
   { "socks/", socks_tests },
   { "addr/", addr_tests },
+  { "address/", address_tests },
   { "crypto/", crypto_tests },
   { "container/", container_tests },
   { "util/", util_tests },

+ 467 - 0
src/test/test_address.c

@@ -0,0 +1,467 @@
+/* Copyright (c) 2014-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define ADDRESS_PRIVATE
+
+#ifdef _WIN32
+#include <winsock2.h>
+/* For access to structs needed by GetAdaptersAddresses */
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#include <iphlpapi.h>
+#endif
+
+#ifdef HAVE_IFADDRS_TO_SMARTLIST
+#include <net/if.h>
+#include <ifaddrs.h>
+#endif
+
+#ifdef HAVE_IFCONF_TO_SMARTLIST
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#include <net/if.h>
+#endif
+
+#include "or.h"
+#include "address.h"
+#include "test.h"
+
+/** Return 1 iff <b>sockaddr1</b> and <b>sockaddr2</b> represent
+ * the same IP address and port combination. Otherwise, return 0.
+ */
+static uint8_t
+sockaddr_in_are_equal(struct sockaddr_in *sockaddr1,
+                      struct sockaddr_in *sockaddr2)
+{
+   return ((sockaddr1->sin_family == sockaddr2->sin_family) &&
+           (sockaddr1->sin_port == sockaddr2->sin_port) &&
+           (sockaddr1->sin_addr.s_addr == sockaddr2->sin_addr.s_addr));
+}
+
+/** Return 1 iff <b>sockaddr1</b> and <b>sockaddr2</b> represent
+ * the same IP address and port combination. Otherwise, return 0.
+ */
+static uint8_t
+sockaddr_in6_are_equal(struct sockaddr_in6 *sockaddr1,
+                       struct sockaddr_in6 *sockaddr2)
+{
+   return ((sockaddr1->sin6_family == sockaddr2->sin6_family) &&
+           (sockaddr1->sin6_port == sockaddr2->sin6_port) &&
+           (tor_memeq(sockaddr1->sin6_addr.s6_addr,
+                      sockaddr2->sin6_addr.s6_addr,16)));
+}
+
+/** Create a sockaddr_in structure from IP address string <b>ip_str</b>.
+ *
+ * If <b>out</b> is not NULL, write the result
+ * to the memory address in <b>out</b>. Otherwise, allocate the memory
+ * for result. On success, return pointer to result. Otherwise, return
+ * NULL.
+ */
+static struct sockaddr_in *
+sockaddr_in_from_string(const char *ip_str, struct sockaddr_in *out)
+{
+  // [FIXME: add some error checking?]
+  if (!out)
+    out = tor_malloc_zero(sizeof(struct sockaddr_in));
+
+  out->sin_family = AF_INET;
+  out->sin_port = 0;
+  tor_inet_pton(AF_INET,ip_str,&(out->sin_addr));
+
+  return out;
+}
+
+/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
+ * that points to 127.0.0.1. Otherwise, return 0.
+ */
+static int
+smartlist_contains_localhost_tor_addr(smartlist_t *smartlist)
+{
+  int found_localhost = 0;
+
+  struct sockaddr_in *sockaddr_localhost;
+  struct sockaddr_storage *sockaddr_to_check;
+
+  sockaddr_localhost = sockaddr_in_from_string("127.0.0.1",NULL);
+
+  sockaddr_to_check = tor_malloc(sizeof(struct sockaddr_in));
+
+  SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
+    tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
+                         sizeof(struct sockaddr_in));
+
+    if (sockaddr_in_are_equal((struct sockaddr_in *)sockaddr_to_check,
+                              sockaddr_localhost)) {
+      found_localhost = 1;
+      break;
+    }
+  } SMARTLIST_FOREACH_END(tor_addr);
+
+  tor_free(sockaddr_localhost);
+  tor_free(sockaddr_to_check);
+
+  return found_localhost;
+}
+
+#ifdef HAVE_IFADDRS_TO_SMARTLIST
+static void
+test_address_ifaddrs_to_smartlist(void *arg)
+{
+   struct ifaddrs *ifa = NULL;
+   struct ifaddrs *ifa_ipv4 = NULL;
+   struct ifaddrs *ifa_ipv6 = NULL;
+   struct sockaddr_in *ipv4_sockaddr_local = NULL;
+   struct sockaddr_in *netmask_slash8 = NULL;
+   struct sockaddr_in *ipv4_sockaddr_remote = NULL;
+   struct sockaddr_in6 *ipv6_sockaddr = NULL;
+   smartlist_t *smartlist = NULL;
+   tor_addr_t *tor_addr = NULL;
+   struct sockaddr *sockaddr_to_check = NULL;
+   socklen_t addr_len;
+
+   (void)arg;
+
+   netmask_slash8 = sockaddr_in_from_string("255.0.0.0",NULL);
+   ipv4_sockaddr_local = sockaddr_in_from_string("127.0.0.1",NULL);
+   ipv4_sockaddr_remote = sockaddr_in_from_string("128.52.160.20",NULL);
+
+   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));
+
+   ifa = tor_malloc(sizeof(struct ifaddrs));
+   ifa_ipv4 = tor_malloc(sizeof(struct ifaddrs));
+   ifa_ipv6 = tor_malloc(sizeof(struct ifaddrs));
+
+   ifa->ifa_next = ifa_ipv4;
+   ifa->ifa_name = tor_strdup("eth0");
+   ifa->ifa_flags = IFF_UP | IFF_RUNNING;
+   ifa->ifa_addr = (struct sockaddr *)ipv4_sockaddr_local;
+   ifa->ifa_netmask = (struct sockaddr *)netmask_slash8;
+   ifa->ifa_dstaddr = NULL;
+   ifa->ifa_data = NULL;
+
+   ifa_ipv4->ifa_next = ifa_ipv6;
+   ifa_ipv4->ifa_name = tor_strdup("eth1");
+   ifa_ipv4->ifa_flags = IFF_UP | IFF_RUNNING;
+   ifa_ipv4->ifa_addr = (struct sockaddr *)ipv4_sockaddr_remote;
+   ifa_ipv4->ifa_netmask = (struct sockaddr *)netmask_slash8;
+   ifa_ipv4->ifa_dstaddr = NULL;
+   ifa_ipv4->ifa_data = NULL;
+
+   ifa_ipv6->ifa_next = NULL;
+   ifa_ipv6->ifa_name = tor_strdup("eth2");
+   ifa_ipv6->ifa_flags = IFF_UP | IFF_RUNNING;
+   ifa_ipv6->ifa_addr = (struct sockaddr *)ipv6_sockaddr;
+   ifa_ipv6->ifa_netmask = NULL;
+   ifa_ipv6->ifa_dstaddr = NULL;
+   ifa_ipv6->ifa_data = NULL;
+
+   smartlist = ifaddrs_to_smartlist(ifa);
+
+   tt_assert(smartlist);
+   tt_assert(smartlist_len(smartlist) == 3);
+
+   sockaddr_to_check = tor_malloc(sizeof(struct sockaddr_in6));
+
+   tor_addr = smartlist_get(smartlist,0);
+   addr_len =
+   tor_addr_to_sockaddr(tor_addr,0,sockaddr_to_check,
+                        sizeof(struct sockaddr_in));
+
+   tt_int_op(addr_len,==,sizeof(struct sockaddr_in));
+   tt_assert(sockaddr_in_are_equal((struct sockaddr_in *)sockaddr_to_check,
+                                   ipv4_sockaddr_local));
+
+   tor_addr = smartlist_get(smartlist,1);
+   addr_len =
+   tor_addr_to_sockaddr(tor_addr,0,sockaddr_to_check,
+                        sizeof(struct sockaddr_in));
+
+   tt_int_op(addr_len,==,sizeof(struct sockaddr_in));
+   tt_assert(sockaddr_in_are_equal((struct sockaddr_in *)sockaddr_to_check,
+                                   ipv4_sockaddr_remote));
+
+   tor_addr = smartlist_get(smartlist,2);
+   addr_len =
+   tor_addr_to_sockaddr(tor_addr,0,sockaddr_to_check,
+                        sizeof(struct sockaddr_in6));
+
+   tt_int_op(addr_len,==,sizeof(struct sockaddr_in6));
+   tt_assert(sockaddr_in6_are_equal((struct sockaddr_in6*)sockaddr_to_check,
+                                    ipv6_sockaddr));
+
+   done:
+   tor_free(netmask_slash8);
+   tor_free(ipv4_sockaddr_local);
+   tor_free(ipv4_sockaddr_remote);
+   tor_free(ipv6_sockaddr);
+   tor_free(ifa);
+   tor_free(ifa_ipv4);
+   tor_free(ifa_ipv6);
+   tor_free(sockaddr_to_check);
+   SMARTLIST_FOREACH(smartlist, tor_addr_t *, t, tor_free(t));
+   smartlist_free(smartlist);
+
+   return;
+}
+
+static void
+test_address_get_if_addrs_ifaddrs(void *arg)
+{
+
+  smartlist_t *results = NULL;
+
+  (void)arg;
+
+  results = get_interface_addresses_ifaddrs(0);
+
+  tt_int_op(smartlist_len(results),>=,1);
+  tt_assert(smartlist_contains_localhost_tor_addr(results));
+
+  done:
+  SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
+  tor_free(results);
+  return;
+}
+
+#endif
+
+#ifdef HAVE_IP_ADAPTER_TO_SMARTLIST
+
+static void
+test_address_get_if_addrs_win32(void *arg)
+{
+
+  smartlist_t *results = NULL;
+
+  (void)arg;
+
+  results = get_interface_addresses_win32(0);
+
+  tt_int_op(smartlist_len(results),>=,1);
+  tt_assert(smartlist_contains_localhost_tor_addr(results));
+
+  done:
+  SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
+  tor_free(results);
+  return;
+}
+
+static void
+test_address_ip_adapter_addresses_to_smartlist(void *arg)
+{
+
+  IP_ADAPTER_ADDRESSES *addrs1;
+  IP_ADAPTER_ADDRESSES *addrs2;
+
+  IP_ADAPTER_UNICAST_ADDRESS *unicast11;
+  IP_ADAPTER_UNICAST_ADDRESS *unicast12;
+  IP_ADAPTER_UNICAST_ADDRESS *unicast21;
+
+  smartlist_t *result = NULL;
+
+  struct sockaddr_in *sockaddr_test1;
+  struct sockaddr_in *sockaddr_test2;
+  struct sockaddr_in *sockaddr_localhost;
+  struct sockaddr_in *sockaddr_to_check;
+
+  tor_addr_t *tor_addr;
+
+  (void)arg;
+  (void)sockaddr_in6_are_equal;
+
+  sockaddr_to_check = tor_malloc_zero(sizeof(struct sockaddr_in));
+
+  addrs1 =
+  tor_malloc_zero(sizeof(IP_ADAPTER_ADDRESSES));
+
+  addrs1->FirstUnicastAddress =
+  unicast11 = tor_malloc_zero(sizeof(IP_ADAPTER_UNICAST_ADDRESS));
+  sockaddr_test1 = sockaddr_in_from_string("86.59.30.40",NULL);
+  unicast11->Address.lpSockaddr = (LPSOCKADDR)sockaddr_test1;
+
+  unicast11->Next = unicast12 =
+  tor_malloc_zero(sizeof(IP_ADAPTER_UNICAST_ADDRESS));
+  sockaddr_test2 = sockaddr_in_from_string("93.95.227.222", NULL);
+  unicast12->Address.lpSockaddr = (LPSOCKADDR)sockaddr_test2;
+
+  addrs1->Next = addrs2 =
+  tor_malloc_zero(sizeof(IP_ADAPTER_ADDRESSES));
+
+  addrs2->FirstUnicastAddress =
+  unicast21 = tor_malloc_zero(sizeof(IP_ADAPTER_UNICAST_ADDRESS));
+  sockaddr_localhost = sockaddr_in_from_string("127.0.0.1", NULL);
+  unicast21->Address.lpSockaddr = (LPSOCKADDR)sockaddr_localhost;
+
+  result = ip_adapter_addresses_to_smartlist(addrs1);
+
+  tt_assert(result);
+  tt_assert(smartlist_len(result) == 3);
+
+  tor_addr = smartlist_get(result,0);
+
+  tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
+                       sizeof(struct sockaddr_in));
+
+  tt_assert(sockaddr_in_are_equal(sockaddr_test1,sockaddr_to_check));
+
+  tor_addr = smartlist_get(result,1);
+
+  tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
+                       sizeof(struct sockaddr_in));
+
+  tt_assert(sockaddr_in_are_equal(sockaddr_test2,sockaddr_to_check));
+
+  tor_addr = smartlist_get(result,2);
+
+  tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
+                       sizeof(struct sockaddr_in));
+
+  tt_assert(sockaddr_in_are_equal(sockaddr_localhost,sockaddr_to_check));
+
+  done:
+  SMARTLIST_FOREACH(result, tor_addr_t *, t, tor_free(t));
+  smartlist_free(result);
+  tor_free(addrs1);
+  tor_free(addrs2);
+  tor_free(unicast11->Address.lpSockaddr);
+  tor_free(unicast11);
+  tor_free(unicast12->Address.lpSockaddr);
+  tor_free(unicast12);
+  tor_free(unicast21->Address.lpSockaddr);
+  tor_free(unicast21);
+  tor_free(sockaddr_to_check);
+  return;
+}
+#endif
+
+#ifdef HAVE_IFCONF_TO_SMARTLIST
+
+static void
+test_address_ifreq_to_smartlist(void *arg)
+{
+  smartlist_t *results = NULL;
+  tor_addr_t *tor_addr = NULL;
+  struct sockaddr_in *sockaddr = NULL;
+  struct sockaddr_in *sockaddr_eth1 = NULL;
+  struct sockaddr_in *sockaddr_to_check = NULL;
+
+  struct ifconf *ifc;
+  struct ifreq *ifr;
+  struct ifreq *ifr_next;
+
+  socklen_t addr_len;
+
+  (void)arg;
+
+  sockaddr_to_check = tor_malloc(sizeof(struct sockaddr_in));
+
+  ifr = tor_malloc(sizeof(struct ifreq));
+  memset(ifr,0,sizeof(struct ifreq));
+  strlcpy(ifr->ifr_name,"lo",3);
+  sockaddr = (struct sockaddr_in *) &(ifr->ifr_ifru.ifru_addr);
+  sockaddr_in_from_string("127.0.0.1",sockaddr);
+
+  ifc = tor_malloc(sizeof(struct ifconf));
+  memset(ifc,0,sizeof(struct ifconf));
+  ifc->ifc_len = sizeof(struct ifreq);
+  ifc->ifc_ifcu.ifcu_req = ifr;
+
+  results = ifreq_to_smartlist((struct ifreq *)ifc->ifc_buf,ifc->ifc_len);
+  tt_int_op(smartlist_len(results),==,1);
+
+  tor_addr = smartlist_get(results, 0);
+  addr_len =
+  tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
+                       sizeof(struct sockaddr_in));
+
+  tt_int_op(addr_len,==,sizeof(struct sockaddr_in));
+  tt_assert(sockaddr_in_are_equal(sockaddr,sockaddr_to_check));
+
+  ifr = tor_realloc(ifr,2*sizeof(struct ifreq));
+  ifr_next = ifr+1;
+  strlcpy(ifr_next->ifr_name,"eth1",5);
+  ifc->ifc_len = 2*sizeof(struct ifreq);
+  ifc->ifc_ifcu.ifcu_req = ifr;
+  sockaddr = (struct sockaddr_in *) &(ifr->ifr_ifru.ifru_addr);
+
+  sockaddr_eth1 = (struct sockaddr_in *) &(ifr_next->ifr_ifru.ifru_addr);
+  sockaddr_in_from_string("192.168.10.55",sockaddr_eth1);
+
+  smartlist_free(results);
+
+  results = ifreq_to_smartlist((struct ifreq *)ifc->ifc_buf,ifc->ifc_len);
+  tt_int_op(smartlist_len(results),==,2);
+
+  tor_addr = smartlist_get(results, 0);
+  addr_len =
+  tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
+                       sizeof(struct sockaddr_in));
+
+  tt_int_op(addr_len,==,sizeof(struct sockaddr_in));
+  tt_assert(sockaddr_in_are_equal(sockaddr,sockaddr_to_check));
+
+  tor_addr = smartlist_get(results, 1);
+  addr_len =
+  tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
+                       sizeof(struct sockaddr_in));
+
+  tt_int_op(addr_len,==,sizeof(struct sockaddr_in));
+  tt_assert(sockaddr_in_are_equal(sockaddr_eth1,sockaddr_to_check));
+
+  done:
+  tor_free(sockaddr_to_check);
+  smartlist_free(results);
+  tor_free(tor_addr);
+  tor_free(ifc);
+  tor_free(ifr);
+  return;
+}
+
+static void
+test_address_get_if_addrs_ioctl(void *arg)
+{
+
+  smartlist_t *result = NULL;
+
+  (void)arg;
+
+  result = get_interface_addresses_ioctl(LOG_ERR);
+
+  tt_assert(result);
+  tt_int_op(smartlist_len(result),>=,1);
+
+  tt_assert(smartlist_contains_localhost_tor_addr(result));
+
+  done:
+  SMARTLIST_FOREACH(result, tor_addr_t *, t, tor_free(t));
+  smartlist_free(result);
+  return;
+}
+
+#endif
+
+#define ADDRESS_TEST(name, flags) \
+  { #name, test_address_ ## name, flags, NULL, NULL }
+
+struct testcase_t address_tests[] = {
+#ifdef HAVE_IFADDRS_TO_SMARTLIST
+  ADDRESS_TEST(get_if_addrs_ifaddrs, TT_FORK),
+  ADDRESS_TEST(ifaddrs_to_smartlist, 0),
+#endif
+#ifdef HAVE_IP_ADAPTER_TO_SMARTLIST
+  ADDRESS_TEST(get_if_addrs_win32, TT_FORK),
+  ADDRESS_TEST(ip_adapter_addresses_to_smartlist, 0),
+#endif
+#ifdef HAVE_IFCONF_TO_SMARTLIST
+  ADDRESS_TEST(get_if_addrs_ioctl, TT_FORK),
+  ADDRESS_TEST(ifreq_to_smartlist, 0),
+#endif
+  END_OF_TESTCASES
+};
+