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