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
13 /* Uncomment for logging of connections and address files every 10 seconds */
16 /* List of all address file objects managed by this driver */
17 LIST_ENTRY AddressFileListHead
;
18 KSPIN_LOCK AddressFileListLock
;
20 /* List of all connection endpoint file objects managed by this driver */
21 LIST_ENTRY ConnectionEndpointListHead
;
22 KSPIN_LOCK ConnectionEndpointListLock
;
25 * FUNCTION: Searches through address file entries to find the first match
27 * Address = IP address
29 * Protocol = Protocol number
30 * SearchContext = Pointer to search context
32 * Pointer to address file, NULL if none was found
34 PADDRESS_FILE
AddrSearchFirst(
38 PAF_SEARCH SearchContext
)
42 SearchContext
->Address
= Address
;
43 SearchContext
->Port
= Port
;
44 SearchContext
->Protocol
= Protocol
;
46 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
48 SearchContext
->Next
= AddressFileListHead
.Flink
;
50 if (!IsListEmpty(&AddressFileListHead
))
51 ReferenceObject(CONTAINING_RECORD(SearchContext
->Next
, ADDRESS_FILE
, ListEntry
));
53 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
55 return AddrSearchNext(SearchContext
);
58 BOOLEAN
AddrIsBroadcastMatch(
59 PIP_ADDRESS UnicastAddress
,
60 PIP_ADDRESS BroadcastAddress
) {
63 ForEachInterface(IF
) {
64 if ((AddrIsUnspecified(UnicastAddress
) ||
65 AddrIsEqual(&IF
->Unicast
, UnicastAddress
)) &&
66 (AddrIsEqual(&IF
->Broadcast
, BroadcastAddress
)))
73 BOOLEAN
AddrReceiveMatch(
74 PIP_ADDRESS LocalAddress
,
75 PIP_ADDRESS RemoteAddress
)
77 if (AddrIsEqual(LocalAddress
, RemoteAddress
))
79 /* Unicast address match */
83 if (AddrIsBroadcastMatch(LocalAddress
, RemoteAddress
))
85 /* Broadcast address match */
89 if (AddrIsUnspecified(LocalAddress
))
91 /* Local address unspecified */
95 if (AddrIsUnspecified(RemoteAddress
))
97 /* Remote address unspecified */
105 LogActiveObjects(VOID
)
108 PLIST_ENTRY CurrentEntry
;
110 PADDRESS_FILE AddrFile
;
111 PCONNECTION_ENDPOINT Conn
;
113 DbgPrint("----------- TCP/IP Active Object Dump -------------\n");
115 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
117 CurrentEntry
= AddressFileListHead
.Flink
;
118 while (CurrentEntry
!= &AddressFileListHead
)
120 AddrFile
= CONTAINING_RECORD(CurrentEntry
, ADDRESS_FILE
, ListEntry
);
122 DbgPrint("Address File (%s, %d, %d) @ 0x%p | Ref count: %d | Sharers: %d\n",
123 A2S(&AddrFile
->Address
), WN2H(AddrFile
->Port
), AddrFile
->Protocol
,
124 AddrFile
, AddrFile
->RefCount
, AddrFile
->Sharers
);
125 DbgPrint("\tListener: ");
126 if (AddrFile
->Listener
== NULL
)
127 DbgPrint("<None>\n");
129 DbgPrint("0x%p\n", AddrFile
->Listener
);
130 DbgPrint("\tAssociated endpoints: ");
131 if (AddrFile
->Connection
== NULL
)
132 DbgPrint("<None>\n");
135 Conn
= AddrFile
->Connection
;
138 DbgPrint("0x%p ", Conn
);
144 CurrentEntry
= CurrentEntry
->Flink
;
147 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
149 TcpipAcquireSpinLock(&ConnectionEndpointListLock
, &OldIrql
);
151 CurrentEntry
= ConnectionEndpointListHead
.Flink
;
152 while (CurrentEntry
!= &ConnectionEndpointListHead
)
154 Conn
= CONTAINING_RECORD(CurrentEntry
, CONNECTION_ENDPOINT
, ListEntry
);
156 DbgPrint("Connection @ 0x%p | Ref count: %d\n", Conn
, Conn
->RefCount
);
158 if (Conn
->SocketContext
== NULL
)
159 DbgPrint("<None>\n");
162 DbgPrint("0x%p\n", Conn
->SocketContext
);
163 LibTCPDumpPcb(Conn
->SocketContext
);
165 DbgPrint("\tPacket queue status: %s\n", IsListEmpty(&Conn
->PacketQueue
) ? "Empty" : "Not Empty");
166 DbgPrint("\tRequest lists: Connect: %s | Recv: %s | Send: %s | Shutdown: %s | Listen: %s\n",
167 IsListEmpty(&Conn
->ConnectRequest
) ? "Empty" : "Not Empty",
168 IsListEmpty(&Conn
->ReceiveRequest
) ? "Empty" : "Not Empty",
169 IsListEmpty(&Conn
->SendRequest
) ? "Empty" : "Not Empty",
170 IsListEmpty(&Conn
->ShutdownRequest
) ? "Empty" : "Not Empty",
171 IsListEmpty(&Conn
->ListenRequest
) ? "Empty" : "Not Empty");
172 DbgPrint("\tSend shutdown: %s\n", Conn
->SendShutdown
? "Yes" : "No");
173 DbgPrint("\tReceive shutdown: %s\n", Conn
->ReceiveShutdown
? "Yes" : "No");
174 if (Conn
->ReceiveShutdown
) DbgPrint("\tReceive shutdown status: 0x%x\n", Conn
->ReceiveShutdownStatus
);
175 DbgPrint("\tClosing: %s\n", Conn
->Closing
? "Yes" : "No");
177 CurrentEntry
= CurrentEntry
->Flink
;
180 TcpipReleaseSpinLock(&ConnectionEndpointListLock
, OldIrql
);
182 DbgPrint("---------------------------------------------------\n");
186 PADDRESS_FILE
AddrFindShared(
187 PIP_ADDRESS BindAddress
,
191 PLIST_ENTRY CurrentEntry
;
193 PADDRESS_FILE Current
= NULL
;
195 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
197 CurrentEntry
= AddressFileListHead
.Flink
;
198 while (CurrentEntry
!= &AddressFileListHead
) {
199 Current
= CONTAINING_RECORD(CurrentEntry
, ADDRESS_FILE
, ListEntry
);
201 /* See if this address matches the search criteria */
202 if ((Current
->Port
== Port
) &&
203 (Current
->Protocol
== Protocol
))
205 /* Increase the sharer count */
206 ASSERT(Current
->Sharers
!= 0);
207 InterlockedIncrement(&Current
->Sharers
);
211 CurrentEntry
= CurrentEntry
->Flink
;
215 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
221 * FUNCTION: Searches through address file entries to find next match
223 * SearchContext = Pointer to search context
225 * Pointer to referenced address file, NULL if none was found
227 PADDRESS_FILE
AddrSearchNext(
228 PAF_SEARCH SearchContext
)
230 PLIST_ENTRY CurrentEntry
;
231 PIP_ADDRESS IPAddress
;
233 PADDRESS_FILE Current
= NULL
;
234 BOOLEAN Found
= FALSE
;
235 PADDRESS_FILE StartingAddrFile
;
237 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
239 if (SearchContext
->Next
== &AddressFileListHead
)
241 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
245 /* Save this pointer so we can dereference it later */
246 StartingAddrFile
= CONTAINING_RECORD(SearchContext
->Next
, ADDRESS_FILE
, ListEntry
);
248 CurrentEntry
= SearchContext
->Next
;
250 while (CurrentEntry
!= &AddressFileListHead
) {
251 Current
= CONTAINING_RECORD(CurrentEntry
, ADDRESS_FILE
, ListEntry
);
253 IPAddress
= &Current
->Address
;
255 TI_DbgPrint(DEBUG_ADDRFILE
, ("Comparing: ((%d, %d, %s), (%d, %d, %s)).\n",
259 WN2H(SearchContext
->Port
),
260 SearchContext
->Protocol
,
261 A2S(SearchContext
->Address
)));
263 /* See if this address matches the search criteria */
264 if ((Current
->Port
== SearchContext
->Port
) &&
265 (Current
->Protocol
== SearchContext
->Protocol
) &&
266 (AddrReceiveMatch(IPAddress
, SearchContext
->Address
))) {
267 /* We've found a match */
271 CurrentEntry
= CurrentEntry
->Flink
;
276 SearchContext
->Next
= CurrentEntry
->Flink
;
278 if (SearchContext
->Next
!= &AddressFileListHead
)
280 /* Reference the next address file to prevent the link from disappearing behind our back */
281 ReferenceObject(CONTAINING_RECORD(SearchContext
->Next
, ADDRESS_FILE
, ListEntry
));
284 /* Reference the returned address file before dereferencing the starting
285 * address file because it may be that Current == StartingAddrFile */
286 ReferenceObject(Current
);
291 DereferenceObject(StartingAddrFile
);
293 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
301 * FUNCTION: Frees an address file object
303 * Object = Pointer to address file object to free
306 PADDRESS_FILE AddrFile
= Object
;
308 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest
;
309 PDATAGRAM_SEND_REQUEST SendRequest
;
310 PLIST_ENTRY CurrentEntry
;
312 TI_DbgPrint(MID_TRACE
, ("Called.\n"));
314 /* We should not be associated with a connection here */
315 ASSERT(!AddrFile
->Connection
);
317 /* Remove address file from the global list */
318 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
319 RemoveEntryList(&AddrFile
->ListEntry
);
320 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
322 /* FIXME: Kill TCP connections on this address file object */
324 /* Return pending requests with error */
326 TI_DbgPrint(DEBUG_ADDRFILE
, ("Aborting receive requests on AddrFile at (0x%X).\n", AddrFile
));
328 /* Go through pending receive request list and cancel them all */
329 while ((CurrentEntry
= ExInterlockedRemoveHeadList(&AddrFile
->ReceiveQueue
, &AddrFile
->Lock
))) {
330 ReceiveRequest
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_RECEIVE_REQUEST
, ListEntry
);
331 (*ReceiveRequest
->Complete
)(ReceiveRequest
->Context
, STATUS_CANCELLED
, 0);
332 /* ExFreePoolWithTag(ReceiveRequest, DATAGRAM_RECV_TAG); FIXME: WTF? */
335 TI_DbgPrint(DEBUG_ADDRFILE
, ("Aborting send requests on address file at (0x%X).\n", AddrFile
));
337 /* Go through pending send request list and cancel them all */
338 while ((CurrentEntry
= ExInterlockedRemoveHeadList(&AddrFile
->ReceiveQueue
, &AddrFile
->Lock
))) {
339 SendRequest
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_SEND_REQUEST
, ListEntry
);
340 (*SendRequest
->Complete
)(SendRequest
->Context
, STATUS_CANCELLED
, 0);
341 ExFreePoolWithTag(SendRequest
, DATAGRAM_SEND_TAG
);
344 /* Protocol specific handling */
345 switch (AddrFile
->Protocol
) {
349 TCPFreePort(AddrFile
->Port
);
354 UDPFreePort( AddrFile
->Port
);
358 RemoveEntityByContext(AddrFile
);
360 ExFreePoolWithTag(Object
, ADDR_FILE_TAG
);
364 VOID
ControlChannelFree(
367 * FUNCTION: Frees an address file object
369 * Object = Pointer to address file object to free
372 ExFreePoolWithTag(Object
, CONTROL_CHANNEL_TAG
);
377 * FUNCTION: Open an address file object
379 * Request = Pointer to TDI request structure for this request
380 * Address = Pointer to address to be opened
381 * Protocol = Protocol on which to open the address
382 * Shared = Specifies if the address is opened for shared access
383 * Options = Pointer to option buffer
385 * Status of operation
387 NTSTATUS
FileOpenAddress(
388 PTDI_REQUEST Request
,
389 PTA_IP_ADDRESS Address
,
394 PADDRESS_FILE AddrFile
;
396 TI_DbgPrint(MID_TRACE
, ("Called (Proto %d).\n", Protocol
));
398 /* If it's shared and has a port specified, look for a match */
399 if ((Shared
!= FALSE
) && (Address
->Address
[0].Address
[0].sin_port
!= 0))
401 AddrFile
= AddrFindShared(NULL
, Address
->Address
[0].Address
[0].sin_port
, Protocol
);
402 if (AddrFile
!= NULL
)
404 Request
->Handle
.AddressHandle
= AddrFile
;
405 return STATUS_SUCCESS
;
409 AddrFile
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(ADDRESS_FILE
),
412 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
413 return STATUS_INSUFFICIENT_RESOURCES
;
416 RtlZeroMemory(AddrFile
, sizeof(ADDRESS_FILE
));
418 AddrFile
->RefCount
= 1;
419 AddrFile
->Free
= AddrFileFree
;
420 AddrFile
->Sharers
= 1;
422 /* Set our default options */
426 AddrFile
->HeaderIncl
= 1;
427 AddrFile
->ProcessId
= PsGetCurrentProcessId();
428 KeQuerySystemTime(&AddrFile
->CreationTime
);
430 /* Make sure address is a local unicast address or 0 */
431 /* FIXME: IPv4 only */
432 AddrFile
->Family
= Address
->Address
[0].AddressType
;
433 AddrFile
->Address
.Address
.IPv4Address
= Address
->Address
[0].Address
[0].in_addr
;
434 AddrFile
->Address
.Type
= IP_ADDRESS_V4
;
436 if (!AddrIsUnspecified(&AddrFile
->Address
) &&
437 !AddrLocateInterface(&AddrFile
->Address
)) {
438 TI_DbgPrint(MIN_TRACE
, ("Non-local address given (0x%X).\n", A2S(&AddrFile
->Address
)));
439 ExFreePoolWithTag(AddrFile
, ADDR_FILE_TAG
);
440 return STATUS_INVALID_ADDRESS
;
443 TI_DbgPrint(MID_TRACE
, ("Opening address %s for communication (P=%d U=%d).\n",
444 A2S(&AddrFile
->Address
), Protocol
, IPPROTO_UDP
));
446 /* Protocol specific handling */
449 if (Address
->Address
[0].Address
[0].sin_port
)
451 /* The client specified an explicit port so we force a bind to this */
452 AddrFile
->Port
= TCPAllocatePort(Address
->Address
[0].Address
[0].sin_port
);
454 /* Check for bind success */
455 if (AddrFile
->Port
== 0xffff)
457 ExFreePoolWithTag(AddrFile
, ADDR_FILE_TAG
);
458 return STATUS_ADDRESS_ALREADY_EXISTS
;
462 ASSERT(Address
->Address
[0].Address
[0].sin_port
== AddrFile
->Port
);
464 else if (!AddrIsUnspecified(&AddrFile
->Address
))
466 /* The client is trying to bind to a local address so allocate a port now too */
467 AddrFile
->Port
= TCPAllocatePort(0);
469 /* Check for bind success */
470 if (AddrFile
->Port
== 0xffff)
472 ExFreePoolWithTag(AddrFile
, ADDR_FILE_TAG
);
473 return STATUS_ADDRESS_ALREADY_EXISTS
;
478 /* The client wants an unspecified port with an unspecified address so we wait to see what the TCP library gives us */
482 AddEntity(CO_TL_ENTITY
, AddrFile
, CO_TL_TCP
);
484 AddrFile
->Send
= NULL
; /* TCPSendData */
488 TI_DbgPrint(MID_TRACE
,("Allocating udp port\n"));
490 UDPAllocatePort(Address
->Address
[0].Address
[0].sin_port
);
492 if ((Address
->Address
[0].Address
[0].sin_port
&&
493 AddrFile
->Port
!= Address
->Address
[0].Address
[0].sin_port
) ||
494 AddrFile
->Port
== 0xffff)
496 ExFreePoolWithTag(AddrFile
, ADDR_FILE_TAG
);
497 return STATUS_ADDRESS_ALREADY_EXISTS
;
500 TI_DbgPrint(MID_TRACE
,("Setting port %d (wanted %d)\n",
502 Address
->Address
[0].Address
[0].sin_port
));
504 AddEntity(CL_TL_ENTITY
, AddrFile
, CL_TL_UDP
);
506 AddrFile
->Send
= UDPSendDatagram
;
511 AddrFile
->Send
= ICMPSendDatagram
;
513 /* FIXME: Verify this */
514 AddEntity(ER_ENTITY
, AddrFile
, ER_ICMP
);
518 /* Use raw IP for all other protocols */
520 AddrFile
->Send
= RawIPSendDatagram
;
522 /* FIXME: Verify this */
523 AddEntity(CL_TL_ENTITY
, AddrFile
, 0);
527 TI_DbgPrint(MID_TRACE
, ("IP protocol number for address file object is %d.\n",
530 TI_DbgPrint(MID_TRACE
, ("Port number for address file object is %d.\n",
531 WN2H(AddrFile
->Port
)));
534 AddrFile
->Protocol
= Protocol
;
536 /* Initialize receive and transmit queues */
537 InitializeListHead(&AddrFile
->ReceiveQueue
);
538 InitializeListHead(&AddrFile
->TransmitQueue
);
540 /* Initialize spin lock that protects the address file object */
541 KeInitializeSpinLock(&AddrFile
->Lock
);
543 /* Return address file object */
544 Request
->Handle
.AddressHandle
= AddrFile
;
546 /* Add address file to global list */
547 ExInterlockedInsertTailList(
548 &AddressFileListHead
,
549 &AddrFile
->ListEntry
,
550 &AddressFileListLock
);
552 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
554 return STATUS_SUCCESS
;
559 * FUNCTION: Closes an address file object
561 * Request = Pointer to TDI request structure for this request
563 * Status of operation
565 NTSTATUS
FileCloseAddress(
566 PTDI_REQUEST Request
)
568 PADDRESS_FILE AddrFile
= Request
->Handle
.AddressHandle
;
571 if (!Request
->Handle
.AddressHandle
) return STATUS_INVALID_PARAMETER
;
573 LockObject(AddrFile
, &OldIrql
);
575 if (InterlockedDecrement(&AddrFile
->Sharers
) != 0)
577 /* Still other guys have open handles to this, so keep it around */
578 UnlockObject(AddrFile
, OldIrql
);
579 return STATUS_SUCCESS
;
582 /* We have to close this listener because we started it */
583 if( AddrFile
->Listener
)
585 TCPClose( AddrFile
->Listener
);
588 UnlockObject(AddrFile
, OldIrql
);
590 DereferenceObject(AddrFile
);
592 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
594 return STATUS_SUCCESS
;
599 * FUNCTION: Opens a connection file object
601 * Request = Pointer to TDI request structure for this request
602 * ClientContext = Pointer to client context information
604 * Status of operation
606 NTSTATUS
FileOpenConnection(
607 PTDI_REQUEST Request
,
611 PCONNECTION_ENDPOINT Connection
;
613 TI_DbgPrint(MID_TRACE
, ("Called.\n"));
615 Connection
= TCPAllocateConnectionEndpoint( ClientContext
);
617 if( !Connection
) return STATUS_NO_MEMORY
;
619 Status
= TCPSocket( Connection
, AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
621 if( !NT_SUCCESS(Status
) ) {
622 DereferenceObject( Connection
);
626 /* Return connection endpoint file object */
627 Request
->Handle
.ConnectionContext
= Connection
;
629 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
631 return STATUS_SUCCESS
;
635 * FUNCTION: Closes an connection file object
637 * Request = Pointer to TDI request structure for this request
639 * Status of operation
641 NTSTATUS
FileCloseConnection(
642 PTDI_REQUEST Request
)
644 PCONNECTION_ENDPOINT Connection
;
646 TI_DbgPrint(MID_TRACE
, ("Called.\n"));
648 Connection
= Request
->Handle
.ConnectionContext
;
650 if (!Connection
) return STATUS_INVALID_PARAMETER
;
652 TCPClose( Connection
);
654 Request
->Handle
.ConnectionContext
= NULL
;
656 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
658 return STATUS_SUCCESS
;
662 * FUNCTION: Opens a control channel file object
664 * Request = Pointer to TDI request structure for this request
666 * Status of operation
668 NTSTATUS
FileOpenControlChannel(
669 PTDI_REQUEST Request
)
671 PCONTROL_CHANNEL ControlChannel
;
672 TI_DbgPrint(MID_TRACE
, ("Called.\n"));
674 ControlChannel
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ControlChannel
),
675 CONTROL_CHANNEL_TAG
);
677 if (!ControlChannel
) {
678 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
679 return STATUS_INSUFFICIENT_RESOURCES
;
682 RtlZeroMemory(ControlChannel
, sizeof(CONTROL_CHANNEL
));
684 /* Make sure address is a local unicast address or 0 */
686 /* Locate address entry. If specified address is 0, a random address is chosen */
688 /* Initialize receive and transmit queues */
689 InitializeListHead(&ControlChannel
->ListEntry
);
691 /* Initialize spin lock that protects the address file object */
692 KeInitializeSpinLock(&ControlChannel
->Lock
);
694 ControlChannel
->RefCount
= 1;
695 ControlChannel
->Free
= ControlChannelFree
;
697 /* Return address file object */
698 Request
->Handle
.ControlChannel
= ControlChannel
;
700 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
702 return STATUS_SUCCESS
;
706 * FUNCTION: Closes a control channel file object
708 * Request = Pointer to TDI request structure for this request
710 * Status of operation
712 NTSTATUS
FileCloseControlChannel(
713 PTDI_REQUEST Request
)
715 if (!Request
->Handle
.ControlChannel
) return STATUS_INVALID_PARAMETER
;
717 DereferenceObject((PCONTROL_CHANNEL
)Request
->Handle
.ControlChannel
);
719 Request
->Handle
.ControlChannel
= NULL
;
721 return STATUS_SUCCESS
;