From ca88ce50260bc0a9645d2c49f82b2619ca74fec0 Mon Sep 17 00:00:00 2001 From: fabriziobertocci Date: Wed, 17 Mar 2021 01:05:59 -0400 Subject: [PATCH] Add socketpair (#122) --- libc/calls/internal.h | 3 + libc/calls/pipe-nt.c | 6 +- libc/sock/internal.h | 6 ++ libc/sock/sock.h | 2 +- libc/sock/socketpair-nt.c | 111 +++++++++++++++++++++++++++++++ libc/sock/socketpair.c | 54 +++++++++++++++ test/libc/sock/socketpair_test.c | 60 +++++++++++++++++ 7 files changed, 238 insertions(+), 4 deletions(-) create mode 100644 libc/sock/socketpair-nt.c create mode 100644 libc/sock/socketpair.c create mode 100644 test/libc/sock/socketpair_test.c diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 4714e12a4..4a50760fc 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -67,6 +67,7 @@ hidden extern int __vforked; hidden extern unsigned __sighandrvas[NSIG]; hidden extern struct Fds g_fds; hidden extern const struct NtSecurityAttributes kNtIsInheritable; +hidden extern const char kPipeNamePrefix[]; int __reservefd(void) hidden; void __releasefd(int) hidden; @@ -290,6 +291,8 @@ int __mkntpathat(int, const char *, int, char16_t[PATH_MAX]) hidden; unsigned __wincrash_nt(struct NtExceptionPointers *); ssize_t sys_readv_nt(struct Fd *, const struct iovec *, int) hidden; ssize_t sys_writev_nt(struct Fd *, const struct iovec *, int) hidden; +char16_t *CreatePipeName(char16_t *) hidden; +size_t UintToChar16Array(char16_t *, uint64_t) hidden; /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § syscalls » metal ─╬─│┼ diff --git a/libc/calls/pipe-nt.c b/libc/calls/pipe-nt.c index 2dede30c1..493d5f03a 100644 --- a/libc/calls/pipe-nt.c +++ b/libc/calls/pipe-nt.c @@ -27,9 +27,9 @@ #include "libc/nt/runtime.h" #include "libc/sysv/consts/o.h" -static const char kPipeNamePrefix[] = "\\\\?\\pipe\\cosmo\\"; +const char kPipeNamePrefix[] = "\\\\?\\pipe\\cosmo\\"; -static size_t UintToChar16Array(char16_t *a, uint64_t i) { +size_t UintToChar16Array(char16_t *a, uint64_t i) { size_t j = 0; do { a[j++] = i % 10 + '0'; @@ -40,7 +40,7 @@ static size_t UintToChar16Array(char16_t *a, uint64_t i) { return j; } -static char16_t *CreatePipeName(char16_t *a) { +char16_t *CreatePipeName(char16_t *a) { static long x; unsigned i; for (i = 0; kPipeNamePrefix[i]; ++i) a[i] = kPipeNamePrefix[i]; diff --git a/libc/sock/internal.h b/libc/sock/internal.h index d10ea6a8c..50b646c98 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,11 @@ 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_stream(int, int, int, int[2]) hidden; +int sys_socketpair_nt_dgram(int, int, int, int[2]) 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..c92aa85b4 --- /dev/null +++ b/libc/sock/socketpair-nt.c @@ -0,0 +1,111 @@ +/*-*- 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/sock/internal.h" +#include "libc/sock/yoink.inc" +#include "libc/sysv/consts/fio.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/sock.h" +#include "libc/sysv/consts/af.h" + +#include "libc/alg/reverse.internal.h" +#include "libc/nt/createfile.h" +#include "libc/nt/enum/accessmask.h" +#include "libc/nt/enum/creationdisposition.h" +#include "libc/nt/enum/filesharemode.h" +#include "libc/nt/ipc.h" +#include "libc/nt/process.h" +#include "libc/nt/runtime.h" + + +// {{{ sys_socketpair_nt +int sys_socketpair_nt(int family, int type, int proto, int sv[2]) { + int64_t hpipe, h1, h2; + int reader, writer; + char16_t pipename[64]; + uint32_t mode; + + // Supports only AF_UNIX + if (family != AF_UNIX) { + errno = EAFNOSUPPORT; + return -1; + } + + mode = kNtPipeWait; + if (type == SOCK_STREAM) { + mode |= kNtPipeReadmodeByte | kNtPipeTypeByte; + } else if ((type == SOCK_DGRAM) || (type == SOCK_SEQPACKET)) { + mode |= kNtPipeReadmodeMessage | kNtPipeTypeMessage; + } else { + errno = EOPNOTSUPP; + return -1; + } + + CreatePipeName(pipename); + if ((reader = __reservefd()) == -1) return -1; + if ((writer = __reservefd()) == -1) { + __releasefd(reader); + return -1; + } + if ((hpipe = CreateNamedPipe(pipename, + kNtPipeAccessDuplex, + mode, + 1, + 65536, + 65536, + 0, + &kNtIsInheritable)) == -1) { + __winerr(); + __releasefd(writer); + __releasefd(reader); + return -1; + } + + h1 = CreateFile(pipename, + kNtGenericWrite | kNtGenericRead, + 0, // Not shared + &kNtIsInheritable, + kNtOpenExisting, 0, 0); + if (h1 == -1) { + CloseHandle(hpipe); + __winerr(); + __releasefd(writer); + __releasefd(reader); + return -1; + } + + g_fds.p[reader].kind = kFdFile; + g_fds.p[reader].flags = 0; // TODO + g_fds.p[reader].handle = hpipe; + + g_fds.p[writer].kind = kFdFile; + g_fds.p[writer].flags = 0; // TODO + g_fds.p[writer].handle = h1; + + sv[0] = reader; + sv[1] = writer; + return 0; +} +// }}} + + + + diff --git a/libc/sock/socketpair.c b/libc/sock/socketpair.c new file mode 100644 index 000000000..ae37f7671 --- /dev/null +++ b/libc/sock/socketpair.c @@ -0,0 +1,54 @@ +/*-*- 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/consts/sock.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_UNIX; + } 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. */ + errno = EAFNOSUPPORT; + return epfnosupport(); + } + if (!IsWindows()) { + return sys_socketpair(family, type, protocol, sv); + } + return sys_socketpair_nt(family, type, protocol, sv); +} diff --git a/test/libc/sock/socketpair_test.c b/test/libc/sock/socketpair_test.c new file mode 100644 index 000000000..fb84d153d --- /dev/null +++ b/test/libc/sock/socketpair_test.c @@ -0,0 +1,60 @@ +/*-*- 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 2021 Alison Winters │ +│ │ +│ 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/assert.h" +#include "libc/dce.h" +#include "libc/sock/sock.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/sock.h" +#include "libc/testlib/testlib.h" + +TEST(socketpair, testAfUnixStream) { + int fd[2]; + const char ping[] = "ping"; + const char pong[] = "pong"; + char buf[32]; + + ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, fd)); + ASSERT_EQ(sizeof(ping), write(fd[0], ping, sizeof(ping))); + ASSERT_EQ(sizeof(ping), read(fd[1], buf, sizeof(ping))); + EXPECT_STREQ(ping, buf); + ASSERT_EQ(sizeof(pong), write(fd[1], pong, sizeof(pong))); + ASSERT_EQ(sizeof(pong), read(fd[0], buf, sizeof(pong))); + EXPECT_STREQ(pong, buf); + ASSERT_NE(-1, close(fd[0])); + ASSERT_NE(-1, close(fd[1])); +} + +TEST(socketpair, testAfUnixDgram) { + int fd[2]; + const char ping[] = "ping"; + const char pong[] = "pong"; + char buf[32]; + + ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_DGRAM, 0, fd)); + ASSERT_EQ(sizeof(ping), write(fd[0], ping, sizeof(ping))); + ASSERT_EQ(sizeof(ping), read(fd[1], buf, sizeof(buf))); + EXPECT_STREQ(ping, buf); + ASSERT_EQ(sizeof(pong), write(fd[1], pong, sizeof(pong))); + ASSERT_EQ(sizeof(pong), read(fd[0], buf, sizeof(buf))); + EXPECT_STREQ(pong, buf); + ASSERT_NE(-1, close(fd[0])); + ASSERT_NE(-1, close(fd[1])); +} + +