[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