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.
-- v4: ntdll: Do not send data to port 0.
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..84239b7e6b1 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[12] = "hello world"; + WSABUF data_buf; + DWORD bytesSent = 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, 0); + ok(s != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError()); + + ret = WSASendTo(s, &data_buf, 1, &bytesSent, 0, (struct sockaddr *)&addr, sizeof(addr), NULL, NULL); + todo_wine ok(!ret, "got error %u\n", WSAGetLastError()); + todo_wine ok(bytesSent == sizeof(buf), "Failed to send full data(%d) only sent(%ld)\n", sizeof(buf), bytesSent); + 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 | 56 ++++++++++++++++++++++++++++++++++++++-- dlls/ws2_32/tests/sock.c | 6 ++--- 2 files changed, 57 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index ac351a17a70..d3af99be693 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -979,6 +979,41 @@ 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 int 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; + } + + #ifdef HAS_IPX + case AF_IPX: + { + return uaddr->ipx.sipx_port == 0; + } + #endif + + #ifdef HAS_IRDA + case AF_IRDA: + { + return FALSE; + } + #endif + + case AF_UNSPEC: + return FALSE; + + default: + return FALSE; + } +}
static NTSTATUS try_send( int fd, struct async_send_ioctl *async ) { @@ -1018,6 +1053,7 @@ 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) { if (errno == EISCONN) @@ -1027,8 +1063,24 @@ static NTSTATUS try_send( int fd, struct async_send_ioctl *async ) } else if (errno != EINTR) { - if (errno != EWOULDBLOCK) WARN( "sendmsg: %s\n", strerror( errno ) ); - return sock_errno_to_status( errno ); + if(errno == EINVAL && is_port0(&unix_addr)){ + + /* + * 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++){ + ret += hdr.msg_iov[i].iov_len; + } + WARN("Attempting to send to port 0, skipping over %zd bytes\n", ret); + break; + }else{ + 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 84239b7e6b1..cfd8f51a310 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -13676,10 +13676,10 @@ static void test_WSASendto_port0(void)
s = socket(AF_INET, SOCK_DGRAM, 0); ok(s != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError()); - + WSASetLastError(12345); ret = WSASendTo(s, &data_buf, 1, &bytesSent, 0, (struct sockaddr *)&addr, sizeof(addr), NULL, NULL); - todo_wine ok(!ret, "got error %u\n", WSAGetLastError()); - todo_wine ok(bytesSent == sizeof(buf), "Failed to send full data(%d) only sent(%ld)\n", sizeof(buf), bytesSent); + ok(!ret, "got error %u\n", WSAGetLastError()); + ok(bytesSent == sizeof(buf), "Failed to send full data(%d) only sent(%ld)\n", sizeof(buf), bytesSent); closesocket(s); }