2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: transport/tcp/event.c
5 * PURPOSE: Transmission Control Protocol
6 * PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org)
13 #include "lwip/pbuf.h"
19 static const char * const tcp_state_str
[] = {
33 extern NPAGED_LOOKASIDE_LIST TdiBucketLookasideList
;
37 BucketCompletionWorker(PVOID Context
)
39 PTDI_BUCKET Bucket
= (PTDI_BUCKET
)Context
;
40 PTCP_COMPLETION_ROUTINE Complete
;
42 Complete
= (PTCP_COMPLETION_ROUTINE
)Bucket
->Request
.RequestNotifyObject
;
44 Complete(Bucket
->Request
.RequestContext
, Bucket
->Status
, Bucket
->Information
);
46 DereferenceObject(Bucket
->AssociatedEndpoint
);
48 ExFreeToNPagedLookasideList(&TdiBucketLookasideList
, Bucket
);
52 CompleteBucket(PCONNECTION_ENDPOINT Connection
, PTDI_BUCKET Bucket
, const BOOLEAN Synchronous
)
54 ReferenceObject(Connection
);
55 Bucket
->AssociatedEndpoint
= Connection
;
58 BucketCompletionWorker(Bucket
);
62 ChewCreate(BucketCompletionWorker
, Bucket
);
67 FlushReceiveQueue(PCONNECTION_ENDPOINT Connection
, const NTSTATUS Status
, const BOOLEAN interlocked
)
72 ReferenceObject(Connection
);
76 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->ReceiveRequest
, &Connection
->Lock
)))
78 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
80 TI_DbgPrint(DEBUG_TCP
,
81 ("Completing Receive request: %x %x\n",
82 Bucket
->Request
, Status
));
84 Bucket
->Status
= Status
;
85 Bucket
->Information
= 0;
87 CompleteBucket(Connection
, Bucket
, FALSE
);
92 while (!IsListEmpty(&Connection
->ReceiveRequest
))
94 Entry
= RemoveHeadList(&Connection
->ReceiveRequest
);
96 Bucket
= CONTAINING_RECORD(Entry
, TDI_BUCKET
, Entry
);
98 Bucket
->Information
= 0;
99 Bucket
->Status
= Status
;
101 CompleteBucket(Connection
, Bucket
, FALSE
);
105 DereferenceObject(Connection
);
109 FlushSendQueue(PCONNECTION_ENDPOINT Connection
, const NTSTATUS Status
, const BOOLEAN interlocked
)
114 ReferenceObject(Connection
);
118 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->SendRequest
, &Connection
->Lock
)))
120 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
122 TI_DbgPrint(DEBUG_TCP
,
123 ("Completing Send request: %x %x\n",
124 Bucket
->Request
, Status
));
126 Bucket
->Status
= Status
;
127 Bucket
->Information
= 0;
129 CompleteBucket(Connection
, Bucket
, FALSE
);
134 while (!IsListEmpty(&Connection
->SendRequest
))
136 Entry
= RemoveHeadList(&Connection
->SendRequest
);
138 Bucket
= CONTAINING_RECORD(Entry
, TDI_BUCKET
, Entry
);
140 Bucket
->Information
= 0;
141 Bucket
->Status
= Status
;
143 CompleteBucket(Connection
, Bucket
, FALSE
);
147 DereferenceObject(Connection
);
151 FlushShutdownQueue(PCONNECTION_ENDPOINT Connection
, const NTSTATUS Status
, const BOOLEAN interlocked
)
156 ReferenceObject(Connection
);
160 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->ShutdownRequest
, &Connection
->Lock
)))
162 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
164 Bucket
->Status
= Status
;
165 Bucket
->Information
= 0;
167 CompleteBucket(Connection
, Bucket
, FALSE
);
172 while (!IsListEmpty(&Connection
->ShutdownRequest
))
174 Entry
= RemoveHeadList(&Connection
->ShutdownRequest
);
176 Bucket
= CONTAINING_RECORD(Entry
, TDI_BUCKET
, Entry
);
178 Bucket
->Information
= 0;
179 Bucket
->Status
= Status
;
181 CompleteBucket(Connection
, Bucket
, FALSE
);
185 DereferenceObject(Connection
);
189 FlushConnectQueue(PCONNECTION_ENDPOINT Connection
, const NTSTATUS Status
)
194 ReferenceObject(Connection
);
196 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->ConnectRequest
, &Connection
->Lock
)))
198 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
200 Bucket
->Status
= Status
;
201 Bucket
->Information
= 0;
203 CompleteBucket(Connection
, Bucket
, FALSE
);
206 DereferenceObject(Connection
);
210 FlushListenQueue(PCONNECTION_ENDPOINT Connection
, const NTSTATUS Status
)
215 ReferenceObject(Connection
);
217 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->ListenRequest
, &Connection
->Lock
)))
219 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
221 Bucket
->Status
= Status
;
222 Bucket
->Information
= 0;
224 DereferenceObject(Bucket
->AssociatedEndpoint
);
225 CompleteBucket(Connection
, Bucket
, FALSE
);
228 DereferenceObject(Connection
);
232 FlushAllQueues(PCONNECTION_ENDPOINT Connection
, NTSTATUS Status
)
234 ReferenceObject(Connection
);
236 // flush receive queue
237 FlushReceiveQueue(Connection
, Status
, TRUE
);
239 /* We completed the reads successfully but we need to return failure now */
240 if (Status
== STATUS_SUCCESS
)
242 Status
= STATUS_FILE_CLOSED
;
245 // flush listen queue
246 FlushListenQueue(Connection
, Status
);
249 FlushSendQueue(Connection
, Status
, TRUE
);
251 // flush connect queue
252 FlushConnectQueue(Connection
, Status
);
254 // flush shutdown queue
255 FlushShutdownQueue(Connection
, Status
, TRUE
);
257 DereferenceObject(Connection
);
261 TCPFinEventHandler(void *arg
, const err_t err
)
263 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)arg
, LastConnection
;
264 const NTSTATUS Status
= TCPTranslateError(err
);
267 ASSERT(Connection
->AddressFile
);
268 ASSERT(err
!= ERR_OK
);
270 /* First off all, remove the PCB pointer */
271 Connection
->SocketContext
= NULL
;
273 /* Complete all outstanding requests now */
274 FlushAllQueues(Connection
, Status
);
276 LockObject(Connection
, &OldIrql
);
278 LockObjectAtDpcLevel(Connection
->AddressFile
);
280 /* Unlink this connection from the address file */
281 if (Connection
->AddressFile
->Connection
== Connection
)
283 Connection
->AddressFile
->Connection
= Connection
->Next
;
284 DereferenceObject(Connection
);
286 else if (Connection
->AddressFile
->Listener
== Connection
)
288 Connection
->AddressFile
->Listener
= NULL
;
289 DereferenceObject(Connection
);
293 LastConnection
= Connection
->AddressFile
->Connection
;
294 while (LastConnection
->Next
!= Connection
&& LastConnection
->Next
!= NULL
)
295 LastConnection
= LastConnection
->Next
;
296 if (LastConnection
->Next
== Connection
)
298 LastConnection
->Next
= Connection
->Next
;
299 DereferenceObject(Connection
);
303 UnlockObjectFromDpcLevel(Connection
->AddressFile
);
305 /* Remove the address file from this connection */
306 DereferenceObject(Connection
->AddressFile
);
307 Connection
->AddressFile
= NULL
;
309 UnlockObject(Connection
, OldIrql
);
313 TCPAcceptEventHandler(void *arg
, PTCP_PCB newpcb
)
315 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)arg
;
322 ReferenceObject(Connection
);
324 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->ListenRequest
, &Connection
->Lock
)))
326 PIO_STACK_LOCATION IrpSp
;
328 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
330 Irp
= Bucket
->Request
.RequestContext
;
331 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
333 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPAcceptEventHandler] Getting the socket\n"));
335 Status
= TCPCheckPeerForAccept(newpcb
,
336 (PTDI_REQUEST_KERNEL
)&IrpSp
->Parameters
);
338 TI_DbgPrint(DEBUG_TCP
,("Socket: Status: %x\n", Status
));
340 Bucket
->Status
= Status
;
341 Bucket
->Information
= 0;
343 if (Status
== STATUS_SUCCESS
)
345 LockObject(Bucket
->AssociatedEndpoint
, &OldIrql
);
347 /* sanity assert...this should never be in anything else but a CLOSED state */
348 ASSERT( ((PTCP_PCB
)Bucket
->AssociatedEndpoint
->SocketContext
)->state
== CLOSED
);
350 /* free socket context created in FileOpenConnection, as we're using a new one */
351 LibTCPClose(Bucket
->AssociatedEndpoint
, TRUE
, FALSE
);
353 /* free previously created socket context (we don't use it, we use newpcb) */
354 Bucket
->AssociatedEndpoint
->SocketContext
= newpcb
;
356 LibTCPAccept(newpcb
, (PTCP_PCB
)Connection
->SocketContext
, Bucket
->AssociatedEndpoint
);
358 UnlockObject(Bucket
->AssociatedEndpoint
, OldIrql
);
361 DereferenceObject(Bucket
->AssociatedEndpoint
);
363 CompleteBucket(Connection
, Bucket
, FALSE
);
366 DereferenceObject(Connection
);
370 TCPSendEventHandler(void *arg
, u16_t space
)
372 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)arg
;
380 ReferenceObject(Connection
);
382 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->SendRequest
, &Connection
->Lock
)))
385 PVOID SendBuffer
= 0;
387 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
389 Irp
= Bucket
->Request
.RequestContext
;
390 Mdl
= Irp
->MdlAddress
;
392 TI_DbgPrint(DEBUG_TCP
,
393 ("Getting the user buffer from %x\n", Mdl
));
395 NdisQueryBuffer( Mdl
, &SendBuffer
, &SendLen
);
397 TI_DbgPrint(DEBUG_TCP
,
398 ("Writing %d bytes to %x\n", SendLen
, SendBuffer
));
400 TI_DbgPrint(DEBUG_TCP
, ("Connection: %x\n", Connection
));
403 ("Connection->SocketContext: %x\n",
404 Connection
->SocketContext
));
406 Status
= TCPTranslateError(LibTCPSend(Connection
,
408 SendLen
, &BytesSent
, TRUE
));
410 TI_DbgPrint(DEBUG_TCP
,("TCP Bytes: %d\n", BytesSent
));
412 if( Status
== STATUS_PENDING
)
414 ExInterlockedInsertHeadList(&Connection
->SendRequest
,
421 TI_DbgPrint(DEBUG_TCP
,
422 ("Completing Send request: %x %x\n",
423 Bucket
->Request
, Status
));
425 Bucket
->Status
= Status
;
426 Bucket
->Information
= (Bucket
->Status
== STATUS_SUCCESS
) ? BytesSent
: 0;
428 CompleteBucket(Connection
, Bucket
, FALSE
);
432 // If we completed all outstanding send requests then finish all pending shutdown requests,
433 // cancel the timer and dereference the connection
434 if (IsListEmpty(&Connection
->SendRequest
))
436 FlushShutdownQueue(Connection
, STATUS_SUCCESS
, FALSE
);
438 if (KeCancelTimer(&Connection
->DisconnectTimer
))
440 DereferenceObject(Connection
);
444 DereferenceObject(Connection
);
448 TCPRecvEventHandler(void *arg
)
450 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)arg
;
460 ReferenceObject(Connection
);
462 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->ReceiveRequest
, &Connection
->Lock
)))
464 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
466 Irp
= Bucket
->Request
.RequestContext
;
467 Mdl
= Irp
->MdlAddress
;
469 NdisQueryBuffer( Mdl
, &RecvBuffer
, &RecvLen
);
471 Status
= LibTCPGetDataFromConnectionQueue(Connection
, RecvBuffer
, RecvLen
, &Received
);
472 if (Status
== STATUS_PENDING
)
474 ExInterlockedInsertHeadList(&Connection
->ReceiveRequest
,
480 Bucket
->Status
= Status
;
481 Bucket
->Information
= Received
;
483 CompleteBucket(Connection
, Bucket
, FALSE
);
486 DereferenceObject(Connection
);
490 TCPConnectEventHandler(void *arg
, err_t err
)
492 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)arg
;
496 ReferenceObject(Connection
);
498 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->ConnectRequest
, &Connection
->Lock
)))
501 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
503 Bucket
->Status
= TCPTranslateError(err
);
504 Bucket
->Information
= 0;
506 CompleteBucket(Connection
, Bucket
, FALSE
);
509 DereferenceObject(Connection
);