[PATCH 2/6] ntdll: Introduce IOCTL_AFD_WINE_RECVMSG.

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


Signed-off-by: Zebediah Figura <z.figura12 at 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
-- 
2.30.2




More information about the wine-devel mailing list