[PATCH 6/6] ws2_32: Use IOCTL_AFD_WINE_SENDMSG in WS2_sendto().

Zebediah Figura z.figura12 at gmail.com
Tue May 25 20:52:55 CDT 2021


Signed-off-by: Zebediah Figura <z.figura12 at 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, &params, 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());
-- 
2.30.2




More information about the wine-devel mailing list