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
);
253 InternalAcceptEventHandler(void *arg
, PTCP_PCB newpcb
, const err_t err
)
255 /* Make sure the socket didn't get closed */
259 TCPAcceptEventHandler(arg
, newpcb
);
261 /* Set in LibTCPAccept (called from TCPAcceptEventHandler) */
262 if (newpcb
->callback_arg
)
270 InternalConnectEventHandler(void *arg
, PTCP_PCB pcb
, const err_t err
)
272 /* Make sure the socket didn't get closed */
276 TCPConnectEventHandler(arg
, err
);
283 InternalErrorEventHandler(void *arg
, const err_t err
)
285 PCONNECTION_ENDPOINT Connection
= arg
;
288 /* Make sure the socket didn't get closed */
291 /* Check if data is left to be read */
292 LockObject(Connection
, &OldIrql
);
293 if (IsListEmpty(&Connection
->PacketQueue
))
295 UnlockObject(Connection
, OldIrql
);
297 /* Deliver the error now */
298 TCPFinEventHandler(arg
, err
);
302 UnlockObject(Connection
, OldIrql
);
304 /* Defer the error delivery until all data is gone */
305 Connection
->ReceiveShutdown
= TRUE
;
306 Connection
->ReceiveShutdownStatus
= TCPTranslateError(err
);
308 TCPRecvEventHandler(arg
);
314 LibTCPSocketCallback(void *arg
)
316 struct lwip_callback_msg
*msg
= arg
;
320 msg
->Output
.Socket
.NewPcb
= tcp_new();
322 if (msg
->Output
.Socket
.NewPcb
)
324 tcp_arg(msg
->Output
.Socket
.NewPcb
, msg
->Input
.Socket
.Arg
);
325 tcp_err(msg
->Output
.Socket
.NewPcb
, InternalErrorEventHandler
);
328 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
332 LibTCPSocket(void *arg
)
334 struct lwip_callback_msg
*msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
339 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
340 msg
->Input
.Socket
.Arg
= arg
;
342 tcpip_callback_with_block(LibTCPSocketCallback
, msg
, 1);
344 if (WaitForEventSafely(&msg
->Event
))
345 ret
= msg
->Output
.Socket
.NewPcb
;
349 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
359 LibTCPBindCallback(void *arg
)
361 struct lwip_callback_msg
*msg
= arg
;
362 PTCP_PCB pcb
= msg
->Input
.Bind
.Connection
->SocketContext
;
366 if (!msg
->Input
.Bind
.Connection
->SocketContext
)
368 msg
->Output
.Bind
.Error
= ERR_CLSD
;
372 /* We're guaranteed that the local address is valid to bind at this point */
373 pcb
->so_options
|= SOF_REUSEADDR
;
375 msg
->Output
.Bind
.Error
= tcp_bind(pcb
,
376 msg
->Input
.Bind
.IpAddress
,
377 ntohs(msg
->Input
.Bind
.Port
));
380 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
384 LibTCPBind(PCONNECTION_ENDPOINT Connection
, struct ip_addr
*const ipaddr
, const u16_t port
)
386 struct lwip_callback_msg
*msg
;
389 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
392 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
393 msg
->Input
.Bind
.Connection
= Connection
;
394 msg
->Input
.Bind
.IpAddress
= ipaddr
;
395 msg
->Input
.Bind
.Port
= port
;
397 tcpip_callback_with_block(LibTCPBindCallback
, msg
, 1);
399 if (WaitForEventSafely(&msg
->Event
))
400 ret
= msg
->Output
.Bind
.Error
;
404 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
414 LibTCPListenCallback(void *arg
)
416 struct lwip_callback_msg
*msg
= arg
;
420 if (!msg
->Input
.Listen
.Connection
->SocketContext
)
422 msg
->Output
.Listen
.NewPcb
= NULL
;
426 msg
->Output
.Listen
.NewPcb
= tcp_listen_with_backlog((PTCP_PCB
)msg
->Input
.Listen
.Connection
->SocketContext
, msg
->Input
.Listen
.Backlog
);
428 if (msg
->Output
.Listen
.NewPcb
)
430 tcp_accept(msg
->Output
.Listen
.NewPcb
, InternalAcceptEventHandler
);
434 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
438 LibTCPListen(PCONNECTION_ENDPOINT Connection
, const u8_t backlog
)
440 struct lwip_callback_msg
*msg
;
443 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
446 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
447 msg
->Input
.Listen
.Connection
= Connection
;
448 msg
->Input
.Listen
.Backlog
= backlog
;
450 tcpip_callback_with_block(LibTCPListenCallback
, msg
, 1);
452 if (WaitForEventSafely(&msg
->Event
))
453 ret
= msg
->Output
.Listen
.NewPcb
;
457 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
467 LibTCPSendCallback(void *arg
)
469 struct lwip_callback_msg
*msg
= arg
;
470 PTCP_PCB pcb
= msg
->Input
.Send
.Connection
->SocketContext
;
476 if (!msg
->Input
.Send
.Connection
->SocketContext
)
478 msg
->Output
.Send
.Error
= ERR_CLSD
;
482 if (msg
->Input
.Send
.Connection
->SendShutdown
)
484 msg
->Output
.Send
.Error
= ERR_CLSD
;
488 SendFlags
= TCP_WRITE_FLAG_COPY
;
489 SendLength
= msg
->Input
.Send
.DataLength
;
490 if (tcp_sndbuf(pcb
) == 0)
492 /* No buffer space so return pending */
493 msg
->Output
.Send
.Error
= ERR_INPROGRESS
;
496 else if (tcp_sndbuf(pcb
) < SendLength
)
498 /* We've got some room so let's send what we can */
499 SendLength
= tcp_sndbuf(pcb
);
501 /* Don't set the push flag */
502 SendFlags
|= TCP_WRITE_FLAG_MORE
;
505 msg
->Output
.Send
.Error
= tcp_write(pcb
,
506 msg
->Input
.Send
.Data
,
509 if (msg
->Output
.Send
.Error
== ERR_OK
)
511 /* Queued successfully so try to send it */
512 tcp_output((PTCP_PCB
)msg
->Input
.Send
.Connection
->SocketContext
);
513 msg
->Output
.Send
.Information
= SendLength
;
515 else if (msg
->Output
.Send
.Error
== ERR_MEM
)
517 /* The queue is too long */
518 msg
->Output
.Send
.Error
= ERR_INPROGRESS
;
522 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
526 LibTCPSend(PCONNECTION_ENDPOINT Connection
, void *const dataptr
, const u16_t len
, u32_t
*sent
, const int safe
)
529 struct lwip_callback_msg
*msg
;
531 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
534 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
535 msg
->Input
.Send
.Connection
= Connection
;
536 msg
->Input
.Send
.Data
= dataptr
;
537 msg
->Input
.Send
.DataLength
= len
;
540 LibTCPSendCallback(msg
);
542 tcpip_callback_with_block(LibTCPSendCallback
, msg
, 1);
544 if (WaitForEventSafely(&msg
->Event
))
545 ret
= msg
->Output
.Send
.Error
;
550 *sent
= msg
->Output
.Send
.Information
;
554 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
564 LibTCPConnectCallback(void *arg
)
566 struct lwip_callback_msg
*msg
= arg
;
571 if (!msg
->Input
.Connect
.Connection
->SocketContext
)
573 msg
->Output
.Connect
.Error
= ERR_CLSD
;
577 tcp_recv((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
, InternalRecvEventHandler
);
578 tcp_sent((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
, InternalSendEventHandler
);
580 Error
= tcp_connect((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
,
581 msg
->Input
.Connect
.IpAddress
, ntohs(msg
->Input
.Connect
.Port
),
582 InternalConnectEventHandler
);
584 msg
->Output
.Connect
.Error
= Error
== ERR_OK
? ERR_INPROGRESS
: Error
;
587 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
591 LibTCPConnect(PCONNECTION_ENDPOINT Connection
, struct ip_addr
*const ipaddr
, const u16_t port
)
593 struct lwip_callback_msg
*msg
;
596 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
599 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
600 msg
->Input
.Connect
.Connection
= Connection
;
601 msg
->Input
.Connect
.IpAddress
= ipaddr
;
602 msg
->Input
.Connect
.Port
= port
;
604 tcpip_callback_with_block(LibTCPConnectCallback
, msg
, 1);
606 if (WaitForEventSafely(&msg
->Event
))
608 ret
= msg
->Output
.Connect
.Error
;
613 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
623 LibTCPShutdownCallback(void *arg
)
625 struct lwip_callback_msg
*msg
= arg
;
626 PTCP_PCB pcb
= msg
->Input
.Shutdown
.Connection
->SocketContext
;
628 if (!msg
->Input
.Shutdown
.Connection
->SocketContext
)
630 msg
->Output
.Shutdown
.Error
= ERR_CLSD
;
634 /* These need to be called separately, otherwise we get a tcp_close() */
635 if (msg
->Input
.Shutdown
.shut_rx
) {
636 msg
->Output
.Shutdown
.Error
= tcp_shutdown(pcb
, TRUE
, FALSE
);
638 if (msg
->Input
.Shutdown
.shut_tx
) {
639 msg
->Output
.Shutdown
.Error
= tcp_shutdown(pcb
, FALSE
, TRUE
);
642 if (!msg
->Output
.Shutdown
.Error
)
644 if (msg
->Input
.Shutdown
.shut_rx
)
646 msg
->Input
.Shutdown
.Connection
->ReceiveShutdown
= TRUE
;
647 msg
->Input
.Shutdown
.Connection
->ReceiveShutdownStatus
= STATUS_FILE_CLOSED
;
650 if (msg
->Input
.Shutdown
.shut_tx
)
651 msg
->Input
.Shutdown
.Connection
->SendShutdown
= TRUE
;
655 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
659 LibTCPShutdown(PCONNECTION_ENDPOINT Connection
, const int shut_rx
, const int shut_tx
)
661 struct lwip_callback_msg
*msg
;
664 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
667 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
669 msg
->Input
.Shutdown
.Connection
= Connection
;
670 msg
->Input
.Shutdown
.shut_rx
= shut_rx
;
671 msg
->Input
.Shutdown
.shut_tx
= shut_tx
;
673 tcpip_callback_with_block(LibTCPShutdownCallback
, msg
, 1);
675 if (WaitForEventSafely(&msg
->Event
))
676 ret
= msg
->Output
.Shutdown
.Error
;
680 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
690 LibTCPCloseCallback(void *arg
)
692 struct lwip_callback_msg
*msg
= arg
;
693 PTCP_PCB pcb
= msg
->Input
.Close
.Connection
->SocketContext
;
695 /* Empty the queue even if we're already "closed" */
696 LibTCPEmptyQueue(msg
->Input
.Close
.Connection
);
698 if (!msg
->Input
.Close
.Connection
->SocketContext
)
700 msg
->Output
.Close
.Error
= ERR_OK
;
704 /* Clear the PCB pointer */
705 msg
->Input
.Close
.Connection
->SocketContext
= NULL
;
712 msg
->Output
.Close
.Error
= tcp_close(pcb
);
714 if (!msg
->Output
.Close
.Error
&& msg
->Input
.Close
.Callback
)
715 TCPFinEventHandler(msg
->Input
.Close
.Connection
, ERR_CLSD
);
719 /* Abort the socket */
721 msg
->Output
.Close
.Error
= ERR_OK
;
725 if (msg
->Output
.Close
.Error
)
727 /* Restore the PCB pointer */
728 msg
->Input
.Close
.Connection
->SocketContext
= pcb
;
732 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
736 LibTCPClose(PCONNECTION_ENDPOINT Connection
, const int safe
, const int callback
)
739 struct lwip_callback_msg
*msg
;
741 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
744 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
746 msg
->Input
.Close
.Connection
= Connection
;
747 msg
->Input
.Close
.Callback
= callback
;
750 LibTCPCloseCallback(msg
);
752 tcpip_callback_with_block(LibTCPCloseCallback
, msg
, 1);
754 if (WaitForEventSafely(&msg
->Event
))
755 ret
= msg
->Output
.Close
.Error
;
759 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
768 LibTCPAccept(PTCP_PCB pcb
, struct tcp_pcb
*listen_pcb
, void *arg
)
773 tcp_recv(pcb
, InternalRecvEventHandler
);
774 tcp_sent(pcb
, InternalSendEventHandler
);
775 tcp_err(pcb
, InternalErrorEventHandler
);
778 tcp_accepted(listen_pcb
);
782 LibTCPGetHostName(PTCP_PCB pcb
, struct ip_addr
*const ipaddr
, u16_t
*const port
)
787 *ipaddr
= pcb
->local_ip
;
788 *port
= pcb
->local_port
;
794 LibTCPGetPeerName(PTCP_PCB pcb
, struct ip_addr
* const ipaddr
, u16_t
* const port
)
799 *ipaddr
= pcb
->remote_ip
;
800 *port
= pcb
->remote_port
;