From: Paul Gofman pgofman@codeweavers.com
Signed-off-by: Paul Gofman pgofman@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 */ +@END + + +/* Store info needed for protocol fixup */ +@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 */ +@REPLY +@END + + +/* Retrieve protocol fixup info */ +@REQ(socket_get_fixup_data) + obj_handle_t handle; /* socket handle */ + unsigned short icmp_seq; /* ICMP packed sequence */ +@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 ); +}