f45fdbf2dedbf3b0d89d56624c38fa19d28db22c
[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 /* Make sure address is a local unicast address or 0 */
205 /* FIXME: IPv4 only */
206 AddrFile->Family = Address->Address[0].AddressType;
207 AddrFile->Address.Address.IPv4Address = Address->Address[0].Address[0].in_addr;
208 AddrFile->Address.Type = IP_ADDRESS_V4;
209
210 if (!AddrIsUnspecified(&AddrFile->Address) &&
211 !AddrLocateInterface(&AddrFile->Address)) {
212 exFreePool(AddrFile);
213 TI_DbgPrint(MIN_TRACE, ("Non-local address given (0x%X).\n", DN2H(IPv4Address)));
214 return STATUS_INVALID_PARAMETER;
215 }
216
217 TI_DbgPrint(MID_TRACE, ("Opening address %s for communication (P=%d U=%d).\n",
218 A2S(&AddrFile->Address), Protocol, IPPROTO_UDP));
219
220 /* Protocol specific handling */
221 switch (Protocol) {
222 case IPPROTO_TCP:
223 AddrFile->Port =
224 TCPAllocatePort(Address->Address[0].Address[0].sin_port);
225
226 if ((Address->Address[0].Address[0].sin_port &&
227 AddrFile->Port != Address->Address[0].Address[0].sin_port) ||
228 AddrFile->Port == 0xffff)
229 {
230 exFreePool(AddrFile);
231 return STATUS_INVALID_PARAMETER;
232 }
233
234 AddrFile->Send = NULL; /* TCPSendData */
235 break;
236
237 case IPPROTO_UDP:
238 TI_DbgPrint(MID_TRACE,("Allocating udp port\n"));
239 AddrFile->Port =
240 UDPAllocatePort(Address->Address[0].Address[0].sin_port);
241
242 if ((Address->Address[0].Address[0].sin_port &&
243 AddrFile->Port != Address->Address[0].Address[0].sin_port) ||
244 AddrFile->Port == 0xffff)
245 {
246 exFreePool(AddrFile);
247 return STATUS_INVALID_PARAMETER;
248 }
249
250 TI_DbgPrint(MID_TRACE,("Setting port %d (wanted %d)\n",
251 AddrFile->Port,
252 Address->Address[0].Address[0].sin_port));
253 AddrFile->Send = UDPSendDatagram;
254 break;
255
256 case IPPROTO_ICMP:
257 AddrFile->Port = 0;
258 AddrFile->Send = ICMPSendDatagram;
259 break;
260
261 default:
262 /* Use raw IP for all other protocols */
263 AddrFile->Port = 0;
264 AddrFile->Send = RawIPSendDatagram;
265 break;
266 }
267
268 TI_DbgPrint(MID_TRACE, ("IP protocol number for address file object is %d.\n",
269 Protocol));
270
271 TI_DbgPrint(MID_TRACE, ("Port number for address file object is %d.\n",
272 WN2H(AddrFile->Port)));
273
274 /* Set protocol */
275 AddrFile->Protocol = Protocol;
276
277 /* Initialize receive and transmit queues */
278 InitializeListHead(&AddrFile->ReceiveQueue);
279 InitializeListHead(&AddrFile->TransmitQueue);
280
281 /* Initialize spin lock that protects the address file object */
282 KeInitializeSpinLock(&AddrFile->Lock);
283
284 /* Set valid flag so the address can be used */
285 AF_SET_VALID(AddrFile);
286
287 /* Return address file object */
288 Request->Handle.AddressHandle = AddrFile;
289
290 /* Add address file to global list */
291 ExInterlockedInsertTailList(
292 &AddressFileListHead,
293 &AddrFile->ListEntry,
294 &AddressFileListLock);
295
296 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
297
298 return STATUS_SUCCESS;
299 }
300
301
302 /*
303 * FUNCTION: Closes an address file object
304 * ARGUMENTS:
305 * Request = Pointer to TDI request structure for this request
306 * RETURNS:
307 * Status of operation
308 */
309 NTSTATUS FileCloseAddress(
310 PTDI_REQUEST Request)
311 {
312 PADDRESS_FILE AddrFile;
313 NTSTATUS Status = STATUS_SUCCESS;
314 KIRQL OldIrql;
315 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest;
316 PDATAGRAM_SEND_REQUEST SendRequest;
317 PLIST_ENTRY CurrentEntry, NextEntry;
318
319 AddrFile = Request->Handle.AddressHandle;
320
321 TI_DbgPrint(MID_TRACE, ("Called.\n"));
322
323 /* Remove address file from the global list */
324 TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
325 RemoveEntryList(&AddrFile->ListEntry);
326 TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
327
328 TcpipAcquireSpinLock(&AddrFile->Lock, &OldIrql);
329
330 /* FIXME: Kill TCP connections on this address file object */
331
332 /* Return pending requests with error */
333
334 TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting receive requests on AddrFile at (0x%X).\n", AddrFile));
335
336 /* Go through pending receive request list and cancel them all */
337 CurrentEntry = AddrFile->ReceiveQueue.Flink;
338 while (CurrentEntry != &AddrFile->ReceiveQueue) {
339 NextEntry = CurrentEntry->Flink;
340 ReceiveRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
341 /* Abort the request and free its resources */
342 TcpipReleaseSpinLock(&AddrFile->Lock, OldIrql);
343 (*ReceiveRequest->Complete)(ReceiveRequest->Context, STATUS_CANCELLED, 0);
344 TcpipAcquireSpinLock(&AddrFile->Lock, &OldIrql);
345 CurrentEntry = NextEntry;
346 }
347
348 TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting send requests on address file at (0x%X).\n", AddrFile));
349
350 /* Go through pending send request list and cancel them all */
351 CurrentEntry = AddrFile->TransmitQueue.Flink;
352 while (CurrentEntry != &AddrFile->TransmitQueue) {
353 NextEntry = CurrentEntry->Flink;
354 SendRequest = CONTAINING_RECORD(CurrentEntry,
355 DATAGRAM_SEND_REQUEST, ListEntry);
356 /* Abort the request and free its resources */
357 TcpipReleaseSpinLock(&AddrFile->Lock, OldIrql);
358 (*SendRequest->Complete)(SendRequest->Context, STATUS_CANCELLED, 0);
359 exFreePool(SendRequest);
360 TcpipAcquireSpinLock(&AddrFile->Lock, &OldIrql);
361 CurrentEntry = NextEntry;
362 }
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 (*AddrFile->Free)(AddrFile);
384
385 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
386
387 return Status;
388 }
389
390
391 /*
392 * FUNCTION: Opens a connection file object
393 * ARGUMENTS:
394 * Request = Pointer to TDI request structure for this request
395 * ClientContext = Pointer to client context information
396 * RETURNS:
397 * Status of operation
398 */
399 NTSTATUS FileOpenConnection(
400 PTDI_REQUEST Request,
401 PVOID ClientContext)
402 {
403 NTSTATUS Status;
404 PCONNECTION_ENDPOINT Connection;
405
406 TI_DbgPrint(MID_TRACE, ("Called.\n"));
407
408 Connection = TCPAllocateConnectionEndpoint( ClientContext );
409
410 if( !Connection ) return STATUS_NO_MEMORY;
411
412 TcpipRecursiveMutexEnter(&TCPLock, TRUE);
413 Status = TCPSocket( Connection, AF_INET, SOCK_STREAM, IPPROTO_TCP );
414 TcpipRecursiveMutexLeave(&TCPLock);
415
416 if( !NT_SUCCESS(Status) ) {
417 TCPFreeConnectionEndpoint( Connection );
418 return Status;
419 }
420
421 /* Return connection endpoint file object */
422 Request->Handle.ConnectionContext = Connection;
423
424 /* Add connection endpoint to global list */
425 ExInterlockedInsertTailList(
426 &ConnectionEndpointListHead,
427 &Connection->ListEntry,
428 &ConnectionEndpointListLock);
429
430 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
431
432 return STATUS_SUCCESS;
433 }
434
435
436 /*
437 * FUNCTION: Find a connection by examining the context field. This
438 * is needed in some situations where a FIN reply is needed after a
439 * socket is formally broken.
440 * ARGUMENTS:
441 * Request = Pointer to TDI request structure for this request
442 * RETURNS:
443 * Status of operation
444 */
445 PCONNECTION_ENDPOINT FileFindConnectionByContext( PVOID Context ) {
446 PLIST_ENTRY Entry;
447 KIRQL OldIrql;
448 PCONNECTION_ENDPOINT Connection = NULL;
449
450 TcpipAcquireSpinLock( &ConnectionEndpointListLock, &OldIrql );
451
452 for( Entry = ConnectionEndpointListHead.Flink;
453 Entry != &ConnectionEndpointListHead;
454 Entry = Entry->Flink ) {
455 Connection =
456 CONTAINING_RECORD( Entry, CONNECTION_ENDPOINT, ListEntry );
457 if( Connection->SocketContext == Context ) break;
458 else Connection = NULL;
459 }
460
461 TcpipReleaseSpinLock( &ConnectionEndpointListLock, OldIrql );
462
463 return Connection;
464 }
465
466 /*
467 * FUNCTION: Closes an connection file object
468 * ARGUMENTS:
469 * Request = Pointer to TDI request structure for this request
470 * RETURNS:
471 * Status of operation
472 */
473 NTSTATUS FileCloseConnection(
474 PTDI_REQUEST Request)
475 {
476 PCONNECTION_ENDPOINT Connection;
477 KIRQL OldIrql;
478
479 TI_DbgPrint(MID_TRACE, ("Called.\n"));
480
481 Connection = Request->Handle.ConnectionContext;
482
483 TcpipAcquireSpinLock(&ConnectionEndpointListLock, &OldIrql);
484 RemoveEntryList(&Connection->ListEntry);
485 TcpipReleaseSpinLock(&ConnectionEndpointListLock, OldIrql);
486
487 TcpipRecursiveMutexEnter( &TCPLock, TRUE );
488 TCPClose( Connection );
489 TcpipRecursiveMutexLeave( &TCPLock );
490
491 TCPFreeConnectionEndpoint(Connection);
492
493 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
494
495 return STATUS_SUCCESS;
496 }
497
498
499 /*
500 * FUNCTION: Opens a control channel file object
501 * ARGUMENTS:
502 * Request = Pointer to TDI request structure for this request
503 * RETURNS:
504 * Status of operation
505 */
506 NTSTATUS FileOpenControlChannel(
507 PTDI_REQUEST Request)
508 {
509 PCONTROL_CHANNEL ControlChannel;
510 TI_DbgPrint(MID_TRACE, ("Called.\n"));
511
512 ControlChannel = exAllocatePool(NonPagedPool, sizeof(*ControlChannel));
513
514 if (!ControlChannel) {
515 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
516 return STATUS_INSUFFICIENT_RESOURCES;
517 }
518
519 RtlZeroMemory(ControlChannel, sizeof(CONTROL_CHANNEL));
520
521 /* Make sure address is a local unicast address or 0 */
522
523 /* Locate address entry. If specified address is 0, a random address is chosen */
524
525 /* Initialize receive and transmit queues */
526 InitializeListHead(&ControlChannel->ListEntry);
527
528 /* Initialize spin lock that protects the address file object */
529 KeInitializeSpinLock(&ControlChannel->Lock);
530
531 /* Return address file object */
532 Request->Handle.ControlChannel = ControlChannel;
533
534 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
535
536 return STATUS_SUCCESS;
537 }
538
539 /*
540 * FUNCTION: Closes a control channel file object
541 * ARGUMENTS:
542 * Request = Pointer to TDI request structure for this request
543 * RETURNS:
544 * Status of operation
545 */
546 NTSTATUS FileCloseControlChannel(
547 PTDI_REQUEST Request)
548 {
549 PCONTROL_CHANNEL ControlChannel = Request->Handle.ControlChannel;
550 NTSTATUS Status = STATUS_SUCCESS;
551
552 exFreePool(ControlChannel);
553 Request->Handle.ControlChannel = NULL;
554
555 return Status;
556 }
557
558 /* EOF */