nsiproxy.sys: Use the Unix call helpers.
[wine.git] / dlls / nsiproxy.sys / device.c
blob4528e934991b4774325278ced511143235d3582f
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, link;
309 DEVICE_OBJECT *device;
310 NTSTATUS status;
312 RtlInitUnicodeString( &name, L"\\Device\\Nsi" );
313 RtlInitUnicodeString( &link, L"\\??\\Nsi" );
315 if (!(status = IoCreateDevice( driver, 0, &name, FILE_DEVICE_NETWORK, FILE_DEVICE_SECURE_OPEN, FALSE, &device )))
316 status = IoCreateSymbolicLink( &link, &name );
317 if (status)
319 FIXME( "failed to create device error %lx\n", status );
320 return 0;
323 return 1;
326 static DWORD WINAPI listen_thread_proc( void *arg )
328 IRP *irp = arg;
329 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
330 struct nsiproxy_icmp_echo *in = irp->AssociatedIrp.SystemBuffer;
331 struct icmp_close_params close_params;
332 struct icmp_listen_params params;
333 NTSTATUS status;
335 TRACE( "\n" );
337 params.user_reply_ptr = in->user_reply_ptr;
338 params.handle = irp_get_icmp_handle( irp );
339 params.timeout = in->timeout;
340 params.bits = in->bits;
341 params.reply = irp->AssociatedIrp.SystemBuffer;
342 params.reply_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
344 status = nsiproxy_call( icmp_listen, &params );
345 TRACE( "icmp_listen rets %08lx\n", status );
347 EnterCriticalSection( &nsiproxy_cs );
349 close_params.handle = irp_set_icmp_handle( irp, 0 );
350 nsiproxy_call( icmp_close, &close_params );
352 irp->IoStatus.Status = status;
353 if (status == STATUS_SUCCESS)
354 irp->IoStatus.Information = params.reply_len;
355 else
356 irp->IoStatus.Information = 0;
357 IoCompleteRequest( irp, IO_NO_INCREMENT );
359 LeaveCriticalSection( &nsiproxy_cs );
361 return 0;
364 static void handle_queued_send_echo( IRP *irp )
366 struct nsiproxy_icmp_echo *in = (struct nsiproxy_icmp_echo *)irp->AssociatedIrp.SystemBuffer;
367 struct icmp_send_echo_params params;
368 icmp_handle handle;
369 NTSTATUS status;
371 TRACE( "\n" );
372 params.request = in->data + ((in->opt_size + 3) & ~3);
373 params.request_size = in->req_size;
374 params.reply = irp->AssociatedIrp.SystemBuffer;
375 params.bits = in->bits;
376 params.ttl = in->ttl;
377 params.tos = in->tos;
378 params.dst = &in->dst;
379 params.handle = &handle;
381 status = nsiproxy_call( icmp_send_echo, &params );
382 TRACE( "icmp_send_echo rets %08lx\n", status );
384 if (status != STATUS_PENDING)
386 irp->IoStatus.Status = status;
387 if (status == STATUS_SUCCESS)
388 irp->IoStatus.Information = params.reply_len;
389 IoCompleteRequest( irp, IO_NO_INCREMENT );
391 else
393 irp_set_icmp_handle( irp, handle );
394 RtlQueueWorkItem( listen_thread_proc, irp, WT_EXECUTELONGFUNCTION );
398 static DWORD WINAPI request_thread_proc( void *arg )
400 LIST_ENTRY *entry;
402 while (WaitForSingleObject( request_event, INFINITE ) == WAIT_OBJECT_0)
404 TRACE( "request_event triggered\n" );
405 EnterCriticalSection( &nsiproxy_cs );
406 while ((entry = RemoveHeadList( &request_queue )) != &request_queue )
408 IRP *irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry );
410 if (irp->Cancel)
412 irp->IoStatus.Status = STATUS_CANCELLED;
413 TRACE( "already cancelled\n" );
414 IoCompleteRequest( irp, IO_NO_INCREMENT );
415 continue;
418 handle_queued_send_echo( irp );
420 LeaveCriticalSection( &nsiproxy_cs );
422 return 0;
425 NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
427 NTSTATUS status;
428 HANDLE thread;
430 TRACE( "(%p, %s)\n", driver, debugstr_w( path->Buffer ) );
432 status = __wine_init_unix_call();
433 if (status) return status;
435 driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = nsi_ioctl;
437 add_device( driver );
439 request_event = CreateEventW( NULL, FALSE, FALSE, NULL );
440 thread = CreateThread( NULL, 0, request_thread_proc, NULL, 0, NULL );
441 CloseHandle( thread );
443 return STATUS_SUCCESS;