a2d632e944ef9c19b6dd4cd01cbfa14bdf5dcf8d
[reactos.git] / reactos / 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
269 /* Check if this was a partial socket closure */
270 if (err == ERR_OK && Connection->SocketContext)
271 {
272 /* Just flush the receive queue and get out of here */
273 FlushReceiveQueue(Connection, STATUS_SUCCESS, TRUE);
274 }
275 else
276 {
277 /* First off all, remove the PCB pointer */
278 Connection->SocketContext = NULL;
279
280 /* Complete all outstanding requests now */
281 FlushAllQueues(Connection, Status);
282
283 LockObject(Connection, &OldIrql);
284
285 LockObjectAtDpcLevel(Connection->AddressFile);
286
287 /* Unlink this connection from the address file */
288 if (Connection->AddressFile->Connection == Connection)
289 {
290 Connection->AddressFile->Connection = Connection->Next;
291 DereferenceObject(Connection);
292 }
293 else if (Connection->AddressFile->Listener == Connection)
294 {
295 Connection->AddressFile->Listener = NULL;
296 DereferenceObject(Connection);
297 }
298 else
299 {
300 LastConnection = Connection->AddressFile->Connection;
301 while (LastConnection->Next != Connection && LastConnection->Next != NULL)
302 LastConnection = LastConnection->Next;
303 if (LastConnection->Next == Connection)
304 {
305 LastConnection->Next = Connection->Next;
306 DereferenceObject(Connection);
307 }
308 }
309
310 UnlockObjectFromDpcLevel(Connection->AddressFile);
311
312 /* Remove the address file from this connection */
313 DereferenceObject(Connection->AddressFile);
314 Connection->AddressFile = NULL;
315
316 UnlockObject(Connection, OldIrql);
317 }
318 }
319
320 VOID
321 TCPAcceptEventHandler(void *arg, PTCP_PCB newpcb)
322 {
323 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
324 PTDI_BUCKET Bucket;
325 PLIST_ENTRY Entry;
326 PIRP Irp;
327 NTSTATUS Status;
328 KIRQL OldIrql;
329
330 ReferenceObject(Connection);
331
332 while ((Entry = ExInterlockedRemoveHeadList(&Connection->ListenRequest, &Connection->Lock)))
333 {
334 PIO_STACK_LOCATION IrpSp;
335
336 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
337
338 Irp = Bucket->Request.RequestContext;
339 IrpSp = IoGetCurrentIrpStackLocation( Irp );
340
341 TI_DbgPrint(DEBUG_TCP,("[IP, TCPAcceptEventHandler] Getting the socket\n"));
342
343 Status = TCPCheckPeerForAccept(newpcb,
344 (PTDI_REQUEST_KERNEL)&IrpSp->Parameters);
345
346 TI_DbgPrint(DEBUG_TCP,("Socket: Status: %x\n", Status));
347
348 Bucket->Status = Status;
349 Bucket->Information = 0;
350
351 if (Status == STATUS_SUCCESS)
352 {
353 LockObject(Bucket->AssociatedEndpoint, &OldIrql);
354
355 /* sanity assert...this should never be in anything else but a CLOSED state */
356 ASSERT( ((PTCP_PCB)Bucket->AssociatedEndpoint->SocketContext)->state == CLOSED );
357
358 /* free socket context created in FileOpenConnection, as we're using a new one */
359 LibTCPClose(Bucket->AssociatedEndpoint, TRUE, FALSE);
360
361 /* free previously created socket context (we don't use it, we use newpcb) */
362 Bucket->AssociatedEndpoint->SocketContext = newpcb;
363
364 LibTCPAccept(newpcb, (PTCP_PCB)Connection->SocketContext, Bucket->AssociatedEndpoint);
365
366 UnlockObject(Bucket->AssociatedEndpoint, OldIrql);
367 }
368
369 DereferenceObject(Bucket->AssociatedEndpoint);
370
371 CompleteBucket(Connection, Bucket, FALSE);
372 }
373
374 DereferenceObject(Connection);
375 }
376
377 VOID
378 TCPSendEventHandler(void *arg, u16_t space)
379 {
380 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
381 PTDI_BUCKET Bucket;
382 PLIST_ENTRY Entry;
383 PIRP Irp;
384 NTSTATUS Status;
385 PMDL Mdl;
386 ULONG BytesSent;
387
388 ReferenceObject(Connection);
389
390 while ((Entry = ExInterlockedRemoveHeadList(&Connection->SendRequest, &Connection->Lock)))
391 {
392 UINT SendLen = 0;
393 PVOID SendBuffer = 0;
394
395 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
396
397 Irp = Bucket->Request.RequestContext;
398 Mdl = Irp->MdlAddress;
399
400 TI_DbgPrint(DEBUG_TCP,
401 ("Getting the user buffer from %x\n", Mdl));
402
403 NdisQueryBuffer( Mdl, &SendBuffer, &SendLen );
404
405 TI_DbgPrint(DEBUG_TCP,
406 ("Writing %d bytes to %x\n", SendLen, SendBuffer));
407
408 TI_DbgPrint(DEBUG_TCP, ("Connection: %x\n", Connection));
409 TI_DbgPrint
410 (DEBUG_TCP,
411 ("Connection->SocketContext: %x\n",
412 Connection->SocketContext));
413
414 Status = TCPTranslateError(LibTCPSend(Connection,
415 SendBuffer,
416 SendLen, &BytesSent, TRUE));
417
418 TI_DbgPrint(DEBUG_TCP,("TCP Bytes: %d\n", BytesSent));
419
420 if( Status == STATUS_PENDING )
421 {
422 ExInterlockedInsertHeadList(&Connection->SendRequest,
423 &Bucket->Entry,
424 &Connection->Lock);
425 break;
426 }
427 else
428 {
429 TI_DbgPrint(DEBUG_TCP,
430 ("Completing Send request: %x %x\n",
431 Bucket->Request, Status));
432
433 Bucket->Status = Status;
434 Bucket->Information = (Bucket->Status == STATUS_SUCCESS) ? BytesSent : 0;
435
436 CompleteBucket(Connection, Bucket, FALSE);
437 }
438 }
439
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))
443 {
444 FlushShutdownQueue(Connection, STATUS_SUCCESS, FALSE);
445
446 if (KeCancelTimer(&Connection->DisconnectTimer))
447 {
448 DereferenceObject(Connection);
449 }
450 }
451
452 DereferenceObject(Connection);
453 }
454
455 VOID
456 TCPRecvEventHandler(void *arg)
457 {
458 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
459 PTDI_BUCKET Bucket;
460 PLIST_ENTRY Entry;
461 PIRP Irp;
462 PMDL Mdl;
463 UINT Received;
464 UINT RecvLen;
465 PUCHAR RecvBuffer;
466 NTSTATUS Status;
467
468 ReferenceObject(Connection);
469
470 while ((Entry = ExInterlockedRemoveHeadList(&Connection->ReceiveRequest, &Connection->Lock)))
471 {
472 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
473
474 Irp = Bucket->Request.RequestContext;
475 Mdl = Irp->MdlAddress;
476
477 NdisQueryBuffer( Mdl, &RecvBuffer, &RecvLen );
478
479 Status = LibTCPGetDataFromConnectionQueue(Connection, RecvBuffer, RecvLen, &Received);
480 if (Status == STATUS_PENDING)
481 {
482 ExInterlockedInsertHeadList(&Connection->ReceiveRequest,
483 &Bucket->Entry,
484 &Connection->Lock);
485 break;
486 }
487
488 Bucket->Status = Status;
489 Bucket->Information = Received;
490
491 CompleteBucket(Connection, Bucket, FALSE);
492 }
493
494 DereferenceObject(Connection);
495 }
496
497 VOID
498 TCPConnectEventHandler(void *arg, err_t err)
499 {
500 PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg;
501 PTDI_BUCKET Bucket;
502 PLIST_ENTRY Entry;
503
504 ReferenceObject(Connection);
505
506 while ((Entry = ExInterlockedRemoveHeadList(&Connection->ConnectRequest, &Connection->Lock)))
507 {
508
509 Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
510
511 Bucket->Status = TCPTranslateError(err);
512 Bucket->Information = 0;
513
514 CompleteBucket(Connection, Bucket, FALSE);
515 }
516
517 DereferenceObject(Connection);
518 }