summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGlenn Strauss <gstrauss@gluelogic.com>2021-02-08 13:15:16 -0500
committerGlenn Strauss <gstrauss@gluelogic.com>2023-05-03 23:11:34 -0400
commit24c5bc8869288fad7d016f966be269fb31364f09 (patch)
tree31e981db5dd9590b5a86ec1c4601ea8afa8b4342 /src
parent21eee1fad6554160daab6858591004af46bd83bb (diff)
downloadlighttpd-git-24c5bc8869288fad7d016f966be269fb31364f09.tar.gz
[core] _WIN32 implementation of socketpair()
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/Makefile.am2
-rw-r--r--src/fdevent.c41
-rw-r--r--src/fdevent.h2
-rw-r--r--src/fdevent_win32.c258
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 */