[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