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
24 #define WIN32_NO_STATUS
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
);
65 nsi_get_all_parameters_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
;
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;
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
;
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;
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
;
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;
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
, ¶ms
);
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
);
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
;
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
)
238 if (in
->dst
.Ipv4
.sin_addr
.s_addr
== INADDR_ANY
) return STATUS_INVALID_ADDRESS_WILDCARD
;
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
);
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
);
280 case IOCTL_NSIPROXY_WINE_GET_ALL_PARAMETERS
:
281 status
= nsiproxy_get_all_parameters( irp
);
284 case IOCTL_NSIPROXY_WINE_GET_PARAMETER
:
285 status
= nsiproxy_get_parameter( irp
);
288 case IOCTL_NSIPROXY_WINE_ICMP_ECHO
:
289 status
= nsiproxy_icmp_echo( irp
);
293 FIXME( "ioctl %lx not supported\n", irpsp
->Parameters
.DeviceIoControl
.IoControlCode
);
294 status
= STATUS_NOT_SUPPORTED
;
298 if (status
!= STATUS_PENDING
)
300 irp
->IoStatus
.Status
= status
;
301 IoCompleteRequest( irp
, IO_NO_INCREMENT
);
306 static int add_device( DRIVER_OBJECT
*driver
)
308 UNICODE_STRING name
, link
;
309 DEVICE_OBJECT
*device
;
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
);
319 FIXME( "failed to create device error %lx\n", status
);
326 static DWORD WINAPI
listen_thread_proc( void *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
;
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
, ¶ms
);
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
;
356 irp
->IoStatus
.Information
= 0;
357 IoCompleteRequest( irp
, IO_NO_INCREMENT
);
359 LeaveCriticalSection( &nsiproxy_cs
);
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
;
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
, ¶ms
);
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
);
393 irp_set_icmp_handle( irp
, handle
);
394 RtlQueueWorkItem( listen_thread_proc
, irp
, WT_EXECUTELONGFUNCTION
);
398 static DWORD WINAPI
request_thread_proc( void *arg
)
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
);
412 irp
->IoStatus
.Status
= STATUS_CANCELLED
;
413 TRACE( "already cancelled\n" );
414 IoCompleteRequest( irp
, IO_NO_INCREMENT
);
418 handle_queued_send_echo( irp
);
420 LeaveCriticalSection( &nsiproxy_cs
);
425 NTSTATUS WINAPI
DriverEntry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
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
;