Sync with trunk r58740.
[reactos.git] / 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 KIRQL OldIrql;
39
40 SearchContext->Address = Address;
41 SearchContext->Port = Port;
42 SearchContext->Protocol = Protocol;
43
44 TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
45
46 SearchContext->Next = AddressFileListHead.Flink;
47
48 if (!IsListEmpty(&AddressFileListHead))
49 ReferenceObject(CONTAINING_RECORD(SearchContext->Next, ADDRESS_FILE, ListEntry));
50
51 TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
52
53 return AddrSearchNext(SearchContext);
54 }
55
56 BOOLEAN AddrIsBroadcastMatch(
57 PIP_ADDRESS UnicastAddress,
58 PIP_ADDRESS BroadcastAddress ) {
59 IF_LIST_ITER(IF);
60
61 ForEachInterface(IF) {
62 if ((AddrIsUnspecified(UnicastAddress) ||
63 AddrIsEqual(&IF->Unicast, UnicastAddress)) &&
64 (AddrIsEqual(&IF->Broadcast, BroadcastAddress)))
65 return TRUE;
66 } EndFor(IF);
67
68 return FALSE;
69 }
70
71 BOOLEAN AddrReceiveMatch(
72 PIP_ADDRESS LocalAddress,
73 PIP_ADDRESS RemoteAddress)
74 {
75 if (AddrIsEqual(LocalAddress, RemoteAddress))
76 {
77 /* Unicast address match */
78 return TRUE;
79 }
80
81 if (AddrIsBroadcastMatch(LocalAddress, RemoteAddress))
82 {
83 /* Broadcast address match */
84 return TRUE;
85 }
86
87 if (AddrIsUnspecified(LocalAddress))
88 {
89 /* Local address unspecified */
90 return TRUE;
91 }
92
93 if (AddrIsUnspecified(RemoteAddress))
94 {
95 /* Remote address unspecified */
96 return TRUE;
97 }
98
99 return FALSE;
100 }
101
102 PADDRESS_FILE AddrFindShared(
103 PIP_ADDRESS BindAddress,
104 USHORT Port,
105 USHORT Protocol)
106 {
107 PLIST_ENTRY CurrentEntry;
108 KIRQL OldIrql;
109 PADDRESS_FILE Current = NULL;
110
111 TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
112
113 CurrentEntry = AddressFileListHead.Flink;
114 while (CurrentEntry != &AddressFileListHead) {
115 Current = CONTAINING_RECORD(CurrentEntry, ADDRESS_FILE, ListEntry);
116
117 /* See if this address matches the search criteria */
118 if ((Current->Port == Port) &&
119 (Current->Protocol == Protocol))
120 {
121 /* Increase the sharer count */
122 ASSERT(Current->Sharers != 0);
123 InterlockedIncrement(&Current->Sharers);
124 break;
125 }
126
127 CurrentEntry = CurrentEntry->Flink;
128 Current = NULL;
129 }
130
131 TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
132
133 return Current;
134 }
135
136 /*
137 * FUNCTION: Searches through address file entries to find next match
138 * ARGUMENTS:
139 * SearchContext = Pointer to search context
140 * RETURNS:
141 * Pointer to address file, NULL if none was found
142 */
143 PADDRESS_FILE AddrSearchNext(
144 PAF_SEARCH SearchContext)
145 {
146 PLIST_ENTRY CurrentEntry;
147 PIP_ADDRESS IPAddress;
148 KIRQL OldIrql;
149 PADDRESS_FILE Current = NULL;
150 BOOLEAN Found = FALSE;
151
152 TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
153
154 if (SearchContext->Next == &AddressFileListHead)
155 {
156 TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
157 return NULL;
158 }
159
160 /* Remove the extra reference we added to keep this address file in memory */
161 DereferenceObject(CONTAINING_RECORD(SearchContext->Next, ADDRESS_FILE, ListEntry));
162
163 CurrentEntry = SearchContext->Next;
164
165 while (CurrentEntry != &AddressFileListHead) {
166 Current = CONTAINING_RECORD(CurrentEntry, ADDRESS_FILE, ListEntry);
167
168 IPAddress = &Current->Address;
169
170 TI_DbgPrint(DEBUG_ADDRFILE, ("Comparing: ((%d, %d, %s), (%d, %d, %s)).\n",
171 WN2H(Current->Port),
172 Current->Protocol,
173 A2S(IPAddress),
174 WN2H(SearchContext->Port),
175 SearchContext->Protocol,
176 A2S(SearchContext->Address)));
177
178 /* See if this address matches the search criteria */
179 if ((Current->Port == SearchContext->Port) &&
180 (Current->Protocol == SearchContext->Protocol) &&
181 (AddrReceiveMatch(IPAddress, SearchContext->Address))) {
182 /* We've found a match */
183 Found = TRUE;
184 break;
185 }
186 CurrentEntry = CurrentEntry->Flink;
187 }
188
189 if (Found)
190 {
191 SearchContext->Next = CurrentEntry->Flink;
192
193 if (SearchContext->Next != &AddressFileListHead)
194 {
195 /* Reference the next address file to prevent the link from disappearing behind our back */
196 ReferenceObject(CONTAINING_RECORD(SearchContext->Next, ADDRESS_FILE, ListEntry));
197 }
198 }
199 else
200 Current = NULL;
201
202 TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
203
204 return Current;
205 }
206
207 VOID AddrFileFree(
208 PVOID Object)
209 /*
210 * FUNCTION: Frees an address file object
211 * ARGUMENTS:
212 * Object = Pointer to address file object to free
213 */
214 {
215 PADDRESS_FILE AddrFile = Object;
216 KIRQL OldIrql;
217 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest;
218 PDATAGRAM_SEND_REQUEST SendRequest;
219 PLIST_ENTRY CurrentEntry;
220
221 TI_DbgPrint(MID_TRACE, ("Called.\n"));
222
223 /* We should not be associated with a connection here */
224 ASSERT(!AddrFile->Connection);
225
226 /* Remove address file from the global list */
227 TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
228 RemoveEntryList(&AddrFile->ListEntry);
229 TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
230
231 /* FIXME: Kill TCP connections on this address file object */
232
233 /* Return pending requests with error */
234
235 TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting receive requests on AddrFile at (0x%X).\n", AddrFile));
236
237 /* Go through pending receive request list and cancel them all */
238 while ((CurrentEntry = ExInterlockedRemoveHeadList(&AddrFile->ReceiveQueue, &AddrFile->Lock))) {
239 ReceiveRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
240 (*ReceiveRequest->Complete)(ReceiveRequest->Context, STATUS_CANCELLED, 0);
241 /* ExFreePoolWithTag(ReceiveRequest, DATAGRAM_RECV_TAG); FIXME: WTF? */
242 }
243
244 TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting send requests on address file at (0x%X).\n", AddrFile));
245
246 /* Go through pending send request list and cancel them all */
247 while ((CurrentEntry = ExInterlockedRemoveHeadList(&AddrFile->ReceiveQueue, &AddrFile->Lock))) {
248 SendRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_SEND_REQUEST, ListEntry);
249 (*SendRequest->Complete)(SendRequest->Context, STATUS_CANCELLED, 0);
250 ExFreePoolWithTag(SendRequest, DATAGRAM_SEND_TAG);
251 }
252
253 /* Protocol specific handling */
254 switch (AddrFile->Protocol) {
255 case IPPROTO_TCP:
256 if (AddrFile->Port)
257 {
258 TCPFreePort(AddrFile->Port);
259 }
260 break;
261
262 case IPPROTO_UDP:
263 UDPFreePort( AddrFile->Port );
264 break;
265 }
266
267 RemoveEntityByContext(AddrFile);
268
269 ExFreePoolWithTag(Object, ADDR_FILE_TAG);
270 }
271
272
273 VOID ControlChannelFree(
274 PVOID Object)
275 /*
276 * FUNCTION: Frees an address file object
277 * ARGUMENTS:
278 * Object = Pointer to address file object to free
279 */
280 {
281 ExFreePoolWithTag(Object, CONTROL_CHANNEL_TAG);
282 }
283
284
285 /*
286 * FUNCTION: Open an address file object
287 * ARGUMENTS:
288 * Request = Pointer to TDI request structure for this request
289 * Address = Pointer to address to be opened
290 * Protocol = Protocol on which to open the address
291 * Shared = Specifies if the address is opened for shared access
292 * Options = Pointer to option buffer
293 * RETURNS:
294 * Status of operation
295 */
296 NTSTATUS FileOpenAddress(
297 PTDI_REQUEST Request,
298 PTA_IP_ADDRESS Address,
299 USHORT Protocol,
300 BOOLEAN Shared,
301 PVOID Options)
302 {
303 PADDRESS_FILE AddrFile;
304
305 TI_DbgPrint(MID_TRACE, ("Called (Proto %d).\n", Protocol));
306
307 /* If it's shared and has a port specified, look for a match */
308 if ((Shared != FALSE) && (Address->Address[0].Address[0].sin_port != 0))
309 {
310 AddrFile = AddrFindShared(NULL, Address->Address[0].Address[0].sin_port, Protocol);
311 if (AddrFile != NULL)
312 {
313 Request->Handle.AddressHandle = AddrFile;
314 return STATUS_SUCCESS;
315 }
316 }
317
318 AddrFile = ExAllocatePoolWithTag(NonPagedPool, sizeof(ADDRESS_FILE),
319 ADDR_FILE_TAG);
320 if (!AddrFile) {
321 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
322 return STATUS_INSUFFICIENT_RESOURCES;
323 }
324
325 RtlZeroMemory(AddrFile, sizeof(ADDRESS_FILE));
326
327 AddrFile->RefCount = 1;
328 AddrFile->Free = AddrFileFree;
329 AddrFile->Sharers = 1;
330
331 /* Set our default options */
332 AddrFile->TTL = 128;
333 AddrFile->DF = 0;
334 AddrFile->BCast = 1;
335 AddrFile->HeaderIncl = 1;
336
337 /* Make sure address is a local unicast address or 0 */
338 /* FIXME: IPv4 only */
339 AddrFile->Family = Address->Address[0].AddressType;
340 AddrFile->Address.Address.IPv4Address = Address->Address[0].Address[0].in_addr;
341 AddrFile->Address.Type = IP_ADDRESS_V4;
342
343 if (!AddrIsUnspecified(&AddrFile->Address) &&
344 !AddrLocateInterface(&AddrFile->Address)) {
345 TI_DbgPrint(MIN_TRACE, ("Non-local address given (0x%X).\n", A2S(&AddrFile->Address)));
346 ExFreePoolWithTag(AddrFile, ADDR_FILE_TAG);
347 return STATUS_INVALID_ADDRESS;
348 }
349
350 TI_DbgPrint(MID_TRACE, ("Opening address %s for communication (P=%d U=%d).\n",
351 A2S(&AddrFile->Address), Protocol, IPPROTO_UDP));
352
353 /* Protocol specific handling */
354 switch (Protocol) {
355 case IPPROTO_TCP:
356 if (Address->Address[0].Address[0].sin_port)
357 {
358 /* The client specified an explicit port so we force a bind to this */
359 AddrFile->Port = TCPAllocatePort(Address->Address[0].Address[0].sin_port);
360
361 /* Check for bind success */
362 if (AddrFile->Port == 0xffff)
363 {
364 ExFreePoolWithTag(AddrFile, ADDR_FILE_TAG);
365 return STATUS_ADDRESS_ALREADY_EXISTS;
366 }
367
368 /* Sanity check */
369 ASSERT(Address->Address[0].Address[0].sin_port == AddrFile->Port);
370 }
371 else if (!AddrIsUnspecified(&AddrFile->Address))
372 {
373 /* The client is trying to bind to a local address so allocate a port now too */
374 AddrFile->Port = TCPAllocatePort(0);
375
376 /* Check for bind success */
377 if (AddrFile->Port == 0xffff)
378 {
379 ExFreePoolWithTag(AddrFile, ADDR_FILE_TAG);
380 return STATUS_ADDRESS_ALREADY_EXISTS;
381 }
382 }
383 else
384 {
385 /* The client wants an unspecified port with an unspecified address so we wait to see what the TCP library gives us */
386 AddrFile->Port = 0;
387 }
388
389 AddEntity(CO_TL_ENTITY, AddrFile, CO_TL_TCP);
390
391 AddrFile->Send = NULL; /* TCPSendData */
392 break;
393
394 case IPPROTO_UDP:
395 TI_DbgPrint(MID_TRACE,("Allocating udp port\n"));
396 AddrFile->Port =
397 UDPAllocatePort(Address->Address[0].Address[0].sin_port);
398
399 if ((Address->Address[0].Address[0].sin_port &&
400 AddrFile->Port != Address->Address[0].Address[0].sin_port) ||
401 AddrFile->Port == 0xffff)
402 {
403 ExFreePoolWithTag(AddrFile, ADDR_FILE_TAG);
404 return STATUS_ADDRESS_ALREADY_EXISTS;
405 }
406
407 TI_DbgPrint(MID_TRACE,("Setting port %d (wanted %d)\n",
408 AddrFile->Port,
409 Address->Address[0].Address[0].sin_port));
410
411 AddEntity(CL_TL_ENTITY, AddrFile, CL_TL_UDP);
412
413 AddrFile->Send = UDPSendDatagram;
414 break;
415
416 case IPPROTO_ICMP:
417 AddrFile->Port = 0;
418 AddrFile->Send = ICMPSendDatagram;
419
420 /* FIXME: Verify this */
421 AddEntity(ER_ENTITY, AddrFile, ER_ICMP);
422 break;
423
424 default:
425 /* Use raw IP for all other protocols */
426 AddrFile->Port = 0;
427 AddrFile->Send = RawIPSendDatagram;
428
429 /* FIXME: Verify this */
430 AddEntity(CL_TL_ENTITY, AddrFile, 0);
431 break;
432 }
433
434 TI_DbgPrint(MID_TRACE, ("IP protocol number for address file object is %d.\n",
435 Protocol));
436
437 TI_DbgPrint(MID_TRACE, ("Port number for address file object is %d.\n",
438 WN2H(AddrFile->Port)));
439
440 /* Set protocol */
441 AddrFile->Protocol = Protocol;
442
443 /* Initialize receive and transmit queues */
444 InitializeListHead(&AddrFile->ReceiveQueue);
445 InitializeListHead(&AddrFile->TransmitQueue);
446
447 /* Initialize spin lock that protects the address file object */
448 KeInitializeSpinLock(&AddrFile->Lock);
449
450 /* Return address file object */
451 Request->Handle.AddressHandle = AddrFile;
452
453 /* Add address file to global list */
454 ExInterlockedInsertTailList(
455 &AddressFileListHead,
456 &AddrFile->ListEntry,
457 &AddressFileListLock);
458
459 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
460
461 return STATUS_SUCCESS;
462 }
463
464
465 /*
466 * FUNCTION: Closes an address file object
467 * ARGUMENTS:
468 * Request = Pointer to TDI request structure for this request
469 * RETURNS:
470 * Status of operation
471 */
472 NTSTATUS FileCloseAddress(
473 PTDI_REQUEST Request)
474 {
475 PADDRESS_FILE AddrFile = Request->Handle.AddressHandle;
476 KIRQL OldIrql;
477
478 if (!Request->Handle.AddressHandle) return STATUS_INVALID_PARAMETER;
479
480 LockObject(AddrFile, &OldIrql);
481
482 if (InterlockedDecrement(&AddrFile->Sharers) != 0)
483 {
484 /* Still other guys have open handles to this, so keep it around */
485 UnlockObject(AddrFile, OldIrql);
486 return STATUS_SUCCESS;
487 }
488
489 /* We have to close this listener because we started it */
490 if( AddrFile->Listener )
491 {
492 TCPClose( AddrFile->Listener );
493 }
494
495 UnlockObject(AddrFile, OldIrql);
496
497 DereferenceObject(AddrFile);
498
499 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
500
501 return STATUS_SUCCESS;
502 }
503
504
505 /*
506 * FUNCTION: Opens a connection file object
507 * ARGUMENTS:
508 * Request = Pointer to TDI request structure for this request
509 * ClientContext = Pointer to client context information
510 * RETURNS:
511 * Status of operation
512 */
513 NTSTATUS FileOpenConnection(
514 PTDI_REQUEST Request,
515 PVOID ClientContext)
516 {
517 NTSTATUS Status;
518 PCONNECTION_ENDPOINT Connection;
519
520 TI_DbgPrint(MID_TRACE, ("Called.\n"));
521
522 Connection = TCPAllocateConnectionEndpoint( ClientContext );
523
524 if( !Connection ) return STATUS_NO_MEMORY;
525
526 Status = TCPSocket( Connection, AF_INET, SOCK_STREAM, IPPROTO_TCP );
527
528 if( !NT_SUCCESS(Status) ) {
529 DereferenceObject( Connection );
530 return Status;
531 }
532
533 /* Return connection endpoint file object */
534 Request->Handle.ConnectionContext = Connection;
535
536 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
537
538 return STATUS_SUCCESS;
539 }
540
541 /*
542 * FUNCTION: Closes an connection file object
543 * ARGUMENTS:
544 * Request = Pointer to TDI request structure for this request
545 * RETURNS:
546 * Status of operation
547 */
548 NTSTATUS FileCloseConnection(
549 PTDI_REQUEST Request)
550 {
551 PCONNECTION_ENDPOINT Connection;
552
553 TI_DbgPrint(MID_TRACE, ("Called.\n"));
554
555 Connection = Request->Handle.ConnectionContext;
556
557 if (!Connection) return STATUS_INVALID_PARAMETER;
558
559 TCPClose( Connection );
560
561 Request->Handle.ConnectionContext = NULL;
562
563 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
564
565 return STATUS_SUCCESS;
566 }
567
568 /*
569 * FUNCTION: Opens a control channel file object
570 * ARGUMENTS:
571 * Request = Pointer to TDI request structure for this request
572 * RETURNS:
573 * Status of operation
574 */
575 NTSTATUS FileOpenControlChannel(
576 PTDI_REQUEST Request)
577 {
578 PCONTROL_CHANNEL ControlChannel;
579 TI_DbgPrint(MID_TRACE, ("Called.\n"));
580
581 ControlChannel = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ControlChannel),
582 CONTROL_CHANNEL_TAG);
583
584 if (!ControlChannel) {
585 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
586 return STATUS_INSUFFICIENT_RESOURCES;
587 }
588
589 RtlZeroMemory(ControlChannel, sizeof(CONTROL_CHANNEL));
590
591 /* Make sure address is a local unicast address or 0 */
592
593 /* Locate address entry. If specified address is 0, a random address is chosen */
594
595 /* Initialize receive and transmit queues */
596 InitializeListHead(&ControlChannel->ListEntry);
597
598 /* Initialize spin lock that protects the address file object */
599 KeInitializeSpinLock(&ControlChannel->Lock);
600
601 ControlChannel->RefCount = 1;
602 ControlChannel->Free = ControlChannelFree;
603
604 /* Return address file object */
605 Request->Handle.ControlChannel = ControlChannel;
606
607 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
608
609 return STATUS_SUCCESS;
610 }
611
612 /*
613 * FUNCTION: Closes a control channel file object
614 * ARGUMENTS:
615 * Request = Pointer to TDI request structure for this request
616 * RETURNS:
617 * Status of operation
618 */
619 NTSTATUS FileCloseControlChannel(
620 PTDI_REQUEST Request)
621 {
622 if (!Request->Handle.ControlChannel) return STATUS_INVALID_PARAMETER;
623
624 DereferenceObject((PCONTROL_CHANNEL)Request->Handle.ControlChannel);
625
626 Request->Handle.ControlChannel = NULL;
627
628 return STATUS_SUCCESS;
629 }
630
631 /* EOF */