From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsi/nsi.c | 8 ++++++++ dlls/nsi/nsi.spec | 2 +- include/wine/nsi.h | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/dlls/nsi/nsi.c b/dlls/nsi/nsi.c index 3f324ef555b..d7dd9f09090 100644 --- a/dlls/nsi/nsi.c +++ b/dlls/nsi/nsi.c @@ -307,3 +307,11 @@ err: CloseHandle( device ); return err; } + +DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module, DWORD table, OVERLAPPED *ovr, + HANDLE *handle ) +{ + FIXME( "%lu %p %lu %p %p stub.\n", unk, module, table, ovr, handle ); + + return ERROR_NOT_SUPPORTED; +} diff --git a/dlls/nsi/nsi.spec b/dlls/nsi/nsi.spec index ba326572fb8..ca2dc21168c 100644 --- a/dlls/nsi/nsi.spec +++ b/dlls/nsi/nsi.spec @@ -16,7 +16,7 @@ @ stdcall NsiGetParameterEx(ptr) @ stub NsiRegisterChangeNotification @ stub NsiRegisterChangeNotificationEx -@ stub NsiRequestChangeNotification +@ stdcall NsiRequestChangeNotification(long ptr long ptr ptr) @ stub NsiRequestChangeNotificationEx @ stub NsiSetAllParameters @ stub NsiSetAllParametersEx diff --git a/include/wine/nsi.h b/include/wine/nsi.h index af35593b29c..30713ae4a80 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -508,5 +508,7 @@ DWORD WINAPI NsiGetAllParametersEx( struct nsi_get_all_parameters_ex *params ); DWORD WINAPI NsiGetParameter( DWORD unk, const NPI_MODULEID *module, DWORD table, const void *key, DWORD key_size, DWORD param_type, void *data, DWORD data_size, DWORD data_offset ); DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ); +DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module, DWORD table, OVERLAPPED *ovr, + HANDLE *handle );
#endif /* __WINE_NSI_H */
From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsi/nsi.c | 7 +++++++ dlls/nsi/nsi.spec | 2 +- include/wine/nsi.h | 1 + 3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/dlls/nsi/nsi.c b/dlls/nsi/nsi.c index d7dd9f09090..76193c42982 100644 --- a/dlls/nsi/nsi.c +++ b/dlls/nsi/nsi.c @@ -315,3 +315,10 @@ DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module
return ERROR_NOT_SUPPORTED; } + +DWORD WINAPI NsiCancelChangeNotification( OVERLAPPED *ovr ) +{ + FIXME( "%p stub.\n", ovr ); + + return ERROR_NOT_SUPPORTED; +} diff --git a/dlls/nsi/nsi.spec b/dlls/nsi/nsi.spec index ca2dc21168c..ed391e98138 100644 --- a/dlls/nsi/nsi.spec +++ b/dlls/nsi/nsi.spec @@ -1,6 +1,6 @@ @ stub NsiAllocateAndGetPersistentDataWithMaskTable @ stdcall NsiAllocateAndGetTable(long ptr long ptr long ptr long ptr long ptr long ptr long) -@ stub NsiCancelChangeNotification +@ stdcall NsiCancelChangeNotification(ptr) @ stub NsiDeregisterChangeNotification @ stub NsiDeregisterChangeNotificationEx @ stdcall NsiEnumerateObjectsAllParameters(long long ptr long ptr long ptr long ptr long ptr long ptr) diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 30713ae4a80..ee83dbb82e5 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -510,5 +510,6 @@ DWORD WINAPI NsiGetParameter( DWORD unk, const NPI_MODULEID *module, DWORD table DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ); DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module, DWORD table, OVERLAPPED *ovr, HANDLE *handle ); +DWORD WINAPI NsiCancelChangeNotification( OVERLAPPED *ovr );
#endif /* __WINE_NSI_H */
From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsi/tests/nsi.c | 58 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+)
diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index 4cf49993ea1..d1aa7071d06 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -18,6 +18,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "ntstatus.h" +#define WIN32_NO_STATUS #include "winsock2.h" #include "winternl.h" #include "ws2ipdef.h" @@ -1023,6 +1025,60 @@ static void test_udp_tables( int family ) winetest_pop_context(); }
+void test_change_notifications(void) +{ + HANDLE handle, handle2; + OVERLAPPED ovr, ovr2; + DWORD bytes; + DWORD ret; + BOOL bret; + + memset( &ovr, 0, sizeof(ovr) ); + ovr.hEvent = CreateEventW( NULL, FALSE, FALSE, NULL ); + + 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 ); + + handle2 = (HANDLE)0xdeadbeef; + memset( &ovr2, 0, sizeof(ovr2) ); + ret = NsiRequestChangeNotification( 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, &ovr2, &handle2 ); + todo_wine 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() ); + + ret = NsiCancelChangeNotification( NULL ); + todo_wine ok( ret == ERROR_NOT_FOUND, "got %lu.\n", ret ); + + ret = NsiCancelChangeNotification( &ovr ); + todo_wine ok( !ret, "got %lu.\n", ret ); + + 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 ); + + bret = GetOverlappedResult( handle2, &ovr2, &bytes, FALSE ); + todo_wine 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 ); + todo_wine ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); + + ret = NsiRequestChangeNotification( 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_INDEX_LUID_TABLE, &ovr, &handle ); + 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 ); + ret = NsiCancelChangeNotification( &ovr ); + todo_wine ok( !ret, "got %lu.\n", ret ); + bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); + todo_wine ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); +} + START_TEST( nsi ) { test_nsi_api(); @@ -1056,4 +1112,6 @@ START_TEST( nsi ) test_udp_stats( AF_INET6 ); test_udp_tables( AF_INET ); test_udp_tables( AF_INET6 ); + + test_change_notifications(); }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsi/nsi.c | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-)
diff --git a/dlls/nsi/nsi.c b/dlls/nsi/nsi.c index 76193c42982..9cb8e7d451b 100644 --- a/dlls/nsi/nsi.c +++ b/dlls/nsi/nsi.c @@ -30,9 +30,34 @@
WINE_DEFAULT_DEBUG_CHANNEL(nsi);
+static HANDLE nsi_device = INVALID_HANDLE_VALUE; + +BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls( hinst ); + break; + case DLL_PROCESS_DETACH: + if (nsi_device != INVALID_HANDLE_VALUE) CloseHandle( nsi_device ); + break; + } + return TRUE; +} + static inline HANDLE get_nsi_device( void ) { - return CreateFileW( L"\\.\Nsi", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + HANDLE device; + + if (nsi_device == INVALID_HANDLE_VALUE) + { + device = CreateFileW( L"\\.\Nsi", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + if (device != INVALID_HANDLE_VALUE + && InterlockedCompareExchangePointer( &nsi_device, device, INVALID_HANDLE_VALUE ) != INVALID_HANDLE_VALUE) + CloseHandle( device ); + } + return nsi_device; }
DWORD WINAPI NsiAllocateAndGetTable( DWORD unk, const NPI_MODULEID *module, DWORD table, void **key_data, DWORD key_size, @@ -133,11 +158,7 @@ DWORD WINAPI NsiEnumerateObjectsAllParametersEx( struct nsi_enumerate_all_ex *pa (params->key_size + params->rw_size + params->dynamic_size + params->static_size) * params->count;
out = heap_alloc( out_size ); - if (!out) - { - CloseHandle( device ); - return ERROR_OUTOFMEMORY; - } + if (!out) return ERROR_OUTOFMEMORY;
in.module = *params->module; in.first_arg = params->first_arg; @@ -165,7 +186,6 @@ DWORD WINAPI NsiEnumerateObjectsAllParametersEx( struct nsi_enumerate_all_ex *pa }
heap_free( out ); - CloseHandle( device );
return err; } @@ -249,7 +269,6 @@ DWORD WINAPI NsiGetAllParametersEx( struct nsi_get_all_parameters_ex *params ) err: heap_free( out ); heap_free( in ); - CloseHandle( device ); return err; }
@@ -286,11 +305,7 @@ DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ) if (device == INVALID_HANDLE_VALUE) return GetLastError();
in = heap_alloc( in_size ); - if (!in) - { - err = ERROR_OUTOFMEMORY; - goto err; - } + if (!in) return ERROR_OUTOFMEMORY; in->module = *params->module; in->first_arg = params->first_arg; in->table = params->table; @@ -302,9 +317,7 @@ DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ) if (!DeviceIoControl( device, IOCTL_NSIPROXY_WINE_GET_PARAMETER, in, in_size, params->data, params->data_size, &received, NULL )) err = GetLastError();
-err: heap_free( in ); - CloseHandle( device ); return err; }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsi/nsi.c | 54 +++++++++++++++++++++++++++++++------- dlls/nsi/tests/nsi.c | 13 ++++----- dlls/nsiproxy.sys/device.c | 46 ++++++++++++++++++++++++++++++++ include/wine/nsi.h | 8 ++++++ 4 files changed, 105 insertions(+), 16 deletions(-)
diff --git a/dlls/nsi/nsi.c b/dlls/nsi/nsi.c index 9cb8e7d451b..fafb5ea5053 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,25 @@ 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, FILE_FLAG_OVERLAPPED, 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, @@ -148,7 +151,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;
@@ -228,7 +231,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; @@ -297,7 +300,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; @@ -324,9 +327,40 @@ DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ) DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module, DWORD table, OVERLAPPED *ovr, HANDLE *handle ) { - FIXME( "%lu %p %lu %p %p stub.\n", unk, module, table, ovr, handle ); + HANDLE device = get_nsi_device( TRUE ); + struct nsiproxy_request_notification *in; + ULONG in_size = sizeof(struct nsiproxy_get_parameter), received; + DWORD err = ERROR_SUCCESS; + OVERLAPPED overlapped; + DWORD len;
- return ERROR_NOT_SUPPORTED; + TRACE( "%lu %p %lu %p %p.\n", unk, module, table, ovr, handle ); + + if (device == INVALID_HANDLE_VALUE) return GetLastError(); + + in = malloc( in_size ); + if (!in) return ERROR_OUTOFMEMORY; + in->module = *module; + in->table = table; + + if (!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 (handle && ovr && err == ERROR_IO_PENDING) + *handle = device; + + free( in ); + return err; }
DWORD WINAPI NsiCancelChangeNotification( OVERLAPPED *ovr ) diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index d1aa7071d06..744b508d373 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -1038,16 +1038,16 @@ 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 );
handle2 = (HANDLE)0xdeadbeef; memset( &ovr2, 0, sizeof(ovr2) ); ret = NsiRequestChangeNotification( 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, &ovr2, &handle2 ); - 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 ); @@ -1057,12 +1057,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 ); @@ -1072,7 +1073,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 ee83dbb82e5..1a999fdcc18 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
From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsi/nsi.c | 10 ++++++++-- dlls/nsi/tests/nsi.c | 24 ++++++++++++------------ 2 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/dlls/nsi/nsi.c b/dlls/nsi/nsi.c index fafb5ea5053..e2e0d2d0809 100644 --- a/dlls/nsi/nsi.c +++ b/dlls/nsi/nsi.c @@ -365,7 +365,13 @@ DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module
DWORD WINAPI NsiCancelChangeNotification( OVERLAPPED *ovr ) { - FIXME( "%p stub.\n", ovr ); + DWORD err = ERROR_SUCCESS; + + TRACE( "%p.\n", ovr );
- return ERROR_NOT_SUPPORTED; + if (!ovr) return ERROR_NOT_FOUND; + if (!CancelIoEx( get_nsi_device( TRUE ), ovr )) + err = GetLastError(); + + return err; } diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index 744b508d373..0b8804acaad 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -1050,24 +1050,24 @@ void test_change_notifications(void) 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 ); + ok( ret == ERROR_NOT_FOUND, "got %lu.\n", ret );
ret = NsiCancelChangeNotification( &ovr ); - todo_wine ok( !ret, "got %lu.\n", ret ); + ok( !ret, "got %lu.\n", ret );
bytes = 0xdeadbeef; - bret = GetOverlappedResult( handle, &ovr, &bytes, FALSE ); + bret = GetOverlappedResult( handle, &ovr, &bytes, TRUE );
- 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 ); - todo_wine ok( !bytes, "got %lu.\n", bytes ); + ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); + ok( ovr.Internal == (ULONG)STATUS_CANCELLED, "got %Ix.\n", ovr.Internal ); + ok( !bytes, "got %lu.\n", bytes );
bret = GetOverlappedResult( handle2, &ovr2, &bytes, FALSE ); 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 ); - todo_wine ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); + ok( !ret, "got %lu.\n", ret ); + bret = GetOverlappedResult( handle, &ovr, &bytes, TRUE ); + ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() );
ret = NsiRequestChangeNotification( 0, &NPI_MS_NDIS_MODULEID, NSI_NDIS_INDEX_LUID_TABLE, &ovr, &handle ); todo_wine ok( ret == ERROR_INVALID_PARAMETER, "got %lu.\n", ret ); @@ -1075,9 +1075,9 @@ void test_change_notifications(void) ret = NsiRequestChangeNotification( 0, &NPI_MS_IPV4_MODULEID, NSI_IP_FORWARD_TABLE, &ovr, &handle ); 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 ); - todo_wine ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); + ok( !ret, "got %lu.\n", ret ); + bret = GetOverlappedResult( handle, &ovr, &bytes, TRUE ); + ok( !bret && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", bret, GetLastError() ); }
START_TEST( nsi )
From: Paul Gofman pgofman@codeweavers.com
--- dlls/nsiproxy.sys/device.c | 56 +++++++++++- dlls/nsiproxy.sys/nsi.c | 122 ++++++++++++++++++++++++++- dlls/nsiproxy.sys/nsiproxy_private.h | 7 ++ 3 files changed, 183 insertions(+), 2 deletions(-)
diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index 3bac642ec79..c64f61ed0fe 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -51,6 +51,12 @@ DECLARE_CRITICAL_SECTION( nsiproxy_cs ); static LIST_ENTRY request_queue = LIST_ENTRY_INIT( request_queue ); static LIST_ENTRY notification_queue = LIST_ENTRY_INIT( notification_queue );
+struct notification_data +{ + NPI_MODULEID module; + UINT table; +}; + static NTSTATUS nsiproxy_call( unsigned int code, void *args ) { return WINE_UNIX_CALL( code, args ); @@ -65,6 +71,7 @@ enum unix_calls nsi_enumerate_all_ex, nsi_get_all_parameters_ex, nsi_get_parameter_ex, + nsi_get_notification, };
static NTSTATUS nsiproxy_enumerate_all( IRP *irp ) @@ -270,6 +277,7 @@ static void WINAPI change_notification_cancel( DEVICE_OBJECT *device, IRP *irp )
EnterCriticalSection( &nsiproxy_cs ); RemoveEntryList( &irp->Tail.Overlay.ListEntry ); + free( irp->Tail.Overlay.DriverContext[0] ); LeaveCriticalSection( &nsiproxy_cs );
irp->IoStatus.Status = STATUS_CANCELLED; @@ -281,10 +289,12 @@ 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; + struct notification_data *data;
- FIXME( "\n" ); + TRACE( "irp %p.\n", irp );
if (in_len < sizeof(*in)) return STATUS_INVALID_PARAMETER; + if (!(data = calloc( 1, sizeof(*data) ))) return STATUS_NO_MEMORY; /* FIXME: validate module and table. */
EnterCriticalSection( &nsiproxy_cs ); @@ -294,10 +304,14 @@ static NTSTATUS nsiproxy_change_notification( IRP *irp ) /* IRP was canceled before we set cancel routine */ InitializeListHead( &irp->Tail.Overlay.ListEntry ); LeaveCriticalSection( &nsiproxy_cs ); + free( data ); return STATUS_CANCELLED; } InsertTailList( ¬ification_queue, &irp->Tail.Overlay.ListEntry ); IoMarkIrpPending( irp ); + data->module = in->module; + data->table = in->table; + irp->Tail.Overlay.DriverContext[0] = data; LeaveCriticalSection( &nsiproxy_cs );
return STATUS_PENDING; @@ -466,6 +480,44 @@ static DWORD WINAPI request_thread_proc( void *arg ) return 0; }
+static DWORD WINAPI notification_thread_proc( void *arg ) +{ + struct nsi_get_notification_params params; + LIST_ENTRY *entry, *next; + NTSTATUS status; + + while (!(status = nsiproxy_call( nsi_get_notification, ¶ms ))) + { + EnterCriticalSection( &nsiproxy_cs ); + for (entry = notification_queue.Flink; entry != ¬ification_queue; entry = next) + { + IRP *irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry ); + struct notification_data *data = irp->Tail.Overlay.DriverContext[0]; + + next = entry->Flink; + if(irp->Cancel) + { + /* Cancel routine should care of freeing data and completing IRP. */ + TRACE( "irp %p canceled.\n", irp ); + continue; + } + if (!NmrIsEqualNpiModuleId( &data->module, ¶ms.module ) || data->table != params.table) + continue; + + irp->IoStatus.Status = 0; + RemoveEntryList( entry ); + irp->Tail.Overlay.DriverContext[0] = NULL; + free( data ); + TRACE("completing irp %p.\n", irp); + IoCompleteRequest( irp, IO_NO_INCREMENT ); + } + LeaveCriticalSection( &nsiproxy_cs ); + } + + WARN( "nsi_get_notification failed, status %#lx.\n", status ); + return 0; +} + NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) { NTSTATUS status; @@ -483,6 +535,8 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) 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 );
return STATUS_SUCCESS; } diff --git a/dlls/nsiproxy.sys/nsi.c b/dlls/nsiproxy.sys/nsi.c index b9b04e63545..2f6d2d59573 100644 --- a/dlls/nsiproxy.sys/nsi.c +++ b/dlls/nsiproxy.sys/nsi.c @@ -21,7 +21,16 @@ #pragma makedep unix #endif
+#include "config.h" #include <stdarg.h> +#include <assert.h> +#include <errno.h> +#include <unistd.h> +#include <sys/socket.h> +#include <limits.h> +#ifdef HAVE_LINUX_RTNETLINK_H +#include <linux/rtnetlink.h> +#endif
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -32,11 +41,13 @@ #include "ddk/wdm.h" #include "ifdef.h" #define __WINE_INIT_NPI_MODULEID +#define USE_WS_PREFIX #include "netiodef.h" #include "wine/nsi.h" #include "wine/debug.h" #include "wine/unixlib.h" #include "unix_private.h" +#include "nsiproxy_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(nsi);
@@ -145,6 +156,114 @@ static NTSTATUS unix_nsi_get_parameter_ex( void *args ) return nsi_get_parameter_ex( params ); }
+#ifdef HAVE_LINUX_RTNETLINK_H +static struct +{ + const NPI_MODULEID *module; + UINT32 table; +} +queued_notifications[256]; +static unsigned int queued_notification_count; + +static NTSTATUS add_notification( const NPI_MODULEID *module, UINT32 table ) +{ + unsigned int i; + + for (i = 0; i < queued_notification_count; ++i) + if (queued_notifications[i].module == module && queued_notifications[i].table == table) return STATUS_SUCCESS; + if (queued_notification_count == ARRAY_SIZE(queued_notifications)) + { + ERR( "Notification queue full.\n" ); + return STATUS_NO_MEMORY; + } + queued_notifications[i].module = module; + queued_notifications[i].table = table; + ++queued_notification_count; + return STATUS_SUCCESS; +} + +static NTSTATUS poll_netlink(void) +{ + static int netlink_fd = -1; + char buffer[PIPE_BUF]; + struct nlmsghdr *nlh; + NTSTATUS status; + int len; + + if (netlink_fd == -1) + { + struct sockaddr_nl addr; + + if ((netlink_fd = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE )) == -1) + { + ERR( "netlink socket creation failed, errno %d.\n", errno ); + return STATUS_NOT_IMPLEMENTED; + } + + memset( &addr, 0, sizeof(addr) ); + addr.nl_family = AF_NETLINK; + addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; + if (bind( netlink_fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1) + { + close( netlink_fd ); + netlink_fd = -1; + ERR( "bind failed, errno %d.\n", errno ); + return STATUS_NOT_IMPLEMENTED; + } + } + + while (1) + { + len = recv( netlink_fd, buffer, sizeof(buffer), 0 ); + if (len <= 0) + { + if (errno == EINTR) continue; + ERR( "error receivng, len %d, errno %d.\n", len, errno ); + return STATUS_UNSUCCESSFUL; + } + for (nlh = (struct nlmsghdr *)buffer; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) + { + if (nlh->nlmsg_type == NLMSG_DONE) break; + if (nlh->nlmsg_type == RTM_NEWADDR || nlh->nlmsg_type == RTM_DELADDR) + { + struct ifaddrmsg *addrmsg = (struct ifaddrmsg *)(nlh + 1); + const NPI_MODULEID *module; + + if (addrmsg->ifa_family == AF_INET) module = &NPI_MS_IPV4_MODULEID; + else if (addrmsg->ifa_family == AF_INET6) module = &NPI_MS_IPV6_MODULEID; + else + { + WARN( "Unknown addrmsg->ifa_family %d.\n", addrmsg->ifa_family ); + continue; + } + if ((status = add_notification( module, NSI_IP_UNICAST_TABLE))) return status; + } + } + if (queued_notification_count) break; + } + return STATUS_SUCCESS; +} + +static NTSTATUS unix_nsi_get_notification( void *args ) +{ + struct nsi_get_notification_params *params = (struct nsi_get_notification_params *)args; + NTSTATUS status; + + if (!queued_notification_count && (status = poll_netlink())) return status; + assert( queued_notification_count ); + params->module = *queued_notifications[0].module; + params->table = queued_notifications[0].table; + --queued_notification_count; + memmove( queued_notifications, queued_notifications + 1, sizeof(*queued_notifications) * queued_notification_count ); + return STATUS_SUCCESS; +} +#else +static NTSTATUS unix_nsi_get_notification( void *args ) +{ + return STATUS_NOT_IMPLEMENTED; +} +#endif + const unixlib_entry_t __wine_unix_call_funcs[] = { icmp_cancel_listen, @@ -153,5 +272,6 @@ const unixlib_entry_t __wine_unix_call_funcs[] = icmp_send_echo, unix_nsi_enumerate_all_ex, unix_nsi_get_all_parameters_ex, - unix_nsi_get_parameter_ex + unix_nsi_get_parameter_ex, + unix_nsi_get_notification, }; diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h index 241106fe228..1b6eacf7d08 100644 --- a/dlls/nsiproxy.sys/nsiproxy_private.h +++ b/dlls/nsiproxy.sys/nsiproxy_private.h @@ -84,3 +84,10 @@ struct icmp_echo_reply_64 ULONGLONG options_ptr; } opts; }; + +struct nsi_get_notification_params +{ + /* output parameters */ + NPI_MODULEID module; + UINT32 table; +};
From: Paul Gofman pgofman@codeweavers.com
--- dlls/iphlpapi/iphlpapi_main.c | 21 +++++++++------------ dlls/iphlpapi/tests/iphlpapi.c | 14 +++++++++----- 2 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index f8ecb89c328..f30ed03d5e6 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -127,14 +127,15 @@ DWORD WINAPI AddIPAddress(IPAddr Address, IPMask IpMask, DWORD IfIndex, PULONG N * RETURNS * Success: TRUE * Failure: FALSE - * - * FIXME - * Stub, returns FALSE. */ BOOL WINAPI CancelIPChangeNotify(LPOVERLAPPED overlapped) { - FIXME("(overlapped %p): stub\n", overlapped); - return FALSE; + DWORD err; + + TRACE("overlapped %p.\n", overlapped); + + if ((err = NsiCancelChangeNotification( overlapped ))) SetLastError( err ); + return !err; }
@@ -3786,16 +3787,12 @@ DWORD WINAPI IpRenewAddress(PIP_ADAPTER_INDEX_MAP AdapterInfo) * RETURNS * Success: NO_ERROR * Failure: error code from winerror.h - * - * FIXME - * Stub, returns ERROR_NOT_SUPPORTED. */ DWORD WINAPI NotifyAddrChange(PHANDLE Handle, LPOVERLAPPED overlapped) { - FIXME("(Handle %p, overlapped %p): stub\n", Handle, overlapped); - if (Handle) *Handle = INVALID_HANDLE_VALUE; - if (overlapped) ((IO_STATUS_BLOCK *) overlapped)->Status = STATUS_PENDING; - return ERROR_IO_PENDING; + TRACE("Handle %p, overlapped %p.\n", Handle, overlapped); + + return NsiRequestChangeNotification(0, &NPI_MS_IPV4_MODULEID, NSI_IP_UNICAST_TABLE, overlapped, Handle); }
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 4def00730fc..6ac7b88778e 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1670,9 +1670,11 @@ static void testNotifyAddrChange(void) ret = NotifyAddrChange(&handle, &overlapped); ok(ret == ERROR_IO_PENDING, "NotifyAddrChange returned %ld, expected ERROR_IO_PENDING\n", ret); ret = GetLastError(); - todo_wine ok(ret == ERROR_IO_PENDING, "GetLastError returned %ld, expected ERROR_IO_PENDING\n", ret); + ok(ret == ERROR_IO_PENDING, "GetLastError returned %ld, expected ERROR_IO_PENDING\n", ret); success = CancelIPChangeNotify(&overlapped); - todo_wine ok(success == TRUE, "CancelIPChangeNotify returned FALSE, expected TRUE\n"); + ok(success == TRUE, "CancelIPChangeNotify returned FALSE, expected TRUE\n"); + success = GetOverlappedResult( handle, &overlapped, &bytes, TRUE ); + ok( !success && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", success, GetLastError() );
ZeroMemory(&overlapped, sizeof(overlapped)); success = CancelIPChangeNotify(&overlapped); @@ -1683,13 +1685,15 @@ static void testNotifyAddrChange(void) overlapped.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); ret = NotifyAddrChange(&handle, &overlapped); ok(ret == ERROR_IO_PENDING, "NotifyAddrChange returned %ld, expected ERROR_IO_PENDING\n", ret); - todo_wine ok(handle != INVALID_HANDLE_VALUE, "NotifyAddrChange returned invalid file handle\n"); + ok(handle != INVALID_HANDLE_VALUE, "NotifyAddrChange returned invalid file handle\n"); success = GetOverlappedResult(handle, &overlapped, &bytes, FALSE); ok(success == FALSE, "GetOverlappedResult returned TRUE, expected FALSE\n"); ret = GetLastError(); ok(ret == ERROR_IO_INCOMPLETE, "GetLastError returned %ld, expected ERROR_IO_INCOMPLETE\n", ret); success = CancelIPChangeNotify(&overlapped); - todo_wine ok(success == TRUE, "CancelIPChangeNotify returned FALSE, expected TRUE\n"); + ok(success == TRUE, "CancelIPChangeNotify returned FALSE, expected TRUE\n"); + success = GetOverlappedResult( handle, &overlapped, &bytes, TRUE ); + ok( !success && GetLastError() == ERROR_OPERATION_ABORTED, "got bret %d, err %lu.\n", success, GetLastError() );
if (winetest_interactive) { @@ -1710,7 +1714,7 @@ static void testNotifyAddrChange(void) trace("Testing synchronous ipv4 address change notification. Please " "change the ipv4 address of one of your network interfaces\n"); ret = NotifyAddrChange(NULL, NULL); - todo_wine ok(ret == NO_ERROR, "NotifyAddrChange returned %ld, expected NO_ERROR\n", ret); + ok(ret == NO_ERROR, "NotifyAddrChange returned %ld, expected NO_ERROR\n", ret); } }
The number of patches here looks big, but 4 of 8 patches are trivial so I am hoping that maybe it is ok.
Regarding patch 4 ("nsi: Cache nsi device handle."), looks like Windows has one cached handle for everything. First of all, NotifyAddrChange docs suggest something like that [1] in the warning regarding the returned handle: "Warning Do not close this handle, and do not associate it with a completion port.". And it looks like that would be really bad idea. I tested that by putting my added test_change_notifications() first between tests and closing the handle in the end of it, the consequent tests which try to call Nsi functions are broken then. Yet in patch 5 ("nsi: Forward request to nsiproxy from NsiRequestChangeNotification().") I am creating a different device handle for async use in NsiRequestChangeNotification(). Otherwise DeviceIoControl calls in the other functions are not fully valid without providing overlapped structure with an event and handling overlapped result (even if that doesn't currently lead to problems in practice because nsiproxy.sys handles those requests sync hronously and those DeviceIoControl's complete synchronously anyway). It seems to me it is better to use different device handles rather than create and close event for each request and wait for async result.
So far I don't have anything for the other iphlpapi notification functions (e. g., NotifyIpInterfaceChange). I only tested that those notifications are always delivered in the same thread which thread is not the same as from where NotifyIpInterfaceChange was called. So probably one possible way to implement that is to have the notification thread in nsi.dll which will use NsiRequestChangeNotification on the appropriate tables, poll for async result on a completion port and call notifications functions. That will, however, require some additional data returned from nsiproxy.dll (possibly from the same ioctl) to indicate the changed row needed as a callback parameter.
Not related to the patches themselves, NsiRequestChangeNotification / NsiCancelChangeNotification (as well as all the other Nsi functions) are completely undocumented, so maybe it is interesting to share how I deduced the parameters. First of all, I measured stack pointer difference resulting from these WINAPI function calls on i386, and that is 20 bytes (5 parameters) for NsiRequestChangeNotification and 4 bytes (one parameter) for NsiCancelChangeNotification. It is pretty obvious that for the latter it could only be overlapped pointer, no other way to cancel the notification. For NsiRequestChangeNotification, calling with all NULL results in access violation on the user side. I found that it is only 4th parameter being NULL causes such an access violation. And that was access violation for write, requiring 20 bytes writeable, and, most notably, upon returning the error with this parameter set the first dword was set to 0x103, which directly suggested that 4th parameter is OVERLAPPE D *. I was pretty sure that output handle should most likely go as either first or last parameter, so there were 3 left. Then, shuffling the parameters, I could get errors ERROR_NOACCESS (suggesting some bad pointer accessed on the kernel side) and ERROR_NOT_SUPPORTED (which was interesting in its old way). I figured that ERROR_NOACCESS is due to the 2nd parameter requiring 24 bytes of readable memory. At this point I payed more attention to the existing Nsi function prototypes (and also ERROR_NOT_SUPPORTED was suggesting that my request is maybe gets routed to the wrong module) and it became apparent what could be there (the thing works with the first parameter being 0, with 1 it returns ERROR_INVALID_PARAMETER and I didn't test that exhaustively).
1. https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-not...
Huw Davies (@huw) commented about include/wine/nsi.h:
DWORD WINAPI NsiGetParameterEx( struct nsi_get_parameter_ex *params ); DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module, DWORD table, OVERLAPPED *ovr, HANDLE *handle ); +DWORD WINAPI NsiCancelChangeNotification( OVERLAPPED *ovr );
Note that these are in alphabetical order.
Huw Davies (@huw) commented about dlls/nsi/nsi.c:
CloseHandle( device ); return err;
}
+DWORD WINAPI NsiRequestChangeNotification( DWORD unk, const NPI_MODULEID *module, DWORD table, OVERLAPPED *ovr,
HANDLE *handle )
+{
- FIXME( "%lu %p %lu %p %p stub.\n", unk, module, table, ovr, handle );
- return ERROR_NOT_SUPPORTED;
+}
Would you mind reversing `NsiRequestChangeNotificationEx()`? It likely takes a ptr to a structure containing the params of the non-Ex version.
Huw Davies (@huw) commented about dlls/nsi/nsi.c:
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, FILE_FLAG_OVERLAPPED, NULL );
```suggestion:-0+0 device = CreateFileW( L"\\.\Nsi", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, async ? FILE_FLAG_OVERLAPPED : 0, NULL ); ```
Could we split this MR so that this one stops after the handle caching commit? That'll make it rather easier for me to review.
So far I don't have anything for the other iphlpapi notification functions (e. g., NotifyIpInterfaceChange).
I suspect the newer notifications like this one use the `Nsi(De)RegisterChangeNotification()` api instead.