[USB]
[reactos.git] / reactos / lib / drivers / lwip / src / rostcp.c
1 #include "lwip/sys.h"
2 #include "lwip/tcpip.h"
3
4 #include "rosip.h"
5
6 #include <debug.h>
7
8 static const char * const tcp_state_str[] = {
9 "CLOSED",
10 "LISTEN",
11 "SYN_SENT",
12 "SYN_RCVD",
13 "ESTABLISHED",
14 "FIN_WAIT_1",
15 "FIN_WAIT_2",
16 "CLOSE_WAIT",
17 "CLOSING",
18 "LAST_ACK",
19 "TIME_WAIT"
20 };
21
22 /* The way that lwIP does multi-threading is really not ideal for our purposes but
23 * we best go along with it unless we want another unstable TCP library. lwIP uses
24 * a thread called the "tcpip thread" which is the only one allowed to call raw API
25 * functions. Since this is the case, for each of our LibTCP* functions, we queue a request
26 * for a callback to "tcpip thread" which calls our LibTCP*Callback functions. Yes, this is
27 * a lot of unnecessary thread swapping and it could definitely be faster, but I don't want
28 * to going messing around in lwIP because I have no desire to create another mess like oskittcp */
29
30 extern KEVENT TerminationEvent;
31 extern NPAGED_LOOKASIDE_LIST MessageLookasideList;
32 extern NPAGED_LOOKASIDE_LIST QueueEntryLookasideList;
33
34 static
35 void
36 LibTCPEmptyQueue(PCONNECTION_ENDPOINT Connection)
37 {
38 PLIST_ENTRY Entry;
39 PQUEUE_ENTRY qp = NULL;
40
41 ReferenceObject(Connection);
42
43 while (!IsListEmpty(&Connection->PacketQueue))
44 {
45 Entry = RemoveHeadList(&Connection->PacketQueue);
46 qp = CONTAINING_RECORD(Entry, QUEUE_ENTRY, ListEntry);
47
48 /* We're in the tcpip thread here so this is safe */
49 pbuf_free(qp->p);
50
51 ExFreeToNPagedLookasideList(&QueueEntryLookasideList, qp);
52 }
53
54 DereferenceObject(Connection);
55 }
56
57 void LibTCPEnqueuePacket(PCONNECTION_ENDPOINT Connection, struct pbuf *p)
58 {
59 PQUEUE_ENTRY qp;
60
61 qp = (PQUEUE_ENTRY)ExAllocateFromNPagedLookasideList(&QueueEntryLookasideList);
62 qp->p = p;
63
64 ExInterlockedInsertTailList(&Connection->PacketQueue, &qp->ListEntry, &Connection->Lock);
65 }
66
67 PQUEUE_ENTRY LibTCPDequeuePacket(PCONNECTION_ENDPOINT Connection)
68 {
69 PLIST_ENTRY Entry;
70 PQUEUE_ENTRY qp = NULL;
71
72 if (IsListEmpty(&Connection->PacketQueue)) return NULL;
73
74 Entry = RemoveHeadList(&Connection->PacketQueue);
75
76 qp = CONTAINING_RECORD(Entry, QUEUE_ENTRY, ListEntry);
77
78 return qp;
79 }
80
81 NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHAR RecvBuffer, UINT RecvLen, UINT *Received)
82 {
83 PQUEUE_ENTRY qp;
84 struct pbuf* p;
85 NTSTATUS Status = STATUS_PENDING;
86 UINT ReadLength, ExistingDataLength;
87 KIRQL OldIrql;
88
89 (*Received) = 0;
90
91 LockObject(Connection, &OldIrql);
92
93 if (!IsListEmpty(&Connection->PacketQueue))
94 {
95 while ((qp = LibTCPDequeuePacket(Connection)) != NULL)
96 {
97 p = qp->p;
98 ExistingDataLength = (*Received);
99
100 Status = STATUS_SUCCESS;
101
102 ReadLength = MIN(p->tot_len, RecvLen);
103 if (ReadLength != p->tot_len)
104 {
105 if (ExistingDataLength)
106 {
107 /* The packet was too big but we used some data already so give it another shot later */
108 InsertHeadList(&Connection->PacketQueue, &qp->ListEntry);
109 break;
110 }
111 else
112 {
113 /* The packet is just too big to fit fully in our buffer, even when empty so
114 * return an informative status but still copy all the data we can fit.
115 */
116 Status = STATUS_BUFFER_OVERFLOW;
117 }
118 }
119
120 UnlockObject(Connection, OldIrql);
121
122 /* Return to a lower IRQL because the receive buffer may be pageable memory */
123 for (; (*Received) < ReadLength + ExistingDataLength; (*Received) += p->len, p = p->next)
124 {
125 RtlCopyMemory(RecvBuffer + (*Received), p->payload, p->len);
126 }
127
128 LockObject(Connection, &OldIrql);
129
130 RecvLen -= ReadLength;
131
132 /* Use this special pbuf free callback function because we're outside tcpip thread */
133 pbuf_free_callback(qp->p);
134
135 ExFreeToNPagedLookasideList(&QueueEntryLookasideList, qp);
136
137 if (!RecvLen)
138 break;
139
140 if (Status != STATUS_SUCCESS)
141 break;
142 }
143 }
144 else
145 {
146 if (Connection->ReceiveShutdown)
147 Status = STATUS_SUCCESS;
148 else
149 Status = STATUS_PENDING;
150 }
151
152 UnlockObject(Connection, OldIrql);
153
154 return Status;
155 }
156
157 static
158 BOOLEAN
159 WaitForEventSafely(PRKEVENT Event)
160 {
161 PVOID WaitObjects[] = {Event, &TerminationEvent};
162
163 if (KeWaitForMultipleObjects(2,
164 WaitObjects,
165 WaitAny,
166 Executive,
167 KernelMode,
168 FALSE,
169 NULL,
170 NULL) == STATUS_WAIT_0)
171 {
172 /* Signalled by the caller's event */
173 return TRUE;
174 }
175 else /* if KeWaitForMultipleObjects() == STATUS_WAIT_1 */
176 {
177 /* Signalled by our termination event */
178 return FALSE;
179 }
180 }
181
182 static
183 err_t
184 InternalSendEventHandler(void *arg, PTCP_PCB pcb, const u16_t space)
185 {
186 /* Make sure the socket didn't get closed */
187 if (!arg) return ERR_OK;
188
189 TCPSendEventHandler(arg, space);
190
191 return ERR_OK;
192 }
193
194 static
195 err_t
196 InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t err)
197 {
198 PCONNECTION_ENDPOINT Connection = arg;
199
200 /* Make sure the socket didn't get closed */
201 if (!arg)
202 {
203 if (p)
204 pbuf_free(p);
205
206 return ERR_OK;
207 }
208
209 if (p)
210 {
211 LibTCPEnqueuePacket(Connection, p);
212
213 tcp_recved(pcb, p->tot_len);
214
215 TCPRecvEventHandler(arg);
216 }
217 else if (err == ERR_OK)
218 {
219 /* Complete pending reads with 0 bytes to indicate a graceful closure,
220 * but note that send is still possible in this state so we don't close the
221 * whole socket here (by calling tcp_close()) as that would violate TCP specs
222 */
223 Connection->ReceiveShutdown = TRUE;
224 TCPFinEventHandler(arg, ERR_OK);
225 }
226
227 return ERR_OK;
228 }
229
230 static
231 err_t
232 InternalAcceptEventHandler(void *arg, PTCP_PCB newpcb, const err_t err)
233 {
234 /* Make sure the socket didn't get closed */
235 if (!arg)
236 return ERR_ABRT;
237
238 TCPAcceptEventHandler(arg, newpcb);
239
240 /* Set in LibTCPAccept (called from TCPAcceptEventHandler) */
241 if (newpcb->callback_arg)
242 return ERR_OK;
243 else
244 return ERR_ABRT;
245 }
246
247 static
248 err_t
249 InternalConnectEventHandler(void *arg, PTCP_PCB pcb, const err_t err)
250 {
251 /* Make sure the socket didn't get closed */
252 if (!arg)
253 return ERR_OK;
254
255 TCPConnectEventHandler(arg, err);
256
257 return ERR_OK;
258 }
259
260 static
261 void
262 InternalErrorEventHandler(void *arg, const err_t err)
263 {
264 /* Make sure the socket didn't get closed */
265 if (!arg) return;
266
267 TCPFinEventHandler(arg, err);
268 }
269
270 static
271 void
272 LibTCPSocketCallback(void *arg)
273 {
274 struct lwip_callback_msg *msg = arg;
275
276 ASSERT(msg);
277
278 msg->Output.Socket.NewPcb = tcp_new();
279
280 if (msg->Output.Socket.NewPcb)
281 {
282 tcp_arg(msg->Output.Socket.NewPcb, msg->Input.Socket.Arg);
283 tcp_err(msg->Output.Socket.NewPcb, InternalErrorEventHandler);
284 }
285
286 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
287 }
288
289 struct tcp_pcb *
290 LibTCPSocket(void *arg)
291 {
292 struct lwip_callback_msg *msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
293 struct tcp_pcb *ret;
294
295 if (msg)
296 {
297 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
298 msg->Input.Socket.Arg = arg;
299
300 tcpip_callback_with_block(LibTCPSocketCallback, msg, 1);
301
302 if (WaitForEventSafely(&msg->Event))
303 ret = msg->Output.Socket.NewPcb;
304 else
305 ret = NULL;
306
307 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
308
309 return ret;
310 }
311
312 return NULL;
313 }
314
315 static
316 void
317 LibTCPBindCallback(void *arg)
318 {
319 struct lwip_callback_msg *msg = arg;
320
321 ASSERT(msg);
322
323 if (!msg->Input.Bind.Connection->SocketContext)
324 {
325 msg->Output.Bind.Error = ERR_CLSD;
326 goto done;
327 }
328
329 msg->Output.Bind.Error = tcp_bind((PTCP_PCB)msg->Input.Bind.Connection->SocketContext,
330 msg->Input.Bind.IpAddress,
331 ntohs(msg->Input.Bind.Port));
332
333 done:
334 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
335 }
336
337 err_t
338 LibTCPBind(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port)
339 {
340 struct lwip_callback_msg *msg;
341 err_t ret;
342
343 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
344 if (msg)
345 {
346 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
347 msg->Input.Bind.Connection = Connection;
348 msg->Input.Bind.IpAddress = ipaddr;
349 msg->Input.Bind.Port = port;
350
351 tcpip_callback_with_block(LibTCPBindCallback, msg, 1);
352
353 if (WaitForEventSafely(&msg->Event))
354 ret = msg->Output.Bind.Error;
355 else
356 ret = ERR_CLSD;
357
358 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
359
360 return ret;
361 }
362
363 return ERR_MEM;
364 }
365
366 static
367 void
368 LibTCPListenCallback(void *arg)
369 {
370 struct lwip_callback_msg *msg = arg;
371
372 ASSERT(msg);
373
374 if (!msg->Input.Listen.Connection->SocketContext)
375 {
376 msg->Output.Listen.NewPcb = NULL;
377 goto done;
378 }
379
380 msg->Output.Listen.NewPcb = tcp_listen_with_backlog((PTCP_PCB)msg->Input.Listen.Connection->SocketContext, msg->Input.Listen.Backlog);
381
382 if (msg->Output.Listen.NewPcb)
383 {
384 tcp_accept(msg->Output.Listen.NewPcb, InternalAcceptEventHandler);
385 }
386
387 done:
388 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
389 }
390
391 PTCP_PCB
392 LibTCPListen(PCONNECTION_ENDPOINT Connection, const u8_t backlog)
393 {
394 struct lwip_callback_msg *msg;
395 PTCP_PCB ret;
396
397 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
398 if (msg)
399 {
400 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
401 msg->Input.Listen.Connection = Connection;
402 msg->Input.Listen.Backlog = backlog;
403
404 tcpip_callback_with_block(LibTCPListenCallback, msg, 1);
405
406 if (WaitForEventSafely(&msg->Event))
407 ret = msg->Output.Listen.NewPcb;
408 else
409 ret = NULL;
410
411 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
412
413 return ret;
414 }
415
416 return NULL;
417 }
418
419 static
420 void
421 LibTCPSendCallback(void *arg)
422 {
423 struct lwip_callback_msg *msg = arg;
424
425 ASSERT(msg);
426
427 if (!msg->Input.Send.Connection->SocketContext)
428 {
429 msg->Output.Send.Error = ERR_CLSD;
430 goto done;
431 }
432
433 if (msg->Input.Send.Connection->SendShutdown)
434 {
435 msg->Output.Send.Error = ERR_CLSD;
436 goto done;
437 }
438
439 msg->Output.Send.Error = tcp_write((PTCP_PCB)msg->Input.Send.Connection->SocketContext,
440 msg->Input.Send.Data,
441 msg->Input.Send.DataLength,
442 TCP_WRITE_FLAG_COPY);
443 if (msg->Output.Send.Error == ERR_MEM)
444 {
445 /* No buffer space so return pending */
446 msg->Output.Send.Error = ERR_INPROGRESS;
447 }
448 else if (msg->Output.Send.Error == ERR_OK)
449 {
450 /* Queued successfully so try to send it */
451 tcp_output((PTCP_PCB)msg->Input.Send.Connection->SocketContext);
452 }
453
454 done:
455 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
456 }
457
458 err_t
459 LibTCPSend(PCONNECTION_ENDPOINT Connection, void *const dataptr, const u16_t len, const int safe)
460 {
461 err_t ret;
462 struct lwip_callback_msg *msg;
463
464 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
465 if (msg)
466 {
467 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
468 msg->Input.Send.Connection = Connection;
469 msg->Input.Send.Data = dataptr;
470 msg->Input.Send.DataLength = len;
471
472 if (safe)
473 LibTCPSendCallback(msg);
474 else
475 tcpip_callback_with_block(LibTCPSendCallback, msg, 1);
476
477 if (WaitForEventSafely(&msg->Event))
478 ret = msg->Output.Send.Error;
479 else
480 ret = ERR_CLSD;
481
482 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
483
484 return ret;
485 }
486
487 return ERR_MEM;
488 }
489
490 static
491 void
492 LibTCPConnectCallback(void *arg)
493 {
494 struct lwip_callback_msg *msg = arg;
495 err_t Error;
496
497 ASSERT(arg);
498
499 if (!msg->Input.Connect.Connection->SocketContext)
500 {
501 msg->Output.Connect.Error = ERR_CLSD;
502 goto done;
503 }
504
505 tcp_recv((PTCP_PCB)msg->Input.Connect.Connection->SocketContext, InternalRecvEventHandler);
506 tcp_sent((PTCP_PCB)msg->Input.Connect.Connection->SocketContext, InternalSendEventHandler);
507
508 Error = tcp_connect((PTCP_PCB)msg->Input.Connect.Connection->SocketContext,
509 msg->Input.Connect.IpAddress, ntohs(msg->Input.Connect.Port),
510 InternalConnectEventHandler);
511
512 msg->Output.Connect.Error = Error == ERR_OK ? ERR_INPROGRESS : Error;
513
514 done:
515 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
516 }
517
518 err_t
519 LibTCPConnect(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port)
520 {
521 struct lwip_callback_msg *msg;
522 err_t ret;
523
524 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
525 if (msg)
526 {
527 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
528 msg->Input.Connect.Connection = Connection;
529 msg->Input.Connect.IpAddress = ipaddr;
530 msg->Input.Connect.Port = port;
531
532 tcpip_callback_with_block(LibTCPConnectCallback, msg, 1);
533
534 if (WaitForEventSafely(&msg->Event))
535 {
536 ret = msg->Output.Connect.Error;
537 }
538 else
539 ret = ERR_CLSD;
540
541 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
542
543 return ret;
544 }
545
546 return ERR_MEM;
547 }
548
549 static
550 void
551 LibTCPShutdownCallback(void *arg)
552 {
553 struct lwip_callback_msg *msg = arg;
554 PTCP_PCB pcb = msg->Input.Shutdown.Connection->SocketContext;
555
556 if (!msg->Input.Shutdown.Connection->SocketContext)
557 {
558 msg->Output.Shutdown.Error = ERR_CLSD;
559 goto done;
560 }
561
562 if (pcb->state == CLOSE_WAIT)
563 {
564 /* This case actually results in a socket closure later (lwIP bug?) */
565 msg->Input.Shutdown.Connection->SocketContext = NULL;
566 }
567
568 msg->Output.Shutdown.Error = tcp_shutdown(pcb, msg->Input.Shutdown.shut_rx, msg->Input.Shutdown.shut_tx);
569 if (msg->Output.Shutdown.Error)
570 {
571 msg->Input.Shutdown.Connection->SocketContext = pcb;
572 }
573 else
574 {
575 if (msg->Input.Shutdown.shut_rx)
576 msg->Input.Shutdown.Connection->ReceiveShutdown = TRUE;
577
578 if (msg->Input.Shutdown.shut_tx)
579 msg->Input.Shutdown.Connection->SendShutdown = TRUE;
580 }
581
582 done:
583 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
584 }
585
586 err_t
587 LibTCPShutdown(PCONNECTION_ENDPOINT Connection, const int shut_rx, const int shut_tx)
588 {
589 struct lwip_callback_msg *msg;
590 err_t ret;
591
592 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
593 if (msg)
594 {
595 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
596
597 msg->Input.Shutdown.Connection = Connection;
598 msg->Input.Shutdown.shut_rx = shut_rx;
599 msg->Input.Shutdown.shut_tx = shut_tx;
600
601 tcpip_callback_with_block(LibTCPShutdownCallback, msg, 1);
602
603 if (WaitForEventSafely(&msg->Event))
604 ret = msg->Output.Shutdown.Error;
605 else
606 ret = ERR_CLSD;
607
608 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
609
610 return ret;
611 }
612
613 return ERR_MEM;
614 }
615
616 static
617 void
618 LibTCPCloseCallback(void *arg)
619 {
620 struct lwip_callback_msg *msg = arg;
621 PTCP_PCB pcb = msg->Input.Close.Connection->SocketContext;
622
623 /* Empty the queue even if we're already "closed" */
624 LibTCPEmptyQueue(msg->Input.Close.Connection);
625
626 if (!msg->Input.Close.Connection->SocketContext)
627 {
628 msg->Output.Close.Error = ERR_OK;
629 goto done;
630 }
631
632 /* Clear the PCB pointer */
633 msg->Input.Close.Connection->SocketContext = NULL;
634
635 switch (pcb->state)
636 {
637 case CLOSED:
638 case LISTEN:
639 case SYN_SENT:
640 msg->Output.Close.Error = tcp_close(pcb);
641
642 if (!msg->Output.Close.Error && msg->Input.Close.Callback)
643 TCPFinEventHandler(msg->Input.Close.Connection, ERR_OK);
644 break;
645
646 default:
647 if (msg->Input.Close.Connection->SendShutdown &&
648 msg->Input.Close.Connection->ReceiveShutdown)
649 {
650 /* Abort the connection */
651 tcp_abort(pcb);
652
653 /* Aborts always succeed */
654 msg->Output.Close.Error = ERR_OK;
655 }
656 else
657 {
658 /* Start the graceful close process (or send RST for pending data) */
659 msg->Output.Close.Error = tcp_close(pcb);
660 }
661 break;
662 }
663
664 if (msg->Output.Close.Error)
665 {
666 /* Restore the PCB pointer */
667 msg->Input.Close.Connection->SocketContext = pcb;
668 }
669
670 done:
671 KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
672 }
673
674 err_t
675 LibTCPClose(PCONNECTION_ENDPOINT Connection, const int safe, const int callback)
676 {
677 err_t ret;
678 struct lwip_callback_msg *msg;
679
680 msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
681 if (msg)
682 {
683 KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
684
685 msg->Input.Close.Connection = Connection;
686 msg->Input.Close.Callback = callback;
687
688 if (safe)
689 LibTCPCloseCallback(msg);
690 else
691 tcpip_callback_with_block(LibTCPCloseCallback, msg, 1);
692
693 if (WaitForEventSafely(&msg->Event))
694 ret = msg->Output.Close.Error;
695 else
696 ret = ERR_CLSD;
697
698 ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
699
700 return ret;
701 }
702
703 return ERR_MEM;
704 }
705
706 void
707 LibTCPAccept(PTCP_PCB pcb, struct tcp_pcb *listen_pcb, void *arg)
708 {
709 ASSERT(arg);
710
711 tcp_arg(pcb, NULL);
712 tcp_recv(pcb, InternalRecvEventHandler);
713 tcp_sent(pcb, InternalSendEventHandler);
714 tcp_err(pcb, InternalErrorEventHandler);
715 tcp_arg(pcb, arg);
716
717 tcp_accepted(listen_pcb);
718 }
719
720 err_t
721 LibTCPGetHostName(PTCP_PCB pcb, struct ip_addr *const ipaddr, u16_t *const port)
722 {
723 if (!pcb)
724 return ERR_CLSD;
725
726 *ipaddr = pcb->local_ip;
727 *port = pcb->local_port;
728
729 return ERR_OK;
730 }
731
732 err_t
733 LibTCPGetPeerName(PTCP_PCB pcb, struct ip_addr * const ipaddr, u16_t * const port)
734 {
735 if (!pcb)
736 return ERR_CLSD;
737
738 *ipaddr = pcb->remote_ip;
739 *port = pcb->remote_port;
740
741 return ERR_OK;
742 }