2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: transport/tcp/tcp.c
5 * PURPOSE: Transmission Control Protocol
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Art Yerkes (arty@users.sf.net)
9 * CSH 01/08-2000 Created
10 * arty 12/21/2004 Added accept
15 LONG TCP_IPIdentification
= 0;
16 static BOOLEAN TCPInitialized
= FALSE
;
17 static NPAGED_LOOKASIDE_LIST TCPSegmentList
;
19 CLIENT_DATA ClientInfo
;
22 CompleteBucketWorker(PVOID Context
)
24 PTDI_BUCKET Bucket
= Context
;
25 PCONNECTION_ENDPOINT Connection
;
26 PTCP_COMPLETION_ROUTINE Complete
;
30 Connection
= Bucket
->AssociatedEndpoint
;
31 Complete
= Bucket
->Request
.RequestNotifyObject
;
33 Complete(Bucket
->Request
.RequestContext
, Bucket
->Status
, Bucket
->Information
);
35 ExFreePoolWithTag(Bucket
, TDI_BUCKET_TAG
);
37 DereferenceObject(Connection
);
41 CompleteBucket(PCONNECTION_ENDPOINT Connection
, PTDI_BUCKET Bucket
)
43 Bucket
->AssociatedEndpoint
= Connection
;
44 ReferenceObject(Connection
);
45 ChewCreate(CompleteBucketWorker
, Bucket
);
48 VOID
HandleSignalledConnection(PCONNECTION_ENDPOINT Connection
)
56 if (ClientInfo
.Unlocked
)
57 LockObjectAtDpcLevel(Connection
);
59 TI_DbgPrint(MID_TRACE
,("Handling signalled state on %x (%x)\n",
60 Connection
, Connection
->SocketContext
));
62 /* Things that can happen when we try the initial connection */
63 if( Connection
->SignalState
& (SEL_CONNECT
| SEL_FIN
| SEL_ERROR
) ) {
64 while (!IsListEmpty(&Connection
->ConnectRequest
)) {
65 Entry
= RemoveHeadList( &Connection
->ConnectRequest
);
67 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
69 if (Connection
->SignalState
& SEL_ERROR
)
71 Bucket
->Status
= TCPTranslateError(OskitTCPGetSocketError(Connection
->SocketContext
));
73 else if (Connection
->SignalState
& SEL_FIN
)
75 Bucket
->Status
= STATUS_CANCELLED
;
79 Bucket
->Status
= STATUS_SUCCESS
;
81 Bucket
->Information
= 0;
83 CompleteBucket(Connection
, Bucket
);
87 if( Connection
->SignalState
& (SEL_ACCEPT
| SEL_FIN
| SEL_ERROR
) ) {
88 /* Handle readable on a listening socket --
89 * TODO: Implement filtering
91 TI_DbgPrint(DEBUG_TCP
,("Accepting new connection on %x (Queue: %s)\n",
93 IsListEmpty(&Connection
->ListenRequest
) ?
94 "empty" : "nonempty"));
96 while (!IsListEmpty(&Connection
->ListenRequest
)) {
97 PIO_STACK_LOCATION IrpSp
;
99 Entry
= RemoveHeadList( &Connection
->ListenRequest
);
101 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
103 Irp
= Bucket
->Request
.RequestContext
;
104 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
106 TI_DbgPrint(DEBUG_TCP
,("Getting the socket\n"));
108 if (Connection
->SignalState
& SEL_ERROR
)
110 Status
= TCPTranslateError(OskitTCPGetSocketError(Connection
->SocketContext
));
112 else if (Connection
->SignalState
& SEL_FIN
)
114 Status
= STATUS_CANCELLED
;
118 Status
= TCPServiceListeningSocket(Connection
->AddressFile
->Listener
,
119 Bucket
->AssociatedEndpoint
,
120 (PTDI_REQUEST_KERNEL
)&IrpSp
->Parameters
);
123 TI_DbgPrint(DEBUG_TCP
,("Socket: Status: %x\n"));
125 if( Status
== STATUS_PENDING
) {
126 InsertHeadList( &Connection
->ListenRequest
, &Bucket
->Entry
);
129 Bucket
->Status
= Status
;
130 Bucket
->Information
= 0;
131 DereferenceObject(Bucket
->AssociatedEndpoint
);
133 CompleteBucket(Connection
, Bucket
);
138 /* Things that happen after we're connected */
139 if( Connection
->SignalState
& (SEL_READ
| SEL_FIN
| SEL_ERROR
) ) {
140 TI_DbgPrint(DEBUG_TCP
,("Readable: irp list %s\n",
141 IsListEmpty(&Connection
->ReceiveRequest
) ?
142 "empty" : "nonempty"));
144 while (!IsListEmpty(&Connection
->ReceiveRequest
)) {
145 OSK_UINT RecvLen
= 0, Received
= 0;
146 PVOID RecvBuffer
= 0;
148 Entry
= RemoveHeadList( &Connection
->ReceiveRequest
);
150 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
152 Irp
= Bucket
->Request
.RequestContext
;
153 Mdl
= Irp
->MdlAddress
;
155 TI_DbgPrint(DEBUG_TCP
,
156 ("Getting the user buffer from %x\n", Mdl
));
158 NdisQueryBuffer( Mdl
, &RecvBuffer
, &RecvLen
);
160 TI_DbgPrint(DEBUG_TCP
,
161 ("Reading %d bytes to %x\n", RecvLen
, RecvBuffer
));
163 TI_DbgPrint(DEBUG_TCP
, ("Connection: %x\n", Connection
));
166 ("Connection->SocketContext: %x\n",
167 Connection
->SocketContext
));
168 TI_DbgPrint(DEBUG_TCP
, ("RecvBuffer: %x\n", RecvBuffer
));
170 if (Connection
->SignalState
& SEL_ERROR
)
172 Status
= TCPTranslateError(OskitTCPGetSocketError(Connection
->SocketContext
));
174 else if (Connection
->SignalState
& SEL_FIN
)
176 /* We got here because of a SEL_FIN event */
177 Status
= STATUS_CANCELLED
;
181 Status
= TCPTranslateError(OskitTCPRecv(Connection
->SocketContext
,
188 TI_DbgPrint(DEBUG_TCP
,("TCP Bytes: %d\n", Received
));
190 if( Status
== STATUS_PENDING
) {
191 InsertHeadList( &Connection
->ReceiveRequest
, &Bucket
->Entry
);
194 TI_DbgPrint(DEBUG_TCP
,
195 ("Completing Receive request: %x %x\n",
196 Bucket
->Request
, Status
));
198 Bucket
->Status
= Status
;
199 Bucket
->Information
= (Bucket
->Status
== STATUS_SUCCESS
) ? Received
: 0;
201 CompleteBucket(Connection
, Bucket
);
205 if( Connection
->SignalState
& (SEL_WRITE
| SEL_FIN
| SEL_ERROR
) ) {
206 TI_DbgPrint(DEBUG_TCP
,("Writeable: irp list %s\n",
207 IsListEmpty(&Connection
->SendRequest
) ?
208 "empty" : "nonempty"));
210 while (!IsListEmpty(&Connection
->SendRequest
)) {
211 OSK_UINT SendLen
= 0, Sent
= 0;
212 PVOID SendBuffer
= 0;
214 Entry
= RemoveHeadList( &Connection
->SendRequest
);
216 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
218 Irp
= Bucket
->Request
.RequestContext
;
219 Mdl
= Irp
->MdlAddress
;
221 TI_DbgPrint(DEBUG_TCP
,
222 ("Getting the user buffer from %x\n", Mdl
));
224 NdisQueryBuffer( Mdl
, &SendBuffer
, &SendLen
);
226 TI_DbgPrint(DEBUG_TCP
,
227 ("Writing %d bytes to %x\n", SendLen
, SendBuffer
));
229 TI_DbgPrint(DEBUG_TCP
, ("Connection: %x\n", Connection
));
232 ("Connection->SocketContext: %x\n",
233 Connection
->SocketContext
));
235 if (Connection
->SignalState
& SEL_ERROR
)
237 Status
= TCPTranslateError(OskitTCPGetSocketError(Connection
->SocketContext
));
239 else if (Connection
->SignalState
& SEL_FIN
)
241 /* We got here because of a SEL_FIN event */
242 Status
= STATUS_CANCELLED
;
246 Status
= TCPTranslateError(OskitTCPSend(Connection
->SocketContext
,
253 TI_DbgPrint(DEBUG_TCP
,("TCP Bytes: %d\n", Sent
));
255 if( Status
== STATUS_PENDING
) {
256 InsertHeadList( &Connection
->SendRequest
, &Bucket
->Entry
);
259 TI_DbgPrint(DEBUG_TCP
,
260 ("Completing Send request: %x %x\n",
261 Bucket
->Request
, Status
));
263 Bucket
->Status
= Status
;
264 Bucket
->Information
= (Bucket
->Status
== STATUS_SUCCESS
) ? Sent
: 0;
266 CompleteBucket(Connection
, Bucket
);
270 if( Connection
->SignalState
& (SEL_WRITE
| SEL_FIN
| SEL_ERROR
) ) {
271 while (!IsListEmpty(&Connection
->ShutdownRequest
)) {
272 Entry
= RemoveHeadList( &Connection
->ShutdownRequest
);
274 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
276 if (Connection
->SignalState
& SEL_ERROR
)
278 Status
= TCPTranslateError(OskitTCPGetSocketError(Connection
->SocketContext
));
280 else if (Connection
->SignalState
& SEL_FIN
)
282 /* We were cancelled by a FIN */
283 Status
= STATUS_CANCELLED
;
287 /* See if we can satisfy this after the events */
288 if (IsListEmpty(&Connection
->SendRequest
))
290 /* Send queue is empty so we're good to go */
291 Status
= TCPTranslateError(OskitTCPShutdown(Connection
->SocketContext
, FWRITE
));
295 /* We still have to wait */
296 Status
= STATUS_PENDING
;
300 if( Status
== STATUS_PENDING
) {
301 InsertHeadList( &Connection
->ShutdownRequest
, &Bucket
->Entry
);
304 TI_DbgPrint(DEBUG_TCP
,
305 ("Completing shutdown request: %x %x\n",
306 Bucket
->Request
, Status
));
308 if (KeCancelTimer(&Connection
->DisconnectTimer
))
310 DereferenceObject(Connection
);
313 Bucket
->Status
= Status
;
314 Bucket
->Information
= 0;
316 CompleteBucket(Connection
, Bucket
);
321 if (Connection
->SignalState
& SEL_FIN
)
323 Connection
->SocketContext
= NULL
;
324 DereferenceObject(Connection
);
327 if (ClientInfo
.Unlocked
)
328 UnlockObjectFromDpcLevel(Connection
);
332 DisconnectTimeoutDpc(PKDPC Dpc
,
333 PVOID DeferredContext
,
334 PVOID SystemArgument1
,
335 PVOID SystemArgument2
)
337 PCONNECTION_ENDPOINT Connection
= DeferredContext
;
341 LockObjectAtDpcLevel(Connection
);
343 /* We timed out waiting for pending sends so force it to shutdown */
344 OskitTCPShutdown(Connection
->SocketContext
, FWRITE
);
346 while (!IsListEmpty(&Connection
->SendRequest
))
348 Entry
= RemoveHeadList(&Connection
->SendRequest
);
350 Bucket
= CONTAINING_RECORD(Entry
, TDI_BUCKET
, Entry
);
352 Bucket
->Information
= 0;
353 Bucket
->Status
= STATUS_FILE_CLOSED
;
355 CompleteBucket(Connection
, Bucket
);
358 while (!IsListEmpty(&Connection
->ShutdownRequest
)) {
359 Entry
= RemoveHeadList( &Connection
->ShutdownRequest
);
361 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
363 Bucket
->Status
= STATUS_TIMEOUT
;
364 Bucket
->Information
= 0;
366 CompleteBucket(Connection
, Bucket
);
369 UnlockObjectFromDpcLevel(Connection
);
371 DereferenceObject(Connection
);
374 VOID
ConnectionFree(PVOID Object
) {
375 PCONNECTION_ENDPOINT Connection
= Object
;
378 TI_DbgPrint(DEBUG_TCP
, ("Freeing TCP Endpoint\n"));
380 TcpipAcquireSpinLock(&ConnectionEndpointListLock
, &OldIrql
);
381 RemoveEntryList(&Connection
->ListEntry
);
382 TcpipReleaseSpinLock(&ConnectionEndpointListLock
, OldIrql
);
384 ExFreePoolWithTag( Connection
, CONN_ENDPT_TAG
);
387 PCONNECTION_ENDPOINT
TCPAllocateConnectionEndpoint( PVOID ClientContext
) {
388 PCONNECTION_ENDPOINT Connection
=
389 ExAllocatePoolWithTag(NonPagedPool
, sizeof(CONNECTION_ENDPOINT
),
394 TI_DbgPrint(DEBUG_CPOINT
, ("Connection point file object allocated at (0x%X).\n", Connection
));
396 RtlZeroMemory(Connection
, sizeof(CONNECTION_ENDPOINT
));
398 /* Initialize spin lock that protects the connection endpoint file object */
399 KeInitializeSpinLock(&Connection
->Lock
);
400 InitializeListHead(&Connection
->ConnectRequest
);
401 InitializeListHead(&Connection
->ListenRequest
);
402 InitializeListHead(&Connection
->ReceiveRequest
);
403 InitializeListHead(&Connection
->SendRequest
);
404 InitializeListHead(&Connection
->ShutdownRequest
);
406 KeInitializeTimer(&Connection
->DisconnectTimer
);
407 KeInitializeDpc(&Connection
->DisconnectDpc
, DisconnectTimeoutDpc
, Connection
);
409 /* Save client context pointer */
410 Connection
->ClientContext
= ClientContext
;
412 Connection
->RefCount
= 2;
413 Connection
->Free
= ConnectionFree
;
415 /* Add connection endpoint to global list */
416 ExInterlockedInsertTailList(&ConnectionEndpointListHead
,
417 &Connection
->ListEntry
,
418 &ConnectionEndpointListLock
);
423 NTSTATUS
TCPSocket( PCONNECTION_ENDPOINT Connection
,
424 UINT Family
, UINT Type
, UINT Proto
) {
428 LockObject(Connection
, &OldIrql
);
430 TI_DbgPrint(DEBUG_TCP
,("Called: Connection %x, Family %d, Type %d, "
432 Connection
, Family
, Type
, Proto
));
434 Status
= TCPTranslateError( OskitTCPSocket( Connection
,
435 &Connection
->SocketContext
,
440 ASSERT_KM_POINTER(Connection
->SocketContext
);
442 TI_DbgPrint(DEBUG_TCP
,("Connection->SocketContext %x\n",
443 Connection
->SocketContext
));
445 UnlockObject(Connection
, OldIrql
);
450 VOID
TCPReceive(PIP_INTERFACE Interface
, PIP_PACKET IPPacket
)
452 * FUNCTION: Receives and queues TCP data
454 * IPPacket = Pointer to an IP packet that was received
456 * This is the low level interface for receiving TCP data
461 TI_DbgPrint(DEBUG_TCP
,("Sending packet %d (%d) to oskit\n",
463 IPPacket
->HeaderSize
));
465 KeAcquireSpinLock(&ClientInfo
.Lock
, &OldIrql
);
466 ClientInfo
.Unlocked
= TRUE
;
467 ClientInfo
.OldIrql
= OldIrql
;
469 OskitTCPReceiveDatagram( IPPacket
->Header
,
471 IPPacket
->HeaderSize
);
473 ClientInfo
.Unlocked
= FALSE
;
474 KeReleaseSpinLock(&ClientInfo
.Lock
, OldIrql
);
478 int TCPSocketState( void *ClientData
,
480 void *WhichConnection
,
483 int TCPPacketSend( void *ClientData
,
487 POSK_IFADDR
TCPFindInterface( void *ClientData
,
490 OSK_SOCKADDR
*ReqAddr
);
492 NTSTATUS
TCPMemStartup( void );
493 void *TCPMalloc( void *ClientData
,
494 OSK_UINT bytes
, OSK_PCHAR file
, OSK_UINT line
);
495 void TCPFree( void *ClientData
,
496 void *data
, OSK_PCHAR file
, OSK_UINT line
);
497 void TCPMemShutdown( void );
499 OSKITTCP_EVENT_HANDLERS EventHandlers
= {
500 NULL
, /* Client Data */
501 TCPSocketState
, /* SocketState */
502 TCPPacketSend
, /* PacketSend */
503 TCPFindInterface
, /* FindInterface */
504 TCPMalloc
, /* Malloc */
510 static KEVENT TimerLoopEvent
;
511 static HANDLE TimerThreadHandle
;
514 * We are running 2 timers here, one with a 200ms interval (fast) and the other
515 * with a 500ms interval (slow). So we need to time out at 200, 400, 500, 600,
516 * 800, 1000 and process the "fast" events at 200, 400, 600, 800, 1000 and the
517 * "slow" events at 500 and 1000.
520 TimerThread(PVOID Context
)
522 LARGE_INTEGER Timeout
;
524 unsigned Current
, NextFast
, NextSlow
, Next
;
531 if (Next
== NextFast
) {
534 if (Next
== NextSlow
) {
537 Next
= min(NextFast
, NextSlow
);
538 Timeout
.QuadPart
= (LONGLONG
) (Next
- Current
) * -1000000; /* 100 ms */
539 Status
= KeWaitForSingleObject(&TimerLoopEvent
, Executive
, KernelMode
,
541 if (Status
!= STATUS_TIMEOUT
) {
542 PsTerminateSystemThread(Status
);
545 TimerOskitTCP( Next
== NextFast
, Next
== NextSlow
);
560 KeInitializeEvent(&TimerLoopEvent
, NotificationEvent
, FALSE
);
561 PsCreateSystemThread(&TimerThreadHandle
, THREAD_ALL_ACCESS
, 0, 0, 0,
565 NTSTATUS
TCPStartup(VOID
)
567 * FUNCTION: Initializes the TCP subsystem
569 * Status of operation
574 Status
= TCPMemStartup();
575 if ( ! NT_SUCCESS(Status
) ) {
579 Status
= PortsStartup( &TCPPorts
, 1, 0xfffe );
580 if( !NT_SUCCESS(Status
) ) {
585 KeInitializeSpinLock(&ClientInfo
.Lock
);
586 ClientInfo
.Unlocked
= FALSE
;
588 RegisterOskitTCPEventHandlers( &EventHandlers
);
591 /* Register this protocol with IP layer */
592 IPRegisterProtocol(IPPROTO_TCP
, TCPReceive
);
594 ExInitializeNPagedLookasideList(
595 &TCPSegmentList
, /* Lookaside list */
596 NULL
, /* Allocate routine */
597 NULL
, /* Free routine */
599 sizeof(TCP_SEGMENT
), /* Size of each entry */
605 TCPInitialized
= TRUE
;
607 return STATUS_SUCCESS
;
611 NTSTATUS
TCPShutdown(VOID
)
613 * FUNCTION: Shuts down the TCP subsystem
615 * Status of operation
618 LARGE_INTEGER WaitForThread
;
621 return STATUS_SUCCESS
;
623 WaitForThread
.QuadPart
= -2500000; /* 250 ms */
624 KeSetEvent(&TimerLoopEvent
, IO_NO_INCREMENT
, FALSE
);
625 ZwWaitForSingleObject(TimerThreadHandle
, FALSE
, &WaitForThread
);
627 /* Deregister this protocol with IP layer */
628 IPRegisterProtocol(IPPROTO_TCP
, NULL
);
630 ExDeleteNPagedLookasideList(&TCPSegmentList
);
632 TCPInitialized
= FALSE
;
636 PortsShutdown( &TCPPorts
);
640 return STATUS_SUCCESS
;
643 NTSTATUS
TCPTranslateError( int OskitError
) {
646 switch( OskitError
) {
647 case 0: Status
= STATUS_SUCCESS
; break;
648 case OSK_EADDRNOTAVAIL
:
649 Status
= STATUS_INVALID_ADDRESS
;
650 DbgPrint("OskitTCP: EADDRNOTAVAIL\n");
653 Status
= STATUS_ADDRESS_ALREADY_EXISTS
;
654 DbgPrint("OskitTCP: EADDRINUSE\n");
656 case OSK_EAFNOSUPPORT
:
657 Status
= STATUS_INVALID_CONNECTION
;
658 DbgPrint("OskitTCP: EAFNOSUPPORT\n");
660 case OSK_ECONNREFUSED
:
661 Status
= STATUS_REMOTE_NOT_LISTENING
;
662 DbgPrint("OskitTCP: ECONNREFUSED\n");
665 Status
= STATUS_REMOTE_DISCONNECT
;
666 DbgPrint("OskitTCP: ECONNRESET\n");
668 case OSK_ECONNABORTED
:
669 Status
= STATUS_LOCAL_DISCONNECT
;
670 DbgPrint("OskitTCP: ECONNABORTED\n");
672 case OSK_EWOULDBLOCK
:
673 case OSK_EINPROGRESS
: Status
= STATUS_PENDING
; break;
675 Status
= STATUS_INVALID_PARAMETER
;
676 DbgPrint("OskitTCP: EINVAL\n");
680 Status
= STATUS_INSUFFICIENT_RESOURCES
;
681 DbgPrint("OskitTCP: ENOMEM/ENOBUFS\n");
685 Status
= STATUS_FILE_CLOSED
;
686 DbgPrint("OskitTCP: ESHUTDOWN/EPIPE\n");
689 Status
= STATUS_BUFFER_TOO_SMALL
;
690 DbgPrint("OskitTCP: EMSGSIZE\n");
693 Status
= STATUS_TIMEOUT
;
694 DbgPrint("OskitTCP: ETIMEDOUT\n");
696 case OSK_ENETUNREACH
:
697 Status
= STATUS_NETWORK_UNREACHABLE
;
698 DbgPrint("OskitTCP: ENETUNREACH\n");
701 Status
= STATUS_ACCESS_VIOLATION
;
702 DbgPrint("OskitTCP: EFAULT\n");
705 DbgPrint("OskitTCP returned unhandled error code: %d\n", OskitError
);
706 Status
= STATUS_INVALID_CONNECTION
;
710 TI_DbgPrint(DEBUG_TCP
,("Error %d -> %x\n", OskitError
, Status
));
715 ( PCONNECTION_ENDPOINT Connection
,
716 PTDI_CONNECTION_INFORMATION ConnInfo
,
717 PTDI_CONNECTION_INFORMATION ReturnInfo
,
718 PTCP_COMPLETION_ROUTINE Complete
,
721 SOCKADDR_IN AddressToConnect
= { 0 }, AddressToBind
= { 0 };
722 IP_ADDRESS RemoteAddress
;
724 TA_IP_ADDRESS LocalAddress
;
726 PNEIGHBOR_CACHE_ENTRY NCE
= NULL
;
729 TI_DbgPrint(DEBUG_TCP
,("TCPConnect: Called\n"));
731 Status
= AddrBuildAddress
732 ((PTRANSPORT_ADDRESS
)ConnInfo
->RemoteAddress
,
736 if (!NT_SUCCESS(Status
)) {
737 TI_DbgPrint(DEBUG_TCP
, ("Could not AddrBuildAddress in TCPConnect\n"));
741 /* Freed in TCPSocketState */
742 TI_DbgPrint(DEBUG_TCP
,
743 ("Connecting to address %x:%x\n",
744 RemoteAddress
.Address
.IPv4Address
,
747 AddressToConnect
.sin_family
= AF_INET
;
748 AddressToBind
= AddressToConnect
;
750 LockObject(Connection
, &OldIrql
);
752 if (!Connection
->AddressFile
)
754 UnlockObject(Connection
, OldIrql
);
755 return STATUS_INVALID_PARAMETER
;
758 if (AddrIsUnspecified(&Connection
->AddressFile
->Address
))
760 if (!(NCE
= RouteGetRouteToDestination(&RemoteAddress
)))
762 UnlockObject(Connection
, OldIrql
);
763 return STATUS_NETWORK_UNREACHABLE
;
767 if (Connection
->AddressFile
->Port
)
769 /* See if we had an unspecified bind address */
772 /* We did, so use the interface unicast address associated with the route */
773 AddressToBind
.sin_addr
.s_addr
= NCE
->Interface
->Unicast
.Address
.IPv4Address
;
777 /* Bind address was explicit so use it */
778 AddressToBind
.sin_addr
.s_addr
= Connection
->AddressFile
->Address
.Address
.IPv4Address
;
781 AddressToBind
.sin_port
= Connection
->AddressFile
->Port
;
783 /* Perform an explicit bind */
784 Status
= TCPTranslateError(OskitTCPBind(Connection
->SocketContext
,
786 sizeof(AddressToBind
)));
790 /* An implicit bind will be performed */
791 Status
= STATUS_SUCCESS
;
794 if (NT_SUCCESS(Status
)) {
795 if (NT_SUCCESS(Status
))
797 memcpy( &AddressToConnect
.sin_addr
,
798 &RemoteAddress
.Address
.IPv4Address
,
799 sizeof(AddressToConnect
.sin_addr
) );
800 AddressToConnect
.sin_port
= RemotePort
;
802 Status
= TCPTranslateError
803 ( OskitTCPConnect( Connection
->SocketContext
,
805 sizeof(AddressToConnect
) ) );
807 if (NT_SUCCESS(Status
))
809 /* Check if we had an unspecified port */
810 if (!Connection
->AddressFile
->Port
)
812 /* We did, so we need to copy back the port */
813 if (NT_SUCCESS(TCPGetSockAddress(Connection
, (PTRANSPORT_ADDRESS
)&LocalAddress
, FALSE
)))
815 /* Allocate the port in the port bitmap */
816 Connection
->AddressFile
->Port
= TCPAllocatePort(LocalAddress
.Address
[0].Address
[0].sin_port
);
818 /* This should never fail */
819 ASSERT(Connection
->AddressFile
->Port
!= 0xFFFF);
823 /* Check if the address was unspecified */
824 if (AddrIsUnspecified(&Connection
->AddressFile
->Address
))
826 /* It is, so store the address of the outgoing NIC */
827 Connection
->AddressFile
->Address
= NCE
->Interface
->Unicast
;
831 if (Status
== STATUS_PENDING
)
833 Bucket
= ExAllocatePoolWithTag( NonPagedPool
, sizeof(*Bucket
), TDI_BUCKET_TAG
);
836 UnlockObject(Connection
, OldIrql
);
837 return STATUS_NO_MEMORY
;
840 Bucket
->Request
.RequestNotifyObject
= (PVOID
)Complete
;
841 Bucket
->Request
.RequestContext
= Context
;
843 InsertTailList( &Connection
->ConnectRequest
, &Bucket
->Entry
);
848 UnlockObject(Connection
, OldIrql
);
853 NTSTATUS TCPDisconnect
854 ( PCONNECTION_ENDPOINT Connection
,
856 PLARGE_INTEGER Timeout
,
857 PTDI_CONNECTION_INFORMATION ConnInfo
,
858 PTDI_CONNECTION_INFORMATION ReturnInfo
,
859 PTCP_COMPLETION_ROUTINE Complete
,
861 NTSTATUS Status
= STATUS_INVALID_PARAMETER
;
865 LARGE_INTEGER ActualTimeout
;
867 TI_DbgPrint(DEBUG_TCP
,("started\n"));
869 LockObject(Connection
, &OldIrql
);
871 if (Flags
& TDI_DISCONNECT_RELEASE
)
873 /* See if we can satisfy this right now */
874 if (IsListEmpty(&Connection
->SendRequest
))
876 /* Send queue is empty so we're good to go */
877 Status
= TCPTranslateError(OskitTCPShutdown(Connection
->SocketContext
, FWRITE
));
879 UnlockObject(Connection
, OldIrql
);
884 /* Check if the timeout was 0 */
885 if (Timeout
&& Timeout
->QuadPart
== 0)
887 OskitTCPShutdown(Connection
->SocketContext
, FWRITE
);
889 while (!IsListEmpty(&Connection
->SendRequest
))
891 Entry
= RemoveHeadList(&Connection
->SendRequest
);
893 Bucket
= CONTAINING_RECORD(Entry
, TDI_BUCKET
, Entry
);
895 Bucket
->Information
= 0;
896 Bucket
->Status
= STATUS_FILE_CLOSED
;
898 CompleteBucket(Connection
, Bucket
);
901 UnlockObject(Connection
, OldIrql
);
903 return STATUS_TIMEOUT
;
906 /* Otherwise we wait for the send queue to be empty */
909 if ((Flags
& TDI_DISCONNECT_ABORT
) || !Flags
)
911 /* This request overrides any pending graceful disconnects */
912 while (!IsListEmpty(&Connection
->ShutdownRequest
))
914 Entry
= RemoveHeadList(&Connection
->ShutdownRequest
);
916 Bucket
= CONTAINING_RECORD(Entry
, TDI_BUCKET
, Entry
);
918 Bucket
->Information
= 0;
919 Bucket
->Status
= STATUS_FILE_CLOSED
;
921 CompleteBucket(Connection
, Bucket
);
924 /* Also kill any pending reads and writes */
925 while (!IsListEmpty(&Connection
->SendRequest
))
927 Entry
= RemoveHeadList(&Connection
->SendRequest
);
929 Bucket
= CONTAINING_RECORD(Entry
, TDI_BUCKET
, Entry
);
931 Bucket
->Information
= 0;
932 Bucket
->Status
= STATUS_FILE_CLOSED
;
934 CompleteBucket(Connection
, Bucket
);
937 while (!IsListEmpty(&Connection
->ReceiveRequest
))
939 Entry
= RemoveHeadList(&Connection
->ReceiveRequest
);
941 Bucket
= CONTAINING_RECORD(Entry
, TDI_BUCKET
, Entry
);
943 Bucket
->Information
= 0;
944 Bucket
->Status
= STATUS_FILE_CLOSED
;
946 CompleteBucket(Connection
, Bucket
);
949 /* An abort never pends; we just drop everything and complete */
950 Status
= TCPTranslateError(OskitTCPShutdown(Connection
->SocketContext
, FWRITE
| FREAD
));
952 UnlockObject(Connection
, OldIrql
);
957 /* We couldn't complete the request now because we need to wait for outstanding I/O */
958 Bucket
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*Bucket
), TDI_BUCKET_TAG
);
961 UnlockObject(Connection
, OldIrql
);
962 return STATUS_NO_MEMORY
;
965 Bucket
->Request
.RequestNotifyObject
= (PVOID
)Complete
;
966 Bucket
->Request
.RequestContext
= Context
;
968 InsertTailList(&Connection
->ShutdownRequest
, &Bucket
->Entry
);
970 /* Use the timeout specified or 1 second if none was specified */
973 ActualTimeout
= *Timeout
;
977 ActualTimeout
.QuadPart
= -1000000;
980 ReferenceObject(Connection
);
981 KeSetTimer(&Connection
->DisconnectTimer
, ActualTimeout
, &Connection
->DisconnectDpc
);
983 UnlockObject(Connection
, OldIrql
);
985 TI_DbgPrint(DEBUG_TCP
,("finished %x\n", Status
));
987 return STATUS_PENDING
;
991 ( PCONNECTION_ENDPOINT Connection
)
997 LockObject(Connection
, &OldIrql
);
998 Socket
= Connection
->SocketContext
;
999 Connection
->SocketContext
= NULL
;
1001 /* We should not be associated to an address file at this point */
1002 ASSERT(!Connection
->AddressFile
);
1004 /* Don't try to close again if the other side closed us already */
1007 /* We need to close here otherwise oskit will never indicate
1008 * SEL_FIN and we will never fully close the connection */
1009 Status
= TCPTranslateError( OskitTCPClose( Socket
) );
1011 if (!NT_SUCCESS(Status
))
1013 Connection
->SocketContext
= Socket
;
1014 UnlockObject(Connection
, OldIrql
);
1020 /* We are already closed by the other end so return success */
1021 Status
= STATUS_SUCCESS
;
1024 UnlockObject(Connection
, OldIrql
);
1026 DereferenceObject(Connection
);
1031 NTSTATUS TCPReceiveData
1032 ( PCONNECTION_ENDPOINT Connection
,
1033 PNDIS_BUFFER Buffer
,
1034 ULONG ReceiveLength
,
1035 PULONG BytesReceived
,
1037 PTCP_COMPLETION_ROUTINE Complete
,
1040 UINT DataLen
, Received
= 0;
1045 TI_DbgPrint(DEBUG_TCP
,("Called for %d bytes (on socket %x)\n",
1046 ReceiveLength
, Connection
->SocketContext
));
1048 NdisQueryBuffer( Buffer
, &DataBuffer
, &DataLen
);
1050 TI_DbgPrint(DEBUG_TCP
,("TCP>|< Got an MDL %x (%x:%d)\n", Buffer
, DataBuffer
, DataLen
));
1052 LockObject(Connection
, &OldIrql
);
1054 Status
= TCPTranslateError
1056 ( Connection
->SocketContext
,
1062 TI_DbgPrint(DEBUG_TCP
,("OskitTCPReceive: %x, %d\n", Status
, Received
));
1064 /* Keep this request around ... there was no data yet */
1065 if( Status
== STATUS_PENDING
) {
1066 /* Freed in TCPSocketState */
1067 Bucket
= ExAllocatePoolWithTag( NonPagedPool
, sizeof(*Bucket
), TDI_BUCKET_TAG
);
1069 TI_DbgPrint(DEBUG_TCP
,("Failed to allocate bucket\n"));
1070 UnlockObject(Connection
, OldIrql
);
1071 return STATUS_NO_MEMORY
;
1074 Bucket
->Request
.RequestNotifyObject
= Complete
;
1075 Bucket
->Request
.RequestContext
= Context
;
1078 InsertTailList( &Connection
->ReceiveRequest
, &Bucket
->Entry
);
1079 TI_DbgPrint(DEBUG_TCP
,("Queued read irp\n"));
1081 TI_DbgPrint(DEBUG_TCP
,("Got status %x, bytes %d\n", Status
, Received
));
1082 *BytesReceived
= Received
;
1085 UnlockObject(Connection
, OldIrql
);
1087 TI_DbgPrint(DEBUG_TCP
,("Status %x\n", Status
));
1092 NTSTATUS TCPSendData
1093 ( PCONNECTION_ENDPOINT Connection
,
1098 PTCP_COMPLETION_ROUTINE Complete
,
1105 LockObject(Connection
, &OldIrql
);
1107 TI_DbgPrint(DEBUG_TCP
,("Called for %d bytes (on socket %x)\n",
1108 SendLength
, Connection
->SocketContext
));
1110 TI_DbgPrint(DEBUG_TCP
,("Connection = %x\n", Connection
));
1111 TI_DbgPrint(DEBUG_TCP
,("Connection->SocketContext = %x\n",
1112 Connection
->SocketContext
));
1114 Status
= TCPTranslateError
1115 ( OskitTCPSend( Connection
->SocketContext
,
1116 (OSK_PCHAR
)BufferData
, SendLength
,
1119 TI_DbgPrint(DEBUG_TCP
,("OskitTCPSend: %x, %d\n", Status
, Sent
));
1121 /* Keep this request around ... there was no data yet */
1122 if( Status
== STATUS_PENDING
) {
1123 /* Freed in TCPSocketState */
1124 Bucket
= ExAllocatePoolWithTag( NonPagedPool
, sizeof(*Bucket
), TDI_BUCKET_TAG
);
1126 UnlockObject(Connection
, OldIrql
);
1127 TI_DbgPrint(DEBUG_TCP
,("Failed to allocate bucket\n"));
1128 return STATUS_NO_MEMORY
;
1131 Bucket
->Request
.RequestNotifyObject
= Complete
;
1132 Bucket
->Request
.RequestContext
= Context
;
1135 InsertTailList( &Connection
->SendRequest
, &Bucket
->Entry
);
1136 TI_DbgPrint(DEBUG_TCP
,("Queued write irp\n"));
1138 TI_DbgPrint(DEBUG_TCP
,("Got status %x, bytes %d\n", Status
, Sent
));
1142 UnlockObject(Connection
, OldIrql
);
1144 TI_DbgPrint(DEBUG_TCP
,("Status %x\n", Status
));
1149 UINT
TCPAllocatePort( UINT HintPort
) {
1151 if( AllocatePort( &TCPPorts
, HintPort
) ) return HintPort
;
1154 (MID_TRACE
,("We got a hint port but couldn't allocate it\n"));
1157 } else return AllocatePortFromRange( &TCPPorts
, 1024, 5000 );
1160 VOID
TCPFreePort( UINT Port
) {
1161 DeallocatePort( &TCPPorts
, Port
);
1164 NTSTATUS TCPGetSockAddress
1165 ( PCONNECTION_ENDPOINT Connection
,
1166 PTRANSPORT_ADDRESS Address
,
1167 BOOLEAN GetRemote
) {
1168 OSK_UINT LocalAddress
, RemoteAddress
;
1169 OSK_UI16 LocalPort
, RemotePort
;
1170 PTA_IP_ADDRESS AddressIP
= (PTA_IP_ADDRESS
)Address
;
1174 LockObject(Connection
, &OldIrql
);
1176 Status
= TCPTranslateError(OskitTCPGetAddress(Connection
->SocketContext
,
1177 &LocalAddress
, &LocalPort
,
1178 &RemoteAddress
, &RemotePort
));
1180 UnlockObject(Connection
, OldIrql
);
1182 if (!NT_SUCCESS(Status
))
1185 AddressIP
->TAAddressCount
= 1;
1186 AddressIP
->Address
[0].AddressLength
= TDI_ADDRESS_LENGTH_IP
;
1187 AddressIP
->Address
[0].AddressType
= TDI_ADDRESS_TYPE_IP
;
1188 AddressIP
->Address
[0].Address
[0].sin_port
= GetRemote
? RemotePort
: LocalPort
;
1189 AddressIP
->Address
[0].Address
[0].in_addr
= GetRemote
? RemoteAddress
: LocalAddress
;
1194 BOOLEAN
TCPRemoveIRP( PCONNECTION_ENDPOINT Endpoint
, PIRP Irp
) {
1196 PLIST_ENTRY ListHead
[5];
1200 BOOLEAN Found
= FALSE
;
1202 ListHead
[0] = &Endpoint
->SendRequest
;
1203 ListHead
[1] = &Endpoint
->ReceiveRequest
;
1204 ListHead
[2] = &Endpoint
->ConnectRequest
;
1205 ListHead
[3] = &Endpoint
->ListenRequest
;
1206 ListHead
[4] = &Endpoint
->ShutdownRequest
;
1208 LockObject(Endpoint
, &OldIrql
);
1210 for( i
= 0; i
< 5; i
++ )
1212 for( Entry
= ListHead
[i
]->Flink
;
1213 Entry
!= ListHead
[i
];
1214 Entry
= Entry
->Flink
)
1216 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
1217 if( Bucket
->Request
.RequestContext
== Irp
)
1219 RemoveEntryList( &Bucket
->Entry
);
1220 ExFreePoolWithTag( Bucket
, TDI_BUCKET_TAG
);
1227 UnlockObject(Endpoint
, OldIrql
);