Commit 0f337f3d authored by Paul Gofman's avatar Paul Gofman Committed by Alexandre Julliard

ntdll: Fixup ICMP packet id if SOCK_DGRAM fallback is used.

parent 1e35966e
......@@ -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)
{
......
......@@ -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;
......
......@@ -1797,6 +1797,37 @@ struct send_socket_reply
struct socket_send_icmp_id_request
{
struct request_header __header;
obj_handle_t handle;
unsigned short icmp_id;
unsigned short icmp_seq;
char __pad_20[4];
};
struct socket_send_icmp_id_reply
{
struct reply_header __header;
};
struct socket_get_icmp_id_request
{
struct request_header __header;
obj_handle_t handle;
unsigned short icmp_seq;
char __pad_18[6];
};
struct socket_get_icmp_id_reply
{
struct reply_header __header;
unsigned short icmp_id;
char __pad_10[6];
};
struct get_next_console_request_request
{
struct request_header __header;
......@@ -5516,6 +5547,8 @@ enum request
REQ_unlock_file,
REQ_recv_socket,
REQ_send_socket,
REQ_socket_send_icmp_id,
REQ_socket_get_icmp_id,
REQ_get_next_console_request,
REQ_read_directory_changes,
REQ_read_change,
......@@ -5799,6 +5832,8 @@ union generic_request
struct unlock_file_request unlock_file_request;
struct recv_socket_request recv_socket_request;
struct send_socket_request send_socket_request;
struct socket_send_icmp_id_request socket_send_icmp_id_request;
struct socket_get_icmp_id_request socket_get_icmp_id_request;
struct get_next_console_request_request get_next_console_request_request;
struct read_directory_changes_request read_directory_changes_request;
struct read_change_request read_change_request;
......@@ -6080,6 +6115,8 @@ union generic_reply
struct unlock_file_reply unlock_file_reply;
struct recv_socket_reply recv_socket_reply;
struct send_socket_reply send_socket_reply;
struct socket_send_icmp_id_reply socket_send_icmp_id_reply;
struct socket_get_icmp_id_reply socket_get_icmp_id_reply;
struct get_next_console_request_reply get_next_console_request_reply;
struct read_directory_changes_reply read_directory_changes_reply;
struct read_change_reply read_change_reply;
......@@ -6304,7 +6341,7 @@ union generic_reply
/* ### protocol_version begin ### */
#define SERVER_PROTOCOL_VERSION 753
#define SERVER_PROTOCOL_VERSION 754
/* ### protocol_version end ### */
......
......@@ -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 */
......
......@@ -175,6 +175,8 @@ DECL_HANDLER(lock_file);
DECL_HANDLER(unlock_file);
DECL_HANDLER(recv_socket);
DECL_HANDLER(send_socket);
DECL_HANDLER(socket_send_icmp_id);
DECL_HANDLER(socket_get_icmp_id);
DECL_HANDLER(get_next_console_request);
DECL_HANDLER(read_directory_changes);
DECL_HANDLER(read_change);
......@@ -457,6 +459,8 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_unlock_file,
(req_handler)req_recv_socket,
(req_handler)req_send_socket,
(req_handler)req_socket_send_icmp_id,
(req_handler)req_socket_get_icmp_id,
(req_handler)req_get_next_console_request,
(req_handler)req_read_directory_changes,
(req_handler)req_read_change,
......@@ -1059,6 +1063,16 @@ C_ASSERT( FIELD_OFFSET(struct send_socket_reply, wait) == 8 );
C_ASSERT( FIELD_OFFSET(struct send_socket_reply, options) == 12 );
C_ASSERT( FIELD_OFFSET(struct send_socket_reply, nonblocking) == 16 );
C_ASSERT( sizeof(struct send_socket_reply) == 24 );
C_ASSERT( FIELD_OFFSET(struct socket_send_icmp_id_request, handle) == 12 );
C_ASSERT( FIELD_OFFSET(struct socket_send_icmp_id_request, icmp_id) == 16 );
C_ASSERT( FIELD_OFFSET(struct socket_send_icmp_id_request, icmp_seq) == 18 );
C_ASSERT( sizeof(struct socket_send_icmp_id_request) == 24 );
C_ASSERT( sizeof(struct socket_send_icmp_id_reply) == 8 );
C_ASSERT( FIELD_OFFSET(struct socket_get_icmp_id_request, handle) == 12 );
C_ASSERT( FIELD_OFFSET(struct socket_get_icmp_id_request, icmp_seq) == 16 );
C_ASSERT( sizeof(struct socket_get_icmp_id_request) == 24 );
C_ASSERT( FIELD_OFFSET(struct socket_get_icmp_id_reply, icmp_id) == 8 );
C_ASSERT( sizeof(struct socket_get_icmp_id_reply) == 16 );
C_ASSERT( FIELD_OFFSET(struct get_next_console_request_request, handle) == 12 );
C_ASSERT( FIELD_OFFSET(struct get_next_console_request_request, signal) == 16 );
C_ASSERT( FIELD_OFFSET(struct get_next_console_request_request, read) == 20 );
......
......@@ -168,6 +168,8 @@ enum connection_state
SOCK_CONNECTIONLESS,
};
#define MAX_ICMP_HISTORY_LENGTH 8
struct sock
{
struct object obj; /* object header */
......@@ -215,6 +217,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? */
......@@ -1502,6 +1511,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 );
......@@ -3589,3 +3599,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 );
}
......@@ -2055,6 +2055,24 @@ static void dump_send_socket_reply( const struct send_socket_reply *req )
fprintf( stderr, ", nonblocking=%d", req->nonblocking );
}
static void dump_socket_send_icmp_id_request( const struct socket_send_icmp_id_request *req )
{
fprintf( stderr, " handle=%04x", req->handle );
fprintf( stderr, ", icmp_id=%04x", req->icmp_id );
fprintf( stderr, ", icmp_seq=%04x", req->icmp_seq );
}
static void dump_socket_get_icmp_id_request( const struct socket_get_icmp_id_request *req )
{
fprintf( stderr, " handle=%04x", req->handle );
fprintf( stderr, ", icmp_seq=%04x", req->icmp_seq );
}
static void dump_socket_get_icmp_id_reply( const struct socket_get_icmp_id_reply *req )
{
fprintf( stderr, " icmp_id=%04x", req->icmp_id );
}
static void dump_get_next_console_request_request( const struct get_next_console_request_request *req )
{
fprintf( stderr, " handle=%04x", req->handle );
......@@ -4548,6 +4566,8 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_unlock_file_request,
(dump_func)dump_recv_socket_request,
(dump_func)dump_send_socket_request,
(dump_func)dump_socket_send_icmp_id_request,
(dump_func)dump_socket_get_icmp_id_request,
(dump_func)dump_get_next_console_request_request,
(dump_func)dump_read_directory_changes_request,
(dump_func)dump_read_change_request,
......@@ -4827,6 +4847,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
NULL,
(dump_func)dump_recv_socket_reply,
(dump_func)dump_send_socket_reply,
NULL,
(dump_func)dump_socket_get_icmp_id_reply,
(dump_func)dump_get_next_console_request_reply,
NULL,
(dump_func)dump_read_change_reply,
......@@ -5106,6 +5128,8 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"unlock_file",
"recv_socket",
"send_socket",
"socket_send_icmp_id",
"socket_get_icmp_id",
"get_next_console_request",
"read_directory_changes",
"read_change",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment