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 /* FIXME: including pstypes.h without ntifs fails */
15 #include <ndk/pstypes.h>
17 /* Uncomment for logging of connections and address files every 10 seconds */
20 /* List of all address file objects managed by this driver */
21 LIST_ENTRY AddressFileListHead
;
22 KSPIN_LOCK AddressFileListLock
;
24 /* List of all connection endpoint file objects managed by this driver */
25 LIST_ENTRY ConnectionEndpointListHead
;
26 KSPIN_LOCK ConnectionEndpointListLock
;
29 * FUNCTION: Searches through address file entries to find the first match
31 * Address = IP address
33 * Protocol = Protocol number
34 * SearchContext = Pointer to search context
36 * Pointer to address file, NULL if none was found
38 PADDRESS_FILE
AddrSearchFirst(
42 PAF_SEARCH SearchContext
)
46 SearchContext
->Address
= Address
;
47 SearchContext
->Port
= Port
;
48 SearchContext
->Protocol
= Protocol
;
50 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
52 SearchContext
->Next
= AddressFileListHead
.Flink
;
54 if (!IsListEmpty(&AddressFileListHead
))
55 ReferenceObject(CONTAINING_RECORD(SearchContext
->Next
, ADDRESS_FILE
, ListEntry
));
57 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
59 return AddrSearchNext(SearchContext
);
62 BOOLEAN
AddrIsBroadcastMatch(
63 PIP_ADDRESS UnicastAddress
,
64 PIP_ADDRESS BroadcastAddress
) {
67 ForEachInterface(IF
) {
68 if ((AddrIsUnspecified(UnicastAddress
) ||
69 AddrIsEqual(&IF
->Unicast
, UnicastAddress
)) &&
70 (AddrIsEqual(&IF
->Broadcast
, BroadcastAddress
)))
77 BOOLEAN
AddrReceiveMatch(
78 PIP_ADDRESS LocalAddress
,
79 PIP_ADDRESS RemoteAddress
)
81 if (AddrIsEqual(LocalAddress
, RemoteAddress
))
83 /* Unicast address match */
87 if (AddrIsBroadcastMatch(LocalAddress
, RemoteAddress
))
89 /* Broadcast address match */
93 if (AddrIsUnspecified(LocalAddress
))
95 /* Local address unspecified */
99 if (AddrIsUnspecified(RemoteAddress
))
101 /* Remote address unspecified */
109 LogActiveObjects(VOID
)
112 PLIST_ENTRY CurrentEntry
;
114 PADDRESS_FILE AddrFile
;
115 PCONNECTION_ENDPOINT Conn
;
117 DbgPrint("----------- TCP/IP Active Object Dump -------------\n");
119 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
121 CurrentEntry
= AddressFileListHead
.Flink
;
122 while (CurrentEntry
!= &AddressFileListHead
)
124 AddrFile
= CONTAINING_RECORD(CurrentEntry
, ADDRESS_FILE
, ListEntry
);
126 DbgPrint("Address File (%s, %d, %d) @ 0x%p | Ref count: %d | Sharers: %d\n",
127 A2S(&AddrFile
->Address
), WN2H(AddrFile
->Port
), AddrFile
->Protocol
,
128 AddrFile
, AddrFile
->RefCount
, AddrFile
->Sharers
);
129 DbgPrint("\tListener: ");
130 if (AddrFile
->Listener
== NULL
)
131 DbgPrint("<None>\n");
133 DbgPrint("0x%p\n", AddrFile
->Listener
);
134 DbgPrint("\tAssociated endpoints: ");
135 if (AddrFile
->Connection
== NULL
)
136 DbgPrint("<None>\n");
139 Conn
= AddrFile
->Connection
;
142 DbgPrint("0x%p ", Conn
);
148 CurrentEntry
= CurrentEntry
->Flink
;
151 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
153 TcpipAcquireSpinLock(&ConnectionEndpointListLock
, &OldIrql
);
155 CurrentEntry
= ConnectionEndpointListHead
.Flink
;
156 while (CurrentEntry
!= &ConnectionEndpointListHead
)
158 Conn
= CONTAINING_RECORD(CurrentEntry
, CONNECTION_ENDPOINT
, ListEntry
);
160 DbgPrint("Connection @ 0x%p | Ref count: %d\n", Conn
, Conn
->RefCount
);
162 if (Conn
->SocketContext
== NULL
)
163 DbgPrint("<None>\n");
166 DbgPrint("0x%p\n", Conn
->SocketContext
);
167 LibTCPDumpPcb(Conn
->SocketContext
);
169 DbgPrint("\tPacket queue status: %s\n", IsListEmpty(&Conn
->PacketQueue
) ? "Empty" : "Not Empty");
170 DbgPrint("\tRequest lists: Connect: %s | Recv: %s | Send: %s | Shutdown: %s | Listen: %s\n",
171 IsListEmpty(&Conn
->ConnectRequest
) ? "Empty" : "Not Empty",
172 IsListEmpty(&Conn
->ReceiveRequest
) ? "Empty" : "Not Empty",
173 IsListEmpty(&Conn
->SendRequest
) ? "Empty" : "Not Empty",
174 IsListEmpty(&Conn
->ShutdownRequest
) ? "Empty" : "Not Empty",
175 IsListEmpty(&Conn
->ListenRequest
) ? "Empty" : "Not Empty");
176 DbgPrint("\tSend shutdown: %s\n", Conn
->SendShutdown
? "Yes" : "No");
177 DbgPrint("\tReceive shutdown: %s\n", Conn
->ReceiveShutdown
? "Yes" : "No");
178 if (Conn
->ReceiveShutdown
) DbgPrint("\tReceive shutdown status: 0x%x\n", Conn
->ReceiveShutdownStatus
);
179 DbgPrint("\tClosing: %s\n", Conn
->Closing
? "Yes" : "No");
181 CurrentEntry
= CurrentEntry
->Flink
;
184 TcpipReleaseSpinLock(&ConnectionEndpointListLock
, OldIrql
);
186 DbgPrint("---------------------------------------------------\n");
190 PADDRESS_FILE
AddrFindShared(
191 PIP_ADDRESS BindAddress
,
195 PLIST_ENTRY CurrentEntry
;
197 PADDRESS_FILE Current
= NULL
;
199 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
201 CurrentEntry
= AddressFileListHead
.Flink
;
202 while (CurrentEntry
!= &AddressFileListHead
) {
203 Current
= CONTAINING_RECORD(CurrentEntry
, ADDRESS_FILE
, ListEntry
);
205 /* See if this address matches the search criteria */
206 if ((Current
->Port
== Port
) &&
207 (Current
->Protocol
== Protocol
))
209 /* Increase the sharer count */
210 ASSERT(Current
->Sharers
!= 0);
211 InterlockedIncrement(&Current
->Sharers
);
215 CurrentEntry
= CurrentEntry
->Flink
;
219 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
225 * FUNCTION: Searches through address file entries to find next match
227 * SearchContext = Pointer to search context
229 * Pointer to referenced address file, NULL if none was found
231 PADDRESS_FILE
AddrSearchNext(
232 PAF_SEARCH SearchContext
)
234 PLIST_ENTRY CurrentEntry
;
235 PIP_ADDRESS IPAddress
;
237 PADDRESS_FILE Current
= NULL
;
238 BOOLEAN Found
= FALSE
;
239 PADDRESS_FILE StartingAddrFile
;
241 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
243 if (SearchContext
->Next
== &AddressFileListHead
)
245 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
249 /* Save this pointer so we can dereference it later */
250 StartingAddrFile
= CONTAINING_RECORD(SearchContext
->Next
, ADDRESS_FILE
, ListEntry
);
252 CurrentEntry
= SearchContext
->Next
;
254 while (CurrentEntry
!= &AddressFileListHead
) {
255 Current
= CONTAINING_RECORD(CurrentEntry
, ADDRESS_FILE
, ListEntry
);
257 IPAddress
= &Current
->Address
;
259 TI_DbgPrint(DEBUG_ADDRFILE
, ("Comparing: ((%d, %d, %s), (%d, %d, %s)).\n",
263 WN2H(SearchContext
->Port
),
264 SearchContext
->Protocol
,
265 A2S(SearchContext
->Address
)));
267 /* See if this address matches the search criteria */
268 if ((Current
->Port
== SearchContext
->Port
) &&
269 (Current
->Protocol
== SearchContext
->Protocol
) &&
270 (AddrReceiveMatch(IPAddress
, SearchContext
->Address
))) {
271 /* We've found a match */
275 CurrentEntry
= CurrentEntry
->Flink
;
280 SearchContext
->Next
= CurrentEntry
->Flink
;
282 if (SearchContext
->Next
!= &AddressFileListHead
)
284 /* Reference the next address file to prevent the link from disappearing behind our back */
285 ReferenceObject(CONTAINING_RECORD(SearchContext
->Next
, ADDRESS_FILE
, ListEntry
));
288 /* Reference the returned address file before dereferencing the starting
289 * address file because it may be that Current == StartingAddrFile */
290 ReferenceObject(Current
);
295 DereferenceObject(StartingAddrFile
);
297 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
305 * FUNCTION: Frees an address file object
307 * Object = Pointer to address file object to free
310 PADDRESS_FILE AddrFile
= Object
;
312 PDATAGRAM_RECEIVE_REQUEST ReceiveRequest
;
313 PDATAGRAM_SEND_REQUEST SendRequest
;
314 PLIST_ENTRY CurrentEntry
;
316 TI_DbgPrint(MID_TRACE
, ("Called.\n"));
318 /* We should not be associated with a connection here */
319 ASSERT(!AddrFile
->Connection
);
321 /* Remove address file from the global list */
322 TcpipAcquireSpinLock(&AddressFileListLock
, &OldIrql
);
323 RemoveEntryList(&AddrFile
->ListEntry
);
324 TcpipReleaseSpinLock(&AddressFileListLock
, OldIrql
);
326 /* FIXME: Kill TCP connections on this address file object */
328 /* Return pending requests with error */
330 TI_DbgPrint(DEBUG_ADDRFILE
, ("Aborting receive requests on AddrFile at (0x%X).\n", AddrFile
));
332 /* Go through pending receive request list and cancel them all */
333 while ((CurrentEntry
= ExInterlockedRemoveHeadList(&AddrFile
->ReceiveQueue
, &AddrFile
->Lock
))) {
334 ReceiveRequest
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_RECEIVE_REQUEST
, ListEntry
);
335 (*ReceiveRequest
->Complete
)(ReceiveRequest
->Context
, STATUS_CANCELLED
, 0);
336 /* ExFreePoolWithTag(ReceiveRequest, DATAGRAM_RECV_TAG); FIXME: WTF? */
339 TI_DbgPrint(DEBUG_ADDRFILE
, ("Aborting send requests on address file at (0x%X).\n", AddrFile
));
341 /* Go through pending send request list and cancel them all */
342 while ((CurrentEntry
= ExInterlockedRemoveHeadList(&AddrFile
->ReceiveQueue
, &AddrFile
->Lock
))) {
343 SendRequest
= CONTAINING_RECORD(CurrentEntry
, DATAGRAM_SEND_REQUEST
, ListEntry
);
344 (*SendRequest
->Complete
)(SendRequest
->Context
, STATUS_CANCELLED
, 0);
345 ExFreePoolWithTag(SendRequest
, DATAGRAM_SEND_TAG
);
348 /* Protocol specific handling */
349 switch (AddrFile
->Protocol
) {
353 TCPFreePort(AddrFile
->Port
);
358 UDPFreePort( AddrFile
->Port
);
362 RemoveEntityByContext(AddrFile
);
364 ExFreePoolWithTag(Object
, ADDR_FILE_TAG
);
368 VOID
ControlChannelFree(
371 * FUNCTION: Frees an address file object
373 * Object = Pointer to address file object to free
376 ExFreePoolWithTag(Object
, CONTROL_CHANNEL_TAG
);
381 * FUNCTION: Open an address file object
383 * Request = Pointer to TDI request structure for this request
384 * Address = Pointer to address to be opened
385 * Protocol = Protocol on which to open the address
386 * Shared = Specifies if the address is opened for shared access
387 * Options = Pointer to option buffer
389 * Status of operation
391 NTSTATUS
FileOpenAddress(
392 PTDI_REQUEST Request
,
393 PTA_IP_ADDRESS Address
,
398 PADDRESS_FILE AddrFile
;
400 TI_DbgPrint(MID_TRACE
, ("Called (Proto %d).\n", Protocol
));
402 /* If it's shared and has a port specified, look for a match */
403 if ((Shared
!= FALSE
) && (Address
->Address
[0].Address
[0].sin_port
!= 0))
405 AddrFile
= AddrFindShared(NULL
, Address
->Address
[0].Address
[0].sin_port
, Protocol
);
406 if (AddrFile
!= NULL
)
408 Request
->Handle
.AddressHandle
= AddrFile
;
409 return STATUS_SUCCESS
;
413 AddrFile
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(ADDRESS_FILE
),
416 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
417 return STATUS_INSUFFICIENT_RESOURCES
;
420 RtlZeroMemory(AddrFile
, sizeof(ADDRESS_FILE
));
422 AddrFile
->RefCount
= 1;
423 AddrFile
->Free
= AddrFileFree
;
424 AddrFile
->Sharers
= 1;
426 /* Set our default options */
430 AddrFile
->HeaderIncl
= 1;
431 AddrFile
->ProcessId
= PsGetCurrentProcessId();
436 Teb
= PsGetCurrentThreadTeb();
438 AddrFile
->SubProcessTag
= Teb
->SubProcessTag
;
439 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
440 AddrFile
->SubProcessTag
= 0;
443 KeQuerySystemTime(&AddrFile
->CreationTime
);
445 /* Make sure address is a local unicast address or 0 */
446 /* FIXME: IPv4 only */
447 AddrFile
->Family
= Address
->Address
[0].AddressType
;
448 AddrFile
->Address
.Address
.IPv4Address
= Address
->Address
[0].Address
[0].in_addr
;
449 AddrFile
->Address
.Type
= IP_ADDRESS_V4
;
451 if (!AddrIsUnspecified(&AddrFile
->Address
) &&
452 !AddrLocateInterface(&AddrFile
->Address
)) {
453 TI_DbgPrint(MIN_TRACE
, ("Non-local address given (0x%X).\n", A2S(&AddrFile
->Address
)));
454 ExFreePoolWithTag(AddrFile
, ADDR_FILE_TAG
);
455 return STATUS_INVALID_ADDRESS
;
458 TI_DbgPrint(MID_TRACE
, ("Opening address %s for communication (P=%d U=%d).\n",
459 A2S(&AddrFile
->Address
), Protocol
, IPPROTO_UDP
));
461 /* Protocol specific handling */
464 if (Address
->Address
[0].Address
[0].sin_port
)
466 /* The client specified an explicit port so we force a bind to this */
467 AddrFile
->Port
= TCPAllocatePort(Address
->Address
[0].Address
[0].sin_port
);
469 /* Check for bind success */
470 if (AddrFile
->Port
== 0xffff)
472 ExFreePoolWithTag(AddrFile
, ADDR_FILE_TAG
);
473 return STATUS_ADDRESS_ALREADY_EXISTS
;
477 ASSERT(Address
->Address
[0].Address
[0].sin_port
== AddrFile
->Port
);
479 else if (!AddrIsUnspecified(&AddrFile
->Address
))
481 /* The client is trying to bind to a local address so allocate a port now too */
482 AddrFile
->Port
= TCPAllocatePort(0);
484 /* Check for bind success */
485 if (AddrFile
->Port
== 0xffff)
487 ExFreePoolWithTag(AddrFile
, ADDR_FILE_TAG
);
488 return STATUS_ADDRESS_ALREADY_EXISTS
;
493 /* The client wants an unspecified port with an unspecified address so we wait to see what the TCP library gives us */
497 AddEntity(CO_TL_ENTITY
, AddrFile
, CO_TL_TCP
);
499 AddrFile
->Send
= NULL
; /* TCPSendData */
503 TI_DbgPrint(MID_TRACE
,("Allocating udp port\n"));
505 UDPAllocatePort(Address
->Address
[0].Address
[0].sin_port
);
507 if ((Address
->Address
[0].Address
[0].sin_port
&&
508 AddrFile
->Port
!= Address
->Address
[0].Address
[0].sin_port
) ||
509 AddrFile
->Port
== 0xffff)
511 ExFreePoolWithTag(AddrFile
, ADDR_FILE_TAG
);
512 return STATUS_ADDRESS_ALREADY_EXISTS
;
515 TI_DbgPrint(MID_TRACE
,("Setting port %d (wanted %d)\n",
517 Address
->Address
[0].Address
[0].sin_port
));
519 AddEntity(CL_TL_ENTITY
, AddrFile
, CL_TL_UDP
);
521 AddrFile
->Send
= UDPSendDatagram
;
526 AddrFile
->Send
= ICMPSendDatagram
;
528 /* FIXME: Verify this */
529 AddEntity(ER_ENTITY
, AddrFile
, ER_ICMP
);
533 /* Use raw IP for all other protocols */
535 AddrFile
->Send
= RawIPSendDatagram
;
537 /* FIXME: Verify this */
538 AddEntity(CL_TL_ENTITY
, AddrFile
, 0);
542 TI_DbgPrint(MID_TRACE
, ("IP protocol number for address file object is %d.\n",
545 TI_DbgPrint(MID_TRACE
, ("Port number for address file object is %d.\n",
546 WN2H(AddrFile
->Port
)));
549 AddrFile
->Protocol
= Protocol
;
551 /* Initialize receive and transmit queues */
552 InitializeListHead(&AddrFile
->ReceiveQueue
);
553 InitializeListHead(&AddrFile
->TransmitQueue
);
555 /* Initialize spin lock that protects the address file object */
556 KeInitializeSpinLock(&AddrFile
->Lock
);
558 /* Return address file object */
559 Request
->Handle
.AddressHandle
= AddrFile
;
561 /* Add address file to global list */
562 ExInterlockedInsertTailList(
563 &AddressFileListHead
,
564 &AddrFile
->ListEntry
,
565 &AddressFileListLock
);
567 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
569 return STATUS_SUCCESS
;
574 * FUNCTION: Closes an address file object
576 * Request = Pointer to TDI request structure for this request
578 * Status of operation
580 NTSTATUS
FileCloseAddress(
581 PTDI_REQUEST Request
)
583 PADDRESS_FILE AddrFile
= Request
->Handle
.AddressHandle
;
586 if (!Request
->Handle
.AddressHandle
) return STATUS_INVALID_PARAMETER
;
588 LockObject(AddrFile
, &OldIrql
);
590 if (InterlockedDecrement(&AddrFile
->Sharers
) != 0)
592 /* Still other guys have open handles to this, so keep it around */
593 UnlockObject(AddrFile
, OldIrql
);
594 return STATUS_SUCCESS
;
597 /* We have to close this listener because we started it */
598 if( AddrFile
->Listener
)
600 TCPClose( AddrFile
->Listener
);
603 UnlockObject(AddrFile
, OldIrql
);
605 DereferenceObject(AddrFile
);
607 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
609 return STATUS_SUCCESS
;
614 * FUNCTION: Opens a connection file object
616 * Request = Pointer to TDI request structure for this request
617 * ClientContext = Pointer to client context information
619 * Status of operation
621 NTSTATUS
FileOpenConnection(
622 PTDI_REQUEST Request
,
626 PCONNECTION_ENDPOINT Connection
;
628 TI_DbgPrint(MID_TRACE
, ("Called.\n"));
630 Connection
= TCPAllocateConnectionEndpoint( ClientContext
);
632 if( !Connection
) return STATUS_NO_MEMORY
;
634 Status
= TCPSocket( Connection
, AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
636 if( !NT_SUCCESS(Status
) ) {
637 DereferenceObject( Connection
);
641 /* Return connection endpoint file object */
642 Request
->Handle
.ConnectionContext
= Connection
;
644 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
646 return STATUS_SUCCESS
;
650 * FUNCTION: Closes an connection file object
652 * Request = Pointer to TDI request structure for this request
654 * Status of operation
656 NTSTATUS
FileCloseConnection(
657 PTDI_REQUEST Request
)
659 PCONNECTION_ENDPOINT Connection
;
661 TI_DbgPrint(MID_TRACE
, ("Called.\n"));
663 Connection
= Request
->Handle
.ConnectionContext
;
665 if (!Connection
) return STATUS_INVALID_PARAMETER
;
667 TCPClose( Connection
);
669 Request
->Handle
.ConnectionContext
= NULL
;
671 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
673 return STATUS_SUCCESS
;
677 * FUNCTION: Opens a control channel file object
679 * Request = Pointer to TDI request structure for this request
681 * Status of operation
683 NTSTATUS
FileOpenControlChannel(
684 PTDI_REQUEST Request
)
686 PCONTROL_CHANNEL ControlChannel
;
687 TI_DbgPrint(MID_TRACE
, ("Called.\n"));
689 ControlChannel
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*ControlChannel
),
690 CONTROL_CHANNEL_TAG
);
692 if (!ControlChannel
) {
693 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
694 return STATUS_INSUFFICIENT_RESOURCES
;
697 RtlZeroMemory(ControlChannel
, sizeof(CONTROL_CHANNEL
));
699 /* Make sure address is a local unicast address or 0 */
701 /* Locate address entry. If specified address is 0, a random address is chosen */
703 /* Initialize receive and transmit queues */
704 InitializeListHead(&ControlChannel
->ListEntry
);
706 /* Initialize spin lock that protects the address file object */
707 KeInitializeSpinLock(&ControlChannel
->Lock
);
709 ControlChannel
->RefCount
= 1;
710 ControlChannel
->Free
= ControlChannelFree
;
712 /* Return address file object */
713 Request
->Handle
.ControlChannel
= ControlChannel
;
715 TI_DbgPrint(MAX_TRACE
, ("Leaving.\n"));
717 return STATUS_SUCCESS
;
721 * FUNCTION: Closes a control channel file object
723 * Request = Pointer to TDI request structure for this request
725 * Status of operation
727 NTSTATUS
FileCloseControlChannel(
728 PTDI_REQUEST Request
)
730 if (!Request
->Handle
.ControlChannel
) return STATUS_INVALID_PARAMETER
;
732 DereferenceObject((PCONTROL_CHANNEL
)Request
->Handle
.ControlChannel
);
734 Request
->Handle
.ControlChannel
= NULL
;
736 return STATUS_SUCCESS
;