From: Paul Gofman pgofman@codeweavers.com
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntdll/unix/socket.c | 98 +++++++++++++++++++++++++++++++++++++++- dlls/ws2_32/tests/sock.c | 2 +- server/protocol.def | 18 ++++++++ server/sock.c | 54 ++++++++++++++++++++++ 4 files changed, 169 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 53b4ac9dc48..e938eb9b627 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -581,10 +581,55 @@ 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 ssize_t fixup_icmp_over_dgram( struct msghdr *hdr, union unix_sockaddr *unix_addr, - ssize_t recv_len, NTSTATUS *status ) + HANDLE handle, ssize_t recv_len, NTSTATUS *status ) { unsigned int tot_len = sizeof(struct ip_hdr) + recv_len; + struct icmp_hdr *icmp_h = NULL; + NTSTATUS fixup_status; struct cmsghdr *cmsg; struct ip_hdr ip_h; size_t buf_len; @@ -610,6 +655,8 @@ static ssize_t fixup_icmp_over_dgram( struct msghdr *hdr, union unix_sockaddr *u { recv_len = min( recv_len, buf_len - sizeof(ip_h) ); memmove( buf + sizeof(ip_h), buf, recv_len ); + if (recv_len >= sizeof(struct icmp_hdr)) + icmp_h = (struct icmp_hdr *)(buf + sizeof(ip_h)); recv_len += sizeof(ip_h); } memset( &ip_h, 0, sizeof(ip_h) ); @@ -645,6 +692,25 @@ static ssize_t fixup_icmp_over_dgram( struct msghdr *hdr, union unix_sockaddr *u #endif } } + if (icmp_h) + { + SERVER_START_REQ( socket_get_icmp_id ) + { + req->handle = wine_server_obj_handle( 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, recv_len - sizeof(ip_h) ); + } + } memcpy( buf, &ip_h, min( sizeof(ip_h), buf_len ));
return recv_len; @@ -687,7 +753,7 @@ 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 ); + ret = fixup_icmp_over_dgram( &hdr, &unix_addr, async->io.handle, ret, &status );
if (async->control) { @@ -1001,6 +1067,31 @@ static BOOL async_send_proc( void *user, ULONG_PTR *info, NTSTATUS *status ) return TRUE; }
+static void sock_save_icmp_id( struct async_send_ioctl *async ) +{ + unsigned short id, seq; + struct icmp_hdr *h; + + if (async->count != 1 || async->iov[0].iov_len < sizeof(*h)) + { + FIXME( "ICMP over DGRAM fixup is not supported for count %u, len %zu.\n", async->count, async->iov[0].iov_len ); + return; + } + + h = async->iov[0].iov_base; + id = h->un.echo.id; + seq = h->un.echo.sequence; + SERVER_START_REQ( socket_send_icmp_id ) + { + req->handle = wine_server_obj_handle( async->io.handle ); + req->icmp_id = id; + req->icmp_seq = seq; + if (wine_server_call( req )) + WARN( "socket_fixup_send_data failed.\n" ); + } + SERVER_END_REQ; +} + static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io, int fd, struct async_send_ioctl *async, int force_async ) { @@ -1021,6 +1112,9 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi } SERVER_END_REQ;
+ if (!NT_ERROR(status) && is_icmp_over_dgram( fd )) + sock_save_icmp_id( async ); + alerted = status == STATUS_ALERTED; if (alerted) { diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index d24556ccdd2..32872e1b683 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -12952,7 +12952,7 @@ static void test_icmp(void)
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 6cb6275f2f2..a8044582ef8 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1470,6 +1470,24 @@ enum server_fd_type @END
+/* Store ICMP id for ICMP over datagram fixup */ +@REQ(socket_send_icmp_id) + obj_handle_t handle; /* socket handle */ + unsigned short icmp_id; /* ICMP packet id */ + unsigned short icmp_seq; /* ICMP packed sequence */ +@REPLY +@END + + +/* Get ICMP id for ICMP over datagram fixup */ +@REQ(socket_get_icmp_id) + obj_handle_t handle; /* socket handle */ + unsigned short icmp_seq; /* ICMP packed sequence */ +@REPLY + unsigned short icmp_id; /* ICMP packet id */ +@END + + /* Retrieve the next pending console ioctl request */ @REQ(get_next_console_request) obj_handle_t handle; /* console server handle */ diff --git a/server/sock.c b/server/sock.c index f3f43c58426..f413c45ef23 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 */ @@ -216,6 +218,13 @@ struct sock unsigned int sndbuf; /* advisory send buffer size */ unsigned int rcvtimeo; /* receive timeout in ms */ unsigned int sndtimeo; /* send timeout in ms */ + 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? */ @@ -1506,6 +1515,7 @@ static struct sock *create_socket(void) sock->sndbuf = 0; sock->rcvtimeo = 0; sock->sndtimeo = 0; + sock->icmp_fixup_data_len = 0; init_async_queue( &sock->read_q ); init_async_queue( &sock->write_q ); init_async_queue( &sock->ifchange_q ); @@ -3594,3 +3604,47 @@ DECL_HANDLER(send_socket) } release_object( sock ); } + +DECL_HANDLER(socket_send_icmp_id) +{ + struct sock *sock = (struct sock *)get_handle_obj( current->process, req->handle, 0, &sock_ops ); + + if (!sock) return; + + if (sock->icmp_fixup_data_len == MAX_ICMP_HISTORY_LENGTH) + { + memmove( sock->icmp_fixup_data, sock->icmp_fixup_data + 1, + sizeof(*sock->icmp_fixup_data) * (MAX_ICMP_HISTORY_LENGTH - 1) ); + --sock->icmp_fixup_data_len; + } + + 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_icmp_id) +{ + struct sock *sock = (struct sock *)get_handle_obj( current->process, req->handle, 0, &sock_ops ); + unsigned int i; + + if (!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 ); +}