Signed-off-by: Zebediah Figura z.figura12@gmail.com --- server/sock.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/server/sock.c b/server/sock.c index 3925018c7a5..6e9ef3d9d97 100644 --- a/server/sock.c +++ b/server/sock.c @@ -416,7 +416,6 @@ static int sock_reselect( struct sock *sock ) if (!(sock->state & ~FD_WINE_NONBLOCKING)) return 0; /* ok, it is, attach it to the wineserver's main poll loop */ sock->polling = 1; - allow_fd_caching( sock->fd ); } /* update condition mask */ set_fd_events( sock->fd, ev ); @@ -1358,7 +1357,11 @@ static int init_socket( struct sock *sock, int family, int type, int protocol, u { return -1; } - sock_reselect( sock ); + + /* We can't immediately allow caching for a connection-mode socket, since it + * might be accepted into (changing the underlying fd object.) */ + if (sock->type != WS_SOCK_STREAM) allow_fd_caching( sock->fd ); + return 0; }
@@ -1750,6 +1753,9 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) sock->state |= FD_WINE_LISTENING; sock->state &= ~(FD_CONNECT | FD_WINE_CONNECTED);
+ /* a listening socket can no longer be accepted into */ + allow_fd_caching( sock->fd ); + /* we may already be selecting for FD_ACCEPT */ sock_reselect( sock ); return 0; @@ -1790,6 +1796,9 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) return 0; }
+ /* a connected or connecting socket can no longer be accepted into */ + allow_fd_caching( sock->fd ); + sock->pending_events &= ~(FD_CONNECT | FD_READ | FD_WRITE); sock->reported_events &= ~(FD_CONNECT | FD_READ | FD_WRITE);
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- server/sock.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/server/sock.c b/server/sock.c index 6e9ef3d9d97..549cde17296 100644 --- a/server/sock.c +++ b/server/sock.c @@ -151,7 +151,6 @@ struct sock * any event once until it is reset.) */ unsigned int reported_events; unsigned int flags; /* socket flags */ - int polling; /* is socket being polled? */ int wr_shutdown_pending; /* is a write shutdown pending? */ unsigned short proto; /* socket protocol */ unsigned short type; /* socket type */ @@ -410,14 +409,6 @@ static int sock_reselect( struct sock *sock ) if (debug_level) fprintf(stderr,"sock_reselect(%p): new mask %x\n", sock, ev);
- if (!sock->polling) /* FIXME: should find a better way to do this */ - { - /* previously unconnected socket, is this reselect supposed to connect it? */ - if (!(sock->state & ~FD_WINE_NONBLOCKING)) return 0; - /* ok, it is, attach it to the wineserver's main poll loop */ - sock->polling = 1; - } - /* update condition mask */ set_fd_events( sock->fd, ev ); return ev; } @@ -986,6 +977,15 @@ static int sock_get_poll_events( struct fd *fd )
assert( sock->obj.ops == &sock_ops );
+ if (!sock->type) /* not initialized yet */ + return -1; + + /* A connection-mode Windows socket which has never been connected does not + * return any events, but Linux returns POLLOUT | POLLHUP. Hence we need to + * return -1 here, to prevent the socket from being polled on at all. */ + if (sock->type == WS_SOCK_STREAM && !(sock->state & (FD_CONNECT | FD_WINE_CONNECTED | FD_WINE_LISTENING))) + return -1; + if (sock->state & FD_CONNECT) /* connecting, wait for writable */ return POLLOUT; @@ -1169,7 +1169,6 @@ static struct sock *create_socket(void) sock->mask = 0; sock->pending_events = 0; sock->reported_events = 0; - sock->polling = 0; sock->wr_shutdown_pending = 0; sock->flags = 0; sock->proto = 0; @@ -1464,7 +1463,6 @@ static int accept_into_socket( struct sock *sock, struct sock *acceptsock ) acceptsock->state |= FD_WINE_CONNECTED|FD_READ|FD_WRITE; acceptsock->pending_events = 0; acceptsock->reported_events = 0; - acceptsock->polling = 0; acceptsock->proto = sock->proto; acceptsock->type = sock->type; acceptsock->family = sock->family;
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/unix/socket.c | 1 + server/protocol.def | 1 + server/sock.c | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 0812ec4970f..eb278096b43 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -223,6 +223,7 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi req->status = status; req->total = information; req->async = server_async( handle, &async->io, event, apc, apc_user, io ); + req->oob = !!(unix_flags & MSG_OOB); status = wine_server_call( req ); wait_handle = wine_server_ptr_handle( reply->wait ); options = reply->options; diff --git a/server/protocol.def b/server/protocol.def index f0a9107cfbe..88e5ad6a96b 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1468,6 +1468,7 @@ enum server_fd_type
/* Perform a recv on a socket */ @REQ(recv_socket) + int oob; /* are we receiving OOB data? */ async_data_t async; /* async I/O parameters */ unsigned int status; /* status of initial call */ unsigned int total; /* number of bytes already read */ diff --git a/server/sock.c b/server/sock.c index 549cde17296..04e27328d80 100644 --- a/server/sock.c +++ b/server/sock.c @@ -2452,8 +2452,8 @@ DECL_HANDLER(recv_socket) /* are we shut down? */ if (status == STATUS_PENDING && !(sock->state & FD_READ)) status = STATUS_PIPE_DISCONNECTED;
- sock->pending_events &= ~FD_READ; - sock->reported_events &= ~FD_READ; + sock->pending_events &= ~(req->oob ? FD_OOB : FD_READ); + sock->reported_events &= ~(req->oob ? FD_OOB : FD_READ);
if ((async = create_request_async( fd, get_fd_comp_flags( fd ), &req->async ))) {
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/unix/file.c | 1 + dlls/ntdll/unix/socket.c | 320 +++++++++++++++++++++++++++++++++++++-- include/wine/afd.h | 18 +++ 3 files changed, 328 insertions(+), 11 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index d974968d0ca..a2d0a4b1483 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -5730,6 +5730,7 @@ NTSTATUS WINAPI NtDeviceIoControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUT switch (device) { case FILE_DEVICE_BEEP: + case FILE_DEVICE_NETWORK: status = sock_ioctl( handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size ); break; case FILE_DEVICE_DISK: diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index eb278096b43..aa56e529f0d 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -28,6 +28,32 @@ #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> #endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#ifdef HAVE_NETIPX_IPX_H +# include <netipx/ipx.h> +#elif defined(HAVE_LINUX_IPX_H) +# ifdef HAVE_ASM_TYPES_H +# include <asm/types.h> +# endif +# ifdef HAVE_LINUX_TYPES_H +# include <linux/types.h> +# endif +# include <linux/ipx.h> +#endif +#if defined(SOL_IPX) || defined(SO_DEFAULT_HEADERS) +# define HAS_IPX +#endif + +#ifdef HAVE_LINUX_IRDA_H +# ifdef HAVE_LINUX_TYPES_H +# include <linux/types.h> +# endif +# include <linux/irda.h> +# define HAS_IRDA +#endif
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -35,6 +61,10 @@ #include "winioctl.h" #define USE_WS_PREFIX #include "winsock2.h" +#include "mswsock.h" +#include "ws2tcpip.h" +#include "wsipx.h" +#include "af_irda.h" #include "wine/afd.h"
#include "unix_private.h" @@ -62,6 +92,10 @@ static NTSTATUS wait_async( HANDLE handle, BOOL alertable ) struct async_recv_ioctl { struct async_fileio io; + WSABUF *control; + struct WS_sockaddr *addr; + int *addr_len; + DWORD *ret_flags; int unix_flags; unsigned int count; struct iovec iov[1]; @@ -115,15 +149,200 @@ static NTSTATUS sock_errno_to_status( int err )
extern ssize_t CDECL __wine_locked_recvmsg( int fd, struct msghdr *hdr, int flags );
+union unix_sockaddr +{ + struct sockaddr addr; + struct sockaddr_in in; + struct sockaddr_in6 in6; +#ifdef HAS_IPX + struct sockaddr_ipx ipx; +#endif +#ifdef HAS_IRDA + struct sockaddr_irda irda; +#endif +}; + +static int sockaddr_from_unix( const union unix_sockaddr *uaddr, struct WS_sockaddr *wsaddr, socklen_t wsaddrlen ) +{ + memset( wsaddr, 0, wsaddrlen ); + + switch (uaddr->addr.sa_family) + { + case AF_INET: + { + struct WS_sockaddr_in win = {0}; + + if (wsaddrlen < sizeof(win)) return -1; + win.sin_family = WS_AF_INET; + win.sin_port = uaddr->in.sin_port; + memcpy( &win.sin_addr, &uaddr->in.sin_addr, sizeof(win.sin_addr) ); + memcpy( wsaddr, &win, sizeof(win) ); + return sizeof(win); + } + + case AF_INET6: + { + struct WS_sockaddr_in6 win = {0}; + + if (wsaddrlen < sizeof(struct WS_sockaddr_in6_old)) return -1; + win.sin6_family = WS_AF_INET6; + win.sin6_port = uaddr->in6.sin6_port; + win.sin6_flowinfo = uaddr->in6.sin6_flowinfo; + memcpy( &win.sin6_addr, &uaddr->in6.sin6_addr, sizeof(win.sin6_addr) ); +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID + win.sin6_scope_id = uaddr->in6.sin6_scope_id; +#endif + if (wsaddrlen >= sizeof(struct WS_sockaddr_in6)) + { + memcpy( wsaddr, &win, sizeof(struct WS_sockaddr_in6) ); + return sizeof(struct WS_sockaddr_in6); + } + memcpy( wsaddr, &win, sizeof(struct WS_sockaddr_in6_old) ); + return sizeof(struct WS_sockaddr_in6_old); + } + +#ifdef HAS_IPX + case AF_IPX: + { + struct WS_sockaddr_ipx win = {0}; + + if (wsaddrlen < sizeof(win)) return -1; + win.sa_family = WS_AF_IPX; + memcpy( win.sa_netnum, &uaddr->ipx.sipx_network, sizeof(win.sa_netnum) ); + memcpy( win.sa_nodenum, &uaddr->ipx.sipx_node, sizeof(win.sa_nodenum) ); + win.sa_socket = uaddr->ipx.sipx_port; + memcpy( wsaddr, &win, sizeof(win) ); + return sizeof(win); + } +#endif + +#ifdef HAS_IRDA + case AF_IRDA: + { + SOCKADDR_IRDA win; + + if (wsaddrlen < sizeof(win)) return -1; + win.irdaAddressFamily = WS_AF_IRDA; + memcpy( win.irdaDeviceID, &uaddr->irda.sir_addr, sizeof(win.irdaDeviceID) ); + if (uaddr->irda.sir_lsap_sel != LSAP_ANY) + snprintf( win.irdaServiceName, sizeof(win.irdaServiceName), "LSAP-SEL%u", uaddr->irda.sir_lsap_sel ); + else + memcpy( win.irdaServiceName, uaddr->irda.sir_name, sizeof(win.irdaServiceName) ); + memcpy( wsaddr, &win, sizeof(win) ); + return sizeof(win); + } +#endif + + case AF_UNSPEC: + return 0; + + default: + FIXME( "unknown address family %d\n", uaddr->addr.sa_family ); + return -1; + } +} + +#ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS +#if defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) +static WSACMSGHDR *fill_control_message( int level, int type, WSACMSGHDR *current, ULONG *maxsize, void *data, int len ) +{ + ULONG msgsize = sizeof(WSACMSGHDR) + WSA_CMSG_ALIGN(len); + char *ptr = (char *) current + sizeof(WSACMSGHDR); + + if (msgsize > *maxsize) + return NULL; + *maxsize -= msgsize; + current->cmsg_len = sizeof(WSACMSGHDR) + len; + current->cmsg_level = level; + current->cmsg_type = type; + memcpy(ptr, data, len); + return (WSACMSGHDR *)(ptr + WSA_CMSG_ALIGN(len)); +} +#endif /* defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) */ + +static int convert_control_headers(struct msghdr *hdr, WSABUF *control) +{ +#if defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) + WSACMSGHDR *cmsg_win = (WSACMSGHDR *)control->buf, *ptr; + ULONG ctlsize = control->len; + struct cmsghdr *cmsg_unix; + + ptr = cmsg_win; + for (cmsg_unix = CMSG_FIRSTHDR(hdr); cmsg_unix != NULL; cmsg_unix = CMSG_NXTHDR(hdr, cmsg_unix)) + { + switch (cmsg_unix->cmsg_level) + { + case IPPROTO_IP: + switch (cmsg_unix->cmsg_type) + { +#if defined(IP_PKTINFO) + case IP_PKTINFO: + { + const struct in_pktinfo *data_unix = (struct in_pktinfo *)CMSG_DATA(cmsg_unix); + struct WS_in_pktinfo data_win; + + memcpy( &data_win.ipi_addr, &data_unix->ipi_addr.s_addr, 4 ); /* 4 bytes = 32 address bits */ + data_win.ipi_ifindex = data_unix->ipi_ifindex; + ptr = fill_control_message( WS_IPPROTO_IP, WS_IP_PKTINFO, ptr, &ctlsize, + (void *)&data_win, sizeof(data_win) ); + if (!ptr) goto error; + break; + } +#elif defined(IP_RECVDSTADDR) + case IP_RECVDSTADDR: + { + const struct in_addr *addr_unix = (struct in_addr *)CMSG_DATA(cmsg_unix); + struct WS_in_pktinfo data_win; + + memcpy( &data_win.ipi_addr, &addr_unix->s_addr, 4 ); /* 4 bytes = 32 address bits */ + data_win.ipi_ifindex = 0; /* FIXME */ + ptr = fill_control_message( WS_IPPROTO_IP, WS_IP_PKTINFO, ptr, &ctlsize, + (void *)&data_win, sizeof(data_win) ); + if (!ptr) goto error; + break; + } +#endif /* IP_PKTINFO */ + default: + FIXME("Unhandled IPPROTO_IP message header type %d\n", cmsg_unix->cmsg_type); + break; + } + break; + + default: + FIXME("Unhandled message header level %d\n", cmsg_unix->cmsg_level); + break; + } + } + + control->len = (char *)ptr - (char *)cmsg_win; + return 1; + +error: + control->len = 0; + return 0; +#else /* defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) */ + control->len = 0; + return 1; +#endif /* defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) */ +} +#endif /* HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */ + static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *size ) { #ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS char control_buffer[512]; #endif + union unix_sockaddr unix_addr; struct msghdr hdr; + NTSTATUS status; ssize_t ret;
memset( &hdr, 0, sizeof(hdr) ); + if (async->addr) + { + hdr.msg_name = &unix_addr.addr; + hdr.msg_namelen = sizeof(unix_addr); + } hdr.msg_iov = async->iov; hdr.msg_iovlen = async->count; #ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS @@ -143,8 +362,34 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz return sock_errno_to_status( errno ); }
+ status = (hdr.msg_flags & MSG_TRUNC) ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS; + +#ifdef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS + if (async->control) + { + ERR( "Message control headers cannot be properly supported on this system.\n" ); + async->control->len = 0; + } +#else + if (async->control && !convert_control_headers( &hdr, async->control )) + { + WARN( "Application passed insufficient room for control headers.\n" ); + *async->ret_flags |= WS_MSG_CTRUNC; + status = STATUS_BUFFER_OVERFLOW; + } +#endif + + /* If this socket is connected, Linux doesn't give us msg_name and + * msg_namelen from recvmsg, but it does set msg_namelen to zero. + * + * MSDN says that the address is ignored for connection-oriented sockets, so + * don't try to translate it. + */ + if (async->addr && hdr.msg_namelen) + *async->addr_len = sockaddr_from_unix( &unix_addr, async->addr, *async->addr_len ); + *size = ret; - return (hdr.msg_flags & MSG_TRUNC) ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS; + return status; }
static NTSTATUS async_recv_proc( void *user, IO_STATUS_BLOCK *io, NTSTATUS status ) @@ -178,7 +423,8 @@ static NTSTATUS async_recv_proc( void *user, IO_STATUS_BLOCK *io, NTSTATUS statu }
static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io, - int fd, const WSABUF *buffers, unsigned int count, int unix_flags, int force_async ) + int fd, const WSABUF *buffers, unsigned int count, WSABUF *control, + struct WS_sockaddr *addr, int *addr_len, DWORD *ret_flags, int unix_flags, int force_async ) { struct async_recv_ioctl *async; ULONG_PTR information; @@ -188,6 +434,20 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi unsigned int i; ULONG options;
+ if (unix_flags & MSG_OOB) + { + int oobinline; + socklen_t len = sizeof(oobinline); + if (!getsockopt( fd, SOL_SOCKET, SO_OOBINLINE, (char *)&oobinline, &len ) && oobinline) + return STATUS_INVALID_PARAMETER; + } + + for (i = 0; i < count; ++i) + { + if (!virtual_check_buffer_for_write( buffers[i].buf, buffers[i].len )) + return STATUS_ACCESS_VIOLATION; + } + async_size = offsetof( struct async_recv_ioctl, iov[count] );
if (!(async = (struct async_recv_ioctl *)alloc_fileio( async_size, async_recv_proc, handle ))) @@ -200,6 +460,10 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi async->iov[i].iov_len = buffers[i].len; } async->unix_flags = unix_flags; + async->control = control; + async->addr = addr; + async->addr_len = addr_len; + async->ret_flags = ret_flags;
status = try_recv( fd, async, &information );
@@ -392,15 +656,12 @@ static NTSTATUS sock_poll( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size ) { - int fd, needs_close; + int fd, needs_close = FALSE; NTSTATUS status;
TRACE( "handle %p, code %#x, in_buffer %p, in_size %u, out_buffer %p, out_size %u\n", handle, code, in_buffer, in_size, out_buffer, out_size );
- if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL ))) - return status; - switch (code) { case IOCTL_AFD_LISTEN: @@ -421,6 +682,9 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc const struct afd_recv_params *params = in_buffer; int unix_flags = 0;
+ if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL ))) + return status; + if (out_size) FIXME( "unexpected output size %u\n", out_size );
if (in_size < sizeof(struct afd_recv_params)) @@ -448,8 +712,34 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc if (params->msg_flags & AFD_MSG_WAITALL) FIXME( "MSG_WAITALL is not supported\n" );
- status = sock_recv( handle, event, apc, apc_user, io, fd, params->buffers, params->count, - unix_flags, !!(params->recv_flags & AFD_RECV_FORCE_ASYNC) ); + status = sock_recv( handle, event, apc, apc_user, io, fd, params->buffers, params->count, NULL, + NULL, NULL, NULL, unix_flags, !!(params->recv_flags & AFD_RECV_FORCE_ASYNC) ); + break; + } + + case IOCTL_AFD_WINE_RECVMSG: + { + struct afd_recvmsg_params *params = in_buffer; + int unix_flags = 0; + + if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL ))) + return status; + + if (in_size < sizeof(*params)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + if (*params->ws_flags & WS_MSG_OOB) + unix_flags |= MSG_OOB; + if (*params->ws_flags & WS_MSG_PEEK) + unix_flags |= MSG_PEEK; + if (*params->ws_flags & WS_MSG_WAITALL) + FIXME( "MSG_WAITALL is not supported\n" ); + + status = sock_recv( handle, event, apc, apc_user, io, fd, params->buffers, params->count, params->control, + params->addr, params->addr_len, params->ws_flags, unix_flags, params->force_async ); break; }
@@ -459,9 +749,17 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc
default: { - FIXME( "Unknown ioctl %#x (device %#x, access %#x, function %#x, method %#x)\n", - code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3 ); - status = STATUS_INVALID_DEVICE_REQUEST; + if ((code >> 16) == FILE_DEVICE_NETWORK) + { + /* Wine-internal ioctl */ + status = STATUS_BAD_DEVICE_TYPE; + } + else + { + FIXME( "Unknown ioctl %#x (device %#x, access %#x, function %#x, method %#x)\n", + code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3 ); + status = STATUS_INVALID_DEVICE_REQUEST; + } break; } } diff --git a/include/wine/afd.h b/include/wine/afd.h index 264e76ef69a..245aa525a7b 100644 --- a/include/wine/afd.h +++ b/include/wine/afd.h @@ -25,6 +25,12 @@ #include <winioctl.h> #include "wine/server_protocol.h"
+#ifdef USE_WS_PREFIX +# define WS(x) WS_##x +#else +# define WS(x) x +#endif + #define IOCTL_AFD_LISTEN CTL_CODE(FILE_DEVICE_BEEP, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS) #define IOCTL_AFD_RECV CTL_CODE(FILE_DEVICE_BEEP, 0x805, METHOD_NEITHER, FILE_ANY_ACCESS) #define IOCTL_AFD_POLL CTL_CODE(FILE_DEVICE_BEEP, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS) @@ -85,6 +91,7 @@ struct afd_poll_params #define IOCTL_AFD_WINE_ACCEPT_INTO CTL_CODE(FILE_DEVICE_NETWORK, 202, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_AFD_WINE_CONNECT CTL_CODE(FILE_DEVICE_NETWORK, 203, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_AFD_WINE_SHUTDOWN CTL_CODE(FILE_DEVICE_NETWORK, 204, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_AFD_WINE_RECVMSG CTL_CODE(FILE_DEVICE_NETWORK, 205, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_AFD_WINE_ADDRESS_LIST_CHANGE CTL_CODE(FILE_DEVICE_NETWORK, 323, METHOD_BUFFERED, FILE_ANY_ACCESS)
@@ -108,4 +115,15 @@ struct afd_connect_params /* VARARG(data, bytes); */ };
+struct afd_recvmsg_params +{ + WSABUF *control; + struct WS(sockaddr) *addr; + int *addr_len; + unsigned int *ws_flags; + int force_async; + unsigned int count; + WSABUF *buffers; +}; + #endif
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=91315
Your paranoid android.
=== debiant2 (64 bit WoW report) ===
ntdll: om.c:2307: Test failed: got 87
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ws2_32/socket.c | 466 ++++++--------------------------------- dlls/ws2_32/tests/sock.c | 18 +- 2 files changed, 72 insertions(+), 412 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 3632397ef3d..620226d7296 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -197,8 +197,6 @@ static struct interface_filter generic_interface_filter = { }; #endif /* LINUX_BOUND_IF */
-extern ssize_t CDECL __wine_locked_recvmsg( int fd, struct msghdr *hdr, int flags ); - /* * The actual definition of WSASendTo, wrapped in a different function name * so that internal calls from ws2_32 itself will not trigger programs like @@ -210,17 +208,6 @@ static int WS2_sendto( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
-/* - * Internal fundamental receive function, essentially WSARecvFrom with an - * additional parameter to support message control headers. - */ -static int WS2_recv_base( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, - LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, - struct WS_sockaddr *lpFrom, - LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, - LPWSABUF lpControlBuffer ); - DECLARE_CRITICAL_SECTION(cs_if_addr_cache); DECLARE_CRITICAL_SECTION(cs_socket_list);
@@ -715,95 +702,6 @@ static const int ws_poll_map[][2] = { WS_POLLRDBAND, POLLPRI } };
-#ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS -#if defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) -static inline WSACMSGHDR *fill_control_message(int level, int type, WSACMSGHDR *current, ULONG *maxsize, void *data, int len) -{ - ULONG msgsize = sizeof(WSACMSGHDR) + WSA_CMSG_ALIGN(len); - char *ptr = (char *) current + sizeof(WSACMSGHDR); - - /* Make sure there is at least enough room for this entry */ - if (msgsize > *maxsize) - return NULL; - *maxsize -= msgsize; - /* Fill in the entry */ - current->cmsg_len = sizeof(WSACMSGHDR) + len; - current->cmsg_level = level; - current->cmsg_type = type; - memcpy(ptr, data, len); - /* Return the pointer to where next entry should go */ - return (WSACMSGHDR *) (ptr + WSA_CMSG_ALIGN(len)); -} -#endif /* defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) */ - -static inline int convert_control_headers(struct msghdr *hdr, WSABUF *control) -{ -#if defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) - WSACMSGHDR *cmsg_win = (WSACMSGHDR *) control->buf, *ptr; - ULONG ctlsize = control->len; - struct cmsghdr *cmsg_unix; - - ptr = cmsg_win; - /* Loop over all the headers, converting as appropriate */ - for (cmsg_unix = CMSG_FIRSTHDR(hdr); cmsg_unix != NULL; cmsg_unix = CMSG_NXTHDR(hdr, cmsg_unix)) - { - switch(cmsg_unix->cmsg_level) - { - case IPPROTO_IP: - switch(cmsg_unix->cmsg_type) - { -#if defined(IP_PKTINFO) - case IP_PKTINFO: - { - /* Convert the Unix IP_PKTINFO structure to the Windows version */ - struct in_pktinfo *data_unix = (struct in_pktinfo *) CMSG_DATA(cmsg_unix); - struct WS_in_pktinfo data_win; - - memcpy(&data_win.ipi_addr,&data_unix->ipi_addr.s_addr,4); /* 4 bytes = 32 address bits */ - data_win.ipi_ifindex = data_unix->ipi_ifindex; - ptr = fill_control_message(WS_IPPROTO_IP, WS_IP_PKTINFO, ptr, &ctlsize, - (void*)&data_win, sizeof(data_win)); - if (!ptr) goto error; - } break; -#elif defined(IP_RECVDSTADDR) - case IP_RECVDSTADDR: - { - struct in_addr *addr_unix = (struct in_addr *) CMSG_DATA(cmsg_unix); - struct WS_in_pktinfo data_win; - - memcpy(&data_win.ipi_addr, &addr_unix->s_addr, 4); /* 4 bytes = 32 address bits */ - data_win.ipi_ifindex = 0; /* FIXME */ - ptr = fill_control_message(WS_IPPROTO_IP, WS_IP_PKTINFO, ptr, &ctlsize, - (void*)&data_win, sizeof(data_win)); - if (!ptr) goto error; - } break; -#endif /* IP_PKTINFO */ - default: - FIXME("Unhandled IPPROTO_IP message header type %d\n", cmsg_unix->cmsg_type); - break; - } - break; - default: - FIXME("Unhandled message header level %d\n", cmsg_unix->cmsg_level); - break; - } - } - - /* Set the length of the returned control headers */ - control->len = (char*)ptr - (char*)cmsg_win; - return 1; -error: - control->len = 0; - return 0; -#else /* defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) */ - control->len = 0; - return 1; -#endif /* defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) */ -} -#endif /* HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */ - -/* ----------------------------------- error handling */ - static NTSTATUS sock_get_ntstatus( int err ) { switch ( err ) @@ -2021,127 +1919,6 @@ static void WINAPI ws2_async_apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserv release_async_io( &wsa->io ); }
-/*********************************************************************** - * WS2_recv (INTERNAL) - * - * Workhorse for both synchronous and asynchronous recv() operations. - */ -static int WS2_recv( int fd, struct ws2_async *wsa, int flags ) -{ -#ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS - char pktbuf[512]; -#endif - struct msghdr hdr; - union generic_unix_sockaddr unix_sockaddr; - int n; - - hdr.msg_name = NULL; - - if (wsa->addr) - { - hdr.msg_namelen = sizeof(unix_sockaddr); - hdr.msg_name = &unix_sockaddr; - } - else - hdr.msg_namelen = 0; - - hdr.msg_iov = wsa->iovec + wsa->first_iovec; - hdr.msg_iovlen = wsa->n_iovecs - wsa->first_iovec; -#ifdef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS - hdr.msg_accrights = NULL; - hdr.msg_accrightslen = 0; -#else - hdr.msg_control = pktbuf; - hdr.msg_controllen = sizeof(pktbuf); - hdr.msg_flags = 0; -#endif - - while ((n = __wine_locked_recvmsg( fd, &hdr, flags )) == -1) - { - if (errno != EINTR) - return -1; - } - -#ifdef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS - if (wsa->control) - { - ERR("Message control headers cannot be properly supported on this system.\n"); - wsa->control->len = 0; - } -#else - if (wsa->control && !convert_control_headers(&hdr, wsa->control)) - { - WARN("Application passed insufficient room for control headers.\n"); - *wsa->lpFlags |= WS_MSG_CTRUNC; - errno = EMSGSIZE; - return -1; - } -#endif - - /* if this socket is connected and lpFrom is not NULL, Linux doesn't give us - * msg_name and msg_namelen from recvmsg, but it does set msg_namelen to zero. - * - * quoting linux 2.6 net/ipv4/tcp.c: - * "According to UNIX98, msg_name/msg_namelen are ignored - * on connected socket. I was just happy when found this 8) --ANK" - * - * likewise MSDN says that lpFrom and lpFromlen are ignored for - * connection-oriented sockets, so don't try to update lpFrom. - */ - if (wsa->addr && hdr.msg_namelen) - ws_sockaddr_u2ws( &unix_sockaddr.addr, wsa->addr, wsa->addrlen.ptr ); - - return n; -} - -/*********************************************************************** - * WS2_async_recv (INTERNAL) - * - * Handler for overlapped recv() operations. - */ -static NTSTATUS WS2_async_recv( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status ) -{ - struct ws2_async *wsa = user; - int result = 0, fd; - - switch (status) - { - case STATUS_ALERTED: - if ((status = wine_server_handle_to_fd( wsa->hSocket, FILE_READ_DATA, &fd, NULL ) )) - break; - - result = WS2_recv( fd, wsa, convert_flags(wsa->flags) ); - close( fd ); - if (result >= 0) - { - status = STATUS_SUCCESS; - _enable_event( wsa->hSocket, (wsa->flags & WS_MSG_OOB) ? FD_OOB : FD_READ, 0, 0 ); - } - else - { - if (errno == EAGAIN) - { - status = STATUS_PENDING; - _enable_event( wsa->hSocket, (wsa->flags & WS_MSG_OOB) ? FD_OOB : FD_READ, 0, 0 ); - } - else - { - result = 0; - status = wsaErrStatus(); - } - } - break; - } - if (status != STATUS_PENDING) - { - iosb->u.Status = status; - iosb->Information = result; - if (!wsa->completion_func) - release_async_io( &wsa->io ); - } - return status; -} - /*********************************************************************** * WS2_send (INTERNAL) * @@ -2663,6 +2440,69 @@ static void WINAPI WS2_GetAcceptExSockaddrs(PVOID buffer, DWORD data_size, DWORD *remote_addr = (struct WS_sockaddr *)(cbuf + sizeof(int)); }
+ +static void WINAPI socket_apc( void *apc_user, IO_STATUS_BLOCK *io, ULONG reserved ) +{ + LPWSAOVERLAPPED_COMPLETION_ROUTINE func = apc_user; + func( NtStatusToWSAError( io->u.Status ), io->Information, (OVERLAPPED *)io, 0 ); +} + +static int WS2_recv_base( SOCKET s, WSABUF *buffers, DWORD buffer_count, DWORD *ret_size, DWORD *flags, + struct WS_sockaddr *addr, int *addr_len, OVERLAPPED *overlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE completion, WSABUF *control ) +{ + IO_STATUS_BLOCK iosb, *piosb = &iosb; + struct afd_recvmsg_params params; + PIO_APC_ROUTINE apc = NULL; + HANDLE event = NULL; + void *cvalue = NULL; + NTSTATUS status; + + TRACE( "socket %#lx, buffers %p, buffer_count %u, flags %#x, addr %p, " + "addr_len %d, overlapped %p, completion %p, control %p\n", + s, buffers, buffer_count, *flags, addr, addr_len ? *addr_len : -1, overlapped, completion, control ); + + if (overlapped) + { + piosb = (IO_STATUS_BLOCK *)overlapped; + if (!((ULONG_PTR)overlapped->hEvent & 1)) cvalue = overlapped; + event = overlapped->hEvent; + } + else + { + if (!(event = get_sync_event())) return -1; + } + piosb->u.Status = STATUS_PENDING; + + if (completion) + { + event = NULL; + cvalue = completion; + apc = socket_apc; + } + + params.control = control; + params.addr = addr; + params.addr_len = addr_len; + params.ws_flags = flags; + params.force_async = !!overlapped; + params.count = buffer_count; + params.buffers = buffers; + + status = NtDeviceIoControlFile( (HANDLE)s, event, apc, cvalue, piosb, + IOCTL_AFD_WINE_RECVMSG, ¶ms, sizeof(params), NULL, 0 ); + if (status == STATUS_PENDING && !overlapped) + { + if (WaitForSingleObject( event, INFINITE ) == WAIT_FAILED) + return -1; + status = piosb->u.Status; + } + if (!status && ret_size) *ret_size = piosb->Information; + SetLastError( NtStatusToWSAError( status ) ); + return status ? -1 : 0; +} + + /*********************************************************************** * WSASendMsg */ @@ -5900,186 +5740,6 @@ int WINAPI WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, NULL, NULL, lpOverlapped, lpCompletionRoutine, NULL); }
-static int WS2_recv_base( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, - LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, - struct WS_sockaddr *lpFrom, - LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, - LPWSABUF lpControlBuffer ) -{ - unsigned int i, options; - int n, fd, err, overlapped, flags; - struct ws2_async *wsa = NULL, localwsa; - BOOL is_blocking; - DWORD timeout_start = GetTickCount(); - ULONG_PTR cvalue = (lpOverlapped && ((ULONG_PTR)lpOverlapped->hEvent & 1) == 0) ? (ULONG_PTR)lpOverlapped : 0; - - TRACE("socket %04lx, wsabuf %p, nbufs %d, flags %d, from %p, fromlen %d, ovl %p, func %p\n", - s, lpBuffers, dwBufferCount, *lpFlags, lpFrom, - (lpFromlen ? *lpFromlen : -1), - lpOverlapped, lpCompletionRoutine); - - fd = get_sock_fd( s, FILE_READ_DATA, &options ); - TRACE( "fd=%d, options=%x\n", fd, options ); - - if (fd == -1) return SOCKET_ERROR; - - if (*lpFlags & WS_MSG_OOB) - { - /* It's invalid to receive OOB data from an OOBINLINED socket - * as OOB data is turned into normal data. */ - socklen_t len = sizeof(n); - if (!getsockopt(fd, SOL_SOCKET, SO_OOBINLINE, (char*) &n, &len) && n) - { - err = WSAEINVAL; - goto error; - } - } - - overlapped = (lpOverlapped || lpCompletionRoutine) && - !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)); - if (overlapped || dwBufferCount > 1) - { - if (!(wsa = (struct ws2_async *)alloc_async_io( offsetof(struct ws2_async, iovec[dwBufferCount]), - WS2_async_recv ))) - { - err = WSAEFAULT; - goto error; - } - } - else - wsa = &localwsa; - - wsa->hSocket = SOCKET2HANDLE(s); - wsa->flags = *lpFlags; - wsa->lpFlags = lpFlags; - wsa->addr = lpFrom; - wsa->addrlen.ptr = lpFromlen; - wsa->control = lpControlBuffer; - wsa->n_iovecs = dwBufferCount; - wsa->first_iovec = 0; - for (i = 0; i < dwBufferCount; i++) - { - /* check buffer first to trigger write watches */ - if (IsBadWritePtr( lpBuffers[i].buf, lpBuffers[i].len )) - { - err = WSAEFAULT; - goto error; - } - wsa->iovec[i].iov_base = lpBuffers[i].buf; - wsa->iovec[i].iov_len = lpBuffers[i].len; - } - - flags = convert_flags(wsa->flags); - for (;;) - { - n = WS2_recv( fd, wsa, flags ); - if (n == -1) - { - /* Unix-like systems return EINVAL when attempting to read OOB data from - * an empty socket buffer, convert that to a Windows expected return. */ - if ((flags & MSG_OOB) && errno == EINVAL) - errno = EWOULDBLOCK; - - if (errno != EAGAIN) - { - err = wsaErrno(); - goto error; - } - } - else if (lpNumberOfBytesRecvd) *lpNumberOfBytesRecvd = n; - - if (overlapped) - { - IO_STATUS_BLOCK *iosb = lpOverlapped ? (IO_STATUS_BLOCK *)lpOverlapped : &wsa->local_iosb; - - wsa->user_overlapped = lpOverlapped; - wsa->completion_func = lpCompletionRoutine; - release_sock_fd( s, fd ); - - if (n == -1) - { - iosb->u.Status = STATUS_PENDING; - iosb->Information = 0; - - if (wsa->completion_func) - err = register_async( ASYNC_TYPE_READ, wsa->hSocket, &wsa->io, NULL, - ws2_async_apc, wsa, iosb ); - else - err = register_async( ASYNC_TYPE_READ, wsa->hSocket, &wsa->io, lpOverlapped->hEvent, - NULL, (void *)cvalue, iosb ); - - if (err != STATUS_PENDING) HeapFree( GetProcessHeap(), 0, wsa ); - SetLastError(NtStatusToWSAError( err )); - return SOCKET_ERROR; - } - - iosb->u.Status = STATUS_SUCCESS; - iosb->Information = n; - if (!wsa->completion_func) - { - if (cvalue) WS_AddCompletion( s, cvalue, STATUS_SUCCESS, n, FALSE ); - if (lpOverlapped->hEvent) SetEvent( lpOverlapped->hEvent ); - HeapFree( GetProcessHeap(), 0, wsa ); - } - else NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)ws2_async_apc, - (ULONG_PTR)wsa, (ULONG_PTR)iosb, 0 ); - _enable_event(SOCKET2HANDLE(s), (wsa->flags & WS_MSG_OOB) ? FD_OOB : FD_READ, 0, 0); - return 0; - } - - if (n != -1) break; - - if ((err = sock_is_blocking( s, &is_blocking ))) goto error; - - if ( is_blocking ) - { - struct pollfd pfd; - int poll_timeout = -1; - INT64 timeout = get_rcvsnd_timeo(fd, TRUE); - - if (timeout) - { - timeout -= GetTickCount() - timeout_start; - if (timeout < 0) poll_timeout = 0; - else poll_timeout = timeout <= INT_MAX ? timeout : INT_MAX; - } - - pfd.fd = fd; - pfd.events = POLLIN; - if (*lpFlags & WS_MSG_OOB) pfd.events |= POLLPRI; - - if (!poll_timeout || !poll( &pfd, 1, poll_timeout )) - { - err = WSAETIMEDOUT; - /* a timeout is not fatal */ - _enable_event(SOCKET2HANDLE(s), (wsa->flags & WS_MSG_OOB) ? FD_OOB : FD_READ, 0, 0); - goto error; - } - } - else - { - _enable_event(SOCKET2HANDLE(s), (wsa->flags & WS_MSG_OOB) ? FD_OOB : FD_READ, 0, 0); - err = WSAEWOULDBLOCK; - goto error; - } - } - - TRACE(" -> %i bytes\n", n); - if (wsa != &localwsa) HeapFree( GetProcessHeap(), 0, wsa ); - release_sock_fd( s, fd ); - _enable_event(SOCKET2HANDLE(s), (wsa->flags & WS_MSG_OOB) ? FD_OOB : FD_READ, 0, 0); - SetLastError(ERROR_SUCCESS); - - return 0; - -error: - if (wsa != &localwsa) HeapFree( GetProcessHeap(), 0, wsa ); - release_sock_fd( s, fd ); - WARN(" -> ERROR %d\n", err); - SetLastError( err ); - return SOCKET_ERROR; -}
/*********************************************************************** * WSARecvFrom (WS2_32.69) diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 912bf9ee5d8..6801392be1e 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -1891,14 +1891,14 @@ static void test_ip_pktinfo(void) ok(GetLastError() == ERROR_SUCCESS, "Expected 0, got %d\n", GetLastError()); ok(!WaitForSingleObject(ov.hEvent, 100), "wait failed\n"); ok((NTSTATUS)ov.Internal == STATUS_BUFFER_OVERFLOW, "got status %#x\n", (NTSTATUS)ov.Internal); - todo_wine ok(ov.InternalHigh == sizeof(msg), "got size %Iu\n", ov.InternalHigh); + ok(ov.InternalHigh == sizeof(msg), "got size %Iu\n", ov.InternalHigh); ok(ov.Offset == 0xdead3, "got Offset %Iu\n", ov.Offset); ok(ov.OffsetHigh == 0xdead4, "got OffsetHigh %Iu\n", ov.OffsetHigh); dwFlags = 0xdeadbeef; rc = WSAGetOverlappedResult(s1, &ov, &dwSize, FALSE, &dwFlags); ok(!rc, "expected failure\n"); ok(WSAGetLastError() == WSAEMSGSIZE, "got error %u\n", WSAGetLastError()); - todo_wine ok(dwSize == sizeof(msg), "got size %u\n", dwSize); + ok(dwSize == sizeof(msg), "got size %u\n", dwSize); todo_wine ok(dwFlags == 0xdeadbeef, "got flags %#x\n", dwFlags); ok(hdr.dwFlags == MSG_CTRUNC, "WSARecvMsg() overlapped operation set unexpected flags %d.\n", hdr.dwFlags); @@ -9698,11 +9698,11 @@ static DWORD CALLBACK nonblocking_async_recv_thread(void *arg) wsabuf.len = sizeof(buffer); memset(buffer, 0, sizeof(buffer)); ret = WSARecv(params->client, &wsabuf, 1, NULL, &flags, &overlapped, NULL); - todo_wine_if (!params->event) ok(!ret, "got %d\n", ret); + ok(!ret, "got %d\n", ret); ret = GetOverlappedResult((HANDLE)params->client, &overlapped, &size, FALSE); ok(ret, "got error %u\n", GetLastError()); - todo_wine ok(size == 4, "got size %u\n", size); - todo_wine_if (!params->event) ok(!strcmp(buffer, "data"), "got %s\n", debugstr_an(buffer, size)); + ok(size == 4, "got size %u\n", size); + ok(!strcmp(buffer, "data"), "got %s\n", debugstr_an(buffer, size));
return 0; } @@ -9827,7 +9827,7 @@ static void test_nonblocking_async_recv(void) thread = CreateThread(NULL, 0, nonblocking_async_recv_thread, ¶ms, 0, NULL);
ret = WaitForSingleObject(thread, 200); - todo_wine ok(ret == WAIT_TIMEOUT, "expected timeout\n"); + ok(ret == WAIT_TIMEOUT, "expected timeout\n");
ret = send(server, "data", 4, 0); ok(ret == 4, "got %d\n", ret); @@ -9843,7 +9843,7 @@ static void test_nonblocking_async_recv(void) thread = CreateThread(NULL, 0, nonblocking_async_recv_thread, ¶ms, 0, NULL);
ret = WaitForSingleObject(thread, 200); - todo_wine ok(ret == WAIT_TIMEOUT, "expected timeout\n"); + ok(ret == WAIT_TIMEOUT, "expected timeout\n");
ret = send(server, "data", 4, 0); ok(ret == 4, "got %d\n", ret); @@ -9861,9 +9861,9 @@ static void test_nonblocking_async_recv(void) ret = WSARecv(client, &wsabuf, 1, NULL, &flags, &overlapped, NULL); ok(!ret, "got %d\n", ret); ret = GetOverlappedResult((HANDLE)client, &overlapped, &size, FALSE); - todo_wine ok(ret, "got error %u\n", GetLastError()); + ok(ret, "got error %u\n", GetLastError()); ok(size == 4, "got size %u\n", size); - todo_wine ok(!strcmp(buffer, "data"), "got %s\n", debugstr_an(buffer, size)); + ok(!strcmp(buffer, "data"), "got %s\n", debugstr_an(buffer, size));
closesocket(client); closesocket(server);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=91316
Your paranoid android.
=== debiant2 (32 bit Chinese:China report) ===
ws2_32: sock.c:4177: Test failed: got events 0x10 sock.c:4178: Test failed: expected timeout sock.c:4178: Test failed: got events 0x2
=== debiant2 (64 bit WoW report) ===
ws2_32: sock.c:4177: Test failed: got events 0x10 sock.c:4178: Test failed: expected timeout sock.c:4178: Test failed: got events 0x2
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/ntdll.spec | 3 --- dlls/ntdll/unix/socket.c | 4 +--- dlls/ntdll/unix/unix_private.h | 3 +++ dlls/ntdll/unix/virtual.c | 4 ++-- 4 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index c092e7a9d78..529bf6f269a 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -1623,9 +1623,6 @@ @ cdecl -norelay __wine_dbg_output(str) @ cdecl -norelay __wine_dbg_strdup(str)
-# Virtual memory -@ cdecl -syscall __wine_locked_recvmsg(long ptr long) - # Version @ cdecl wine_get_version() @ cdecl wine_get_build_id() diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index aa56e529f0d..8d126e004f0 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -147,8 +147,6 @@ static NTSTATUS sock_errno_to_status( int err ) } }
-extern ssize_t CDECL __wine_locked_recvmsg( int fd, struct msghdr *hdr, int flags ); - union unix_sockaddr { struct sockaddr addr; @@ -349,7 +347,7 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz hdr.msg_control = control_buffer; hdr.msg_controllen = sizeof(control_buffer); #endif - while ((ret = __wine_locked_recvmsg( fd, &hdr, async->unix_flags )) < 0 && errno == EINTR); + while ((ret = virtual_locked_recvmsg( fd, &hdr, async->unix_flags )) < 0 && errno == EINTR);
if (ret < 0) { diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index fdede25cfbc..f28d5cf4e99 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -27,6 +27,8 @@ #include "wine/server.h" #include "wine/list.h"
+struct msghdr; + #ifdef __i386__ static const WORD current_machine = IMAGE_FILE_MACHINE_I386; #elif defined(__x86_64__) @@ -216,6 +218,7 @@ extern NTSTATUS virtual_handle_fault( void *addr, DWORD err, void *stack ) DECLS extern unsigned int virtual_locked_server_call( void *req_ptr ) DECLSPEC_HIDDEN; extern ssize_t virtual_locked_read( int fd, void *addr, size_t size ) DECLSPEC_HIDDEN; extern ssize_t virtual_locked_pread( int fd, void *addr, size_t size, off_t offset ) DECLSPEC_HIDDEN; +extern ssize_t virtual_locked_recvmsg( int fd, struct msghdr *hdr, int flags ) DECLSPEC_HIDDEN; extern BOOL virtual_is_valid_code_address( const void *addr, SIZE_T size ) DECLSPEC_HIDDEN; extern void *virtual_setup_exception( void *stack_ptr, size_t size, EXCEPTION_RECORD *rec ) DECLSPEC_HIDDEN; extern BOOL virtual_check_buffer_for_read( const void *ptr, SIZE_T size ) DECLSPEC_HIDDEN; diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index fd2e80c75f3..2ef72220a45 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -3356,9 +3356,9 @@ ssize_t virtual_locked_pread( int fd, void *addr, size_t size, off_t offset )
/*********************************************************************** - * __wine_locked_recvmsg (NTDLL.@) + * virtual_locked_recvmsg */ -ssize_t CDECL __wine_locked_recvmsg( int fd, struct msghdr *hdr, int flags ) +ssize_t virtual_locked_recvmsg( int fd, struct msghdr *hdr, int flags ) { sigset_t sigset; size_t i;
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/unix/socket.c | 310 ++++++++++++++++++++++++++++++++++++++- include/wine/afd.h | 11 ++ server/protocol.def | 11 ++ server/sock.c | 92 +++++++++++- 4 files changed, 410 insertions(+), 14 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 8d126e004f0..3d4b98e5871 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -89,6 +89,19 @@ static NTSTATUS wait_async( HANDLE handle, BOOL alertable ) return NtWaitForSingleObject( handle, alertable, NULL ); }
+union unix_sockaddr +{ + struct sockaddr addr; + struct sockaddr_in in; + struct sockaddr_in6 in6; +#ifdef HAS_IPX + struct sockaddr_ipx ipx; +#endif +#ifdef HAS_IRDA + struct sockaddr_irda irda; +#endif +}; + struct async_recv_ioctl { struct async_fileio io; @@ -101,6 +114,18 @@ struct async_recv_ioctl struct iovec iov[1]; };
+struct async_send_ioctl +{ + struct async_fileio io; + const struct WS_sockaddr *addr; + int addr_len; + int unix_flags; + unsigned int sent_len; + unsigned int count; + unsigned int iov_cursor; + struct iovec iov[1]; +}; + static NTSTATUS sock_errno_to_status( int err ) { switch (err) @@ -147,18 +172,108 @@ static NTSTATUS sock_errno_to_status( int err ) } }
-union unix_sockaddr +static socklen_t sockaddr_to_unix( const struct WS_sockaddr *wsaddr, int wsaddrlen, union unix_sockaddr *uaddr ) { - struct sockaddr addr; - struct sockaddr_in in; - struct sockaddr_in6 in6; + memset( uaddr, 0, sizeof(*uaddr) ); + + switch (wsaddr->sa_family) + { + case WS_AF_INET: + { + struct WS_sockaddr_in win = {0}; + + if (wsaddrlen < sizeof(win)) return 0; + memcpy( &win, wsaddr, sizeof(win) ); + uaddr->in.sin_family = AF_INET; + uaddr->in.sin_port = win.sin_port; + memcpy( &uaddr->in.sin_addr, &win.sin_addr, sizeof(win.sin_addr) ); + return sizeof(uaddr->in); + } + + case WS_AF_INET6: + { + struct WS_sockaddr_in6 win = {0}; + + if (wsaddrlen < sizeof(struct WS_sockaddr_in6_old)) return 0; + if (wsaddrlen < sizeof(struct WS_sockaddr_in6)) + memcpy( &win, wsaddr, sizeof(struct WS_sockaddr_in6_old) ); + else + memcpy( &win, wsaddr, sizeof(struct WS_sockaddr_in6) ); + + uaddr->in6.sin6_family = AF_INET6; + uaddr->in6.sin6_port = win.sin6_port; + uaddr->in6.sin6_flowinfo = win.sin6_flowinfo; + memcpy( &uaddr->in6.sin6_addr, &win.sin6_addr, sizeof(win.sin6_addr) ); +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID + if (wsaddrlen >= sizeof(struct WS_sockaddr_in6)) + uaddr->in6.sin6_scope_id = win.sin6_scope_id; +#endif + return sizeof(uaddr->in6); + } + #ifdef HAS_IPX - struct sockaddr_ipx ipx; + case WS_AF_IPX: + { + struct WS_sockaddr_ipx win = {0}; + + if (wsaddrlen < sizeof(win)) return 0; + memcpy( &win, wsaddr, sizeof(win) ); + uaddr->ipx.sipx_family = AF_IPX; + memcpy( &uaddr->ipx.sipx_network, win.sa_netnum, sizeof(win.sa_netnum) ); + memcpy( &uaddr->ipx.sipx_node, win.sa_nodenum, sizeof(win.sa_nodenum) ); + uaddr->ipx.sipx_port = win.sa_socket; + return sizeof(uaddr->ipx); + } #endif + #ifdef HAS_IRDA - struct sockaddr_irda irda; + case WS_AF_IRDA: + { + SOCKADDR_IRDA win = {0}; + unsigned int lsap_sel; + + if (wsaddrlen < sizeof(win)) return 0; + memcpy( &win, wsaddr, sizeof(win) ); + uaddr->irda.sir_family = AF_IRDA; + if (sscanf( win.irdaServiceName, "LSAP-SEL%u", &lsap_sel ) == 1) + uaddr->sir_lsap_sel = lsap_sel; + else + { + uaddr->sir_lsap_sel = LSAP_ANY; + memcpy( uaddr->irda.sir_name, win.irdaServiceName, sizeof(win.irdaServiceName) ); + } + memcpy( &uaddr->irda.sir_addr, win.irdaDeviceID, sizeof(win.irdaDeviceID) ); + return sizeof(uaddr->irda); + } #endif -}; + + case WS_AF_UNSPEC: + switch (wsaddrlen) + { + default: /* likely an ipv4 address */ + case sizeof(struct WS_sockaddr_in): + return sizeof(uaddr->in); + +#ifdef HAS_IPX + case sizeof(struct WS_sockaddr_ipx): + return sizeof(uaddr->ipx); +#endif + +#ifdef HAS_IRDA + case sizeof(SOCKADDR_IRDA): + return sizeof(uaddr->irda); +#endif + + case sizeof(struct WS_sockaddr_in6): + case sizeof(struct WS_sockaddr_in6_old): + return sizeof(uaddr->in6); + } + + default: + FIXME( "unknown address family %u\n", wsaddr->sa_family ); + return 0; + } +}
static int sockaddr_from_unix( const union unix_sockaddr *uaddr, struct WS_sockaddr *wsaddr, socklen_t wsaddrlen ) { @@ -650,6 +765,161 @@ static NTSTATUS sock_poll( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi return status; }
+static NTSTATUS try_send( int fd, struct async_send_ioctl *async ) +{ + union unix_sockaddr unix_addr; + struct msghdr hdr; + ssize_t ret; + + memset( &hdr, 0, sizeof(hdr) ); + if (async->addr) + { + hdr.msg_name = &unix_addr; + hdr.msg_namelen = sockaddr_to_unix( async->addr, async->addr_len, &unix_addr ); + if (!hdr.msg_namelen) + { + ERR( "failed to convert address\n" ); + return STATUS_ACCESS_VIOLATION; + } + +#if defined(HAS_IPX) && defined(SOL_IPX) + if (async->addr->sa_family == WS_AF_IPX) + { + int type; + socklen_t len = sizeof(type); + + /* The packet type is stored at the IPX socket level. At least the + * linux kernel seems to do something with it in case hdr.msg_name + * is NULL. Nonetheless we can use it to store the packet type, and + * then we can retrieve it using getsockopt. After that we can set + * the IPX type in the sockaddr_ipx structure with the stored value. + */ + if (getsockopt(fd, SOL_IPX, IPX_TYPE, &type, &len) >= 0) + unix_addr.ipx.sipx_type = type; + } +#endif + } + + 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; + } + else if (errno != EINTR) + { + if (errno != EWOULDBLOCK) WARN( "sendmsg: %s\n", strerror( errno ) ); + return sock_errno_to_status( errno ); + } + } + + async->sent_len += ret; + + while (async->iov_cursor < async->count && ret >= async->iov[async->iov_cursor].iov_len) + ret -= async->iov[async->iov_cursor++].iov_len; + if (async->iov_cursor < async->count) + { + async->iov[async->iov_cursor].iov_base = (char *)async->iov[async->iov_cursor].iov_base + ret; + async->iov[async->iov_cursor].iov_len -= ret; + return STATUS_DEVICE_NOT_READY; + } + return STATUS_SUCCESS; +} + +static NTSTATUS async_send_proc( void *user, IO_STATUS_BLOCK *io, NTSTATUS status ) +{ + struct async_send_ioctl *async = user; + int fd, needs_close; + + TRACE( "%#x\n", status ); + + if (status == STATUS_ALERTED) + { + if ((status = server_get_unix_fd( async->io.handle, 0, &fd, &needs_close, NULL, NULL ))) + return status; + + status = try_send( fd, async ); + TRACE( "got status %#x\n", status ); + + if (status == STATUS_DEVICE_NOT_READY) + status = STATUS_PENDING; + + if (needs_close) close( fd ); + } + if (status != STATUS_PENDING) + { + io->Status = status; + io->Information = async->sent_len; + release_fileio( &async->io ); + } + return status; +} + +static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, + IO_STATUS_BLOCK *io, int fd, const WSABUF *buffers, unsigned int count, + const struct WS_sockaddr *addr, unsigned int addr_len, int unix_flags, int force_async ) +{ + struct async_send_ioctl *async; + HANDLE wait_handle; + DWORD async_size; + NTSTATUS status; + unsigned int i; + ULONG options; + + async_size = offsetof( struct async_send_ioctl, iov[count] ); + + if (!(async = (struct async_send_ioctl *)alloc_fileio( async_size, async_send_proc, handle ))) + return STATUS_NO_MEMORY; + + async->count = count; + for (i = 0; i < count; ++i) + { + async->iov[i].iov_base = buffers[i].buf; + async->iov[i].iov_len = buffers[i].len; + } + async->unix_flags = unix_flags; + async->addr = addr; + async->addr_len = addr_len; + async->iov_cursor = 0; + async->sent_len = 0; + + status = try_send( fd, async ); + + if (status != STATUS_SUCCESS && status != STATUS_DEVICE_NOT_READY) + { + release_fileio( &async->io ); + return status; + } + + if (status == STATUS_DEVICE_NOT_READY && force_async) + status = STATUS_PENDING; + + if (!NT_ERROR(status)) + { + io->Status = status; + io->Information = async->sent_len; + } + + SERVER_START_REQ( send_socket ) + { + req->status = status; + req->total = async->sent_len; + req->async = server_async( handle, &async->io, event, apc, apc_user, io ); + status = wine_server_call( req ); + wait_handle = wine_server_ptr_handle( reply->wait ); + options = reply->options; + } + SERVER_END_REQ; + + if (status != STATUS_PENDING) release_fileio( &async->io ); + + if (wait_handle) status = wait_async( wait_handle, options & FILE_SYNCHRONOUS_IO_ALERT ); + return status; +}
NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size ) @@ -741,6 +1011,32 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc break; }
+ case IOCTL_AFD_WINE_SENDMSG: + { + const struct afd_sendmsg_params *params = in_buffer; + int unix_flags = 0; + + if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL ))) + return status; + + if (in_size < sizeof(*params)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + if (params->ws_flags & WS_MSG_OOB) + unix_flags |= MSG_OOB; + if (params->ws_flags & WS_MSG_PARTIAL) + WARN( "ignoring MSG_PARTIAL\n" ); + if (params->ws_flags & ~(WS_MSG_OOB | WS_MSG_PARTIAL)) + FIXME( "unknown flags %#x\n", params->ws_flags ); + + status = sock_send( handle, event, apc, apc_user, io, fd, params->buffers, params->count, + params->addr, params->addr_len, unix_flags, params->force_async ); + break; + } + case IOCTL_AFD_POLL: status = sock_poll( handle, event, apc, apc_user, io, in_buffer, in_size, out_buffer, out_size ); break; diff --git a/include/wine/afd.h b/include/wine/afd.h index 245aa525a7b..f003263bfc8 100644 --- a/include/wine/afd.h +++ b/include/wine/afd.h @@ -92,6 +92,7 @@ struct afd_poll_params #define IOCTL_AFD_WINE_CONNECT CTL_CODE(FILE_DEVICE_NETWORK, 203, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_AFD_WINE_SHUTDOWN CTL_CODE(FILE_DEVICE_NETWORK, 204, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_AFD_WINE_RECVMSG CTL_CODE(FILE_DEVICE_NETWORK, 205, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_AFD_WINE_SENDMSG CTL_CODE(FILE_DEVICE_NETWORK, 206, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_AFD_WINE_ADDRESS_LIST_CHANGE CTL_CODE(FILE_DEVICE_NETWORK, 323, METHOD_BUFFERED, FILE_ANY_ACCESS)
@@ -126,4 +127,14 @@ struct afd_recvmsg_params WSABUF *buffers; };
+struct afd_sendmsg_params +{ + const struct WS(sockaddr) *addr; + unsigned int addr_len; + unsigned int ws_flags; + int force_async; + unsigned int count; + const WSABUF *buffers; +}; + #endif diff --git a/server/protocol.def b/server/protocol.def index 88e5ad6a96b..93e2f56e037 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1502,6 +1502,17 @@ struct poll_socket_output @END
+/* Perform a send on a socket */ +@REQ(send_socket) + async_data_t async; /* async I/O parameters */ + unsigned int status; /* status of initial call */ + unsigned int total; /* number of bytes already sent */ +@REPLY + obj_handle_t wait; /* handle to wait on for blocking send */ + unsigned int options; /* device open options */ +@END + + /* Retrieve the next pending console ioctl request */ @REQ(get_next_console_request) obj_handle_t handle; /* console server handle */ diff --git a/server/sock.c b/server/sock.c index 04e27328d80..e521818dc4b 100644 --- a/server/sock.c +++ b/server/sock.c @@ -768,14 +768,11 @@ static int sock_dispatch_asyncs( struct sock *sock, int event, int error ) event &= ~(POLLIN | POLLPRI); }
- if (is_fd_overlapped( sock->fd )) + if (event & POLLOUT && async_waiting( &sock->write_q )) { - if (event & POLLOUT && async_waiting( &sock->write_q )) - { - if (debug_level) fprintf( stderr, "activating write queue for socket %p\n", sock ); - async_wake_up( &sock->write_q, STATUS_ALERTED ); - event &= ~POLLOUT; - } + if (debug_level) fprintf( stderr, "activating write queue for socket %p\n", sock ); + async_wake_up( &sock->write_q, STATUS_ALERTED ); + event &= ~POLLOUT; }
if (event & (POLLERR | POLLHUP)) @@ -2508,3 +2505,84 @@ DECL_HANDLER(poll_socket)
release_object( sock ); } + +DECL_HANDLER(send_socket) +{ + struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops ); + unsigned int status = req->status; + timeout_t timeout = 0; + struct async *async; + struct fd *fd; + + if (!sock) return; + fd = sock->fd; + + if (status != STATUS_SUCCESS) + { + /* send() calls only clear and reselect events if unsuccessful. */ + sock->pending_events &= ~FD_WRITE; + sock->reported_events &= ~FD_WRITE; + } + + /* If we had a short write and the socket is nonblocking (and the client is + * not trying to force the operation to be asynchronous), return success. + * Windows actually refuses to send any data in this case, and returns + * EWOULDBLOCK, but we have no way of doing that. */ + if (status == STATUS_DEVICE_NOT_READY && req->total && (sock->state & FD_WINE_NONBLOCKING)) + status = STATUS_SUCCESS; + + /* send() returned EWOULDBLOCK or a short write, i.e. cannot send all data yet */ + if (status == STATUS_DEVICE_NOT_READY && !(sock->state & FD_WINE_NONBLOCKING)) + { +#ifdef SO_SNDTIMEO + struct timeval tv; + socklen_t len = sizeof(tv); + + /* Set a timeout on the async if necessary. + * + * We want to do this *only* if the client gave us STATUS_DEVICE_NOT_READY. + * If the client gave us STATUS_PENDING, it expects the async to always + * block (it was triggered by WSASend*() with a valid OVERLAPPED + * structure) and for the timeout not to be respected. */ + if (is_fd_overlapped( fd ) && !getsockopt( get_unix_fd( fd ), SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, &len )) + timeout = tv.tv_sec * -10000000 + tv.tv_usec * -10; +#endif + + status = STATUS_PENDING; + } + + /* are we shut down? */ + if (status == STATUS_PENDING && !(sock->state & FD_WRITE)) status = STATUS_PIPE_DISCONNECTED; + + if ((async = create_request_async( fd, get_fd_comp_flags( fd ), &req->async ))) + { + int success = 0; + + if (status == STATUS_SUCCESS) + { + struct iosb *iosb = async_get_iosb( async ); + iosb->result = req->total; + release_object( iosb ); + success = 1; + } + else if (status == STATUS_PENDING) + { + success = 1; + } + set_error( status ); + + if (timeout) + async_set_timeout( async, timeout, STATUS_IO_TIMEOUT ); + + if (status == STATUS_PENDING) + queue_async( &sock->write_q, async ); + + /* always reselect; we changed reported_events above */ + sock_reselect( sock ); + + reply->wait = async_handoff( async, success, NULL, 0 ); + reply->options = get_fd_options( fd ); + release_object( async ); + } + release_object( sock ); +}
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ws2_32/socket.c | 334 ++++++++++----------------------------- dlls/ws2_32/tests/sock.c | 2 +- 2 files changed, 88 insertions(+), 248 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 620226d7296..ff2db136f89 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -197,17 +197,6 @@ static struct interface_filter generic_interface_filter = { }; #endif /* LINUX_BOUND_IF */
-/* - * The actual definition of WSASendTo, wrapped in a different function name - * so that internal calls from ws2_32 itself will not trigger programs like - * Garena, which hooks WSASendTo/WSARecvFrom calls. - */ -static int WS2_sendto( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, - LPDWORD lpNumberOfBytesSent, DWORD dwFlags, - const struct WS_sockaddr *to, int tolen, - LPWSAOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine ); - DECLARE_CRITICAL_SECTION(cs_if_addr_cache); DECLARE_CRITICAL_SECTION(cs_socket_list);
@@ -433,6 +422,25 @@ static BOOL socket_list_add(SOCKET socket) return TRUE; }
+ +static BOOL socket_list_find( SOCKET socket ) +{ + unsigned int i; + + EnterCriticalSection( &cs_socket_list ); + for (i = 0; i < socket_list_size; ++i) + { + if (socket_list[i] == socket) + { + LeaveCriticalSection( &cs_socket_list ); + return TRUE; + } + } + LeaveCriticalSection( &cs_socket_list ); + return FALSE; +} + + static void socket_list_remove(SOCKET socket) { unsigned int i; @@ -1995,61 +2003,6 @@ static int WS2_send( int fd, struct ws2_async *wsa, int flags ) return ret; }
-/*********************************************************************** - * WS2_async_send (INTERNAL) - * - * Handler for overlapped send() operations. - */ -static NTSTATUS WS2_async_send( void *user, IO_STATUS_BLOCK *iosb, NTSTATUS status ) -{ - struct ws2_async *wsa = user; - int result = 0, fd; - - switch (status) - { - case STATUS_ALERTED: - if ( wsa->n_iovecs <= wsa->first_iovec ) - { - /* Nothing to do */ - status = STATUS_SUCCESS; - break; - } - if ((status = wine_server_handle_to_fd( wsa->hSocket, FILE_WRITE_DATA, &fd, NULL ) )) - break; - - /* check to see if the data is ready (non-blocking) */ - result = WS2_send( fd, wsa, convert_flags(wsa->flags) ); - close( fd ); - - if (result >= 0) - { - if (wsa->first_iovec < wsa->n_iovecs) - status = STATUS_PENDING; - else - status = STATUS_SUCCESS; - - iosb->Information += result; - } - else if (errno == EAGAIN) - { - status = STATUS_PENDING; - } - else - { - status = wsaErrStatus(); - } - break; - } - if (status != STATUS_PENDING) - { - iosb->u.Status = status; - if (!wsa->completion_func) - release_async_io( &wsa->io ); - } - return status; -} - - /*********************************************************************** * accept (WS2_32.1) */ @@ -2502,6 +2455,72 @@ static int WS2_recv_base( SOCKET s, WSABUF *buffers, DWORD buffer_count, DWORD * return status ? -1 : 0; }
+static int WS2_sendto( SOCKET s, WSABUF *buffers, DWORD buffer_count, DWORD *ret_size, DWORD flags, + const struct WS_sockaddr *addr, int addr_len, OVERLAPPED *overlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE completion ) +{ + IO_STATUS_BLOCK iosb, *piosb = &iosb; + struct afd_sendmsg_params params; + PIO_APC_ROUTINE apc = NULL; + HANDLE event = NULL; + void *cvalue = NULL; + NTSTATUS status; + + TRACE( "socket %#lx, buffers %p, buffer_count %u, flags %#x, addr %p, " + "addr_len %d, overlapped %p, completion %p\n", + s, buffers, buffer_count, flags, addr, addr_len, overlapped, completion ); + + if (!socket_list_find( s )) + { + SetLastError( WSAENOTSOCK ); + return -1; + } + + if (!overlapped && !ret_size) + { + SetLastError( WSAEFAULT ); + return -1; + } + + if (overlapped) + { + piosb = (IO_STATUS_BLOCK *)overlapped; + if (!((ULONG_PTR)overlapped->hEvent & 1)) cvalue = overlapped; + event = overlapped->hEvent; + } + else + { + if (!(event = get_sync_event())) return -1; + } + piosb->u.Status = STATUS_PENDING; + + if (completion) + { + event = NULL; + cvalue = completion; + apc = socket_apc; + } + + params.addr = addr; + params.addr_len = addr_len; + params.ws_flags = flags; + params.force_async = !!overlapped; + params.count = buffer_count; + params.buffers = buffers; + + status = NtDeviceIoControlFile( (HANDLE)s, event, apc, cvalue, piosb, + IOCTL_AFD_WINE_SENDMSG, ¶ms, sizeof(params), NULL, 0 ); + if (status == STATUS_PENDING && !overlapped) + { + if (WaitForSingleObject( event, INFINITE ) == WAIT_FAILED) + return -1; + status = piosb->u.Status; + } + if (!status && ret_size) *ret_size = piosb->Information; + SetLastError( NtStatusToWSAError( status ) ); + return status ? -1 : 0; +} +
/*********************************************************************** * WSASendMsg @@ -4725,187 +4744,6 @@ INT WINAPI WSASendDisconnect( SOCKET s, LPWSABUF lpBuffers ) }
-static int WS2_sendto( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, - LPDWORD lpNumberOfBytesSent, DWORD dwFlags, - const struct WS_sockaddr *to, int tolen, - LPWSAOVERLAPPED lpOverlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine ) -{ - unsigned int i, options; - int n, fd, err, overlapped, flags; - struct ws2_async *wsa = NULL, localwsa; - int totalLength = 0; - DWORD bytes_sent; - BOOL is_blocking; - - TRACE("socket %04lx, wsabuf %p, nbufs %d, flags %d, to %p, tolen %d, ovl %p, func %p\n", - s, lpBuffers, dwBufferCount, dwFlags, - to, tolen, lpOverlapped, lpCompletionRoutine); - - fd = get_sock_fd( s, FILE_WRITE_DATA, &options ); - TRACE( "fd=%d, options=%x\n", fd, options ); - - if ( fd == -1 ) return SOCKET_ERROR; - - if (!lpOverlapped && !lpNumberOfBytesSent) - { - err = WSAEFAULT; - goto error; - } - - overlapped = (lpOverlapped || lpCompletionRoutine) && - !(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)); - if (overlapped || dwBufferCount > 1) - { - if (!(wsa = (struct ws2_async *)alloc_async_io( offsetof(struct ws2_async, iovec[dwBufferCount]), - WS2_async_send ))) - { - err = WSAEFAULT; - goto error; - } - } - else - wsa = &localwsa; - - wsa->hSocket = SOCKET2HANDLE(s); - wsa->addr = (struct WS_sockaddr *)to; - wsa->addrlen.val = tolen; - wsa->flags = dwFlags; - wsa->lpFlags = &wsa->flags; - wsa->control = NULL; - wsa->n_iovecs = dwBufferCount; - wsa->first_iovec = 0; - for ( i = 0; i < dwBufferCount; i++ ) - { - wsa->iovec[i].iov_base = lpBuffers[i].buf; - wsa->iovec[i].iov_len = lpBuffers[i].len; - totalLength += lpBuffers[i].len; - } - - flags = convert_flags(dwFlags); - n = WS2_send( fd, wsa, flags ); - if (n == -1 && errno != EAGAIN) - { - err = wsaErrno(); - goto error; - } - - if (overlapped) - { - IO_STATUS_BLOCK *iosb = lpOverlapped ? (IO_STATUS_BLOCK *)lpOverlapped : &wsa->local_iosb; - ULONG_PTR cvalue = (lpOverlapped && ((ULONG_PTR)lpOverlapped->hEvent & 1) == 0) ? (ULONG_PTR)lpOverlapped : 0; - - wsa->user_overlapped = lpOverlapped; - wsa->completion_func = lpCompletionRoutine; - release_sock_fd( s, fd ); - - if (n == -1 || n < totalLength) - { - iosb->u.Status = STATUS_PENDING; - iosb->Information = n == -1 ? 0 : n; - - if (wsa->completion_func) - err = register_async( ASYNC_TYPE_WRITE, wsa->hSocket, &wsa->io, NULL, - ws2_async_apc, wsa, iosb ); - else - err = register_async( ASYNC_TYPE_WRITE, wsa->hSocket, &wsa->io, lpOverlapped->hEvent, - NULL, (void *)cvalue, iosb ); - - /* Enable the event only after starting the async. The server will deliver it as soon as - the async is done. */ - _enable_event(SOCKET2HANDLE(s), FD_WRITE, 0, 0); - - if (err != STATUS_PENDING) HeapFree( GetProcessHeap(), 0, wsa ); - SetLastError(NtStatusToWSAError( err )); - return SOCKET_ERROR; - } - - iosb->u.Status = STATUS_SUCCESS; - iosb->Information = n; - if (lpNumberOfBytesSent) *lpNumberOfBytesSent = n; - if (!wsa->completion_func) - { - if (cvalue) WS_AddCompletion( s, cvalue, STATUS_SUCCESS, n, FALSE ); - if (lpOverlapped->hEvent) SetEvent( lpOverlapped->hEvent ); - HeapFree( GetProcessHeap(), 0, wsa ); - } - else NtQueueApcThread( GetCurrentThread(), (PNTAPCFUNC)ws2_async_apc, - (ULONG_PTR)wsa, (ULONG_PTR)iosb, 0 ); - SetLastError(ERROR_SUCCESS); - return 0; - } - - if ((err = sock_is_blocking( s, &is_blocking ))) goto error; - - if ( is_blocking ) - { - /* On a blocking non-overlapped stream socket, - * sending blocks until the entire buffer is sent. */ - DWORD timeout_start = GetTickCount(); - - bytes_sent = n == -1 ? 0 : n; - - while (wsa->first_iovec < wsa->n_iovecs) - { - struct pollfd pfd; - int poll_timeout = -1; - INT64 timeout = get_rcvsnd_timeo(fd, FALSE); - - if (timeout) - { - timeout -= GetTickCount() - timeout_start; - if (timeout < 0) poll_timeout = 0; - else poll_timeout = timeout <= INT_MAX ? timeout : INT_MAX; - } - - pfd.fd = fd; - pfd.events = POLLOUT; - - if (!poll_timeout || !poll( &pfd, 1, poll_timeout )) - { - err = WSAETIMEDOUT; - goto error; /* msdn says a timeout in send is fatal */ - } - - n = WS2_send( fd, wsa, flags ); - if (n == -1 && errno != EAGAIN) - { - err = wsaErrno(); - goto error; - } - - if (n >= 0) - bytes_sent += n; - } - } - else /* non-blocking */ - { - if (n < totalLength) - _enable_event(SOCKET2HANDLE(s), FD_WRITE, 0, 0); - if (n == -1) - { - err = WSAEWOULDBLOCK; - goto error; - } - bytes_sent = n; - } - - TRACE(" -> %i bytes\n", bytes_sent); - - if (lpNumberOfBytesSent) *lpNumberOfBytesSent = bytes_sent; - if (wsa != &localwsa) HeapFree( GetProcessHeap(), 0, wsa ); - release_sock_fd( s, fd ); - SetLastError(ERROR_SUCCESS); - return 0; - -error: - if (wsa != &localwsa) HeapFree( GetProcessHeap(), 0, wsa ); - release_sock_fd( s, fd ); - WARN(" -> ERROR %d\n", err); - SetLastError(err); - return SOCKET_ERROR; -} - /*********************************************************************** * WSASendTo (WS2_32.74) */ @@ -4915,6 +4753,7 @@ INT WINAPI WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine ) { + /* Garena hooks WSASendTo(), so we need a wrapper */ return WS2_sendto( s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, to, tolen, @@ -5750,6 +5589,7 @@ INT WINAPI WSARecvFrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine )
{ + /* Garena hooks WSARecvFrom(), so we need a wrapper */ return WS2_recv_base( s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpFrom, lpFromlen, diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 6801392be1e..f6304c0f5ce 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -4301,7 +4301,7 @@ static void test_write_events(struct event_test_ctx *ctx) if (!broken(1)) { while (send(server, buffer, buffer_size, 0) == buffer_size); - todo_wine ok(WSAGetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError()); + ok(WSAGetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError());
while (recv(client, buffer, buffer_size, 0) > 0); ok(WSAGetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError());