diff options
Diffstat (limited to 'evutil.c')
-rw-r--r-- | evutil.c | 289 |
1 files changed, 216 insertions, 73 deletions
@@ -25,10 +25,9 @@ */ #include "event2/event-config.h" +#include "evconfig-private.h" -#define _GNU_SOURCE - -#ifdef WIN32 +#ifdef _WIN32 #include <winsock2.h> #include <ws2tcpip.h> #define WIN32_LEAN_AND_MEAN @@ -36,6 +35,10 @@ #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> @@ -64,12 +67,18 @@ #ifdef _EVENT_HAVE_ARPA_INET_H #include <arpa/inet.h> #endif - +#if !defined(_EVENT_HAVE_NANOSLEEP) && !defined(EVENT_HAVE_USLEEP) && \ + !defined(_WIN32) +#include <sys/select.h> +#endif #ifndef _EVENT_HAVE_GETTIMEOFDAY #include <sys/timeb.h> #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" @@ -79,7 +88,7 @@ #include "strlcpy-internal.h" #include "ipv6-internal.h" -#ifdef WIN32 +#ifdef _WIN32 #define open _open #define read _read #define close _close @@ -131,7 +140,7 @@ evutil_read_file(const char *filename, char **content_out, size_t *len_out, return -2; } read_so_far = 0; -#ifdef WIN32 +#ifdef _WIN32 #define N_TO_READ(x) ((x) > INT_MAX) ? INT_MAX : ((int)(x)) #else #define N_TO_READ(x) (x) @@ -157,7 +166,7 @@ evutil_read_file(const char *filename, char **content_out, size_t *len_out, int evutil_socketpair(int family, int type, int protocol, evutil_socket_t fd[2]) { -#ifndef WIN32 +#ifndef _WIN32 return socketpair(family, type, protocol, fd); #else return evutil_ersatz_socketpair(family, type, protocol, fd); @@ -175,7 +184,7 @@ evutil_ersatz_socketpair(int family, int type, int protocol, * for now, and really, when localhost is down sometimes, we * have other problems too. */ -#ifdef WIN32 +#ifdef _WIN32 #define ERR(e) WSA##e #else #define ERR(e) e @@ -269,7 +278,7 @@ evutil_ersatz_socketpair(int family, int type, int protocol, int evutil_make_socket_nonblocking(evutil_socket_t fd) { -#ifdef WIN32 +#ifdef _WIN32 { u_long nonblocking = 1; if (ioctlsocket(fd, FIONBIO, &nonblocking) == SOCKET_ERROR) { @@ -296,7 +305,7 @@ evutil_make_socket_nonblocking(evutil_socket_t fd) int evutil_make_listen_socket_reuseable(evutil_socket_t sock) { -#ifndef WIN32 +#ifndef _WIN32 int one = 1; /* REUSEADDR on Unix means, "don't hang on to this address after the * listener is closed." On Windows, though, it means "don't keep other @@ -311,7 +320,7 @@ evutil_make_listen_socket_reuseable(evutil_socket_t sock) int evutil_make_socket_closeonexec(evutil_socket_t fd) { -#if !defined(WIN32) && defined(_EVENT_HAVE_SETFD) +#if !defined(_WIN32) && defined(_EVENT_HAVE_SETFD) int flags; if ((flags = fcntl(fd, F_GETFD, NULL)) < 0) { event_warn("fcntl(%d, F_GETFD)", fd); @@ -328,7 +337,7 @@ evutil_make_socket_closeonexec(evutil_socket_t fd) int evutil_closesocket(evutil_socket_t sock) { -#ifndef WIN32 +#ifndef _WIN32 return close(sock); #else return closesocket(sock); @@ -342,7 +351,7 @@ evutil_strtoll(const char *s, char **endptr, int base) return (ev_int64_t)strtoll(s, endptr, base); #elif _EVENT_SIZEOF_LONG == 8 return (ev_int64_t)strtol(s, endptr, base); -#elif defined(WIN32) && defined(_MSC_VER) && _MSC_VER < 1300 +#elif defined(_WIN32) && defined(_MSC_VER) && _MSC_VER < 1300 /* XXXX on old versions of MS APIs, we only support base * 10. */ ev_int64_t r; @@ -358,7 +367,7 @@ evutil_strtoll(const char *s, char **endptr, int base) if (endptr) *endptr = (char*) s; return r; -#elif defined(WIN32) +#elif defined(_WIN32) return (ev_int64_t) _strtoi64(s, endptr, base); #elif defined(_EVENT_SIZEOF_LONG_LONG) && _EVENT_SIZEOF_LONG_LONG == 8 long long r; @@ -396,31 +405,46 @@ evutil_strtoll(const char *s, char **endptr, int base) } #ifndef _EVENT_HAVE_GETTIMEOFDAY -/* No gettimeofday; this muse be windows. */ +/* No gettimeofday; this must be windows. */ int evutil_gettimeofday(struct timeval *tv, struct timezone *tz) { - struct _timeb tb; +#ifdef _MSC_VER +#define U64_LITERAL(n) n##ui64 +#else +#define U64_LITERAL(n) n##llu +#endif + + /* Conversion logic taken from Tor, which in turn took it + * from Perl. GetSystemTimeAsFileTime returns its value as + * an unaligned (!) 64-bit value containing the number of + * 100-nanosecond intervals since 1 January 1601 UTC. */ +#define EPOCH_BIAS U64_LITERAL(116444736000000000) +#define UNITS_PER_SEC U64_LITERAL(10000000) +#define USEC_PER_SEC U64_LITERAL(1000000) +#define UNITS_PER_USEC U64_LITERAL(10) + union { + FILETIME ft_ft; + ev_uint64_t ft_64; + } ft; if (tv == NULL) return -1; - /* XXXX - * _ftime is not the greatest interface here; GetSystemTimeAsFileTime - * would give us better resolution, whereas something cobbled together - * with GetTickCount could maybe give us monotonic behavior. - * - * Either way, I think this value might be skewed to ignore the - * timezone, and just return local time. That's not so good. - */ - _ftime(&tb); - tv->tv_sec = (long) tb.time; - tv->tv_usec = ((int) tb.millitm) * 1000; + GetSystemTimeAsFileTime(&ft.ft_ft); + + if (EVUTIL_UNLIKELY(ft.ft_64 < EPOCH_BIAS)) { + /* Time before the unix epoch. */ + return -1; + } + ft.ft_64 -= EPOCH_BIAS; + tv->tv_sec = (long) (ft.ft_64 / UNITS_PER_SEC); + tv->tv_usec = (long) ((ft.ft_64 / UNITS_PER_USEC) % USEC_PER_SEC); return 0; } #endif -#ifdef WIN32 +#ifdef _WIN32 int evutil_socket_geterror(evutil_socket_t sock) { @@ -520,28 +544,150 @@ 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"; + + 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; + } + } +} + +#ifdef _WIN32 +typedef ULONG (WINAPI *GetAdaptersAddresses_fn_t)( + ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG); +#endif + +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 = mm_malloc(size); + if (!addresses) + goto done; + res = fn(AF_UNSPEC, FLAGS, NULL, addresses, &size); + } + if (res != NO_ERROR) + goto done; + + 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); @@ -562,21 +708,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); @@ -585,21 +717,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) @@ -700,7 +818,7 @@ evutil_parse_servname(const char *servname, const char *protocol, int n = parse_numeric_servname(servname); if (n>=0) return n; -#if defined(_EVENT_HAVE_GETSERVBYNAME) || defined(WIN32) +#if defined(_EVENT_HAVE_GETSERVBYNAME) || defined(_WIN32) if (!(hints->ai_flags & EVUTIL_AI_NUMERICSERV)) { struct servent *ent = getservbyname(servname, protocol); if (ent) { @@ -1200,7 +1318,7 @@ evutil_getaddrinfo(const char *nodename, const char *servname, * ever resolving even a literal IPv6 address when * ai_addrtype is PF_UNSPEC. */ -#ifdef WIN32 +#ifdef _WIN32 { int tmp_port; err = evutil_getaddrinfo_common(nodename,servname,&hints, @@ -1297,7 +1415,7 @@ evutil_getaddrinfo(const char *nodename, const char *servname, /* fall back to gethostbyname. */ /* XXXX This needs a lock everywhere but Windows. */ ent = gethostbyname(nodename); -#ifdef WIN32 +#ifdef _WIN32 err = WSAGetLastError(); #else err = h_errno; @@ -1433,7 +1551,7 @@ evutil_gai_strerror(int err) case EVUTIL_EAI_SYSTEM: return "system error"; default: -#if defined(USE_NATIVE_GETADDRINFO) && defined(WIN32) +#if defined(USE_NATIVE_GETADDRINFO) && defined(_WIN32) return gai_strerrorA(err); #elif defined(USE_NATIVE_GETADDRINFO) return gai_strerror(err); @@ -1443,7 +1561,7 @@ evutil_gai_strerror(int err) } } -#ifdef WIN32 +#ifdef _WIN32 #define E(code, s) { code, (s " [" #code " ]") } static struct { int code; const char *msg; } windows_socket_errors[] = { E(WSAEINTR, "Interrupted function call"), @@ -2077,7 +2195,7 @@ evutil_getenv(const char *varname) long _evutil_weakrand(void) { -#ifdef WIN32 +#ifdef _WIN32 return rand(); #else return random(); @@ -2136,7 +2254,7 @@ evutil_hex_char_to_int(char c) return -1; } -#ifdef WIN32 +#ifdef _WIN32 HANDLE evutil_load_windows_system_library(const TCHAR *library_name) { @@ -2151,3 +2269,28 @@ evutil_load_windows_system_library(const TCHAR *library_name) } #endif +void +evutil_usleep(const struct timeval *tv) +{ + if (!tv) + return; +#if defined(_WIN32) + { + long msec = evutil_tv_to_msec(tv); + Sleep((DWORD)msec); + } +#elif defined(_EVENT_HAVE_NANOSLEEP_X) + { + struct timespec ts; + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec*1000; + nanosleep(&ts, NULL); + } +#elif defined(_EVENT_HAVE_USLEEP) + /* Some systems don't like to usleep more than 999999 usec */ + sleep(tv->tv_sec); + usleep(tv->tv_usec); +#else + select(0, NULL, NULL, NULL, tv); +#endif +} |