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
;
19 #include "lwip/pbuf.h"
21 #include "lwip/init.h"
22 #include "lwip/arch.h"
27 DisconnectTimeoutDpc(PKDPC Dpc
,
28 PVOID DeferredContext
,
29 PVOID SystemArgument1
,
30 PVOID SystemArgument2
)
32 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)DeferredContext
;
37 LockObjectAtDpcLevel(Connection
);
39 /* We timed out waiting for pending sends so force it to shutdown */
40 Status
= TCPTranslateError(LibTCPShutdown(Connection
, 0, 1));
42 while (!IsListEmpty(&Connection
->SendRequest
))
44 Entry
= RemoveHeadList(&Connection
->SendRequest
);
46 Bucket
= CONTAINING_RECORD(Entry
, TDI_BUCKET
, Entry
);
48 Bucket
->Information
= 0;
49 Bucket
->Status
= STATUS_FILE_CLOSED
;
51 CompleteBucket(Connection
, Bucket
, FALSE
);
54 while (!IsListEmpty(&Connection
->ShutdownRequest
))
56 Entry
= RemoveHeadList( &Connection
->ShutdownRequest
);
58 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
60 Bucket
->Status
= STATUS_TIMEOUT
;
61 Bucket
->Information
= 0;
63 CompleteBucket(Connection
, Bucket
, FALSE
);
66 UnlockObjectFromDpcLevel(Connection
);
68 DereferenceObject(Connection
);
71 VOID
ConnectionFree(PVOID Object
)
73 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)Object
;
76 TI_DbgPrint(DEBUG_TCP
, ("Freeing TCP Endpoint\n"));
78 TcpipAcquireSpinLock(&ConnectionEndpointListLock
, &OldIrql
);
79 RemoveEntryList(&Connection
->ListEntry
);
80 TcpipReleaseSpinLock(&ConnectionEndpointListLock
, OldIrql
);
82 ExFreePoolWithTag( Connection
, CONN_ENDPT_TAG
);
85 PCONNECTION_ENDPOINT
TCPAllocateConnectionEndpoint( PVOID ClientContext
)
87 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)
88 ExAllocatePoolWithTag(NonPagedPool
, sizeof(CONNECTION_ENDPOINT
),
93 TI_DbgPrint(DEBUG_CPOINT
, ("Connection point file object allocated at (0x%X).\n", Connection
));
95 RtlZeroMemory(Connection
, sizeof(CONNECTION_ENDPOINT
));
97 /* Initialize spin lock that protects the connection endpoint file object */
98 KeInitializeSpinLock(&Connection
->Lock
);
99 InitializeListHead(&Connection
->ConnectRequest
);
100 InitializeListHead(&Connection
->ListenRequest
);
101 InitializeListHead(&Connection
->ReceiveRequest
);
102 InitializeListHead(&Connection
->SendRequest
);
103 InitializeListHead(&Connection
->ShutdownRequest
);
104 InitializeListHead(&Connection
->PacketQueue
);
106 /* Initialize disconnect timer */
107 KeInitializeTimer(&Connection
->DisconnectTimer
);
108 KeInitializeDpc(&Connection
->DisconnectDpc
, DisconnectTimeoutDpc
, Connection
);
110 /* Save client context pointer */
111 Connection
->ClientContext
= ClientContext
;
113 Connection
->RefCount
= 1;
114 Connection
->Free
= ConnectionFree
;
116 /* Add connection endpoint to global list */
117 ExInterlockedInsertTailList(&ConnectionEndpointListHead
,
118 &Connection
->ListEntry
,
119 &ConnectionEndpointListLock
);
124 NTSTATUS
TCPSocket( PCONNECTION_ENDPOINT Connection
,
125 UINT Family
, UINT Type
, UINT Proto
)
130 LockObject(Connection
, &OldIrql
);
132 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSocket] Called: Connection %x, Family %d, Type %d, "
133 "Proto %d, sizeof(CONNECTION_ENDPOINT) = %d\n",
134 Connection
, Family
, Type
, Proto
, sizeof(CONNECTION_ENDPOINT
)));
136 Connection
->SocketContext
= LibTCPSocket(Connection
);
137 if (Connection
->SocketContext
)
138 Status
= STATUS_SUCCESS
;
140 Status
= STATUS_INSUFFICIENT_RESOURCES
;
142 UnlockObject(Connection
, OldIrql
);
144 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSocket] Leaving. Status = 0x%x\n", Status
));
149 NTSTATUS
TCPClose( PCONNECTION_ENDPOINT Connection
)
154 LockObject(Connection
, &OldIrql
);
156 Socket
= Connection
->SocketContext
;
158 FlushAllQueues(Connection
, STATUS_CANCELLED
);
160 LibTCPClose(Connection
, FALSE
, TRUE
);
162 UnlockObject(Connection
, OldIrql
);
164 DereferenceObject(Connection
);
166 return STATUS_SUCCESS
;
169 VOID
TCPReceive(PIP_INTERFACE Interface
, PIP_PACKET IPPacket
)
171 * FUNCTION: Receives and queues TCP data
173 * IPPacket = Pointer to an IP packet that was received
175 * This is the low level interface for receiving TCP data
178 TI_DbgPrint(DEBUG_TCP
,("Sending packet %d (%d) to lwIP\n",
180 IPPacket
->HeaderSize
));
182 LibIPInsertPacket(Interface
->TCPContext
, IPPacket
->Header
, IPPacket
->TotalSize
);
185 NTSTATUS
TCPStartup(VOID
)
187 * FUNCTION: Initializes the TCP subsystem
189 * Status of operation
194 Status
= PortsStartup( &TCPPorts
, 1, 0xfffe );
195 if (!NT_SUCCESS(Status
))
200 /* Initialize our IP library */
203 /* Register this protocol with IP layer */
204 IPRegisterProtocol(IPPROTO_TCP
, TCPReceive
);
206 TCPInitialized
= TRUE
;
208 return STATUS_SUCCESS
;
212 NTSTATUS
TCPShutdown(VOID
)
214 * FUNCTION: Shuts down the TCP subsystem
216 * Status of operation
220 return STATUS_SUCCESS
;
224 /* Deregister this protocol with IP layer */
225 IPRegisterProtocol(IPPROTO_TCP
, NULL
);
227 TCPInitialized
= FALSE
;
229 PortsShutdown( &TCPPorts
);
231 return STATUS_SUCCESS
;
234 NTSTATUS
TCPTranslateError(const err_t err
)
240 case ERR_OK
: Status
= STATUS_SUCCESS
; return Status
; //0
241 case ERR_MEM
: Status
= STATUS_INSUFFICIENT_RESOURCES
; break; //-1
242 case ERR_BUF
: Status
= STATUS_BUFFER_TOO_SMALL
; break; //-2
243 case ERR_TIMEOUT
: Status
= STATUS_TIMEOUT
; break; // -3
244 case ERR_RTE
: Status
= STATUS_NETWORK_UNREACHABLE
; break; //-4
245 case ERR_INPROGRESS
: Status
= STATUS_PENDING
; return Status
; //-5
246 case ERR_VAL
: Status
= STATUS_INVALID_PARAMETER
; break; //-6
247 case ERR_WOULDBLOCK
: Status
= STATUS_CANT_WAIT
; break; //-7
248 case ERR_USE
: Status
= STATUS_ADDRESS_ALREADY_EXISTS
; break; //-8
249 case ERR_ISCONN
: Status
= STATUS_UNSUCCESSFUL
; break; //-9 (FIXME)
250 case ERR_ABRT
: Status
= STATUS_LOCAL_DISCONNECT
; break; //-10
251 case ERR_RST
: Status
= STATUS_REMOTE_DISCONNECT
; break; //-11
252 case ERR_CLSD
: Status
= STATUS_FILE_CLOSED
; break; //-12
253 case ERR_CONN
: Status
= STATUS_INVALID_CONNECTION
; break; //-13
254 case ERR_ARG
: Status
= STATUS_INVALID_PARAMETER
; break; //-14
255 case ERR_IF
: Status
= STATUS_UNEXPECTED_NETWORK_ERROR
; break; //-15
257 DbgPrint("Invalid error value: %d\n", err
);
259 Status
= STATUS_UNSUCCESSFUL
;
263 DbgPrint("TCP operation failed: 0x%x (%d)\n", Status
, err
);
269 ( PCONNECTION_ENDPOINT Connection
,
270 PTDI_CONNECTION_INFORMATION ConnInfo
,
271 PTDI_CONNECTION_INFORMATION ReturnInfo
,
272 PTCP_COMPLETION_ROUTINE Complete
,
276 struct ip_addr bindaddr
, connaddr
;
277 IP_ADDRESS RemoteAddress
;
279 TA_IP_ADDRESS LocalAddress
;
281 PNEIGHBOR_CACHE_ENTRY NCE
;
284 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPConnect] Called\n"));
286 Status
= AddrBuildAddress
287 ((PTRANSPORT_ADDRESS
)ConnInfo
->RemoteAddress
,
291 if (!NT_SUCCESS(Status
))
293 TI_DbgPrint(DEBUG_TCP
, ("Could not AddrBuildAddress in TCPConnect\n"));
297 /* Freed in TCPSocketState */
298 TI_DbgPrint(DEBUG_TCP
,
299 ("Connecting to address %x:%x\n",
300 RemoteAddress
.Address
.IPv4Address
,
303 LockObject(Connection
, &OldIrql
);
305 if (!Connection
->AddressFile
)
307 UnlockObject(Connection
, OldIrql
);
308 return STATUS_INVALID_PARAMETER
;
311 if (AddrIsUnspecified(&Connection
->AddressFile
->Address
))
313 if (!(NCE
= RouteGetRouteToDestination(&RemoteAddress
)))
315 UnlockObject(Connection
, OldIrql
);
316 return STATUS_NETWORK_UNREACHABLE
;
319 bindaddr
.addr
= NCE
->Interface
->Unicast
.Address
.IPv4Address
;
323 bindaddr
.addr
= Connection
->AddressFile
->Address
.Address
.IPv4Address
;
326 Status
= TCPTranslateError(LibTCPBind(Connection
,
328 Connection
->AddressFile
->Port
));
330 if (NT_SUCCESS(Status
))
332 /* Check if we had an unspecified port */
333 if (!Connection
->AddressFile
->Port
)
335 /* We did, so we need to copy back the port */
336 Status
= TCPGetSockAddress(Connection
, (PTRANSPORT_ADDRESS
)&LocalAddress
, FALSE
);
337 if (NT_SUCCESS(Status
))
339 /* Allocate the port in the port bitmap */
340 Connection
->AddressFile
->Port
= TCPAllocatePort(LocalAddress
.Address
[0].Address
[0].sin_port
);
342 /* This should never fail */
343 ASSERT(Connection
->AddressFile
->Port
!= 0xFFFF);
347 if (NT_SUCCESS(Status
))
349 connaddr
.addr
= RemoteAddress
.Address
.IPv4Address
;
351 Bucket
= ExAllocatePoolWithTag( NonPagedPool
, sizeof(*Bucket
), TDI_BUCKET_TAG
);
354 UnlockObject(Connection
, OldIrql
);
355 return STATUS_NO_MEMORY
;
358 Bucket
->Request
.RequestNotifyObject
= (PVOID
)Complete
;
359 Bucket
->Request
.RequestContext
= Context
;
361 InsertTailList( &Connection
->ConnectRequest
, &Bucket
->Entry
);
363 Status
= TCPTranslateError(LibTCPConnect(Connection
,
369 UnlockObject(Connection
, OldIrql
);
371 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPConnect] Leaving. Status = 0x%x\n", Status
));
376 NTSTATUS TCPDisconnect
377 ( PCONNECTION_ENDPOINT Connection
,
379 PLARGE_INTEGER Timeout
,
380 PTDI_CONNECTION_INFORMATION ConnInfo
,
381 PTDI_CONNECTION_INFORMATION ReturnInfo
,
382 PTCP_COMPLETION_ROUTINE Complete
,
385 NTSTATUS Status
= STATUS_INVALID_PARAMETER
;
388 LARGE_INTEGER ActualTimeout
;
390 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPDisconnect] Called\n"));
392 LockObject(Connection
, &OldIrql
);
394 if (Connection
->SocketContext
)
396 if (Flags
& TDI_DISCONNECT_RELEASE
)
398 if (IsListEmpty(&Connection
->SendRequest
))
400 Status
= TCPTranslateError(LibTCPShutdown(Connection
, 0, 1));
402 else if (Timeout
&& Timeout
->QuadPart
== 0)
404 FlushSendQueue(Connection
, STATUS_FILE_CLOSED
, FALSE
);
405 TCPTranslateError(LibTCPShutdown(Connection
, 0, 1));
406 Status
= STATUS_TIMEOUT
;
410 /* Use the timeout specified or 1 second if none was specified */
413 ActualTimeout
= *Timeout
;
417 ActualTimeout
.QuadPart
= -1000000;
420 /* We couldn't complete the request now because we need to wait for outstanding I/O */
421 Bucket
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*Bucket
), TDI_BUCKET_TAG
);
424 UnlockObject(Connection
, OldIrql
);
425 return STATUS_NO_MEMORY
;
428 Bucket
->Request
.RequestNotifyObject
= (PVOID
)Complete
;
429 Bucket
->Request
.RequestContext
= Context
;
431 InsertTailList(&Connection
->ShutdownRequest
, &Bucket
->Entry
);
433 ReferenceObject(Connection
);
434 if (KeCancelTimer(&Connection
->DisconnectTimer
))
436 DereferenceObject(Connection
);
438 KeSetTimer(&Connection
->DisconnectTimer
, ActualTimeout
, &Connection
->DisconnectDpc
);
440 Status
= STATUS_PENDING
;
444 if ((Flags
& TDI_DISCONNECT_ABORT
) || !Flags
)
446 FlushReceiveQueue(Connection
, STATUS_FILE_CLOSED
, FALSE
);
447 FlushSendQueue(Connection
, STATUS_FILE_CLOSED
, FALSE
);
448 FlushShutdownQueue(Connection
, STATUS_FILE_CLOSED
, FALSE
);
449 Status
= TCPTranslateError(LibTCPShutdown(Connection
, 1, 1));
454 /* We already got closed by the other side so just return success */
455 Status
= STATUS_SUCCESS
;
458 UnlockObject(Connection
, OldIrql
);
460 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPDisconnect] Leaving. Status = 0x%x\n", Status
));
465 NTSTATUS TCPReceiveData
466 ( PCONNECTION_ENDPOINT Connection
,
469 PULONG BytesReceived
,
471 PTCP_COMPLETION_ROUTINE Complete
,
476 UINT DataLen
, Received
;
479 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPReceiveData] Called for %d bytes (on socket %x)\n",
480 ReceiveLength
, Connection
->SocketContext
));
482 NdisQueryBuffer(Buffer
, &DataBuffer
, &DataLen
);
484 Status
= LibTCPGetDataFromConnectionQueue(Connection
, DataBuffer
, DataLen
, &Received
);
486 if (Status
== STATUS_PENDING
)
489 /* Freed in TCPSocketState */
490 Bucket
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(*Bucket
), TDI_BUCKET_TAG
);
493 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPReceiveData] Failed to allocate bucket\n"));
495 return STATUS_NO_MEMORY
;
498 Bucket
->Request
.RequestNotifyObject
= Complete
;
499 Bucket
->Request
.RequestContext
= Context
;
501 ExInterlockedInsertTailList( &Connection
->ReceiveRequest
, &Bucket
->Entry
, &Connection
->Lock
);
502 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPReceiveData] Queued read irp\n"));
504 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPReceiveData] Leaving. Status = STATUS_PENDING\n"));
506 (*BytesReceived
) = 0;
510 (*BytesReceived
) = Received
;
517 ( PCONNECTION_ENDPOINT Connection
,
522 PTCP_COMPLETION_ROUTINE Complete
,
529 LockObject(Connection
, &OldIrql
);
531 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSendData] Called for %d bytes (on socket %x)\n",
532 SendLength
, Connection
->SocketContext
));
534 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSendData] Connection = %x\n", Connection
));
535 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSendData] Connection->SocketContext = %x\n",
536 Connection
->SocketContext
));
538 Status
= TCPTranslateError(LibTCPSend(Connection
,
543 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSendData] Send: %x, %d\n", Status
, SendLength
));
545 /* Keep this request around ... there was no data yet */
546 if (Status
== STATUS_PENDING
)
548 /* Freed in TCPSocketState */
549 Bucket
= ExAllocatePoolWithTag( NonPagedPool
, sizeof(*Bucket
), TDI_BUCKET_TAG
);
552 UnlockObject(Connection
, OldIrql
);
553 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSendData] Failed to allocate bucket\n"));
554 return STATUS_NO_MEMORY
;
557 Bucket
->Request
.RequestNotifyObject
= Complete
;
558 Bucket
->Request
.RequestContext
= Context
;
561 InsertTailList( &Connection
->SendRequest
, &Bucket
->Entry
);
562 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSendData] Queued write irp\n"));
564 else if (Status
== STATUS_SUCCESS
)
566 *BytesSent
= SendLength
;
573 UnlockObject(Connection
, OldIrql
);
575 TI_DbgPrint(DEBUG_TCP
, ("[IP, TCPSendData] Leaving. Status = %x\n", Status
));
580 UINT
TCPAllocatePort(const UINT HintPort
)
584 if (AllocatePort(&TCPPorts
, HintPort
))
588 TI_DbgPrint(MID_TRACE
,("We got a hint port but couldn't allocate it\n"));
593 return AllocatePortFromRange( &TCPPorts
, 1024, 5000 );
596 VOID
TCPFreePort(const UINT Port
)
598 DeallocatePort(&TCPPorts
, Port
);
601 NTSTATUS TCPGetSockAddress
602 ( PCONNECTION_ENDPOINT Connection
,
603 PTRANSPORT_ADDRESS Address
,
606 PTA_IP_ADDRESS AddressIP
= (PTA_IP_ADDRESS
)Address
;
607 struct ip_addr ipaddr
;
611 AddressIP
->TAAddressCount
= 1;
612 AddressIP
->Address
[0].AddressLength
= TDI_ADDRESS_LENGTH_IP
;
613 AddressIP
->Address
[0].AddressType
= TDI_ADDRESS_TYPE_IP
;
615 LockObject(Connection
, &OldIrql
);
619 Status
= TCPTranslateError(LibTCPGetPeerName(Connection
->SocketContext
,
621 &AddressIP
->Address
[0].Address
[0].sin_port
));
625 Status
= TCPTranslateError(LibTCPGetHostName(Connection
->SocketContext
,
627 &AddressIP
->Address
[0].Address
[0].sin_port
));
630 UnlockObject(Connection
, OldIrql
);
632 AddressIP
->Address
[0].Address
[0].in_addr
= ipaddr
.addr
;
637 BOOLEAN
TCPRemoveIRP( PCONNECTION_ENDPOINT Endpoint
, PIRP Irp
)
640 PLIST_ENTRY ListHead
[5];
644 BOOLEAN Found
= FALSE
;
646 ListHead
[0] = &Endpoint
->SendRequest
;
647 ListHead
[1] = &Endpoint
->ReceiveRequest
;
648 ListHead
[2] = &Endpoint
->ConnectRequest
;
649 ListHead
[3] = &Endpoint
->ListenRequest
;
650 ListHead
[4] = &Endpoint
->ShutdownRequest
;
652 LockObject(Endpoint
, &OldIrql
);
654 for( i
= 0; i
< 5; i
++ )
656 for( Entry
= ListHead
[i
]->Flink
;
657 Entry
!= ListHead
[i
];
658 Entry
= Entry
->Flink
)
660 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
661 if( Bucket
->Request
.RequestContext
== Irp
)
663 RemoveEntryList( &Bucket
->Entry
);
664 ExFreePoolWithTag( Bucket
, TDI_BUCKET_TAG
);
671 UnlockObject(Endpoint
, OldIrql
);