Paul Gofman : ntdll: Support SOCK_RAW / IPPROTO_ICMP fallback over SOCK_DGRAM.

Alexandre Julliard julliard at winehq.org
Mon Jul 18 15:44:45 CDT 2022


Module: wine
Branch: master
Commit: 1e35966eb57db7060d0c459bc7aaac2f6cad7442
URL:    https://gitlab.winehq.org/wine/wine/-/commit/1e35966eb57db7060d0c459bc7aaac2f6cad7442

Author: Paul Gofman <pgofman at codeweavers.com>
Date:   Mon Jul  4 21:15:38 2022 -0500

ntdll: Support SOCK_RAW / IPPROTO_ICMP fallback over SOCK_DGRAM.

Signed-off-by: Paul Gofman <pgofman at codeweavers.com>

---

 dlls/ntdll/unix/socket.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++-
 server/sock.c            |  17 ++++++++
 2 files changed, 125 insertions(+), 1 deletion(-)

diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index ef3c8494c3d..53b4ac9dc48 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;
+    BOOL icmp_over_dgram;
     struct iovec iov[1];
 };
 
@@ -566,6 +567,89 @@ 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 ssize_t fixup_icmp_over_dgram( struct msghdr *hdr, union unix_sockaddr *unix_addr,
+                                      ssize_t recv_len, NTSTATUS *status )
+{
+    unsigned int tot_len = sizeof(struct ip_hdr) + recv_len;
+    struct cmsghdr *cmsg;
+    struct ip_hdr ip_h;
+    size_t buf_len;
+    char *buf;
+
+    if (hdr->msg_iovlen != 1)
+    {
+        FIXME( "Buffer count %zu is not supported for ICMP fixup.\n", (size_t)hdr->msg_iovlen );
+        return recv_len;
+    }
+
+    buf = hdr->msg_iov[0].iov_base;
+    buf_len = hdr->msg_iov[0].iov_len;
+
+    if (recv_len + sizeof(ip_h) > buf_len)
+        *status = STATUS_BUFFER_OVERFLOW;
+
+    if (buf_len < sizeof(ip_h))
+    {
+        recv_len = buf_len;
+    }
+    else
+    {
+        recv_len = min( recv_len, buf_len - sizeof(ip_h) );
+        memmove( buf + sizeof(ip_h), buf, recv_len );
+        recv_len += 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), buf_len ));
+
+    return recv_len;
+}
+
 static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *size )
 {
 #ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
@@ -577,7 +661,7 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz
     ssize_t ret;
 
     memset( &hdr, 0, sizeof(hdr) );
-    if (async->addr)
+    if (async->addr || async->icmp_over_dgram)
     {
         hdr.msg_name = &unix_addr.addr;
         hdr.msg_namelen = sizeof(unix_addr);
@@ -602,9 +686,14 @@ 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->icmp_over_dgram)
+        ret = fixup_icmp_over_dgram( &hdr, &unix_addr, ret, &status );
 
     if (async->control)
     {
+        if (async->icmp_over_dgram)
+            FIXME( "May return extra control headers.\n" );
+
         if (in_wow64_call())
         {
             char control_buffer64[512];
@@ -675,6 +764,23 @@ static BOOL async_recv_proc( void *user, ULONG_PTR *info, NTSTATUS *status )
     return TRUE;
 }
 
+static BOOL is_icmp_over_dgram( int fd )
+{
+#ifdef linux
+    socklen_t len;
+    int val;
+
+    len = sizeof(val);
+    if (getsockopt( fd, SOL_SOCKET, SO_PROTOCOL, (char *)&val, &len ) || val != IPPROTO_ICMP)
+        return FALSE;
+
+    len = sizeof(val);
+    return !getsockopt( fd, SOL_SOCKET, SO_TYPE, (char *)&val, &len ) && val == SOCK_DGRAM;
+#else
+    return FALSE;
+#endif
+}
+
 static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io,
                            int fd, struct async_recv_ioctl *async, int force_async )
 {
@@ -777,6 +883,7 @@ static NTSTATUS sock_ioctl_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap
     async->addr = addr;
     async->addr_len = addr_len;
     async->ret_flags = ret_flags;
+    async->icmp_over_dgram = is_icmp_over_dgram( fd );
 
     return sock_recv( handle, event, apc, apc_user, io, fd, async, force_async );
 }
diff --git a/server/sock.c b/server/sock.c
index fed8e3b3093..dbb6d231a46 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -1618,6 +1618,23 @@ static int init_socket( struct sock *sock, int family, int type, int protocol )
     }
 
     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;
+
+            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 );




More information about the wine-cvs mailing list