diff options
author | Glenn Strauss <gstrauss@gluelogic.com> | 2021-02-08 13:15:16 -0500 |
---|---|---|
committer | Glenn Strauss <gstrauss@gluelogic.com> | 2023-05-03 23:11:34 -0400 |
commit | 24c5bc8869288fad7d016f966be269fb31364f09 (patch) | |
tree | 31e981db5dd9590b5a86ec1c4601ea8afa8b4342 /src | |
parent | 21eee1fad6554160daab6858591004af46bd83bb (diff) | |
download | lighttpd-git-24c5bc8869288fad7d016f966be269fb31364f09.tar.gz |
[core] _WIN32 implementation of socketpair()
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/fdevent.c | 41 | ||||
-rw-r--r-- | src/fdevent.h | 2 | ||||
-rw-r--r-- | src/fdevent_win32.c | 258 |
5 files changed, 306 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1afcc17a..60269013 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -840,6 +840,9 @@ set(COMMON_SRC sys-setjmp.c ck.c ) +if(WIN32) + set(COMMON_SRC ${COMMON_SRC} fdevent_win32.c) +endif() set(BUILTIN_MODS mod_rewrite.c diff --git a/src/Makefile.am b/src/Makefile.am index 98afe53a..4ab4e3a4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -66,6 +66,8 @@ common_src=base64.c buffer.c burl.c log.c \ sys-setjmp.c \ ck.c +common_src += fdevent_win32.c + src = server.c response.c connections.c h2.c reqpool.c \ plugin.c \ sock_addr_cache.c \ diff --git a/src/fdevent.c b/src/fdevent.c index 2100dbc2..c18fac57 100644 --- a/src/fdevent.c +++ b/src/fdevent.c @@ -135,6 +135,47 @@ int fdevent_socket_nb_cloexec(int domain, int type, int protocol) { return fd; } +#ifndef _WIN32 +#if 0 /* not used */ + +int fdevent_socketpair_cloexec (int domain, int typ, int protocol, int sv[2]) +{ + sv[0] = sv[1] = -1; + #if defined(SOCK_CLOEXEC) + return socketpair(domain, typ | SOCK_CLOEXEC, protocol, sv); + #else + if (0 == socketpair(domain, typ, protocol, sv)) { + fdevent_setfd_cloexec(sv[0]); + fdevent_setfd_cloexec(sv[1]); + return 0; + } + return -1; + #endif +} + +int fdevent_socketpair_nb_cloexec (int domain, int typ, int protocol, int sv[2]) +{ + sv[0] = sv[1] = -1; + #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + return socketpair(domain, typ | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol, sv); + #else + if (0 == socketpair(domain, typ, protocol, sv)) { + if (0 == fdevent_fcntl_set_nb_cloexec(sv[0]) + && 0 == fdevent_fcntl_set_nb_cloexec(sv[1])) + return 0; + + close(sv[0]); + close(sv[1]); + sv[0] = sv[1] = -1; + } + return -1; + #endif +} + +#endif /* not used */ + +#endif /* !_WIN32 */ + int fdevent_dup_cloexec (int fd) { #ifdef F_DUPFD_CLOEXEC return fcntl(fd, F_DUPFD_CLOEXEC, 3); diff --git a/src/fdevent.h b/src/fdevent.h index 7dcfb0cf..94f2e3b6 100644 --- a/src/fdevent.h +++ b/src/fdevent.h @@ -83,6 +83,8 @@ int fdevent_fcntl_set_nb_cloexec(int fd); int fdevent_fcntl_set_nb_cloexec_sock(int fd); int fdevent_socket_cloexec(int domain, int type, int protocol); int fdevent_socket_nb_cloexec(int domain, int type, int protocol); +int fdevent_socketpair_cloexec(int domain, int typ, int protocol, int sv[2]); +int fdevent_socketpair_nb_cloexec(int domain, int typ, int protocol, int sv[2]); int fdevent_dup_cloexec(int fd); int fdevent_open_cloexec(const char *pathname, int symlinks, int flags, mode_t mode); int fdevent_pipe_cloexec (int *fds, unsigned int bufsz_hint); diff --git a/src/fdevent_win32.c b/src/fdevent_win32.c new file mode 100644 index 00000000..20c887b2 --- /dev/null +++ b/src/fdevent_win32.c @@ -0,0 +1,258 @@ +/* + * fdevent_win32 - _WIN32 compatibility using awful MS APIs + * + * Copyright(c) 2021 Glenn Strauss gstrauss()gluelogic.com All rights reserved + * License: BSD 3-clause (same as lighttpd) + * + * x86_64-w64-mingw32-gcc -I. -o a.out fdevent_win32.c -lws2_32 -DTEST + */ +#ifdef _WIN32 +/* https://docs.microsoft.com/en-us/windows/win32/winprog/using-the-windows-headers */ +/* http://web.archive.org/web/20121219084749/http://support.microsoft.com/kb/166474 */ +#define WIN32_LEAN_AND_MEAN +#define VC_EXTRALEAN +/* https://docs.microsoft.com/en-us/previous-versions/ms235384(v=vs.100) */ +#ifdef _MSC_VER +#define _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif +#endif /* _WIN32 */ + +#include "first.h" + + +#ifdef _WIN32 + + +/* + * Microsoft: you look foolish needing 10+ syscalls and ~200 lines of code + * to accomplish what is 1 syscall and 1 line in *nix (socketpair()), + * and taking some ~670 us in Windows instead of ~1 us in *nix. + */ + +#include <winsock2.h> +#include <ws2tcpip.h> +#include <io.h> +#ifdef HAVE_AFUNIX_H +#include <afunix.h> +#else +#define UNIX_PATH_MAX 108 +typedef struct sockaddr_un +{ + ADDRESS_FAMILY sun_family; /* AF_UNIX */ + char sun_path[UNIX_PATH_MAX]; /* pathname */ +} SOCKADDR_UN, *PSOCKADDR_UN; +#endif +#include <string.h> +#include <stdio.h> + +#include "fdevent.h" + + +int fdevent_socketpair_cloexec (int domain, int typ, int protocol, int sv[2]) +{ + struct sockaddr_storage ss; + struct sockaddr *addr = (struct sockaddr *)&ss; + socklen_t addrlen; + + if (NULL == sv) { + WSASetLastError(WSAEINVAL); + return SOCKET_ERROR; + } + sv[0] = sv[1] = -1; + + if (domain == AF_UNIX) { + struct sockaddr_un *un = (struct sockaddr_un *)&ss; + memset(un, 0, sizeof(struct sockaddr_un)); + un->sun_family = AF_UNIX; + #if 0 /* Windows does not support connect() to abstract AF_UNIX sockets */ + /* https://github.com/microsoft/WSL/issues/4240 */ + /* XXX: TODO: generate random address (use stack addr?) */ + char sun_path[] = "\0./abcd"; + addrlen = 2+sizeof(sun_path); /*(include trailing '\0')*/ + memcpy(un->sun_path, sun_path, addrlen-2); + #else + if (0 != tmpnam_s(un->sun_path, UNIX_PATH_MAX)) + return SOCKET_ERROR; + addrlen = 2+strlen(un->sun_path)+1; + if (un->sun_path[0] == '\\') + memmove(un->sun_path, un->sun_path+1, --addrlen - 2); + #endif + } + else if (domain == AF_INET) { + /* TCP/IP might be faster than filesystem shenanigans with AF_UNIX */ + struct sockaddr_in *in = (struct sockaddr_in *)&ss; + memset(in, 0, sizeof(struct sockaddr_in)); + in->sin_family = AF_INET; + in->sin_port = 0; + in->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addrlen = sizeof(struct sockaddr_in); + } + else if (domain == AF_INET6) { + /* TCP/IP might be faster than filesystem shenanigans with AF_UNIX */ + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&ss; + memset(in6, 0, sizeof(struct sockaddr_in6)); + in6->sin6_family = AF_INET6; + in6->sin6_port = 0; + memcpy(&in6->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)); + addrlen = sizeof(struct sockaddr_in6); + } + else { + WSASetLastError(WSAEAFNOSUPPORT); + return SOCKET_ERROR; + } + + /* _WIN32: Temporarily listen() on constructed addr. + * Then connect() to self to create socketpair. + */ + + SOCKET lfd; + SOCKET fds[2] = { INVALID_SOCKET, INVALID_SOCKET }; + + do { + + /* !!! sockets are blocking by default !!! + * (_WIN32 does not have the equivalent of SOCK_NONBLOCK) */ + + /* set up listener */ + + /* WSA_FLAG_NO_HANDLE_INHERIT has similar effect to unix SOCK_CLOEXEC */ + /* WSA_FLAG_NO_HANDLE_INHERIT available since Windows 7 Service Pack 1*/ + lfd = WSASocket(addr->sa_family, typ, protocol, NULL, 0, + WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); + if (INVALID_SOCKET == lfd) + break; + #if 0 + if (addr->sa_family != AF_UNIX) { + /* SO_REUSEADDR on AF_UNIX results in connect() fail WSAOPNOTSUPP */ + /* SO_REUSEADDR on AF_INET or AF_INET6 is not desirable here since + * kernel assigned port and we do not want others to be able to bind + * to this address and port while we are listening */ + int v = 1; + if (0!=setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,(char *)&v,sizeof(v))) + break; + } + #endif + #if 0 /* should not be necessary on path generated by tmpname_s() */ + /*(bind() below should fail if path exists)*/ + if (addr->sa_family == AF_UNIX + && ((struct sockaddr_un *)addr)->sun_path[0] != '\0') { + if (0 != DeleteFile(((struct sockaddr_un *)addr)->sun_path) + && ERROR_FILE_NOT_FOUND != GetLastError()) + break; + } + #endif + + if (0 != bind(lfd, addr, addrlen)) + break; + if (0 != listen(lfd, 1))/*(backlog explicit 1 for a bit more security)*/ + break; + + /* retrieve port assigned by kernel if AF_INET or AF_INET6 port == 0 */ + if ((addr->sa_family == AF_INET + && 0 == ((struct sockaddr_in *)addr)->sin_port) + || (addr->sa_family == AF_INET6 + && 0 == ((struct sockaddr_in6 *)addr)->sin6_port)) { + if (0 != getsockname(lfd, addr, &addrlen)) + break; + } + + /* connect to listener (create first half of socketpair) */ + + /* listen() backlog 1 so that if malicious actor races to connect() + * to listen() socket (established on unused port by kernel), then + * (blocking) connect() here should fail with WSAECONNREFUSED. + * For similar reason, a persistent socketpair() server would have to + * take additional precautions to prevent misuse, e.g. by requiring + * client to pass an auth token secret upon connect() + * (e.g. secret auth token could be a 64-bit number (8-bytes) + * of secret random data generated here and validated here) + * Still, a malicious user on the system could send flood of connection + * requests to DoS the listening socket unless Windows Firewall was also + * configured to only permit connections from the same user as the user + * holding the listening socket + * Note: intentionally create fds[0] without WSA_FLAG_OVERLAPPED since + * this function is intended for use with lighttpd mod_cgi. + * I/O redirection to sockets of MS standard input and output handles + * works only when the sockets are non-overlapped (and inheritable, but + * inheritability can be added back later with SetHandleInformation())*/ + fds[0] = WSASocket(addr->sa_family, typ, protocol, NULL, 0, + /*WSA_FLAG_OVERLAPPED |*/WSA_FLAG_NO_HANDLE_INHERIT); + if (INVALID_SOCKET == fds[0]) + break; + /* XXX: AF_UNIX abstract socket path does not work; fails WSAEINVAL + * https://github.com/microsoft/WSL/issues/4240 */ + if (0 != connect(fds[0], addr, addrlen)) + break; + + /* accept connection (create second half of socketpair) */ + + /* WSA_FLAG_NO_HANDLE_INHERIT state may be inherited from listen() + * socket state, but that behavior is not documented according to + * https://curl.se/mail/lib-2019-12/0008.html */ + fds[1] = accept(lfd, NULL, NULL); + if (INVALID_SOCKET == fds[1]) + break; + /*(race condition after accept() exists if noinherit not already set)*/ + SetHandleInformation((HANDLE)fds[1], HANDLE_FLAG_INHERIT, 0); + + closesocket(lfd); + sv[0] = (int)fds[0]; + sv[1] = (int)fds[1]; + if (addr->sa_family == AF_UNIX + && ((struct sockaddr_un *)addr)->sun_path[0] != '\0') + DeleteFile(((struct sockaddr_un *)addr)->sun_path); + return 0; + + } while (0); + + int errnum = WSAGetLastError(); + if (INVALID_SOCKET != lfd) closesocket(lfd); + if (INVALID_SOCKET != fds[0]) closesocket(fds[0]); + if (INVALID_SOCKET != fds[1]) closesocket(fds[1]); + if (addr->sa_family == AF_UNIX + && ((struct sockaddr_un *)addr)->sun_path[0] != '\0') + DeleteFile(((struct sockaddr_un *)addr)->sun_path); + WSASetLastError(errnum); + return SOCKET_ERROR; +} + + +int fdevent_socketpair_nb_cloexec (int domain, int typ, int protocol, int sv[2]) +{ + if (0 != fdevent_socketpair_cloexec(domain, typ, protocol, sv)) + return SOCKET_ERROR; + + /* nonblocking sockets */ + u_long ul; + if (0 == ioctlsocket((SOCKET)(uint64_t)sv[0], FIONBIO, (ul = 1, &ul)) + && 0 == ioctlsocket((SOCKET)(uint64_t)sv[1], FIONBIO, (ul = 1, &ul))) + return 0; + + int errnum = WSAGetLastError(); + closesocket((SOCKET)(uint64_t)sv[0]); + closesocket((SOCKET)(uint64_t)sv[1]); + WSASetLastError(errnum); + sv[0] = sv[1] = -1; + return SOCKET_ERROR; +} + + +#ifdef TEST +int main (void) +{ + WSADATA wsaData; + if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData)) + return -1; + + int sv[2]; + int rc = 0 == fdevent_socketpair_cloexec(AF_INET, SOCK_STREAM, 0, sv) + && 0 == fdevent_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sv); + + WSACleanup(); + return rc ? 0 : -1; +} +#endif + + +#endif /* _WIN32 */ |