From aa529f6c32f2103a5571f98047ee3bd2d5074330 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 2 Dec 2011 17:05:52 -0500 Subject: [PATCH 1/4] Use getifaddrs, not connect+getsockname, to find our address This resolves bug1827, and lets us avoid freaking people out. Later, we can use it to get a complete list of our interfaces. --- configure.in | 2 + src/common/address.c | 143 ++++++++++++++++++++++++++++++++++++++++++- src/test/test_addr.c | 11 ++-- 3 files changed, 149 insertions(+), 7 deletions(-) diff --git a/configure.in b/configure.in index 1f045f1349..a2aa417502 100644 --- a/configure.in +++ b/configure.in @@ -300,6 +300,7 @@ AC_CHECK_FUNCS( flock \ ftime \ getaddrinfo \ + getifaddrs \ getrlimit \ gettimeofday \ gmtime_r \ @@ -624,6 +625,7 @@ dnl These headers are not essential AC_CHECK_HEADERS( arpa/inet.h \ grp.h \ + ifaddrs.h \ inttypes.h \ limits.h \ linux/types.h \ diff --git a/src/common/address.c b/src/common/address.c index c53368743f..9ee5d61f2c 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -13,10 +13,16 @@ #include "util.h" #include "address.h" #include "torlog.h" +#include "container.h" #ifdef MS_WINDOWS #include #include +#include +/* For access to structs needed by GetAdaptersAddresses */ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#include #endif #ifdef HAVE_SYS_TIME_H @@ -46,6 +52,9 @@ #ifdef HAVE_SYS_UN_H #include #endif +#ifdef HAVE_IFADDRS_H +#include +#endif #include #include #include @@ -1086,6 +1095,114 @@ tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out) return -1; } +#ifdef MS_WINDOWS +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 severity. */ +static smartlist_t * +get_interface_addresses_raw(int severity) +{ +#if defined(HAVE_GETIFADDRS) + /* Most free Unixy systems provide getifaddrs, which gives us a linked list + * of struct ifaddrs. */ + struct ifaddrs *ifa = NULL; + 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_create(); + for (i = ifa; i; i = i->ifa_next) { + tor_addr_t tmp; + if (!i->ifa_addr) + continue; + if (i->ifa_addr->sa_family != AF_INET && + i->ifa_addr->sa_family != AF_INET6) + continue; + if (tor_addr_from_sockaddr(&tmp, i->ifa_addr, NULL) < 0) + continue; + smartlist_add(result, tor_memdup(&tmp, sizeof(tmp))); + } + + freeifaddrs(ifa); + return result; +#elif defined(MS_WINDOWS) + /* 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. + */ + HANDLE lib = load_windows_system_library(TEXT("iphlpapi.dll")); + smartlist_t *result = NULL; + GetAdaptersAddresses_fn_t fn; + ULONG size, res; + IP_ADAPTER_ADDRESSES *addresses = NULL, *address; + + (void) severity; + +#define FLAGS (GAA_FLAG_SKIP_ANYCAST | \ + GAA_FLAG_SKIP_MULTICAST | \ + GAA_FLAG_SKIP_DNS_SERVER) + + if (!lib) { + log_fn(severity, LD_NET, "Unable to load iphlpapi.dll"); + goto done; + } + + if (!(fn = (GetAdaptersAddresses_fn_t) + GetProcAddress(lib, "GetAdaptersAddresses"))) { + log_fn(severity, LD_NET, "Unable to obtain pointer to " + "GetAdaptersAddresses"); + goto done; + } + + /* Guess how much space we need. */ + size = 15*1024; + addresses = tor_malloc(size); + res = fn(AF_UNSPEC, FLAGS, NULL, addresses, &size); + if (res == ERROR_BUFFER_OVERFLOW) { + /* we didn't guess that we needed enough space; try again */ + tor_free(addresses); + addresses = tor_malloc(size); + res = fn(AF_UNSPEC, FLAGS, NULL, addresses, &size); + } + if (res != NO_ERROR) { + log_fn(severity, LD_NET, "GetAdaptersAddresses failed (result: %lu)", res); + goto done; + } + + result = smartlist_create(); + 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))); + } + } + + done: + if (lib) + FreeLibrary(lib); + tor_free(addresses); + return result; +#else + (void) severity; + return NULL; +#endif +} /** Set *addr 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. @@ -1093,12 +1210,36 @@ tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out) 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; int sock=-1, r=-1; struct sockaddr_storage my_addr, target_addr; 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)) + 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 diff --git a/src/test/test_addr.c b/src/test/test_addr.c index 9d8e1fe8c5..cf9c8f91d5 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -613,12 +613,11 @@ test_addr_ip6_helpers(void) /* get interface addresses */ r = get_interface_address6(LOG_DEBUG, AF_INET, &t1); i = get_interface_address6(LOG_DEBUG, AF_INET6, &t2); -#if 0 - tor_inet_ntop(AF_INET, &t1.sa.sin_addr, buf, sizeof(buf)); - printf("\nv4 address: %s (family=%d)", buf, IN_FAMILY(&t1)); - tor_inet_ntop(AF_INET6, &t2.sa6.sin6_addr, buf, sizeof(buf)); - printf("\nv6 address: %s (family=%d)", buf, IN_FAMILY(&t2)); -#endif + + TT_BLATHER(("v4 address: %s (family=%d)", fmt_addr(&t1), + tor_addr_family(&t1))); + TT_BLATHER(("v6 address: %s (family=%d)", fmt_addr(&t2), + tor_addr_family(&t2))); done: ; From 5d44a6b334f647b162801aa4d0c8607ac14c3ef7 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 7 Dec 2011 16:03:32 -0500 Subject: [PATCH 2/4] Multicast addresses, if any were configured, would not be good if addrs --- src/common/address.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/common/address.c b/src/common/address.c index 9ee5d61f2c..be205e4725 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -1203,6 +1203,24 @@ get_interface_addresses_raw(int severity) return NULL; #endif } + +/** Return true iff a is a multicast address. */ +static int +tor_addr_is_multicast(const tor_addr_t *a) +{ + sa_family_t family = tor_addr_family(a); + if (family == AF_INET) { + uint32_t ipv4h = tor_addr_to_ipv4h(a); + if ((ipv4h >> 24) == 0xe0) + return 1; /* Multicast */ + } else if (family == AF_INET6) { + const uint8_t *a32 = tor_addr_to_in6_addr8(a); + if (a32[0] == 0xff) + return 1; + } + return 0; +} + /** Set *addr 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. @@ -1223,8 +1241,10 @@ get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr) SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) { if (family != AF_UNSPEC && family != tor_addr_family(a)) continue; - if (tor_addr_is_loopback(a)) + if (tor_addr_is_loopback(a) || + tor_addr_is_multicast(a)) continue; + tor_addr_copy(addr, a); rv = 0; From 9f06ec0c13f0b25ce3bb2c0c352edb04083dbe2c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 12 Dec 2011 11:37:02 -0500 Subject: [PATCH 3/4] Add interface enumeration based on SIOCGIFCONF for older unixes --- configure.in | 2 ++ src/common/address.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/configure.in b/configure.in index a2aa417502..c3d53f79b6 100644 --- a/configure.in +++ b/configure.in @@ -305,6 +305,7 @@ AC_CHECK_FUNCS( gettimeofday \ gmtime_r \ inet_aton \ + ioctl \ localtime_r \ lround \ memmem \ @@ -634,6 +635,7 @@ AC_CHECK_HEADERS( malloc/malloc.h \ malloc_np.h \ netdb.h \ + net/if.h \ netinet/in.h \ netinet/in6.h \ pwd.h \ diff --git a/src/common/address.c b/src/common/address.c index be205e4725..2e9892c4dc 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -55,6 +55,12 @@ #ifdef HAVE_IFADDRS_H #include #endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif #include #include #include @@ -1198,6 +1204,43 @@ get_interface_addresses_raw(int severity) FreeLibrary(lib); tor_free(addresses); return result; +#elif defined(SIOCGIFCONF) && defined(HAVE_IOCTL) + /* Some older unixy systems make us use ioctl(SIOCGIFCONF) */ + struct ifconf ifc; + int fd, i, sz, n; + smartlist_t *result = NULL; + /* This interface, AFAICT, only supports AF_INET addresses */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + 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); + if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { + log(severity, LD_NET, "ioctl failed: %s", strerror(errno)); + close(fd); + goto done; + } + close(fd); + result = smartlist_create(); + 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))); + } + done: + tor_free(ifc.ifc_ifcu.ifcu_req); + return result; #else (void) severity; return NULL; From e94f7d6ffba5552d9610a6c869fbd52c79557f4e Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Thu, 22 Dec 2011 16:52:31 +0100 Subject: [PATCH 4/4] Remove tabs from configure.in --- configure.in | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/configure.in b/configure.in index c3d53f79b6..c167b8d40d 100644 --- a/configure.in +++ b/configure.in @@ -295,22 +295,22 @@ dnl Check for functions before libevent, since libevent-1.2 apparently dnl exports strlcpy without defining it in a header. AC_CHECK_FUNCS( - accept4 \ - clock_gettime \ + accept4 \ + clock_gettime \ flock \ ftime \ getaddrinfo \ - getifaddrs \ + getifaddrs \ getrlimit \ gettimeofday \ gmtime_r \ inet_aton \ - ioctl \ + ioctl \ localtime_r \ - lround \ + lround \ memmem \ prctl \ - rint \ + rint \ socketpair \ strlcat \ strlcpy \ @@ -626,7 +626,7 @@ dnl These headers are not essential AC_CHECK_HEADERS( arpa/inet.h \ grp.h \ - ifaddrs.h \ + ifaddrs.h \ inttypes.h \ limits.h \ linux/types.h \ @@ -635,7 +635,7 @@ AC_CHECK_HEADERS( malloc/malloc.h \ malloc_np.h \ netdb.h \ - net/if.h \ + net/if.h \ netinet/in.h \ netinet/in6.h \ pwd.h \