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)
8 * CSH 01/08-2000 Created
13 LONG TCP_IPIdentification
= 0;
14 static BOOLEAN TCPInitialized
= FALSE
;
15 static NPAGED_LOOKASIDE_LIST TCPSegmentList
;
16 LIST_ENTRY SignalledConnections
;
17 LIST_ENTRY SleepingThreadsList
;
18 FAST_MUTEX SleepingThreadsLock
;
19 RECURSIVE_MUTEX TCPLock
;
22 static VOID
HandleSignalledConnection( PCONNECTION_ENDPOINT Connection
,
24 NTSTATUS Status
= STATUS_SUCCESS
;
25 PTCP_COMPLETION_ROUTINE Complete
;
28 BOOLEAN CompletedOne
= FALSE
;
30 /* Things that can happen when we try the initial connection */
31 if( ((NewState
& SEL_CONNECT
) || (NewState
& SEL_FIN
)) &&
33 !(Connection
->State
& (SEL_CONNECT
| SEL_FIN
)) ) {
34 while( !IsListEmpty( &Connection
->ConnectRequest
) ) {
35 Connection
->State
|= NewState
& (SEL_CONNECT
| SEL_FIN
);
36 Entry
= RemoveHeadList( &Connection
->ConnectRequest
);
37 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
38 Complete
= Bucket
->Request
.RequestNotifyObject
;
39 TI_DbgPrint(DEBUG_TCP
,
40 ("Completing Connect Request %x\n", Bucket
->Request
));
41 if( NewState
& SEL_FIN
) Status
= STATUS_CONNECTION_REFUSED
;
42 Complete( Bucket
->Request
.RequestContext
, Status
, 0 );
43 /* Frees the bucket allocated in TCPConnect */
44 PoolFreeBuffer( Bucket
);
48 /* Things that happen after we're connected */
49 if( (NewState
& SEL_READ
) ) {
50 TI_DbgPrint(DEBUG_TCP
,("Readable: irp list %s\n",
51 IsListEmpty(&Connection
->ReceiveRequest
) ?
52 "empty" : "nonempty"));
54 while( !IsListEmpty( &Connection
->ReceiveRequest
) ) {
56 OSK_UINT RecvLen
= 0, Received
= 0;
57 OSK_PCHAR RecvBuffer
= 0;
61 Entry
= RemoveHeadList( &Connection
->ReceiveRequest
);
62 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
63 Complete
= Bucket
->Request
.RequestNotifyObject
;
65 TI_DbgPrint(DEBUG_TCP
,
66 ("Readable, Completing read request %x\n",
69 Irp
= Bucket
->Request
.RequestContext
;
70 Mdl
= Irp
->MdlAddress
;
72 TI_DbgPrint(DEBUG_TCP
,
73 ("Getting the user buffer from %x\n", Mdl
));
75 NdisQueryBuffer( Mdl
, &RecvBuffer
, &RecvLen
);
77 TI_DbgPrint(DEBUG_TCP
,
78 ("Reading %d bytes to %x\n", RecvLen
, RecvBuffer
));
80 TI_DbgPrint(DEBUG_TCP
, ("Connection: %x\n", Connection
));
83 ("Connection->SocketContext: %x\n",
84 Connection
->SocketContext
));
85 TI_DbgPrint(DEBUG_TCP
, ("RecvBuffer: %x\n", RecvBuffer
));
87 Status
= TCPTranslateError
88 ( OskitTCPRecv( Connection
->SocketContext
,
94 TI_DbgPrint(DEBUG_TCP
,("TCP Bytes: %d\n", Received
));
96 if( Status
== STATUS_SUCCESS
) {
97 TI_DbgPrint(DEBUG_TCP
,("Received %d bytes with status %x\n",
100 TI_DbgPrint(DEBUG_TCP
,
101 ("Completing Receive Request: %x\n",
104 Complete( Bucket
->Request
.RequestContext
,
105 STATUS_SUCCESS
, Received
);
107 } else if( Status
== STATUS_PENDING
) {
108 InsertHeadList( &Connection
->ReceiveRequest
,
112 TI_DbgPrint(DEBUG_TCP
,
113 ("Completing Receive request: %x %x\n",
114 Bucket
->Request
, Status
));
115 Complete( Bucket
->Request
.RequestContext
, Status
, 0 );
120 if( NewState
& SEL_FIN
) {
121 TI_DbgPrint(DEBUG_TCP
, ("EOF From socket\n"));
123 while( !IsListEmpty( &Connection
->ReceiveRequest
) ) {
124 Entry
= RemoveHeadList( &Connection
->ReceiveRequest
);
125 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
126 Complete
= Bucket
->Request
.RequestNotifyObject
;
128 Complete( Bucket
->Request
.RequestContext
, STATUS_SUCCESS
, 0 );
132 Connection
->Signalled
= FALSE
;
135 VOID
DrainSignals() {
136 PCONNECTION_ENDPOINT Connection
;
137 PLIST_ENTRY ListEntry
;
139 while( !IsListEmpty( &SignalledConnections
) ) {
140 ListEntry
= RemoveHeadList( &SignalledConnections
);
141 Connection
= CONTAINING_RECORD( ListEntry
, CONNECTION_ENDPOINT
,
143 HandleSignalledConnection( Connection
, Connection
->SignalState
);
147 PCONNECTION_ENDPOINT
TCPAllocateConnectionEndpoint( PVOID ClientContext
) {
148 PCONNECTION_ENDPOINT Connection
=
149 ExAllocatePool(NonPagedPool
, sizeof(CONNECTION_ENDPOINT
));
153 TI_DbgPrint(DEBUG_CPOINT
, ("Connection point file object allocated at (0x%X).\n", Connection
));
155 RtlZeroMemory(Connection
, sizeof(CONNECTION_ENDPOINT
));
157 /* Initialize spin lock that protects the connection endpoint file object */
158 TcpipInitializeSpinLock(&Connection
->Lock
);
159 InitializeListHead(&Connection
->ConnectRequest
);
160 InitializeListHead(&Connection
->ListenRequest
);
161 InitializeListHead(&Connection
->ReceiveRequest
);
163 /* Save client context pointer */
164 Connection
->ClientContext
= ClientContext
;
169 VOID
TCPFreeConnectionEndpoint( PCONNECTION_ENDPOINT Connection
) {
170 TI_DbgPrint(MAX_TRACE
,("FIXME: Cancel all pending requests\n"));
171 /* XXX Cancel all pending requests */
172 ExFreePool( Connection
);
175 NTSTATUS
TCPSocket( PCONNECTION_ENDPOINT Connection
,
176 UINT Family
, UINT Type
, UINT Proto
) {
179 TI_DbgPrint(DEBUG_TCP
,("Called: Connection %x, Family %d, Type %d, "
181 Connection
, Family
, Type
, Proto
));
183 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
184 Status
= TCPTranslateError( OskitTCPSocket( Connection
,
185 &Connection
->SocketContext
,
190 ASSERT_KM_POINTER(Connection
->SocketContext
);
192 TI_DbgPrint(DEBUG_TCP
,("Connection->SocketContext %x\n",
193 Connection
->SocketContext
));
195 TcpipRecursiveMutexLeave( &TCPLock
);
200 VOID
TCPReceive(PIP_INTERFACE Interface
, PIP_PACKET IPPacket
)
202 * FUNCTION: Receives and queues TCP data
204 * IPPacket = Pointer to an IP packet that was received
206 * This is the low level interface for receiving TCP data
209 TI_DbgPrint(DEBUG_TCP
,("Sending packet %d (%d) to oskit\n",
211 IPPacket
->HeaderSize
));
213 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
215 OskitTCPReceiveDatagram( IPPacket
->Header
,
217 IPPacket
->HeaderSize
);
221 TcpipRecursiveMutexLeave( &TCPLock
);
225 int TCPSocketState( void *ClientData
,
227 void *WhichConnection
,
230 int TCPPacketSend( void *ClientData
,
234 POSK_IFADDR
TCPFindInterface( void *ClientData
,
237 OSK_SOCKADDR
*ReqAddr
);
239 void *TCPMalloc( void *ClientData
,
240 OSK_UINT bytes
, OSK_PCHAR file
, OSK_UINT line
);
241 void TCPFree( void *ClientData
,
242 void *data
, OSK_PCHAR file
, OSK_UINT line
);
244 int TCPSleep( void *ClientData
, void *token
, int priority
, char *msg
,
247 void TCPWakeup( void *ClientData
, void *token
);
249 OSKITTCP_EVENT_HANDLERS EventHandlers
= {
250 NULL
, /* Client Data */
251 TCPSocketState
, /* SocketState */
252 TCPPacketSend
, /* PacketSend */
253 TCPFindInterface
, /* FindInterface */
254 TCPMalloc
, /* Malloc */
256 TCPSleep
, /* Sleep */
257 TCPWakeup
/* Wakeup */
260 NTSTATUS
TCPStartup(VOID
)
262 * FUNCTION: Initializes the TCP subsystem
264 * Status of operation
267 TcpipRecursiveMutexInit( &TCPLock
);
268 ExInitializeFastMutex( &SleepingThreadsLock
);
269 InitializeListHead( &SleepingThreadsList
);
270 InitializeListHead( &SignalledConnections
);
272 PortsStartup( &TCPPorts
, 1, 0xfffe );
274 RegisterOskitTCPEventHandlers( &EventHandlers
);
277 /* Register this protocol with IP layer */
278 IPRegisterProtocol(IPPROTO_TCP
, TCPReceive
);
280 ExInitializeNPagedLookasideList(
281 &TCPSegmentList
, /* Lookaside list */
282 NULL
, /* Allocate routine */
283 NULL
, /* Free routine */
285 sizeof(TCP_SEGMENT
), /* Size of each entry */
286 TAG('T','C','P','S'), /* Tag */
289 TCPInitialized
= TRUE
;
291 return STATUS_SUCCESS
;
295 NTSTATUS
TCPShutdown(VOID
)
297 * FUNCTION: Shuts down the TCP subsystem
299 * Status of operation
303 return STATUS_SUCCESS
;
305 /* Deregister this protocol with IP layer */
306 IPRegisterProtocol(IPPROTO_TCP
, NULL
);
308 ExDeleteNPagedLookasideList(&TCPSegmentList
);
310 TCPInitialized
= FALSE
;
314 PortsShutdown( &TCPPorts
);
316 return STATUS_SUCCESS
;
319 NTSTATUS
TCPTranslateError( int OskitError
) {
320 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
322 switch( OskitError
) {
323 case 0: Status
= STATUS_SUCCESS
; break;
324 case OSK_EADDRNOTAVAIL
:
325 case OSK_EAFNOSUPPORT
: Status
= STATUS_INVALID_CONNECTION
; break;
326 case OSK_ECONNREFUSED
:
327 case OSK_ECONNRESET
: Status
= STATUS_REMOTE_NOT_LISTENING
; break;
328 case OSK_EINPROGRESS
:
329 case OSK_EAGAIN
: Status
= STATUS_PENDING
; break;
330 default: Status
= STATUS_INVALID_CONNECTION
; break;
333 TI_DbgPrint(DEBUG_TCP
,("Error %d -> %x\n", OskitError
, Status
));
339 ( PCONNECTION_ENDPOINT Connection
,
340 PTDI_CONNECTION_INFORMATION ConnInfo
) {
342 SOCKADDR_IN AddressToConnect
;
343 PIP_ADDRESS LocalAddress
;
346 TI_DbgPrint(DEBUG_TCP
,("Called\n"));
348 Status
= AddrBuildAddress
349 ((PTA_ADDRESS
)ConnInfo
->LocalAddress
,
353 AddressToBind
.sin_family
= AF_INET
;
354 memcpy( &AddressToBind
.sin_addr
,
355 &LocalAddress
->Address
.IPv4Address
,
356 sizeof(AddressToBind
.sin_addr
) );
357 AddressToBind
.sin_port
= LocalPort
;
359 Status
= OskitTCPBind( Connection
->SocketContext
,
362 sizeof(AddressToBind
));
364 TI_DbgPrint(DEBUG_TCP
,("Leaving %x\n", Status
));
371 ( PCONNECTION_ENDPOINT Connection
,
372 PTDI_CONNECTION_INFORMATION ConnInfo
,
373 PTDI_CONNECTION_INFORMATION ReturnInfo
,
374 PTCP_COMPLETION_ROUTINE Complete
,
377 SOCKADDR_IN AddressToConnect
= { 0 }, AddressToBind
= { 0 };
378 IP_ADDRESS RemoteAddress
;
382 DbgPrint("TCPConnect: Called\n");
384 Bucket
= ExAllocatePool( NonPagedPool
, sizeof(*Bucket
) );
385 if( !Bucket
) return STATUS_NO_MEMORY
;
387 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
389 /* Freed in TCPSocketState */
390 Bucket
->Request
.RequestNotifyObject
= (PVOID
)Complete
;
391 Bucket
->Request
.RequestContext
= Context
;
393 InsertHeadList( &Connection
->ConnectRequest
, &Bucket
->Entry
);
395 Status
= AddrBuildAddress
396 ((PTRANSPORT_ADDRESS
)ConnInfo
->RemoteAddress
,
400 DbgPrint("Connecting to address %x:%x\n",
401 RemoteAddress
.Address
.IPv4Address
,
404 if (!NT_SUCCESS(Status
)) {
405 TI_DbgPrint(DEBUG_TCP
, ("Could not AddrBuildAddress in TCPConnect\n"));
409 AddressToConnect
.sin_family
= AF_INET
;
410 AddressToBind
= AddressToConnect
;
412 OskitTCPBind( Connection
->SocketContext
,
415 sizeof(AddressToBind
) );
417 memcpy( &AddressToConnect
.sin_addr
,
418 &RemoteAddress
.Address
.IPv4Address
,
419 sizeof(AddressToConnect
.sin_addr
) );
420 AddressToConnect
.sin_port
= RemotePort
;
422 Status
= TCPTranslateError
423 ( OskitTCPConnect( Connection
->SocketContext
,
426 sizeof(AddressToConnect
) ) );
428 TcpipRecursiveMutexLeave( &TCPLock
);
430 if( Status
== OSK_EINPROGRESS
|| Status
== STATUS_SUCCESS
)
431 return STATUS_PENDING
;
436 NTSTATUS TCPDisconnect
437 ( PCONNECTION_ENDPOINT Connection
,
439 PTDI_CONNECTION_INFORMATION ConnInfo
,
440 PTDI_CONNECTION_INFORMATION ReturnInfo
,
441 PTCP_COMPLETION_ROUTINE Complete
,
445 TI_DbgPrint(DEBUG_TCP
,("started\n"));
447 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
449 switch( Flags
& (TDI_DISCONNECT_ABORT
| TDI_DISCONNECT_RELEASE
) ) {
451 case TDI_DISCONNECT_ABORT
:
455 case TDI_DISCONNECT_ABORT
| TDI_DISCONNECT_RELEASE
:
459 case TDI_DISCONNECT_RELEASE
:
464 Status
= TCPTranslateError
465 ( OskitTCPShutdown( Connection
->SocketContext
, Flags
) );
467 TcpipRecursiveMutexLeave( &TCPLock
);
469 TI_DbgPrint(DEBUG_TCP
,("finished %x\n", Status
));
475 ( PCONNECTION_ENDPOINT Connection
) {
478 TI_DbgPrint(DEBUG_TCP
,("TCPClose started\n"));
480 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
482 Status
= TCPTranslateError( OskitTCPClose( Connection
->SocketContext
) );
484 if( Connection
->Signalled
)
485 RemoveEntryList( &Connection
->SignalList
);
487 TcpipRecursiveMutexLeave( &TCPLock
);
489 TI_DbgPrint(DEBUG_TCP
,("TCPClose finished %x\n", Status
));
495 ( PCONNECTION_ENDPOINT Connection
,
497 PTCP_COMPLETION_ROUTINE Complete
,
501 TI_DbgPrint(DEBUG_TCP
,("TCPListen started\n"));
503 TI_DbgPrint(DEBUG_TCP
,("Connection->SocketContext %x\n",
504 Connection
->SocketContext
));
507 ASSERT_KM_POINTER(Connection
->SocketContext
);
509 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
511 Status
= TCPTranslateError( OskitTCPListen( Connection
->SocketContext
,
514 TcpipRecursiveMutexLeave( &TCPLock
);
516 TI_DbgPrint(DEBUG_TCP
,("TCPListen finished %x\n", Status
));
522 ( PTDI_REQUEST Request
,
523 VOID
**NewSocketContext
) {
526 TI_DbgPrint(DEBUG_TCP
,("TCPAccept started\n"));
527 Status
= STATUS_UNSUCCESSFUL
;
528 TI_DbgPrint(DEBUG_TCP
,("TCPAccept finished %x\n", Status
));
532 NTSTATUS TCPReceiveData
533 ( PCONNECTION_ENDPOINT Connection
,
536 PULONG BytesReceived
,
538 PTCP_COMPLETION_ROUTINE Complete
,
541 UINT DataLen
, Received
= 0;
545 TI_DbgPrint(DEBUG_TCP
,("Called for %d bytes\n", ReceiveLength
));
547 ASSERT_KM_POINTER(Connection
->SocketContext
);
549 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
551 NdisQueryBuffer( Buffer
, &DataBuffer
, &DataLen
);
553 TI_DbgPrint(DEBUG_TCP
,("TCP>|< Got an MDL %x (%x:%d)\n", Buffer
, DataBuffer
, DataLen
));
555 Status
= TCPTranslateError
557 ( Connection
->SocketContext
,
563 TI_DbgPrint(DEBUG_TCP
,("OskitTCPReceive: %x, %d\n", Status
, Received
));
565 /* Keep this request around ... there was no data yet */
566 if( Status
== STATUS_PENDING
) {
567 /* Freed in TCPSocketState */
568 Bucket
= ExAllocatePool( NonPagedPool
, sizeof(*Bucket
) );
570 TI_DbgPrint(DEBUG_TCP
,("Failed to allocate bucket\n"));
571 TcpipRecursiveMutexLeave( &TCPLock
);
572 return STATUS_NO_MEMORY
;
575 Bucket
->Request
.RequestNotifyObject
= Complete
;
576 Bucket
->Request
.RequestContext
= Context
;
579 InsertHeadList( &Connection
->ReceiveRequest
, &Bucket
->Entry
);
580 Status
= STATUS_PENDING
;
581 TI_DbgPrint(DEBUG_TCP
,("Queued read irp\n"));
583 TI_DbgPrint(DEBUG_TCP
,("Got status %x, bytes %d\n", Status
, Received
));
584 *BytesReceived
= Received
;
587 TcpipRecursiveMutexLeave( &TCPLock
);
589 TI_DbgPrint(DEBUG_TCP
,("Status %x\n", Status
));
595 ( PCONNECTION_ENDPOINT Connection
,
602 ASSERT_KM_POINTER(Connection
->SocketContext
);
604 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
606 TI_DbgPrint(DEBUG_TCP
,("Connection = %x\n", Connection
));
607 TI_DbgPrint(DEBUG_TCP
,("Connection->SocketContext = %x\n",
608 Connection
->SocketContext
));
610 Status
= OskitTCPSend( Connection
->SocketContext
,
611 BufferData
, PacketSize
, (PUINT
)DataUsed
, 0 );
613 TcpipRecursiveMutexLeave( &TCPLock
);
618 VOID
TCPTimeout(VOID
) {
619 static int Times
= 0;
620 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
621 if( (Times
++ % 5) == 0 ) {
625 TcpipRecursiveMutexLeave( &TCPLock
);
628 UINT
TCPAllocatePort( UINT HintPort
) {
630 if( AllocatePort( &TCPPorts
, HintPort
) ) return HintPort
;
631 else return (UINT
)-1;
632 } else return AllocatePortFromRange( &TCPPorts
, 1024, 5000 );
635 VOID
TCPFreePort( UINT Port
) {
636 DeallocatePort( &TCPPorts
, Port
);