[PATCH 3/4] ntdll: Support SOCK_RAW / IPPROTO_ICMP fallback over SOCK_DGRAM.

Paul Gofman wine at gitlab.winehq.org
Tue Jul 5 14:53:18 CDT 2022


From: Paul Gofman <pgofman at codeweavers.com>

Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 dlls/ntdll/unix/socket.c | 112 +++++++++++++++++++++++++++++++++++----
 server/protocol.def      |   8 ++-
 server/sock.c            |  32 +++++++++++
 3 files changed, 140 insertions(+), 12 deletions(-)

diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index 351028d4983..d200c0c999c 100644
--- a/dlls/ntdll/unix/socket.c
+++ b/dlls/ntdll/unix/socket.c
@@ -115,6 +115,7 @@ struct async_recv_ioctl
     DWORD *ret_flags;
     int unix_flags;
     unsigned int count;
+    enum sock_prot_fixup_type fixup_type;
     struct iovec iov[1];
 };
 
@@ -566,6 +567,20 @@ static int wow64_translate_control( const WSABUF *control64, struct afd_wsabuf_3
     return 1;
 }
 
+struct ip_hdr
+{
+    BYTE v_hl; /* version << 4 | hdr_len */
+    BYTE tos;
+    UINT16 tot_len;
+    UINT16 id;
+    UINT16 frag_off;
+    BYTE ttl;
+    BYTE protocol;
+    UINT16 checksum;
+    ULONG saddr;
+    ULONG daddr;
+};
+
 static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *size )
 {
 #ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
@@ -576,8 +591,14 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz
     NTSTATUS status;
     ssize_t ret;
 
+    if (async->fixup_type == SOCK_PROT_FIXUP_ICMP_OVER_DGRAM && async->count != 1)
+    {
+        FIXME( "async->count %d not supported for ICMP fixup.\n", async->count );
+        async->fixup_type = SOCK_PROT_FIXUP_NONE;
+    }
+
     memset( &hdr, 0, sizeof(hdr) );
-    if (async->addr)
+    if (async->addr || async->fixup_type == SOCK_PROT_FIXUP_ICMP_OVER_DGRAM)
     {
         hdr.msg_name = &unix_addr.addr;
         hdr.msg_namelen = sizeof(unix_addr);
@@ -602,9 +623,68 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz
     }
 
     status = (hdr.msg_flags & MSG_TRUNC) ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
+    if (async->fixup_type == SOCK_PROT_FIXUP_ICMP_OVER_DGRAM)
+    {
+        unsigned int tot_len = sizeof(struct ip_hdr) + ret;
+        size_t len = hdr.msg_iov[0].iov_len;
+        char *buf = hdr.msg_iov[0].iov_base;
+        struct cmsghdr *cmsg;
+        struct ip_hdr ip_h;
+
+        if (ret + sizeof(ip_h) > len)
+            status = STATUS_BUFFER_OVERFLOW;
+
+        if (len < sizeof(ip_h))
+        {
+            ret = len;
+        }
+        else
+        {
+            ret = min( ret, len - sizeof(ip_h) );
+            memmove( buf + sizeof(ip_h), buf, ret );
+            ret += sizeof(ip_h);
+        }
+        memset( &ip_h, 0, sizeof(ip_h) );
+        ip_h.v_hl = (4 << 4) | (sizeof(ip_h) >> 2);
+        ip_h.tot_len = htons( tot_len );
+        ip_h.protocol = 1;
+        ip_h.saddr = unix_addr.in.sin_addr.s_addr;
+
+        for (cmsg = CMSG_FIRSTHDR( &hdr ); cmsg; cmsg = CMSG_NXTHDR( &hdr, cmsg ))
+        {
+            if (cmsg->cmsg_level != IPPROTO_IP) continue;
+            switch (cmsg->cmsg_type)
+            {
+#if defined(IP_TTL)
+                case IP_TTL:
+                    ip_h.ttl = *(BYTE *)CMSG_DATA( cmsg );
+                    break;
+#endif
+#if defined(IP_TOS)
+                case IP_TOS:
+                    ip_h.tos = *(BYTE *)CMSG_DATA( cmsg );
+                    break;
+#endif
+#if defined(IP_PKTINFO)
+                case IP_PKTINFO:
+                {
+                    struct in_pktinfo *info;
+
+                    info = (struct in_pktinfo *)CMSG_DATA( cmsg );
+                    ip_h.daddr = info->ipi_addr.s_addr;
+                    break;
+                }
+#endif
+            }
+        }
+        memcpy( buf, &ip_h, min( sizeof(ip_h), len ));
+    }
 
     if (async->control)
     {
+        if (async->fixup_type == SOCK_PROT_FIXUP_ICMP_OVER_DGRAM)
+            FIXME( "May return extra control headers.\n" );
+
         if (in_wow64_call())
         {
             char control_buffer64[512];
@@ -727,6 +807,7 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
     async->addr = addr;
     async->addr_len = addr_len;
     async->ret_flags = ret_flags;
+    async->fixup_type = SOCK_PROT_FIXUP_NONE;
 
     for (i = 0; i < count; ++i)
     {
@@ -737,17 +818,26 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
         }
     }
 
-    SERVER_START_REQ( recv_socket )
+    do
     {
-        req->force_async = force_async;
-        req->async  = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) );
-        req->oob    = !!(unix_flags & MSG_OOB);
-        status = wine_server_call( req );
-        wait_handle = wine_server_ptr_handle( reply->wait );
-        options     = reply->options;
-        nonblocking = reply->nonblocking;
-    }
-    SERVER_END_REQ;
+        SERVER_START_REQ( recv_socket )
+        {
+            req->force_async = force_async;
+            req->async  = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) );
+            req->oob    = !!(unix_flags & MSG_OOB);
+            req->fixup_type = async->fixup_type;
+            status = wine_server_call( req );
+            if (status == STATUS_MORE_PROCESSING_REQUIRED)
+            {
+                async->fixup_type = reply->fixup_type;
+                continue;
+            }
+            wait_handle = wine_server_ptr_handle( reply->wait );
+            options     = reply->options;
+            nonblocking = reply->nonblocking;
+        }
+        SERVER_END_REQ;
+    } while (status == STATUS_MORE_PROCESSING_REQUIRED);
 
     alerted = status == STATUS_ALERTED;
     if (alerted)
