commit aa529f6c32f2103a5571f98047ee3bd2d5074330 Author: Nick Mathewson nickm@torproject.org Date: Fri Dec 2 17:05:52 2011 -0500
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 1f045f1..a2aa417 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 c533687..9ee5d61 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 <process.h> #include <windows.h> +#include <winsock2.h> +/* For access to structs needed by GetAdaptersAddresses */ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#include <iphlpapi.h> #endif
#ifdef HAVE_SYS_TIME_H @@ -46,6 +52,9 @@ #ifdef HAVE_SYS_UN_H #include <sys/un.h> #endif +#ifdef HAVE_IFADDRS_H +#include <ifaddrs.h> +#endif #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -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 <b>severity</b>. */ +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 *<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. @@ -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 9d8e1fe..cf9c8f9 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: ;