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)
8 * CSH 01/08-2000 Created
14 /* List of all address file objects managed by this driver */
15 LIST_ENTRY AddressFileListHead
;
16 KSPIN_LOCK AddressFileListLock
;
18 /* List of all connection endpoint file objects managed by this driver */
19 LIST_ENTRY ConnectionEndpointListHead
;
20 KSPIN_LOCK ConnectionEndpointListLock
;
23 * FUNCTION: Searches through address file entries to find the first match
25 * Address = IP address
27 * Protocol = Protocol number
28 * SearchContext = Pointer to search context
30 * Pointer to address file, NULL if none was found
32 PADDRESS_FILE
AddrSearchFirst(
36 PAF_SEARCH SearchContext
)
40 SearchContext
->Address
= Address
;
41 SearchContext
->Port
= Port
;
42 SearchContext
->Protocol
= Protocol
;
44 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
46 SearchContext
->Next
= AddressFileListHead
.Flink
;
48 if (!IsListEmpty(&AddressFileListHead
))
49 ReferenceObject(CONTAINING_RECORD(SearchContext
->Next
, ADDRESS_FILE
, ListEntry
));
51 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
53 return AddrSearchNext(SearchContext
);
56 BOOLEAN
AddrIsBroadcastMatch(
57 PIP_ADDRESS UnicastAddress
,
58 PIP_ADDRESS BroadcastAddress
) {
61 ForEachInterface(IF
) {
62 if ((AddrIsUnspecified(UnicastAddress
) ||
63 AddrIsEqual(&IF
->Unicast
, UnicastAddress
)) &&
64 (AddrIsEqual(&IF
->Broadcast
, BroadcastAddress
)))
71 BOOLEAN
AddrReceiveMatch(
72 PIP_ADDRESS LocalAddress
,
73 PIP_ADDRESS RemoteAddress
)
75 if (AddrIsEqual(LocalAddress
, RemoteAddress
))
77 /* Unicast address match */
81 if (AddrIsBroadcastMatch(LocalAddress
, RemoteAddress
))
83 /* Broadcast address match */
87 if (AddrIsUnspecified(LocalAddress
))
89 /* Local address unspecified */
93 if (AddrIsUnspecified(RemoteAddress
))
95 /* Remote address unspecified */
102 PADDRESS_FILE
AddrFindShared(
103 PIP_ADDRESS BindAddress
,
107 PLIST_ENTRY CurrentEntry
;
109 PADDRESS_FILE Current
= NULL
;
111 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
113 CurrentEntry
= AddressFileListHead
.Flink
;
114 while (CurrentEntry
!= &AddressFileListHead
) {
115 Current
= CONTAINING_RECORD(CurrentEntry
, ADDRESS_FILE
, ListEntry
);
117 /* See if this address matches the search criteria */
118 if ((Current
->Port
== Port
) &&
119 (Current
->Protocol
== Protocol
))
121 /* Increase the sharer count */
122 ASSERT(Current
->Sharers
!= 0);
123 InterlockedIncrement(&Current
->Sharers
);
127 CurrentEntry
= CurrentEntry
->Flink
;
131 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
137 * FUNCTION: Searches through address file entries to find next match
139 * SearchContext = Pointer to search context
141 * Pointer to address file, NULL if none was found
143 PADDRESS_FILE
AddrSearchNext(
144 PAF_SEARCH SearchContext
)
146 PLIST_ENTRY CurrentEntry
;
147 PIP_ADDRESS IPAddress
;
149 PADDRESS_FILE Current
= NULL
;
150 BOOLEAN Found
= FALSE
;
152 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
154 if (SearchContext
->Next
== &AddressFileListHead
)
156 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
160 /* Remove the extra reference we added to keep this address file in memory */
161 DereferenceObject(CONTAINING_RECORD(SearchContext
->Next
, ADDRESS_FILE
, ListEntry
));
163 CurrentEntry
= SearchContext
->Next
;
165 while (CurrentEntry
!= &AddressFileListHead
) {
166 Current
= CONTAINING_RECORD(CurrentEntry
, ADDRESS_FILE
, ListEntry
);
168 IPAddress
= &Current
->Address
;
170 TI_DbgPrint(DEBUG_ADDRFILE
, ("Comparing: ((%d, %d, %s), (%d, %d, %s)).\n",
174 WN2H(SearchContext
->Port
),
175 SearchContext
->Protocol
,
176 A2S(SearchContext
->Address
)));
178 /* See if this address matches the search criteria */
179 if ((Current
->Port
== SearchContext
->Port
) &&
180 (Current
->Protocol
== SearchContext
->Protocol
) &&
181 (AddrReceiveMatch(IPAddress
, SearchContext
->Address
))) {
182 /* We've found a match */
186 CurrentEntry
= CurrentEntry
->Flink
;
191 SearchContext
->Next
= CurrentEntry
->Flink
;
193 if (SearchContext
->Next
!= &AddressFileListHead
)
195 /* Reference the next address file to prevent the link from disappearing behind our back */
196 ReferenceObject(CONTAINING_RECORD(SearchContext
->Next
, ADDRESS_FILE
, ListEntry
));
202 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
210 * FUNCTION: Frees an address file object
212 * Object = Pointer to address file object to free
215 PADDRESS_FILE AddrFile
= Object
;
217 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest
;
218 PDATAGRAM_SEND_REQUEST SendRequest
;
219 PLIST_ENTRY CurrentEntry
;
221 TI_DbgPrint(MID_TRACE
, ("Called.\n"));
223 /* We should not be associated with a connection here */
224 ASSERT(!AddrFile
->Connection
);
226 /* Remove address file from the global list */
227 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
228 RemoveEntryList(&AddrFile
->ListEntry
);
229 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
231 /* FIXME: Kill TCP connections on this address file object */
233 /* Return pending requests with error */
235 TI_DbgPrint(DEBUG_ADDRFILE
, ("Aborting receive requests on AddrFile at (0x%X).\n", AddrFile
));
237 /* Go through pending receive request list and cancel them all */
238 while ((CurrentEntry
= ExInterlockedRemoveHeadList(&AddrFile
->ReceiveQueue
, &AddrFile
->Lock
))) {
239 ReceiveRequest
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_RECEIVE_REQUEST
, ListEntry
);
240 (*ReceiveRequest
->Complete
)(ReceiveRequest
->Context
, STATUS_CANCELLED
, 0);
241 /* ExFreePoolWithTag(ReceiveRequest, DATAGRAM_RECV_TAG); FIXME: WTF? */
244 TI_DbgPrint(DEBUG_ADDRFILE
, ("Aborting send requests on address file at (0x%X).\n", AddrFile
));
246 /* Go through pending send request list and cancel them all */
247 while ((CurrentEntry
= ExInterlockedRemoveHeadList(&AddrFile
->ReceiveQueue
, &AddrFile
->Lock
))) {
248 SendRequest
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_SEND_REQUEST
, ListEntry
);
249 (*SendRequest
->Complete
)(SendRequest
->Context
, STATUS_CANCELLED
, 0);
250 ExFreePoolWithTag(SendRequest
, DATAGRAM_SEND_TAG
);
253 /* Protocol specific handling */
254 switch (AddrFile
->Protocol
) {
258 TCPFreePort(AddrFile
->Port
);
263 UDPFreePort( AddrFile
->Port
);
267 RemoveEntityByContext(AddrFile
);
269 ExFreePoolWithTag(Object
, ADDR_FILE_TAG
);
273 VOID
ControlChannelFree(
276 * FUNCTION: Frees an address file object
278 * Object = Pointer to address file object to free
281 ExFreePoolWithTag(Object
, CONTROL_CHANNEL_TAG
);
286 * FUNCTION: Open an address file object
288 * Request = Pointer to TDI request structure for this request
289 * Address = Pointer to address to be opened
290 * Protocol = Protocol on which to open the address
291 * Shared = Specifies if the address is opened for shared access
292 * Options = Pointer to option buffer
294 * Status of operation
296 NTSTATUS
FileOpenAddress(
297 PTDI_REQUEST Request
,
298 PTA_IP_ADDRESS Address
,
303 PADDRESS_FILE AddrFile
;
305 TI_DbgPrint(MID_TRACE
, ("Called (Proto %d).\n", Protocol
));
307 /* If it's shared and has a port specified, look for a match */
308 if ((Shared
!= FALSE
) && (Address
->Address
[0].Address
[0].sin_port
!= 0))
310 AddrFile
= AddrFindShared(NULL
, Address
->Address
[0].Address
[0].sin_port
, Protocol
);
311 if (AddrFile
!= NULL
)
313 Request
->Handle
.AddressHandle
= AddrFile
;
314 return STATUS_SUCCESS
;
318 AddrFile
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(ADDRESS_FILE
),
321 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
322 return STATUS_INSUFFICIENT_RESOURCES
;
325 RtlZeroMemory(AddrFile
, sizeof(ADDRESS_FILE
));
327 AddrFile
->RefCount
= 1;
328 AddrFile
->Free
= AddrFileFree
;
329 AddrFile
->Sharers
= 1;
331 /* Set our default options */
335 AddrFile
->HeaderIncl
= 1;
337 /* Make sure address is a local unicast address or 0 */
338 /* FIXME: IPv4 only */
339 AddrFile
->Family
= Address
->Address
[0].AddressType
;
340 AddrFile
->Address
.Address
.IPv4Address
= Address
->Address
[0].Address
[0].in_addr
;
341 AddrFile
->Address
.Type
= IP_ADDRESS_V4
;
343 if (!AddrIsUnspecified(&AddrFile
->Address
) &&
344 !AddrLocateInterface(&AddrFile
->Address
)) {
345 TI_DbgPrint(MIN_TRACE
, ("Non-local address given (0x%X).\n", A2S(&AddrFile
->Address
)));
346 ExFreePoolWithTag(AddrFile
, ADDR_FILE_TAG
);
347 return STATUS_INVALID_ADDRESS
;
350 TI_DbgPrint(MID_TRACE
, ("Opening address %s for communication (P=%d U=%d).\n",
351 A2S(&AddrFile
->Address
), Protocol
, IPPROTO_UDP
));
353 /* Protocol specific handling */
356 if (Address
->Address
[0].Address
[0].sin_port
)
358 /* The client specified an explicit port so we force a bind to this */
359 AddrFile
->Port
= TCPAllocatePort(Address
->Address
[0].Address
[0].sin_port
);
361 /* Check for bind success */
362 if (AddrFile
->Port
== 0xffff)
364 ExFreePoolWithTag(AddrFile
, ADDR_FILE_TAG
);
365 return STATUS_ADDRESS_ALREADY_EXISTS
;
369 ASSERT(Address
->Address
[0].Address
[0].sin_port
== AddrFile
->Port
);
371 else if (!AddrIsUnspecified(&AddrFile
->Address
))
373 /* The client is trying to bind to a local address so allocate a port now too */
374 AddrFile
->Port
= TCPAllocatePort(0);
376 /* Check for bind success */
377 if (AddrFile
->Port
== 0xffff)
379 ExFreePoolWithTag(AddrFile
, ADDR_FILE_TAG
);
380 return STATUS_ADDRESS_ALREADY_EXISTS
;
385 /* The client wants an unspecified port with an unspecified address so we wait to see what the TCP library gives us */
389 AddEntity(CO_TL_ENTITY
, AddrFile
, CO_TL_TCP
);
391 AddrFile
->Send
= NULL
; /* TCPSendData */
395 TI_DbgPrint(MID_TRACE
,("Allocating udp port\n"));
397 UDPAllocatePort(Address
->Address
[0].Address
[0].sin_port
);
399 if ((Address
->Address
[0].Address
[0].sin_port
&&
400 AddrFile
->Port
!= Address
->Address
[0].Address
[0].sin_port
) ||
401 AddrFile
->Port
== 0xffff)
403 ExFreePoolWithTag(AddrFile
, ADDR_FILE_TAG
);
404 return STATUS_ADDRESS_ALREADY_EXISTS
;
407 TI_DbgPrint(MID_TRACE
,("Setting port %d (wanted %d)\n",
409 Address
->Address
[0].Address
[0].sin_port
));
411 AddEntity(CL_TL_ENTITY
, AddrFile
, CL_TL_UDP
);
413 AddrFile
->Send
= UDPSendDatagram
;
418 AddrFile
->Send
= ICMPSendDatagram
;
420 /* FIXME: Verify this */
421 AddEntity(ER_ENTITY
, AddrFile
, ER_ICMP
);
425 /* Use raw IP for all other protocols */
427 AddrFile
->Send
= RawIPSendDatagram
;
429 /* FIXME: Verify this */
430 AddEntity(CL_TL_ENTITY
, AddrFile
, 0);
434 TI_DbgPrint(MID_TRACE
, ("IP protocol number for address file object is %d.\n",
437 TI_DbgPrint(MID_TRACE
, ("Port number for address file object is %d.\n",
438 WN2H(AddrFile
->Port
)));
441 AddrFile
->Protocol
= Protocol
;
443 /* Initialize receive and transmit queues */
444 InitializeListHead(&AddrFile
->ReceiveQueue
);
445 InitializeListHead(&AddrFile
->TransmitQueue
);
447 /* Initialize spin lock that protects the address file object */
448 KeInitializeSpinLock(&AddrFile
->Lock
);
450 /* Return address file object */
451 Request
->Handle
.AddressHandle
= AddrFile
;
453 /* Add address file to global list */
454 ExInterlockedInsertTailList(
455 &AddressFileListHead
,
456 &AddrFile
->ListEntry
,
457 &AddressFileListLock
);
459 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
461 return STATUS_SUCCESS
;
466 * FUNCTION: Closes an address file object
468 * Request = Pointer to TDI request structure for this request
470 * Status of operation
472 NTSTATUS
FileCloseAddress(
473 PTDI_REQUEST Request
)
475 PADDRESS_FILE AddrFile
= Request
->Handle
.AddressHandle
;
478 if (!Request
->Handle
.AddressHandle
) return STATUS_INVALID_PARAMETER
;
480 LockObject(AddrFile
, &OldIrql
);
482 if (InterlockedDecrement(&AddrFile
->Sharers
) != 0)
484 /* Still other guys have open handles to this, so keep it around */
485 UnlockObject(AddrFile
, OldIrql
);
486 return STATUS_SUCCESS
;
489 /* We have to close this listener because we started it */
490 if( AddrFile
->Listener
)
492 TCPClose( AddrFile
->Listener
);
495 UnlockObject(AddrFile
, OldIrql
);
497 DereferenceObject(AddrFile
);
499 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
501 return STATUS_SUCCESS
;
506 * FUNCTION: Opens a connection file object
508 * Request = Pointer to TDI request structure for this request
509 * ClientContext = Pointer to client context information
511 * Status of operation
513 NTSTATUS
FileOpenConnection(
514 PTDI_REQUEST Request
,
518 PCONNECTION_ENDPOINT Connection
;
520 TI_DbgPrint(MID_TRACE
, ("Called.\n"));
522 Connection
= TCPAllocateConnectionEndpoint( ClientContext
);
524 if( !Connection
) return STATUS_NO_MEMORY
;
526 Status
= TCPSocket( Connection
, AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
528 if( !NT_SUCCESS(Status
) ) {
529 DereferenceObject( Connection
);
533 /* Return connection endpoint file object */
534 Request
->Handle
.ConnectionContext
= Connection
;
536 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
538 return STATUS_SUCCESS
;
542 * FUNCTION: Closes an connection file object
544 * Request = Pointer to TDI request structure for this request
546 * Status of operation
548 NTSTATUS
FileCloseConnection(
549 PTDI_REQUEST Request
)
551 PCONNECTION_ENDPOINT Connection
;
553 TI_DbgPrint(MID_TRACE
, ("Called.\n"));
555 Connection
= Request
->Handle
.ConnectionContext
;
557 if (!Connection
) return STATUS_INVALID_PARAMETER
;
559 TCPClose( Connection
);
561 Request
->Handle
.ConnectionContext
= NULL
;
563 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
565 return STATUS_SUCCESS
;
569 * FUNCTION: Opens a control channel file object
571 * Request = Pointer to TDI request structure for this request
573 * Status of operation
575 NTSTATUS
FileOpenControlChannel(
576 PTDI_REQUEST Request
)
578 PCONTROL_CHANNEL ControlChannel
;
579 TI_DbgPrint(MID_TRACE
, ("Called.\n"));
581 ControlChannel
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ControlChannel
),
582 CONTROL_CHANNEL_TAG
);
584 if (!ControlChannel
) {
585 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
586 return STATUS_INSUFFICIENT_RESOURCES
;
589 RtlZeroMemory(ControlChannel
, sizeof(CONTROL_CHANNEL
));
591 /* Make sure address is a local unicast address or 0 */
593 /* Locate address entry. If specified address is 0, a random address is chosen */
595 /* Initialize receive and transmit queues */
596 InitializeListHead(&ControlChannel
->ListEntry
);
598 /* Initialize spin lock that protects the address file object */
599 KeInitializeSpinLock(&ControlChannel
->Lock
);
601 ControlChannel
->RefCount
= 1;
602 ControlChannel
->Free
= ControlChannelFree
;
604 /* Return address file object */
605 Request
->Handle
.ControlChannel
= ControlChannel
;
607 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
609 return STATUS_SUCCESS
;
613 * FUNCTION: Closes a control channel file object
615 * Request = Pointer to TDI request structure for this request
617 * Status of operation
619 NTSTATUS
FileCloseControlChannel(
620 PTDI_REQUEST Request
)
622 if (!Request
->Handle
.ControlChannel
) return STATUS_INVALID_PARAMETER
;
624 DereferenceObject((PCONTROL_CHANNEL
)Request
->Handle
.ControlChannel
);
626 Request
->Handle
.ControlChannel
= NULL
;
628 return STATUS_SUCCESS
;