[SERVMAN]
[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 AddrFile->Listener->AddressFile = NULL;
413 TCPClose( AddrFile->Listener );
414 }
415
416 UnlockObject(AddrFile, OldIrql);
417
418 DereferenceObject(AddrFile);
419
420 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
421
422 return STATUS_SUCCESS;
423 }
424
425
426 /*
427 * FUNCTION: Opens a connection file object
428 * ARGUMENTS:
429 * Request = Pointer to TDI request structure for this request
430 * ClientContext = Pointer to client context information
431 * RETURNS:
432 * Status of operation
433 */
434 NTSTATUS FileOpenConnection(
435 PTDI_REQUEST Request,
436 PVOID ClientContext)
437 {
438 NTSTATUS Status;
439 PCONNECTION_ENDPOINT Connection;
440
441 TI_DbgPrint(MID_TRACE, ("Called.\n"));
442
443 Connection = TCPAllocateConnectionEndpoint( ClientContext );
444
445 if( !Connection ) return STATUS_NO_MEMORY;
446
447 Status = TCPSocket( Connection, AF_INET, SOCK_STREAM, IPPROTO_TCP );
448
449 if( !NT_SUCCESS(Status) ) {
450 DereferenceObject( Connection );
451 return Status;
452 }
453
454 /* Return connection endpoint file object */
455 Request->Handle.ConnectionContext = Connection;
456
457 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
458
459 return STATUS_SUCCESS;
460 }
461
462 /*
463 * FUNCTION: Closes an connection file object
464 * ARGUMENTS:
465 * Request = Pointer to TDI request structure for this request
466 * RETURNS:
467 * Status of operation
468 */
469 NTSTATUS FileCloseConnection(
470 PTDI_REQUEST Request)
471 {
472 PCONNECTION_ENDPOINT Connection;
473
474 TI_DbgPrint(MID_TRACE, ("Called.\n"));
475
476 Connection = Request->Handle.ConnectionContext;
477
478 if (!Connection) return STATUS_INVALID_PARAMETER;
479
480 TCPClose( Connection );
481
482 Request->Handle.ConnectionContext = NULL;
483
484 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
485
486 return STATUS_SUCCESS;
487 }
488
489 /*
490 * FUNCTION: Opens a control channel file object
491 * ARGUMENTS:
492 * Request = Pointer to TDI request structure for this request
493 * RETURNS:
494 * Status of operation
495 */
496 NTSTATUS FileOpenControlChannel(
497 PTDI_REQUEST Request)
498 {
499 PCONTROL_CHANNEL ControlChannel;
500 TI_DbgPrint(MID_TRACE, ("Called.\n"));
501
502 ControlChannel = ExAllocatePoolWithTag(NonPagedPool, sizeof(*ControlChannel),
503 CONTROL_CHANNEL_TAG);
504
505 if (!ControlChannel) {
506 TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
507 return STATUS_INSUFFICIENT_RESOURCES;
508 }
509
510 RtlZeroMemory(ControlChannel, sizeof(CONTROL_CHANNEL));
511
512 /* Make sure address is a local unicast address or 0 */
513
514 /* Locate address entry. If specified address is 0, a random address is chosen */
515
516 /* Initialize receive and transmit queues */
517 InitializeListHead(&ControlChannel->ListEntry);
518
519 /* Initialize spin lock that protects the address file object */
520 KeInitializeSpinLock(&ControlChannel->Lock);
521
522 ControlChannel->RefCount = 1;
523 ControlChannel->Free = ControlChannelFree;
524
525 /* Return address file object */
526 Request->Handle.ControlChannel = ControlChannel;
527
528 TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
529
530 return STATUS_SUCCESS;
531 }
532
533 /*
534 * FUNCTION: Closes a control channel file object
535 * ARGUMENTS:
536 * Request = Pointer to TDI request structure for this request
537 * RETURNS:
538 * Status of operation
539 */
540 NTSTATUS FileCloseControlChannel(
541 PTDI_REQUEST Request)
542 {
543 if (!Request->Handle.ControlChannel) return STATUS_INVALID_PARAMETER;
544
545 DereferenceObject((PCONTROL_CHANNEL)Request->Handle.ControlChannel);
546
547 Request->Handle.ControlChannel = NULL;
548
549 return STATUS_SUCCESS;
550 }
551
552 /* EOF */