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..5905bf69cca 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..6968238ab3f 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;
+#include <pshpack1.h> +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; +#include <poppack.h> + +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 5905bf69cca..3c6832c22dd 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 3c6832c22dd..eaaf7bdc632 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(); }
WRT patch 1 ("nsiproxy.sys: Get rid of echo request thread."), it doesn't look like the icmp sending part is supposed to be ever blocked for considerable time, so extra queue for performing send looks unneeded. That allows to return some types of failures synchronously (like Windows does) from the Unix side handling.
WRT "nsiproxy.sys: Don't try to check for original packet for ping socket.", ping sockets (both ipv4 and ipv6) are not supposed to receive unrelated packets (unlike raw sockets) and don't have original raw packet appended. In fact, those sockets do not seem to receive icmp error packets at all (probably because most of them are originating not from target host but from something in between), while maybe this part in theory can be improved in the kernel in the future.