From a72f91f5f5bad7ebb00997e8d5a3e5979d74e527 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 25 Jul 2023 19:26:18 -0600 Subject: [PATCH] nsi: Forward request to nsiproxy from NsiRequestChangeNotification(). --- dlls/nsi/nsi.c | 57 +++++++++++++++++++++++++++++++------- dlls/nsi/tests/nsi.c | 13 +++++---- dlls/nsiproxy.sys/device.c | 46 ++++++++++++++++++++++++++++++ include/wine/nsi.h | 8 ++++++ 4 files changed, 108 insertions(+), 16 deletions(-) diff --git a/dlls/nsi/nsi.c b/dlls/nsi/nsi.c index c219d59ccf3..a44339fc3af 100644 --- a/dlls/nsi/nsi.c +++ b/dlls/nsi/nsi.c @@ -31,6 +31,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(nsi); static HANDLE nsi_device = INVALID_HANDLE_VALUE; +static HANDLE nsi_device_async = INVALID_HANDLE_VALUE; BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved) { @@ -41,23 +42,26 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved) break; case DLL_PROCESS_DETACH: if (nsi_device != INVALID_HANDLE_VALUE) CloseHandle( nsi_device ); + if (nsi_device_async != INVALID_HANDLE_VALUE) CloseHandle( nsi_device_async ); break; } return TRUE; } -static inline HANDLE get_nsi_device( void ) +static inline HANDLE get_nsi_device( BOOL async ) { + HANDLE *cached_device = async ? &nsi_device_async : &nsi_device; HANDLE device; - if (nsi_device == INVALID_HANDLE_VALUE) + if (*cached_device == INVALID_HANDLE_VALUE) { - device = CreateFileW( L"\\\\.\\Nsi", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + device = CreateFileW( L"\\\\.\\Nsi", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + async ? FILE_FLAG_OVERLAPPED : 0, NULL ); if (device != INVALID_HANDLE_VALUE - && InterlockedCompareExchangePointer( &nsi_device, device, INVALID_HANDLE_VALUE ) != INVALID_HANDLE_VALUE) + && InterlockedCompareExchangePointer( cached_device, device, INVALID_HANDLE_VALUE ) != INVALID_HANDLE_VALUE) CloseHandle( device ); } - return nsi_device; + return *cached_device; } DWORD WINAPI NsiAllocateAndGetTable( DWORD unk, const NPI_MODULEID *module, DWORD table, void **key_data, DWORD key_size, @@ -155,7 +159,7 @@ DWORD WINAPI NsiEnumerateObjectsAllParameters( DWORD unk, DWORD unk2, const NPI_ DWORD WINAPI NsiEnumerateObjectsAllParametersEx( struct nsi_enumerate_all_ex *params ) { DWORD out_size, received, err = ERROR_SUCCESS; - HANDLE device = get_nsi_device(); + HANDLE device = get_nsi_device( FALSE ); struct nsiproxy_enumerate_all in; BYTE *out, *ptr; @@ -235,7 +239,7 @@ DWORD WINAPI NsiGetAllParameters( DWORD unk, const NPI_MODULEID *module, DWORD t DWORD WINAPI NsiGetAllParametersEx( struct nsi_get_all_parameters_ex *params ) { - HANDLE device = get_nsi_device(); + HANDLE device = get_nsi_device( FALSE ); struct nsiproxy_get_all_parameters *in; ULONG in_size = FIELD_OFFSET( struct nsiproxy_get_all_parameters, key[params->key_size] ), received; ULONG out_size = params->rw_size + params->dynamic_size + params->static_size; @@ -304,7 +308,7 @@ DWORD WINAPI NsiGetParameter( DWORD unk, const NPI_MODULEID *module, DWORD table DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ) { - HANDLE device = get_nsi_device(); + HANDLE device = get_nsi_device( FALSE ); struct nsiproxy_get_parameter *in; ULONG in_size = FIELD_OFFSET( struct nsiproxy_get_parameter, key[params->key_size] ), received; DWORD err = ERROR_SUCCESS; @@ -345,7 +349,40 @@ DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module DWORD WINAPI NsiRequestChangeNotificationEx( struct nsi_request_change_notification_ex *params ) { - FIXME( "%p stub.\n", params ); + HANDLE device = get_nsi_device( TRUE ); + struct nsiproxy_request_notification *in; + ULONG in_size = sizeof(struct nsiproxy_get_parameter), received; + OVERLAPPED overlapped, *ovr; + DWORD err = ERROR_SUCCESS; + DWORD len; - return ERROR_NOT_SUPPORTED; + TRACE( "%p.\n", params ); + + if (params->unk) FIXME( "unknown parameter %#lx.\n", params->unk ); + + if (device == INVALID_HANDLE_VALUE) return GetLastError(); + + in = malloc( in_size ); + if (!in) return ERROR_OUTOFMEMORY; + in->module = *params->module; + in->table = params->table; + + if (!(ovr = params->ovr)) + { + overlapped.hEvent = CreateEventW( NULL, FALSE, FALSE, NULL ); + ovr = &overlapped; + } + if (!DeviceIoControl( device, IOCTL_NSIPROXY_WINE_CHANGE_NOTIFICATION, in, in_size, NULL, 0, &received, ovr )) + err = GetLastError(); + if (ovr == &overlapped) + { + if (err == ERROR_IO_PENDING) + err = GetOverlappedResult( device, ovr, &len, TRUE ) ? 0 : GetLastError(); + CloseHandle( overlapped.hEvent ); + } + else if (params->handle && ovr && err == ERROR_IO_PENDING) + *params->handle = device; + + free( in ); + return err; } diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index 5b3a6775992..6d759debfd6 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -1039,7 +1039,7 @@ void test_change_notifications(void) handle = (HANDLE)0xdeadbeef; ret = NsiRequestChangeNotification( 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, &ovr, &handle ); - todo_wine ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); + ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); memset( ¶ms, 0, sizeof(params) ); handle2 = (HANDLE)0xdeadbeef; @@ -1049,11 +1049,11 @@ void test_change_notifications(void) params.ovr = &ovr2; params.handle = &handle2; ret = NsiRequestChangeNotificationEx( ¶ms ); - todo_wine ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); + ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); ok( handle2 == handle, "got %p, %p.\n", handle, handle2 ); bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); - todo_wine ok( !bret && GetLastError() == ERROR_IO_INCOMPLETE, "got bret %d, err %lu.\n", bret, GetLastError() ); + ok( !bret && GetLastError() == ERROR_IO_INCOMPLETE, "got bret %d, err %lu.\n", bret, GetLastError() ); ret = NsiCancelChangeNotification( NULL ); todo_wine ok( ret == ERROR_NOT_FOUND, "got %lu.\n", ret ); @@ -1063,12 +1063,13 @@ void test_change_notifications(void) bytes = 0xdeadbeef; bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); + todo_wine ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); todo_wine ok( ovr.Internal == (ULONG)STATUS_CANCELLED, "got %Ix.\n", ovr.Internal ); - ok( !bytes, "got %lu.\n", bytes ); + todo_wine ok( !bytes, "got %lu.\n", bytes ); bret = GetOverlappedResult( handle2, &ovr2, &bytes, FALSE ); - todo_wine ok( !bret && GetLastError() == ERROR_IO_INCOMPLETE, "got bret %d, err %lu.\n", bret, GetLastError() ); + ok( !bret && GetLastError() == ERROR_IO_INCOMPLETE, "got bret %d, err %lu.\n", bret, GetLastError() ); ret = NsiCancelChangeNotification( &ovr2 ); todo_wine ok( !ret, "got %lu.\n", ret ); bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); @@ -1078,7 +1079,7 @@ void test_change_notifications(void) todo_wine ok( ret == ERROR_INVALID_PARAMETER, "got %lu.\n", ret ); ret = NsiRequestChangeNotification( 0, &NPI_MS_IPV4_MODULEID, NSI_IP_FORWARD_TABLE, &ovr, &handle ); - todo_wine ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); + ok( ret == ERROR_IO_PENDING, "got %lu.\n", ret ); ret = NsiCancelChangeNotification( &ovr ); todo_wine ok( !ret, "got %lu.\n", ret ); bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index 87d634ff5c0..3bac642ec79 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -49,6 +49,7 @@ 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 ); static NTSTATUS nsiproxy_call( unsigned int code, void *args ) { @@ -261,6 +262,47 @@ static NTSTATUS nsiproxy_icmp_echo( IRP *irp ) return STATUS_PENDING; } +static void WINAPI change_notification_cancel( DEVICE_OBJECT *device, IRP *irp ) +{ + TRACE( "device %p, irp %p.\n", device, irp ); + + IoReleaseCancelSpinLock( irp->CancelIrql ); + + EnterCriticalSection( &nsiproxy_cs ); + RemoveEntryList( &irp->Tail.Overlay.ListEntry ); + LeaveCriticalSection( &nsiproxy_cs ); + + irp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest( irp, IO_NO_INCREMENT ); +} + +static NTSTATUS nsiproxy_change_notification( IRP *irp ) +{ + IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); + struct nsiproxy_request_notification *in = (struct nsiproxy_request_notification *)irp->AssociatedIrp.SystemBuffer; + DWORD in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength; + + FIXME( "\n" ); + + if (in_len < sizeof(*in)) return STATUS_INVALID_PARAMETER; + /* FIXME: validate module and table. */ + + EnterCriticalSection( &nsiproxy_cs ); + IoSetCancelRoutine( irp, change_notification_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( ¬ification_queue, &irp->Tail.Overlay.ListEntry ); + IoMarkIrpPending( irp ); + LeaveCriticalSection( &nsiproxy_cs ); + + return STATUS_PENDING; +} + static NTSTATUS WINAPI nsi_ioctl( DEVICE_OBJECT *device, IRP *irp ) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); @@ -289,6 +331,10 @@ static NTSTATUS WINAPI nsi_ioctl( DEVICE_OBJECT *device, IRP *irp ) status = nsiproxy_icmp_echo( irp ); break; + case IOCTL_NSIPROXY_WINE_CHANGE_NOTIFICATION: + status = nsiproxy_change_notification( irp ); + break; + default: FIXME( "ioctl %lx not supported\n", irpsp->Parameters.DeviceIoControl.IoControlCode ); status = STATUS_NOT_SUPPORTED; diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 1a562c3c36d..6bf3ce5bd5d 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -380,6 +380,7 @@ struct nsi_udp_endpoint_static #define IOCTL_NSIPROXY_WINE_GET_ALL_PARAMETERS CTL_CODE(FILE_DEVICE_NETWORK, 0x401, METHOD_BUFFERED, 0) #define IOCTL_NSIPROXY_WINE_GET_PARAMETER CTL_CODE(FILE_DEVICE_NETWORK, 0x402, METHOD_BUFFERED, 0) #define IOCTL_NSIPROXY_WINE_ICMP_ECHO CTL_CODE(FILE_DEVICE_NETWORK, 0x403, METHOD_BUFFERED, 0) +#define IOCTL_NSIPROXY_WINE_CHANGE_NOTIFICATION CTL_CODE(FILE_DEVICE_NETWORK, 0x404, METHOD_BUFFERED, 0) /* input for IOCTL_NSIPROXY_WINE_ENUMERATE_ALL */ struct nsiproxy_enumerate_all @@ -436,6 +437,13 @@ struct nsiproxy_icmp_echo BYTE data[1]; /* ((opt_size + 3) & ~3) + req_size */ }; +/* input for IOCTL_NSIPROXY_WINE_CHANGE_NOTIFICATION */ +struct nsiproxy_request_notification +{ + NPI_MODULEID module; + UINT table; +}; + /* Undocumented Nsi api */ #define NSI_PARAM_TYPE_RW 0