diff --git a/libc/sock/accept-nt.c b/libc/sock/accept-nt.c index b5dff070560..9aa7027607d 100644 --- a/libc/sock/accept-nt.c +++ b/libc/sock/accept-nt.c @@ -87,10 +87,6 @@ static int sys_accept_nt_start(int64_t handle, struct NtOverlapped *overlap, if (g_acceptex.lpAcceptEx(args->listensock, handle, args->buffer, 0, sizeof(args->buffer->local), sizeof(args->buffer->remote), 0, overlap)) { - // inherit properties of listening socket - unassert(!__imp_setsockopt(args->listensock, SOL_SOCKET, - kNtSoUpdateAcceptContext, &handle, - sizeof(handle))); return 0; } else { return -1; @@ -123,6 +119,13 @@ textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr, goto Finish; } + // inherit properties of listening socket + // errors ignored as if f->handle was created before forking + // this fails with WSAENOTSOCK, see + // https://github.com/jart/cosmopolitan/issues/1174 + __imp_setsockopt(resources.handle, SOL_SOCKET, kNtSoUpdateAcceptContext, + &f->handle, sizeof(f->handle)); + // create file descriptor for new socket // don't inherit the file open mode bits int oflags = 0; diff --git a/libc/sock/connect-nt.c b/libc/sock/connect-nt.c index 3065c51c4b6..ce19514522e 100644 --- a/libc/sock/connect-nt.c +++ b/libc/sock/connect-nt.c @@ -35,11 +35,14 @@ #include "libc/sock/syscall_fd.internal.h" #include "libc/sock/wsaid.internal.h" #include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/sol.h" #include "libc/sysv/errfuns.h" #ifdef __x86_64__ #include "libc/sock/yoink.inc" +__msabi extern typeof(__sys_setsockopt_nt) *const __imp_setsockopt; + struct ConnectArgs { const void *addr; uint32_t addrsize; @@ -113,6 +116,8 @@ static textwindows int sys_connect_nt_impl(struct Fd *f, const void *addr, // return ETIMEDOUT if SO_SNDTIMEO elapsed // note that Linux will return EINPROGRESS errno = etimedout(); + } else if (!rc) { + __imp_setsockopt(f->handle, SOL_SOCKET, kNtSoUpdateConnectContext, 0, 0); } return rc; } @@ -131,7 +136,11 @@ static textwindows int sys_connect_nt_impl(struct Fd *f, const void *addr, ok = WSAGetOverlappedResult(f->handle, overlap, &dwBytes, false, &dwFlags); WSACloseEvent(overlap->hEvent); free(overlap); - return ok ? 0 : __winsockerr(); + if (!ok) { + return __winsockerr(); + } + __imp_setsockopt(f->handle, SOL_SOCKET, kNtSoUpdateConnectContext, 0, 0); + return 0; } else if (WSAGetLastError() == kNtErrorIoPending) { f->connect_op = overlap; return einprogress(); diff --git a/test/libc/sock/BUILD.mk b/test/libc/sock/BUILD.mk index b4ca093a8bb..bb8159b5360 100644 --- a/test/libc/sock/BUILD.mk +++ b/test/libc/sock/BUILD.mk @@ -68,7 +68,8 @@ o/$(MODE)/test/libc/sock/shutdown_test.runs \ o/$(MODE)/test/libc/sock/setsockopt_test.runs \ o/$(MODE)/test/libc/sock/sendfile_test.runs \ o/$(MODE)/test/libc/sock/poll_test.runs \ -o/$(MODE)/test/libc/sock/pollfd_test.runs: \ +o/$(MODE)/test/libc/sock/pollfd_test.runs \ +o/$(MODE)/test/libc/sock/getpeername_test.runs: \ private .PLEDGE = stdio rpath wpath cpath fattr proc inet o/$(MODE)/test/libc/sock/sendrecvmsg_test.runs: \ diff --git a/test/libc/sock/getpeername_test.c b/test/libc/sock/getpeername_test.c new file mode 100644 index 00000000000..e46f0061299 --- /dev/null +++ b/test/libc/sock/getpeername_test.c @@ -0,0 +1,154 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2024 Gavin Arthur Hayes │ +│ │ +│ 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/sock.h" +#include "libc/sock/struct/sockaddr.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/ipproto.h" +#include "libc/sysv/consts/limits.h" +#include "libc/sysv/consts/sock.h" +#include "libc/testlib/subprocess.h" +#include "libc/testlib/testlib.h" + +TEST(getpeername, worksAfterAcceptingOnFork) { + // https://github.com/jart/cosmopolitan/issues/1174 + if (IsWindows()) { + return; + } + char buf[16] = {0}; + uint32_t addrsize = sizeof(struct sockaddr_in); + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(0x7f000001), + }; + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr))); + ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize)); + ASSERT_SYS(0, 0, listen(3, SOMAXCONN)); + SPAWN(fork); + ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize)); + struct sockaddr_storage out = {0}; + uint32_t out_size = sizeof(out); + ASSERT_SYS(0, 0, getpeername(4, (struct sockaddr *)&out, &out_size)); + EXPECT_GE(sizeof(struct sockaddr_in), out_size); + EXPECT_EQ(AF_INET, ((struct sockaddr_in *)&out)->sin_family); + EXPECT_EQ(htonl(0x7f000001), ((struct sockaddr_in *)&out)->sin_addr.s_addr); + ASSERT_SYS(0, 5, send(4, "hello", 5, 0)); + PARENT(); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr))); + ASSERT_SYS(0, 5, read(3, buf, 16)); + ASSERT_STREQ("hello", buf); + ASSERT_SYS(0, 0, close(3)); + WAIT(exit, 0); +} + +TEST(getpeername, worksAfterAcceptingOnParent) { + char buf[16] = {0}; + uint32_t addrsize = sizeof(struct sockaddr_in); + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(0x7f000001), + }; + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr))); + ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize)); + ASSERT_SYS(0, 0, listen(3, SOMAXCONN)); + SPAWN(fork); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr))); + ASSERT_SYS(0, 5, read(3, buf, 16)); + ASSERT_STREQ("hello", buf); + PARENT(); + ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize)); + struct sockaddr_storage out = {0}; + uint32_t out_size = sizeof(out); + ASSERT_SYS(0, 0, getpeername(4, (struct sockaddr *)&out, &out_size)); + EXPECT_GE(sizeof(struct sockaddr_in), out_size); + EXPECT_EQ(AF_INET, ((struct sockaddr_in *)&out)->sin_family); + EXPECT_EQ(htonl(0x7f000001), ((struct sockaddr_in *)&out)->sin_addr.s_addr); + ASSERT_SYS(0, 5, send(4, "hello", 5, 0)); + ASSERT_SYS(0, 0, close(4)); + ASSERT_SYS(0, 0, close(3)); + WAIT(exit, 0); +} + +TEST(getpeername, worksAfterConnectingOnFork) { + char buf[16] = {0}; + uint32_t addrsize = sizeof(struct sockaddr_in); + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(0x7f000001), + }; + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr))); + ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize)); + ASSERT_SYS(0, 0, listen(3, SOMAXCONN)); + ASSERT_SYS(0, 4, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + SPAWN(fork); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 0, connect(4, (struct sockaddr *)&addr, sizeof(addr))); + struct sockaddr_storage out = {0}; + uint32_t out_size = sizeof(out); + ASSERT_SYS(0, 0, getpeername(4, (struct sockaddr *)&out, &out_size)); + EXPECT_GE(sizeof(struct sockaddr_in), out_size); + EXPECT_EQ(AF_INET, ((struct sockaddr_in *)&out)->sin_family); + EXPECT_EQ(htonl(0x7f000001), ((struct sockaddr_in *)&out)->sin_addr.s_addr); + ASSERT_SYS(0, 5, send(4, "hello", 5, 0)); + PARENT(); + ASSERT_SYS(0, 0, close(4)); + ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize)); + ASSERT_SYS(0, 5, read(4, buf, 16)); + ASSERT_STREQ("hello", buf); + ASSERT_SYS(0, 0, close(4)); + ASSERT_SYS(0, 0, close(3)); + WAIT(exit, 0); +} + +TEST(getpeername, worksAfterConnectingOnParent) { + char buf[16] = {0}; + uint32_t addrsize = sizeof(struct sockaddr_in); + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(0x7f000001), + }; + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr))); + ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize)); + ASSERT_SYS(0, 0, listen(3, SOMAXCONN)); + SPAWN(fork); + ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize)); + ASSERT_SYS(0, 5, read(4, buf, 16)); + ASSERT_STREQ("hello", buf); + PARENT(); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr))); + struct sockaddr_storage out = {0}; + uint32_t out_size = sizeof(out); + ASSERT_SYS(0, 0, getpeername(3, (struct sockaddr *)&out, &out_size)); + EXPECT_GE(sizeof(struct sockaddr_in), out_size); + EXPECT_EQ(AF_INET, ((struct sockaddr_in *)&out)->sin_family); + EXPECT_EQ(htonl(0x7f000001), ((struct sockaddr_in *)&out)->sin_addr.s_addr); + ASSERT_SYS(0, 5, send(3, "hello", 5, 0)); + ASSERT_SYS(0, 0, close(3)); + WAIT(exit, 0); +} diff --git a/test/libc/sock/socket_test.c b/test/libc/sock/socket_test.c index 74983072d33..b3a5e1d3275 100644 --- a/test/libc/sock/socket_test.c +++ b/test/libc/sock/socket_test.c @@ -152,8 +152,6 @@ __attribute__((__constructor__)) static void StdioPro(int argc, char *argv[]) { } TEST(socket, canBeUsedAsExecutedStdio) { - if (IsWindows()) - return; // TODO(jart): What broke this? char buf[16] = {0}; const char *prog; uint32_t addrsize = sizeof(struct sockaddr_in);