From: Paul Gofman pgofman@codeweavers.com
--- 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
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 a44339fc3af..5dc54d0035f 100644 --- a/dlls/nsi/nsi.c +++ b/dlls/nsi/nsi.c @@ -119,9 +119,15 @@ err:
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; }
DWORD WINAPI NsiEnumerateObjectsAllParameters( DWORD unk, DWORD unk2, const NPI_MODULEID *module, DWORD table, diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index 6d759debfd6..1bae78d2563 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -1056,24 +1056,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 ); @@ -1081,9 +1081,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); } }
This merge request was approved by Huw Davies.