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; +};