From 7085a4568c2144520cbb26fc11ff28b283821f15 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 7 Dec 2011 18:16:32 -0500 Subject: [PATCH] Use getifaddrs to detect our interfaces if possible The old scheme involved connected UDP sockets and getsockname(), and is generally best avoied. --- configure.in | 4 +- evutil.c | 176 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 140 insertions(+), 40 deletions(-) diff --git a/configure.in b/configure.in index a997de6f..67342842 100644 --- a/configure.in +++ b/configure.in @@ -178,7 +178,7 @@ LIBEVENT_OPENSSL dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in.h netinet/in6.h sys/socket.h sys/uio.h arpa/inet.h sys/eventfd.h sys/mman.h sys/sendfile.h sys/wait.h netdb.h]) +AC_CHECK_HEADERS([fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in.h netinet/in6.h sys/socket.h sys/uio.h arpa/inet.h sys/eventfd.h sys/mman.h sys/sendfile.h sys/wait.h netdb.h ifaddrs.h]) AC_CHECK_HEADERS(sys/sysctl.h, [], [], [ #ifdef HAVE_SYS_PARAM_H #include @@ -277,7 +277,7 @@ AC_HEADER_TIME dnl Checks for library functions. AC_CHECK_FUNCS([gettimeofday vasprintf fcntl clock_gettime strtok_r strsep]) -AC_CHECK_FUNCS([getnameinfo strlcpy inet_ntop inet_pton signal sigaction strtoll inet_aton pipe eventfd sendfile mmap splice arc4random arc4random_buf issetugid geteuid getegid getprotobynumber setenv unsetenv putenv]) +AC_CHECK_FUNCS([getnameinfo strlcpy inet_ntop inet_pton signal sigaction strtoll inet_aton pipe eventfd sendfile mmap splice arc4random arc4random_buf issetugid geteuid getegid getprotobynumber setenv unsetenv putenv getifaddrs]) AC_CACHE_CHECK( [for getaddrinfo], diff --git a/evutil.c b/evutil.c index 19f41bc2..292bcb93 100644 --- a/evutil.c +++ b/evutil.c @@ -35,6 +35,10 @@ #undef WIN32_LEAN_AND_MEAN #include #include +#undef _WIN32_WINNT +/* For structs needed by GetAdaptersAddresses */ +#define _WIN32_WINNT 0x0501 +#include #endif #include @@ -69,6 +73,9 @@ #include #endif #include +#ifdef _EVENT_HAVE_IFADDRS_H +#include +#endif #include "event2/util.h" #include "util-internal.h" @@ -534,28 +541,149 @@ static int have_checked_interfaces, had_ipv4_address, had_ipv6_address; */ #define EVUTIL_V4ADDR_IS_CLASSD(addr) ((((addr)>>24) & 0xf0) == 0xe0) +static void +evutil_found_ifaddr(const struct sockaddr *sa) +{ + const char ZEROES[] = "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00"; + char buf[128]; + + printf("Sockaddr is %s\n", evutil_format_sockaddr_port(sa, buf, sizeof(buf))); + + if (sa->sa_family == AF_INET) { + const struct sockaddr_in *sin = (struct sockaddr_in *)sa; + ev_uint32_t addr = ntohl(sin->sin_addr.s_addr); + if (addr == 0 || + EVUTIL_V4ADDR_IS_LOCALHOST(addr) || + EVUTIL_V4ADDR_IS_CLASSD(addr)) { + /* Not actually a usable external address. */ + } else { + event_debug(("Detected an IPv4 interface")); + had_ipv4_address = 1; + } + } else if (sa->sa_family == AF_INET6) { + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + const unsigned char *addr = + (unsigned char*)sin6->sin6_addr.s6_addr; + if (!memcmp(addr, ZEROES, 8) || + ((addr[0] & 0xfe) == 0xfc) || + (addr[0] == 0xfe && (addr[1] & 0xc0) == 0x80) || + (addr[0] == 0xfe && (addr[1] & 0xc0) == 0xc0) || + (addr[0] == 0xff)) { + /* This is a reserved, ipv4compat, ipv4map, loopback, + * link-local, multicast, or unspecified address. */ + } else { + event_debug(("Detected an IPv6 interface")); + had_ipv6_address = 1; + } + } +} + +static int +evutil_check_ifaddrs(void) +{ +#if defined(_EVENT_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; + if (getifaddrs(&ifa) < 0) { + event_warn("Unable to call getifaddrs()"); + return -1; + } + + for (i = ifa; i; i = i->ifa_next) { + if (!i->ifa_addr) + continue; + evutil_found_ifaddr(i->ifa_addr); + } + + freeifaddrs(ifa); + return 0; +#elif defined(_WIN32) + /* 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 = evutil_load_windows_system_library(TEXT("ihplapi.dll")); + GetAdaptersAddresses_fn_t fn; + ULONG size, res; + IP_ADAPTER_ADDRESSES *addresses = NULL, *address; + int result = -1; + +#define FLAGS (GAA_FLAG_SKIP_ANYCAST | \ + GAA_FLAG_SKIP_MULTICAST | \ + GAA_FLAG_SKIP_DNS_SERVER) + + if (!lib) + goto done; + + if (!(fn = (GetAdaptersAddresses_fn_t) GetProcAddress(lib, "GetAdaptersAddresses"))) + goto done; + + /* Guess how much space we need. */ + size = 15*1024; + addresses = mm_malloc(size); + if (!addresses) + goto done; + res = fn(AF_UNSPEC, FLAGS, NULL, addresses, &size); + if (res == ERROR_BUFFER_OVERFLOW) { + /* we didn't guess that we needed enough space; try again */ + mm_free(addresses); + addresses = tor_malloc(size); + if (!addresses) + goto done; + res = fn(AF_UNSPEC, FLAGS, NULL, addresses, &size); + } + if (res != NO_ERROR) + 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; + evutil_found_ifaddr(sa); + } + } + + result = 0; +done: + if (lib) + FreeLibrary(lib); + if (addresses) + mm_free(addresses); + return result; +#else + return -1; +#endif +} + /* Test whether we have an ipv4 interface and an ipv6 interface. Return 0 if * the test seemed successful. */ static int evutil_check_interfaces(int force_recheck) { - const char ZEROES[] = "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00"; evutil_socket_t fd = -1; struct sockaddr_in sin, sin_out; struct sockaddr_in6 sin6, sin6_out; ev_socklen_t sin_out_len = sizeof(sin_out); ev_socklen_t sin6_out_len = sizeof(sin6_out); int r; - char buf[128]; if (have_checked_interfaces && !force_recheck) return 0; - /* To check whether we have an interface open for a given protocol, we - * try to make a UDP 'connection' to a remote host on the internet. - * We don't actually use it, so the address doesn't matter, but we - * want to pick one that keep us from using a host- or link-local - * interface. */ + if (evutil_check_ifaddrs() == 0) { + /* Use a nice sane interface, if this system has one. */ + return 0; + } + + /* Ugh. There was no nice sane interface. So to check whether we have + * an interface open for a given protocol, will try to make a UDP + * 'connection' to a remote host on the internet. We don't actually + * use it, so the address doesn't matter, but we want to pick one that + * keep us from using a host- or link-local interface. */ memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(53); @@ -576,21 +704,7 @@ evutil_check_interfaces(int force_recheck) connect(fd, (struct sockaddr*)&sin, sizeof(sin)) == 0 && getsockname(fd, (struct sockaddr*)&sin_out, &sin_out_len) == 0) { /* We might have an IPv4 interface. */ - ev_uint32_t addr = ntohl(sin_out.sin_addr.s_addr); - if (addr == 0 || - EVUTIL_V4ADDR_IS_LOCALHOST(addr) || - EVUTIL_V4ADDR_IS_CLASSD(addr)) { - evutil_inet_ntop(AF_INET, &sin_out.sin_addr, - buf, sizeof(buf)); - /* This is a reserved, ipv4compat, ipv4map, loopback, - * link-local or unspecified address. The host should - * never have given it to us; it could never connect - * to sin. */ - event_warnx("Got a strange local ipv4 address %s",buf); - } else { - event_debug(("Detected an IPv4 interface")); - had_ipv4_address = 1; - } + evutil_found_ifaddr((struct sockaddr*) &sin_out); } if (fd >= 0) evutil_closesocket(fd); @@ -599,21 +713,7 @@ evutil_check_interfaces(int force_recheck) connect(fd, (struct sockaddr*)&sin6, sizeof(sin6)) == 0 && getsockname(fd, (struct sockaddr*)&sin6_out, &sin6_out_len) == 0) { /* We might have an IPv6 interface. */ - const unsigned char *addr = - (unsigned char*)sin6_out.sin6_addr.s6_addr; - if (!memcmp(addr, ZEROES, 8) || - (addr[0] == 0xfe && (addr[1] & 0xc0) == 0x80)) { - /* This is a reserved, ipv4compat, ipv4map, loopback, - * link-local or unspecified address. The host should - * never have given it to us; it could never connect - * to sin6. */ - evutil_inet_ntop(AF_INET6, &sin6_out.sin6_addr, - buf, sizeof(buf)); - event_warnx("Got a strange local ipv6 address %s",buf); - } else { - event_debug(("Detected an IPv4 interface")); - had_ipv6_address = 1; - } + evutil_found_ifaddr((struct sockaddr*) &sin6_out); } if (fd >= 0) -- 2.40.0