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"
26 NPAGED_LOOKASIDE_LIST TdiBucketLookasideList
;
29 DisconnectTimeoutDpc(PKDPC Dpc
,
30 PVOID DeferredContext
,
31 PVOID SystemArgument1
,
32 PVOID SystemArgument2
)
34 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)DeferredContext
;
38 LockObjectAtDpcLevel(Connection
);
40 /* We timed out waiting for pending sends so force it to shutdown */
41 TCPTranslateError(LibTCPShutdown(Connection
, 0, 1));
43 while (!IsListEmpty(&Connection
->SendRequest
))
45 Entry
= RemoveHeadList(&Connection
->SendRequest
);
47 Bucket
= CONTAINING_RECORD(Entry
, TDI_BUCKET
, Entry
);
49 Bucket
->Information
= 0;
50 Bucket
->Status
= STATUS_FILE_CLOSED
;
52 CompleteBucket(Connection
, Bucket
, FALSE
);
55 while (!IsListEmpty(&Connection
->ShutdownRequest
))
57 Entry
= RemoveHeadList( &Connection
->ShutdownRequest
);
59 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
61 Bucket
->Status
= STATUS_TIMEOUT
;
62 Bucket
->Information
= 0;
64 CompleteBucket(Connection
, Bucket
, FALSE
);
67 UnlockObjectFromDpcLevel(Connection
);
69 DereferenceObject(Connection
);
72 VOID
ConnectionFree(PVOID Object
)
74 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)Object
;
77 TI_DbgPrint(DEBUG_TCP
, ("Freeing TCP Endpoint\n"));
79 TcpipAcquireSpinLock(&ConnectionEndpointListLock
, &OldIrql
);
80 RemoveEntryList(&Connection
->ListEntry
);
81 TcpipReleaseSpinLock(&ConnectionEndpointListLock
, OldIrql
);
83 ExFreePoolWithTag( Connection
, CONN_ENDPT_TAG
);
86 PCONNECTION_ENDPOINT
TCPAllocateConnectionEndpoint( PVOID ClientContext
)
88 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)
89 ExAllocatePoolWithTag(NonPagedPool
, sizeof(CONNECTION_ENDPOINT
), CONN_ENDPT_TAG
);
94 TI_DbgPrint(DEBUG_CPOINT
, ("Connection point file object allocated at (0x%X).\n", Connection
));
96 RtlZeroMemory(Connection
, sizeof(CONNECTION_ENDPOINT
));
98 /* Initialize spin lock that protects the connection endpoint file object */
99 KeInitializeSpinLock(&Connection
->Lock
);
100 InitializeListHead(&Connection
->ConnectRequest
);
101 InitializeListHead(&Connection
->ListenRequest
);
102 InitializeListHead(&Connection
->ReceiveRequest
);
103 InitializeListHead(&Connection
->SendRequest
);
104 InitializeListHead(&Connection
->ShutdownRequest
);
105 InitializeListHead(&Connection
->PacketQueue
);
107 /* Initialize disconnect timer */
108 KeInitializeTimer(&Connection
->DisconnectTimer
);
109 KeInitializeDpc(&Connection
->DisconnectDpc
, DisconnectTimeoutDpc
, Connection
);
111 /* Save client context pointer */
112 Connection
->ClientContext
= ClientContext
;
114 Connection
->RefCount
= 1;
115 Connection
->Free
= ConnectionFree
;
117 /* Add connection endpoint to global list */
118 ExInterlockedInsertTailList(&ConnectionEndpointListHead
,
119 &Connection
->ListEntry
,
120 &ConnectionEndpointListLock
);
125 NTSTATUS
TCPSocket( PCONNECTION_ENDPOINT Connection
,
126 UINT Family
, UINT Type
, UINT Proto
)
131 LockObject(Connection
, &OldIrql
);
133 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSocket] Called: Connection %x, Family %d, Type %d, "
134 "Proto %d, sizeof(CONNECTION_ENDPOINT) = %d\n",
135 Connection
, Family
, Type
, Proto
, sizeof(CONNECTION_ENDPOINT
)));
137 Connection
->SocketContext
= LibTCPSocket(Connection
);
138 if (Connection
->SocketContext
)
139 Status
= STATUS_SUCCESS
;
141 Status
= STATUS_INSUFFICIENT_RESOURCES
;
143 UnlockObject(Connection
, OldIrql
);
145 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSocket] Leaving. Status = 0x%x\n", Status
));
150 NTSTATUS
TCPClose( PCONNECTION_ENDPOINT Connection
)
154 LockObject(Connection
, &OldIrql
);
156 FlushAllQueues(Connection
, STATUS_CANCELLED
);
158 LibTCPClose(Connection
, FALSE
, TRUE
);
160 UnlockObject(Connection
, OldIrql
);
162 DereferenceObject(Connection
);
164 return STATUS_SUCCESS
;
167 VOID
TCPReceive(PIP_INTERFACE Interface
, PIP_PACKET IPPacket
)
169 * FUNCTION: Receives and queues TCP data
171 * IPPacket = Pointer to an IP packet that was received
173 * This is the low level interface for receiving TCP data
176 TI_DbgPrint(DEBUG_TCP
,("Sending packet %d (%d) to lwIP\n",
178 IPPacket
->HeaderSize
));
180 LibIPInsertPacket(Interface
->TCPContext
, IPPacket
->Header
, IPPacket
->TotalSize
);
183 NTSTATUS
TCPStartup(VOID
)
185 * FUNCTION: Initializes the TCP subsystem
187 * Status of operation
192 Status
= PortsStartup( &TCPPorts
, 1, 0xfffe );
193 if (!NT_SUCCESS(Status
))
198 ExInitializeNPagedLookasideList(&TdiBucketLookasideList
,
206 /* Initialize our IP library */
209 /* Register this protocol with IP layer */
210 IPRegisterProtocol(IPPROTO_TCP
, TCPReceive
);
212 TCPInitialized
= TRUE
;
214 return STATUS_SUCCESS
;
218 NTSTATUS
TCPShutdown(VOID
)
220 * FUNCTION: Shuts down the TCP subsystem
222 * Status of operation
226 return STATUS_SUCCESS
;
228 ExDeleteNPagedLookasideList(&TdiBucketLookasideList
);
232 /* Deregister this protocol with IP layer */
233 IPRegisterProtocol(IPPROTO_TCP
, NULL
);
235 TCPInitialized
= FALSE
;
237 PortsShutdown( &TCPPorts
);
239 return STATUS_SUCCESS
;
242 NTSTATUS
TCPTranslateError(const err_t err
)
248 case ERR_OK
: Status
= STATUS_SUCCESS
; return Status
; //0
249 case ERR_MEM
: Status
= STATUS_INSUFFICIENT_RESOURCES
; break; //-1
250 case ERR_BUF
: Status
= STATUS_BUFFER_TOO_SMALL
; break; //-2
251 case ERR_TIMEOUT
: Status
= STATUS_TIMEOUT
; break; // -3
252 case ERR_RTE
: Status
= STATUS_NETWORK_UNREACHABLE
; break; //-4
253 case ERR_INPROGRESS
: Status
= STATUS_PENDING
; return Status
; //-5
254 case ERR_VAL
: Status
= STATUS_INVALID_PARAMETER
; break; //-6
255 case ERR_WOULDBLOCK
: Status
= STATUS_CANT_WAIT
; break; //-7
256 case ERR_USE
: Status
= STATUS_ADDRESS_ALREADY_EXISTS
; break; //-8
257 case ERR_ISCONN
: Status
= STATUS_UNSUCCESSFUL
; break; //-9 (FIXME)
258 case ERR_ABRT
: Status
= STATUS_LOCAL_DISCONNECT
; break; //-10
259 case ERR_RST
: Status
= STATUS_REMOTE_DISCONNECT
; break; //-11
260 case ERR_CLSD
: Status
= STATUS_FILE_CLOSED
; break; //-12
261 case ERR_CONN
: Status
= STATUS_INVALID_CONNECTION
; break; //-13
262 case ERR_ARG
: Status
= STATUS_INVALID_PARAMETER
; break; //-14
263 case ERR_IF
: Status
= STATUS_UNEXPECTED_NETWORK_ERROR
; break; //-15
265 DbgPrint("Invalid error value: %d\n", err
);
267 Status
= STATUS_UNSUCCESSFUL
;
271 DbgPrint("TCP operation failed: 0x%x (%d)\n", Status
, err
);
277 ( PCONNECTION_ENDPOINT Connection
,
278 PTDI_CONNECTION_INFORMATION ConnInfo
,
279 PTDI_CONNECTION_INFORMATION ReturnInfo
,
280 PTCP_COMPLETION_ROUTINE Complete
,
284 struct ip_addr bindaddr
, connaddr
;
285 IP_ADDRESS RemoteAddress
;
287 TA_IP_ADDRESS LocalAddress
;
289 PNEIGHBOR_CACHE_ENTRY NCE
;
292 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPConnect] Called\n"));
294 Status
= AddrBuildAddress
295 ((PTRANSPORT_ADDRESS
)ConnInfo
->RemoteAddress
,
299 if (!NT_SUCCESS(Status
))
301 TI_DbgPrint(DEBUG_TCP
, ("Could not AddrBuildAddress in TCPConnect\n"));
305 /* Freed in TCPSocketState */
306 TI_DbgPrint(DEBUG_TCP
,
307 ("Connecting to address %x:%x\n",
308 RemoteAddress
.Address
.IPv4Address
,
311 LockObject(Connection
, &OldIrql
);
313 if (!Connection
->AddressFile
)
315 UnlockObject(Connection
, OldIrql
);
316 return STATUS_INVALID_PARAMETER
;
319 if (AddrIsUnspecified(&Connection
->AddressFile
->Address
))
321 if (!(NCE
= RouteGetRouteToDestination(&RemoteAddress
)))
323 UnlockObject(Connection
, OldIrql
);
324 return STATUS_NETWORK_UNREACHABLE
;
327 bindaddr
.addr
= NCE
->Interface
->Unicast
.Address
.IPv4Address
;
331 bindaddr
.addr
= Connection
->AddressFile
->Address
.Address
.IPv4Address
;
334 Status
= TCPTranslateError(LibTCPBind(Connection
,
336 Connection
->AddressFile
->Port
));
338 if (NT_SUCCESS(Status
))
340 /* Check if we had an unspecified port */
341 if (!Connection
->AddressFile
->Port
)
343 /* We did, so we need to copy back the port */
344 Status
= TCPGetSockAddress(Connection
, (PTRANSPORT_ADDRESS
)&LocalAddress
, FALSE
);
345 if (NT_SUCCESS(Status
))
347 /* Allocate the port in the port bitmap */
348 Connection
->AddressFile
->Port
= TCPAllocatePort(LocalAddress
.Address
[0].Address
[0].sin_port
);
350 /* This should never fail */
351 ASSERT(Connection
->AddressFile
->Port
!= 0xFFFF);
355 if (NT_SUCCESS(Status
))
357 connaddr
.addr
= RemoteAddress
.Address
.IPv4Address
;
359 Bucket
= ExAllocateFromNPagedLookasideList(&TdiBucketLookasideList
);
362 UnlockObject(Connection
, OldIrql
);
363 return STATUS_NO_MEMORY
;
366 Bucket
->Request
.RequestNotifyObject
= (PVOID
)Complete
;
367 Bucket
->Request
.RequestContext
= Context
;
369 InsertTailList( &Connection
->ConnectRequest
, &Bucket
->Entry
);
371 Status
= TCPTranslateError(LibTCPConnect(Connection
,
377 UnlockObject(Connection
, OldIrql
);
379 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPConnect] Leaving. Status = 0x%x\n", Status
));
384 NTSTATUS TCPDisconnect
385 ( PCONNECTION_ENDPOINT Connection
,
387 PLARGE_INTEGER Timeout
,
388 PTDI_CONNECTION_INFORMATION ConnInfo
,
389 PTDI_CONNECTION_INFORMATION ReturnInfo
,
390 PTCP_COMPLETION_ROUTINE Complete
,
393 NTSTATUS Status
= STATUS_INVALID_PARAMETER
;
396 LARGE_INTEGER ActualTimeout
;
398 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPDisconnect] Called\n"));
400 LockObject(Connection
, &OldIrql
);
402 if (Connection
->SocketContext
)
404 if (Flags
& TDI_DISCONNECT_RELEASE
)
406 if (IsListEmpty(&Connection
->SendRequest
))
408 Status
= TCPTranslateError(LibTCPShutdown(Connection
, 0, 1));
410 else if (Timeout
&& Timeout
->QuadPart
== 0)
412 FlushSendQueue(Connection
, STATUS_FILE_CLOSED
, FALSE
);
413 TCPTranslateError(LibTCPShutdown(Connection
, 0, 1));
414 Status
= STATUS_TIMEOUT
;
418 /* Use the timeout specified or 1 second if none was specified */
421 ActualTimeout
= *Timeout
;
425 ActualTimeout
.QuadPart
= -1000000;
428 /* We couldn't complete the request now because we need to wait for outstanding I/O */
429 Bucket
= ExAllocateFromNPagedLookasideList(&TdiBucketLookasideList
);
432 UnlockObject(Connection
, OldIrql
);
433 return STATUS_NO_MEMORY
;
436 Bucket
->Request
.RequestNotifyObject
= (PVOID
)Complete
;
437 Bucket
->Request
.RequestContext
= Context
;
439 InsertTailList(&Connection
->ShutdownRequest
, &Bucket
->Entry
);
441 ReferenceObject(Connection
);
442 if (KeCancelTimer(&Connection
->DisconnectTimer
))
444 DereferenceObject(Connection
);
446 KeSetTimer(&Connection
->DisconnectTimer
, ActualTimeout
, &Connection
->DisconnectDpc
);
448 Status
= STATUS_PENDING
;
452 if ((Flags
& TDI_DISCONNECT_ABORT
) || !Flags
)
454 FlushReceiveQueue(Connection
, STATUS_FILE_CLOSED
, FALSE
);
455 FlushSendQueue(Connection
, STATUS_FILE_CLOSED
, FALSE
);
456 FlushShutdownQueue(Connection
, STATUS_FILE_CLOSED
, FALSE
);
457 Status
= TCPTranslateError(LibTCPShutdown(Connection
, 1, 1));
462 /* We already got closed by the other side so just return success */
463 Status
= STATUS_SUCCESS
;
466 UnlockObject(Connection
, OldIrql
);
468 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPDisconnect] Leaving. Status = 0x%x\n", Status
));
473 NTSTATUS TCPReceiveData
474 ( PCONNECTION_ENDPOINT Connection
,
477 PULONG BytesReceived
,
479 PTCP_COMPLETION_ROUTINE Complete
,
484 UINT DataLen
, Received
;
487 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPReceiveData] Called for %d bytes (on socket %x)\n",
488 ReceiveLength
, Connection
->SocketContext
));
490 NdisQueryBuffer(Buffer
, &DataBuffer
, &DataLen
);
492 Status
= LibTCPGetDataFromConnectionQueue(Connection
, DataBuffer
, DataLen
, &Received
);
494 if (Status
== STATUS_PENDING
)
496 Bucket
= ExAllocateFromNPagedLookasideList(&TdiBucketLookasideList
);
499 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPReceiveData] Failed to allocate bucket\n"));
501 return STATUS_NO_MEMORY
;
504 Bucket
->Request
.RequestNotifyObject
= Complete
;
505 Bucket
->Request
.RequestContext
= Context
;
507 ExInterlockedInsertTailList( &Connection
->ReceiveRequest
, &Bucket
->Entry
, &Connection
->Lock
);
508 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPReceiveData] Queued read irp\n"));
510 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPReceiveData] Leaving. Status = STATUS_PENDING\n"));
512 (*BytesReceived
) = 0;
516 (*BytesReceived
) = Received
;
523 ( PCONNECTION_ENDPOINT Connection
,
528 PTCP_COMPLETION_ROUTINE Complete
,
535 LockObject(Connection
, &OldIrql
);
537 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSendData] Called for %d bytes (on socket %x)\n",
538 SendLength
, Connection
->SocketContext
));
540 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSendData] Connection = %x\n", Connection
));
541 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSendData] Connection->SocketContext = %x\n",
542 Connection
->SocketContext
));
544 Status
= TCPTranslateError(LibTCPSend(Connection
,
549 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSendData] Send: %x, %d\n", Status
, SendLength
));
551 /* Keep this request around ... there was no data yet */
552 if (Status
== STATUS_PENDING
)
554 /* Freed in TCPSocketState */
555 Bucket
= ExAllocateFromNPagedLookasideList(&TdiBucketLookasideList
);
558 UnlockObject(Connection
, OldIrql
);
559 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSendData] Failed to allocate bucket\n"));
560 return STATUS_NO_MEMORY
;
563 Bucket
->Request
.RequestNotifyObject
= Complete
;
564 Bucket
->Request
.RequestContext
= Context
;
567 InsertTailList( &Connection
->SendRequest
, &Bucket
->Entry
);
568 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPSendData] Queued write irp\n"));
570 else if (Status
== STATUS_SUCCESS
)
572 *BytesSent
= SendLength
;
579 UnlockObject(Connection
, OldIrql
);
581 TI_DbgPrint(DEBUG_TCP
, ("[IP, TCPSendData] Leaving. Status = %x\n", Status
));
586 UINT
TCPAllocatePort(const UINT HintPort
)
590 if (AllocatePort(&TCPPorts
, HintPort
))
594 TI_DbgPrint(MID_TRACE
,("We got a hint port but couldn't allocate it\n"));
599 return AllocatePortFromRange( &TCPPorts
, 1024, 5000 );
602 VOID
TCPFreePort(const UINT Port
)
604 DeallocatePort(&TCPPorts
, Port
);
607 NTSTATUS TCPGetSockAddress
608 ( PCONNECTION_ENDPOINT Connection
,
609 PTRANSPORT_ADDRESS Address
,
612 PTA_IP_ADDRESS AddressIP
= (PTA_IP_ADDRESS
)Address
;
613 struct ip_addr ipaddr
;
617 AddressIP
->TAAddressCount
= 1;
618 AddressIP
->Address
[0].AddressLength
= TDI_ADDRESS_LENGTH_IP
;
619 AddressIP
->Address
[0].AddressType
= TDI_ADDRESS_TYPE_IP
;
621 LockObject(Connection
, &OldIrql
);
625 Status
= TCPTranslateError(LibTCPGetPeerName(Connection
->SocketContext
,
627 &AddressIP
->Address
[0].Address
[0].sin_port
));
631 Status
= TCPTranslateError(LibTCPGetHostName(Connection
->SocketContext
,
633 &AddressIP
->Address
[0].Address
[0].sin_port
));
636 UnlockObject(Connection
, OldIrql
);
638 AddressIP
->Address
[0].Address
[0].in_addr
= ipaddr
.addr
;
640 RtlZeroMemory(&AddressIP
->Address
[0].Address
[0].sin_zero
,
641 sizeof(AddressIP
->Address
[0].Address
[0].sin_zero
));
646 BOOLEAN
TCPRemoveIRP( PCONNECTION_ENDPOINT Endpoint
, PIRP Irp
)
649 PLIST_ENTRY ListHead
[5];
653 BOOLEAN Found
= FALSE
;
655 ListHead
[0] = &Endpoint
->SendRequest
;
656 ListHead
[1] = &Endpoint
->ReceiveRequest
;
657 ListHead
[2] = &Endpoint
->ConnectRequest
;
658 ListHead
[3] = &Endpoint
->ListenRequest
;
659 ListHead
[4] = &Endpoint
->ShutdownRequest
;
661 LockObject(Endpoint
, &OldIrql
);
663 for( i
= 0; i
< 5; i
++ )
665 for( Entry
= ListHead
[i
]->Flink
;
666 Entry
!= ListHead
[i
];
667 Entry
= Entry
->Flink
)
669 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
670 if( Bucket
->Request
.RequestContext
== Irp
)
672 RemoveEntryList( &Bucket
->Entry
);
673 ExFreeToNPagedLookasideList(&TdiBucketLookasideList
, Bucket
);
680 UnlockObject(Endpoint
, OldIrql
);