]> granicus.if.org Git - libevent/commitdiff
Use getifaddrs to detect our interfaces if possible
authorNick Mathewson <nickm@torproject.org>
Wed, 7 Dec 2011 23:16:32 +0000 (18:16 -0500)
committerNick Mathewson <nickm@torproject.org>
Wed, 7 Dec 2011 23:16:32 +0000 (18:16 -0500)
The old scheme involved connected UDP sockets and getsockname(), and is
generally best avoied.

configure.in
evutil.c

index a997de6f9f159cd050bb5970378e03410883f08a..6734284253c786e1d503aea45d1d0ac880ebb59f 100644 (file)
@@ -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 <sys/param.h>
@@ -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],
index 19f41bc233f0b02dace96b05f2f439e86434dcf4..292bcb93eab70daba034ba36763e78bf23ceefc2 100644 (file)
--- a/evutil.c
+++ b/evutil.c
 #undef WIN32_LEAN_AND_MEAN
 #include <io.h>
 #include <tchar.h>
+#undef _WIN32_WINNT
+/* For structs needed by GetAdaptersAddresses */
+#define _WIN32_WINNT 0x0501
+#include <iphlpapi.h>
 #endif
 
 #include <sys/types.h>
@@ -69,6 +73,9 @@
 #include <time.h>
 #endif
 #include <sys/stat.h>
+#ifdef _EVENT_HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#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)