From: Paul Gofman <pgofman@codeweavers.com> --- dlls/nsiproxy.sys/device.c | 91 +++++++++------ dlls/nsiproxy.sys/icmp_echo.c | 166 +++++++++++++++++---------- dlls/nsiproxy.sys/nsi.c | 2 - dlls/nsiproxy.sys/nsiproxy_private.h | 12 +- dlls/nsiproxy.sys/unix_private.h | 2 - 5 files changed, 161 insertions(+), 112 deletions(-) diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index 18adc4ec037..b7180d2cb94 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -19,6 +19,7 @@ */ #include <stdarg.h> +#include <assert.h> #include "ntstatus.h" #include "windef.h" @@ -61,8 +62,6 @@ static NTSTATUS nsiproxy_call( unsigned int code, void *args ) enum unix_calls { - icmp_cancel_listen, - icmp_close, icmp_get_reply, icmp_send_echo, nsi_enumerate_all_ex, @@ -184,35 +183,41 @@ static NTSTATUS nsiproxy_get_parameter( IRP *irp ) return status; } -static inline icmp_handle irp_get_icmp_handle( IRP *irp ) +struct icmp_data { - return PtrToUlong( irp->Tail.Overlay.DriverContext[0] ); + icmp_handle handle; + HANDLE completion_event; + HANDLE wait_object; +}; + +static void free_icmp_data( struct icmp_data *data ) +{ + UnregisterWait( data->wait_object ); + CloseHandle( data->completion_event ); + free( data ); +} + +static struct icmp_data *irp_get_icmp_data( IRP *irp ) +{ + return irp->Tail.Overlay.DriverContext[0]; } -static inline icmp_handle irp_set_icmp_handle( IRP *irp, icmp_handle handle ) +static struct icmp_data *irp_set_icmp_data( IRP *irp, struct icmp_data *data ) { - return PtrToUlong( InterlockedExchangePointer( irp->Tail.Overlay.DriverContext, - ULongToPtr( handle ) ) ); + return InterlockedExchangePointer( irp->Tail.Overlay.DriverContext, data ); } DECLARE_CRITICAL_SECTION( icmp_echo_completion_cs ); static void WINAPI icmp_echo_cancel( DEVICE_OBJECT *device, IRP *irp ) { - struct icmp_cancel_listen_params params; + struct icmp_data *data; TRACE( "device %p, irp %p.\n", device, irp ); IoReleaseCancelSpinLock( irp->CancelIrql ); EnterCriticalSection( &icmp_echo_completion_cs ); - - /* If the handle is not set, either the irp is still - in the request queue, in which case the request thread will - cancel it, or the irp has already finished. If the handle - does exist then notify the listen thread. In all cases the irp - will be completed elsewhere. */ - params.handle = irp_get_icmp_handle( irp ); - if (params.handle) nsiproxy_call( icmp_cancel_listen, ¶ms ); + if ((data = irp_get_icmp_data( irp ))) SetEvent( data->completion_event ); LeaveCriticalSection( &icmp_echo_completion_cs ); } @@ -225,48 +230,56 @@ static int icmp_echo_reply_struct_len( ULONG family, ULONG bits ) return 0; } -static DWORD WINAPI icmp_wait_reply( void *arg ) +static void CALLBACK icmp_wait_complete( void *arg, BOOLEAN timed_out ) { 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_get_reply_params params; + struct icmp_data *data; NTSTATUS status; - TRACE( "\n" ); + TRACE( "irp %p, timed_out %d.\n", irp, timed_out ); + EnterCriticalSection( &icmp_echo_completion_cs ); + data = irp_set_icmp_data( irp, NULL ); + assert( data ); params.user_reply_ptr = in->user_reply_ptr; - params.handle = irp_get_icmp_handle( irp ); - params.timeout = in->timeout; + params.handle = data->handle; params.bits = in->bits; params.reply = irp->AssociatedIrp.SystemBuffer; params.reply_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength; status = nsiproxy_call( icmp_get_reply, ¶ms ); - TRACE( "icmp_get_reply rets %08lx\n", status ); - - EnterCriticalSection( &icmp_echo_completion_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; + irp->IoStatus.Information = 0; + if (irp->Cancel) + { + TRACE( "irp %p cancelled.\n", irp ); + irp->IoStatus.Status = STATUS_CANCELLED; + } else - irp->IoStatus.Information = 0; + { + irp->IoStatus.Status = status; + if (status == STATUS_SUCCESS) + irp->IoStatus.Information = params.reply_len; + } LeaveCriticalSection( &icmp_echo_completion_cs ); IoCompleteRequest( irp, IO_NO_INCREMENT ); - return 0; + free_icmp_data( data ); } 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; + struct icmp_data *data; NTSTATUS status; + data = calloc( 1, sizeof(*data) ); + data->completion_event = CreateEventW( NULL, TRUE, FALSE, NULL ); + irp_set_icmp_data( irp, data ); + TRACE( "\n" ); params.request = in->data + ((in->opt_size + 3) & ~3); params.request_size = in->req_size; @@ -277,7 +290,8 @@ static NTSTATUS handle_send_echo( IRP *irp ) params.tos = in->tos; params.src = &in->src; params.dst = &in->dst; - params.handle = &handle; + params.completion_event = data->completion_event; + params.handle = &data->handle; status = nsiproxy_call( icmp_send_echo, ¶ms ); TRACE( "icmp_send_echo status %#lx\n", status ); @@ -287,17 +301,24 @@ static NTSTATUS handle_send_echo( IRP *irp ) irp->IoStatus.Status = status; if (status == STATUS_SUCCESS) irp->IoStatus.Information = params.reply_len; + free_icmp_data( data ); return status; } IoSetCancelRoutine( irp, icmp_echo_cancel ); if (irp->Cancel && IoSetCancelRoutine( irp, NULL )) { /* IRP was canceled before we set cancel routine */ + icmp_wait_complete( irp, FALSE ); return STATUS_CANCELLED; } IoMarkIrpPending( irp ); - irp_set_icmp_handle( irp, handle ); - RtlQueueWorkItem( icmp_wait_reply, irp, WT_EXECUTELONGFUNCTION ); + if (in->dst.si_family == AF_INET6 && !in->hop_limit) + { + /* This should fail at once but asynchronously. */ + SetEvent( data->completion_event ); + } + RegisterWaitForSingleObject( &data->wait_object, data->completion_event, icmp_wait_complete, irp, in->timeout, + WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD ); return STATUS_PENDING; } diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c index 0de8ec5d2e9..7eb2675a16f 100644 --- a/dlls/nsiproxy.sys/icmp_echo.c +++ b/dlls/nsiproxy.sys/icmp_echo.c @@ -31,6 +31,7 @@ #include <pthread.h> #include <poll.h> #include <sys/socket.h> +#include <assert.h> #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> @@ -150,13 +151,23 @@ struct icmp_data struct sockaddr_storage dst_storage; int dst_len; struct icmp_socket *s; + struct icmp_reply_ctx reply_ctx; + HANDLE completion_event; + BOOL polling; + NTSTATUS status; + HANDLE listen_thread; }; + +static pthread_mutex_t listen_lock = PTHREAD_MUTEX_INITIALIZER; + #define MAX_HANDLES 256 /* Max number of simultaneous pings - could become dynamic if need be */ static struct icmp_data *handle_table[MAX_HANDLES]; static pthread_mutex_t handle_lock = PTHREAD_MUTEX_INITIALIZER; static struct icmp_data **next_free, **next_unused = handle_table; +static void icmp_listen( void *args ); + static icmp_handle handle_alloc( struct icmp_data *data ) { struct icmp_data **entry; @@ -784,8 +795,24 @@ static void icmp_release_socket( struct icmp_socket *s ) free( s ); } +static BOOL icmp_data_stop_polling( struct icmp_data *data, NTSTATUS status ) +{ + BOOL ret; + + pthread_mutex_lock( &listen_lock ); + if ((ret = data->polling)) + { + data->polling = FALSE; + data->status = status; + write( data->cancel_pipe[1], "x", 1 ); + } + pthread_mutex_unlock( &listen_lock ); + return ret; +} + static void icmp_data_free( struct icmp_data *data ) { + assert( !data->polling ); icmp_release_socket( data->s ); close( data->cancel_pipe[0] ); close( data->cancel_pipe[1] ); @@ -802,7 +829,7 @@ static NTSTATUS icmp_data_create( struct icmp_send_echo_params *params, struct i else if (params->dst->si_family == WS_AF_INET) ops = &ipv4; else return STATUS_INVALID_PARAMETER; - data = malloc( sizeof(*data) ); + data = calloc( 1, sizeof(*data) ); if (!data) return STATUS_NO_MEMORY; if (!(data->s = calloc( 1, sizeof(*data->s) ))) { @@ -851,8 +878,8 @@ static NTSTATUS icmp_data_create( struct icmp_send_echo_params *params, struct i } data->s->ops = ops; - data->s->ops->set_socket_opts( data->s ); - *icmp_data = data; + data->s->ops->set_socket_opts( data->s ); *icmp_data = data; + data->completion_event = params->completion_event; return STATUS_SUCCESS; } @@ -861,7 +888,7 @@ 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 icmp_data *data; - int ret; + int ret, err; NTSTATUS status; @@ -878,35 +905,40 @@ NTSTATUS icmp_send_echo( void *args ) memcpy( icmp_hdr + 1, params->request, params->request_size ); icmp_hdr->checksum = data->s->ops->chksum( data, (BYTE *)icmp_hdr, sizeof(*icmp_hdr) + params->request_size ); + pthread_mutex_lock( &listen_lock ); + data->polling = TRUE; + pthread_mutex_unlock( &listen_lock ); + + if (PsCreateSystemThread( &data->listen_thread, THREAD_ALL_ACCESS, NULL, 0, NULL, icmp_listen, data )) + ERR( "Could not create listen thread.\n" ); + NtQueryPerformanceCounter( &data->send_time, NULL ); ret = sendto( data->s->socket, icmp_hdr, sizeof(*icmp_hdr) + params->request_size, 0, (struct sockaddr *)&data->dst_storage, data->dst_len ); + if (ret < 0) err = errno; free( icmp_hdr ); if (ret < 0) { - TRACE( "sendto() rets %d errno %d\n", ret, errno ); - params->reply_len = data->s->ops->set_reply_ip_status( errno_to_ip_status( errno ), params->bits, params->reply ); + status = errno_to_ip_status( err ); + TRACE( "sendto() rets %d errno %d\n", ret, err ); + icmp_data_stop_polling( data, status ); + NtWaitForSingleObject( data->listen_thread, FALSE, NULL ); + NtClose( data->listen_thread ); + params->reply_len = data->s->ops->set_reply_ip_status( status, params->bits, params->reply ); icmp_data_free( data ); return STATUS_SUCCESS; } *params->handle = handle_alloc( data ); - if (!*params->handle) icmp_data_free( data ); + if (!*params->handle) + { + icmp_data_stop_polling( data, STATUS_NO_MEMORY ); + icmp_data_free( data ); + } return *params->handle ? STATUS_PENDING : STATUS_NO_MEMORY; } -static int get_timeout( LARGE_INTEGER start, UINT timeout ) -{ - LARGE_INTEGER now, end; - - end.QuadPart = start.QuadPart + (ULONGLONG)timeout * 10000; - NtQueryPerformanceCounter( &now, NULL ); - if (now.QuadPart >= end.QuadPart) return 0; - - return min( (end.QuadPart - now.QuadPart) / 10000, INT_MAX ); -} - static ULONG get_rtt( LARGE_INTEGER start ) { LARGE_INTEGER now; @@ -915,7 +947,7 @@ static ULONG get_rtt( LARGE_INTEGER start ) return (now.QuadPart - start.QuadPart) / 10000; } -static NTSTATUS recv_msg( struct icmp_data *data, struct icmp_get_reply_params *params ) +static NTSTATUS recv_msg( struct icmp_data *data ) { struct sockaddr_storage addr; struct icmp_reply_ctx ctx; @@ -943,77 +975,87 @@ static NTSTATUS recv_msg( struct icmp_data *data, struct icmp_get_reply_params * sockaddr_to_SOCKADDR_INET( (struct sockaddr *)&addr, &ctx.addr ); ctx.round_trip_time = get_rtt( data->send_time ); ctx.data_offset = (BYTE *)(icmp_hdr + 1) - ctx.packet; - - if (!data->s->ops->fill_reply( params, &ctx )) - params->reply_len = data->s->ops->set_reply_ip_status( IP_GENERAL_FAILURE, params->bits, params->reply ); - + data->reply_ctx = ctx; return STATUS_SUCCESS; } -NTSTATUS icmp_get_reply( void *args ) +static void icmp_listen( void *args ) { - struct icmp_get_reply_params *params = args; - struct icmp_data *data; + struct icmp_data *data = args; struct pollfd fds[2]; NTSTATUS status; int ret; - data = handle_data( params->handle ); - if (!data) return STATUS_INVALID_PARAMETER; - - if (data->dst_storage.ss_family == AF_INET6 && !data->s->hop_limit) - { - TRACE( "Invalid hop_limit.\n" ); - params->reply_len = data->s->ops->set_reply_ip_status( IP_GENERAL_FAILURE, params->bits, params->reply ); - return STATUS_SUCCESS; - } - fds[0].fd = data->s->socket; fds[0].events = POLLIN; fds[1].fd = data->cancel_pipe[0]; fds[1].events = POLLIN; - while ((ret = poll( fds, ARRAY_SIZE(fds), get_timeout( data->send_time, params->timeout ) )) > 0) + while ((ret = poll( fds, ARRAY_SIZE(fds), -1 )) > 0) { if (fds[1].revents & POLLIN) { TRACE( "cancelled\n" ); - return STATUS_CANCELLED; + icmp_data_stop_polling( data, STATUS_CANCELLED ); + return; } - status = recv_msg( data, params ); + pthread_mutex_lock( &listen_lock ); + if (data->polling) + { + status = recv_msg( data ); + pthread_mutex_unlock( &listen_lock ); + if (status != STATUS_RETRY) + { + data->status = status; + data->polling = FALSE; + NtSetEvent( data->completion_event, NULL ); + } + } + else status = STATUS_CANCELLED; + pthread_mutex_unlock( &listen_lock ); if (status == STATUS_RETRY) continue; - return status; + return; } - - if (!ret) /* timeout */ - { - TRACE( "timeout\n" ); - params->reply_len = data->s->ops->set_reply_ip_status( IP_REQ_TIMED_OUT, params->bits, params->reply ); - return STATUS_SUCCESS; - } - /* ret < 0 */ - params->reply_len = data->s->ops->set_reply_ip_status( errno_to_ip_status( errno ), params->bits, params->reply ); - return STATUS_SUCCESS; } -NTSTATUS icmp_cancel_listen( void *args ) +NTSTATUS icmp_get_reply( void *args ) { - struct icmp_cancel_listen_params *params = args; - struct icmp_data *data = handle_data( params->handle ); + struct icmp_get_reply_params *params = args; + struct icmp_data *data; + NTSTATUS ret = STATUS_SUCCESS; + data = handle_data( params->handle ); if (!data) return STATUS_INVALID_PARAMETER; - write( data->cancel_pipe[1], "x", 1 ); - return STATUS_SUCCESS; -} -NTSTATUS icmp_close( void *args ) -{ - struct icmp_close_params *params = args; - struct icmp_data *data = handle_data( params->handle ); + if (data->dst_storage.ss_family == AF_INET6 && !data->s->hop_limit) + { + TRACE( "data %p, seq %u, invalid hop_limit.\n", data, data->seq ); + icmp_data_stop_polling( data, STATUS_SUCCESS ); + params->reply_len = data->s->ops->set_reply_ip_status( IP_GENERAL_FAILURE, params->bits, params->reply ); + goto done; + } + + if (icmp_data_stop_polling( data, STATUS_TIMEOUT )) + { + /* Was still polling, timeout. */ + TRACE( "data %p, seq %u, timeout.\n", data, data->seq ); + params->reply_len = data->s->ops->set_reply_ip_status( IP_REQ_TIMED_OUT, params->bits, params->reply ); + } + else if (!(ret = data->status)) + { + if (!data->s->ops->fill_reply( params, &data->reply_ctx )) + { + WARN( "data %p, seq %u, buffer too small.\n", data, data->seq ); + params->reply_len = data->s->ops->set_reply_ip_status( IP_GENERAL_FAILURE, params->bits, params->reply ); + } + else TRACE( "data %p, seq %u, got reply %u.\n", data, data->seq, data->reply_ctx.status ); + } +done: + NtWaitForSingleObject( data->listen_thread, FALSE, NULL ); + NtClose( data->listen_thread ); - if (!data) return STATUS_INVALID_PARAMETER; icmp_data_free( data ); handle_free( params->handle ); - return STATUS_SUCCESS; + return ret; } diff --git a/dlls/nsiproxy.sys/nsi.c b/dlls/nsiproxy.sys/nsi.c index 41aace2c2c4..09ced8f814e 100644 --- a/dlls/nsiproxy.sys/nsi.c +++ b/dlls/nsiproxy.sys/nsi.c @@ -343,8 +343,6 @@ static NTSTATUS unix_nsi_get_notification( void *args ) const unixlib_entry_t __wine_unix_call_funcs[] = { - icmp_cancel_listen, - icmp_close, icmp_get_reply, icmp_send_echo, unix_nsi_enumerate_all_ex, diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h index a15d594bbdd..cd1a7508087 100644 --- a/dlls/nsiproxy.sys/nsiproxy_private.h +++ b/dlls/nsiproxy.sys/nsiproxy_private.h @@ -20,23 +20,12 @@ typedef UINT icmp_handle; -struct icmp_cancel_listen_params -{ - icmp_handle handle; -}; - -struct icmp_close_params -{ - icmp_handle handle; -}; - struct icmp_get_reply_params { icmp_handle handle; void *reply; ULONGLONG user_reply_ptr; unsigned int bits, reply_len; - int timeout; }; struct icmp_send_echo_params @@ -47,6 +36,7 @@ struct icmp_send_echo_params UINT request_size, reply_len; BYTE bits, ttl, tos; int hop_limit; + HANDLE completion_event; icmp_handle *handle; }; diff --git a/dlls/nsiproxy.sys/unix_private.h b/dlls/nsiproxy.sys/unix_private.h index 8a332f2ba07..170bdba665d 100644 --- a/dlls/nsiproxy.sys/unix_private.h +++ b/dlls/nsiproxy.sys/unix_private.h @@ -177,7 +177,5 @@ static inline int ascii_strcasecmp( const char *s1, const char *s2 ) return ascii_strncasecmp( s1, s2, -1 ); } -NTSTATUS icmp_cancel_listen( void *args ); -NTSTATUS icmp_close( void *args ); NTSTATUS icmp_get_reply( void *args ); NTSTATUS icmp_send_echo( void *args ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10954