Fix firefox exit. Implement a simple work queue for possibly dispatch level
[reactos.git] / reactos / drivers / net / tcpip / tcpip / fileobjs.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: tcpip/fileobjs.c
5 * PURPOSE: Routines for handling file objects
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISIONS:
8 * CSH 01/08-2000 Created
9 */
10
11 #include "precomp.h"
12
13 /* List of all address file objects managed by this driver */
14 LIST_ENTRY AddressFileListHead;
15 KSPIN_LOCK AddressFileListLock;
16
17 /* List of all connection endpoint file objects managed by this driver */
18 LIST_ENTRY ConnectionEndpointListHead;
19 KSPIN_LOCK ConnectionEndpointListLock;
20
21 /*
22 * FUNCTION: Searches through address file entries to find the first match
23 * ARGUMENTS:
24 * Address = IP address
25 * Port = Port number
26 * Protocol = Protocol number
27 * SearchContext = Pointer to search context
28 * RETURNS:
29 * Pointer to address file, NULL if none was found
30 */
31 PADDRESS_FILE AddrSearchFirst(
32 PIP_ADDRESS Address,
33 USHORT Port,
34 USHORT Protocol,
35 PAF_SEARCH SearchContext)
36 {
37 SearchContext->Address = Address;
38 SearchContext->Port = Port;
39 SearchContext->Next = AddressFileListHead.Flink;
40 SearchContext->Protocol = Protocol;
41
42 return AddrSearchNext(SearchContext);
43 }
44
45 BOOLEAN AddrIsBroadcast(
46 PIP_ADDRESS PossibleMatch,
47 PIP_ADDRESS TargetAddress ) {
48 IF_LIST_ITER(IF);
49
50 ForEachInterface(IF) {
51 if( AddrIsEqual( &IF->Unicast, PossibleMatch ) &&
52 AddrIsEqual( &IF->Broadcast, TargetAddress ) )
53 return TRUE;
54 } EndFor(IF);
55
56 return FALSE;
57 }
58
59 /*
60 * FUNCTION: Searches through address file entries to find next match
61 * ARGUMENTS:
62 * SearchContext = Pointer to search context
63 * RETURNS:
64 * Pointer to address file, NULL if none was found
65 */
66 PADDRESS_FILE AddrSearchNext(
67 PAF_SEARCH SearchContext)
68 {
69 PLIST_ENTRY CurrentEntry;
70 PIP_ADDRESS IPAddress;
71 KIRQL OldIrql;
72 PADDRESS_FILE Current = NULL;
73 BOOLEAN Found = FALSE;
74
75 if (IsListEmpty(SearchContext->Next))
76 return NULL;
77
78 CurrentEntry = SearchContext->Next;
79
80 TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
81
82 while (CurrentEntry != &AddressFileListHead) {
83 Current = CONTAINING_RECORD(CurrentEntry, ADDRESS_FILE, ListEntry);
84
85 IPAddress = &Current->Address;
86
87 TI_DbgPrint(DEBUG_ADDRFILE, ("Comparing: ((%d, %d, %s), (%d, %d, %s)).\n",
88 WN2H(Current->Port),
89 Current->Protocol,
90 A2S(IPAddress),
91 WN2H(SearchContext->Port),
92 SearchContext->Protocol,
93 A2S(SearchContext->Address)));
94
95 /* See if this address matches the search criteria */
96 if ((Current->Port == SearchContext->Port) &&
97 (Current->Protocol == SearchContext->Protocol) &&
98 (AddrIsEqual(IPAddress, SearchContext->Address) ||
99 AddrIsBroadcast(IPAddress, SearchContext->Address) ||
100 AddrIsUnspecified(IPAddress))) {
101 /* We've found a match */
102 Found = TRUE;
103 break;
104 }
105 CurrentEntry = CurrentEntry->Flink;
106 }
107
108 TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
109
110 if (Found) {
111 SearchContext->Next = CurrentEntry->Flink;
112 return Current;
113 } else
114 return NULL;
115 }
116
117 VOID AddrFileFree(
118 PVOID Object)
119 /*
120 * FUNCTION: Frees an address file object
121 * ARGUMENTS:
122 * Object = Pointer to address file object to free
123 */
124 {
125 ExFreePool(Object);
126 }
127
128
129 VOID ControlChannelFree(
130 PVOID Object)
131 /*
132 * FUNCTION: Frees an address file object
133 * ARGUMENTS:
134 * Object = Pointer to address file object to free
135 */
136 {
137 ExFreePool(Object);
138 }
139
140
141 VOID DeleteAddress(PADDRESS_FILE AddrFile)
142 /*
143 * FUNCTION: Deletes an address file object
144 * ARGUMENTS:
145 * AddrFile = Pointer to address file object to delete
146 */
147 {
148 KIRQL OldIrql;
149 PLIST_ENTRY CurrentEntry;
150 PLIST_ENTRY NextEntry;
151 PDATAGRAM_SEND_REQUEST SendRequest;
152 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest;
153
154 TI_DbgPrint(MID_TRACE, ("Called.\n"));
155
156 /* Remove address file from the global list */
157 TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
158 RemoveEntryList(&AddrFile->ListEntry);
159 TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
160
161 TcpipAcquireSpinLock(&AddrFile->Lock, &OldIrql);
162
163 /* FIXME: Kill TCP connections on this address file object */
164
165 /* Return pending requests with error */
166
167 TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting receive requests on AddrFile at (0x%X).\n", AddrFile));
168
169 /* Go through pending receive request list and cancel them all */
170 CurrentEntry = AddrFile->ReceiveQueue.Flink;
171 while (CurrentEntry != &AddrFile->ReceiveQueue) {
172 NextEntry = CurrentEntry->Flink;
173 ReceiveRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
174 /* Abort the request and free its resources */
175 TcpipReleaseSpinLock(&AddrFile->Lock, OldIrql);
176 (*ReceiveRequest->Complete)(ReceiveRequest->Context, STATUS_ADDRESS_CLOSED, 0);
177 TcpipAcquireSpinLock(&AddrFile->Lock, &OldIrql);
178 CurrentEntry = NextEntry;
179 }
180
181 TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting send requests on address file at (0x%X).\n", AddrFile));
182
183 /* Go through pending send request list and cancel them all */
184 CurrentEntry = AddrFile->TransmitQueue.Flink;
185 while (CurrentEntry != &AddrFile->TransmitQueue) {
186 NextEntry = CurrentEntry->Flink;
187 SendRequest = CONTAINING_RECORD(CurrentEntry,
188 DATAGRAM_SEND_REQUEST, ListEntry);
189 /* Abort the request and free its resources */
190 TcpipReleaseSpinLock(&AddrFile->Lock, OldIrql);
191 (*SendRequest->Complete)(SendRequest->Context, STATUS_ADDRESS_CLOSED, 0);
192 ExFreePool(SendRequest);
193 TcpipAcquireSpinLock(&AddrFile->Lock, &OldIrql);
194 CurrentEntry = NextEntry;
195 }
196
197 TcpipReleaseSpinLock(&AddrFile->Lock, OldIrql);
198
199 (*AddrFile->Free)(AddrFile);
200
201 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
202 }
203
204
205 /*
206 * FUNCTION: Deletes a connection endpoint file object
207 * ARGUMENTS:
208 * Connection = Pointer to connection endpoint to delete
209 */
210 VOID DeleteConnectionEndpoint(
211 PCONNECTION_ENDPOINT Connection)
212 {
213 KIRQL OldIrql;
214
215 TI_DbgPrint(MID_TRACE, ("Called.\n"));
216
217 /* Remove connection endpoint from the global list */
218 TcpipAcquireSpinLock(&ConnectionEndpointListLock, &OldIrql);
219 RemoveEntryList(&Connection->ListEntry);
220 TcpipReleaseSpinLock(&ConnectionEndpointListLock, OldIrql);
221
222 ExFreePool(Connection);
223
224 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
225 }
226
227 /*
228 * FUNCTION: Open an address file object
229 * ARGUMENTS:
230 * Request = Pointer to TDI request structure for this request
231 * Address = Pointer to address to be opened
232 * Protocol = Protocol on which to open the address
233 * Options = Pointer to option buffer
234 * RETURNS:
235 * Status of operation
236 */
237 NTSTATUS FileOpenAddress(
238 PTDI_REQUEST Request,
239 PTA_IP_ADDRESS Address,
240 USHORT Protocol,
241 PVOID Options)
242 {
243 IPv4_RAW_ADDRESS IPv4Address;
244 BOOLEAN Matched;
245 PADDRESS_FILE AddrFile;
246
247 TI_DbgPrint(MID_TRACE, ("Called (Proto %d).\n", Protocol));
248
249 AddrFile = ExAllocatePool(NonPagedPool, sizeof(ADDRESS_FILE));
250 if (!AddrFile) {
251 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
252 return STATUS_INSUFFICIENT_RESOURCES;
253 }
254
255 TI_DbgPrint(DEBUG_ADDRFILE, ("Address file object allocated at (0x%X).\n", AddrFile));
256
257 RtlZeroMemory(AddrFile, sizeof(ADDRESS_FILE));
258
259 AddrFile->Free = AddrFileFree;
260
261 /* Make sure address is a local unicast address or 0 */
262
263 /* Locate address entry. If specified address is 0, a random address is chosen */
264
265 /* FIXME: IPv4 only */
266 AddrFile->Family = Address->Address[0].AddressType;
267 IPv4Address = Address->Address[0].Address[0].in_addr;
268 if (IPv4Address == 0)
269 Matched = IPGetDefaultAddress(&AddrFile->Address);
270 else
271 Matched = AddrLocateADEv4(IPv4Address, &AddrFile->Address);
272
273 if (!Matched) {
274 ExFreePool(AddrFile);
275 TI_DbgPrint(MIN_TRACE, ("Non-local address given (0x%X).\n", DN2H(IPv4Address)));
276 return STATUS_INVALID_PARAMETER;
277 }
278
279 TI_DbgPrint(MID_TRACE, ("Opening address %s for communication (P=%d U=%d).\n",
280 A2S(&AddrFile->Address), Protocol, IPPROTO_UDP));
281
282 /* Protocol specific handling */
283 switch (Protocol) {
284 case IPPROTO_TCP:
285 AddrFile->Port =
286 TCPAllocatePort(Address->Address[0].Address[0].sin_port);
287 AddrFile->Send = NULL; /* TCPSendData */
288 break;
289
290 case IPPROTO_UDP:
291 TI_DbgPrint(MID_TRACE,("Allocating udp port\n"));
292 AddrFile->Port =
293 UDPAllocatePort(Address->Address[0].Address[0].sin_port);
294 TI_DbgPrint(MID_TRACE,("Setting port %d (wanted %d)\n",
295 AddrFile->Port,
296 Address->Address[0].Address[0].sin_port));
297 AddrFile->Send = UDPSendDatagram;
298 break;
299
300 default:
301 /* Use raw IP for all other protocols */
302 AddrFile->Port = 0;
303 AddrFile->Send = RawIPSendDatagram;
304 break;
305 }
306
307 TI_DbgPrint(MID_TRACE, ("IP protocol number for address file object is %d.\n",
308 Protocol));
309
310 TI_DbgPrint(MID_TRACE, ("Port number for address file object is %d.\n",
311 WN2H(AddrFile->Port)));
312
313 /* Set protocol */
314 AddrFile->Protocol = Protocol;
315
316 /* Initialize receive and transmit queues */
317 InitializeListHead(&AddrFile->ReceiveQueue);
318 InitializeListHead(&AddrFile->TransmitQueue);
319
320 /* Initialize spin lock that protects the address file object */
321 KeInitializeSpinLock(&AddrFile->Lock);
322
323 /* Set valid flag so the address can be used */
324 AF_SET_VALID(AddrFile);
325
326 /* Return address file object */
327 Request->Handle.AddressHandle = AddrFile;
328
329 /* Add address file to global list */
330 ExInterlockedInsertTailList(
331 &AddressFileListHead,
332 &AddrFile->ListEntry,
333 &AddressFileListLock);
334
335 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
336
337 return STATUS_SUCCESS;
338 }
339
340
341 /*
342 * FUNCTION: Closes an address file object
343 * ARGUMENTS:
344 * Request = Pointer to TDI request structure for this request
345 * RETURNS:
346 * Status of operation
347 */
348 NTSTATUS FileCloseAddress(
349 PTDI_REQUEST Request)
350 {
351 KIRQL OldIrql;
352 PADDRESS_FILE AddrFile;
353 NTSTATUS Status = STATUS_SUCCESS;
354 PCANCEL_REQUEST CancelReq;
355
356 TI_DbgPrint(MID_TRACE, ("Called.\n"));
357
358 AddrFile = Request->Handle.AddressHandle;
359
360 TcpipAcquireSpinLock(&AddrFile->Lock, &OldIrql);
361
362 /* Set address file object exclusive to us */
363 AF_SET_BUSY(AddrFile);
364 AF_CLR_VALID(AddrFile);
365
366 TcpipReleaseSpinLock(&AddrFile->Lock, OldIrql);
367
368 /* Protocol specific handling */
369 switch (AddrFile->Protocol) {
370 case IPPROTO_TCP:
371 TCPFreePort( AddrFile->Port );
372 if( AddrFile->Listener ) {
373 CancelReq = ExAllocatePoolWithTag
374 ( sizeof(CANCEL_REQUEST), NonPagedPool, FOURCC('T','c','l','s') );
375 if( CancelReq ) {
376 CancelReq->Type = TCP_CANCEL_CLOSE;
377 CancelReq->Context = AddrFile->Listener;
378 AddrFile->Listener = NULL;
379 ExQueueWorkItem( &CancelQueueWork, CriticalWorkQueue );
380 }
381 }
382 break;
383
384 case IPPROTO_UDP:
385 UDPFreePort( AddrFile->Port );
386 break;
387 }
388
389 DeleteAddress(AddrFile);
390
391 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
392
393 return Status;
394 }
395
396
397 /*
398 * FUNCTION: Opens a connection file object
399 * ARGUMENTS:
400 * Request = Pointer to TDI request structure for this request
401 * ClientContext = Pointer to client context information
402 * RETURNS:
403 * Status of operation
404 */
405 NTSTATUS FileOpenConnection(
406 PTDI_REQUEST Request,
407 PVOID ClientContext)
408 {
409 NTSTATUS Status;
410 PCONNECTION_ENDPOINT Connection;
411
412 TI_DbgPrint(MID_TRACE, ("Called.\n"));
413
414 Connection = TCPAllocateConnectionEndpoint( ClientContext );
415
416 if( !Connection ) return STATUS_NO_MEMORY;
417
418 Status = TCPSocket( Connection, AF_INET, SOCK_STREAM, IPPROTO_TCP );
419
420 /* Return connection endpoint file object */
421 Request->Handle.ConnectionContext = Connection;
422
423 /* Add connection endpoint to global list */
424 ExInterlockedInsertTailList(
425 &ConnectionEndpointListHead,
426 &Connection->ListEntry,
427 &ConnectionEndpointListLock);
428
429 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
430
431 return STATUS_SUCCESS;
432 }
433
434
435 /*
436 * FUNCTION: Find a connection by examining the context field. This
437 * is needed in some situations where a FIN reply is needed after a
438 * socket is formally broken.
439 * ARGUMENTS:
440 * Request = Pointer to TDI request structure for this request
441 * RETURNS:
442 * Status of operation
443 */
444 PCONNECTION_ENDPOINT FileFindConnectionByContext( PVOID Context ) {
445 PLIST_ENTRY Entry;
446 KIRQL OldIrql;
447 PCONNECTION_ENDPOINT Connection = NULL;
448
449 TcpipAcquireSpinLock( &ConnectionEndpointListLock, &OldIrql );
450
451 for( Entry = ConnectionEndpointListHead.Flink;
452 Entry != &ConnectionEndpointListHead;
453 Entry = Entry->Flink ) {
454 Connection =
455 CONTAINING_RECORD( Entry, CONNECTION_ENDPOINT, ListEntry );
456 if( Connection->SocketContext == Context ) break;
457 else Connection = NULL;
458 }
459
460 TcpipReleaseSpinLock( &ConnectionEndpointListLock, OldIrql );
461
462 return Connection;
463 }
464
465 /*
466 * FUNCTION: Closes an connection file object
467 * ARGUMENTS:
468 * Request = Pointer to TDI request structure for this request
469 * RETURNS:
470 * Status of operation
471 */
472 NTSTATUS FileCloseConnection(
473 PTDI_REQUEST Request)
474 {
475 PCONNECTION_ENDPOINT Connection;
476 NTSTATUS Status = STATUS_SUCCESS;
477 PCANCEL_REQUEST CancelReq;
478
479 TI_DbgPrint(MID_TRACE, ("Called.\n"));
480
481 Connection = Request->Handle.ConnectionContext;
482
483 CancelReq = ExAllocatePoolWithTag
484 ( sizeof(CANCEL_REQUEST), NonPagedPool, FOURCC('T','c','l','s') );
485 if( CancelReq ) {
486 CancelReq->Type = TCP_CANCEL_CLOSE;
487 CancelReq->Context = Connection;
488 ExQueueWorkItem( &CancelQueueWork, CriticalWorkQueue );
489 }
490
491 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
492
493 return Status;
494 }
495
496
497 /*
498 * FUNCTION: Opens a control channel file object
499 * ARGUMENTS:
500 * Request = Pointer to TDI request structure for this request
501 * RETURNS:
502 * Status of operation
503 */
504 NTSTATUS FileOpenControlChannel(
505 PTDI_REQUEST Request)
506 {
507 PCONTROL_CHANNEL ControlChannel;
508 TI_DbgPrint(MID_TRACE, ("Called.\n"));
509
510 ControlChannel = ExAllocatePool(NonPagedPool, sizeof(*ControlChannel));
511
512 if (!ControlChannel) {
513 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
514 return STATUS_INSUFFICIENT_RESOURCES;
515 }
516
517 RtlZeroMemory(ControlChannel, sizeof(CONTROL_CHANNEL));
518
519 /* Make sure address is a local unicast address or 0 */
520
521 /* Locate address entry. If specified address is 0, a random address is chosen */
522
523 /* Initialize receive and transmit queues */
524 InitializeListHead(&ControlChannel->ListEntry);
525
526 /* Initialize spin lock that protects the address file object */
527 KeInitializeSpinLock(&ControlChannel->Lock);
528
529 /* Return address file object */
530 Request->Handle.ControlChannel = ControlChannel;
531
532 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
533
534 return STATUS_SUCCESS;
535 }
536
537 /*
538 * FUNCTION: Closes a control channel file object
539 * ARGUMENTS:
540 * Request = Pointer to TDI request structure for this request
541 * RETURNS:
542 * Status of operation
543 */
544 NTSTATUS FileCloseControlChannel(
545 PTDI_REQUEST Request)
546 {
547 PCONTROL_CHANNEL ControlChannel = Request->Handle.ControlChannel;
548 NTSTATUS Status = STATUS_SUCCESS;
549
550 ExFreePool(ControlChannel);
551 Request->Handle.ControlChannel = NULL;
552
553 return Status;
554 }
555
556 /* EOF */