-- v3: iphlpapi/tests: Add tests for Icmp6SendEcho2(). iphlpapi: Implement Icmp6SendEcho2(). nsiproxy.sys: Don't try to check for original packet for ping socket. iphlpapi: Implement Icmp6CreateFile(). iphlpapi: Implement Icmp6ParseReplies(). iphlpapi: Only supply APC routine if no event in icmp_send_echo(). iphlpapi/tests: Refactor APC testing in testIcmpSendEcho(). iphlpapi: Factor out icmp_send_echo() function. nsiproxy.sys: Store socket type in struct icmp_data.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsiproxy.sys/device.c | 205 +++++++++++++++---------------------- 1 file changed, 81 insertions(+), 124 deletions(-)
diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index fe12ba6544b..9db77f73c89 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -37,8 +37,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(nsi);
-static HANDLE request_event; - #define DECLARE_CRITICAL_SECTION(cs) \ static CRITICAL_SECTION cs; \ static CRITICAL_SECTION_DEBUG cs##_debug = \ @@ -48,7 +46,6 @@ static HANDLE request_event; DECLARE_CRITICAL_SECTION( nsiproxy_cs );
#define LIST_ENTRY_INIT( list ) { .Flink = &(list), .Blink = &(list) } -static LIST_ENTRY request_queue = LIST_ENTRY_INIT( request_queue ); static LIST_ENTRY notification_queue = LIST_ENTRY_INIT( notification_queue );
struct notification_data @@ -226,14 +223,92 @@ static int icmp_echo_reply_struct_len( ULONG family, ULONG bits ) return 0; }
+static DWORD WINAPI listen_thread_proc( void *arg ) +{ + IRP *irp = arg; + IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); + struct nsiproxy_icmp_echo *in = irp->AssociatedIrp.SystemBuffer; + struct icmp_close_params close_params; + struct icmp_listen_params params; + NTSTATUS status; + + 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; + + status = nsiproxy_call( icmp_listen, ¶ms ); + TRACE( "icmp_listen rets %08lx\n", status ); + + EnterCriticalSection( &nsiproxy_cs ); + + close_params.handle = irp_set_icmp_handle( irp, 0 ); + nsiproxy_call( icmp_close, &close_params ); + + irp->IoStatus.Status = status; + if (status == STATUS_SUCCESS) + irp->IoStatus.Information = params.reply_len; + else + irp->IoStatus.Information = 0; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + + LeaveCriticalSection( &nsiproxy_cs ); + + return 0; +} + +static NTSTATUS handle_send_echo( IRP *irp ) +{ + struct nsiproxy_icmp_echo *in = (struct nsiproxy_icmp_echo *)irp->AssociatedIrp.SystemBuffer; + struct icmp_send_echo_params params; + icmp_handle handle; + NTSTATUS status; + + TRACE( "\n" ); + params.request = in->data + ((in->opt_size + 3) & ~3); + params.request_size = in->req_size; + params.reply = irp->AssociatedIrp.SystemBuffer; + params.bits = in->bits; + params.ttl = in->ttl; + params.tos = in->tos; + params.dst = &in->dst; + params.handle = &handle; + + status = nsiproxy_call( icmp_send_echo, ¶ms ); + TRACE( "icmp_send_echo status %#lx\n", status ); + + if (status != STATUS_PENDING) + { + irp->IoStatus.Status = status; + if (status == STATUS_SUCCESS) + irp->IoStatus.Information = params.reply_len; + return status; + } + IoSetCancelRoutine( irp, icmp_echo_cancel ); + if (irp->Cancel && !IoSetCancelRoutine( irp, NULL )) + { + /* IRP was canceled before we set cancel routine */ + return STATUS_CANCELLED; + } + IoMarkIrpPending( irp ); + irp_set_icmp_handle( irp, handle ); + RtlQueueWorkItem( listen_thread_proc, irp, WT_EXECUTELONGFUNCTION ); + return STATUS_PENDING; +} + static NTSTATUS nsiproxy_icmp_echo( IRP *irp ) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); struct nsiproxy_icmp_echo *in = (struct nsiproxy_icmp_echo *)irp->AssociatedIrp.SystemBuffer; DWORD in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength; DWORD out_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength; + NTSTATUS ret;
- TRACE( "\n" ); + TRACE( ".\n" );
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]) || @@ -250,23 +325,9 @@ static NTSTATUS nsiproxy_icmp_echo( IRP *irp ) }
EnterCriticalSection( &nsiproxy_cs ); - - IoSetCancelRoutine( irp, icmp_echo_cancel ); - if (irp->Cancel && !IoSetCancelRoutine( irp, NULL )) - { - /* IRP was canceled before we set cancel routine */ - InitializeListHead( &irp->Tail.Overlay.ListEntry ); - LeaveCriticalSection( &nsiproxy_cs ); - return STATUS_CANCELLED; - } - - InsertTailList( &request_queue, &irp->Tail.Overlay.ListEntry ); - IoMarkIrpPending( irp ); - + ret = handle_send_echo( irp ); LeaveCriticalSection( &nsiproxy_cs ); - SetEvent( request_event ); - - return STATUS_PENDING; + return ret; }
static void WINAPI change_notification_cancel( DEVICE_OBJECT *device, IRP *irp ) @@ -381,107 +442,6 @@ static int add_device( DRIVER_OBJECT *driver ) return 1; }
-static DWORD WINAPI listen_thread_proc( void *arg ) -{ - IRP *irp = arg; - IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); - struct nsiproxy_icmp_echo *in = irp->AssociatedIrp.SystemBuffer; - struct icmp_close_params close_params; - struct icmp_listen_params params; - NTSTATUS status; - - 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; - - status = nsiproxy_call( icmp_listen, ¶ms ); - TRACE( "icmp_listen rets %08lx\n", status ); - - EnterCriticalSection( &nsiproxy_cs ); - - close_params.handle = irp_set_icmp_handle( irp, 0 ); - nsiproxy_call( icmp_close, &close_params ); - - irp->IoStatus.Status = status; - if (status == STATUS_SUCCESS) - irp->IoStatus.Information = params.reply_len; - else - irp->IoStatus.Information = 0; - IoCompleteRequest( irp, IO_NO_INCREMENT ); - - LeaveCriticalSection( &nsiproxy_cs ); - - return 0; -} - -static void handle_queued_send_echo( IRP *irp ) -{ - struct nsiproxy_icmp_echo *in = (struct nsiproxy_icmp_echo *)irp->AssociatedIrp.SystemBuffer; - struct icmp_send_echo_params params; - icmp_handle handle; - NTSTATUS status; - - TRACE( "\n" ); - params.request = in->data + ((in->opt_size + 3) & ~3); - params.request_size = in->req_size; - params.reply = irp->AssociatedIrp.SystemBuffer; - params.bits = in->bits; - params.ttl = in->ttl; - params.tos = in->tos; - params.dst = &in->dst; - params.handle = &handle; - - status = nsiproxy_call( icmp_send_echo, ¶ms ); - TRACE( "icmp_send_echo rets %08lx\n", status ); - - if (status != STATUS_PENDING) - { - irp->IoStatus.Status = status; - if (status == STATUS_SUCCESS) - irp->IoStatus.Information = params.reply_len; - IoCompleteRequest( irp, IO_NO_INCREMENT ); - } - else - { - irp_set_icmp_handle( irp, handle ); - RtlQueueWorkItem( listen_thread_proc, irp, WT_EXECUTELONGFUNCTION ); - } -} - -static DWORD WINAPI request_thread_proc( void *arg ) -{ - LIST_ENTRY *entry; - - SetThreadDescription( GetCurrentThread(), L"wine_nsi_request" ); - - while (WaitForSingleObject( request_event, INFINITE ) == WAIT_OBJECT_0) - { - TRACE( "request_event triggered\n" ); - EnterCriticalSection( &nsiproxy_cs ); - while ((entry = RemoveHeadList( &request_queue )) != &request_queue ) - { - IRP *irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry ); - - if (irp->Cancel) - { - irp->IoStatus.Status = STATUS_CANCELLED; - TRACE( "already cancelled\n" ); - IoCompleteRequest( irp, IO_NO_INCREMENT ); - continue; - } - - handle_queued_send_echo( irp ); - } - LeaveCriticalSection( &nsiproxy_cs ); - } - return 0; -} - static DWORD WINAPI notification_thread_proc( void *arg ) { struct nsi_get_notification_params params; @@ -536,9 +496,6 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
add_device( driver );
- request_event = CreateEventW( NULL, FALSE, FALSE, NULL ); - thread = CreateThread( NULL, 0, request_thread_proc, NULL, 0, NULL ); - CloseHandle( thread ); thread = CreateThread( NULL, 0, notification_thread_proc, NULL, 0, NULL ); CloseHandle( thread );
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/tests/iphlpapi.c | 6 ++++++ dlls/nsiproxy.sys/device.c | 1 + dlls/nsiproxy.sys/icmp_echo.c | 26 ++++++++++++++++++++------ dlls/nsiproxy.sys/nsiproxy_private.h | 1 + 4 files changed, 28 insertions(+), 6 deletions(-)
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index e189fd795f8..ea7ec0e5e70 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1240,6 +1240,12 @@ static void testIcmpSendEcho(void) ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, ICMP_MINLEN, NULL, replydata2, replysz, 1000); ok(ret, "IcmpSendEcho2 failed unexpectedly with error %ld\n", GetLastError());
+ SetLastError(0xdeadbeef); + ret = IcmpSendEcho2Ex(icmp, NULL, NULL, NULL, 0x01010101, address, senddata, ICMP_MINLEN, NULL, replydata2, replysz, 1000); + ok(!ret, "IcmpSendEcho2 succeded unexpectedly\n"); + error = GetLastError(); + ok(error == ERROR_INVALID_NETNAME, "got %ld\n", error); + SetLastError(0xdeadbeef); replysz = sizeof(replydata2); ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index 9db77f73c89..b3d00fd5187 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -275,6 +275,7 @@ static NTSTATUS handle_send_echo( IRP *irp ) params.bits = in->bits; params.ttl = in->ttl; params.tos = in->tos; + params.src = &in->src; params.dst = &in->dst; params.handle = &handle;
diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c index e92fa4394c1..8fc97a858db 100644 --- a/dlls/nsiproxy.sys/icmp_echo.c +++ b/dlls/nsiproxy.sys/icmp_echo.c @@ -113,6 +113,10 @@ struct icmp_data unsigned short id; unsigned short seq; const struct family_ops *ops; + struct sockaddr_storage src_storage; + struct sockaddr_storage dst_storage; + int src_len; + int dst_len; };
#define MAX_HANDLES 256 /* Max number of simultaneous pings - could become dynamic if need be */ @@ -625,14 +629,26 @@ NTSTATUS icmp_send_echo( void *args ) { struct icmp_send_echo_params *params = args; struct icmp_hdr *icmp_hdr; /* this is the same for both ipv4 and ipv6 */ - struct sockaddr_storage dst_storage; - struct sockaddr *dst = (struct sockaddr *)&dst_storage; struct icmp_data *data; - int dst_len, ret; + int ret; + struct sockaddr *src, *dst; + NTSTATUS status;
status = icmp_data_create( params->dst->si_family, &data ); if (status) return status; + + src = (struct sockaddr *)&data->src_storage; + dst = (struct sockaddr *)&data->dst_storage; + data->src_len = SOCKADDR_INET_to_sockaddr( params->src, src, sizeof(data->src_storage) ); + data->dst_len = SOCKADDR_INET_to_sockaddr( params->dst, dst, sizeof(data->dst_storage) ); + + if (bind( data->socket, src, data->src_len )) + { + icmp_data_free( data ); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + data->ops->set_socket_opts( data, params );
icmp_hdr = malloc( sizeof(*icmp_hdr) + params->request_size ); @@ -645,10 +661,8 @@ NTSTATUS icmp_send_echo( void *args ) memcpy( icmp_hdr + 1, params->request, params->request_size ); icmp_hdr->checksum = data->ops->chksum( (BYTE *)icmp_hdr, sizeof(*icmp_hdr) + params->request_size );
- dst_len = SOCKADDR_INET_to_sockaddr( params->dst, dst, sizeof(dst_storage) ); - NtQueryPerformanceCounter( &data->send_time, NULL ); - ret = sendto( data->socket, icmp_hdr, sizeof(*icmp_hdr) + params->request_size, 0, dst, dst_len ); + ret = sendto( data->socket, icmp_hdr, sizeof(*icmp_hdr) + params->request_size, 0, dst, data->dst_len ); free( icmp_hdr );
if (ret < 0) diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h index 1b6eacf7d08..e7cc707f729 100644 --- a/dlls/nsiproxy.sys/nsiproxy_private.h +++ b/dlls/nsiproxy.sys/nsiproxy_private.h @@ -41,6 +41,7 @@ struct icmp_listen_params
struct icmp_send_echo_params { + SOCKADDR_INET *src; SOCKADDR_INET *dst; void *request, *reply; UINT request_size, reply_len;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsiproxy.sys/icmp_echo.c | 44 +++++++++++------------------------ 1 file changed, 14 insertions(+), 30 deletions(-)
diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c index 8fc97a858db..af6e2517bba 100644 --- a/dlls/nsiproxy.sys/icmp_echo.c +++ b/dlls/nsiproxy.sys/icmp_echo.c @@ -117,6 +117,7 @@ struct icmp_data struct sockaddr_storage dst_storage; int src_len; int dst_len; + BOOL ping_socket; };
#define MAX_HANDLES 256 /* Max number of simultaneous pings - could become dynamic if need be */ @@ -189,11 +190,13 @@ static void ipv4_init_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp_hd }
/* rfc 1071 checksum */ -static unsigned short chksum( BYTE *data, unsigned int count ) +static unsigned short chksum( struct icmp_data *icmp_data, BYTE *data, unsigned int count ) { unsigned int sum = 0, carry = 0; unsigned short check, s;
+ if (icmp_data->ping_socket) return 0; + while (count > 1) { s = *(unsigned short *)data; @@ -216,13 +219,6 @@ static unsigned short chksum( BYTE *data, unsigned int count ) return check; }
-#ifdef __linux__ -static unsigned short null_chksum( BYTE *data, unsigned int count ) -{ - return 0; -} -#endif - static int ipv4_set_reply_ip_status( IP_STATUS ip_status, unsigned int bits, void *out ) { if (bits == 32) @@ -326,8 +322,8 @@ static BOOL ipv4_linux_ping_parse_ip_hdr( struct msghdr *msg, int recvd, int *ip } #endif
-static int ipv4_parse_icmp_hdr_( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_size, - struct icmp_reply_ctx *ctx, int ping_socket ) +static int ipv4_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_size, + struct icmp_reply_ctx *ctx ) { static const IP_STATUS unreach_codes[] = { @@ -356,7 +352,7 @@ static int ipv4_parse_icmp_hdr_( struct icmp_data *data, struct icmp_hdr *icmp, switch (icmp->type) { case ICMP4_ECHO_REPLY: - if ((!ping_socket && icmp->un.echo.id != data->id) || + if ((!data->ping_socket && icmp->un.echo.id != data->id) || icmp->un.echo.sequence != data->seq) return -1;
ctx->status = IP_SUCCESS; @@ -399,27 +395,13 @@ static int ipv4_parse_icmp_hdr_( struct icmp_data *data, struct icmp_hdr *icmp, orig_icmp_hdr = (const struct icmp_hdr *)((const BYTE *)orig_ip_hdr + orig_ip_hdr_len); if (orig_icmp_hdr->type != ICMP4_ECHO_REQUEST || orig_icmp_hdr->code != 0 || - (!ping_socket && orig_icmp_hdr->un.echo.id != data->id) || + (!data->ping_socket && orig_icmp_hdr->un.echo.id != data->id) || orig_icmp_hdr->un.echo.sequence != data->seq) return -1;
ctx->status = status; return 0; }
-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, ctx, 0 ); -} - -#ifdef __linux__ -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, ctx, 1 ); -} -#endif - static void ipv4_fill_reply( struct icmp_listen_params *params, struct icmp_reply_ctx *ctx) { void *options_data; @@ -472,7 +454,7 @@ struct family_ops int family; int icmp_protocol; void (*init_icmp_hdr)( struct icmp_data *data, struct icmp_hdr *icmp_hdr ); - unsigned short (*chksum)( BYTE *data, unsigned int count ); + unsigned short (*chksum)( struct icmp_data *icmp_data, BYTE *data, unsigned int count ); int (*set_reply_ip_status)( IP_STATUS ip_status, unsigned int bits, void *out ); void (*set_socket_opts)( struct icmp_data *data, struct icmp_send_echo_params *params ); int (*reply_buffer_len)( struct icmp_listen_params *params ); @@ -502,12 +484,12 @@ static const struct family_ops ipv4_linux_ping = AF_INET, IPPROTO_ICMP, ipv4_init_icmp_hdr, - null_chksum, + chksum, ipv4_set_reply_ip_status, ipv4_linux_ping_set_socket_opts, ipv4_linux_ping_reply_buffer_len, ipv4_linux_ping_parse_ip_hdr, - ipv4_linux_ping_parse_icmp_hdr, + ipv4_parse_icmp_hdr, ipv4_fill_reply, }; #endif @@ -590,6 +572,7 @@ static NTSTATUS icmp_data_create( ADDRESS_FAMILY win_family, struct icmp_data ** data = malloc( sizeof(*data) ); if (!data) return STATUS_NO_MEMORY;
+ data->ping_socket = FALSE; data->socket = socket( ops->family, SOCK_RAW, ops->icmp_protocol ); if (data->socket < 0) /* Try a ping-socket */ { @@ -604,6 +587,7 @@ static NTSTATUS icmp_data_create( ADDRESS_FAMILY win_family, struct icmp_data ** #ifdef __linux__ if (ops->family == AF_INET) ops = &ipv4_linux_ping; #endif + data->ping_socket = TRUE; } if (pipe( data->cancel_pipe )) { @@ -659,7 +643,7 @@ NTSTATUS icmp_send_echo( void *args ) } data->ops->init_icmp_hdr( data, icmp_hdr ); memcpy( icmp_hdr + 1, params->request, params->request_size ); - icmp_hdr->checksum = data->ops->chksum( (BYTE *)icmp_hdr, sizeof(*icmp_hdr) + params->request_size ); + icmp_hdr->checksum = data->ops->chksum( data, (BYTE *)icmp_hdr, sizeof(*icmp_hdr) + params->request_size );
NtQueryPerformanceCounter( &data->send_time, NULL ); ret = sendto( data->socket, icmp_hdr, sizeof(*icmp_hdr) + params->request_size, 0, dst, data->dst_len );
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/iphlpapi_main.c | 57 +++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 20 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 94769ed09fe..03bb376c35a 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -21,6 +21,8 @@ #include <stdarg.h>
#define IPHLPAPI_DLL_LINKAGE +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "winreg.h" @@ -4863,17 +4865,15 @@ void WINAPI icmp_apc_routine( void *context, IO_STATUS_BLOCK *iosb, ULONG reserv heap_free( ctxt ); }
-/*********************************************************************** - * IcmpSendEcho2Ex (IPHLPAPI.@) - */ -DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine, void *apc_ctxt, - IPAddr src, IPAddr dst, void *request, WORD request_size, IP_OPTION_INFORMATION *opts, - void *reply, DWORD reply_size, DWORD timeout ) +static NTSTATUS icmp_send_echo( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine, void *apc_ctxt, + SOCKADDR_INET *src_addr, SOCKADDR_INET *dst_addr, void *request, + WORD request_size, IP_OPTION_INFORMATION *opts, void *reply, DWORD reply_size, + DWORD timeout ) { struct icmp_handle_data *data = (struct icmp_handle_data *)handle; struct icmp_apc_ctxt *ctxt = heap_alloc( sizeof(*ctxt) ); IO_STATUS_BLOCK *iosb = &ctxt->iosb; - DWORD opt_size, in_size, ret = 0; + DWORD opt_size, in_size; struct nsiproxy_icmp_echo *in; HANDLE request_event; NTSTATUS status; @@ -4881,8 +4881,7 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r if (handle == INVALID_HANDLE_VALUE || !reply) { heap_free( ctxt ); - SetLastError( ERROR_INVALID_PARAMETER ); - return 0; + return STATUS_INVALID_PARAMETER; }
ctxt->apc_routine = apc_routine; @@ -4895,16 +4894,13 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r if (!in) { heap_free( ctxt ); - SetLastError( IP_NO_RESOURCES ); - return 0; + return STATUS_NO_MEMORY; }
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; - in->dst.Ipv4.sin_addr.s_addr = dst; + in->src = *src_addr; + in->dst = *dst_addr; if (opts) { in->ttl = opts->Ttl; @@ -4929,15 +4925,36 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r status = iosb->Status; }
- if (!status) - ret = IcmpParseReplies( reply, reply_size ); - if (!event && request_event) CloseHandle( request_event ); if ((!apc_routine && !event) || status != STATUS_PENDING) heap_free( ctxt ); heap_free( in ); + return status; +}
- if (status) SetLastError( RtlNtStatusToDosError( status ) ); - return ret; +/*********************************************************************** + * IcmpSendEcho2Ex (IPHLPAPI.@) + */ +DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine, void *apc_ctxt, + IPAddr src, IPAddr dst, void *request, WORD request_size, IP_OPTION_INFORMATION *opts, + void *reply, DWORD reply_size, DWORD timeout ) +{ + SOCKADDR_INET src_addr, dst_addr; + NTSTATUS status; + + TRACE( "(%p %p %p %p %#lx %#lx %p %u %p %p %lu %lu).\n", handle, event, apc_routine, apc_ctxt, src, dst, + request, request_size, opts, reply, reply_size, timeout ); + + memset( &src_addr, 0, sizeof(src_addr) ); + src_addr.Ipv4.sin_family = AF_INET; + src_addr.Ipv4.sin_addr.s_addr = src; + memset( &dst_addr, 0, sizeof(dst_addr) ); + dst_addr.Ipv4.sin_family = AF_INET; + dst_addr.Ipv4.sin_addr.s_addr = dst; + status = icmp_send_echo( handle, event, apc_routine, apc_ctxt, &src_addr, &dst_addr, request, request_size, + opts, reply, reply_size, timeout ); + if (!status) return IcmpParseReplies( reply, reply_size ); + SetLastError( RtlNtStatusToDosError( status ) ); + return 0; }
/***********************************************************************
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/tests/iphlpapi.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-)
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index ea7ec0e5e70..c2945437cf3 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -943,27 +943,20 @@ static void testSetTcpEntry(void) }
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 int icmp_send_echo_test_line; +static IO_STATUS_BLOCK icmp_send_echo_io;
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%08lx\n", io_status->Status); - ok(io_status->Information == sizeof(ICMP_ECHO_REPLY) + 32 /* sizeof(senddata) */, - "Got IO Information %Iu\n", io_status->Information); + ok_(__FILE__, icmp_send_echo_test_line)(icmp_send_echo_test_apc_expect, "Unexpected APC execution\n"); + ok_(__FILE__, icmp_send_echo_test_line)(context == (void*)0xdeadc0de, "Wrong context: %p\n", context); + icmp_send_echo_test_apc_expect = FALSE; + icmp_send_echo_io = *io_status; }
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)]; @@ -1340,16 +1333,21 @@ static void testIcmpSendEcho(void) address = htonl(INADDR_LOOPBACK); for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = ~i & 0xff; icmp_send_echo_test_apc_expect = TRUE; + memset(&icmp_send_echo_io, 0xcc, sizeof(icmp_send_echo_io)); + icmp_send_echo_test_line = __LINE__; /* 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); + ret = IcmpSendEcho2(icmp, NULL, icmp_send_echo_test_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%08lx\n", ERROR_IO_PENDING, error); SleepEx(200, TRUE); SleepEx(0, TRUE); ok(icmp_send_echo_test_apc_expect == FALSE, "APC was not executed!\n"); + ok(!icmp_send_echo_io.Status, "got %#lx.\n", icmp_send_echo_io.Status); + ok(icmp_send_echo_io.Information == sizeof(replydata), "got %Iu.\n", icmp_send_echo_io.Information); reply = (ICMP_ECHO_REPLY*)replydata2; ok(ntohl(reply->Address) == INADDR_LOOPBACK, "Address mismatch, expect: %s, got: %s\n", ntoa(INADDR_LOOPBACK), ntoa(reply->Address));
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/iphlpapi_main.c | 2 +- dlls/iphlpapi/tests/iphlpapi.c | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 03bb376c35a..3ca0ed26ca4 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4915,7 +4915,7 @@ static NTSTATUS icmp_send_echo( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
request_event = event ? event : (apc_routine ? NULL : CreateEventW( NULL, 0, 0, NULL ));
- status = NtDeviceIoControlFile( data->nsi_device, request_event, apc_routine ? icmp_apc_routine : NULL, + status = NtDeviceIoControlFile( data->nsi_device, request_event, apc_routine && !event ? icmp_apc_routine : NULL, apc_routine ? ctxt : apc_ctxt, iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, in, in_size, reply, reply_size );
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index c2945437cf3..b2312c76f2f 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1325,6 +1325,22 @@ static void testIcmpSendEcho(void) /* 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");
+ SetLastError(0xdeadbeef); + for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = i & 0xff; + ret = IcmpSendEcho2(icmp, event, icmp_send_echo_test_apc, 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%08lx\n", ERROR_IO_PENDING, error); + ret = WaitForSingleObjectEx(event, 2000, TRUE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObjectEx failed unexpectedly with %lu\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%08lx\n", IP_SUCCESS, reply->Status); + ok(reply->DataSize == sizeof(senddata), "Got size: %d\n", reply->DataSize); + ok(!memcmp(senddata, reply + 1, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n"); + CloseHandle(event);
/* asynchronous tests with APC */ @@ -1335,9 +1351,6 @@ static void testIcmpSendEcho(void) icmp_send_echo_test_apc_expect = TRUE; memset(&icmp_send_echo_io, 0xcc, sizeof(icmp_send_echo_io)); icmp_send_echo_test_line = __LINE__; - /* - NOTE: Supplying both event and apc has varying behavior across Windows versions, so not tested. - */ ret = IcmpSendEcho2(icmp, NULL, icmp_send_echo_test_apc, (void*)0xdeadc0de, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); error = GetLastError();
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/iphlpapi.spec | 2 +- dlls/iphlpapi/iphlpapi_main.c | 12 ++++++++++++ dlls/iphlpapi/tests/iphlpapi.c | 19 +++++++++++++++++++ include/ipexport.h | 31 +++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/dlls/iphlpapi/iphlpapi.spec b/dlls/iphlpapi/iphlpapi.spec index c04560ad656..ed2214c99ce 100644 --- a/dlls/iphlpapi/iphlpapi.spec +++ b/dlls/iphlpapi/iphlpapi.spec @@ -153,7 +153,7 @@ @ stdcall GetUnicastIpAddressTable(long ptr) @ stdcall GetUniDirectionalAdapterInfo( ptr ptr ) @ stdcall Icmp6CreateFile() -#@ stub Icmp6ParseReplies +@ stdcall Icmp6ParseReplies( ptr long ) @ stdcall Icmp6SendEcho2(ptr ptr ptr ptr ptr ptr ptr long ptr ptr long long) @ stdcall IcmpCloseHandle(ptr) @ stdcall IcmpCreateFile() diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 3ca0ed26ca4..f68ac36f2eb 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4967,6 +4967,18 @@ HANDLE WINAPI Icmp6CreateFile( void ) return INVALID_HANDLE_VALUE; }
+/****************************************************************** + * Icmp6ParseReplies (IPHLPAPI.@) + */ +DWORD WINAPI Icmp6ParseReplies( void *reply, DWORD reply_size ) +{ + ICMPV6_ECHO_REPLY *icmp_reply = reply; + + if (!icmp_reply->Status) return 1; + SetLastError( icmp_reply->Status ); + return 0; +} + /*********************************************************************** * Icmp6SendEcho2 (IPHLPAPI.@) */ diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index b2312c76f2f..10457a4310f 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1406,6 +1406,24 @@ static void testIcmpParseReplies( void ) ok( !reply.Reserved, "reserved %d\n", reply.Reserved ); }
+static void testIcmp6ParseReplies( void ) +{ + ICMPV6_ECHO_REPLY reply = { 0 }; + DWORD ret; + + SetLastError( 0xdeadbeef ); + ret = Icmp6ParseReplies( &reply, sizeof(reply) ); + ok( ret == 1, "got %ld.\n", ret ); + ok( GetLastError() == 0xdeadbeef, "got error %ld.\n", GetLastError() ); + + reply.Status = 12345; + SetLastError( 0xdeadbeef ); + ret = Icmp6ParseReplies( &reply, sizeof(reply) ); + ok( ret == 0, "ret %ld\n", ret ); + ok( GetLastError() == 12345, "got error %ld.\n", GetLastError() ); + ok( reply.Status == 12345, "got %ld,\n", reply.Status ); +} + static void testWinNT4Functions(void) { testGetNumberOfInterfaces(); @@ -1426,6 +1444,7 @@ static void testWinNT4Functions(void) testSetTcpEntry(); testIcmpSendEcho(); testIcmpParseReplies(); + testIcmp6ParseReplies(); }
static void testGetInterfaceInfo(void) diff --git a/include/ipexport.h b/include/ipexport.h index a0e30ba1533..1a93a0136b3 100644 --- a/include/ipexport.h +++ b/include/ipexport.h @@ -66,6 +66,23 @@ typedef struct ip_option_information IP_OPTION_INFORMATION, *PIP_OPTION_INFORMAT
typedef struct icmp_echo_reply ICMP_ECHO_REPLY, *PICMP_ECHO_REPLY;
+#pragma pack(push,1) +typedef struct _IPV6_ADDRESS_EX { + USHORT sin6_port; + ULONG sin6_flowinfo; + USHORT sin6_addr[8]; + ULONG sin6_scope_id; +} IPV6_ADDRESS_EX, *PIPV6_ADDRESS_EX; +#pragma pack(pop) + +struct icmpv6_echo_reply_lh +{ + IPV6_ADDRESS_EX Address; + ULONG Status; + unsigned int RoundTripTime; +}; + +typedef struct icmpv6_echo_reply_lh ICMPV6_ECHO_REPLY, *PICMPV6_ECHO_REPLY;
#define IP_STATUS_BASE 11000
@@ -98,6 +115,20 @@ typedef struct icmp_echo_reply ICMP_ECHO_REPLY, *PICMP_ECHO_REPLY; #define MAX_IP_STATUS IP_GENERAL_FAILURE #define IP_PENDING (IP_STATUS_BASE + 255)
+/* IPv6 status codes */ +#define IP_DEST_NO_ROUTE (IP_STATUS_BASE + 2) +#define IP_DEST_ADDR_UNREACHABLE (IP_STATUS_BASE + 3) +#define IP_DEST_PROHIBITED (IP_STATUS_BASE + 4) +#define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5) +#define IP_HOP_LIMIT_EXCEEDED (IP_STATUS_BASE + 13) +#define IP_REASSEMBLY_TIME_EXCEEDED (IP_STATUS_BASE + 14) +#define IP_PARAMETER_PROBLEM (IP_STATUS_BASE + 15) +#define IP_DEST_UNREACHABLE (IP_STATUS_BASE + 40) +#define IP_TIME_EXCEEDED (IP_STATUS_BASE + 41) +#define IP_BAD_HEADER (IP_STATUS_BASE + 42) +#define IP_UNRECOGNIZED_NEXT_HEADER (IP_STATUS_BASE + 43) +#define IP_ICMP_ERROR (IP_STATUS_BASE + 44) +#define IP_DEST_SCOPE_MISMATCH (IP_STATUS_BASE + 45)
#define MAX_ADAPTER_NAME 128
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/iphlpapi_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index f68ac36f2eb..83413804d72 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4962,9 +4962,9 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r */ HANDLE WINAPI Icmp6CreateFile( void ) { - FIXME( "stub\n" ); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return INVALID_HANDLE_VALUE; + TRACE( ".\n" ); + + return IcmpCreateFile(); }
/******************************************************************
From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsiproxy.sys/icmp_echo.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c index af6e2517bba..c99e6318f7f 100644 --- a/dlls/nsiproxy.sys/icmp_echo.c +++ b/dlls/nsiproxy.sys/icmp_echo.c @@ -384,6 +384,8 @@ static int ipv4_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, i return -1; }
+ if (data->ping_socket) return 0; + /* Check that the appended packet is really ours - * all handled icmp replies have an 8-byte header * followed by the original ip hdr. */
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/iphlpapi_main.c | 17 +- dlls/iphlpapi/tests/iphlpapi.c | 7 + dlls/nsiproxy.sys/device.c | 8 + dlls/nsiproxy.sys/icmp_echo.c | 231 ++++++++++++++++++++++++++- dlls/nsiproxy.sys/nsiproxy_private.h | 1 + include/wine/nsi.h | 1 + 6 files changed, 262 insertions(+), 3 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 83413804d72..595f499c36e 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4904,11 +4904,13 @@ static NTSTATUS icmp_send_echo( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc if (opts) { in->ttl = opts->Ttl; + in->hop_limit = opts->Ttl; in->tos = opts->Tos; in->flags = opts->Flags; memcpy( in->data, opts->OptionsData, opts->OptionsSize ); in->opt_size = opts->OptionsSize; } + else in->hop_limit = -1; in->req_size = request_size; in->timeout = timeout; memcpy( in->data + opt_size, request, request_size ); @@ -4986,9 +4988,20 @@ DWORD WINAPI Icmp6SendEcho2( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_ro struct sockaddr_in6 *src, struct sockaddr_in6 *dst, void *request, WORD request_size, IP_OPTION_INFORMATION *opts, void *reply, DWORD reply_size, DWORD timeout ) { - FIXME( "(%p, %p, %p, %p, %p, %p, %p, %d, %p, %p, %ld, %ld): stub\n", handle, event, + SOCKADDR_INET src_addr, dst_addr; + NTSTATUS status; + + TRACE( "(%p, %p, %p, %p, %p, %p, %p, %d, %p, %p, %ld, %ld).\n", handle, event, apc_routine, apc_ctxt, src, dst, request, request_size, opts, reply, reply_size, timeout ); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + + src_addr.Ipv6 = *src; + if (!src_addr.si_family) src_addr.si_family = AF_INET6; + dst_addr.Ipv6 = *dst; + if (!dst_addr.si_family) dst_addr.si_family = AF_INET6; + status = icmp_send_echo( handle, event, apc_routine, apc_ctxt, &src_addr, &dst_addr, request, request_size, + opts, reply, reply_size, timeout ); + if (!status) return Icmp6ParseReplies( reply, reply_size ); + SetLastError( RtlNtStatusToDosError( status ) ); return 0; }
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 10457a4310f..26a44b2aeaa 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -961,6 +961,7 @@ static void testIcmpSendEcho(void) char senddata[32], replydata[sizeof(senddata) + sizeof(ICMP_ECHO_REPLY)]; char replydata2[sizeof(replydata) + sizeof(IO_STATUS_BLOCK)]; DWORD ret, error, replysz = sizeof(replydata); + IP_OPTION_INFORMATION opt; IPAddr address; ICMP_ECHO_REPLY *reply; HANDLE event; @@ -1020,6 +1021,12 @@ static void testIcmpSendEcho(void) error = GetLastError(); ok (ret, "IcmpSendEcho failed unexpectedly with error %ld\n", error);
+ memset(&opt, 0, sizeof(opt)); + SetLastError(0xdeadbeef); + ret = IcmpSendEcho(icmp, address, NULL, 0, &opt, replydata, replysz, 1000); + error = GetLastError(); + ok (ret, "IcmpSendEcho failed unexpectedly with error %ld\n", error); + SetLastError(0xdeadbeef); ret = IcmpSendEcho(icmp, address, senddata, sizeof(senddata), NULL, NULL, replysz, 1000); error = GetLastError(); diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index b3d00fd5187..b1c674ca5f3 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -29,6 +29,7 @@ #include "ddk/wdm.h" #include "ifdef.h" #include "netiodef.h" +#include "ipexport.h" #include "wine/nsi.h" #include "wine/debug.h" #include "wine/unixlib.h" @@ -220,6 +221,8 @@ 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); + if (family == AF_INET6) + return sizeof(ICMPV6_ECHO_REPLY); return 0; }
@@ -274,6 +277,7 @@ static NTSTATUS handle_send_echo( IRP *irp ) params.reply = irp->AssociatedIrp.SystemBuffer; params.bits = in->bits; params.ttl = in->ttl; + params.hop_limit = in->hop_limit; params.tos = in->tos; params.src = &in->src; params.dst = &in->dst; @@ -321,6 +325,10 @@ static NTSTATUS nsiproxy_icmp_echo( IRP *irp ) case AF_INET: if (in->dst.Ipv4.sin_addr.s_addr == INADDR_ANY) return STATUS_INVALID_ADDRESS_WILDCARD; break; + case AF_INET6: + if (IN6_IS_ADDR_UNSPECIFIED(&in->dst.Ipv6.sin6_addr)) return STATUS_INVALID_ADDRESS_WILDCARD; + break; + default: return STATUS_INVALID_PARAMETER; } diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c index c99e6318f7f..59ac27b6e97 100644 --- a/dlls/nsiproxy.sys/icmp_echo.c +++ b/dlls/nsiproxy.sys/icmp_echo.c @@ -75,6 +75,28 @@ struct ip_hdr uint32_t daddr; };
+struct ipv6_hdr +{ + uint8_t v_prio; /* version << 4 | priority */ + uint8_t flow_lbl[3]; + uint16_t next_len; + uint8_t next_hdr; + uint8_t hop_limit; + struct in6_addr saddr; + struct in6_addr daddr; +}; + +C_ASSERT( sizeof(struct ipv6_hdr) == 40 ); + +struct ipv6_pseudo_header +{ + struct in6_addr src; + struct in6_addr dst; + UINT32 next_len; /* incapsulated packet length in network byte order */ + BYTE zero[3]; + BYTE next_header; +}; + struct icmp_hdr { uint8_t type; @@ -117,6 +139,7 @@ struct icmp_data struct sockaddr_storage dst_storage; int src_len; int dst_len; + int hop_limit; BOOL ping_socket; };
@@ -496,6 +519,203 @@ static const struct family_ops ipv4_linux_ping = }; #endif
+static void ipv6_init_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp_hdr ) +{ + icmp_hdr->type = ICMP6_ECHO_REQUEST; + icmp_hdr->code = 0; + icmp_hdr->checksum = 0; + icmp_hdr->un.echo.id = data->id = getpid() & 0xffff; /* will be overwritten for linux ping socks */ + icmp_hdr->un.echo.sequence = data->seq = InterlockedIncrement( &icmp_sequence ) & 0xffff; +} + +static unsigned short ipv6_chksum( struct icmp_data *icmp_data, BYTE *data, unsigned int count ) +{ + struct ipv6_pseudo_header *ip_h; + struct sockaddr_in6 addr; + unsigned short sum; + socklen_t slen; + int s, ret; + + if (icmp_data->ping_socket) return 0; + + /* Determine source address. Do it on a separate socket or raw socket won't receive ICMP replies + * originating not from the destination address. */ + s = socket( AF_INET6, SOCK_RAW, IPPROTO_ICMPV6 ); + if (s < 0) + { + TRACE( "failed to open raw sock, trying a dgram sock\n" ); + s = socket( AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6 ); + if (s < 0) + { + WARN( "Unable to create socket\n" ); + return 0; + } + } + if (bind( s, (void *)&icmp_data->src_storage, icmp_data->src_len )) + { + close( s ); + return 0; + } + if (connect( s, (void *)&icmp_data->dst_storage, icmp_data->dst_len )) + { + close( s ); + return 0; + } + slen = sizeof(addr); + ret = getsockname( s, (void *)&addr, &slen ); + close( s ); + if (ret) return 0; + + ip_h = malloc( sizeof(*ip_h) + count ); + if (!ip_h) return 0; + memset( ip_h, 0, sizeof(*ip_h) ); + ip_h->src = addr.sin6_addr; + ip_h->dst = ((struct sockaddr_in6 *)&icmp_data->dst_storage)->sin6_addr; + ip_h->next_len = htonl( count ); + ip_h->next_header = IPPROTO_ICMPV6; + memcpy( ip_h + 1, data, count ); + sum = chksum( icmp_data, (BYTE *)ip_h, sizeof(*ip_h) + count ); + free( ip_h ); + return sum; +} + +static int ipv6_set_reply_ip_status( IP_STATUS ip_status, unsigned int bits, void *out ) +{ + ICMPV6_ECHO_REPLY *reply = out; + + memset( reply, 0, sizeof(*reply) ); + reply->Status = ip_status; + return sizeof(*reply); +} + +static void ipv6_set_socket_opts( struct icmp_data *data, struct icmp_send_echo_params *params ) +{ +#ifdef IPV6_UNICAST_HOPS +{ + int val = params->hop_limit; + + setsockopt( data->socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val) ); +} +#endif +} + +static int ipv6_reply_buffer_len( struct icmp_listen_params *params ) +{ + return sizeof(struct icmp_hdr) + params->reply_len - sizeof(ICMPV6_ECHO_REPLY); +} + +static BOOL ipv6_parse_ip_hdr( struct msghdr *msg, int recvd, int *ip_hdr_len, + struct icmp_reply_ctx *ctx ) +{ + *ip_hdr_len = 0; + ctx->options_data = NULL; + ctx->ttl = 0; + ctx->tos = 0; + ctx->flags = 0; + ctx->options_size = 0; + return TRUE; +} + +static int ipv6_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, + int icmp_size, struct icmp_reply_ctx *ctx ) +{ + static const IP_STATUS unreach_codes[] = + { + IP_DEST_NO_ROUTE, + IP_DEST_PROHIBITED, + IP_DEST_SCOPE_MISMATCH, + IP_DEST_ADDR_UNREACHABLE, + IP_DEST_PORT_UNREACHABLE, + IP_ICMP_ERROR, + IP_DEST_UNREACHABLE, + IP_BAD_HEADER, + }; + const struct ipv6_hdr *orig_ip_hdr; + const struct icmp_hdr *orig_icmp_hdr; + IP_STATUS status; + + switch (icmp->type) + { + case ICMP6_ECHO_REPLY: + if ((!data->ping_socket && icmp->un.echo.id != data->id) || + icmp->un.echo.sequence != data->seq) return -1; + + ctx->status = IP_SUCCESS; + return icmp_size - sizeof(*icmp); + + case ICMP6_DST_UNREACH: + if (icmp->code < ARRAY_SIZE(unreach_codes)) + status = unreach_codes[icmp->code]; + else + status = IP_DEST_HOST_UNREACHABLE; + break; + + case ICMP6_PACKET_TOO_BIG: + status = IP_PACKET_TOO_BIG; + break; + + case ICMP6_TIME_EXCEEDED: + switch(icmp->code) + { + case 0: status = IP_HOP_LIMIT_EXCEEDED; break; + case 1: status = IP_REASSEMBLY_TIME_EXCEEDED; break; + default: status = IP_TIME_EXCEEDED; break; + } + break; + + case ICMP6_PARAM_PROB: + status = IP_PARAMETER_PROBLEM; + break; + + default: + return -1; + } + + if (data->ping_socket) return 0; + /* Check that the appended packet is really ours. */ + if (icmp_size < sizeof(*icmp) + sizeof(*orig_ip_hdr)) return -1; + orig_ip_hdr = (struct ipv6_hdr *)(icmp + 1); + if ((orig_ip_hdr->v_prio >> 4) != 6 || orig_ip_hdr->next_hdr != IPPROTO_ICMPV6) return -1; + if (icmp_size < sizeof(*icmp) + sizeof(*orig_ip_hdr) + sizeof(*orig_icmp_hdr)) return -1; + orig_icmp_hdr = (const struct icmp_hdr *)((const BYTE *)orig_ip_hdr + sizeof(*orig_ip_hdr)); + if (orig_icmp_hdr->type != ICMP6_ECHO_REQUEST || + orig_icmp_hdr->code != 0 || + (!data->ping_socket && orig_icmp_hdr->un.echo.id != data->id) || + orig_icmp_hdr->un.echo.sequence != data->seq) return -1; + + ctx->status = status; + + return 0; +} + +static void ipv6_fill_reply( struct icmp_listen_params *params, struct icmp_reply_ctx *ctx) +{ + ICMPV6_ECHO_REPLY *reply = params->reply; + + reply->Status = ctx->status; + memcpy( reply->Address.sin6_addr, &ctx->addr.Ipv6.sin6_addr, sizeof(ctx->addr.Ipv6.sin6_addr) ); + reply->Address.sin6_flowinfo = ctx->addr.Ipv6.sin6_flowinfo; + reply->Address.sin6_port = ctx->addr.Ipv6.sin6_port; + reply->Address.sin6_scope_id = ctx->addr.Ipv6.sin6_scope_id; + reply->RoundTripTime = ctx->round_trip_time; + memcpy( reply + 1, ctx->data, ctx->data_size ); + params->reply_len = sizeof(*reply) + ctx->data_size; +} + +static const struct family_ops ipv6 = +{ + AF_INET6, + IPPROTO_ICMPV6, + ipv6_init_icmp_hdr, + ipv6_chksum, + ipv6_set_reply_ip_status, + ipv6_set_socket_opts, + ipv6_reply_buffer_len, + ipv6_parse_ip_hdr, + ipv6_parse_icmp_hdr, + ipv6_fill_reply, +}; + static IP_STATUS errno_to_ip_status( int err ) { switch( err ) @@ -568,7 +788,8 @@ static NTSTATUS icmp_data_create( ADDRESS_FAMILY win_family, struct icmp_data ** struct icmp_data *data; const struct family_ops *ops;
- if (win_family == WS_AF_INET) ops = &ipv4; + if (win_family == WS_AF_INET6) ops = &ipv6; + else if (win_family == WS_AF_INET) ops = &ipv4; else return STATUS_INVALID_PARAMETER;
data = malloc( sizeof(*data) ); @@ -624,6 +845,7 @@ NTSTATUS icmp_send_echo( void *args ) status = icmp_data_create( params->dst->si_family, &data ); if (status) return status;
+ data->hop_limit = params->hop_limit; src = (struct sockaddr *)&data->src_storage; dst = (struct sockaddr *)&data->dst_storage; data->src_len = SOCKADDR_INET_to_sockaddr( params->src, src, sizeof(data->src_storage) ); @@ -745,6 +967,13 @@ NTSTATUS icmp_listen( void *args ) data = handle_data( params->handle ); if (!data) return STATUS_INVALID_PARAMETER;
+ if (data->dst_storage.ss_family == AF_INET6 && !data->hop_limit) + { + TRACE( "Invalid hop_limit.\n" ); + params->reply_len = data->ops->set_reply_ip_status( IP_GENERAL_FAILURE, params->bits, params->reply ); + return STATUS_SUCCESS; + } + fds[0].fd = data->socket; fds[0].events = POLLIN; fds[1].fd = data->cancel_pipe[0]; diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h index e7cc707f729..ab40fa2057a 100644 --- a/dlls/nsiproxy.sys/nsiproxy_private.h +++ b/dlls/nsiproxy.sys/nsiproxy_private.h @@ -46,6 +46,7 @@ struct icmp_send_echo_params void *request, *reply; UINT request_size, reply_len; BYTE bits, ttl, tos; + int hop_limit; icmp_handle *handle; };
diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 0df313db624..e281321b026 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -476,6 +476,7 @@ struct nsiproxy_icmp_echo UINT opt_size; UINT req_size; UINT timeout; + int hop_limit; BYTE data[1]; /* ((opt_size + 3) & ~3) + req_size */ };
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/tests/iphlpapi.c | 163 +++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+)
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 26a44b2aeaa..7aa73744813 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -35,6 +35,8 @@ */
#include <stdarg.h> +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "winsock2.h" #include "windef.h" #include "winbase.h" @@ -1413,6 +1415,166 @@ static void testIcmpParseReplies( void ) ok( !reply.Reserved, "reserved %d\n", reply.Reserved ); }
+static void test_Icmp6SendEcho(void) +{ + char senddata[32], replydata[sizeof(senddata) + sizeof(ICMPV6_ECHO_REPLY)]; + struct sockaddr_in6 src_addr, address; + IP_OPTION_INFORMATION opt; + ICMPV6_ECHO_REPLY *reply; + DWORD ret, replysz; + char str[256]; + HANDLE event; + HANDLE icmp; + + icmp = Icmp6CreateFile(); + ok(icmp != INVALID_HANDLE_VALUE, "got error %ld.\n", GetLastError()); + + memset(senddata, 0xdd, sizeof(senddata)); + + memset(&src_addr, 0, sizeof(src_addr)); + memset(&address, 0, sizeof(address)); + ret = inet_pton( AF_INET6, "::1", &address.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + + SetLastError(0xdeadbeef); + replysz = sizeof(replydata); + memset(replydata, 0xcc, sizeof(replydata)); + reply = (ICMPV6_ECHO_REPLY*)replydata; + ret = Icmp6SendEcho2(icmp, NULL, NULL, NULL, &src_addr, &address, senddata, sizeof(senddata), NULL, + replydata, replysz, 1000); + todo_wine_if(!ret && GetLastError() == ERROR_ACCESS_DENIED) + ok(ret == 1, "got ret %lu, error %ld.\n", ret, GetLastError()); + if (!ret) + { + skip( "ipv6 ping is prohibited.\n" ); + IcmpCloseHandle(icmp); + return; + } + ok(!GetLastError(), "got %ld.\n", GetLastError()); + ok(!reply->Status, "got %lu.\n", reply->Status); + ok(!memcmp(reply->Address.sin6_addr, &address.sin6_addr, sizeof(address.sin6_addr)), "got %s.\n", + inet_ntop(AF_INET6, (void *)&reply->Address.sin6_addr, str, sizeof(str))); + ok(!reply->Address.sin6_port, "got %#x.\n", reply->Address.sin6_port); + ok(!reply->Address.sin6_flowinfo, "got %#lx.\n", reply->Address.sin6_flowinfo); + ok(!reply->Address.sin6_scope_id, "got %#lx.\n", reply->Address.sin6_scope_id); + ok(!memcmp(reply + 1, senddata, sizeof(senddata)), "reply data does not match.\n"); + + memset(&src_addr, 0, sizeof(src_addr)); + memset(&address, 0, sizeof(address)); + memset(&opt, 0, sizeof(opt)); + ret = inet_pton( AF_INET6, "::1", &address.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + ret = inet_pton( AF_INET6, "::1", &src_addr.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + memset(replydata, 0xcc, sizeof(replydata)); + ret = Icmp6SendEcho2(icmp, NULL, NULL, NULL, &src_addr, &address, senddata, sizeof(senddata), &opt, + replydata, replysz, 1000); + ok(!ret && GetLastError() == IP_GENERAL_FAILURE, "got ret %#lx, error %ld.\n", ret, GetLastError()); + ok(reply->Status == IP_GENERAL_FAILURE, "got %#lx.\n", reply->Status); + + opt.Ttl = 1; + ret = Icmp6SendEcho2(icmp, NULL, NULL, NULL, &src_addr, &address, senddata, sizeof(senddata), &opt, + replydata, replysz, 1000); + ok(ret == 1, "got ret %lu, error %ld.\n", ret, GetLastError()); + ok(!GetLastError(), "got %ld.\n", GetLastError()); + ok(!reply->Status, "got %#lx.\n", reply->Status); + ok(!memcmp(reply->Address.sin6_addr, &address.sin6_addr, sizeof(address.sin6_addr)), "got %s.\n", + inet_ntop(AF_INET6, (void *)&reply->Address.sin6_addr, str, sizeof(str))); + ok(!reply->Address.sin6_port, "got %#x.\n", reply->Address.sin6_port); + ok(!reply->Address.sin6_flowinfo, "got %#lx.\n", reply->Address.sin6_flowinfo); + ok(!reply->Address.sin6_scope_id, "got %#lx.\n", reply->Address.sin6_scope_id); + ok(!memcmp(reply + 1, senddata, sizeof(senddata)), "reply data does not match.\n"); + + memset(&src_addr, 0, sizeof(src_addr)); + memset(&address, 0, sizeof(address)); + event = CreateEventW(NULL, FALSE, FALSE, NULL); + + icmp_send_echo_test_apc_expect = FALSE; + icmp_send_echo_test_line = __LINE__; + memset(&icmp_send_echo_io, 0xcc, sizeof(icmp_send_echo_io)); + memset(replydata, 0xcc, sizeof(replydata)); + replysz = sizeof(replydata); + ret = Icmp6SendEcho2(icmp, NULL, icmp_send_echo_test_apc, (void*)0xdeadc0de, &src_addr, &address, senddata, + sizeof(senddata), NULL, replydata, replysz, 1000); + ok(!ret && GetLastError() == ERROR_INVALID_NETNAME, "got ret %lu, error %ld.\n", ret, GetLastError()); + + ret = inet_pton( AF_INET6, "::1", &address.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + + icmp_send_echo_test_apc_expect = TRUE; + icmp_send_echo_test_line = __LINE__; + memset(&icmp_send_echo_io, 0xcc, sizeof(icmp_send_echo_io)); + memset(replydata, 0xcc, sizeof(replydata)); + replysz = sizeof(replydata); + ret = Icmp6SendEcho2(icmp, NULL, icmp_send_echo_test_apc, (void*)0xdeadc0de, &src_addr, &address, senddata, + sizeof(senddata), NULL, replydata, replysz, 1000); + ok(!ret && GetLastError() == ERROR_IO_PENDING, "got ret %lu, error %ld.\n", ret, GetLastError()); + ret = WaitForSingleObjectEx(event, INFINITE, TRUE); + ok(ret == WAIT_IO_COMPLETION, "got %#lx.\n", ret); + ok(!icmp_send_echo_io.Status, "got %#lx.\n", icmp_send_echo_io.Status); + ok(icmp_send_echo_io.Information == sizeof(replydata), "got %Iu.\n", icmp_send_echo_io.Information); + ok(!reply->Status, "got %#lx.\n", reply->Status); + ok(!icmp_send_echo_test_apc_expect, "APC was not called.\n"); + ok(!memcmp(reply->Address.sin6_addr, &address.sin6_addr, sizeof(address.sin6_addr)), "got %s.\n", + inet_ntop(AF_INET6, (void *)&reply->Address.sin6_addr, str, sizeof(str))); + ok(!reply->Address.sin6_port, "got %#x.\n", reply->Address.sin6_port); + ok(!reply->Address.sin6_flowinfo, "got %#lx.\n", reply->Address.sin6_flowinfo); + ok(!reply->Address.sin6_scope_id, "got %#lx.\n", reply->Address.sin6_scope_id); + ok(!memcmp(reply + 1, senddata, sizeof(senddata)), "reply data does not match.\n"); + + ret = inet_pton( AF_INET6, "::1", &address.sin6_addr); + icmp_send_echo_test_apc_expect = FALSE; + icmp_send_echo_test_line = __LINE__; + memset(replydata, 0xcc, sizeof(replydata)); + replysz = sizeof(replydata); + ret = Icmp6SendEcho2(icmp, event, icmp_send_echo_test_apc, (void*)0xdeadc0de, &src_addr, &address, senddata, + sizeof(senddata), NULL, replydata, replysz, 1000); + ok(!ret && GetLastError() == ERROR_IO_PENDING, "got ret %lu, error %ld.\n", ret, GetLastError()); + ret = WaitForSingleObjectEx(event, INFINITE, TRUE); + ok(ret == WAIT_OBJECT_0, "got %#lx.\n", ret); + icmp_send_echo_test_line = __LINE__; + SleepEx(0, TRUE); + ok(!reply->Status, "got %#lx.\n", reply->Status); + ok(!memcmp(reply->Address.sin6_addr, &address.sin6_addr, sizeof(address.sin6_addr)), "got %s.\n", + inet_ntop(AF_INET6, (void *)&reply->Address.sin6_addr, str, sizeof(str))); + ok(!reply->Address.sin6_port, "got %#x.\n", reply->Address.sin6_port); + ok(!reply->Address.sin6_flowinfo, "got %#lx.\n", reply->Address.sin6_flowinfo); + ok(!reply->Address.sin6_scope_id, "got %#lx.\n", reply->Address.sin6_scope_id); + ok(!memcmp(reply + 1, senddata, sizeof(senddata)), "reply data does not match.\n"); + + memset(replydata, 0xcc, sizeof(replydata)); + replysz = sizeof(replydata); + opt.Ttl = 0; + icmp_send_echo_test_apc_expect = TRUE; + icmp_send_echo_test_line = __LINE__; + memset(&icmp_send_echo_io, 0xcc, sizeof(icmp_send_echo_io)); + ret = Icmp6SendEcho2(icmp, NULL, icmp_send_echo_test_apc, (void*)0xdeadc0de, &src_addr, &address, senddata, + sizeof(senddata), &opt, replydata, replysz, 1000); + ok(!ret && GetLastError() == ERROR_IO_PENDING, "got ret %lu, error %ld.\n", ret, GetLastError()); + ret = WaitForSingleObjectEx(event, INFINITE, TRUE); + ok(ret == WAIT_IO_COMPLETION, "got %#lx.\n", ret); + ok(!icmp_send_echo_test_apc_expect, "APC was not called.\n"); + todo_wine ok(icmp_send_echo_io.Status == STATUS_INVALID_PARAMETER, "got %#lx.\n", icmp_send_echo_io.Status); + ok(icmp_send_echo_io.Information == sizeof(ICMPV6_ECHO_REPLY), "got %Iu.\n", icmp_send_echo_io.Information); + ok(reply->Status == IP_GENERAL_FAILURE, "got %#lx.\n", reply->Status); + ok(IN6_IS_ADDR_UNSPECIFIED((IN6_ADDR *)&reply->Address.sin6_addr), "got %s.\n", + inet_ntop(AF_INET6, (void *)&reply->Address.sin6_addr, str, sizeof(str))); + ok(!reply->Address.sin6_port, "got %#x.\n", reply->Address.sin6_port); + ok(!reply->Address.sin6_flowinfo, "got %#lx.\n", reply->Address.sin6_flowinfo); + ok(!reply->Address.sin6_scope_id, "got %#lx.\n", reply->Address.sin6_scope_id); + + ResetEvent(event); + ret = inet_pton( AF_INET6, "::ffff", &src_addr.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + ret = Icmp6SendEcho2(icmp, event, NULL, NULL, &src_addr, &address, senddata, + sizeof(senddata), NULL, replydata, replysz, 1000); + ok(!ret && GetLastError() == ERROR_INVALID_NETNAME, "got ret %lu, error %ld.\n", ret, GetLastError()); + + CloseHandle(event); + ret = IcmpCloseHandle(icmp); + ok(ret, "got error %u.\n", WSAGetLastError()); +} + static void testIcmp6ParseReplies( void ) { ICMPV6_ECHO_REPLY reply = { 0 }; @@ -1451,6 +1613,7 @@ static void testWinNT4Functions(void) testSetTcpEntry(); testIcmpSendEcho(); testIcmpParseReplies(); + test_Icmp6SendEcho(); testIcmp6ParseReplies(); }
commit `nsiproxy.sys: Store socket type in struct icmp_data.` is causing failures and a hang on macOS:
``` iphlpapi.c:1023: Test failed: IcmpSendEcho failed unexpectedly with error 11010 iphlpapi.c:1028: Test failed: IcmpSendEcho failed unexpectedly with error 11010 iphlpapi.c:1057: Test failed: expected 11050, got 11010 iphlpapi.c:1065: Test failed: IcmpSendEcho failed unexpectedly with error 11010 iphlpapi.c:1071: Test failed: IcmpSendEcho failed unexpectedly with error 11010 iphlpapi.c:1078: Test failed: expected 11050, got 11010 iphlpapi.c:1086: Test failed: expected 11050, got 11010 iphlpapi.c:1095: Test failed: expected 87, got 11010 ```