[PATCH 4/4] ntdll: Fixup ICMP packet id if SOCK_DGRAM fallback is used.

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


From: Paul Gofman <pgofman at codeweavers.com>

Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 dlls/ntdll/unix/socket.c | 95 ++++++++++++++++++++++++++++++++++++++++
 dlls/ws2_32/tests/sock.c |  2 +-
 server/protocol.def      | 23 +++++++++-
 server/sock.c            | 70 +++++++++++++++++++++++++++++
 4 files changed, 187 insertions(+), 3 deletions(-)

diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index d200c0c999c..d19efba14fa 100644
--- a/dlls/ntdll/unix/socket.c
+++ b/dlls/ntdll/unix/socket.c
@@ -581,6 +581,49 @@ struct ip_hdr
     ULONG daddr;
 };
 
+struct icmp_hdr
+{
+    BYTE type;
+    BYTE code;
+    UINT16 checksum;
+    union
+    {
+        struct
+        {
+            UINT16 id;
+            UINT16 sequence;
+        } echo;
+    } un;
+};
+
+/* rfc 1071 checksum */
+static unsigned short chksum(BYTE *data, unsigned int count)
+{
+    unsigned int sum = 0, carry = 0;
+    unsigned short check, s;
+
+    while (count > 1)
+    {
+        s = *(unsigned short *)data;
+        data += 2;
+        sum += carry;
+        sum += s;
+        carry = s > sum;
+        count -= 2;
+    }
+    sum += carry; /* This won't produce another carry */
+    sum = (sum & 0xffff) + (sum >> 16);
+
+    if (count) sum += *data; /* LE-only */
+
+    sum = (sum & 0xffff) + (sum >> 16);
+    /* fold in any carry */
+    sum = (sum & 0xffff) + (sum >> 16);
+
+    check = ~sum;
+    return check;
+}
+
 static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *size )
 {
 #ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
@@ -628,6 +671,8 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz
         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 icmp_hdr *icmp_h = NULL;
+        NTSTATUS fixup_status;
         struct cmsghdr *cmsg;
         struct ip_hdr ip_h;
 
@@ -642,6 +687,8 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz
         {
             ret = min( ret, len - sizeof(ip_h) );
             memmove( buf + sizeof(ip_h), buf, ret );
+            if (ret >= sizeof(struct icmp_hdr))
+                icmp_h = (struct icmp_hdr *)(buf + sizeof(ip_h));
             ret += sizeof(ip_h);
         }
         memset( &ip_h, 0, sizeof(ip_h) );
@@ -677,6 +724,24 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz
 #endif
             }
         }
+        if (icmp_h)
+        {
+            SERVER_START_REQ( socket_get_fixup_data )
+            {
+                req->handle = wine_server_obj_handle( async->io.handle );
+                req->icmp_seq = icmp_h->un.echo.sequence;
+                if (!(fixup_status = wine_server_call( req )))
+                    icmp_h->un.echo.id = reply->icmp_id;
+                else
+                    WARN( "socket_get_fixup_data returned %#x.\n", fixup_status );
+            }
+            SERVER_END_REQ;
+            if (!fixup_status)
+            {
+                icmp_h->checksum = 0;
+                icmp_h->checksum = chksum( (BYTE *)icmp_h, ret - sizeof(ip_h) );
+            }
+        }
         memcpy( buf, &ip_h, min( sizeof(ip_h), len ));
     }
 
@@ -957,6 +1022,7 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
                            IO_STATUS_BLOCK *io, int fd, const void *buffers_ptr, unsigned int count,
                            const struct WS_sockaddr *addr, unsigned int addr_len, int unix_flags, int force_async )
 {
+    enum sock_prot_fixup_type fixup_type;
     struct async_send_ioctl *async;
     ULONG_PTR information;
     HANDLE wait_handle;
@@ -1006,9 +1072,38 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
         wait_handle = wine_server_ptr_handle( reply->wait );
         options     = reply->options;
         nonblocking = reply->nonblocking;
+        fixup_type  = reply->fixup_type;
     }
     SERVER_END_REQ;
 
