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