Sync with trunk r58033.
[reactos.git] / lib / drivers / ip / transport / tcp / event.c
1 /*
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)
7 */
8
9 #include "precomp.h"
10
11 #include "lwip/err.h"
12 #include "lwip/sys.h"
13 #include "lwip/pbuf.h"
14 #include "lwip/tcp.h"
15 #include "lwip/api.h"
16
17 #include "rosip.h"
18
19 static const char * const tcp_state_str[] = {
20 "CLOSED",
21 "LISTEN",
22 "SYN_SENT",
23 "SYN_RCVD",
24 "ESTABLISHED",
25 "FIN_WAIT_1",
26 "FIN_WAIT_2",
27 "CLOSE_WAIT",
28 "CLOSING",
29 "LAST_ACK",
30 "TIME_WAIT"
31 };
32
33 extern NPAGED_LOOKASIDE_LIST TdiBucketLookasideList;
34
35 static
36 VOID
37 BucketCompletionWorker(PVOID Context)
38 {
39 PTDI_BUCKET Bucket = (PTDI_BUCKET)Context;
40 PTCP_COMPLETION_ROUTINE Complete;
41
42 Complete = (PTCP_COMPLETION_ROUTINE)Bucket->Request.RequestNotifyObject;
43
44 Complete(Bucket->Request.RequestContext, Bucket->Status, Bucket->Information);
45
46 DereferenceObject(Bucket->AssociatedEndpoint);
47
48 ExFreeToNPagedLookasideList(&TdiBucketLookasideList, Bucket);
49 }
50
51 VOID
52 CompleteBucket(PCONNECTION_ENDPOINT Connection, PTDI_BUCKET Bucket, const BOOLEAN Synchronous)
53 {
54 ReferenceObject(Connection);
55 Bucket->AssociatedEndpoint = Connection;
56 if (Synchronous)
57 {
58 BucketCompletionWorker(Bucket);
59 }
60 else
61 {
62 ChewCreate(BucketCompletionWorker, Bucket);
63 }
64 }
65
66 VOID
67 FlushReceiveQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status, const BOOLEAN interlocked)
68 {
69 PTDI_BUCKET Bucket;
70 PLIST_ENTRY Entry;
71
72 ReferenceObject(Connection);
73
74 if (interlocked)
75 {
76 while ((Entry = ExInterlockedRemoveHeadList(&Connection->ReceiveRequest, &Connection->Lock)))
77 {
78 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
79
80 TI_DbgPrint(DEBUG_TCP,
81 ("Completing Receive request: %x %x\n",
82 Bucket->Request, Status));
83
84 Bucket->Status = Status;
85 Bucket->Information = 0;
86
87 CompleteBucket(Connection, Bucket, FALSE);
88 }
89 }
90 else
91 {
92 while (!IsListEmpty(&Connection->ReceiveRequest))
93 {
94 Entry = RemoveHeadList(&Connection->ReceiveRequest);
95
96 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
97
98 Bucket->Information = 0;
99 Bucket->Status = Status;
100
101 CompleteBucket(Connection, Bucket, FALSE);
102 }
103 }
104
105 DereferenceObject(Connection);
106 }
107
108 VOID
109 FlushSendQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status, const BOOLEAN interlocked)
110 {
111 PTDI_BUCKET Bucket;
112 PLIST_ENTRY Entry;
113
114 ReferenceObject(Connection);
115
116 if (interlocked)
117 {
118 while ((Entry = ExInterlockedRemoveHeadList(&Connection->SendRequest, &Connection->Lock)))
119 {
120 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
121
122 TI_DbgPrint(DEBUG_TCP,
123 ("Completing Send request: %x %x\n",
124 Bucket->Request, Status));
125
126 Bucket->Status = Status;
127 Bucket->Information = 0;
128
129 CompleteBucket(Connection, Bucket, FALSE);
130 }
131 }
132 else
133 {
134 while (!IsListEmpty(&Connection->SendRequest))
135 {
136 Entry = RemoveHeadList(&Connection->SendRequest);
137
138 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
139
140 Bucket->Information = 0;
141 Bucket->Status = Status;
142
143 CompleteBucket(Connection, Bucket, FALSE);
144 }
145 }
146
147 DereferenceObject(Connection);
148 }
149
150 VOID
151 FlushShutdownQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status, const BOOLEAN interlocked)
152 {
153 PTDI_BUCKET Bucket;
154 PLIST_ENTRY Entry;
155
156 ReferenceObject(Connection);
157
158 if (interlocked)
159 {
160 while ((Entry = ExInterlockedRemoveHeadList(&Connection->ShutdownRequest, &Connection->Lock)))
161 {
162 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
163
164 Bucket->Status = Status;
165 Bucket->Information = 0;
166
167 CompleteBucket(Connection, Bucket, FALSE);
168 }
169 }
170 else
171 {
172 while (!IsListEmpty(&Connection->ShutdownRequest))
173 {
174 Entry = RemoveHeadList(&Connection->ShutdownRequest);
175
176 Bucket = CONTAINING_RECORD(Entry, TDI_BUCKET, Entry);
177
178 Bucket->Information = 0;
179 Bucket->Status = Status;
180
181 CompleteBucket(Connection, Bucket, FALSE);
182 }
183 }
184
185 DereferenceObject(Connection);
186 }
187
188 VOID
189 FlushConnectQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status)
190 {
191 PTDI_BUCKET Bucket;
192 PLIST_ENTRY Entry;
193
194 ReferenceObject(Connection);
195
196 while ((Entry = ExInterlockedRemoveHeadList(&Connection->ConnectRequest, &Connection->Lock)))
197 {
198 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
199
200 Bucket->Status = Status;
201 Bucket->Information = 0;
202
203 CompleteBucket(Connection, Bucket, FALSE);
204 }
205
206 DereferenceObject(Connection);
207 }
208
209 VOID
210 FlushListenQueue(PCONNECTION_ENDPOINT Connection, const NTSTATUS Status)
211 {
212 PTDI_BUCKET Bucket;
213 PLIST_ENTRY Entry;
214
215 ReferenceObject(Connection);
216
217 while ((Entry = ExInterlockedRemoveHeadList(&Connection->ListenRequest, &Connection->Lock)))
218 {
219 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
220
221 Bucket->Status = Status;
222 Bucket->Information = 0;
223
224 DereferenceObject(Bucket->AssociatedEndpoint);
225 CompleteBucket(Connection, Bucket, FALSE);
226 }
227
228 DereferenceObject(Connection);
229 }
230
231 VOID
232 FlushAllQueues(PCONNECTION_ENDPOINT Connection, NTSTATUS Status)
233 {
234 ReferenceObject(Connection);
235
236 // flush receive queue
237 FlushReceiveQueue(Connection, Status, TRUE);
238
239 /* We completed the reads successfully but we need to return failure now */
240 if (Status == STATUS_SUCCESS)
241 {
242 Status = STATUS_FILE_CLOSED;
243 }
244
245 // flush listen queue
246 FlushListenQueue(Connection, Status);
247
248 // flush send queue
249 FlushSendQueue(Connection, Status, TRUE);
250
251 // flush connect queue
252 FlushConnectQueue(Connection, Status);
253
254 // flush shutdown queue
255 FlushShutdownQueue(Connection, Status, TRUE);
256
257 DereferenceObject(Connection);
258 }
259
260 VOID
261 TCPFinEventHandler(void *arg, const err_t err)
262 {
263 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg, LastConnection;
264 const NTSTATUS Status = TCPTranslateError(err);
265 KIRQL OldIrql;
266
267 ASSERT(Connection->AddressFile);
268 ASSERT(err != ERR_OK);
269
270 /* First off all, remove the PCB pointer */
271 Connection->SocketContext = NULL;
272
273 /* Complete all outstanding requests now */
274 FlushAllQueues(Connection, Status);
275
276 LockObject(Connection, &OldIrql);
277
278 LockObjectAtDpcLevel(Connection->AddressFile);
279
280 /* Unlink this connection from the address file */
281 if (Connection->AddressFile->Connection == Connection)
282 {
283 Connection->AddressFile->Connection = Connection->Next;
284 DereferenceObject(Connection);
285 }
286 else if (Connection->AddressFile->Listener == Connection)
287 {
288 Connection->AddressFile->Listener = NULL;
289 DereferenceObject(Connection);
290 }
291 else
292 {
293 LastConnection = Connection->AddressFile->Connection;
294 while (LastConnection->Next != Connection && LastConnection->Next != NULL)
295 LastConnection = LastConnection->Next;
296 if (LastConnection->Next == Connection)
297 {
298 LastConnection->Next = Connection->Next;
299 DereferenceObject(Connection);
300 }
301 }
302
303 UnlockObjectFromDpcLevel(Connection->AddressFile);
304
305 /* Remove the address file from this connection */
306 DereferenceObject(Connection->AddressFile);
307 Connection->AddressFile = NULL;
308
309 UnlockObject(Connection, OldIrql);
310 }
311
312 VOID
313 TCPAcceptEventHandler(void *arg, PTCP_PCB newpcb)
314 {
315 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
316 PTDI_BUCKET Bucket;
317 PLIST_ENTRY Entry;
318 PIRP Irp;
319 NTSTATUS Status;
320 KIRQL OldIrql;
321
322 ReferenceObject(Connection);
323
324 while ((Entry = ExInterlockedRemoveHeadList(&Connection->ListenRequest, &Connection->Lock)))
325 {
326 PIO_STACK_LOCATION IrpSp;
327
328 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
329
330 Irp = Bucket->Request.RequestContext;
331 IrpSp = IoGetCurrentIrpStackLocation( Irp );
332
333 TI_DbgPrint(DEBUG_TCP,("[IP, TCPAcceptEventHandler] Getting the socket\n"));
334
335 Status = TCPCheckPeerForAccept(newpcb,
336 (PTDI_REQUEST_KERNEL)&IrpSp->Parameters);
337
338 TI_DbgPrint(DEBUG_TCP,("Socket: Status: %x\n", Status));
339
340 Bucket->Status = Status;
341 Bucket->Information = 0;
342
343 if (Status == STATUS_SUCCESS)
344 {
345 LockObject(Bucket->AssociatedEndpoint, &OldIrql);
346
347 /* sanity assert...this should never be in anything else but a CLOSED state */
348 ASSERT( ((PTCP_PCB)Bucket->AssociatedEndpoint->SocketContext)->state == CLOSED );
349
350 /* free socket context created in FileOpenConnection, as we're using a new one */
351 LibTCPClose(Bucket->AssociatedEndpoint, TRUE, FALSE);
352
353 /* free previously created socket context (we don't use it, we use newpcb) */
354 Bucket->AssociatedEndpoint->SocketContext = newpcb;
355
356 LibTCPAccept(newpcb, (PTCP_PCB)Connection->SocketContext, Bucket->AssociatedEndpoint);
357
358 UnlockObject(Bucket->AssociatedEndpoint, OldIrql);
359 }
360
361 DereferenceObject(Bucket->AssociatedEndpoint);
362
363 CompleteBucket(Connection, Bucket, FALSE);
364 }
365
366 DereferenceObject(Connection);
367 }
368
369 VOID
370 TCPSendEventHandler(void *arg, u16_t space)
371 {
372 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
373 PTDI_BUCKET Bucket;
374 PLIST_ENTRY Entry;
375 PIRP Irp;
376 NTSTATUS Status;
377 PMDL Mdl;
378 ULONG BytesSent;
379
380 ReferenceObject(Connection);
381
382 while ((Entry = ExInterlockedRemoveHeadList(&Connection->SendRequest, &Connection->Lock)))
383 {
384 UINT SendLen = 0;
385 PVOID SendBuffer = 0;
386
387 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
388
389 Irp = Bucket->Request.RequestContext;
390 Mdl = Irp->MdlAddress;
391
392 TI_DbgPrint(DEBUG_TCP,
393 ("Getting the user buffer from %x\n", Mdl));
394
395 NdisQueryBuffer( Mdl, &SendBuffer, &SendLen );
396
397 TI_DbgPrint(DEBUG_TCP,
398 ("Writing %d bytes to %x\n", SendLen, SendBuffer));
399
400 TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection));
401 TI_DbgPrint
402 (DEBUG_TCP,
403 ("Connection->SocketContext: %x\n",
404 Connection->SocketContext));
405
406 Status = TCPTranslateError(LibTCPSend(Connection,
407 SendBuffer,
408 SendLen, &BytesSent, TRUE));
409
410 TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", BytesSent));
411
412 if( Status == STATUS_PENDING )
413 {
414 ExInterlockedInsertHeadList(&Connection->SendRequest,
415 &Bucket->Entry,
416 &Connection->Lock);
417 break;
418 }
419 else
420 {
421 TI_DbgPrint(DEBUG_TCP,
422 ("Completing Send request: %x %x\n",
423 Bucket->Request, Status));
424
425 Bucket->Status = Status;
426 Bucket->Information = (Bucket->Status == STATUS_SUCCESS) ? BytesSent : 0;
427
428 CompleteBucket(Connection, Bucket, FALSE);
429 }
430 }
431
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))
435 {
436 FlushShutdownQueue(Connection, STATUS_SUCCESS, FALSE);
437
438 if (KeCancelTimer(&Connection->DisconnectTimer))
439 {
440 DereferenceObject(Connection);
441 }
442 }
443
444 DereferenceObject(Connection);
445 }
446
447 VOID
448 TCPRecvEventHandler(void *arg)
449 {
450 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
451 PTDI_BUCKET Bucket;
452 PLIST_ENTRY Entry;
453 PIRP Irp;
454 PMDL Mdl;
455 UINT Received;
456 UINT RecvLen;
457 PUCHAR RecvBuffer;
458 NTSTATUS Status;
459
460 ReferenceObject(Connection);
461
462 while ((Entry = ExInterlockedRemoveHeadList(&Connection->ReceiveRequest, &Connection->Lock)))
463 {
464 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
465
466 Irp = Bucket->Request.RequestContext;
467 Mdl = Irp->MdlAddress;
468
469 NdisQueryBuffer( Mdl, &RecvBuffer, &RecvLen );
470
471 Status = LibTCPGetDataFromConnectionQueue(Connection, RecvBuffer, RecvLen, &Received);
472 if (Status == STATUS_PENDING)
473 {
474 ExInterlockedInsertHeadList(&Connection->ReceiveRequest,
475 &Bucket->Entry,
476 &Connection->Lock);
477 break;
478 }
479
480 Bucket->Status = Status;
481 Bucket->Information = Received;
482
483 CompleteBucket(Connection, Bucket, FALSE);
484 }
485
486 DereferenceObject(Connection);
487 }
488
489 VOID
490 TCPConnectEventHandler(void *arg, err_t err)
491 {
492 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
493 PTDI_BUCKET Bucket;
494 PLIST_ENTRY Entry;
495
496 ReferenceObject(Connection);
497
498 while ((Entry = ExInterlockedRemoveHeadList(&Connection->ConnectRequest, &Connection->Lock)))
499 {
500
501 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
502
503 Bucket->Status = TCPTranslateError(err);
504 Bucket->Information = 0;
505
506 CompleteBucket(Connection, Bucket, FALSE);
507 }
508
509 DereferenceObject(Connection);
510 }