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
);
39 LibTCPDumpPcb(PVOID SocketContext
)
41 struct tcp_pcb
*pcb
= (struct tcp_pcb
*)SocketContext
;
42 unsigned int addr
= ntohl(pcb
->remote_ip
.addr
);
44 DbgPrint("\tState: %s\n", tcp_state_str
[pcb
->state
]);
45 DbgPrint("\tRemote: (%d.%d.%d.%d, %d)\n",
55 LibTCPEmptyQueue(PCONNECTION_ENDPOINT Connection
)
58 PQUEUE_ENTRY qp
= NULL
;
60 ReferenceObject(Connection
);
62 while (!IsListEmpty(&Connection
->PacketQueue
))
64 Entry
= RemoveHeadList(&Connection
->PacketQueue
);
65 qp
= CONTAINING_RECORD(Entry
, QUEUE_ENTRY
, ListEntry
);
67 /* We're in the tcpip thread here so this is safe */
70 ExFreeToNPagedLookasideList(&QueueEntryLookasideList
, qp
);
73 DereferenceObject(Connection
);
76 void LibTCPEnqueuePacket(PCONNECTION_ENDPOINT Connection
, struct pbuf
*p
)
80 qp
= (PQUEUE_ENTRY
)ExAllocateFromNPagedLookasideList(&QueueEntryLookasideList
);
84 ExInterlockedInsertTailList(&Connection
->PacketQueue
, &qp
->ListEntry
, &Connection
->Lock
);
87 PQUEUE_ENTRY
LibTCPDequeuePacket(PCONNECTION_ENDPOINT Connection
)
90 PQUEUE_ENTRY qp
= NULL
;
92 if (IsListEmpty(&Connection
->PacketQueue
)) return NULL
;
94 Entry
= RemoveHeadList(&Connection
->PacketQueue
);
96 qp
= CONTAINING_RECORD(Entry
, QUEUE_ENTRY
, ListEntry
);
101 NTSTATUS
LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection
, PUCHAR RecvBuffer
, UINT RecvLen
, UINT
*Received
)
106 UINT ReadLength
, PayloadLength
, Offset
, Copied
;
111 LockObject(Connection
, &OldIrql
);
113 if (!IsListEmpty(&Connection
->PacketQueue
))
115 while ((qp
= LibTCPDequeuePacket(Connection
)) != NULL
)
119 /* Calculate the payload length first */
120 PayloadLength
= p
->tot_len
;
121 PayloadLength
-= qp
->Offset
;
124 /* Check if we're reading the whole buffer */
125 ReadLength
= MIN(PayloadLength
, RecvLen
);
126 ASSERT(ReadLength
!= 0);
127 if (ReadLength
!= PayloadLength
)
129 /* Save this one for later */
130 qp
->Offset
+= ReadLength
;
131 InsertHeadList(&Connection
->PacketQueue
, &qp
->ListEntry
);
135 UnlockObject(Connection
, OldIrql
);
137 Copied
= pbuf_copy_partial(p
, RecvBuffer
, ReadLength
, Offset
);
138 ASSERT(Copied
== ReadLength
);
140 LockObject(Connection
, &OldIrql
);
142 /* Update trackers */
143 RecvLen
-= ReadLength
;
144 RecvBuffer
+= ReadLength
;
145 (*Received
) += ReadLength
;
149 /* Use this special pbuf free callback function because we're outside tcpip thread */
150 pbuf_free_callback(qp
->p
);
152 ExFreeToNPagedLookasideList(&QueueEntryLookasideList
, qp
);
156 /* If we get here, it means we've filled the buffer */
157 ASSERT(RecvLen
== 0);
160 ASSERT((*Received
) != 0);
161 Status
= STATUS_SUCCESS
;
169 if (Connection
->ReceiveShutdown
)
170 Status
= Connection
->ReceiveShutdownStatus
;
172 Status
= STATUS_PENDING
;
175 UnlockObject(Connection
, OldIrql
);
182 WaitForEventSafely(PRKEVENT Event
)
184 PVOID WaitObjects
[] = {Event
, &TerminationEvent
};
186 if (KeWaitForMultipleObjects(2,
193 NULL
) == STATUS_WAIT_0
)
195 /* Signalled by the caller's event */
198 else /* if KeWaitForMultipleObjects() == STATUS_WAIT_1 */
200 /* Signalled by our termination event */
207 InternalSendEventHandler(void *arg
, PTCP_PCB pcb
, const u16_t space
)
209 /* Make sure the socket didn't get closed */
210 if (!arg
) return ERR_OK
;
212 TCPSendEventHandler(arg
, space
);
219 InternalRecvEventHandler(void *arg
, PTCP_PCB pcb
, struct pbuf
*p
, const err_t err
)
221 PCONNECTION_ENDPOINT Connection
= arg
;
223 /* Make sure the socket didn't get closed */
234 LibTCPEnqueuePacket(Connection
, p
);
236 tcp_recved(pcb
, p
->tot_len
);
238 TCPRecvEventHandler(arg
);
240 else if (err
== ERR_OK
)
242 /* Complete pending reads with 0 bytes to indicate a graceful closure,
243 * but note that send is still possible in this state so we don't close the
244 * whole socket here (by calling tcp_close()) as that would violate TCP specs
246 Connection
->ReceiveShutdown
= TRUE
;
247 Connection
->ReceiveShutdownStatus
= STATUS_SUCCESS
;
249 /* If we already did a send shutdown, we're in TIME_WAIT so we can't use this PCB anymore */
250 if (Connection
->SendShutdown
)
252 Connection
->SocketContext
= NULL
;
256 /* Indicate the graceful close event */
257 TCPRecvEventHandler(arg
);
259 /* If the PCB is gone, clean up the connection */
260 if (Connection
->SendShutdown
)
262 TCPFinEventHandler(Connection
, ERR_CLSD
);
269 /* This function MUST return an error value that is not ERR_ABRT or ERR_OK if the connection
270 * is not accepted to avoid leaking the new PCB */
273 InternalAcceptEventHandler(void *arg
, PTCP_PCB newpcb
, const err_t err
)
275 /* Make sure the socket didn't get closed */
279 TCPAcceptEventHandler(arg
, newpcb
);
281 /* Set in LibTCPAccept (called from TCPAcceptEventHandler) */
282 if (newpcb
->callback_arg
)
290 InternalConnectEventHandler(void *arg
, PTCP_PCB pcb
, const err_t err
)
292 /* Make sure the socket didn't get closed */
296 TCPConnectEventHandler(arg
, err
);
303 InternalErrorEventHandler(void *arg
, const err_t err
)
305 PCONNECTION_ENDPOINT Connection
= arg
;
307 /* Make sure the socket didn't get closed */
308 if (!arg
|| Connection
->SocketContext
== NULL
) return;
310 /* The PCB is dead now */
311 Connection
->SocketContext
= NULL
;
313 /* Give them one shot to receive the remaining data */
314 Connection
->ReceiveShutdown
= TRUE
;
315 Connection
->ReceiveShutdownStatus
= TCPTranslateError(err
);
316 TCPRecvEventHandler(Connection
);
318 /* Terminate the connection */
319 TCPFinEventHandler(Connection
, err
);
324 LibTCPSocketCallback(void *arg
)
326 struct lwip_callback_msg
*msg
= arg
;
330 msg
->Output
.Socket
.NewPcb
= tcp_new();
332 if (msg
->Output
.Socket
.NewPcb
)
334 tcp_arg(msg
->Output
.Socket
.NewPcb
, msg
->Input
.Socket
.Arg
);
335 tcp_err(msg
->Output
.Socket
.NewPcb
, InternalErrorEventHandler
);
338 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
342 LibTCPSocket(void *arg
)
344 struct lwip_callback_msg
*msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
349 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
350 msg
->Input
.Socket
.Arg
= arg
;
352 tcpip_callback_with_block(LibTCPSocketCallback
, msg
, 1);
354 if (WaitForEventSafely(&msg
->Event
))
355 ret
= msg
->Output
.Socket
.NewPcb
;
359 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
369 LibTCPBindCallback(void *arg
)
371 struct lwip_callback_msg
*msg
= arg
;
372 PTCP_PCB pcb
= msg
->Input
.Bind
.Connection
->SocketContext
;
376 if (!msg
->Input
.Bind
.Connection
->SocketContext
)
378 msg
->Output
.Bind
.Error
= ERR_CLSD
;
382 /* We're guaranteed that the local address is valid to bind at this point */
383 pcb
->so_options
|= SOF_REUSEADDR
;
385 msg
->Output
.Bind
.Error
= tcp_bind(pcb
,
386 msg
->Input
.Bind
.IpAddress
,
387 ntohs(msg
->Input
.Bind
.Port
));
390 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
394 LibTCPBind(PCONNECTION_ENDPOINT Connection
, struct ip_addr
*const ipaddr
, const u16_t port
)
396 struct lwip_callback_msg
*msg
;
399 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
402 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
403 msg
->Input
.Bind
.Connection
= Connection
;
404 msg
->Input
.Bind
.IpAddress
= ipaddr
;
405 msg
->Input
.Bind
.Port
= port
;
407 tcpip_callback_with_block(LibTCPBindCallback
, msg
, 1);
409 if (WaitForEventSafely(&msg
->Event
))
410 ret
= msg
->Output
.Bind
.Error
;
414 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
424 LibTCPListenCallback(void *arg
)
426 struct lwip_callback_msg
*msg
= arg
;
430 if (!msg
->Input
.Listen
.Connection
->SocketContext
)
432 msg
->Output
.Listen
.NewPcb
= NULL
;
436 msg
->Output
.Listen
.NewPcb
= tcp_listen_with_backlog((PTCP_PCB
)msg
->Input
.Listen
.Connection
->SocketContext
, msg
->Input
.Listen
.Backlog
);
438 if (msg
->Output
.Listen
.NewPcb
)
440 tcp_accept(msg
->Output
.Listen
.NewPcb
, InternalAcceptEventHandler
);
444 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
448 LibTCPListen(PCONNECTION_ENDPOINT Connection
, const u8_t backlog
)
450 struct lwip_callback_msg
*msg
;
453 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
456 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
457 msg
->Input
.Listen
.Connection
= Connection
;
458 msg
->Input
.Listen
.Backlog
= backlog
;
460 tcpip_callback_with_block(LibTCPListenCallback
, msg
, 1);
462 if (WaitForEventSafely(&msg
->Event
))
463 ret
= msg
->Output
.Listen
.NewPcb
;
467 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
477 LibTCPSendCallback(void *arg
)
479 struct lwip_callback_msg
*msg
= arg
;
480 PTCP_PCB pcb
= msg
->Input
.Send
.Connection
->SocketContext
;
486 if (!msg
->Input
.Send
.Connection
->SocketContext
)
488 msg
->Output
.Send
.Error
= ERR_CLSD
;
492 if (msg
->Input
.Send
.Connection
->SendShutdown
)
494 msg
->Output
.Send
.Error
= ERR_CLSD
;
498 SendFlags
= TCP_WRITE_FLAG_COPY
;
499 SendLength
= msg
->Input
.Send
.DataLength
;
500 if (tcp_sndbuf(pcb
) == 0)
502 /* No buffer space so return pending */
503 msg
->Output
.Send
.Error
= ERR_INPROGRESS
;
506 else if (tcp_sndbuf(pcb
) < SendLength
)
508 /* We've got some room so let's send what we can */
509 SendLength
= tcp_sndbuf(pcb
);
511 /* Don't set the push flag */
512 SendFlags
|= TCP_WRITE_FLAG_MORE
;
515 msg
->Output
.Send
.Error
= tcp_write(pcb
,
516 msg
->Input
.Send
.Data
,
519 if (msg
->Output
.Send
.Error
== ERR_OK
)
521 /* Queued successfully so try to send it */
522 tcp_output((PTCP_PCB
)msg
->Input
.Send
.Connection
->SocketContext
);
523 msg
->Output
.Send
.Information
= SendLength
;
525 else if (msg
->Output
.Send
.Error
== ERR_MEM
)
527 /* The queue is too long */
528 msg
->Output
.Send
.Error
= ERR_INPROGRESS
;
532 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
536 LibTCPSend(PCONNECTION_ENDPOINT Connection
, void *const dataptr
, const u16_t len
, u32_t
*sent
, const int safe
)
539 struct lwip_callback_msg
*msg
;
541 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
544 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
545 msg
->Input
.Send
.Connection
= Connection
;
546 msg
->Input
.Send
.Data
= dataptr
;
547 msg
->Input
.Send
.DataLength
= len
;
550 LibTCPSendCallback(msg
);
552 tcpip_callback_with_block(LibTCPSendCallback
, msg
, 1);
554 if (WaitForEventSafely(&msg
->Event
))
555 ret
= msg
->Output
.Send
.Error
;
560 *sent
= msg
->Output
.Send
.Information
;
564 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
574 LibTCPConnectCallback(void *arg
)
576 struct lwip_callback_msg
*msg
= arg
;
581 if (!msg
->Input
.Connect
.Connection
->SocketContext
)
583 msg
->Output
.Connect
.Error
= ERR_CLSD
;
587 tcp_recv((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
, InternalRecvEventHandler
);
588 tcp_sent((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
, InternalSendEventHandler
);
590 Error
= tcp_connect((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
,
591 msg
->Input
.Connect
.IpAddress
, ntohs(msg
->Input
.Connect
.Port
),
592 InternalConnectEventHandler
);
594 msg
->Output
.Connect
.Error
= Error
== ERR_OK
? ERR_INPROGRESS
: Error
;
597 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
601 LibTCPConnect(PCONNECTION_ENDPOINT Connection
, struct ip_addr
*const ipaddr
, const u16_t port
)
603 struct lwip_callback_msg
*msg
;
606 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
609 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
610 msg
->Input
.Connect
.Connection
= Connection
;
611 msg
->Input
.Connect
.IpAddress
= ipaddr
;
612 msg
->Input
.Connect
.Port
= port
;
614 tcpip_callback_with_block(LibTCPConnectCallback
, msg
, 1);
616 if (WaitForEventSafely(&msg
->Event
))
618 ret
= msg
->Output
.Connect
.Error
;
623 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
633 LibTCPShutdownCallback(void *arg
)
635 struct lwip_callback_msg
*msg
= arg
;
636 PTCP_PCB pcb
= msg
->Input
.Shutdown
.Connection
->SocketContext
;
638 if (!msg
->Input
.Shutdown
.Connection
->SocketContext
)
640 msg
->Output
.Shutdown
.Error
= ERR_CLSD
;
644 /* LwIP makes the (questionable) assumption that SHUTDOWN_RDWR is equivalent to tcp_close().
645 * This assumption holds even if the shutdown calls are done separately (even through multiple
646 * WinSock shutdown() calls). This assumption means that lwIP has the right to deallocate our
647 * PCB without telling us if we shutdown TX and RX. To avoid these problems, we'll clear the
648 * socket context if we have called shutdown for TX and RX.
650 if (msg
->Input
.Shutdown
.shut_rx
) {
651 msg
->Output
.Shutdown
.Error
= tcp_shutdown(pcb
, TRUE
, FALSE
);
653 if (msg
->Input
.Shutdown
.shut_tx
) {
654 msg
->Output
.Shutdown
.Error
= tcp_shutdown(pcb
, FALSE
, TRUE
);
657 if (!msg
->Output
.Shutdown
.Error
)
659 if (msg
->Input
.Shutdown
.shut_rx
)
661 msg
->Input
.Shutdown
.Connection
->ReceiveShutdown
= TRUE
;
662 msg
->Input
.Shutdown
.Connection
->ReceiveShutdownStatus
= STATUS_FILE_CLOSED
;
665 if (msg
->Input
.Shutdown
.shut_tx
)
666 msg
->Input
.Shutdown
.Connection
->SendShutdown
= TRUE
;
668 if (msg
->Input
.Shutdown
.Connection
->ReceiveShutdown
&&
669 msg
->Input
.Shutdown
.Connection
->SendShutdown
)
671 /* The PCB is not ours anymore */
672 msg
->Input
.Shutdown
.Connection
->SocketContext
= NULL
;
674 TCPFinEventHandler(msg
->Input
.Shutdown
.Connection
, ERR_CLSD
);
679 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
683 LibTCPShutdown(PCONNECTION_ENDPOINT Connection
, const int shut_rx
, const int shut_tx
)
685 struct lwip_callback_msg
*msg
;
688 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
691 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
693 msg
->Input
.Shutdown
.Connection
= Connection
;
694 msg
->Input
.Shutdown
.shut_rx
= shut_rx
;
695 msg
->Input
.Shutdown
.shut_tx
= shut_tx
;
697 tcpip_callback_with_block(LibTCPShutdownCallback
, msg
, 1);
699 if (WaitForEventSafely(&msg
->Event
))
700 ret
= msg
->Output
.Shutdown
.Error
;
704 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
714 LibTCPCloseCallback(void *arg
)
716 struct lwip_callback_msg
*msg
= arg
;
717 PTCP_PCB pcb
= msg
->Input
.Close
.Connection
->SocketContext
;
719 /* Empty the queue even if we're already "closed" */
720 LibTCPEmptyQueue(msg
->Input
.Close
.Connection
);
722 /* Check if we've already been closed */
723 if (msg
->Input
.Close
.Connection
->Closing
)
725 msg
->Output
.Close
.Error
= ERR_OK
;
729 /* Enter "closing" mode if we're doing a normal close */
730 if (msg
->Input
.Close
.Callback
)
731 msg
->Input
.Close
.Connection
->Closing
= TRUE
;
733 /* Check if the PCB was already "closed" but the client doesn't know it yet */
734 if (!msg
->Input
.Close
.Connection
->SocketContext
)
736 msg
->Output
.Close
.Error
= ERR_OK
;
740 /* Clear the PCB pointer and stop callbacks */
741 msg
->Input
.Close
.Connection
->SocketContext
= NULL
;
744 /* This may generate additional callbacks but we don't care,
745 * because they're too inconsistent to rely on */
746 msg
->Output
.Close
.Error
= tcp_close(pcb
);
748 if (msg
->Output
.Close
.Error
)
750 /* Restore the PCB pointer */
751 msg
->Input
.Close
.Connection
->SocketContext
= pcb
;
752 msg
->Input
.Close
.Connection
->Closing
= FALSE
;
754 else if (msg
->Input
.Close
.Callback
)
756 TCPFinEventHandler(msg
->Input
.Close
.Connection
, ERR_CLSD
);
760 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
764 LibTCPClose(PCONNECTION_ENDPOINT Connection
, const int safe
, const int callback
)
767 struct lwip_callback_msg
*msg
;
769 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
772 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
774 msg
->Input
.Close
.Connection
= Connection
;
775 msg
->Input
.Close
.Callback
= callback
;
778 LibTCPCloseCallback(msg
);
780 tcpip_callback_with_block(LibTCPCloseCallback
, msg
, 1);
782 if (WaitForEventSafely(&msg
->Event
))
783 ret
= msg
->Output
.Close
.Error
;
787 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
796 LibTCPAccept(PTCP_PCB pcb
, struct tcp_pcb
*listen_pcb
, void *arg
)
801 tcp_recv(pcb
, InternalRecvEventHandler
);
802 tcp_sent(pcb
, InternalSendEventHandler
);
803 tcp_err(pcb
, InternalErrorEventHandler
);
806 tcp_accepted(listen_pcb
);
810 LibTCPGetHostName(PTCP_PCB pcb
, struct ip_addr
*const ipaddr
, u16_t
*const port
)
815 *ipaddr
= pcb
->local_ip
;
816 *port
= pcb
->local_port
;
822 LibTCPGetPeerName(PTCP_PCB pcb
, struct ip_addr
* const ipaddr
, u16_t
* const port
)
827 *ipaddr
= pcb
->remote_ip
;
828 *port
= pcb
->remote_port
;
839 pcb
->flags
|= TF_NODELAY
;
841 pcb
->flags
&= ~TF_NODELAY
;