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
;
18 LIST_ENTRY SignalledConnections
;
19 LIST_ENTRY SleepingThreadsList
;
20 FAST_MUTEX SleepingThreadsLock
;
21 RECURSIVE_MUTEX TCPLock
;
24 static VOID
HandleSignalledConnection( PCONNECTION_ENDPOINT Connection
,
26 NTSTATUS Status
= STATUS_SUCCESS
;
27 PTCP_COMPLETION_ROUTINE Complete
;
33 TI_DbgPrint(MID_TRACE
,("Handling signalled state on %x (%x)\n",
34 Connection
, Connection
->SocketContext
));
36 /* Things that can happen when we try the initial connection */
37 if( NewState
& SEL_CONNECT
) {
38 while( !IsListEmpty( &Connection
->ConnectRequest
) ) {
39 Connection
->State
|= NewState
;
40 Entry
= RemoveHeadList( &Connection
->ConnectRequest
);
41 TI_DbgPrint(DEBUG_TCP
, ("Connect Event\n"));
43 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
44 Complete
= Bucket
->Request
.RequestNotifyObject
;
45 TI_DbgPrint(DEBUG_TCP
,
46 ("Completing Request %x\n", Bucket
->Request
));
48 if( (NewState
& (SEL_CONNECT
| SEL_FIN
)) ==
49 (SEL_CONNECT
| SEL_FIN
) )
50 Status
= STATUS_CONNECTION_REFUSED
;
52 Status
= STATUS_SUCCESS
;
54 Complete( Bucket
->Request
.RequestContext
, Status
, 0 );
56 /* Frees the bucket allocated in TCPConnect */
57 PoolFreeBuffer( Bucket
);
61 if( NewState
& SEL_ACCEPT
) {
62 /* Handle readable on a listening socket --
63 * TODO: Implement filtering
66 TI_DbgPrint(DEBUG_TCP
,("Accepting new connection on %x (Queue: %s)\n",
68 IsListEmpty(&Connection
->ListenRequest
) ?
69 "empty" : "nonempty"));
71 while( !IsListEmpty( &Connection
->ListenRequest
) ) {
72 PIO_STACK_LOCATION IrpSp
;
74 Entry
= RemoveHeadList( &Connection
->ListenRequest
);
75 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
76 Complete
= Bucket
->Request
.RequestNotifyObject
;
78 Irp
= Bucket
->Request
.RequestContext
;
79 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
81 TI_DbgPrint(DEBUG_TCP
,("Getting the socket\n"));
82 Status
= TCPServiceListeningSocket
83 ( Connection
->AddressFile
->Listener
,
84 Bucket
->AssociatedEndpoint
,
85 (PTDI_REQUEST_KERNEL
)&IrpSp
->Parameters
);
87 TI_DbgPrint(DEBUG_TCP
,("Socket: Status: %x\n"));
89 if( Status
== STATUS_PENDING
) {
90 InsertHeadList( &Connection
->ListenRequest
, &Bucket
->Entry
);
93 Complete( Bucket
->Request
.RequestContext
, Status
, 0 );
97 /* Things that happen after we're connected */
98 if( NewState
& SEL_READ
) {
99 TI_DbgPrint(DEBUG_TCP
,("Readable: irp list %s\n",
100 IsListEmpty(&Connection
->ReceiveRequest
) ?
101 "empty" : "nonempty"));
103 while( !IsListEmpty( &Connection
->ReceiveRequest
) ) {
104 OSK_UINT RecvLen
= 0, Received
= 0;
105 OSK_PCHAR RecvBuffer
= 0;
107 Entry
= RemoveHeadList( &Connection
->ReceiveRequest
);
108 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
109 Complete
= Bucket
->Request
.RequestNotifyObject
;
111 Irp
= Bucket
->Request
.RequestContext
;
112 Mdl
= Irp
->MdlAddress
;
114 TI_DbgPrint(DEBUG_TCP
,
115 ("Getting the user buffer from %x\n", Mdl
));
117 NdisQueryBuffer( Mdl
, &RecvBuffer
, &RecvLen
);
119 TI_DbgPrint(DEBUG_TCP
,
120 ("Reading %d bytes to %x\n", RecvLen
, RecvBuffer
));
122 TI_DbgPrint(DEBUG_TCP
, ("Connection: %x\n", Connection
));
125 ("Connection->SocketContext: %x\n",
126 Connection
->SocketContext
));
127 TI_DbgPrint(DEBUG_TCP
, ("RecvBuffer: %x\n", RecvBuffer
));
129 Status
= TCPTranslateError
130 ( OskitTCPRecv( Connection
->SocketContext
,
136 TI_DbgPrint(DEBUG_TCP
,("TCP Bytes: %d\n", Received
));
138 if( Status
== STATUS_SUCCESS
) {
139 TI_DbgPrint(DEBUG_TCP
,("Received %d bytes with status %x\n",
142 Complete( Bucket
->Request
.RequestContext
,
143 STATUS_SUCCESS
, Received
);
144 } else if( Status
== STATUS_PENDING
) {
146 ( &Connection
->ReceiveRequest
, &Bucket
->Entry
);
149 TI_DbgPrint(DEBUG_TCP
,
150 ("Completing Receive request: %x %x\n",
151 Bucket
->Request
, Status
));
152 Complete( Bucket
->Request
.RequestContext
, Status
, 0 );
157 if( NewState
& SEL_FIN
) {
158 PLIST_ENTRY ListsToErase
[4];
159 NTSTATUS IrpStatus
[4];
162 TI_DbgPrint(DEBUG_TCP
, ("EOF From socket\n"));
164 ListsToErase
[0] = &Connection
->ReceiveRequest
;
165 IrpStatus
[0] = STATUS_SUCCESS
;
166 ListsToErase
[1] = &Connection
->ListenRequest
;
167 IrpStatus
[1] = STATUS_UNSUCCESSFUL
;
168 ListsToErase
[2] = &Connection
->ConnectRequest
;
169 IrpStatus
[2] = STATUS_UNSUCCESSFUL
;
172 for( i
= 0; ListsToErase
[i
]; i
++ ) {
173 while( !IsListEmpty( ListsToErase
[i
] ) ) {
174 Entry
= RemoveHeadList( ListsToErase
[i
] );
175 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
176 Complete
= Bucket
->Request
.RequestNotifyObject
;
177 Complete( Bucket
->Request
.RequestContext
, STATUS_SUCCESS
, 0 );
182 Connection
->Signalled
= FALSE
;
185 VOID
DrainSignals() {
186 PCONNECTION_ENDPOINT Connection
;
187 PLIST_ENTRY ListEntry
;
189 while( !IsListEmpty( &SignalledConnections
) ) {
190 ListEntry
= RemoveHeadList( &SignalledConnections
);
191 Connection
= CONTAINING_RECORD( ListEntry
, CONNECTION_ENDPOINT
,
193 HandleSignalledConnection( Connection
, Connection
->SignalState
);
197 PCONNECTION_ENDPOINT
TCPAllocateConnectionEndpoint( PVOID ClientContext
) {
198 PCONNECTION_ENDPOINT Connection
=
199 ExAllocatePool(NonPagedPool
, sizeof(CONNECTION_ENDPOINT
));
203 TI_DbgPrint(DEBUG_CPOINT
, ("Connection point file object allocated at (0x%X).\n", Connection
));
205 RtlZeroMemory(Connection
, sizeof(CONNECTION_ENDPOINT
));
207 /* Initialize spin lock that protects the connection endpoint file object */
208 TcpipInitializeSpinLock(&Connection
->Lock
);
209 InitializeListHead(&Connection
->ConnectRequest
);
210 InitializeListHead(&Connection
->ListenRequest
);
211 InitializeListHead(&Connection
->ReceiveRequest
);
213 /* Save client context pointer */
214 Connection
->ClientContext
= ClientContext
;
219 VOID
TCPFreeConnectionEndpoint( PCONNECTION_ENDPOINT Connection
) {
220 TI_DbgPrint(MAX_TRACE
,("FIXME: Cancel all pending requests\n"));
221 /* XXX Cancel all pending requests */
222 ExFreePool( Connection
);
225 NTSTATUS
TCPSocket( PCONNECTION_ENDPOINT Connection
,
226 UINT Family
, UINT Type
, UINT Proto
) {
229 TI_DbgPrint(DEBUG_TCP
,("Called: Connection %x, Family %d, Type %d, "
231 Connection
, Family
, Type
, Proto
));
233 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
234 Status
= TCPTranslateError( OskitTCPSocket( Connection
,
235 &Connection
->SocketContext
,
240 ASSERT_KM_POINTER(Connection
->SocketContext
);
242 TI_DbgPrint(DEBUG_TCP
,("Connection->SocketContext %x\n",
243 Connection
->SocketContext
));
245 TcpipRecursiveMutexLeave( &TCPLock
);
250 VOID
TCPReceive(PIP_INTERFACE Interface
, PIP_PACKET IPPacket
)
252 * FUNCTION: Receives and queues TCP data
254 * IPPacket = Pointer to an IP packet that was received
256 * This is the low level interface for receiving TCP data
259 TI_DbgPrint(DEBUG_TCP
,("Sending packet %d (%d) to oskit\n",
261 IPPacket
->HeaderSize
));
263 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
265 OskitTCPReceiveDatagram( IPPacket
->Header
,
267 IPPacket
->HeaderSize
);
271 TcpipRecursiveMutexLeave( &TCPLock
);
275 int TCPSocketState( void *ClientData
,
277 void *WhichConnection
,
280 int TCPPacketSend( void *ClientData
,
284 POSK_IFADDR
TCPFindInterface( void *ClientData
,
287 OSK_SOCKADDR
*ReqAddr
);
289 void *TCPMalloc( void *ClientData
,
290 OSK_UINT bytes
, OSK_PCHAR file
, OSK_UINT line
);
291 void TCPFree( void *ClientData
,
292 void *data
, OSK_PCHAR file
, OSK_UINT line
);
294 int TCPSleep( void *ClientData
, void *token
, int priority
, char *msg
,
297 void TCPWakeup( void *ClientData
, void *token
);
299 OSKITTCP_EVENT_HANDLERS EventHandlers
= {
300 NULL
, /* Client Data */
301 TCPSocketState
, /* SocketState */
302 TCPPacketSend
, /* PacketSend */
303 TCPFindInterface
, /* FindInterface */
304 TCPMalloc
, /* Malloc */
306 TCPSleep
, /* Sleep */
307 TCPWakeup
/* Wakeup */
310 NTSTATUS
TCPStartup(VOID
)
312 * FUNCTION: Initializes the TCP subsystem
314 * Status of operation
317 TcpipRecursiveMutexInit( &TCPLock
);
318 ExInitializeFastMutex( &SleepingThreadsLock
);
319 InitializeListHead( &SleepingThreadsList
);
320 InitializeListHead( &SignalledConnections
);
322 PortsStartup( &TCPPorts
, 1, 0xfffe );
324 RegisterOskitTCPEventHandlers( &EventHandlers
);
327 /* Register this protocol with IP layer */
328 IPRegisterProtocol(IPPROTO_TCP
, TCPReceive
);
330 ExInitializeNPagedLookasideList(
331 &TCPSegmentList
, /* Lookaside list */
332 NULL
, /* Allocate routine */
333 NULL
, /* Free routine */
335 sizeof(TCP_SEGMENT
), /* Size of each entry */
336 TAG('T','C','P','S'), /* Tag */
339 TCPInitialized
= TRUE
;
341 return STATUS_SUCCESS
;
345 NTSTATUS
TCPShutdown(VOID
)
347 * FUNCTION: Shuts down the TCP subsystem
349 * Status of operation
353 return STATUS_SUCCESS
;
355 /* Deregister this protocol with IP layer */
356 IPRegisterProtocol(IPPROTO_TCP
, NULL
);
358 ExDeleteNPagedLookasideList(&TCPSegmentList
);
360 TCPInitialized
= FALSE
;
364 PortsShutdown( &TCPPorts
);
366 return STATUS_SUCCESS
;
369 NTSTATUS
TCPTranslateError( int OskitError
) {
370 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
372 switch( OskitError
) {
373 case 0: Status
= STATUS_SUCCESS
; break;
374 case OSK_EADDRNOTAVAIL
:
375 case OSK_EAFNOSUPPORT
: Status
= STATUS_INVALID_CONNECTION
; break;
376 case OSK_ECONNREFUSED
:
377 case OSK_ECONNRESET
: Status
= STATUS_REMOTE_NOT_LISTENING
; break;
378 case OSK_EINPROGRESS
:
379 case OSK_EAGAIN
: Status
= STATUS_PENDING
; break;
380 default: Status
= STATUS_INVALID_CONNECTION
; break;
383 TI_DbgPrint(DEBUG_TCP
,("Error %d -> %x\n", OskitError
, Status
));
388 ( PCONNECTION_ENDPOINT Connection
,
389 PTDI_CONNECTION_INFORMATION ConnInfo
,
390 PTDI_CONNECTION_INFORMATION ReturnInfo
,
391 PTCP_COMPLETION_ROUTINE Complete
,
394 SOCKADDR_IN AddressToConnect
= { 0 }, AddressToBind
= { 0 };
395 IP_ADDRESS RemoteAddress
;
399 DbgPrint("TCPConnect: Called\n");
401 Bucket
= ExAllocatePool( NonPagedPool
, sizeof(*Bucket
) );
402 if( !Bucket
) return STATUS_NO_MEMORY
;
404 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
406 /* Freed in TCPSocketState */
407 Bucket
->Request
.RequestNotifyObject
= (PVOID
)Complete
;
408 Bucket
->Request
.RequestContext
= Context
;
410 InsertHeadList( &Connection
->ConnectRequest
, &Bucket
->Entry
);
412 Status
= AddrBuildAddress
413 ((PTRANSPORT_ADDRESS
)ConnInfo
->RemoteAddress
,
417 DbgPrint("Connecting to address %x:%x\n",
418 RemoteAddress
.Address
.IPv4Address
,
421 if (!NT_SUCCESS(Status
)) {
422 TI_DbgPrint(DEBUG_TCP
, ("Could not AddrBuildAddress in TCPConnect\n"));
426 AddressToConnect
.sin_family
= AF_INET
;
427 AddressToBind
= AddressToConnect
;
429 OskitTCPBind( Connection
->SocketContext
,
432 sizeof(AddressToBind
) );
434 memcpy( &AddressToConnect
.sin_addr
,
435 &RemoteAddress
.Address
.IPv4Address
,
436 sizeof(AddressToConnect
.sin_addr
) );
437 AddressToConnect
.sin_port
= RemotePort
;
439 Status
= TCPTranslateError
440 ( OskitTCPConnect( Connection
->SocketContext
,
443 sizeof(AddressToConnect
) ) );
445 TcpipRecursiveMutexLeave( &TCPLock
);
447 if( Status
== OSK_EINPROGRESS
)
448 return STATUS_PENDING
;
453 NTSTATUS TCPDisconnect
454 ( PCONNECTION_ENDPOINT Connection
,
456 PTDI_CONNECTION_INFORMATION ConnInfo
,
457 PTDI_CONNECTION_INFORMATION ReturnInfo
,
458 PTCP_COMPLETION_ROUTINE Complete
,
462 TI_DbgPrint(DEBUG_TCP
,("started\n"));
464 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
466 switch( Flags
& (TDI_DISCONNECT_ABORT
| TDI_DISCONNECT_RELEASE
) ) {
468 case TDI_DISCONNECT_ABORT
:
472 case TDI_DISCONNECT_ABORT
| TDI_DISCONNECT_RELEASE
:
476 case TDI_DISCONNECT_RELEASE
:
481 Status
= TCPTranslateError
482 ( OskitTCPShutdown( Connection
->SocketContext
, Flags
) );
484 TcpipRecursiveMutexLeave( &TCPLock
);
486 TI_DbgPrint(DEBUG_TCP
,("finished %x\n", Status
));
492 ( PCONNECTION_ENDPOINT Connection
) {
495 TI_DbgPrint(DEBUG_TCP
,("TCPClose started\n"));
497 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
499 Status
= TCPTranslateError( OskitTCPClose( Connection
->SocketContext
) );
501 /* Make our code remove all pending IRPs */
502 Connection
->State
|= SEL_FIN
;
505 TcpipRecursiveMutexLeave( &TCPLock
);
507 TI_DbgPrint(DEBUG_TCP
,("TCPClose finished %x\n", Status
));
512 NTSTATUS TCPReceiveData
513 ( PCONNECTION_ENDPOINT Connection
,
516 PULONG BytesReceived
,
518 PTCP_COMPLETION_ROUTINE Complete
,
520 OSK_PCHAR DataBuffer
;
521 UINT DataLen
, Received
= 0;
525 TI_DbgPrint(DEBUG_TCP
,("Called for %d bytes (on socket %x)\n",
526 ReceiveLength
, Connection
->SocketContext
));
528 ASSERT_KM_POINTER(Connection
->SocketContext
);
530 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
532 NdisQueryBuffer( Buffer
, &DataBuffer
, &DataLen
);
534 TI_DbgPrint(DEBUG_TCP
,("TCP>|< Got an MDL %x (%x:%d)\n", Buffer
, DataBuffer
, DataLen
));
536 Status
= TCPTranslateError
538 ( Connection
->SocketContext
,
544 TI_DbgPrint(DEBUG_TCP
,("OskitTCPReceive: %x, %d\n", Status
, Received
));
546 /* Keep this request around ... there was no data yet */
547 if( Status
== STATUS_PENDING
) {
548 /* Freed in TCPSocketState */
549 Bucket
= ExAllocatePool( NonPagedPool
, sizeof(*Bucket
) );
551 TI_DbgPrint(DEBUG_TCP
,("Failed to allocate bucket\n"));
552 TcpipRecursiveMutexLeave( &TCPLock
);
553 return STATUS_NO_MEMORY
;
556 Bucket
->Request
.RequestNotifyObject
= Complete
;
557 Bucket
->Request
.RequestContext
= Context
;
560 InsertHeadList( &Connection
->ReceiveRequest
, &Bucket
->Entry
);
561 Status
= STATUS_PENDING
;
562 TI_DbgPrint(DEBUG_TCP
,("Queued read irp\n"));
564 TI_DbgPrint(DEBUG_TCP
,("Got status %x, bytes %d\n", Status
, Received
));
565 *BytesReceived
= Received
;
568 TcpipRecursiveMutexLeave( &TCPLock
);
570 TI_DbgPrint(DEBUG_TCP
,("Status %x\n", Status
));
576 ( PCONNECTION_ENDPOINT Connection
,
583 ASSERT_KM_POINTER(Connection
->SocketContext
);
585 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
587 TI_DbgPrint(DEBUG_TCP
,("Connection = %x\n", Connection
));
588 TI_DbgPrint(DEBUG_TCP
,("Connection->SocketContext = %x\n",
589 Connection
->SocketContext
));
591 Status
= OskitTCPSend( Connection
->SocketContext
,
592 (OSK_PCHAR
)BufferData
, PacketSize
,
593 (PUINT
)DataUsed
, 0 );
595 TcpipRecursiveMutexLeave( &TCPLock
);
600 VOID
TCPTimeout(VOID
) {
601 static int Times
= 0;
602 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
603 if( (Times
++ % 5) == 0 ) {
607 TcpipRecursiveMutexLeave( &TCPLock
);
610 UINT
TCPAllocatePort( UINT HintPort
) {
612 if( AllocatePort( &TCPPorts
, HintPort
) ) return HintPort
;
615 (MID_TRACE
,("We got a hint port but couldn't allocate it\n"));
618 } else return AllocatePortFromRange( &TCPPorts
, 1024, 5000 );
621 VOID
TCPFreePort( UINT Port
) {
622 DeallocatePort( &TCPPorts
, Port
);
625 NTSTATUS TCPGetPeerAddress
626 ( PCONNECTION_ENDPOINT Connection
,
627 PTRANSPORT_ADDRESS Address
) {
628 OSK_UINT LocalAddress
, RemoteAddress
;
629 OSK_UI16 LocalPort
, RemotePort
;
630 PTA_IP_ADDRESS AddressIP
= (PTA_IP_ADDRESS
)Address
;
632 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
635 ( Connection
->SocketContext
,
636 &LocalAddress
, &LocalPort
,
637 &RemoteAddress
, &RemotePort
);
639 AddressIP
->TAAddressCount
= 1;
640 AddressIP
->Address
[0].AddressLength
= TDI_ADDRESS_LENGTH_IP
;
641 AddressIP
->Address
[0].AddressType
= TDI_ADDRESS_TYPE_IP
;
642 AddressIP
->Address
[0].Address
[0].sin_port
= RemotePort
;
643 AddressIP
->Address
[0].Address
[0].in_addr
= RemoteAddress
;
645 TcpipRecursiveMutexLeave( &TCPLock
);
647 return STATUS_SUCCESS
;