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
);
269 /* Check if this was a partial socket closure */
270 if (err
== ERR_OK
&& Connection
->SocketContext
)
272 /* Just flush the receive queue and get out of here */
273 FlushReceiveQueue(Connection
, STATUS_SUCCESS
, TRUE
);
277 /* First off all, remove the PCB pointer */
278 Connection
->SocketContext
= NULL
;
280 /* Complete all outstanding requests now */
281 FlushAllQueues(Connection
, Status
);
283 LockObject(Connection
, &OldIrql
);
285 LockObjectAtDpcLevel(Connection
->AddressFile
);
287 /* Unlink this connection from the address file */
288 if (Connection
->AddressFile
->Connection
== Connection
)
290 Connection
->AddressFile
->Connection
= Connection
->Next
;
291 DereferenceObject(Connection
);
293 else if (Connection
->AddressFile
->Listener
== Connection
)
295 Connection
->AddressFile
->Listener
= NULL
;
296 DereferenceObject(Connection
);
300 LastConnection
= Connection
->AddressFile
->Connection
;
301 while (LastConnection
->Next
!= Connection
&& LastConnection
->Next
!= NULL
)
302 LastConnection
= LastConnection
->Next
;
303 if (LastConnection
->Next
== Connection
)
305 LastConnection
->Next
= Connection
->Next
;
306 DereferenceObject(Connection
);
310 UnlockObjectFromDpcLevel(Connection
->AddressFile
);
312 /* Remove the address file from this connection */
313 DereferenceObject(Connection
->AddressFile
);
314 Connection
->AddressFile
= NULL
;
316 UnlockObject(Connection
, OldIrql
);
321 TCPAcceptEventHandler(void *arg
, PTCP_PCB newpcb
)
323 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)arg
;
330 ReferenceObject(Connection
);
332 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->ListenRequest
, &Connection
->Lock
)))
334 PIO_STACK_LOCATION IrpSp
;
336 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
338 Irp
= Bucket
->Request
.RequestContext
;
339 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
341 TI_DbgPrint(DEBUG_TCP
,("[IP, TCPAcceptEventHandler] Getting the socket\n"));
343 Status
= TCPCheckPeerForAccept(newpcb
,
344 (PTDI_REQUEST_KERNEL
)&IrpSp
->Parameters
);
346 TI_DbgPrint(DEBUG_TCP
,("Socket: Status: %x\n", Status
));
348 Bucket
->Status
= Status
;
349 Bucket
->Information
= 0;
351 if (Status
== STATUS_SUCCESS
)
353 LockObject(Bucket
->AssociatedEndpoint
, &OldIrql
);
355 /* sanity assert...this should never be in anything else but a CLOSED state */
356 ASSERT( ((PTCP_PCB
)Bucket
->AssociatedEndpoint
->SocketContext
)->state
== CLOSED
);
358 /* free socket context created in FileOpenConnection, as we're using a new one */
359 LibTCPClose(Bucket
->AssociatedEndpoint
, TRUE
, FALSE
);
361 /* free previously created socket context (we don't use it, we use newpcb) */
362 Bucket
->AssociatedEndpoint
->SocketContext
= newpcb
;
364 LibTCPAccept(newpcb
, (PTCP_PCB
)Connection
->SocketContext
, Bucket
->AssociatedEndpoint
);
366 UnlockObject(Bucket
->AssociatedEndpoint
, OldIrql
);
369 DereferenceObject(Bucket
->AssociatedEndpoint
);
371 CompleteBucket(Connection
, Bucket
, FALSE
);
374 DereferenceObject(Connection
);
378 TCPSendEventHandler(void *arg
, u16_t space
)
380 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)arg
;
388 ReferenceObject(Connection
);
390 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->SendRequest
, &Connection
->Lock
)))
393 PVOID SendBuffer
= 0;
395 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
397 Irp
= Bucket
->Request
.RequestContext
;
398 Mdl
= Irp
->MdlAddress
;
400 TI_DbgPrint(DEBUG_TCP
,
401 ("Getting the user buffer from %x\n", Mdl
));
403 NdisQueryBuffer( Mdl
, &SendBuffer
, &SendLen
);
405 TI_DbgPrint(DEBUG_TCP
,
406 ("Writing %d bytes to %x\n", SendLen
, SendBuffer
));
408 TI_DbgPrint(DEBUG_TCP
, ("Connection: %x\n", Connection
));
411 ("Connection->SocketContext: %x\n",
412 Connection
->SocketContext
));
414 Status
= TCPTranslateError(LibTCPSend(Connection
,
416 SendLen
, &BytesSent
, TRUE
));
418 TI_DbgPrint(DEBUG_TCP
,("TCP Bytes: %d\n", BytesSent
));
420 if( Status
== STATUS_PENDING
)
422 ExInterlockedInsertHeadList(&Connection
->SendRequest
,
429 TI_DbgPrint(DEBUG_TCP
,
430 ("Completing Send request: %x %x\n",
431 Bucket
->Request
, Status
));
433 Bucket
->Status
= Status
;
434 Bucket
->Information
= (Bucket
->Status
== STATUS_SUCCESS
) ? BytesSent
: 0;
436 CompleteBucket(Connection
, Bucket
, FALSE
);
440 // If we completed all outstanding send requests then finish all pending shutdown requests,
441 // cancel the timer and dereference the connection
442 if (IsListEmpty(&Connection
->SendRequest
))
444 FlushShutdownQueue(Connection
, STATUS_SUCCESS
, FALSE
);
446 if (KeCancelTimer(&Connection
->DisconnectTimer
))
448 DereferenceObject(Connection
);
452 DereferenceObject(Connection
);
456 TCPRecvEventHandler(void *arg
)
458 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)arg
;
468 ReferenceObject(Connection
);
470 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->ReceiveRequest
, &Connection
->Lock
)))
472 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
474 Irp
= Bucket
->Request
.RequestContext
;
475 Mdl
= Irp
->MdlAddress
;
477 NdisQueryBuffer( Mdl
, &RecvBuffer
, &RecvLen
);
479 Status
= LibTCPGetDataFromConnectionQueue(Connection
, RecvBuffer
, RecvLen
, &Received
);
480 if (Status
== STATUS_PENDING
)
482 ExInterlockedInsertHeadList(&Connection
->ReceiveRequest
,
488 Bucket
->Status
= Status
;
489 Bucket
->Information
= Received
;
491 CompleteBucket(Connection
, Bucket
, FALSE
);
494 DereferenceObject(Connection
);
498 TCPConnectEventHandler(void *arg
, err_t err
)
500 PCONNECTION_ENDPOINT Connection
= (PCONNECTION_ENDPOINT
)arg
;
504 ReferenceObject(Connection
);
506 while ((Entry
= ExInterlockedRemoveHeadList(&Connection
->ConnectRequest
, &Connection
->Lock
)))
509 Bucket
= CONTAINING_RECORD( Entry
, TDI_BUCKET
, Entry
);
511 Bucket
->Status
= TCPTranslateError(err
);
512 Bucket
->Information
= 0;
514 CompleteBucket(Connection
, Bucket
, FALSE
);
517 DereferenceObject(Connection
);