summaryrefslogtreecommitdiff
path: root/evutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'evutil.c')
-rw-r--r--evutil.c289
1 files changed, 216 insertions, 73 deletions
diff --git a/evutil.c b/evutil.c
index 56c99097..306f037c 100644
--- a/evutil.c
+++ b/evutil.c
@@ -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
+}