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
;
21 static VOID
HandleSignalledConnection( PCONNECTION_ENDPOINT Connection
,
23 NTSTATUS Status
= STATUS_SUCCESS
;
24 PTCP_COMPLETION_ROUTINE Complete
;
28 if( ((NewState
& SEL_CONNECT
) || (NewState
& SEL_FIN
)) &&
30 !(Connection
->State
& (SEL_CONNECT
| SEL_FIN
)) ) {
31 while( !IsListEmpty( &Connection
->ConnectRequest
) ) {
32 Connection
->State
|= NewState
& (SEL_CONNECT
| SEL_FIN
);
33 Entry
= RemoveHeadList( &Connection
->ConnectRequest
);
34 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
35 Complete
= Bucket
->Request
.RequestNotifyObject
;
36 TI_DbgPrint(DEBUG_TCP
,
37 ("Completing Connect Request %x\n", Bucket
->Request
));
38 if( NewState
& SEL_FIN
) Status
= STATUS_CONNECTION_REFUSED
;
39 Complete( Bucket
->Request
.RequestContext
, Status
, 0 );
40 /* Frees the bucket allocated in TCPConnect */
41 PoolFreeBuffer( Bucket
);
44 if( (NewState
& SEL_READ
) || (NewState
& SEL_FIN
) ) {
45 TI_DbgPrint(DEBUG_TCP
,("Readable (or closed): irp list %s\n",
46 IsListEmpty(&Connection
->ReceiveRequest
) ?
47 "empty" : "nonempty"));
49 while( !IsListEmpty( &Connection
->ReceiveRequest
) ) {
51 OSK_UINT RecvLen
= 0, Received
= 0;
52 OSK_PCHAR RecvBuffer
= 0;
56 Entry
= RemoveHeadList( &Connection
->ReceiveRequest
);
57 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
58 Complete
= Bucket
->Request
.RequestNotifyObject
;
60 TI_DbgPrint(DEBUG_TCP
,
61 ("Readable, Completing read request %x\n",
64 Irp
= Bucket
->Request
.RequestContext
;
65 Mdl
= Irp
->MdlAddress
;
67 TI_DbgPrint(DEBUG_TCP
,
68 ("Getting the user buffer from %x\n", Mdl
));
70 NdisQueryBuffer( Mdl
, &RecvBuffer
, &RecvLen
);
72 TI_DbgPrint(DEBUG_TCP
,
73 ("Reading %d bytes to %x\n", RecvLen
, RecvBuffer
));
75 if( (NewState
& SEL_FIN
) && !RecvLen
) {
76 TI_DbgPrint(DEBUG_TCP
, ("EOF From socket\n"));
77 Status
= STATUS_END_OF_FILE
;
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
,
95 TI_DbgPrint(DEBUG_TCP
,("TCP Bytes: %d\n", Received
));
97 if( Status
== STATUS_SUCCESS
&& Received
!= 0 ) {
98 TI_DbgPrint(DEBUG_TCP
,("Received %d bytes with status %x\n",
101 TI_DbgPrint(DEBUG_TCP
,
102 ("Completing Receive Request: %x\n",
105 Complete( Bucket
->Request
.RequestContext
,
106 STATUS_SUCCESS
, Received
);
107 } else if( Status
== STATUS_PENDING
||
108 (Status
== STATUS_SUCCESS
&& Received
== 0) ) {
109 InsertHeadList( &Connection
->ReceiveRequest
,
113 TI_DbgPrint(DEBUG_TCP
,
114 ("Completing Receive request: %x %x\n",
115 Bucket
->Request
, Status
));
116 Complete( Bucket
->Request
.RequestContext
, Status
, 0 );
121 Connection
->Signalled
= FALSE
;
124 VOID
DrainSignals() {
125 PCONNECTION_ENDPOINT Connection
;
126 PLIST_ENTRY ListEntry
;
128 while( !IsListEmpty( &SignalledConnections
) ) {
129 ListEntry
= RemoveHeadList( &SignalledConnections
);
130 Connection
= CONTAINING_RECORD( ListEntry
, CONNECTION_ENDPOINT
,
132 HandleSignalledConnection( Connection
, Connection
->SignalState
);
136 PCONNECTION_ENDPOINT
TCPAllocateConnectionEndpoint( PVOID ClientContext
) {
137 PCONNECTION_ENDPOINT Connection
=
138 ExAllocatePool(NonPagedPool
, sizeof(CONNECTION_ENDPOINT
));
142 TI_DbgPrint(DEBUG_CPOINT
, ("Connection point file object allocated at (0x%X).\n", Connection
));
144 RtlZeroMemory(Connection
, sizeof(CONNECTION_ENDPOINT
));
146 /* Initialize spin lock that protects the connection endpoint file object */
147 TcpipInitializeSpinLock(&Connection
->Lock
);
148 InitializeListHead(&Connection
->ConnectRequest
);
149 InitializeListHead(&Connection
->ListenRequest
);
150 InitializeListHead(&Connection
->ReceiveRequest
);
152 /* Save client context pointer */
153 Connection
->ClientContext
= ClientContext
;
158 VOID
TCPFreeConnectionEndpoint( PCONNECTION_ENDPOINT Connection
) {
159 TI_DbgPrint(MAX_TRACE
,("FIXME: Cancel all pending requests\n"));
160 /* XXX Cancel all pending requests */
161 ExFreePool( Connection
);
164 NTSTATUS
TCPSocket( PCONNECTION_ENDPOINT Connection
,
165 UINT Family
, UINT Type
, UINT Proto
) {
168 TI_DbgPrint(DEBUG_TCP
,("Called: Connection %x, Family %d, Type %d, "
170 Connection
, Family
, Type
, Proto
));
172 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
173 Status
= TCPTranslateError( OskitTCPSocket( Connection
,
174 &Connection
->SocketContext
,
179 ASSERT_KM_POINTER(Connection
->SocketContext
);
181 TI_DbgPrint(DEBUG_TCP
,("Connection->SocketContext %x\n",
182 Connection
->SocketContext
));
184 TcpipRecursiveMutexLeave( &TCPLock
);
189 VOID
TCPReceive(PIP_INTERFACE Interface
, PIP_PACKET IPPacket
)
191 * FUNCTION: Receives and queues TCP data
193 * IPPacket = Pointer to an IP packet that was received
195 * This is the low level interface for receiving TCP data
198 TI_DbgPrint(DEBUG_TCP
,("Sending packet %d (%d) to oskit\n",
200 IPPacket
->HeaderSize
));
202 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
204 OskitTCPReceiveDatagram( IPPacket
->Header
,
206 IPPacket
->HeaderSize
);
210 TcpipRecursiveMutexLeave( &TCPLock
);
214 int TCPSocketState( void *ClientData
,
216 void *WhichConnection
,
219 int TCPPacketSend( void *ClientData
,
223 POSK_IFADDR
TCPFindInterface( void *ClientData
,
226 OSK_SOCKADDR
*ReqAddr
);
228 void *TCPMalloc( void *ClientData
,
229 OSK_UINT bytes
, OSK_PCHAR file
, OSK_UINT line
);
230 void TCPFree( void *ClientData
,
231 void *data
, OSK_PCHAR file
, OSK_UINT line
);
233 int TCPSleep( void *ClientData
, void *token
, int priority
, char *msg
,
236 void TCPWakeup( void *ClientData
, void *token
);
238 OSKITTCP_EVENT_HANDLERS EventHandlers
= {
239 NULL
, /* Client Data */
240 TCPSocketState
, /* SocketState */
241 TCPPacketSend
, /* PacketSend */
242 TCPFindInterface
, /* FindInterface */
243 TCPMalloc
, /* Malloc */
245 TCPSleep
, /* Sleep */
246 TCPWakeup
/* Wakeup */
249 NTSTATUS
TCPStartup(VOID
)
251 * FUNCTION: Initializes the TCP subsystem
253 * Status of operation
256 TcpipRecursiveMutexInit( &TCPLock
);
257 ExInitializeFastMutex( &SleepingThreadsLock
);
258 InitializeListHead( &SleepingThreadsList
);
259 InitializeListHead( &SignalledConnections
);
261 RegisterOskitTCPEventHandlers( &EventHandlers
);
264 /* Register this protocol with IP layer */
265 IPRegisterProtocol(IPPROTO_TCP
, TCPReceive
);
267 ExInitializeNPagedLookasideList(
268 &TCPSegmentList
, /* Lookaside list */
269 NULL
, /* Allocate routine */
270 NULL
, /* Free routine */
272 sizeof(TCP_SEGMENT
), /* Size of each entry */
273 TAG('T','C','P','S'), /* Tag */
276 TCPInitialized
= TRUE
;
278 return STATUS_SUCCESS
;
282 NTSTATUS
TCPShutdown(VOID
)
284 * FUNCTION: Shuts down the TCP subsystem
286 * Status of operation
290 return STATUS_SUCCESS
;
292 /* Deregister this protocol with IP layer */
293 IPRegisterProtocol(IPPROTO_TCP
, NULL
);
295 ExDeleteNPagedLookasideList(&TCPSegmentList
);
297 TCPInitialized
= FALSE
;
301 return STATUS_SUCCESS
;
304 NTSTATUS
TCPTranslateError( int OskitError
) {
305 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
307 switch( OskitError
) {
308 case 0: Status
= STATUS_SUCCESS
; break;
309 case OSK_EADDRNOTAVAIL
:
310 case OSK_EAFNOSUPPORT
: Status
= STATUS_INVALID_CONNECTION
; break;
311 case OSK_ECONNREFUSED
:
312 case OSK_ECONNRESET
: Status
= STATUS_REMOTE_NOT_LISTENING
; break;
313 case OSK_EINPROGRESS
:
314 case OSK_EAGAIN
: Status
= STATUS_PENDING
; break;
315 default: Status
= STATUS_INVALID_CONNECTION
; break;
318 TI_DbgPrint(DEBUG_TCP
,("Error %d -> %x\n", OskitError
, Status
));
324 ( PCONNECTION_ENDPOINT Connection
,
325 PTDI_CONNECTION_INFORMATION ConnInfo
) {
327 SOCKADDR_IN AddressToConnect
;
328 PIP_ADDRESS LocalAddress
;
331 TI_DbgPrint(DEBUG_TCP
,("Called\n"));
333 Status
= AddrBuildAddress
334 ((PTA_ADDRESS
)ConnInfo
->LocalAddress
,
338 AddressToBind
.sin_family
= AF_INET
;
339 memcpy( &AddressToBind
.sin_addr
,
340 &LocalAddress
->Address
.IPv4Address
,
341 sizeof(AddressToBind
.sin_addr
) );
342 AddressToBind
.sin_port
= LocalPort
;
344 Status
= OskitTCPBind( Connection
->SocketContext
,
347 sizeof(AddressToBind
));
349 TI_DbgPrint(DEBUG_TCP
,("Leaving %x\n", Status
));
356 ( PCONNECTION_ENDPOINT Connection
,
357 PTDI_CONNECTION_INFORMATION ConnInfo
,
358 PTDI_CONNECTION_INFORMATION ReturnInfo
,
359 PTCP_COMPLETION_ROUTINE Complete
,
362 SOCKADDR_IN AddressToConnect
= { 0 }, AddressToBind
= { 0 };
363 IP_ADDRESS RemoteAddress
;
367 DbgPrint("TCPConnect: Called\n");
369 Bucket
= ExAllocatePool( NonPagedPool
, sizeof(*Bucket
) );
370 if( !Bucket
) return STATUS_NO_MEMORY
;
372 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
374 /* Freed in TCPSocketState */
375 Bucket
->Request
.RequestNotifyObject
= (PVOID
)Complete
;
376 Bucket
->Request
.RequestContext
= Context
;
378 InsertHeadList( &Connection
->ConnectRequest
, &Bucket
->Entry
);
380 Status
= AddrBuildAddress
381 ((PTRANSPORT_ADDRESS
)ConnInfo
->RemoteAddress
,
385 DbgPrint("Connecting to address %x:%x\n",
386 RemoteAddress
.Address
.IPv4Address
,
389 if (!NT_SUCCESS(Status
)) {
390 TI_DbgPrint(DEBUG_TCP
, ("Could not AddrBuildAddress in TCPConnect\n"));
394 AddressToConnect
.sin_family
= AF_INET
;
395 AddressToBind
= AddressToConnect
;
397 OskitTCPBind( Connection
->SocketContext
,
400 sizeof(AddressToBind
) );
402 memcpy( &AddressToConnect
.sin_addr
,
403 &RemoteAddress
.Address
.IPv4Address
,
404 sizeof(AddressToConnect
.sin_addr
) );
405 AddressToConnect
.sin_port
= RemotePort
;
407 Status
= OskitTCPConnect(Connection
->SocketContext
,
410 sizeof(AddressToConnect
));
412 TcpipRecursiveMutexLeave( &TCPLock
);
414 if( Status
== OSK_EINPROGRESS
|| Status
== STATUS_SUCCESS
)
415 return STATUS_PENDING
;
421 ( PCONNECTION_ENDPOINT Connection
) {
424 TI_DbgPrint(DEBUG_TCP
,("TCPClose started\n"));
426 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
428 Status
= TCPTranslateError( OskitTCPClose( Connection
->SocketContext
) );
430 if( Connection
->Signalled
)
431 RemoveEntryList( &Connection
->SignalList
);
433 TcpipRecursiveMutexLeave( &TCPLock
);
435 TI_DbgPrint(DEBUG_TCP
,("TCPClose finished %x\n", Status
));
441 ( PCONNECTION_ENDPOINT Connection
,
443 PTCP_COMPLETION_ROUTINE Complete
,
447 TI_DbgPrint(DEBUG_TCP
,("TCPListen started\n"));
449 TI_DbgPrint(DEBUG_TCP
,("Connection->SocketContext %x\n",
450 Connection
->SocketContext
));
453 ASSERT_KM_POINTER(Connection
->SocketContext
);
455 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
457 Status
= TCPTranslateError( OskitTCPListen( Connection
->SocketContext
,
460 TcpipRecursiveMutexLeave( &TCPLock
);
462 TI_DbgPrint(DEBUG_TCP
,("TCPListen finished %x\n", Status
));
468 ( PTDI_REQUEST Request
,
469 VOID
**NewSocketContext
) {
472 TI_DbgPrint(DEBUG_TCP
,("TCPAccept started\n"));
473 Status
= STATUS_UNSUCCESSFUL
;
474 TI_DbgPrint(DEBUG_TCP
,("TCPAccept finished %x\n", Status
));
478 NTSTATUS TCPReceiveData
479 ( PCONNECTION_ENDPOINT Connection
,
482 PULONG BytesReceived
,
484 PTCP_COMPLETION_ROUTINE Complete
,
487 UINT DataLen
, Received
= 0;
491 TI_DbgPrint(DEBUG_TCP
,("Called for %d bytes\n", ReceiveLength
));
493 ASSERT_KM_POINTER(Connection
->SocketContext
);
495 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
497 NdisQueryBuffer( Buffer
, &DataBuffer
, &DataLen
);
499 TI_DbgPrint(DEBUG_TCP
,("TCP>|< Got an MDL %x (%x:%d)\n", Buffer
, DataBuffer
, DataLen
));
501 Status
= TCPTranslateError
503 ( Connection
->SocketContext
,
509 TI_DbgPrint(DEBUG_TCP
,("OskitTCPReceive: %x, %d\n", Status
, Received
));
511 /* Keep this request around ... there was no data yet */
512 if( Status
== STATUS_PENDING
||
513 (Status
== STATUS_SUCCESS
&& Received
== 0) ) {
514 /* Freed in TCPSocketState */
515 Bucket
= ExAllocatePool( NonPagedPool
, sizeof(*Bucket
) );
517 TI_DbgPrint(DEBUG_TCP
,("Failed to allocate bucket\n"));
518 TcpipRecursiveMutexLeave( &TCPLock
);
519 return STATUS_NO_MEMORY
;
522 Bucket
->Request
.RequestNotifyObject
= Complete
;
523 Bucket
->Request
.RequestContext
= Context
;
526 InsertHeadList( &Connection
->ReceiveRequest
, &Bucket
->Entry
);
527 Status
= STATUS_PENDING
;
528 TI_DbgPrint(DEBUG_TCP
,("Queued read irp\n"));
530 TI_DbgPrint(DEBUG_TCP
,("Got status %x, bytes %d\n", Status
, Received
));
531 *BytesReceived
= Received
;
534 TcpipRecursiveMutexLeave( &TCPLock
);
536 TI_DbgPrint(DEBUG_TCP
,("Status %x\n", Status
));
542 ( PCONNECTION_ENDPOINT Connection
,
549 ASSERT_KM_POINTER(Connection
->SocketContext
);
551 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
553 TI_DbgPrint(DEBUG_TCP
,("Connection = %x\n", Connection
));
554 TI_DbgPrint(DEBUG_TCP
,("Connection->SocketContext = %x\n",
555 Connection
->SocketContext
));
557 Status
= OskitTCPSend( Connection
->SocketContext
,
558 BufferData
, PacketSize
, (PUINT
)DataUsed
, 0 );
560 TcpipRecursiveMutexLeave( &TCPLock
);
565 VOID
TCPTimeout(VOID
) {
566 static int Times
= 0;
567 TcpipRecursiveMutexEnter( &TCPLock
, TRUE
);
568 if( (Times
++ % 5) == 0 ) {
572 TcpipRecursiveMutexLeave( &TCPLock
);