Because iphlpapi has no opportunity to convert the reply in async mode.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/iphlpapi_main.c | 44 +----- dlls/nsiproxy.sys/device.c | 36 ++++- dlls/nsiproxy.sys/icmp_echo.c | 203 ++++++++++++++++++--------- dlls/nsiproxy.sys/nsiproxy_private.h | 39 +++++ include/wine/nsi.h | 21 +-- 5 files changed, 212 insertions(+), 131 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index d9a85be..8a31241 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4579,33 +4579,6 @@ DWORD WINAPI IcmpParseReplies( void *reply, DWORD reply_size ) return num_pkts; }
-/************************************************************************* - * icmpv4_echo_reply_fixup - * - * Convert struct nsiproxy_icmpv4_echo_reply into ICMP_ECHO_REPLY. - * - * This is necessary due to the different sizes of ICMP_ECHO_REPLY on - * 32 and 64-bits. Despite mention of ICMP_ECHO_REPLY32, 64-bit Windows - * actually does return a full 64-bit version. - */ -static void icmpv4_echo_reply_fixup( ICMP_ECHO_REPLY *dst, struct nsiproxy_icmp_echo_reply *reply ) -{ - dst->Address = reply->addr.Ipv4.sin_addr.s_addr; - dst->Status = reply->status; - dst->RoundTripTime = reply->round_trip_time; - dst->DataSize = reply->data_size; - dst->Reserved = reply->num_of_pkts; - dst->Data = (BYTE *)(dst + 1) + ((reply->opts.options_size + 3) & ~3); - dst->Options.Ttl = reply->opts.ttl; - dst->Options.Tos = reply->opts.tos; - dst->Options.Flags = reply->opts.flags; - dst->Options.OptionsSize = reply->opts.options_size; - dst->Options.OptionsData = (BYTE *)(reply + 1); - - memcpy( dst->Options.OptionsData, (BYTE *)reply + reply->opts.options_offset, reply->opts.options_size ); - memcpy( dst->Data, (BYTE *)reply + reply->data_offset, reply->data_size ); -} - /*********************************************************************** * IcmpSendEcho (IPHLPAPI.@) */ @@ -4636,9 +4609,8 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r void *reply, DWORD reply_size, DWORD timeout ) { struct icmp_handle_data *data = (struct icmp_handle_data *)handle; - DWORD opt_size, in_size, ret = 0, out_size; + DWORD opt_size, in_size, ret = 0; struct nsiproxy_icmp_echo *in; - struct nsiproxy_icmp_echo_reply *out; HANDLE request_event; IO_STATUS_BLOCK iosb; NTSTATUS status; @@ -4658,17 +4630,15 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r opt_size = opts ? (opts->OptionsSize + 3) & ~3 : 0; in_size = FIELD_OFFSET(struct nsiproxy_icmp_echo, data[opt_size + request_size]); in = heap_alloc_zero( in_size ); - out_size = reply_size - sizeof(ICMP_ECHO_REPLY) + sizeof(*out); - out = heap_alloc( out_size );
- if (!in || !out) + if (!in) { - heap_free( out ); - heap_free( in ); SetLastError( IP_NO_RESOURCES ); return 0; }
+ in->user_reply_ptr = (ULONG_PTR)reply; + in->bits = sizeof(void*) * 8; in->src.Ipv4.sin_family = AF_INET; in->src.Ipv4.sin_addr.s_addr = src; in->dst.Ipv4.sin_family = AF_INET; @@ -4689,19 +4659,15 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r
status = NtDeviceIoControlFile( data->nsi_device, request_event, NULL, NULL, &iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, in, in_size, - out, out_size ); + reply, reply_size );
if (status == STATUS_PENDING && !WaitForSingleObject( request_event, INFINITE )) status = iosb.Status;
if (!status) - { - icmpv4_echo_reply_fixup( reply, out ); ret = IcmpParseReplies( reply, reply_size ); - }
CloseHandle( request_event ); - heap_free( out ); heap_free( in );
if (status) SetLastError( RtlNtStatusToDosError( status ) ); diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index 90a7a3d..4ace66d 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -31,6 +31,7 @@ #include "netiodef.h" #include "wine/nsi.h" #include "wine/debug.h" +#include "wine/heap.h" #include "wine/unixlib.h"
#include "nsiproxy_private.h" @@ -211,6 +212,13 @@ static void WINAPI icmp_echo_cancel( DEVICE_OBJECT *device, IRP *irp ) LeaveCriticalSection( &nsiproxy_cs ); }
+static int icmp_echo_reply_struct_len( ULONG family, ULONG bits ) +{ + if (family == AF_INET) + return (bits == 32) ? sizeof(struct icmp_echo_reply_32) : sizeof(struct icmp_echo_reply_64); + return 0; +} + static NTSTATUS nsiproxy_icmp_echo( IRP *irp ) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); @@ -222,7 +230,7 @@ static NTSTATUS nsiproxy_icmp_echo( IRP *irp )
if (in_len < offsetof(struct nsiproxy_icmp_echo, data[0]) || in_len < offsetof(struct nsiproxy_icmp_echo, data[((in->opt_size + 3) & ~3) + in->req_size]) || - out_len < sizeof(struct nsiproxy_icmp_echo_reply)) + out_len < icmp_echo_reply_struct_len( in->dst.si_family, in->bits )) return STATUS_INVALID_PARAMETER;
switch (in->dst.si_family) @@ -326,8 +334,10 @@ static DWORD WINAPI listen_thread_proc( void *arg )
TRACE( "\n" );
+ params.user_reply_ptr = in->user_reply_ptr; params.handle = irp_get_icmp_handle( irp ); params.timeout = in->timeout; + params.bits = in->bits; params.reply = irp->AssociatedIrp.SystemBuffer; params.reply_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
@@ -353,8 +363,10 @@ static DWORD WINAPI listen_thread_proc( void *arg ) static void handle_queued_send_echo( IRP *irp ) { struct nsiproxy_icmp_echo *in = (struct nsiproxy_icmp_echo *)irp->AssociatedIrp.SystemBuffer; - struct nsiproxy_icmp_echo_reply *reply = (struct nsiproxy_icmp_echo_reply *)irp->AssociatedIrp.SystemBuffer; + void *out = irp->AssociatedIrp.SystemBuffer; struct icmp_send_echo_params params; + ULONG family = in->dst.si_family; + ULONG bits = in->bits; NTSTATUS status;
TRACE( "\n" ); @@ -372,9 +384,23 @@ static void handle_queued_send_echo( IRP *irp ) irp->IoStatus.Status = status; if (status == STATUS_SUCCESS) { - memset( reply, 0, sizeof(*reply) ); - reply->status = params.ip_status; - irp->IoStatus.Information = sizeof(*reply); + if (family == AF_INET) + { + if (bits == 32) + { + struct icmp_echo_reply_32 *reply = out; + memset( reply, 0, sizeof(*reply) ); + reply->status = params.ip_status; + irp->IoStatus.Information = sizeof(*reply); + } + else + { + struct icmp_echo_reply_64 *reply = out; + memset( reply, 0, sizeof(*reply) ); + reply->status = params.ip_status; + irp->IoStatus.Information = sizeof(*reply); + } + } } IoCompleteRequest( irp, IO_NO_INCREMENT ); } diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c index 955fdfd..1e106c6 100644 --- a/dlls/nsiproxy.sys/icmp_echo.c +++ b/dlls/nsiproxy.sys/icmp_echo.c @@ -93,6 +93,20 @@ struct icmp_hdr } un; };
+struct icmp_reply_ctx +{ + SOCKADDR_INET addr; + ULONG status; + ULONG round_trip_time; + LONG data_size; + BYTE ttl; + BYTE tos; + BYTE flags; + BYTE options_size; + void *options_data; + void *data; +}; + struct family_ops; struct icmp_data { @@ -232,20 +246,22 @@ static void ipv4_linux_ping_set_socket_opts( struct icmp_data *data, struct icmp } #endif
-static int ipv4_reply_buffer_len( int reply_len ) +static int ipv4_reply_buffer_len( struct icmp_listen_params *params ) { - return sizeof(struct ip_hdr) + sizeof(struct icmp_hdr) + reply_len - sizeof(struct nsiproxy_icmp_echo_reply); + int struct_len = (params->bits == 32) ? sizeof(struct icmp_echo_reply_32) : sizeof(struct icmp_echo_reply_64); + return sizeof(struct ip_hdr) + sizeof(struct icmp_hdr) + params->reply_len - struct_len; }
#ifdef __linux__ -static int ipv4_linux_ping_reply_buffer_len( int reply_len ) +static int ipv4_linux_ping_reply_buffer_len( struct icmp_listen_params *params ) { - return sizeof(struct icmp_hdr) + reply_len - sizeof(struct nsiproxy_icmp_echo_reply); + int struct_len = (params->bits == 32) ? sizeof(struct icmp_echo_reply_32) : sizeof(struct icmp_echo_reply_64); + return sizeof(struct icmp_hdr) + params->reply_len - struct_len; } #endif
-static BOOL ipv4_parse_ip_hdr( struct msghdr *msg, int recvd, - int *ip_hdr_len, struct nsiproxy_icmp_echo_reply *reply, void **opts ) +static BOOL ipv4_parse_ip_hdr( struct msghdr *msg, int recvd, int *ip_hdr_len, + struct icmp_reply_ctx *ctx ) { struct ip_hdr *ip_hdr;
@@ -254,27 +270,27 @@ static BOOL ipv4_parse_ip_hdr( struct msghdr *msg, int recvd, if (ip_hdr->v_hl >> 4 != 4 || ip_hdr->protocol != IPPROTO_ICMP) return FALSE; *ip_hdr_len = (ip_hdr->v_hl & 0xf) << 2; if (*ip_hdr_len < sizeof(*ip_hdr)) return FALSE; - *opts = ip_hdr + 1; - reply->opts.ttl = ip_hdr->ttl; - reply->opts.tos = ip_hdr->tos; - reply->opts.flags = ip_hdr->frag_off >> 13; - reply->opts.options_size = *ip_hdr_len - sizeof(*ip_hdr); + ctx->options_data = ip_hdr + 1; + ctx->ttl = ip_hdr->ttl; + ctx->tos = ip_hdr->tos; + ctx->flags = ip_hdr->frag_off >> 13; + ctx->options_size = *ip_hdr_len - sizeof(*ip_hdr);
return TRUE; }
#ifdef __linux__ -static BOOL ipv4_linux_ping_parse_ip_hdr( struct msghdr *msg, int recvd, - int *ip_hdr_len, struct nsiproxy_icmp_echo_reply *reply, void **opts ) +static BOOL ipv4_linux_ping_parse_ip_hdr( struct msghdr *msg, int recvd, int *ip_hdr_len, + struct icmp_reply_ctx *ctx ) { struct cmsghdr *cmsg;
*ip_hdr_len = 0; - *opts = NULL; - reply->opts.ttl = 0; - reply->opts.tos = 0; - reply->opts.flags = 0; - reply->opts.options_size = 0; /* FIXME from IP_OPTIONS but will require checking for space in the reply */ + ctx->options_data = NULL; + ctx->ttl = 0; + ctx->tos = 0; + ctx->flags = 0; + ctx->options_size = 0; /* FIXME from IP_OPTIONS but will require checking for space in the reply */
for (cmsg = CMSG_FIRSTHDR( msg ); cmsg; cmsg = CMSG_NXTHDR( msg, cmsg )) { @@ -282,10 +298,10 @@ static BOOL ipv4_linux_ping_parse_ip_hdr( struct msghdr *msg, int recvd, switch (cmsg->cmsg_type) { case IP_TTL: - reply->opts.ttl = *(BYTE *)CMSG_DATA( cmsg ); + ctx->ttl = *(BYTE *)CMSG_DATA( cmsg ); break; case IP_TOS: - reply->opts.tos = *(BYTE *)CMSG_DATA( cmsg ); + ctx->tos = *(BYTE *)CMSG_DATA( cmsg ); break; } } @@ -294,7 +310,7 @@ static BOOL ipv4_linux_ping_parse_ip_hdr( struct msghdr *msg, int recvd, #endif
static int ipv4_parse_icmp_hdr_( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_size, - struct nsiproxy_icmp_echo_reply *reply, int ping_socket ) + struct icmp_reply_ctx *ctx, int ping_socket ) { static const IP_STATUS unreach_codes[] = { @@ -326,7 +342,7 @@ static int ipv4_parse_icmp_hdr_( struct icmp_data *data, struct icmp_hdr *icmp, if ((!ping_socket && icmp->un.echo.id != data->id) || icmp->un.echo.sequence != data->seq) return -1;
- reply->status = IP_SUCCESS; + ctx->status = IP_SUCCESS; return icmp_size - sizeof(*icmp);
case ICMP4_DST_UNREACH: @@ -369,24 +385,90 @@ static int ipv4_parse_icmp_hdr_( struct icmp_data *data, struct icmp_hdr *icmp, (!ping_socket && orig_icmp_hdr->un.echo.id != data->id) || orig_icmp_hdr->un.echo.sequence != data->seq) return -1;
- reply->status = status; + ctx->status = status; return 0; }
-static int ipv4_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_size, - struct nsiproxy_icmp_echo_reply *reply ) +static int ipv4_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, + int icmp_size, struct icmp_reply_ctx *ctx) { - return ipv4_parse_icmp_hdr_( data, icmp, icmp_size, reply, 0 ); + return ipv4_parse_icmp_hdr_( data, icmp, icmp_size, ctx, 0 ); }
#ifdef __linux__ -static int ipv4_linux_ping_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_size, - struct nsiproxy_icmp_echo_reply *reply ) +static int ipv4_linux_ping_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, + int icmp_size, struct icmp_reply_ctx *ctx ) { - return ipv4_parse_icmp_hdr_( data, icmp, icmp_size, reply, 1 ); + return ipv4_parse_icmp_hdr_( data, icmp, icmp_size, ctx, 1 ); } #endif
+static NTSTATUS ipv4_set_reply_ip_status( struct icmp_listen_params *params, IP_STATUS ip_status ) +{ + if (params->bits == 32) + { + struct icmp_echo_reply_32 *reply = params->reply; + memset( reply, 0, sizeof(*reply) ); + reply->status = ip_status; + params->reply_len = sizeof(*reply); + } + else + { + struct icmp_echo_reply_64 *reply = params->reply; + memset( reply, 0, sizeof(*reply) ); + reply->status = ip_status; + params->reply_len = sizeof(*reply); + } + return STATUS_SUCCESS; +} + +static void ipv4_fill_reply( struct icmp_listen_params *params, struct icmp_reply_ctx *ctx) +{ + void *options_data; + ULONG data_offset; + if (params->bits == 32) + { + struct icmp_echo_reply_32 *reply = params->reply; + data_offset = sizeof(*reply) + ((ctx->options_size + 3) & ~3); + reply->addr = ctx->addr.Ipv4.sin_addr.WS_s_addr; + reply->status = ctx->status; + reply->round_trip_time = ctx->round_trip_time; + reply->data_size = ctx->data_size; + reply->num_of_pkts = 1; + reply->data_ptr = params->user_reply_ptr + data_offset; + reply->opts.ttl = ctx->ttl; + reply->opts.tos = ctx->tos; + reply->opts.flags = ctx->flags; + reply->opts.options_size = ctx->options_size; + reply->opts.options_ptr = params->user_reply_ptr + sizeof(*reply); + options_data = reply + 1; + } + else + { + struct icmp_echo_reply_64 *reply = params->reply; + data_offset = sizeof(*reply) + ((ctx->options_size + 3) & ~3); + reply->addr = ctx->addr.Ipv4.sin_addr.WS_s_addr; + reply->status = ctx->status; + reply->round_trip_time = ctx->round_trip_time; + reply->data_size = ctx->data_size; + reply->num_of_pkts = 1; + reply->data_ptr = params->user_reply_ptr + data_offset; + reply->opts.ttl = ctx->ttl; + reply->opts.tos = ctx->tos; + reply->opts.flags = ctx->flags; + reply->opts.options_size = ctx->options_size; + reply->opts.options_ptr = params->user_reply_ptr + sizeof(*reply); + options_data = reply + 1; + } + + memcpy( options_data, ctx->options_data, ctx->options_size ); + if (ctx->options_size & 3) + memset( (char *)options_data + ctx->options_size, 0, 4 - (ctx->options_size & 3) ); + + memcpy( (char *)params->reply + data_offset, ctx->data, ctx->data_size ); + params->reply_len = data_offset + ctx->data_size; +} + struct family_ops { int family; @@ -394,11 +476,11 @@ struct family_ops void (*init_icmp_hdr)( struct icmp_data *data, struct icmp_hdr *icmp_hdr ); unsigned short (*chksum)( BYTE *data, unsigned int count ); void (*set_socket_opts)( struct icmp_data *data, struct icmp_send_echo_params *params ); - int (*reply_buffer_len)( int reply_len ); - BOOL (*parse_ip_hdr)( struct msghdr *msg, int recvd, - int *ip_hdr_len, struct nsiproxy_icmp_echo_reply *reply, void **opts ); - int (*parse_icmp_hdr)( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_len, - struct nsiproxy_icmp_echo_reply *reply ); + int (*reply_buffer_len)( struct icmp_listen_params *params ); + BOOL (*parse_ip_hdr)( struct msghdr *msg, int recvd, int *ip_hdr_len, struct icmp_reply_ctx *ctx ); + int (*parse_icmp_hdr)( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_len, struct icmp_reply_ctx *ctx ); + NTSTATUS (*set_reply_ip_status)( struct icmp_listen_params *params, IP_STATUS ip_status ); + void (*fill_reply)( struct icmp_listen_params *params, struct icmp_reply_ctx *ctx ); };
static const struct family_ops ipv4 = @@ -411,6 +493,8 @@ static const struct family_ops ipv4 = ipv4_reply_buffer_len, ipv4_parse_ip_hdr, ipv4_parse_icmp_hdr, + ipv4_set_reply_ip_status, + ipv4_fill_reply, };
#ifdef __linux__ @@ -425,6 +509,8 @@ static const struct family_ops ipv4_linux_ping = ipv4_linux_ping_reply_buffer_len, ipv4_linux_ping_parse_ip_hdr, ipv4_linux_ping_parse_icmp_hdr, + ipv4_set_reply_ip_status, + ipv4_fill_reply, }; #endif
@@ -584,16 +670,6 @@ NTSTATUS icmp_send_echo( void *args ) return params->handle ? STATUS_PENDING : STATUS_NO_MEMORY; }
-static NTSTATUS set_reply_ip_status( struct icmp_listen_params *params, IP_STATUS ip_status ) -{ - struct nsiproxy_icmp_echo_reply *reply = params->reply; - - memset( reply, 0, sizeof(*reply) ); - reply->status = ip_status; - params->reply_len = sizeof(*reply); - return STATUS_SUCCESS; -} - static int get_timeout( LARGE_INTEGER start, DWORD timeout ) { LARGE_INTEGER now, end; @@ -615,19 +691,18 @@ static ULONG get_rtt( LARGE_INTEGER start )
static NTSTATUS recv_msg( struct icmp_data *data, struct icmp_listen_params *params ) { - struct nsiproxy_icmp_echo_reply *reply = (struct nsiproxy_icmp_echo_reply *)params->reply; struct sockaddr_storage addr; + struct icmp_reply_ctx ctx; struct iovec iov[1]; BYTE cmsg_buf[1024]; struct msghdr msg = { .msg_name = &addr, .msg_namelen = sizeof(addr), .msg_iov = iov, .msg_iovlen = ARRAY_SIZE(iov), .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf) }; - int ip_hdr_len, recvd, reply_buf_len, data_size; + int ip_hdr_len, recvd, reply_buf_len; char *reply_buf; - void *opts; struct icmp_hdr *icmp_hdr;
- reply_buf_len = data->ops->reply_buffer_len( params->reply_len ); + reply_buf_len = data->ops->reply_buffer_len( params ); reply_buf = malloc( reply_buf_len ); if (!reply_buf) return STATUS_NO_MEMORY;
@@ -638,31 +713,23 @@ static NTSTATUS recv_msg( struct icmp_data *data, struct icmp_listen_params *par TRACE( "recvmsg() rets %d errno %d addr_len %d iovlen %d msg_flags %x\n", recvd, errno, msg.msg_namelen, (int)iov[0].iov_len, msg.msg_flags );
- if (!data->ops->parse_ip_hdr( &msg, recvd, &ip_hdr_len, reply, &opts )) goto skip; + if (!data->ops->parse_ip_hdr( &msg, recvd, &ip_hdr_len, &ctx )) goto skip; if (recvd < ip_hdr_len + sizeof(*icmp_hdr)) goto skip;
icmp_hdr = (struct icmp_hdr *)(reply_buf + ip_hdr_len); - if ((data_size = data->ops->parse_icmp_hdr( data, icmp_hdr, recvd - ip_hdr_len, reply )) < 0) goto skip; - reply->data_size = data_size; - if (reply->data_size && msg.msg_flags & MSG_TRUNC) + if ((ctx.data_size = data->ops->parse_icmp_hdr( data, icmp_hdr, recvd - ip_hdr_len, &ctx )) < 0) goto skip; + if (ctx.data_size && msg.msg_flags & MSG_TRUNC) { free( reply_buf ); - return set_reply_ip_status( params, IP_GENERAL_FAILURE ); + return data->ops->set_reply_ip_status( params, IP_GENERAL_FAILURE ); }
- sockaddr_to_SOCKADDR_INET( (struct sockaddr *)&addr, &reply->addr ); - reply->round_trip_time = get_rtt( data->send_time ); - reply->num_of_pkts = 1; - reply->opts.options_offset = sizeof(*reply); - reply->data_offset = sizeof(*reply) + ((reply->opts.options_size + 3) & ~3); - if (reply->opts.options_size) - memcpy( (char *)reply + reply->opts.options_offset, opts, reply->opts.options_size ); - if (reply->opts.options_size & 3) - memset( (char *)reply + reply->opts.options_offset + reply->opts.options_size, 0, 4 - (reply->opts.options_size & 3) ); - if (reply->data_size) - memcpy( (char *)reply + reply->data_offset, icmp_hdr + 1, reply->data_size ); - - params->reply_len = reply->data_offset + reply->data_size; + sockaddr_to_SOCKADDR_INET( (struct sockaddr *)&addr, &ctx.addr ); + ctx.round_trip_time = get_rtt( data->send_time ); + ctx.data = icmp_hdr + 1; + + data->ops->fill_reply( params, &ctx ); + free( reply_buf ); return STATUS_SUCCESS;
@@ -703,10 +770,10 @@ NTSTATUS icmp_listen( void *args ) if (!ret) /* timeout */ { TRACE( "timeout\n" ); - return set_reply_ip_status( params, IP_REQ_TIMED_OUT ); + return data->ops->set_reply_ip_status( params, IP_REQ_TIMED_OUT ); } /* ret < 0 */ - return set_reply_ip_status( params, errno_to_ip_status( errno ) ); + return data->ops->set_reply_ip_status( params, errno_to_ip_status( errno ) ); }
NTSTATUS icmp_cancel_listen( void *args ) diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h index 91dd393..c0b8ca0 100644 --- a/dlls/nsiproxy.sys/nsiproxy_private.h +++ b/dlls/nsiproxy.sys/nsiproxy_private.h @@ -21,7 +21,9 @@ struct icmp_listen_params { HANDLE handle; void *reply; + ULONGLONG user_reply_ptr; unsigned int reply_len; + unsigned int bits; int timeout; };
@@ -34,3 +36,40 @@ struct icmp_send_echo_params HANDLE handle; ULONG ip_status; }; + +/* output for IOCTL_NSIPROXY_WINE_ICMP_ECHO - cf. ICMP_ECHO_REPLY */ +struct icmp_echo_reply_32 +{ + ULONG addr; + ULONG status; + ULONG round_trip_time; + USHORT data_size; + USHORT num_of_pkts; + ULONG data_ptr; + struct + { + BYTE ttl; + BYTE tos; + BYTE flags; + BYTE options_size; + ULONG options_ptr; + } opts; +}; + +struct icmp_echo_reply_64 +{ + ULONG addr; + ULONG status; + ULONG round_trip_time; + USHORT data_size; + USHORT num_of_pkts; + ULONGLONG data_ptr; + struct + { + BYTE ttl; + BYTE tos; + BYTE flags; + BYTE options_size; + ULONGLONG options_ptr; + } opts; +}; diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 9664b53..8c3488d 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -426,6 +426,8 @@ struct nsiproxy_icmp_echo { SOCKADDR_INET src; SOCKADDR_INET dst; + ULONGLONG user_reply_ptr; + BYTE bits; BYTE ttl; BYTE tos; BYTE flags; @@ -435,25 +437,6 @@ struct nsiproxy_icmp_echo BYTE data[1]; /* ((opt_size + 3) & ~3) + req_size */ };
-/* output for IOCTL_NSIPROXY_WINE_ICMP_ECHO - cf. ICMP_ECHO_REPLY */ -struct nsiproxy_icmp_echo_reply -{ - SOCKADDR_INET addr; - ULONG status; - ULONG round_trip_time; - USHORT data_size; - USHORT num_of_pkts; - DWORD data_offset; - struct - { - BYTE ttl; - BYTE tos; - BYTE flags; - BYTE options_size; - DWORD options_offset; - } opts; -}; - /* Undocumented Nsi api */
#define NSI_PARAM_TYPE_RW 0
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/iphlpapi_main.c | 19 ++- dlls/iphlpapi/tests/iphlpapi.c | 248 +++++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+), 11 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 8a31241..15c4f06 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4615,12 +4615,6 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r IO_STATUS_BLOCK iosb; NTSTATUS status;
- if (event || apc_routine) - { - FIXME( "Async requests not yet supported\n" ); - return 0; - } - if (handle == INVALID_HANDLE_VALUE || !reply) { SetLastError( ERROR_INVALID_PARAMETER ); @@ -4655,19 +4649,22 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r in->timeout = timeout; memcpy( in->data + opt_size, request, request_size );
- request_event = CreateEventW( NULL, 0, 0, NULL ); + request_event = event ? event : (apc_routine ? NULL : CreateEventW( NULL, 0, 0, NULL ));
- status = NtDeviceIoControlFile( data->nsi_device, request_event, NULL, NULL, + status = NtDeviceIoControlFile( data->nsi_device, request_event, apc_routine, apc_ctxt, &iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, in, in_size, reply, reply_size );
- if (status == STATUS_PENDING && !WaitForSingleObject( request_event, INFINITE )) - status = iosb.Status; + if (status == STATUS_PENDING) + { + if (!event && !apc_routine && !WaitForSingleObject( request_event, INFINITE )) + status = iosb.Status; + }
if (!status) ret = IcmpParseReplies( reply, reply_size );
- CloseHandle( request_event ); + if (!event && request_event) CloseHandle( request_event ); heap_free( in );
if (status) SetLastError( RtlNtStatusToDosError( status ) ); diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index e20ada8..2adacd3 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -38,6 +38,7 @@ #include "winsock2.h" #include "windef.h" #include "winbase.h" +#include "winternl.h" #include "ws2tcpip.h" #include "windns.h" #include "iphlpapi.h" @@ -873,13 +874,35 @@ static void testSetTcpEntry(void) "got %u, expected %u\n", ret, ERROR_MR_MID_NOT_FOUND); }
+static BOOL icmp_send_echo_test_apc_expect; +static void WINAPI icmp_send_echo_test_apc_xp(void *context) +{ + ok(icmp_send_echo_test_apc_expect, "Unexpected APC execution\n"); + ok(context == (void*)0xdeadc0de, "Wrong context: %p\n", context); + icmp_send_echo_test_apc_expect = FALSE; +} + +static void WINAPI icmp_send_echo_test_apc(void *context, IO_STATUS_BLOCK *io_status, ULONG reserved) +{ + icmp_send_echo_test_apc_xp(context); + ok(io_status->Status == 0, "Got IO Status 0x%08x\n", io_status->Status); + ok(io_status->Information == sizeof(ICMP_ECHO_REPLY) + 32 /* sizeof(senddata) */, + "Got IO Information %lu\n", io_status->Information); +} + static void testIcmpSendEcho(void) { + /* The APC's signature is different pre-Vista */ + const PIO_APC_ROUTINE apc = broken(LOBYTE(LOWORD(GetVersion())) < 6) + ? (PIO_APC_ROUTINE)icmp_send_echo_test_apc_xp + : icmp_send_echo_test_apc; HANDLE icmp; char senddata[32], replydata[sizeof(senddata) + sizeof(ICMP_ECHO_REPLY)]; + char replydata2[sizeof(replydata) + sizeof(IO_STATUS_BLOCK)]; DWORD ret, error, replysz = sizeof(replydata); IPAddr address; ICMP_ECHO_REPLY *reply; + HANDLE event; INT i;
memset(senddata, 0, sizeof(senddata)); @@ -893,6 +916,15 @@ static void testIcmpSendEcho(void) || broken(error == ERROR_INVALID_HANDLE) /* <= 2003 */, "expected 87, got %d\n", error);
+ address = htonl(INADDR_LOOPBACK); + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(INVALID_HANDLE_VALUE, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata, replysz, 1000); + error = GetLastError(); + ok (!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok (error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INVALID_HANDLE) /* <= 2003 */, + "expected 87, got %d\n", error); + icmp = IcmpCreateFile(); ok (icmp != INVALID_HANDLE_VALUE, "IcmpCreateFile failed unexpectedly with error %d\n", GetLastError());
@@ -1036,6 +1068,222 @@ static void testIcmpSendEcho(void) ok(reply->DataSize == sizeof(senddata), "Got size:%d\n", reply->DataSize); ok(!memcmp(senddata, reply->Data, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n");
+ + /* + * IcmpSendEcho2 + */ + address = 0; + replysz = sizeof(replydata2); + memset(senddata, 0, sizeof(senddata)); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_NETNAME + || broken(error == IP_BAD_DESTINATION) /* <= 2003 */, + "expected 1214, got %d\n", error); + + event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(event != NULL, "CreateEventW failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); + ok(error == ERROR_INVALID_NETNAME + || broken(error == ERROR_IO_PENDING) /* <= 2003 */, + "Got last error: 0x%08x\n", error); + if (error == ERROR_IO_PENDING) + { + ret = WaitForSingleObjectEx(event, 2000, TRUE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObjectEx failed unexpectedly with %u\n", ret); + } + + address = htonl(INADDR_LOOPBACK); + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, NULL, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_NOACCESS) /* <= 2003 */, + "expected 87, got %d\n", error); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, NULL, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_NOACCESS) /* <= 2003 */, + "expected 87, got %d\n", error); + ok(WaitForSingleObjectEx(event, 0, TRUE) == WAIT_TIMEOUT, "Event was unexpectedly signalled.\n"); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, 0, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INSUFFICIENT_BUFFER) /* <= 2003 */, + "expected 87, got %d\n", error); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, 0, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INSUFFICIENT_BUFFER) /* <= 2003 */, + "expected 87, got %d\n", error); + ok(WaitForSingleObjectEx(event, 0, TRUE) == WAIT_TIMEOUT, "Event was unexpectedly signalled.\n"); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, NULL, 0, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INSUFFICIENT_BUFFER) /* <= 2003 */, + "expected 87, got %d\n", error); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, NULL, 0, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INSUFFICIENT_BUFFER) /* <= 2003 */, + "expected 87, got %d\n", error); + ok(WaitForSingleObjectEx(event, 0, TRUE) == WAIT_TIMEOUT, "Event was unexpectedly signalled.\n"); + + /* synchronous tests */ + SetLastError(0xdeadbeef); + address = htonl(INADDR_LOOPBACK); + replysz = sizeof(ICMP_ECHO_REPLY) + sizeof(IO_STATUS_BLOCK); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, 0, NULL, replydata2, replysz, 1000); + ok(ret, "IcmpSendEcho2 failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, NULL, 0, NULL, replydata2, replysz, 1000); + ok(ret, "IcmpSendEcho2 failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, 0, NULL, replydata2, replysz, 1000); + ok(ret, "IcmpSendEcho2 failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + replysz = sizeof(ICMP_ECHO_REPLY) + sizeof(IO_STATUS_BLOCK) + ICMP_MINLEN; + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, ICMP_MINLEN, NULL, replydata2, replysz, 1000); + ok(ret, "IcmpSendEcho2 failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + replysz = sizeof(replydata2); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + if (!ret) + { + error = GetLastError(); + skip("Failed to ping with error %d, is lo interface down?\n", error); + } + else if (winetest_debug > 1) + { + reply = (ICMP_ECHO_REPLY*)replydata2; + trace("send addr : %s\n", ntoa(address)); + trace("reply addr : %s\n", ntoa(reply->Address)); + trace("reply size : %u\n", replysz); + trace("roundtrip : %u ms\n", reply->RoundTripTime); + trace("status : %u\n", reply->Status); + trace("recv size : %u\n", reply->DataSize); + trace("ttl : %u\n", reply->Options.Ttl); + trace("flags : 0x%x\n", reply->Options.Flags); + } + + SetLastError(0xdeadbeef); + for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = i & 0xff; + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + reply = (ICMP_ECHO_REPLY*)replydata2; + ok(ret, "IcmpSendEcho2 failed unexpectedly\n"); + ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error); + ok(ntohl(reply->Address) == INADDR_LOOPBACK, "Address mismatch, expect: %s, got: %s\n", ntoa(INADDR_LOOPBACK), + ntoa(reply->Address)); + ok(reply->Status == IP_SUCCESS, "Expect status: 0x%08x, got: 0x%08x\n", IP_SUCCESS, reply->Status); + ok(reply->DataSize == sizeof(senddata), "Got size: %d\n", reply->DataSize); + ok(!memcmp(senddata, reply->Data, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n"); + + /* asynchronous tests with event */ + SetLastError(0xdeadbeef); + replysz = sizeof(replydata2); + address = htonl(INADDR_LOOPBACK); + memset(senddata, 0, sizeof(senddata)); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + if (!ret && error != ERROR_IO_PENDING) + { + skip("Failed to ping with error %d, is lo interface down?\n", error); + } + else + { + ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); + ok(error == ERROR_IO_PENDING, "Expect last error: 0x%08x, got: 0x%08x\n", ERROR_IO_PENDING, error); + ret = WaitForSingleObjectEx(event, 2000, TRUE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObjectEx failed unexpectedly with %u\n", ret); + reply = (ICMP_ECHO_REPLY*)replydata2; + ok(ntohl(reply->Address) == INADDR_LOOPBACK, "Address mismatch, expect: %s, got: %s\n", ntoa(INADDR_LOOPBACK), + ntoa(reply->Address)); + ok(reply->Status == IP_SUCCESS, "Expect status: 0x%08x, got: 0x%08x\n", IP_SUCCESS, reply->Status); + ok(reply->DataSize == sizeof(senddata), "Got size: %d\n", reply->DataSize); + if (winetest_debug > 1) + { + reply = (ICMP_ECHO_REPLY*)replydata2; + trace("send addr : %s\n", ntoa(address)); + trace("reply addr : %s\n", ntoa(reply->Address)); + trace("reply size : %u\n", replysz); + trace("roundtrip : %u ms\n", reply->RoundTripTime); + trace("status : %u\n", reply->Status); + trace("recv size : %u\n", reply->DataSize); + trace("ttl : %u\n", reply->Options.Ttl); + trace("flags : 0x%x\n", reply->Options.Flags); + } + } + + SetLastError(0xdeadbeef); + for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = i & 0xff; + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); + ok(error == ERROR_IO_PENDING, "Expect last error: 0x%08x, got: 0x%08x\n", ERROR_IO_PENDING, error); + ret = WaitForSingleObjectEx(event, 2000, TRUE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObjectEx failed unexpectedly with %u\n", ret); + reply = (ICMP_ECHO_REPLY*)replydata2; + ok(ntohl(reply->Address) == INADDR_LOOPBACK, "Address mismatch, expect: %s, got: %s\n", ntoa(INADDR_LOOPBACK), + ntoa(reply->Address)); + ok(reply->Status == IP_SUCCESS, "Expect status: 0x%08x, got: 0x%08x\n", IP_SUCCESS, reply->Status); + ok(reply->DataSize == sizeof(senddata), "Got size: %d\n", reply->DataSize); + /* pre-Vista, reply->Data is an offset; otherwise it's a pointer, so hardcode the offset */ + ok(!memcmp(senddata, reply + 1, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n"); + + CloseHandle(event); + + /* asynchronous tests with APC */ + SetLastError(0xdeadbeef); + replysz = sizeof(replydata2) + 10; + address = htonl(INADDR_LOOPBACK); + for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = ~i & 0xff; + icmp_send_echo_test_apc_expect = TRUE; + /* + NOTE: Supplying both event and apc has varying behavior across Windows versions, so not tested. + */ + ret = IcmpSendEcho2(icmp, NULL, apc, (void*)0xdeadc0de, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); + ok(error == ERROR_IO_PENDING, "Expect last error: 0x%08x, got: 0x%08x\n", ERROR_IO_PENDING, error); + SleepEx(200, TRUE); + SleepEx(0, TRUE); + ok(icmp_send_echo_test_apc_expect == FALSE, "APC was not executed!\n"); + reply = (ICMP_ECHO_REPLY*)replydata2; + ok(ntohl(reply->Address) == INADDR_LOOPBACK, "Address mismatch, expect: %s, got: %s\n", ntoa(INADDR_LOOPBACK), + ntoa(reply->Address)); + ok(reply->Status == IP_SUCCESS, "Expect status: 0x%08x, got: 0x%08x\n", IP_SUCCESS, reply->Status); + ok(reply->DataSize == sizeof(senddata), "Got size: %d\n", reply->DataSize); + /* pre-Vista, reply->Data is an offset; otherwise it's a pointer, so hardcode the offset */ + ok(!memcmp(senddata, reply + 1, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n"); + IcmpCloseHandle(icmp); }
On Mon, Oct 25, 2021 at 07:26:47PM +0300, Gabriel Ivăncescu wrote:
@@ -372,9 +384,23 @@ static void handle_queued_send_echo( IRP *irp ) irp->IoStatus.Status = status; if (status == STATUS_SUCCESS) {
memset( reply, 0, sizeof(*reply) );
reply->status = params.ip_status;
irp->IoStatus.Information = sizeof(*reply);
if (family == AF_INET)
{
if (bits == 32)
{
struct icmp_echo_reply_32 *reply = out;
memset( reply, 0, sizeof(*reply) );
reply->status = params.ip_status;
irp->IoStatus.Information = sizeof(*reply);
}
else
{
struct icmp_echo_reply_64 *reply = out;
memset( reply, 0, sizeof(*reply) );
reply->status = params.ip_status;
irp->IoStatus.Information = sizeof(*reply);
}
}
Given that this has increased in complexity, I think it would now make sense to do this on the unix-side (it was a bit of a toss-up even before this change). Obviously you'd now need to pass "out" and "bits" to the syscall. Additionally this patch could be split. For example, one could add the new ops before changing any functionality.
Huw.