diff --git a/server/protocol.def b/server/protocol.def
index a8beccaf468..e1e5f3d9faa 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1446,17 +1446,23 @@ enum server_fd_type
     file_pos_t   count;         /* count of bytes to unlock */
 @END
 
-
 /* Perform a recv on a socket */
 @REQ(recv_socket)
     int          oob;           /* are we receiving OOB data? */
     async_data_t async;         /* async I/O parameters */
     int          force_async;   /* Force asynchronous mode? */
+    int          fixup_type;    /* protocol fixup type returned previously */
 @REPLY
     obj_handle_t wait;          /* handle to wait on for blocking recv */
     unsigned int options;       /* device open options */
     int          nonblocking;   /* is socket non-blocking? */
+    int          fixup_type;    /* protocol fixup type (see below) */
 @END
+enum sock_prot_fixup_type
+{
+    SOCK_PROT_FIXUP_NONE,
+    SOCK_PROT_FIXUP_ICMP_OVER_DGRAM,
+};
 
 
 /* Perform a send on a socket */
diff --git a/server/sock.c b/server/sock.c
index 2f1b33a333d..5c09c3bff3b 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -216,6 +216,7 @@ struct sock
     unsigned int        sndbuf;      /* advisory send buffer size */
     unsigned int        rcvtimeo;    /* receive timeout in ms */
     unsigned int        sndtimeo;    /* send timeout in ms */
+    enum sock_prot_fixup_type fixup_type; /* protocol fixup performed */
     unsigned int        rd_shutdown : 1; /* is the read end shut down? */
     unsigned int        wr_shutdown : 1; /* is the write end shut down? */
     unsigned int        wr_shutdown_pending : 1; /* is a write shutdown pending? */
@@ -1551,6 +1552,7 @@ static struct sock *create_socket(void)
     sock->sndbuf = 0;
     sock->rcvtimeo = 0;
     sock->sndtimeo = 0;
+    sock->fixup_type = SOCK_PROT_FIXUP_NONE;
     init_async_queue( &sock->read_q );
     init_async_queue( &sock->write_q );
     init_async_queue( &sock->ifchange_q );
@@ -1667,6 +1669,24 @@ static int init_socket( struct sock *sock, int family, int type, int protocol, u
     }
 
     sockfd = socket( unix_family, unix_type, unix_protocol );
+
+#ifdef linux
+    if (sockfd == -1 && errno == EPERM && unix_family == AF_INET
+        && unix_type == SOCK_RAW && unix_protocol == IPPROTO_ICMP)
+    {
+        sockfd = socket( unix_family, SOCK_DGRAM, unix_protocol );
+        if (sockfd != -1)
+        {
+            const int val = 1;
+
+            sock->fixup_type = SOCK_PROT_FIXUP_ICMP_OVER_DGRAM;
+            setsockopt( sockfd, IPPROTO_IP, IP_RECVTTL, (const char *)&val, sizeof(val) );
+            setsockopt( sockfd, IPPROTO_IP, IP_RECVTOS, (const char *)&val, sizeof(val) );
+            setsockopt( sockfd, IPPROTO_IP, IP_PKTINFO, (const char *)&val, sizeof(val) );
+        }
+    }
+#endif
+
     if (sockfd == -1)
     {
         if (errno == EINVAL) set_win32_error( WSAESOCKTNOSUPPORT );
@@ -3459,6 +3479,18 @@ DECL_HANDLER(recv_socket)
     struct fd *fd;
 
     if (!sock) return;
+
+    if (sock->fixup_type != req->fixup_type)
+    {
+        /* Protocol fixup information should be reflected in client's async data before the
+         * async is delivered to process. So let the client set up the info and restart the
+         * call. */
+        reply->fixup_type = sock->fixup_type;
+        set_error( STATUS_MORE_PROCESSING_REQUIRED );
+        release_object( sock );
+        return;
+    }
+
     fd = sock->fd;
 
     if (!req->force_async && !sock->nonblocking && is_fd_overlapped( fd ))
-- 
GitLab


https://gitlab.winehq.org/wine/wine/-/merge_requests/384



More information about the wine-devel mailing list