Sync to trunk r39350.
[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: Deletes a connection endpoint file object
208 * ARGUMENTS:
209 * Connection = Pointer to connection endpoint to delete
210 */
211 VOID DeleteConnectionEndpoint(
212 PCONNECTION_ENDPOINT Connection)
213 {
214 KIRQL OldIrql;
215
216 TI_DbgPrint(MID_TRACE, ("Called.\n"));
217
218 /* Remove connection endpoint from the global list */
219 TcpipAcquireSpinLock(&ConnectionEndpointListLock, &OldIrql);
220 RemoveEntryList(&Connection->ListEntry);
221 TcpipReleaseSpinLock(&ConnectionEndpointListLock, OldIrql);
222
223 TCPFreeConnectionEndpoint(Connection);
224
225 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
226 }
227
228 /*
229 * FUNCTION: Open an address file object
230 * ARGUMENTS:
231 * Request = Pointer to TDI request structure for this request
232 * Address = Pointer to address to be opened
233 * Protocol = Protocol on which to open the address
234 * Options = Pointer to option buffer
235 * RETURNS:
236 * Status of operation
237 */
238 NTSTATUS FileOpenAddress(
239 PTDI_REQUEST Request,
240 PTA_IP_ADDRESS Address,
241 USHORT Protocol,
242 PVOID Options)
243 {
244 IPv4_RAW_ADDRESS IPv4Address;
245 BOOLEAN Matched;
246 PADDRESS_FILE AddrFile;
247
248 TI_DbgPrint(MID_TRACE, ("Called (Proto %d).\n", Protocol));
249
250 AddrFile = exAllocatePool(NonPagedPool, sizeof(ADDRESS_FILE));
251 if (!AddrFile) {
252 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
253 return STATUS_INSUFFICIENT_RESOURCES;
254 }
255
256 TI_DbgPrint(DEBUG_ADDRFILE, ("Address file object allocated at (0x%X).\n", AddrFile));
257
258 RtlZeroMemory(AddrFile, sizeof(ADDRESS_FILE));
259
260 AddrFile->Free = AddrFileFree;
261
262 /* Make sure address is a local unicast address or 0 */
263
264 /* Locate address entry. If specified address is 0, a random address is chosen */
265
266 /* FIXME: IPv4 only */
267 AddrFile->Family = Address->Address[0].AddressType;
268 IPv4Address = Address->Address[0].Address[0].in_addr;
269 if (IPv4Address == 0)
270 Matched = IPGetDefaultAddress(&AddrFile->Address);
271 else
272 Matched = AddrLocateADEv4(IPv4Address, &AddrFile->Address);
273
274 if (!Matched) {
275 exFreePool(AddrFile);
276 TI_DbgPrint(MIN_TRACE, ("Non-local address given (0x%X).\n", DN2H(IPv4Address)));
277 return STATUS_INVALID_PARAMETER;
278 }
279
280 TI_DbgPrint(MID_TRACE, ("Opening address %s for communication (P=%d U=%d).\n",
281 A2S(&AddrFile->Address), Protocol, IPPROTO_UDP));
282
283 /* Protocol specific handling */
284 switch (Protocol) {
285 case IPPROTO_TCP:
286 AddrFile->Port =
287 TCPAllocatePort(Address->Address[0].Address[0].sin_port);
288
289 if ((Address->Address[0].Address[0].sin_port &&
290 AddrFile->Port != Address->Address[0].Address[0].sin_port) ||
291 AddrFile->Port == 0xffff)
292 {
293 exFreePool(AddrFile);
294 return STATUS_INVALID_PARAMETER;
295 }
296
297 AddrFile->Send = NULL; /* TCPSendData */
298 break;
299
300 case IPPROTO_UDP:
301 TI_DbgPrint(MID_TRACE,("Allocating udp port\n"));
302 AddrFile->Port =
303 UDPAllocatePort(Address->Address[0].Address[0].sin_port);
304
305 if ((Address->Address[0].Address[0].sin_port &&
306 AddrFile->Port != Address->Address[0].Address[0].sin_port) ||
307 AddrFile->Port == 0xffff)
308 {
309 exFreePool(AddrFile);
310 return STATUS_INVALID_PARAMETER;
311 }
312
313 TI_DbgPrint(MID_TRACE,("Setting port %d (wanted %d)\n",
314 AddrFile->Port,
315 Address->Address[0].Address[0].sin_port));
316 AddrFile->Send = UDPSendDatagram;
317 break;
318
319 default:
320 /* Use raw IP for all other protocols */
321 AddrFile->Port = 0;
322 AddrFile->Send = RawIPSendDatagram;
323 break;
324 }
325
326 TI_DbgPrint(MID_TRACE, ("IP protocol number for address file object is %d.\n",
327 Protocol));
328
329 TI_DbgPrint(MID_TRACE, ("Port number for address file object is %d.\n",
330 WN2H(AddrFile->Port)));
331
332 /* Set protocol */
333 AddrFile->Protocol = Protocol;
334
335 /* Initialize receive and transmit queues */
336 InitializeListHead(&AddrFile->ReceiveQueue);
337 InitializeListHead(&AddrFile->TransmitQueue);
338
339 /* Initialize spin lock that protects the address file object */
340 KeInitializeSpinLock(&AddrFile->Lock);
341
342 /* Set valid flag so the address can be used */
343 AF_SET_VALID(AddrFile);
344
345 /* Return address file object */
346 Request->Handle.AddressHandle = AddrFile;
347
348 /* Add address file to global list */
349 ExInterlockedInsertTailList(
350 &AddressFileListHead,
351 &AddrFile->ListEntry,
352 &AddressFileListLock);
353
354 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
355
356 return STATUS_SUCCESS;
357 }
358
359
360 /*
361 * FUNCTION: Closes an address file object
362 * ARGUMENTS:
363 * Request = Pointer to TDI request structure for this request
364 * RETURNS:
365 * Status of operation
366 */
367 NTSTATUS FileCloseAddress(
368 PTDI_REQUEST Request)
369 {
370 KIRQL OldIrql;
371 PADDRESS_FILE AddrFile;
372 NTSTATUS Status = STATUS_SUCCESS;
373
374 TI_DbgPrint(MID_TRACE, ("Called.\n"));
375
376 AddrFile = Request->Handle.AddressHandle;
377
378 TcpipAcquireSpinLock(&AddrFile->Lock, &OldIrql);
379
380 /* Set address file object exclusive to us */
381 AF_SET_BUSY(AddrFile);
382 AF_CLR_VALID(AddrFile);
383
384 TcpipReleaseSpinLock(&AddrFile->Lock, OldIrql);
385
386 /* Protocol specific handling */
387 switch (AddrFile->Protocol) {
388 case IPPROTO_TCP:
389 TCPFreePort( AddrFile->Port );
390 if( AddrFile->Listener ) {
391 TCPClose( AddrFile->Listener );
392 exFreePool( AddrFile->Listener );
393 }
394 break;
395
396 case IPPROTO_UDP:
397 UDPFreePort( AddrFile->Port );
398 break;
399 }
400
401 DeleteAddress(AddrFile);
402
403 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
404
405 return Status;
406 }
407
408
409 /*
410 * FUNCTION: Opens a connection file object
411 * ARGUMENTS:
412 * Request = Pointer to TDI request structure for this request
413 * ClientContext = Pointer to client context information
414 * RETURNS:
415 * Status of operation
416 */
417 NTSTATUS FileOpenConnection(
418 PTDI_REQUEST Request,
419 PVOID ClientContext)
420 {
421 NTSTATUS Status;
422 PCONNECTION_ENDPOINT Connection;
423
424 TI_DbgPrint(MID_TRACE, ("Called.\n"));
425
426 Connection = TCPAllocateConnectionEndpoint( ClientContext );
427
428 if( !Connection ) return STATUS_NO_MEMORY;
429
430 Status = TCPSocket( Connection, AF_INET, SOCK_STREAM, IPPROTO_TCP );
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
494 TI_DbgPrint(MID_TRACE, ("Called.\n"));
495
496 Connection = Request->Handle.ConnectionContext;
497
498 TcpipRecursiveMutexEnter( &TCPLock, TRUE );
499 DeleteConnectionEndpoint(Connection);
500 TcpipRecursiveMutexLeave( &TCPLock );
501
502 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
503
504 return STATUS_SUCCESS;
505 }
506
507
508 /*
509 * FUNCTION: Opens a control channel file object
510 * ARGUMENTS:
511 * Request = Pointer to TDI request structure for this request
512 * RETURNS:
513 * Status of operation
514 */
515 NTSTATUS FileOpenControlChannel(
516 PTDI_REQUEST Request)
517 {
518 PCONTROL_CHANNEL ControlChannel;
519 TI_DbgPrint(MID_TRACE, ("Called.\n"));
520
521 ControlChannel = exAllocatePool(NonPagedPool, sizeof(*ControlChannel));
522
523 if (!ControlChannel) {
524 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
525 return STATUS_INSUFFICIENT_RESOURCES;
526 }
527
528 RtlZeroMemory(ControlChannel, sizeof(CONTROL_CHANNEL));
529
530 /* Make sure address is a local unicast address or 0 */
531
532 /* Locate address entry. If specified address is 0, a random address is chosen */
533
534 /* Initialize receive and transmit queues */
535 InitializeListHead(&ControlChannel->ListEntry);
536
537 /* Initialize spin lock that protects the address file object */
538 KeInitializeSpinLock(&ControlChannel->Lock);
539
540 /* Return address file object */
541 Request->Handle.ControlChannel = ControlChannel;
542
543 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
544
545 return STATUS_SUCCESS;
546 }
547
548 /*
549 * FUNCTION: Closes a control channel file object
550 * ARGUMENTS:
551 * Request = Pointer to TDI request structure for this request
552 * RETURNS:
553 * Status of operation
554 */
555 NTSTATUS FileCloseControlChannel(
556 PTDI_REQUEST Request)
557 {
558 PCONTROL_CHANNEL ControlChannel = Request->Handle.ControlChannel;
559 NTSTATUS Status = STATUS_SUCCESS;
560
561 exFreePool(ControlChannel);
562 Request->Handle.ControlChannel = NULL;
563
564 return Status;
565 }
566
567 /* EOF */