diff --git a/libevent/event_iocp.c b/libevent/event_iocp.c index 4c144af..6104344 100644 --- a/libevent/event_iocp.c +++ b/libevent/event_iocp.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "event2/util.h" #include "util-internal.h" @@ -98,12 +99,72 @@ event_iocp_port_associate(struct event_iocp_port *port, evutil_socket_t fd, return 0; } +static void * +get_extention_function(SOCKET s, const GUID *which_fn) +{ + void *ptr = NULL; + DWORD bytes; + WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINER, + which_fn, sizeof(*which_fn), + &ptr, sizeof(ptr), + NULL, NULL); + + /* No need to detect errors here: if ptr is set, then we have a good + function pointer. Otherwise, we should behave as if we had no + function pointer. + */ + return ptr; +} + +/* Mingw doesn't have these in its mswsock.h. The values are copied from + wine.h. Perhaps if we copy them exactly, the cargo will come again. +*/ +#ifndef WSAID_ACCEPTEX +#define WSAID_ACCEPTEX \ + {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} +#endif +#ifndef WSAID_CONNECTEX +#define WSAID_CONNECTEX \ + {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} +#endif +#ifndef WSAID_GETACCEPTEXSOCKADDRS +#define WSAID_GETACCEPTEXSOCKADDRS \ + {0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} +#endif + +static void +init_extension_functions(struct win32_extension_fns *ext) +{ + const GUID acceptex = WSAID_ACCEPTEX; + const GUID connectex = WSAID_CONNECTEX + SOCKET s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + return; + ext->AcceptEx = get_extension_function(s, &acceptex); + ext->ConnectEx = get_extension_function(s, &connectex); + ext->GetAcceptExSockAddrs = get_extension_function(s, + &getacceptexsockaddrs); + closesocket(s); +} + +static struct win32_extension_fns the_extension_fns; +static int extension_fns_initialized = 0; + +const struct win32_extension_fns * +event_get_win32_extension_fns(void) +{ + return &the_extension_fns; +} + struct event_iocp_port * event_iocp_port_launch(void) { struct event_iocp_port *port; int i; + if (!extension_fns_initialized) + init_extension_functions(&the_extension_fns) + if (!(port = mm_calloc(1, sizeof(struct event_iocp_port)))) return NULL; port->n_threads = 2; diff --git a/libevent/iocp-internal.h b/libevent/iocp-internal.h index d743bd5..4b18fea 100644 --- a/libevent/iocp-internal.h +++ b/libevent/iocp-internal.h @@ -53,6 +53,21 @@ struct event_overlapped { iocp_callback cb; }; +/* Mingw's headers don't define LPFN_ACCEPTEX. */ + +typedef BOOL (WINAPI *AcceptExPtr)(SOCKET, SOCKET, PVOID, DWORD, DWORD, DWORD, LPDWORD, LPOVERLAPPED); +typedef BOOL (WINAPI *ConnectExPtr)(SOCKET, const struct sockaddr *, int, PVOID, DWORD, LPDWORD, LPOVERLAPPED); +typedef void (WINAPI *GetAcceptExSockaddrsPtr)(PVOID, DWORD, DWORD, DWORD, LPSOCKADDR *, LPINT, LPSOCKADDR *, LPINT); + +/** Internal use only. Holds pointers to functions that only some versions of + Windows provide. + */ +struct win32_extention_fns { + AcceptExPtr AcceptEx; + ConnectExPtr ConnectEx; + GetAcceptSockAddrsPtr GetAcceptExSockaddrs; +}; + /** Internal use only. Stores a Windows IO Completion port, along with related data. @@ -73,9 +88,11 @@ struct event_iocp_port { HANDLE *threads; /** Number of threads currently open on this port. */ short n_live_threads; - /* A semaphore to signal when we are done shutting down. */ + /** A semaphore to signal when we are done shutting down. */ HANDLE *shutdownSemaphore; }; + +const struct win32_extension_fns *event_get_win32_extension_fns(void); #else /* Dummy definition so we can test-compile more things on unix. */ struct event_overlapped { diff --git a/libevent/listener.c b/libevent/listener.c index 3051c80..76392eb 100644 --- a/libevent/listener.c +++ b/libevent/listener.c @@ -57,6 +57,10 @@ struct evconnlistener { evconnlistener_cb cb; void *user_data; unsigned flags; +#ifdef WIN32 + evutil_socket_t fd; + struct iocp_port *port; +#endif }; static void listener_read_cb(evutil_socket_t, short, void *); @@ -183,3 +187,106 @@ listener_read_cb(evutil_socket_t fd, short what, void *p) return; event_sock_warn(fd, "Error from accept() call"); } + +#ifdef WIN32 +struct accepting_socket { + struct event_overlapped overlapped; + SOCKET s; + struct evconnlistener *lev; + int buflen; + char addrbuf[1]; /*XXX */ +}; + +static struct accepting_socket * +new_accepting_socket(struct evconnlistener *lev, int family) +{ + struct accepting_socket *res; + int addrlen; + int buflen; + if (family == AF_INET) + addrlen = sizeof(struct sockaddr_in); + else if (family == AF_INET6) + addrlen = sizeof(struct sockaddr_in6); + else + return NULL; + buflen = (addrlen+16)*2; + + mm_calloc(1,sizeof(struct accepting_socket)-1+buflen); + + if (!res) + return NULL; + + event_overlapped_init(&res->overlapped, accepted_socket_cb); + res->socket = INVALID_SOCKET; + res->lev = lev; + res->buflen = buflen; + return res; +} + +static int +start_accepting(struct accepting_socket *as) +{ + const struct win32_extension_fns *ext = get_win32_extension_fns(); + int family = AF_INET; /* XXXX */ + SOCKET s = socket(family, SOCK_STREAM, 0); + DWORD pending = 0; + if (s == INVALID_SOCKET) + return -1; + + setsockopt(s, + SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, + (char *)&as->lev->fd, + sizeof(&as->lev->fd)); + + if (!(as->lev->flags & LEV_OPT_LEAVE_SOCKETS_BLOCKING)) + evutil_make_socket_nonblocking(s); + + if (event_iocp_port_associate(as->lev->iocp_port, s, 1) < 0) + return -1; + + as->s = s; + + if (ext->AcceptEx(as->lev->fd, s, as->addrbuf, 0, + as->buflen/2, as->buflen/2, + &pending, &as->o)) { + /* Immediate success! */ + accepted_socket_cb(&as->o, 1, 0); + return 0; + } else { + int err = WSAGetLastError(); + if (err == ERROR_IO_PENDING) + return 0; + /* XXXX log the error */ + return -1; + } +} + +static void +accepted_socket_cb(struct event_overlapped *o, uintptr_t key, ev_ssize_t n) +{ + /* Run this whole thing deferred unless some MT flag is set */ + + struct sockaddr *sa_local=NULL, *sa_remote=NULL; + int socklen_local=0, socklen_remote=0; + struct accepting_socket *as = + EVUTIL_UPCAST(o, struct accepting_socket, overlapped); + const struct win32_extension_fns *ext = get_win32_extension_fns(); + + EVUTIL_ASSERT(ext->GetAcceptExSockaddrs); + + ext->GetAcceptExSockaddrs(as->addrbuf, 0, + as->buflen/2, as->buflen/2, + &sa_local, &socklen_local, + &sa_remote, &socklen_remote); + + as->s = INVALID_SOCKET; + + as->lev->cb(lev, as->s, sa_remote, socklen_remote, + as->lev->user_data); + + /* Avoid stack overflow XXXX */ + start_accepting(as); +} + +#endif