Sync with trunk head (r48654)
[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 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 PADDRESS_FILE AddrFile = Object;
157 KIRQL OldIrql;
158 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest;
159 PDATAGRAM_SEND_REQUEST SendRequest;
160 PLIST_ENTRY CurrentEntry;
161
162 TI_DbgPrint(MID_TRACE, ("Called.\n"));
163
164 /* Remove address file from the global list */
165 TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
166 RemoveEntryList(&AddrFile->ListEntry);
167 TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
168
169 /* FIXME: Kill TCP connections on this address file object */
170
171 /* Return pending requests with error */
172
173 TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting receive requests on AddrFile at (0x%X).\n", AddrFile));
174
175 /* Go through pending receive request list and cancel them all */
176 while ((CurrentEntry = ExInterlockedRemoveHeadList(&AddrFile->ReceiveQueue, &AddrFile->Lock))) {
177 ReceiveRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
178 (*ReceiveRequest->Complete)(ReceiveRequest->Context, STATUS_CANCELLED, 0);
179 /* ExFreePoolWithTag(ReceiveRequest, DATAGRAM_RECV_TAG); FIXME: WTF? */
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 while ((CurrentEntry = ExInterlockedRemoveHeadList(&AddrFile->ReceiveQueue, &AddrFile->Lock))) {
186 SendRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_SEND_REQUEST, ListEntry);
187 (*SendRequest->Complete)(SendRequest->Context, STATUS_CANCELLED, 0);
188 ExFreePoolWithTag(SendRequest, DATAGRAM_SEND_TAG);
189 }
190
191 /* Protocol specific handling */
192 switch (AddrFile->Protocol) {
193 case IPPROTO_TCP:
194 TCPFreePort( AddrFile->Port );
195 break;
196
197 case IPPROTO_UDP:
198 UDPFreePort( AddrFile->Port );
199 break;
200 }
201
202 RemoveEntityByContext(AddrFile);
203
204 ExFreePoolWithTag(Object, ADDR_FILE_TAG);
205 }
206
207
208 VOID ControlChannelFree(
209 PVOID Object)
210 /*
211 * FUNCTION: Frees an address file object
212 * ARGUMENTS:
213 * Object = Pointer to address file object to free
214 */
215 {
216 ExFreePoolWithTag(Object, CONTROL_CHANNEL_TAG);
217 }
218
219
220 /*
221 * FUNCTION: Open an address file object
222 * ARGUMENTS:
223 * Request = Pointer to TDI request structure for this request
224 * Address = Pointer to address to be opened
225 * Protocol = Protocol on which to open the address
226 * Options = Pointer to option buffer
227 * RETURNS:
228 * Status of operation
229 */
230 NTSTATUS FileOpenAddress(
231 PTDI_REQUEST Request,
232 PTA_IP_ADDRESS Address,
233 USHORT Protocol,
234 PVOID Options)
235 {
236 PADDRESS_FILE AddrFile;
237
238 TI_DbgPrint(MID_TRACE, ("Called (Proto %d).\n", Protocol));
239
240 AddrFile = ExAllocatePoolWithTag(NonPagedPool, sizeof(ADDRESS_FILE),
241 ADDR_FILE_TAG);
242 if (!AddrFile) {
243 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
244 return STATUS_INSUFFICIENT_RESOURCES;
245 }
246
247 RtlZeroMemory(AddrFile, sizeof(ADDRESS_FILE));
248
249 AddrFile->RefCount = 1;
250 AddrFile->Free = AddrFileFree;
251
252 /* Set our default options */
253 AddrFile->TTL = 128;
254 AddrFile->DF = 0;
255 AddrFile->BCast = 1;
256 AddrFile->HeaderIncl = 1;
257
258 /* Make sure address is a local unicast address or 0 */
259 /* FIXME: IPv4 only */
260 AddrFile->Family = Address->Address[0].AddressType;
261 AddrFile->Address.Address.IPv4Address = Address->Address[0].Address[0].in_addr;
262 AddrFile->Address.Type = IP_ADDRESS_V4;
263
264 if (!AddrIsUnspecified(&AddrFile->Address) &&
265 !AddrLocateInterface(&AddrFile->Address)) {
266 ExFreePoolWithTag(AddrFile, ADDR_FILE_TAG);
267 TI_DbgPrint(MIN_TRACE, ("Non-local address given (0x%X).\n", A2S(&AddrFile->Address)));
268 return STATUS_INVALID_ADDRESS;
269 }
270
271 TI_DbgPrint(MID_TRACE, ("Opening address %s for communication (P=%d U=%d).\n",
272 A2S(&AddrFile->Address), Protocol, IPPROTO_UDP));
273
274 /* Protocol specific handling */
275 switch (Protocol) {
276 case IPPROTO_TCP:
277 AddrFile->Port =
278 TCPAllocatePort(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 ExFreePoolWithTag(AddrFile, ADDR_FILE_TAG);
285 return STATUS_ADDRESS_ALREADY_EXISTS;
286 }
287
288 AddEntity(CO_TL_ENTITY, AddrFile, CO_TL_TCP);
289
290 AddrFile->Send = NULL; /* TCPSendData */
291 break;
292
293 case IPPROTO_UDP:
294 TI_DbgPrint(MID_TRACE,("Allocating udp port\n"));
295 AddrFile->Port =
296 UDPAllocatePort(Address->Address[0].Address[0].sin_port);
297
298 if ((Address->Address[0].Address[0].sin_port &&
299 AddrFile->Port != Address->Address[0].Address[0].sin_port) ||
300 AddrFile->Port == 0xffff)
301 {
302 ExFreePoolWithTag(AddrFile, ADDR_FILE_TAG);
303 return STATUS_ADDRESS_ALREADY_EXISTS;
304 }
305
306 TI_DbgPrint(MID_TRACE,("Setting port %d (wanted %d)\n",
307 AddrFile->Port,
308 Address->Address[0].Address[0].sin_port));
309
310 AddEntity(CL_TL_ENTITY, AddrFile, CL_TL_UDP);
311
312 AddrFile->Send = UDPSendDatagram;
313 break;
314
315 case IPPROTO_ICMP:
316 AddrFile->Port = 0;
317 AddrFile->Send = ICMPSendDatagram;
318
319 /* FIXME: Verify this */
320 AddEntity(ER_ENTITY, AddrFile, ER_ICMP);
321 break;
322
323 default:
324 /* Use raw IP for all other protocols */
325 AddrFile->Port = 0;
326 AddrFile->Send = RawIPSendDatagram;
327
328 /* FIXME: Verify this */
329 AddEntity(CL_TL_ENTITY, AddrFile, 0);
330 break;
331 }
332
333 TI_DbgPrint(MID_TRACE, ("IP protocol number for address file object is %d.\n",
334 Protocol));
335
336 TI_DbgPrint(MID_TRACE, ("Port number for address file object is %d.\n",
337 WN2H(AddrFile->Port)));
338
339 /* Set protocol */
340 AddrFile->Protocol = Protocol;
341
342 /* Initialize receive and transmit queues */
343 InitializeListHead(&AddrFile->ReceiveQueue);
344 InitializeListHead(&AddrFile->TransmitQueue);
345
346 /* Initialize spin lock that protects the address file object */
347 KeInitializeSpinLock(&AddrFile->Lock);
348
349 /* Return address file object */
350 Request->Handle.AddressHandle = AddrFile;
351
352 /* Add address file to global list */
353 ExInterlockedInsertTailList(
354 &AddressFileListHead,
355 &AddrFile->ListEntry,
356 &AddressFileListLock);
357
358 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
359
360 return STATUS_SUCCESS;
361 }
362
363
364 /*
365 * FUNCTION: Closes an address file object
366 * ARGUMENTS:
367 * Request = Pointer to TDI request structure for this request
368 * RETURNS:
369 * Status of operation
370 */
371 NTSTATUS FileCloseAddress(
372 PTDI_REQUEST Request)
373 {
374 PADDRESS_FILE AddrFile = Request->Handle.AddressHandle;
375 KIRQL OldIrql;
376
377 if (!Request->Handle.AddressHandle) return STATUS_INVALID_PARAMETER;
378
379 LockObject(AddrFile, &OldIrql);
380 /* We have to close this connection because we started it */
381 if( AddrFile->Listener )
382 {
383 AddrFile->Listener->AddressFile = NULL;
384 TCPClose( AddrFile->Listener );
385 }
386 if( AddrFile->Connection )
387 {
388 AddrFile->Connection->AddressFile = NULL;
389 DereferenceObject( AddrFile->Connection );
390 }
391 UnlockObject(AddrFile, OldIrql);
392
393 DereferenceObject(AddrFile);
394
395 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
396
397 return STATUS_SUCCESS;
398 }
399
400
401 /*
402 * FUNCTION: Opens a connection file object
403 * ARGUMENTS:
404 * Request = Pointer to TDI request structure for this request
405 * ClientContext = Pointer to client context information
406 * RETURNS:
407 * Status of operation
408 */
409 NTSTATUS FileOpenConnection(
410 PTDI_REQUEST Request,
411 PVOID ClientContext)
412 {
413 NTSTATUS Status;
414 PCONNECTION_ENDPOINT Connection;
415
416 TI_DbgPrint(MID_TRACE, ("Called.\n"));
417
418 Connection = TCPAllocateConnectionEndpoint( ClientContext );
419
420 if( !Connection ) return STATUS_NO_MEMORY;
421
422 Status = TCPSocket( Connection, AF_INET, SOCK_STREAM, IPPROTO_TCP );
423
424 if( !NT_SUCCESS(Status) ) {
425 DereferenceObject( Connection );
426 return Status;
427 }
428
429 /* Return connection endpoint file object */
430 Request->Handle.ConnectionContext = Connection;
431
432 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
433
434 return STATUS_SUCCESS;
435 }
436
437 /*
438 * FUNCTION: Closes an connection file object
439 * ARGUMENTS:
440 * Request = Pointer to TDI request structure for this request
441 * RETURNS:
442 * Status of operation
443 */
444 NTSTATUS FileCloseConnection(
445 PTDI_REQUEST Request)
446 {
447 PCONNECTION_ENDPOINT Connection;
448
449 TI_DbgPrint(MID_TRACE, ("Called.\n"));
450
451 Connection = Request->Handle.ConnectionContext;
452
453 if (!Connection) return STATUS_INVALID_PARAMETER;
454
455 TCPClose( Connection );
456
457 Request->Handle.ConnectionContext = NULL;
458
459 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
460
461 return STATUS_SUCCESS;
462 }
463
464 /*
465 * FUNCTION: Opens a control channel file object
466 * ARGUMENTS:
467 * Request = Pointer to TDI request structure for this request
468 * RETURNS:
469 * Status of operation
470 */
471 NTSTATUS FileOpenControlChannel(
472 PTDI_REQUEST Request)
473 {
474 PCONTROL_CHANNEL ControlChannel;
475 TI_DbgPrint(MID_TRACE, ("Called.\n"));
476
477 ControlChannel = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ControlChannel),
478 CONTROL_CHANNEL_TAG);
479
480 if (!ControlChannel) {
481 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
482 return STATUS_INSUFFICIENT_RESOURCES;
483 }
484
485 RtlZeroMemory(ControlChannel, sizeof(CONTROL_CHANNEL));
486
487 /* Make sure address is a local unicast address or 0 */
488
489 /* Locate address entry. If specified address is 0, a random address is chosen */
490
491 /* Initialize receive and transmit queues */
492 InitializeListHead(&ControlChannel->ListEntry);
493
494 /* Initialize spin lock that protects the address file object */
495 KeInitializeSpinLock(&ControlChannel->Lock);
496
497 ControlChannel->RefCount = 1;
498 ControlChannel->Free = ControlChannelFree;
499
500 /* Return address file object */
501 Request->Handle.ControlChannel = ControlChannel;
502
503 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
504
505 return STATUS_SUCCESS;
506 }
507
508 /*
509 * FUNCTION: Closes 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 FileCloseControlChannel(
516 PTDI_REQUEST Request)
517 {
518 if (!Request->Handle.ControlChannel) return STATUS_INVALID_PARAMETER;
519
520 DereferenceObject((PCONTROL_CHANNEL)Request->Handle.ControlChannel);
521
522 Request->Handle.ControlChannel = NULL;
523
524 return STATUS_SUCCESS;
525 }
526
527 /* EOF */