On Windows it seems sending to port 0 does nothing and does not error. Presently sendmsg errors with EINVAL. This works around it, by checking if it's port 0 then skipping the data.
-- v10: ntdll: Do not send data to port 0. ws2_32/tests: Check if sending to port 0 succeeds.
From: Jason Beetham beefers331@gmail.com
--- dlls/ws2_32/tests/sock.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index d29e1c768dc..a388a35d1e1 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -13659,6 +13659,31 @@ static void test_connect_udp(void) closesocket(client); }
+static void test_WSASendto_port0(void) +{ + SOCKET s; + struct sockaddr_in addr; + char buf[] = "hello world"; + WSABUF data_buf; + DWORD bytes_sent = 0; + int ret; + + addr.sin_family = AF_INET; + addr.sin_port = htons(0); + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + data_buf.len = sizeof(buf); + data_buf.buf = buf; + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + ok(s != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError()); + + ret = WSASendTo(s, &data_buf, 1, &bytes_sent, 0, (struct sockaddr *)&addr, sizeof(addr), NULL, NULL); + todo_wine ok(!ret, "got error %u\n", WSAGetLastError()); + todo_wine ok(bytes_sent == sizeof(buf), "Failed to send full data(%lu) only sent(%lu)\n", sizeof(buf), bytes_sent); + closesocket(s); +} + + START_TEST( sock ) { int i; @@ -13740,6 +13765,7 @@ START_TEST( sock ) test_tcp_reset(); test_icmp(); test_connect_udp(); + test_WSASendto_port0();
/* this is an io heavy test, do it at the end so the kernel doesn't start dropping packets */ test_send();
From: Jason Beetham beefers331@gmail.com
--- On Windows it seems sending to port 0 does nothing and does not error. Presently sendmsg errors with EINVAL. This works around it, by checking if it's port 0 then skipping the data. --- dlls/ntdll/unix/socket.c | 50 ++++++++++++++++++++++++++++++++++------ dlls/ws2_32/tests/sock.c | 4 ++-- 2 files changed, 45 insertions(+), 9 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index ac351a17a70..0be2c9e4dd5 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -979,12 +979,28 @@ NTSTATUS sock_read( HANDLE handle, int fd, HANDLE event, PIO_APC_ROUTINE apc, return sock_recv( handle, event, apc, apc_user, io, fd, async, 1 ); }
+static BOOL is_port0( const union unix_sockaddr *uaddr) +{ + switch (uaddr->addr.sa_family) + { + case AF_INET: + return uaddr->in.sin_port == 0; + case AF_INET6: + return uaddr->in6.sin6_port == 0; + default: + return FALSE; + } +}
static NTSTATUS try_send( int fd, struct async_send_ioctl *async ) { union unix_sockaddr unix_addr; struct msghdr hdr; ssize_t ret; + int protocol; + int opt_err; + socklen_t opt_len; +
memset( &hdr, 0, sizeof(hdr) ); if (async->addr) @@ -1018,17 +1034,37 @@ static NTSTATUS try_send( int fd, struct async_send_ioctl *async ) hdr.msg_iov = async->iov + async->iov_cursor; hdr.msg_iovlen = async->count - async->iov_cursor;
- while ((ret = sendmsg( fd, &hdr, async->unix_flags )) == -1) + opt_len = sizeof(protocol); + opt_err = getsockopt(fd, SOL_SOCKET, SO_TYPE, &protocol, &opt_len); + printf("Protocol: %d\n", protocol); + if (!opt_err && protocol == IPPROTO_UDP && is_port0(&unix_addr)) { - if (errno == EISCONN) + /* Some Windows applications(Spellforce 3 is known to) send to port 0. + * This causes 'sendmsg' to throw a EINVAL error on Windows this does nothing but consume the data. + * This workaround says we successfully sent data even though we didnt send anything. + * Matching the Windows behaviour, making the program work(in theory). + */ + ret = 0; + for(ssize_t i = 0; i < hdr.msg_iovlen; i++) { - hdr.msg_name = NULL; - hdr.msg_namelen = 0; + ret += hdr.msg_iov[i].iov_len; } - else if (errno != EINTR) + WARN("Attempting to send to port 0, skipping over %zd bytes\n", ret); + } + else + { + while ((ret = sendmsg( fd, &hdr, async->unix_flags )) == -1) { - if (errno != EWOULDBLOCK) WARN( "sendmsg: %s\n", strerror( errno ) ); - return sock_errno_to_status( errno ); + if (errno == EISCONN) + { + hdr.msg_name = NULL; + hdr.msg_namelen = 0; + } + else if (errno != EINTR) + { + if (errno != EWOULDBLOCK) WARN( "sendmsg: %s\n", strerror( errno ) ); + return sock_errno_to_status( errno ); + } } }
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index a388a35d1e1..ca0527ae438 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -13678,8 +13678,8 @@ static void test_WSASendto_port0(void) ok(s != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError());
ret = WSASendTo(s, &data_buf, 1, &bytes_sent, 0, (struct sockaddr *)&addr, sizeof(addr), NULL, NULL); - todo_wine ok(!ret, "got error %u\n", WSAGetLastError()); - todo_wine ok(bytes_sent == sizeof(buf), "Failed to send full data(%lu) only sent(%lu)\n", sizeof(buf), bytes_sent); + ok(!ret, "got error %u\n", WSAGetLastError()); + ok(bytes_sent == sizeof(buf), "Failed to send full data(%lu) only sent(%lu)\n", (unsigned long) sizeof(buf), (unsigned long) bytes_sent); closesocket(s); }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=129227
Your paranoid android.
=== debian11 (32 bit report) ===
ws2_32: sock.c:13681: Test failed: got error 10022 sock.c:13682: Test failed: Failed to send full data(12) only sent(0)
Report validation errors: winhttp:winhttp prints too much data (249835 bytes) ws2_32:sock prints too much data (124042 bytes)
=== debian11 (32 bit ar:MA report) ===
ws2_32: sock.c:13681: Test failed: got error 10022 sock.c:13682: Test failed: Failed to send full data(12) only sent(0)
Report validation errors: ws2_32:sock prints too much data (123991 bytes)
=== debian11 (32 bit de report) ===
ws2_32: sock.c:13681: Test failed: got error 10022 sock.c:13682: Test failed: Failed to send full data(12) only sent(0)
Report validation errors: ws2_32:sock prints too much data (124039 bytes)
=== debian11 (32 bit fr report) ===
ws2_32: sock.c:13681: Test failed: got error 10022 sock.c:13682: Test failed: Failed to send full data(12) only sent(0)
Report validation errors: ws2_32:sock prints too much data (124039 bytes)
=== debian11 (32 bit he:IL report) ===
ws2_32: sock.c:13681: Test failed: got error 10022 sock.c:13682: Test failed: Failed to send full data(12) only sent(0)
Report validation errors: ws2_32:sock prints too much data (123991 bytes)
=== debian11 (32 bit hi:IN report) ===
ws2_32: sock.c:13681: Test failed: got error 10022 sock.c:13682: Test failed: Failed to send full data(12) only sent(0)
Report validation errors: ws2_32:sock prints too much data (123991 bytes)
=== debian11 (32 bit ja:JP report) ===
ws2_32: sock.c:13681: Test failed: got error 10022 sock.c:13682: Test failed: Failed to send full data(12) only sent(0)
Report validation errors: ws2_32:sock prints too much data (123991 bytes)
=== debian11 (32 bit zh:CN report) ===
ws2_32: sock.c:13681: Test failed: got error 10022 sock.c:13682: Test failed: Failed to send full data(12) only sent(0)
Report validation errors: ws2_32:sock prints too much data (123991 bytes)
=== debian11b (32 bit WoW report) ===
ws2_32: sock.c:13681: Test failed: got error 10022 sock.c:13682: Test failed: Failed to send full data(12) only sent(0)
Report validation errors: ws2_32:sock prints too much data (123991 bytes)
=== debian11b (64 bit WoW report) ===
ws2_32: sock.c:13681: Test failed: got error 10022 sock.c:13682: Test failed: Failed to send full data(12) only sent(0)
Report validation errors: ws2_32:sock prints too much data (124031 bytes)
Any insight into the best way to get the protocol inside `try_send`, presently I'm using `getSockOpt` as follows: ```c opt_err = getsockopt(fd, SOL_SOCKET, SO_TYPE, &protocol, &opt_len); if (!opt_err && protocol == SOCK_DGRAM && is_port0(&unix_addr)) ``` This causes other tests to completely freeze up.