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