Skip to content

Commit f3ce684

Browse files
committed
Fix getpeername() bug on Windows
The WIN32 getpeername() function returns ENOTCONN when it uses connect() the SOCK_NONBLOCK way. So we simply store the address, provided earlier.
1 parent 908b7a8 commit f3ce684

File tree

4 files changed

+71
-3
lines changed

4 files changed

+71
-3
lines changed

libc/intrin/fds.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_
22
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_
3+
#include "libc/sock/struct/sockaddr.h"
34
#include "libc/thread/thread.h"
45
COSMOPOLITAN_C_START_
56

@@ -37,6 +38,7 @@ struct Fd {
3738
unsigned sndtimeo; /* millis; 0 means wait forever */
3839
void *connect_op;
3940
struct Cursor *cursor;
41+
struct sockaddr_storage peer;
4042
};
4143

4244
struct Fds {

libc/sock/connect-nt.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/assert.h"
2020
#include "libc/atomic.h"
21-
#include "libc/intrin/fds.h"
2221
#include "libc/calls/struct/sigset.internal.h"
2322
#include "libc/cosmo.h"
2423
#include "libc/errno.h"
24+
#include "libc/intrin/fds.h"
25+
#include "libc/macros.h"
2526
#include "libc/mem/mem.h"
2627
#include "libc/nt/enum/wsaid.h"
2728
#include "libc/nt/errors.h"
@@ -34,6 +35,7 @@
3435
#include "libc/sock/struct/sockaddr.h"
3536
#include "libc/sock/syscall_fd.internal.h"
3637
#include "libc/sock/wsaid.internal.h"
38+
#include "libc/sysv/consts/af.h"
3739
#include "libc/sysv/consts/o.h"
3840
#include "libc/sysv/consts/sol.h"
3941
#include "libc/sysv/errfuns.h"
@@ -109,6 +111,7 @@ static textwindows int sys_connect_nt_impl(struct Fd *f, const void *addr,
109111

110112
// perform normal connect
111113
if (!(f->flags & O_NONBLOCK)) {
114+
f->peer.ss_family = AF_UNSPEC;
112115
ssize_t rc = __winsock_block(f->handle, 0, false, f->sndtimeo, mask,
113116
sys_connect_nt_start,
114117
&(struct ConnectArgs){addr, addrsize});
@@ -122,6 +125,10 @@ static textwindows int sys_connect_nt_impl(struct Fd *f, const void *addr,
122125
return rc;
123126
}
124127

128+
// win32 getpeername() stops working in non-blocking connect mode
129+
if (addrsize)
130+
memcpy(&f->peer, addr, MIN(addrsize, sizeof(struct sockaddr_storage)));
131+
125132
// perform nonblocking connect(), i.e.
126133
// 1. connect(O_NONBLOCK) → EINPROGRESS
127134
// 2. poll(POLLOUT)

libc/sock/getsockname.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/internal.h"
20-
#include "libc/intrin/fds.h"
2120
#include "libc/dce.h"
21+
#include "libc/intrin/fds.h"
2222
#include "libc/intrin/strace.h"
2323
#include "libc/nt/errors.h"
2424
#include "libc/nt/thunk/msabi.h"
@@ -28,6 +28,7 @@
2828
#include "libc/sock/struct/sockaddr.h"
2929
#include "libc/sock/struct/sockaddr.internal.h"
3030
#include "libc/sock/syscall_fd.internal.h"
31+
#include "libc/sysv/consts/af.h"
3132
#include "libc/sysv/errfuns.h"
3233

3334
__msabi extern typeof(__sys_getsockname_nt) *const __imp_getsockname;
@@ -45,7 +46,12 @@ static int __getsockpeername(int fd, struct sockaddr *out_addr,
4546
if (IsWindows()) {
4647
if (__isfdkind(fd, kFdSocket)) {
4748
if ((rc = impl_win32(g_fds.p[fd].handle, &ss, &size))) {
48-
if (impl_win32 == __imp_getsockname && WSAGetLastError() == WSAEINVAL) {
49+
if (impl_win32 == __imp_getpeername &&
50+
g_fds.p[fd].peer.ss_family != AF_UNSPEC) {
51+
ss = g_fds.p[fd].peer;
52+
rc = 0;
53+
} else if (impl_win32 == __imp_getsockname &&
54+
WSAGetLastError() == WSAEINVAL) {
4955
// The socket has not been bound to an address with bind, or
5056
// ADDR_ANY is specified in bind but connection has not yet
5157
// occurred. -MSDN

test/libc/sock/connect_test.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,55 @@
3434
#include "libc/testlib/testlib.h"
3535
#include "libc/thread/thread.h"
3636

37+
TEST(connect, blocking) {
38+
char buf[16] = {0};
39+
atomic_uint *sem = _mapshared(sizeof(unsigned));
40+
uint32_t addrsize = sizeof(struct sockaddr_in);
41+
struct sockaddr_in addr = {
42+
.sin_family = AF_INET,
43+
.sin_addr.s_addr = htonl(0x7f000001),
44+
};
45+
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
46+
ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr)));
47+
ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize));
48+
ASSERT_SYS(0, 0, listen(3, SOMAXCONN));
49+
SPAWN(fork);
50+
while (!*sem)
51+
pthread_yield();
52+
ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize));
53+
ASSERT_SYS(0, 2, read(4, buf, 16)); // hi
54+
ASSERT_SYS(0, 5, write(4, "hello", 5));
55+
ASSERT_SYS(0, 3, read(4, buf, 16)); // bye
56+
PARENT();
57+
ASSERT_SYS(0, 0, close(3));
58+
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
59+
ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr)));
60+
*sem = 1;
61+
{ // wait until connected
62+
struct pollfd pfd = {3, POLLOUT};
63+
ASSERT_SYS(0, 1, poll(&pfd, 1, -1));
64+
ASSERT_TRUE(!!(POLLOUT & pfd.revents));
65+
}
66+
struct sockaddr_in peer;
67+
uint32_t sz = sizeof(peer);
68+
ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&peer, &sz));
69+
ASSERT_EQ(htonl(0x7f000001), peer.sin_addr.s_addr);
70+
ASSERT_SYS(0, 0, getpeername(3, (struct sockaddr *)&peer, &sz));
71+
ASSERT_EQ(htonl(0x7f000001), peer.sin_addr.s_addr);
72+
ASSERT_SYS(0, 2, write(3, "hi", 2));
73+
{ // wait for other process to send us stuff
74+
struct pollfd pfd = {3, POLLIN};
75+
ASSERT_SYS(0, 1, poll(&pfd, 1, -1));
76+
ASSERT_TRUE(!!(POLLIN & pfd.revents));
77+
}
78+
ASSERT_SYS(0, 5, read(3, buf, 16));
79+
ASSERT_STREQ("hello", buf);
80+
ASSERT_SYS(0, 3, write(3, "bye", 3));
81+
ASSERT_SYS(0, 0, close(3));
82+
WAIT(exit, 0);
83+
munmap(sem, sizeof(unsigned));
84+
}
85+
3786
TEST(connect, nonblocking) {
3887
if (IsFreebsd())
3988
return; // TODO(jart): why did this start flaking?
@@ -74,6 +123,10 @@ TEST(connect, nonblocking) {
74123
ASSERT_SYS(0, 1, poll(&pfd, 1, -1));
75124
ASSERT_TRUE(!!(POLLOUT & pfd.revents));
76125
}
126+
struct sockaddr_in peer;
127+
uint32_t sz = sizeof(peer);
128+
ASSERT_SYS(0, 0, getpeername(3, (struct sockaddr *)&peer, &sz));
129+
ASSERT_EQ(htonl(0x7f000001), peer.sin_addr.s_addr);
77130
ASSERT_SYS(0, 2, write(3, "hi", 2));
78131
{ // wait for other process to send us stuff
79132
struct pollfd pfd = {3, POLLIN};

0 commit comments

Comments
 (0)