diff --git a/libc/sock/internal.h b/libc/sock/internal.h index d10ea6a8c..a52f4717b 100644 --- a/libc/sock/internal.h +++ b/libc/sock/internal.h @@ -82,6 +82,7 @@ int32_t sys_getpeername(int32_t, void *, uint32_t *) hidden; int32_t sys_poll(struct pollfd *, uint64_t, signed) hidden; int32_t sys_shutdown(int32_t, int32_t) hidden; int32_t sys_socket(int32_t, int32_t, int32_t) hidden; +int32_t sys_socketpair(int32_t, int32_t, int32_t, int32_t[2]) hidden; int64_t sys_readv(int32_t, const struct iovec *, int32_t) hidden; int64_t sys_writev(int32_t, const struct iovec *, int32_t) hidden; ssize_t sys_recvfrom(int, void *, size_t, int, void *, uint32_t *) hidden; @@ -104,6 +105,7 @@ 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_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/sock.h b/libc/sock/sock.h index ec1f013be..b40bc2955 100644 --- a/libc/sock/sock.h +++ b/libc/sock/sock.h @@ -91,7 +91,7 @@ ssize_t writev(int, const struct iovec *, int); ssize_t sendfile(int, int, int64_t *, size_t); int getsockopt(int, int, int, void *, uint32_t *) paramsnonnull((5)); int setsockopt(int, int, int, const void *, uint32_t); -int socketpair(int, int, int, int64_t[2]) paramsnonnull(); +int socketpair(int, int, int, int[2]) paramsnonnull(); int poll(struct pollfd *, uint64_t, int32_t) paramsnonnull(); int ppoll(struct pollfd *, uint64_t, const struct timespec *, const struct sigset *) paramsnonnull((1, 4)); diff --git a/libc/sock/socketpair-nt.c b/libc/sock/socketpair-nt.c new file mode 100644 index 000000000..aecd460c0 --- /dev/null +++ b/libc/sock/socketpair-nt.c @@ -0,0 +1,136 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/errno.h" +#include "libc/mem/mem.h" +#include "libc/nt/winsock.h" +#include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" +#include "libc/sock/sock.h" +#include "libc/sysv/consts/fio.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/f.h" +#include "libc/sysv/consts/sock.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/ipproto.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; + + int listensock = -1; + sv[0] = -1; /* INVALID_SOCKET */ + sv[1] = -1; /* INVALID_SOCKET */ + + 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; + } + + listensock = sys_socket_nt(family, SOCK_STREAM, IPPROTO_TCP); + if (listensock == -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 */ + +done: + if (rc == -1) { + errno = WSAGetLastError(); + 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; +} diff --git a/libc/sock/socketpair.c b/libc/sock/socketpair.c new file mode 100644 index 000000000..6489d7978 --- /dev/null +++ b/libc/sock/socketpair.c @@ -0,0 +1,53 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/sock/internal.h" +#include "libc/sock/sock.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/errfuns.h" + +/** + * Creates a pair of connected sockets + * + * @param family can be AF_UNIX, AF_INET, etc. + * @param type can be SOCK_STREAM (for TCP), SOCK_DGRAM (e.g. UDP), or + * SOCK_RAW (IP) so long as IP_HDRINCL was passed to setsockopt(); + * and additionally, may be or'd with SOCK_NONBLOCK, SOCK_CLOEXEC + * @param protocol can be IPPROTO_TCP, IPPROTO_UDP, or IPPROTO_ICMP + * @param sv a vector of 2 integers to store the created sockets. + * @return 0 if success, -1 in case of error + * @error EFAULT, EPFNOSUPPORT, etc. + * @see libc/sysv/consts.sh + * @asyncsignalsafe + */ +int socketpair(int family, int type, int protocol, int sv[2]) { + if (family == AF_UNSPEC) { + family = AF_INET; + } else if (family == AF_INET6) { + /* 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. */ + return epfnosupport(); + } + if (!IsWindows()) { + return sys_socketpair(family, type, protocol, sv); + } else { + return sys_socketpair_nt(family, type, protocol, sv); + } +}