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