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.
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 | 68 ++++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 9 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index ac351a17a70..b4fceb95c05 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -980,6 +980,43 @@ NTSTATUS sock_read( HANDLE handle, int fd, HANDLE event, PIO_APC_ROUTINE apc, }
+static int isPort0( 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: + FIXME( "unknown address family %d\n", uaddr->addr.sa_family ); + return FALSE; + } +} + static NTSTATUS try_send( int fd, struct async_send_ioctl *async ) { union unix_sockaddr unix_addr; @@ -1018,17 +1055,30 @@ 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) - { - hdr.msg_name = NULL; - hdr.msg_namelen = 0; + if(isPort0(&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(int i = 0; i < hdr.msg_iovlen; i++){ + ret += hdr.msg_iov[i].iov_len; } - else if (errno != EINTR) + WARN("Attempting to send to port 0, skipping over %d 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 ); + } } }
Hi Jason, nice work! However, I'm afraid that this change is not going to be accepted without tests. Can you look through the existing tests and see if you can add some for port 0?
Etaash Mathamsetty (@etaash.mathamsetty) commented about dlls/ntdll/unix/socket.c:
}
+static int isPort0( const union unix_sockaddr *uaddr) +{
- switch (uaddr->addr.sa_family)
- {
- case AF_INET:
nitpick: I would indent this (and the rest of the stuff in the switch case)
Can you look through the existing tests and see if you can add some for port 0?
Will do, as I am not much of a C programmer, are there any tests that are reasonably close to what I would need I can refer to?