2 #include "lwip/netif.h"
3 #include "lwip/tcpip.h"
9 static const char * const tcp_state_str
[] = {
23 /* The way that lwIP does multi-threading is really not ideal for our purposes but
24 * we best go along with it unless we want another unstable TCP library. lwIP uses
25 * a thread called the "tcpip thread" which is the only one allowed to call raw API
26 * functions. Since this is the case, for each of our LibTCP* functions, we queue a request
27 * for a callback to "tcpip thread" which calls our LibTCP*Callback functions. Yes, this is
28 * a lot of unnecessary thread swapping and it could definitely be faster, but I don't want
29 * to going messing around in lwIP because I have no desire to create another mess like oskittcp */
31 extern KEVENT TerminationEvent
;
32 extern NPAGED_LOOKASIDE_LIST MessageLookasideList
;
33 extern NPAGED_LOOKASIDE_LIST QueueEntryLookasideList
;
35 /* Required for ERR_T to NTSTATUS translation in receive error handling */
36 NTSTATUS
TCPTranslateError(const err_t err
);
40 LibTCPEmptyQueue(PCONNECTION_ENDPOINT Connection
)
43 PQUEUE_ENTRY qp
= NULL
;
45 ReferenceObject(Connection
);
47 while (!IsListEmpty(&Connection
->PacketQueue
))
49 Entry
= RemoveHeadList(&Connection
->PacketQueue
);
50 qp
= CONTAINING_RECORD(Entry
, QUEUE_ENTRY
, ListEntry
);
52 /* We're in the tcpip thread here so this is safe */
55 ExFreeToNPagedLookasideList(&QueueEntryLookasideList
, qp
);
58 DereferenceObject(Connection
);
61 void LibTCPEnqueuePacket(PCONNECTION_ENDPOINT Connection
, struct pbuf
*p
)
65 qp
= (PQUEUE_ENTRY
)ExAllocateFromNPagedLookasideList(&QueueEntryLookasideList
);
69 ExInterlockedInsertTailList(&Connection
->PacketQueue
, &qp
->ListEntry
, &Connection
->Lock
);
72 PQUEUE_ENTRY
LibTCPDequeuePacket(PCONNECTION_ENDPOINT Connection
)
75 PQUEUE_ENTRY qp
= NULL
;
77 if (IsListEmpty(&Connection
->PacketQueue
)) return NULL
;
79 Entry
= RemoveHeadList(&Connection
->PacketQueue
);
81 qp
= CONTAINING_RECORD(Entry
, QUEUE_ENTRY
, ListEntry
);
86 NTSTATUS
LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection
, PUCHAR RecvBuffer
, UINT RecvLen
, UINT
*Received
)
91 UINT ReadLength
, PayloadLength
, Offset
, Copied
;
96 LockObject(Connection
, &OldIrql
);
98 if (!IsListEmpty(&Connection
->PacketQueue
))
100 while ((qp
= LibTCPDequeuePacket(Connection
)) != NULL
)
104 /* Calculate the payload length first */
105 PayloadLength
= p
->tot_len
;
106 PayloadLength
-= qp
->Offset
;
109 /* Check if we're reading the whole buffer */
110 ReadLength
= MIN(PayloadLength
, RecvLen
);
111 ASSERT(ReadLength
!= 0);
112 if (ReadLength
!= PayloadLength
)
114 /* Save this one for later */
115 qp
->Offset
+= ReadLength
;
116 InsertHeadList(&Connection
->PacketQueue
, &qp
->ListEntry
);
120 UnlockObject(Connection
, OldIrql
);
122 Copied
= pbuf_copy_partial(p
, RecvBuffer
, ReadLength
, Offset
);
123 ASSERT(Copied
== ReadLength
);
125 LockObject(Connection
, &OldIrql
);
127 /* Update trackers */
128 RecvLen
-= ReadLength
;
129 RecvBuffer
+= ReadLength
;
130 (*Received
) += ReadLength
;
134 /* Use this special pbuf free callback function because we're outside tcpip thread */
135 pbuf_free_callback(qp
->p
);
137 ExFreeToNPagedLookasideList(&QueueEntryLookasideList
, qp
);
141 /* If we get here, it means we've filled the buffer */
142 ASSERT(RecvLen
== 0);
145 ASSERT((*Received
) != 0);
146 Status
= STATUS_SUCCESS
;
154 if (Connection
->ReceiveShutdown
)
155 Status
= Connection
->ReceiveShutdownStatus
;
157 Status
= STATUS_PENDING
;
160 UnlockObject(Connection
, OldIrql
);
167 WaitForEventSafely(PRKEVENT Event
)
169 PVOID WaitObjects
[] = {Event
, &TerminationEvent
};
171 if (KeWaitForMultipleObjects(2,
178 NULL
) == STATUS_WAIT_0
)
180 /* Signalled by the caller's event */
183 else /* if KeWaitForMultipleObjects() == STATUS_WAIT_1 */
185 /* Signalled by our termination event */
192 InternalSendEventHandler(void *arg
, PTCP_PCB pcb
, const u16_t space
)
194 /* Make sure the socket didn't get closed */
195 if (!arg
) return ERR_OK
;
197 TCPSendEventHandler(arg
, space
);
204 InternalRecvEventHandler(void *arg
, PTCP_PCB pcb
, struct pbuf
*p
, const err_t err
)
206 PCONNECTION_ENDPOINT Connection
= arg
;
208 /* Make sure the socket didn't get closed */
219 LibTCPEnqueuePacket(Connection
, p
);
221 tcp_recved(pcb
, p
->tot_len
);
223 TCPRecvEventHandler(arg
);
225 else if (err
== ERR_OK
)
227 /* Complete pending reads with 0 bytes to indicate a graceful closure,
228 * but note that send is still possible in this state so we don't close the
229 * whole socket here (by calling tcp_close()) as that would violate TCP specs
231 Connection
->ReceiveShutdown
= TRUE
;
232 Connection
->ReceiveShutdownStatus
= STATUS_SUCCESS
;
234 /* This code path executes for both remotely and locally initiated closures,
235 * and we need to distinguish between them */
236 if (Connection
->SocketContext
)
238 /* Remotely initiated close */
239 TCPRecvEventHandler(arg
);
243 /* Locally initated close */
244 TCPFinEventHandler(arg
, ERR_CLSD
);
251 /* This function MUST return an error value that is not ERR_ABRT or ERR_OK if the connection
252 * is not accepted to avoid leaking the new PCB */
255 InternalAcceptEventHandler(void *arg
, PTCP_PCB newpcb
, const err_t err
)
257 /* Make sure the socket didn't get closed */
261 TCPAcceptEventHandler(arg
, newpcb
);
263 /* Set in LibTCPAccept (called from TCPAcceptEventHandler) */
264 if (newpcb
->callback_arg
)
272 InternalConnectEventHandler(void *arg
, PTCP_PCB pcb
, const err_t err
)
274 /* Make sure the socket didn't get closed */
278 TCPConnectEventHandler(arg
, err
);
285 InternalErrorEventHandler(void *arg
, const err_t err
)
287 PCONNECTION_ENDPOINT Connection
= arg
;
290 /* Make sure the socket didn't get closed */
293 /* Check if data is left to be read */
294 LockObject(Connection
, &OldIrql
);
295 if (IsListEmpty(&Connection
->PacketQueue
))
297 UnlockObject(Connection
, OldIrql
);
299 /* Deliver the error now */
300 TCPFinEventHandler(arg
, err
);
304 UnlockObject(Connection
, OldIrql
);
306 /* Defer the error delivery until all data is gone */
307 Connection
->ReceiveShutdown
= TRUE
;
308 Connection
->ReceiveShutdownStatus
= TCPTranslateError(err
);
310 TCPRecvEventHandler(arg
);
316 LibTCPSocketCallback(void *arg
)
318 struct lwip_callback_msg
*msg
= arg
;
322 msg
->Output
.Socket
.NewPcb
= tcp_new();
324 if (msg
->Output
.Socket
.NewPcb
)
326 tcp_arg(msg
->Output
.Socket
.NewPcb
, msg
->Input
.Socket
.Arg
);
327 tcp_err(msg
->Output
.Socket
.NewPcb
, InternalErrorEventHandler
);
330 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
334 LibTCPSocket(void *arg
)
336 struct lwip_callback_msg
*msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
341 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
342 msg
->Input
.Socket
.Arg
= arg
;
344 tcpip_callback_with_block(LibTCPSocketCallback
, msg
, 1);
346 if (WaitForEventSafely(&msg
->Event
))
347 ret
= msg
->Output
.Socket
.NewPcb
;
351 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
361 LibTCPBindCallback(void *arg
)
363 struct lwip_callback_msg
*msg
= arg
;
364 PTCP_PCB pcb
= msg
->Input
.Bind
.Connection
->SocketContext
;
368 if (!msg
->Input
.Bind
.Connection
->SocketContext
)
370 msg
->Output
.Bind
.Error
= ERR_CLSD
;
374 /* We're guaranteed that the local address is valid to bind at this point */
375 pcb
->so_options
|= SOF_REUSEADDR
;
377 msg
->Output
.Bind
.Error
= tcp_bind(pcb
,
378 msg
->Input
.Bind
.IpAddress
,
379 ntohs(msg
->Input
.Bind
.Port
));
382 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
386 LibTCPBind(PCONNECTION_ENDPOINT Connection
, struct ip_addr
*const ipaddr
, const u16_t port
)
388 struct lwip_callback_msg
*msg
;
391 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
394 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
395 msg
->Input
.Bind
.Connection
= Connection
;
396 msg
->Input
.Bind
.IpAddress
= ipaddr
;
397 msg
->Input
.Bind
.Port
= port
;
399 tcpip_callback_with_block(LibTCPBindCallback
, msg
, 1);
401 if (WaitForEventSafely(&msg
->Event
))
402 ret
= msg
->Output
.Bind
.Error
;
406 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
416 LibTCPListenCallback(void *arg
)
418 struct lwip_callback_msg
*msg
= arg
;
422 if (!msg
->Input
.Listen
.Connection
->SocketContext
)
424 msg
->Output
.Listen
.NewPcb
= NULL
;
428 msg
->Output
.Listen
.NewPcb
= tcp_listen_with_backlog((PTCP_PCB
)msg
->Input
.Listen
.Connection
->SocketContext
, msg
->Input
.Listen
.Backlog
);
430 if (msg
->Output
.Listen
.NewPcb
)
432 tcp_accept(msg
->Output
.Listen
.NewPcb
, InternalAcceptEventHandler
);
436 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
440 LibTCPListen(PCONNECTION_ENDPOINT Connection
, const u8_t backlog
)
442 struct lwip_callback_msg
*msg
;
445 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
448 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
449 msg
->Input
.Listen
.Connection
= Connection
;
450 msg
->Input
.Listen
.Backlog
= backlog
;
452 tcpip_callback_with_block(LibTCPListenCallback
, msg
, 1);
454 if (WaitForEventSafely(&msg
->Event
))
455 ret
= msg
->Output
.Listen
.NewPcb
;
459 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
469 LibTCPSendCallback(void *arg
)
471 struct lwip_callback_msg
*msg
= arg
;
472 PTCP_PCB pcb
= msg
->Input
.Send
.Connection
->SocketContext
;
478 if (!msg
->Input
.Send
.Connection
->SocketContext
)
480 msg
->Output
.Send
.Error
= ERR_CLSD
;
484 if (msg
->Input
.Send
.Connection
->SendShutdown
)
486 msg
->Output
.Send
.Error
= ERR_CLSD
;
490 SendFlags
= TCP_WRITE_FLAG_COPY
;
491 SendLength
= msg
->Input
.Send
.DataLength
;
492 if (tcp_sndbuf(pcb
) == 0)
494 /* No buffer space so return pending */
495 msg
->Output
.Send
.Error
= ERR_INPROGRESS
;
498 else if (tcp_sndbuf(pcb
) < SendLength
)
500 /* We've got some room so let's send what we can */
501 SendLength
= tcp_sndbuf(pcb
);
503 /* Don't set the push flag */
504 SendFlags
|= TCP_WRITE_FLAG_MORE
;
507 msg
->Output
.Send
.Error
= tcp_write(pcb
,
508 msg
->Input
.Send
.Data
,
511 if (msg
->Output
.Send
.Error
== ERR_OK
)
513 /* Queued successfully so try to send it */
514 tcp_output((PTCP_PCB
)msg
->Input
.Send
.Connection
->SocketContext
);
515 msg
->Output
.Send
.Information
= SendLength
;
517 else if (msg
->Output
.Send
.Error
== ERR_MEM
)
519 /* The queue is too long */
520 msg
->Output
.Send
.Error
= ERR_INPROGRESS
;
524 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
528 LibTCPSend(PCONNECTION_ENDPOINT Connection
, void *const dataptr
, const u16_t len
, u32_t
*sent
, const int safe
)
531 struct lwip_callback_msg
*msg
;
533 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
536 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
537 msg
->Input
.Send
.Connection
= Connection
;
538 msg
->Input
.Send
.Data
= dataptr
;
539 msg
->Input
.Send
.DataLength
= len
;
542 LibTCPSendCallback(msg
);
544 tcpip_callback_with_block(LibTCPSendCallback
, msg
, 1);
546 if (WaitForEventSafely(&msg
->Event
))
547 ret
= msg
->Output
.Send
.Error
;
552 *sent
= msg
->Output
.Send
.Information
;
556 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
566 LibTCPConnectCallback(void *arg
)
568 struct lwip_callback_msg
*msg
= arg
;
573 if (!msg
->Input
.Connect
.Connection
->SocketContext
)
575 msg
->Output
.Connect
.Error
= ERR_CLSD
;
579 tcp_recv((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
, InternalRecvEventHandler
);
580 tcp_sent((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
, InternalSendEventHandler
);
582 Error
= tcp_connect((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
,
583 msg
->Input
.Connect
.IpAddress
, ntohs(msg
->Input
.Connect
.Port
),
584 InternalConnectEventHandler
);
586 msg
->Output
.Connect
.Error
= Error
== ERR_OK
? ERR_INPROGRESS
: Error
;
589 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
593 LibTCPConnect(PCONNECTION_ENDPOINT Connection
, struct ip_addr
*const ipaddr
, const u16_t port
)
595 struct lwip_callback_msg
*msg
;
598 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
601 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
602 msg
->Input
.Connect
.Connection
= Connection
;
603 msg
->Input
.Connect
.IpAddress
= ipaddr
;
604 msg
->Input
.Connect
.Port
= port
;
606 tcpip_callback_with_block(LibTCPConnectCallback
, msg
, 1);
608 if (WaitForEventSafely(&msg
->Event
))
610 ret
= msg
->Output
.Connect
.Error
;
615 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
625 LibTCPShutdownCallback(void *arg
)
627 struct lwip_callback_msg
*msg
= arg
;
628 PTCP_PCB pcb
= msg
->Input
.Shutdown
.Connection
->SocketContext
;
630 if (!msg
->Input
.Shutdown
.Connection
->SocketContext
)
632 msg
->Output
.Shutdown
.Error
= ERR_CLSD
;
636 /* These need to be called separately, otherwise we get a tcp_close() */
637 if (msg
->Input
.Shutdown
.shut_rx
) {
638 msg
->Output
.Shutdown
.Error
= tcp_shutdown(pcb
, TRUE
, FALSE
);
640 if (msg
->Input
.Shutdown
.shut_tx
) {
641 msg
->Output
.Shutdown
.Error
= tcp_shutdown(pcb
, FALSE
, TRUE
);
644 if (!msg
->Output
.Shutdown
.Error
)
646 if (msg
->Input
.Shutdown
.shut_rx
)
648 msg
->Input
.Shutdown
.Connection
->ReceiveShutdown
= TRUE
;
649 msg
->Input
.Shutdown
.Connection
->ReceiveShutdownStatus
= STATUS_FILE_CLOSED
;
652 if (msg
->Input
.Shutdown
.shut_tx
)
653 msg
->Input
.Shutdown
.Connection
->SendShutdown
= TRUE
;
657 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
661 LibTCPShutdown(PCONNECTION_ENDPOINT Connection
, const int shut_rx
, const int shut_tx
)
663 struct lwip_callback_msg
*msg
;
666 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
669 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
671 msg
->Input
.Shutdown
.Connection
= Connection
;
672 msg
->Input
.Shutdown
.shut_rx
= shut_rx
;
673 msg
->Input
.Shutdown
.shut_tx
= shut_tx
;
675 tcpip_callback_with_block(LibTCPShutdownCallback
, msg
, 1);
677 if (WaitForEventSafely(&msg
->Event
))
678 ret
= msg
->Output
.Shutdown
.Error
;
682 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
692 LibTCPCloseCallback(void *arg
)
694 struct lwip_callback_msg
*msg
= arg
;
695 PTCP_PCB pcb
= msg
->Input
.Close
.Connection
->SocketContext
;
697 /* Empty the queue even if we're already "closed" */
698 LibTCPEmptyQueue(msg
->Input
.Close
.Connection
);
700 if (!msg
->Input
.Close
.Connection
->SocketContext
)
702 msg
->Output
.Close
.Error
= ERR_OK
;
706 /* Clear the PCB pointer */
707 msg
->Input
.Close
.Connection
->SocketContext
= NULL
;
714 msg
->Output
.Close
.Error
= tcp_close(pcb
);
716 if (!msg
->Output
.Close
.Error
&& msg
->Input
.Close
.Callback
)
717 TCPFinEventHandler(msg
->Input
.Close
.Connection
, ERR_CLSD
);
721 /* Abort the socket */
723 msg
->Output
.Close
.Error
= ERR_OK
;
727 if (msg
->Output
.Close
.Error
)
729 /* Restore the PCB pointer */
730 msg
->Input
.Close
.Connection
->SocketContext
= pcb
;
734 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
738 LibTCPClose(PCONNECTION_ENDPOINT Connection
, const int safe
, const int callback
)
741 struct lwip_callback_msg
*msg
;
743 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
746 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
748 msg
->Input
.Close
.Connection
= Connection
;
749 msg
->Input
.Close
.Callback
= callback
;
752 LibTCPCloseCallback(msg
);
754 tcpip_callback_with_block(LibTCPCloseCallback
, msg
, 1);
756 if (WaitForEventSafely(&msg
->Event
))
757 ret
= msg
->Output
.Close
.Error
;
761 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
770 LibTCPAccept(PTCP_PCB pcb
, struct tcp_pcb
*listen_pcb
, void *arg
)
775 tcp_recv(pcb
, InternalRecvEventHandler
);
776 tcp_sent(pcb
, InternalSendEventHandler
);
777 tcp_err(pcb
, InternalErrorEventHandler
);
780 tcp_accepted(listen_pcb
);
784 LibTCPGetHostName(PTCP_PCB pcb
, struct ip_addr
*const ipaddr
, u16_t
*const port
)
789 *ipaddr
= pcb
->local_ip
;
790 *port
= pcb
->local_port
;
796 LibTCPGetPeerName(PTCP_PCB pcb
, struct ip_addr
* const ipaddr
, u16_t
* const port
)
801 *ipaddr
= pcb
->remote_ip
;
802 *port
= pcb
->remote_port
;