dsdmo: Add Flanger effect stub.
[wine.git] / dlls / nsiproxy.sys / device.c
blob87d634ff5c03261208afe22f9051c0213e7ca02f
1 /*
2 * nsiproxy.sys
4 * Copyright 2021 Huw Davies
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #include "ntstatus.h"
24 #define WIN32_NO_STATUS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winternl.h"
28 #include "winioctl.h"
29 #include "ddk/wdm.h"
30 #include "ifdef.h"
31 #include "netiodef.h"
32 #include "wine/nsi.h"
33 #include "wine/debug.h"
34 #include "wine/unixlib.h"
36 #include "nsiproxy_private.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(nsi);
40 static HANDLE request_event;
42 #define DECLARE_CRITICAL_SECTION(cs) \
43 static CRITICAL_SECTION cs; \
44 static CRITICAL_SECTION_DEBUG cs##_debug = \
45 { 0, 0, &cs, { &cs##_debug.ProcessLocksList, &cs##_debug.ProcessLocksList }, \
46 0, 0, { (DWORD_PTR)(__FILE__ ": " # cs) }}; \
47 static CRITICAL_SECTION cs = { &cs##_debug, -1, 0, 0, 0, 0 };
48 DECLARE_CRITICAL_SECTION( nsiproxy_cs );
50 #define LIST_ENTRY_INIT( list ) { .Flink = &(list), .Blink = &(list) }
51 static LIST_ENTRY request_queue = LIST_ENTRY_INIT( request_queue );
53 static NTSTATUS nsiproxy_call( unsigned int code, void *args )
55 return WINE_UNIX_CALL( code, args );
58 enum unix_calls
60 icmp_cancel_listen,
61 icmp_close,
62 icmp_listen,
63 icmp_send_echo,
64 nsi_enumerate_all_ex,
65 nsi_get_all_parameters_ex,
66 nsi_get_parameter_ex,
69 static NTSTATUS nsiproxy_enumerate_all( IRP *irp )
71 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
72 struct nsiproxy_enumerate_all *in = (struct nsiproxy_enumerate_all *)irp->AssociatedIrp.SystemBuffer;
73 DWORD in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength;
74 void *out = irp->AssociatedIrp.SystemBuffer;
75 DWORD out_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
76 struct nsi_enumerate_all_ex enum_all;
77 NTSTATUS status;
79 if (in_len != sizeof(*in)) return STATUS_INVALID_PARAMETER;
81 if (out_len < sizeof(UINT) + (in->key_size + in->rw_size + in->dynamic_size + in->static_size) * in->count)
82 return STATUS_INVALID_PARAMETER;
84 enum_all.unknown[0] = 0;
85 enum_all.unknown[1] = 0;
86 enum_all.first_arg = in->first_arg;
87 enum_all.second_arg = in->second_arg;
88 enum_all.module = &in->module;
89 enum_all.table = in->table;
90 enum_all.key_data = (BYTE *)out + sizeof(UINT);
91 enum_all.key_size = in->key_size;
92 enum_all.rw_data = (BYTE *)enum_all.key_data + in->key_size * in->count;
93 enum_all.rw_size = in->rw_size;
94 enum_all.dynamic_data = (BYTE *)enum_all.rw_data + in->rw_size * in->count;
95 enum_all.dynamic_size = in->dynamic_size;
96 enum_all.static_data = (BYTE *)enum_all.dynamic_data + in->dynamic_size * in->count;
97 enum_all.static_size = in->static_size;
98 enum_all.count = in->count;
100 status = nsiproxy_call( nsi_enumerate_all_ex, &enum_all );
101 if (status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW)
103 irp->IoStatus.Information = out_len;
104 *(UINT *)out = enum_all.count;
106 else irp->IoStatus.Information = 0;
108 return status;
111 static NTSTATUS nsiproxy_get_all_parameters( IRP *irp )
113 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
114 struct nsiproxy_get_all_parameters *in = (struct nsiproxy_get_all_parameters *)irp->AssociatedIrp.SystemBuffer;
115 DWORD in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength;
116 BYTE *out = irp->AssociatedIrp.SystemBuffer;
117 DWORD out_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
118 struct nsi_get_all_parameters_ex get_all;
119 NTSTATUS status;
121 if (in_len < offsetof(struct nsiproxy_get_all_parameters, key[0]) ||
122 in_len < offsetof(struct nsiproxy_get_all_parameters, key[in->key_size]))
123 return STATUS_INVALID_PARAMETER;
125 if (out_len < in->rw_size + in->dynamic_size + in->static_size)
126 return STATUS_INVALID_PARAMETER;
128 get_all.unknown[0] = 0;
129 get_all.unknown[1] = 0;
130 get_all.first_arg = in->first_arg;
131 get_all.unknown2 = 0;
132 get_all.module = &in->module;
133 get_all.table = in->table;
134 get_all.key = in->key;
135 get_all.key_size = in->key_size;
136 get_all.rw_data = out;
137 get_all.rw_size = in->rw_size;
138 get_all.dynamic_data = out + in->rw_size;
139 get_all.dynamic_size = in->dynamic_size;
140 get_all.static_data = out + in->rw_size + in->dynamic_size;
141 get_all.static_size = in->static_size;
143 status = nsiproxy_call( nsi_get_all_parameters_ex, &get_all );
144 irp->IoStatus.Information = (status == STATUS_SUCCESS) ? out_len : 0;
146 return status;
149 static NTSTATUS nsiproxy_get_parameter( IRP *irp )
151 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
152 struct nsiproxy_get_parameter *in = (struct nsiproxy_get_parameter *)irp->AssociatedIrp.SystemBuffer;
153 DWORD in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength;
154 void *out = irp->AssociatedIrp.SystemBuffer;
155 DWORD out_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
156 struct nsi_get_parameter_ex get_param;
157 NTSTATUS status;
159 if (in_len < offsetof(struct nsiproxy_get_parameter, key[0]) ||
160 in_len < offsetof(struct nsiproxy_get_parameter, key[in->key_size]))
161 return STATUS_INVALID_PARAMETER;
163 get_param.unknown[0] = 0;
164 get_param.unknown[1] = 0;
165 get_param.first_arg = in->first_arg;
166 get_param.unknown2 = 0;
167 get_param.module = &in->module;
168 get_param.table = in->table;
169 get_param.key = in->key;
170 get_param.key_size = in->key_size;
171 get_param.param_type = in->param_type;
172 get_param.data = out;
173 get_param.data_size = out_len;
174 get_param.data_offset = in->data_offset;
176 status = nsiproxy_call( nsi_get_parameter_ex, &get_param );
177 irp->IoStatus.Information = (status == STATUS_SUCCESS) ? out_len : 0;
179 return status;
182 static inline icmp_handle irp_get_icmp_handle( IRP *irp )
184 return PtrToUlong( irp->Tail.Overlay.DriverContext[0] );
187 static inline icmp_handle irp_set_icmp_handle( IRP *irp, icmp_handle handle )
189 return PtrToUlong( InterlockedExchangePointer( irp->Tail.Overlay.DriverContext,
190 ULongToPtr( handle ) ) );
193 static void WINAPI icmp_echo_cancel( DEVICE_OBJECT *device, IRP *irp )
195 struct icmp_cancel_listen_params params;
197 TRACE( "device %p, irp %p.\n", device, irp );
199 IoReleaseCancelSpinLock( irp->CancelIrql );
201 EnterCriticalSection( &nsiproxy_cs );
203 /* If the handle is not set, either the irp is still
204 in the request queue, in which case the request thread will
205 cancel it, or the irp has already finished. If the handle
206 does exist then notify the listen thread. In all cases the irp
207 will be completed elsewhere. */
208 params.handle = irp_get_icmp_handle( irp );
209 if (params.handle) nsiproxy_call( icmp_cancel_listen, &params );
211 LeaveCriticalSection( &nsiproxy_cs );
214 static int icmp_echo_reply_struct_len( ULONG family, ULONG bits )
216 if (family == AF_INET)
217 return (bits == 32) ? sizeof(struct icmp_echo_reply_32) : sizeof(struct icmp_echo_reply_64);
218 return 0;
221 static NTSTATUS nsiproxy_icmp_echo( IRP *irp )
223 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
224 struct nsiproxy_icmp_echo *in = (struct nsiproxy_icmp_echo *)irp->AssociatedIrp.SystemBuffer;
225 DWORD in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength;
226 DWORD out_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
228 TRACE( "\n" );
230 if (in_len < offsetof(struct nsiproxy_icmp_echo, data[0]) ||
231 in_len < offsetof(struct nsiproxy_icmp_echo, data[((in->opt_size + 3) & ~3) + in->req_size]) ||
232 out_len < icmp_echo_reply_struct_len( in->dst.si_family, in->bits ))
233 return STATUS_INVALID_PARAMETER;
235 switch (in->dst.si_family)
237 case AF_INET:
238 if (in->dst.Ipv4.sin_addr.s_addr == INADDR_ANY) return STATUS_INVALID_ADDRESS_WILDCARD;
239 break;
240 default:
241 return STATUS_INVALID_PARAMETER;
244 EnterCriticalSection( &nsiproxy_cs );
246 IoSetCancelRoutine( irp, icmp_echo_cancel );
247 if (irp->Cancel && !IoSetCancelRoutine( irp, NULL ))
249 /* IRP was canceled before we set cancel routine */
250 InitializeListHead( &irp->Tail.Overlay.ListEntry );
251 LeaveCriticalSection( &nsiproxy_cs );
252 return STATUS_CANCELLED;
255 InsertTailList( &request_queue, &irp->Tail.Overlay.ListEntry );
256 IoMarkIrpPending( irp );
258 LeaveCriticalSection( &nsiproxy_cs );
259 SetEvent( request_event );
261 return STATUS_PENDING;
264 static NTSTATUS WINAPI nsi_ioctl( DEVICE_OBJECT *device, IRP *irp )
266 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
267 NTSTATUS status;
269 TRACE( "ioctl %lx insize %lu outsize %lu\n",
270 irpsp->Parameters.DeviceIoControl.IoControlCode,
271 irpsp->Parameters.DeviceIoControl.InputBufferLength,
272 irpsp->Parameters.DeviceIoControl.OutputBufferLength );
274 switch (irpsp->Parameters.DeviceIoControl.IoControlCode)
276 case IOCTL_NSIPROXY_WINE_ENUMERATE_ALL:
277 status = nsiproxy_enumerate_all( irp );
278 break;
280 case IOCTL_NSIPROXY_WINE_GET_ALL_PARAMETERS:
281 status = nsiproxy_get_all_parameters( irp );
282 break;
284 case IOCTL_NSIPROXY_WINE_GET_PARAMETER:
285 status = nsiproxy_get_parameter( irp );
286 break;
288 case IOCTL_NSIPROXY_WINE_ICMP_ECHO:
289 status = nsiproxy_icmp_echo( irp );
290 break;
292 default:
293 FIXME( "ioctl %lx not supported\n", irpsp->Parameters.DeviceIoControl.IoControlCode );
294 status = STATUS_NOT_SUPPORTED;
295 break;
298 if (status != STATUS_PENDING)
300 irp->IoStatus.Status = status;
301 IoCompleteRequest( irp, IO_NO_INCREMENT );
303 return status;
306 static int add_device( DRIVER_OBJECT *driver )
308 UNICODE_STRING name = RTL_CONSTANT_STRING( L"\\Device\\Nsi" );
309 UNICODE_STRING link = RTL_CONSTANT_STRING( L"\\??\\Nsi" );
310 DEVICE_OBJECT *device;
311 NTSTATUS status;
313 if (!(status = IoCreateDevice( driver, 0, &name, FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, &device )))
314 status = IoCreateSymbolicLink( &link, &name );
315 if (status)
317 FIXME( "failed to create device error %lx\n", status );
318 return 0;
321 return 1;
324 static DWORD WINAPI listen_thread_proc( void *arg )
326 IRP *irp = arg;
327 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
328 struct nsiproxy_icmp_echo *in = irp->AssociatedIrp.SystemBuffer;
329 struct icmp_close_params close_params;
330 struct icmp_listen_params params;
331 NTSTATUS status;
333 TRACE( "\n" );
335 params.user_reply_ptr = in->user_reply_ptr;
336 params.handle = irp_get_icmp_handle( irp );
337 params.timeout = in->timeout;
338 params.bits = in->bits;
339 params.reply = irp->AssociatedIrp.SystemBuffer;
340 params.reply_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
342 status = nsiproxy_call( icmp_listen, &params );
343 TRACE( "icmp_listen rets %08lx\n", status );
345 EnterCriticalSection( &nsiproxy_cs );
347 close_params.handle = irp_set_icmp_handle( irp, 0 );
348 nsiproxy_call( icmp_close, &close_params );
350 irp->IoStatus.Status = status;
351 if (status == STATUS_SUCCESS)
352 irp->IoStatus.Information = params.reply_len;
353 else
354 irp->IoStatus.Information = 0;
355 IoCompleteRequest( irp, IO_NO_INCREMENT );
357 LeaveCriticalSection( &nsiproxy_cs );
359 return 0;
362 static void handle_queued_send_echo( IRP *irp )
364 struct nsiproxy_icmp_echo *in = (struct nsiproxy_icmp_echo *)irp->AssociatedIrp.SystemBuffer;
365 struct icmp_send_echo_params params;
366 icmp_handle handle;
367 NTSTATUS status;
369 TRACE( "\n" );
370 params.request = in->data + ((in->opt_size + 3) & ~3);
371 params.request_size = in->req_size;
372 params.reply = irp->AssociatedIrp.SystemBuffer;
373 params.bits = in->bits;
374 params.ttl = in->ttl;
375 params.tos = in->tos;
376 params.dst = &in->dst;
377 params.handle = &handle;
379 status = nsiproxy_call( icmp_send_echo, &params );
380 TRACE( "icmp_send_echo rets %08lx\n", status );
382 if (status != STATUS_PENDING)
384 irp->IoStatus.Status = status;
385 if (status == STATUS_SUCCESS)
386 irp->IoStatus.Information = params.reply_len;
387 IoCompleteRequest( irp, IO_NO_INCREMENT );
389 else
391 irp_set_icmp_handle( irp, handle );
392 RtlQueueWorkItem( listen_thread_proc, irp, WT_EXECUTELONGFUNCTION );
396 static DWORD WINAPI request_thread_proc( void *arg )
398 LIST_ENTRY *entry;
400 while (WaitForSingleObject( request_event, INFINITE ) == WAIT_OBJECT_0)
402 TRACE( "request_event triggered\n" );
403 EnterCriticalSection( &nsiproxy_cs );
404 while ((entry = RemoveHeadList( &request_queue )) != &request_queue )
406 IRP *irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry );
408 if (irp->Cancel)
410 irp->IoStatus.Status = STATUS_CANCELLED;
411 TRACE( "already cancelled\n" );
412 IoCompleteRequest( irp, IO_NO_INCREMENT );
413 continue;
416 handle_queued_send_echo( irp );
418 LeaveCriticalSection( &nsiproxy_cs );
420 return 0;
423 NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
425 NTSTATUS status;
426 HANDLE thread;
428 TRACE( "(%p, %s)\n", driver, debugstr_w( path->Buffer ) );
430 status = __wine_init_unix_call();
431 if (status) return status;
433 driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = nsi_ioctl;
435 add_device( driver );
437 request_event = CreateEventW( NULL, FALSE, FALSE, NULL );
438 thread = CreateThread( NULL, 0, request_thread_proc, NULL, 0, NULL );
439 CloseHandle( thread );
441 return STATUS_SUCCESS;