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.

This commit is contained in:
Fabrizio Bertocci 2021-03-13 14:11:58 -05:00
parent edc5aa3234
commit 7ae7010aa7
3 changed files with 242 additions and 95 deletions

View file

@ -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_accept_nt(struct Fd *, void *, uint32_t *, int) hidden;
int sys_closesocket_nt(struct Fd *) hidden; int sys_closesocket_nt(struct Fd *) hidden;
int sys_socket_nt(int, int, int) 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_select_nt(int, fd_set *, fd_set *, fd_set *, struct timeval *) hidden;
int sys_shutdown_nt(struct Fd *, int) hidden; int sys_shutdown_nt(struct Fd *, int) hidden;

View file

@ -29,31 +29,36 @@
#include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/sock.h"
#include "libc/sysv/consts/af.h" #include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ipproto.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]) { /* 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_storage ss;
struct sockaddr_in *sa = (struct sockaddr_in *)&ss; struct sockaddr_in *sa = (struct sockaddr_in *)&ss;
uint32_t ss_len; uint32_t ss_len;
int spR = -1, spW = -1; int spR = -1, spW = -1;
int opt; int opt;
int rc = -1; int rc = -1;
int listensock = -1; int listensock = -1;
/* Avoid warnings for unused parameters */
(void)family;
(void)proto;
sv[0] = -1; /* INVALID_SOCKET */ sv[0] = -1; /* INVALID_SOCKET */
sv[1] = -1; /* INVALID_SOCKET */ sv[1] = -1; /* INVALID_SOCKET */
memset(&ss, 0, sizeof(ss)); memset(&ss, 0, sizeof(ss));
if (family == AF_INET){ sa->sin_family = AF_INET;
sa->sin_family = family; sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sa->sin_addr.s_addr = htonl(0x7f000001);
sa->sin_port = 0; sa->sin_port = 0;
ss_len = sizeof(struct sockaddr_in); ss_len = sizeof(struct sockaddr_in);
} else {
return -1;
}
listensock = sys_socket_nt(family, SOCK_STREAM, IPPROTO_TCP); listensock = sys_socket_nt(AF_INET, type, IPPROTO_TCP);
if (listensock == -1) { if (listensock == -1) {
errno = WSAGetLastError();
goto done; goto done;
} }
@ -71,11 +76,11 @@ textwindows int sys_socketpair_nt(int family, int type, int protocol, int sv[2])
goto done; goto done;
} }
sa->sin_family = family; sa->sin_family = AF_INET;
sa->sin_addr.s_addr = htonl(0x7f000001); sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
ss_len = sizeof(struct sockaddr_in); ss_len = sizeof(struct sockaddr_in);
spR = socket(family, SOCK_STREAM, IPPROTO_TCP); spR = socket(AF_INET, type, IPPROTO_TCP);
if (spR == -1) { if (spR == -1) {
goto done; goto done;
} }
@ -116,7 +121,6 @@ textwindows int sys_socketpair_nt(int family, int type, int protocol, int sv[2])
done: done:
if (rc == -1) { if (rc == -1) {
errno = WSAGetLastError();
if (spR != -1) { if (spR != -1) {
close(spR); close(spR);
} }
@ -134,3 +138,116 @@ done:
} }
return rc; 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;
}

View file

@ -20,6 +20,7 @@
#include "libc/sock/internal.h" #include "libc/sock/internal.h"
#include "libc/sock/sock.h" #include "libc/sock/sock.h"
#include "libc/sysv/consts/af.h" #include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/sock.h"
#include "libc/sysv/errfuns.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 /* Recommend IPv6 on frontend serving infrastructure only. That's
what Google Cloud does. It's more secure. It also means poll() what Google Cloud does. It's more secure. It also means poll()
will work on Windows, which doesn't allow mixing third layers. */ will work on Windows, which doesn't allow mixing third layers. */
errno = EAFNOSUPPORT;
return epfnosupport(); return epfnosupport();
} }
if (!IsWindows()) { if (!IsWindows()) {
return sys_socketpair(family, type, protocol, sv); return sys_socketpair(family, type, protocol, sv);
} else { } 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;
} }
} }