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