From 7ae7010aa705260860aaf801c27cb3a54e6689f7 Mon Sep 17 00:00:00 2001 From: Fabrizio Bertocci Date: Sat, 13 Mar 2021 14:11:58 -0500 Subject: [PATCH] Re-implemented the Windows version of socketpair(). In particular there are two versions, one that build a packet-oriented socketpair and another one for stream-oriented pairs. --- libc/sock/internal.h | 3 +- libc/sock/socketpair-nt.c | 303 ++++++++++++++++++++++++++------------ libc/sock/socketpair.c | 31 +++- 3 files changed, 242 insertions(+), 95 deletions(-) diff --git a/libc/sock/internal.h b/libc/sock/internal.h index a52f4717b..4359ae5c2 100644 --- a/libc/sock/internal.h +++ b/libc/sock/internal.h @@ -105,7 +105,8 @@ int sys_bind_nt(struct Fd *, const void *, uint32_t); int sys_accept_nt(struct Fd *, void *, uint32_t *, int) hidden; int sys_closesocket_nt(struct Fd *) hidden; int sys_socket_nt(int, int, int) hidden; -int sys_socketpair_nt(int, int, int, int[2]) hidden; +int sys_socketpair_nt_stream(int, int, int, int[2]) hidden; +int sys_socketpair_nt_dgram(int, int, int, int[2]) hidden; int sys_select_nt(int, fd_set *, fd_set *, fd_set *, struct timeval *) hidden; int sys_shutdown_nt(struct Fd *, int) hidden; diff --git a/libc/sock/socketpair-nt.c b/libc/sock/socketpair-nt.c index aecd460c0..dadbc259b 100644 --- a/libc/sock/socketpair-nt.c +++ b/libc/sock/socketpair-nt.c @@ -29,108 +29,225 @@ #include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/ipproto.h" +#include "libc/sysv/consts/inaddr.h" -textwindows int sys_socketpair_nt(int family, int type, int protocol, int sv[2]) { - struct sockaddr_storage ss; - struct sockaddr_in *sa = (struct sockaddr_in *)&ss; - uint32_t ss_len; - int spR = -1, spW = -1; - int opt; - int rc = -1; +/* Polyfill for socketpair() on Windows with family=AF_INET and + * type=SOCK_STREAM or SOCK_SEQPACKET + */ +textwindows int sys_socketpair_nt_stream(int family, int type, int proto, int sv[2]) { + struct sockaddr_storage ss; + struct sockaddr_in *sa = (struct sockaddr_in *)&ss; + uint32_t ss_len; + int spR = -1, spW = -1; + int opt; + int rc = -1; + int listensock = -1; - int listensock = -1; - sv[0] = -1; /* INVALID_SOCKET */ - sv[1] = -1; /* INVALID_SOCKET */ + /* Avoid warnings for unused parameters */ + (void)family; + (void)proto; - memset(&ss, 0, sizeof(ss)); - if (family == AF_INET){ - sa->sin_family = family; - sa->sin_addr.s_addr = htonl(0x7f000001); - sa->sin_port = 0; - ss_len = sizeof(struct sockaddr_in); - } else { - return -1; + sv[0] = -1; /* INVALID_SOCKET */ + sv[1] = -1; /* INVALID_SOCKET */ + + memset(&ss, 0, sizeof(ss)); + sa->sin_family = AF_INET; + sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sa->sin_port = 0; + ss_len = sizeof(struct sockaddr_in); + + listensock = sys_socket_nt(AF_INET, type, IPPROTO_TCP); + if (listensock == -1) { + errno = WSAGetLastError(); + goto done; + } + + if (bind(listensock, (struct sockaddr *)&ss, ss_len) == -1) { + goto done; + } + + if (listen(listensock, 1) == -1) { + goto done; + } + + memset(&ss, 0, sizeof(ss)); + ss_len = sizeof(ss); + if (getsockname(listensock, (struct sockaddr *)&ss, &ss_len) < 0) { + goto done; + } + + sa->sin_family = AF_INET; + sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ss_len = sizeof(struct sockaddr_in); + + spR = socket(AF_INET, type, IPPROTO_TCP); + if (spR == -1) { + goto done; + } + + /* Set non-blocking */ + opt = fcntl(spR, F_GETFL, 0); + if (opt == -1) { + goto done; + } + if(fcntl(spR, F_SETFL, opt | O_NONBLOCK) == -1) { + goto done; + } + + if (connect(spR, (struct sockaddr *)&ss, ss_len) < 0) { + errno = WSAGetLastError(); + if (errno != EINPROGRESS && errno != EWOULDBLOCK) { + goto done; } + } - listensock = sys_socket_nt(family, SOCK_STREAM, IPPROTO_TCP); - if (listensock == -1){ - goto done; + spW = accept(listensock, NULL, 0); + if(spW == -1) { + errno = WSAGetLastError(); + if (errno != EINPROGRESS && errno != EWOULDBLOCK) { + goto done; } + } + /* Set non-blocking */ + opt = fcntl(spW, F_GETFL, 0); + if (opt == -1) { + goto done; + } + if(fcntl(spW, F_SETFL, opt | O_NONBLOCK) == -1) { + goto done; + } - if (bind(listensock, (struct sockaddr *)&ss, ss_len) == -1){ - goto done; - } - - if (listen(listensock, 1) == -1){ - goto done; - } - - memset(&ss, 0, sizeof(ss)); - ss_len = sizeof(ss); - if (getsockname(listensock, (struct sockaddr *)&ss, &ss_len) < 0){ - goto done; - } - - sa->sin_family = family; - sa->sin_addr.s_addr = htonl(0x7f000001); - ss_len = sizeof(struct sockaddr_in); - - spR = socket(family, SOCK_STREAM, IPPROTO_TCP); - if (spR == -1){ - goto done; - } - - /* Set non-blocking */ - opt = fcntl(spR, F_GETFL, 0); - if (opt == -1) { - goto done; - } - if(fcntl(spR, F_SETFL, opt | O_NONBLOCK) == -1){ - goto done; - } - - if (connect(spR, (struct sockaddr *)&ss, ss_len) < 0){ - errno = WSAGetLastError(); - if (errno != EINPROGRESS && errno != EWOULDBLOCK){ - goto done; - } - } - - spW = accept(listensock, NULL, 0); - if(spW == -1){ - errno = WSAGetLastError(); - if (errno != EINPROGRESS && errno != EWOULDBLOCK){ - goto done; - } - } - /* Set non-blocking */ - opt = fcntl(spW, F_GETFL, 0); - if (opt == -1) { - goto done; - } - if(fcntl(spW, F_SETFL, opt | O_NONBLOCK) == -1){ - goto done; - } - - rc = 0; /* Success */ + rc = 0; /* Success */ done: - if (rc == -1) { - errno = WSAGetLastError(); - if (spR != -1) { - close(spR); - } - if (spW != -1) { - close(spW); - } - } else { - sv[0] = spR; - sv[1] = spW; + if (rc == -1) { + if (spR != -1) { + close(spR); } + if (spW != -1) { + close(spW); + } + } else { + sv[0] = spR; + sv[1] = spW; + } - /* Both success and failure */ - if (listensock != -1) { - close(listensock); - } - return rc; + /* Both success and failure */ + if (listensock != -1) { + close(listensock); + } + return rc; } + + +int sys_socketpair_nt_dgram(int family, int type, int proto, int sv[2]) { + struct sockaddr_in sa; + uint32_t sa_len; + int opt; + int spR = -1, spW = -1; + int rc = -1; + + /* Avoid warnings for unused parameters */ + (void)family; + (void)proto; + + sv[0] = -1; /* INVALID_SOCKET */ + sv[1] = -1; /* INVALID_SOCKET */ + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sa.sin_port = 0; + sa_len = sizeof(struct sockaddr_in); + + /* Creates a read socket */ + spR = sys_socket_nt(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (spR == -1) { + errno = WSAGetLastError(); + goto done; + } + + /* ... and bind it to whatever port is available on localhost */ + if (bind(spR, (struct sockaddr *)&sa, sa_len) < 0) { + goto done; + } + + /* Read the port # that bind obtained */ + memset(&sa, 0, sizeof(sa)); + sa_len = sizeof(sa); + if (getsockname(spR, (struct sockaddr *)&sa, &sa_len) < 0) { + goto done; + } + // The port # is on: sa.sin_port + // printf("RD Bound: port=%d, addr=%08x\n", sa.sin_port, sa.sin_addr.s_addr); + + /* Creates a write socket */ + spW = sys_socket_nt(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (spW == -1) { + errno = WSAGetLastError(); + goto done; + } + /* ... and connect it to the read socket port */ + if (connect(spW, (struct sockaddr *)&sa, sa_len) < 0) { + goto done; + } + + /* So far we got connection only in one way: + * WR ----> RD + * To ensure symmetric behavior, read back the port bound of the + * write socket */ + memset(&sa, 0, sizeof(sa)); + sa_len = sizeof(sa); + if (getsockname(spW, (struct sockaddr *)&sa, &sa_len) < 0) { + goto done; + } + + // The write socket is bound at: sa.sin_port + //printf("WR Bound: port=%d, addr=%08x\n", sa.sin_port, sa.sin_addr.s_addr); + + // ... finally connect the READ socket to the WRITE socket + if (connect(spR, (struct sockaddr *)&sa, sa_len) < 0) { + goto done; + } + + /* Now we finally got both bi-directional sockets: RD <---> WR */ + + /* Finally set both sockets as non-blocking */ + opt = fcntl(spW, F_GETFL, 0); + if (opt == -1) { + goto done; + } + if(fcntl(spW, F_SETFL, opt | O_NONBLOCK) == -1) { + goto done; + } + + opt = fcntl(spR, F_GETFL, 0); + if (opt == -1) { + goto done; + } + if(fcntl(spR, F_SETFL, opt | O_NONBLOCK) == -1) { + goto done; + } + + rc = 0; + +done: + if (rc == -1) { + errno = WSAGetLastError(); + if (spR != -1) { + close(spR); + } + if (spW != -1) { + close(spW); + } + } else { + sv[0] = spR; + sv[1] = spW; + } + + return rc; +} + + + + + diff --git a/libc/sock/socketpair.c b/libc/sock/socketpair.c index 6489d7978..41d8aad62 100644 --- a/libc/sock/socketpair.c +++ b/libc/sock/socketpair.c @@ -20,6 +20,7 @@ #include "libc/sock/internal.h" #include "libc/sock/sock.h" #include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/sock.h" #include "libc/sysv/errfuns.h" /** @@ -43,11 +44,39 @@ int socketpair(int family, int type, int protocol, int sv[2]) { /* Recommend IPv6 on frontend serving infrastructure only. That's what Google Cloud does. It's more secure. It also means poll() will work on Windows, which doesn't allow mixing third layers. */ + errno = EAFNOSUPPORT; return epfnosupport(); } if (!IsWindows()) { return sys_socketpair(family, type, protocol, sv); } else { - return sys_socketpair_nt(family, type, protocol, sv); + /* Not all the version of Windows support AF_UNIX + */ + if (family == AF_UNIX) { + /* TODO: Not all the version of Windows support AF_UNIX. + * we should figure out a way to detect if AF_UNIX is + * supported. + * Note that although AF_UNIX may be supported, it only + * works with type==SOCK_STREAM and not with + * SOCK_DGRAM (as well as SOCK_SEQPACKET) + */ + return sys_socketpair_nt_stream(AF_INET, SOCK_STREAM, 0, sv); + /* + errno = EAFNOSUPPORT; + return -1; + */ + } + if (family != AF_INET) { + errno = EAFNOSUPPORT; + return -1; + } + if ((type == SOCK_STREAM) || (type == SOCK_SEQPACKET)) { + return sys_socketpair_nt_stream(family, type, protocol, sv); + } + if (type == SOCK_DGRAM) { + return sys_socketpair_nt_dgram(family, type, protocol, sv); + } + errno = EOPNOTSUPP; + return -1; } }