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