+    if (fixup_type == SOCK_PROT_FIXUP_ICMP_OVER_DGRAM && async->count
+        && (!status || status == STATUS_ALERTED || status == STATUS_PENDING))
+    {
+        unsigned short id, seq;
+        struct icmp_hdr *h;
+        NTSTATUS ret;
+
+        if (async->iov[0].iov_len < sizeof(*h))
+        {
+            FIXME( "ICMP over DGRAM fixup is not supported for count %u, len %zu.\n", count, async->iov[0].iov_len );
+        }
+        else
+        {
+            h = async->iov[0].iov_base;
+            id = h->un.echo.id;
+            seq = h->un.echo.sequence;
+            SERVER_START_REQ( socket_fixup_send_data )
+            {
+                req->handle = wine_server_obj_handle( handle );
+                req->icmp_id = id;
+                req->icmp_seq = seq;
+                ret = wine_server_call( req );
+            }
+            SERVER_END_REQ;
+            if (ret) WARN( "socket_fixup_send_data returned %#x.\n", ret );
+        }
+    }
+
     alerted = status == STATUS_ALERTED;
     if (alerted)
     {
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 03677b72080..7e64c52c16b 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -12952,7 +12952,7 @@ static void test_icmp(const char *dest_addr)
 
     ok(icmp_h->type == ICMP4_ECHO_REPLY, "got type %#x.\n", icmp_h->type);
     ok(!icmp_h->code, "got code %#x.\n", icmp_h->code);
-    todo_wine ok(icmp_h->un.echo.id == 0xbeaf, "got echo id %#x.\n", icmp_h->un.echo.id);
+    ok(icmp_h->un.echo.id == 0xbeaf, "got echo id %#x.\n", icmp_h->un.echo.id);
     ok(icmp_h->un.echo.sequence == 2, "got echo sequence %#x.\n", icmp_h->un.echo.sequence);
 
     recv_checksum = icmp_h->checksum;
diff --git a/server/protocol.def b/server/protocol.def
index e1e5f3d9faa..ba367215bcd 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1467,12 +1467,31 @@ enum sock_prot_fixup_type
 
 /* Perform a send on a socket */
 @REQ(send_socket)
-    async_data_t async;         /* async I/O parameters */
-    int          force_async;   /* Force asynchronous mode? */
+    async_data_t   async;       /* async I/O parameters */
+    int            force_async; /* Force asynchronous mode? */
 @REPLY
     obj_handle_t wait;          /* handle to wait on for blocking send */
     unsigned int options;       /* device open options */
     int          nonblocking;   /* is socket non-blocking? */
+    int          fixup_type;    /* socket protocol fixup */
+ at END
+
+
+/* Store info needed for protocol fixup */
+ at REQ(socket_fixup_send_data)
+    obj_handle_t   handle;        /* socket handle */
+    unsigned short icmp_id;       /* ICMP packet id */
+    unsigned short icmp_seq;      /* ICMP packed sequence */
+ at REPLY
+ at END
+
+
+/* Retrieve protocol fixup info */
+ at REQ(socket_get_fixup_data)
+    obj_handle_t   handle;        /* socket handle */
+    unsigned short icmp_seq;      /* ICMP packed sequence */
+ at REPLY
+    unsigned short icmp_id;       /* ICMP packet id */
 @END
 
 
diff --git a/server/sock.c b/server/sock.c
index 5c09c3bff3b..7fb4bd6c2f6 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -168,6 +168,8 @@ enum connection_state
     SOCK_CONNECTIONLESS,
 };
 
+#define MAX_ICMP_HISTORY_LENGTH 8
+
 struct sock
 {
     struct object       obj;         /* object header */
@@ -217,6 +219,13 @@ struct sock
     unsigned int        rcvtimeo;    /* receive timeout in ms */
     unsigned int        sndtimeo;    /* send timeout in ms */
     enum sock_prot_fixup_type fixup_type; /* protocol fixup performed */
+    struct
+    {
+        unsigned short icmp_id;
+        unsigned short icmp_seq;
+    }
+    icmp_fixup_data[MAX_ICMP_HISTORY_LENGTH]; /* Sent ICMP packets history used to fixup reply id. */
+    unsigned int        icmp_fixup_data_len;  /* Sent ICMP packets history length. */
     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? */
@@ -1553,6 +1562,7 @@ static struct sock *create_socket(void)
     sock->rcvtimeo = 0;
     sock->sndtimeo = 0;
     sock->fixup_type = SOCK_PROT_FIXUP_NONE;
+    sock->icmp_fixup_data_len = 0;
     init_async_queue( &sock->read_q );
     init_async_queue( &sock->write_q );
     init_async_queue( &sock->ifchange_q );
@@ -3574,6 +3584,8 @@ DECL_HANDLER(send_socket)
     if (!sock) return;
     fd = sock->fd;
 
+    reply->fixup_type = sock->fixup_type;
+
     if (sock->type == WS_SOCK_DGRAM && !sock->bound)
     {
         union unix_sockaddr unix_addr;
@@ -3653,3 +3665,61 @@ DECL_HANDLER(send_socket)
     }
     release_object( sock );
 }
+
+DECL_HANDLER(socket_fixup_send_data)
+{
+    struct sock *sock = (struct sock *)get_handle_obj( current->process, req->handle, 0, &sock_ops );
+
+    if (!sock) return;
+
+    if (sock->fixup_type != SOCK_PROT_FIXUP_ICMP_OVER_DGRAM)
+    {
+        set_error( STATUS_INVALID_PARAMETER );
+        release_object( sock );
+        return;
+    }
+
+    if (sock->icmp_fixup_data_len == MAX_ICMP_HISTORY_LENGTH)
+    {
+        set_error( STATUS_BUFFER_OVERFLOW );
+        release_object( sock );
+        return;
+    }
+
+    sock->icmp_fixup_data[sock->icmp_fixup_data_len].icmp_id = req->icmp_id;
+    sock->icmp_fixup_data[sock->icmp_fixup_data_len].icmp_seq = req->icmp_seq;
+    ++sock->icmp_fixup_data_len;
+
+    release_object( sock );
+}
+
+DECL_HANDLER(socket_get_fixup_data)
+{
+    struct sock *sock = (struct sock *)get_handle_obj( current->process, req->handle, 0, &sock_ops );
+    unsigned int i;
+
+    if (!sock) return;
+
+    if (sock->fixup_type != SOCK_PROT_FIXUP_ICMP_OVER_DGRAM)
+    {
+        set_error( STATUS_INVALID_PARAMETER );
+        release_object( sock );
+        return;
+    }
+
+    for (i = 0; i < sock->icmp_fixup_data_len; ++i)
+    {
+        if (sock->icmp_fixup_data[i].icmp_seq == req->icmp_seq)
+        {
+            reply->icmp_id = sock->icmp_fixup_data[i].icmp_id;
+            --sock->icmp_fixup_data_len;
+            memmove( &sock->icmp_fixup_data[i], &sock->icmp_fixup_data[i + 1],
+                     (sock->icmp_fixup_data_len - i) * sizeof(*sock->icmp_fixup_data) );
+            release_object( sock );
+            return;
+        }
+    }
+
+    set_error( STATUS_NOT_FOUND );
+    release_object( sock );
+}
-- 
GitLab

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



More information about the wine-devel mailing list