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