2 #include "lwip/tcpip.h"
8 static const char * const tcp_state_str
[] = {
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 */
30 extern KEVENT TerminationEvent
;
31 extern NPAGED_LOOKASIDE_LIST MessageLookasideList
;
32 extern NPAGED_LOOKASIDE_LIST QueueEntryLookasideList
;
36 LibTCPEmptyQueue(PCONNECTION_ENDPOINT Connection
)
39 PQUEUE_ENTRY qp
= NULL
;
41 ReferenceObject(Connection
);
43 while (!IsListEmpty(&Connection
->PacketQueue
))
45 Entry
= RemoveHeadList(&Connection
->PacketQueue
);
46 qp
= CONTAINING_RECORD(Entry
, QUEUE_ENTRY
, ListEntry
);
48 /* We're in the tcpip thread here so this is safe */
51 ExFreeToNPagedLookasideList(&QueueEntryLookasideList
, qp
);
54 DereferenceObject(Connection
);
57 void LibTCPEnqueuePacket(PCONNECTION_ENDPOINT Connection
, struct pbuf
*p
)
61 qp
= (PQUEUE_ENTRY
)ExAllocateFromNPagedLookasideList(&QueueEntryLookasideList
);
65 ExInterlockedInsertTailList(&Connection
->PacketQueue
, &qp
->ListEntry
, &Connection
->Lock
);
68 PQUEUE_ENTRY
LibTCPDequeuePacket(PCONNECTION_ENDPOINT Connection
)
71 PQUEUE_ENTRY qp
= NULL
;
73 if (IsListEmpty(&Connection
->PacketQueue
)) return NULL
;
75 Entry
= RemoveHeadList(&Connection
->PacketQueue
);
77 qp
= CONTAINING_RECORD(Entry
, QUEUE_ENTRY
, ListEntry
);
82 NTSTATUS
LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection
, PUCHAR RecvBuffer
, UINT RecvLen
, UINT
*Received
)
87 UINT ReadLength
, PayloadLength
;
93 LockObject(Connection
, &OldIrql
);
95 if (!IsListEmpty(&Connection
->PacketQueue
))
97 while ((qp
= LibTCPDequeuePacket(Connection
)) != NULL
)
101 /* Calculate the payload first */
102 Payload
= p
->payload
;
103 Payload
+= qp
->Offset
;
104 PayloadLength
= p
->len
;
105 PayloadLength
-= qp
->Offset
;
107 /* Check if we're reading the whole buffer */
108 ReadLength
= MIN(PayloadLength
, RecvLen
);
109 if (ReadLength
!= PayloadLength
)
111 /* Save this one for later */
112 qp
->Offset
+= ReadLength
;
113 InsertHeadList(&Connection
->PacketQueue
, &qp
->ListEntry
);
117 UnlockObject(Connection
, OldIrql
);
119 /* Return to a lower IRQL because the receive buffer may be pageable memory */
120 RtlCopyMemory(RecvBuffer
,
124 LockObject(Connection
, &OldIrql
);
126 /* Update trackers */
127 RecvLen
-= ReadLength
;
128 RecvBuffer
+= ReadLength
;
129 (*Received
) += ReadLength
;
133 /* Use this special pbuf free callback function because we're outside tcpip thread */
134 pbuf_free_callback(qp
->p
);
136 ExFreeToNPagedLookasideList(&QueueEntryLookasideList
, qp
);
140 /* If we get here, it means we've filled the buffer */
141 ASSERT(RecvLen
== 0);
144 Status
= STATUS_SUCCESS
;
152 if (Connection
->ReceiveShutdown
)
153 Status
= STATUS_SUCCESS
;
155 Status
= STATUS_PENDING
;
158 UnlockObject(Connection
, OldIrql
);
165 WaitForEventSafely(PRKEVENT Event
)
167 PVOID WaitObjects
[] = {Event
, &TerminationEvent
};
169 if (KeWaitForMultipleObjects(2,
176 NULL
) == STATUS_WAIT_0
)
178 /* Signalled by the caller's event */
181 else /* if KeWaitForMultipleObjects() == STATUS_WAIT_1 */
183 /* Signalled by our termination event */
190 InternalSendEventHandler(void *arg
, PTCP_PCB pcb
, const u16_t space
)
192 /* Make sure the socket didn't get closed */
193 if (!arg
) return ERR_OK
;
195 TCPSendEventHandler(arg
, space
);
202 InternalRecvEventHandler(void *arg
, PTCP_PCB pcb
, struct pbuf
*p
, const err_t err
)
204 PCONNECTION_ENDPOINT Connection
= arg
;
208 /* Make sure the socket didn't get closed */
223 /* Enqueue this buffer */
224 LibTCPEnqueuePacket(Connection
, pb
);
227 /* Advance and unchain the buffer */
228 pb
= pbuf_dechain(pb
);;
231 tcp_recved(pcb
, RecvLen
);
233 TCPRecvEventHandler(arg
);
235 else if (err
== ERR_OK
)
237 /* Complete pending reads with 0 bytes to indicate a graceful closure,
238 * but note that send is still possible in this state so we don't close the
239 * whole socket here (by calling tcp_close()) as that would violate TCP specs
241 Connection
->ReceiveShutdown
= TRUE
;
242 TCPFinEventHandler(arg
, ERR_OK
);
250 InternalAcceptEventHandler(void *arg
, PTCP_PCB newpcb
, const err_t err
)
252 /* Make sure the socket didn't get closed */
256 TCPAcceptEventHandler(arg
, newpcb
);
258 /* Set in LibTCPAccept (called from TCPAcceptEventHandler) */
259 if (newpcb
->callback_arg
)
267 InternalConnectEventHandler(void *arg
, PTCP_PCB pcb
, const err_t err
)
269 /* Make sure the socket didn't get closed */
273 TCPConnectEventHandler(arg
, err
);
280 InternalErrorEventHandler(void *arg
, const err_t err
)
282 /* Make sure the socket didn't get closed */
285 TCPFinEventHandler(arg
, err
);
290 LibTCPSocketCallback(void *arg
)
292 struct lwip_callback_msg
*msg
= arg
;
296 msg
->Output
.Socket
.NewPcb
= tcp_new();
298 if (msg
->Output
.Socket
.NewPcb
)
300 tcp_arg(msg
->Output
.Socket
.NewPcb
, msg
->Input
.Socket
.Arg
);
301 tcp_err(msg
->Output
.Socket
.NewPcb
, InternalErrorEventHandler
);
304 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
308 LibTCPSocket(void *arg
)
310 struct lwip_callback_msg
*msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
315 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
316 msg
->Input
.Socket
.Arg
= arg
;
318 tcpip_callback_with_block(LibTCPSocketCallback
, msg
, 1);
320 if (WaitForEventSafely(&msg
->Event
))
321 ret
= msg
->Output
.Socket
.NewPcb
;
325 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
335 LibTCPBindCallback(void *arg
)
337 struct lwip_callback_msg
*msg
= arg
;
338 PTCP_PCB pcb
= msg
->Input
.Bind
.Connection
->SocketContext
;
342 if (!msg
->Input
.Bind
.Connection
->SocketContext
)
344 msg
->Output
.Bind
.Error
= ERR_CLSD
;
348 /* We're guaranteed that the local address is valid to bind at this point */
349 pcb
->so_options
|= SOF_REUSEADDR
;
351 msg
->Output
.Bind
.Error
= tcp_bind(pcb
,
352 msg
->Input
.Bind
.IpAddress
,
353 ntohs(msg
->Input
.Bind
.Port
));
356 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
360 LibTCPBind(PCONNECTION_ENDPOINT Connection
, struct ip_addr
*const ipaddr
, const u16_t port
)
362 struct lwip_callback_msg
*msg
;
365 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
368 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
369 msg
->Input
.Bind
.Connection
= Connection
;
370 msg
->Input
.Bind
.IpAddress
= ipaddr
;
371 msg
->Input
.Bind
.Port
= port
;
373 tcpip_callback_with_block(LibTCPBindCallback
, msg
, 1);
375 if (WaitForEventSafely(&msg
->Event
))
376 ret
= msg
->Output
.Bind
.Error
;
380 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
390 LibTCPListenCallback(void *arg
)
392 struct lwip_callback_msg
*msg
= arg
;
396 if (!msg
->Input
.Listen
.Connection
->SocketContext
)
398 msg
->Output
.Listen
.NewPcb
= NULL
;
402 msg
->Output
.Listen
.NewPcb
= tcp_listen_with_backlog((PTCP_PCB
)msg
->Input
.Listen
.Connection
->SocketContext
, msg
->Input
.Listen
.Backlog
);
404 if (msg
->Output
.Listen
.NewPcb
)
406 tcp_accept(msg
->Output
.Listen
.NewPcb
, InternalAcceptEventHandler
);
410 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
414 LibTCPListen(PCONNECTION_ENDPOINT Connection
, const u8_t backlog
)
416 struct lwip_callback_msg
*msg
;
419 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
422 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
423 msg
->Input
.Listen
.Connection
= Connection
;
424 msg
->Input
.Listen
.Backlog
= backlog
;
426 tcpip_callback_with_block(LibTCPListenCallback
, msg
, 1);
428 if (WaitForEventSafely(&msg
->Event
))
429 ret
= msg
->Output
.Listen
.NewPcb
;
433 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
443 LibTCPSendCallback(void *arg
)
445 struct lwip_callback_msg
*msg
= arg
;
449 if (!msg
->Input
.Send
.Connection
->SocketContext
)
451 msg
->Output
.Send
.Error
= ERR_CLSD
;
455 if (msg
->Input
.Send
.Connection
->SendShutdown
)
457 msg
->Output
.Send
.Error
= ERR_CLSD
;
461 msg
->Output
.Send
.Error
= tcp_write((PTCP_PCB
)msg
->Input
.Send
.Connection
->SocketContext
,
462 msg
->Input
.Send
.Data
,
463 msg
->Input
.Send
.DataLength
,
464 TCP_WRITE_FLAG_COPY
);
465 if (msg
->Output
.Send
.Error
== ERR_MEM
)
467 /* No buffer space so return pending */
468 msg
->Output
.Send
.Error
= ERR_INPROGRESS
;
470 else if (msg
->Output
.Send
.Error
== ERR_OK
)
472 /* Queued successfully so try to send it */
473 tcp_output((PTCP_PCB
)msg
->Input
.Send
.Connection
->SocketContext
);
477 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
481 LibTCPSend(PCONNECTION_ENDPOINT Connection
, void *const dataptr
, const u16_t len
, const int safe
)
484 struct lwip_callback_msg
*msg
;
486 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
489 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
490 msg
->Input
.Send
.Connection
= Connection
;
491 msg
->Input
.Send
.Data
= dataptr
;
492 msg
->Input
.Send
.DataLength
= len
;
495 LibTCPSendCallback(msg
);
497 tcpip_callback_with_block(LibTCPSendCallback
, msg
, 1);
499 if (WaitForEventSafely(&msg
->Event
))
500 ret
= msg
->Output
.Send
.Error
;
504 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
514 LibTCPConnectCallback(void *arg
)
516 struct lwip_callback_msg
*msg
= arg
;
521 if (!msg
->Input
.Connect
.Connection
->SocketContext
)
523 msg
->Output
.Connect
.Error
= ERR_CLSD
;
527 tcp_recv((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
, InternalRecvEventHandler
);
528 tcp_sent((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
, InternalSendEventHandler
);
530 Error
= tcp_connect((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
,
531 msg
->Input
.Connect
.IpAddress
, ntohs(msg
->Input
.Connect
.Port
),
532 InternalConnectEventHandler
);
534 msg
->Output
.Connect
.Error
= Error
== ERR_OK
? ERR_INPROGRESS
: Error
;
537 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
541 LibTCPConnect(PCONNECTION_ENDPOINT Connection
, struct ip_addr
*const ipaddr
, const u16_t port
)
543 struct lwip_callback_msg
*msg
;
546 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
549 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
550 msg
->Input
.Connect
.Connection
= Connection
;
551 msg
->Input
.Connect
.IpAddress
= ipaddr
;
552 msg
->Input
.Connect
.Port
= port
;
554 tcpip_callback_with_block(LibTCPConnectCallback
, msg
, 1);
556 if (WaitForEventSafely(&msg
->Event
))
558 ret
= msg
->Output
.Connect
.Error
;
563 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
573 LibTCPShutdownCallback(void *arg
)
575 struct lwip_callback_msg
*msg
= arg
;
576 PTCP_PCB pcb
= msg
->Input
.Shutdown
.Connection
->SocketContext
;
578 if (!msg
->Input
.Shutdown
.Connection
->SocketContext
)
580 msg
->Output
.Shutdown
.Error
= ERR_CLSD
;
584 if (pcb
->state
== CLOSE_WAIT
)
586 /* This case actually results in a socket closure later (lwIP bug?) */
587 msg
->Input
.Shutdown
.Connection
->SocketContext
= NULL
;
590 msg
->Output
.Shutdown
.Error
= tcp_shutdown(pcb
, msg
->Input
.Shutdown
.shut_rx
, msg
->Input
.Shutdown
.shut_tx
);
591 if (msg
->Output
.Shutdown
.Error
)
593 msg
->Input
.Shutdown
.Connection
->SocketContext
= pcb
;
597 if (msg
->Input
.Shutdown
.shut_rx
)
598 msg
->Input
.Shutdown
.Connection
->ReceiveShutdown
= TRUE
;
600 if (msg
->Input
.Shutdown
.shut_tx
)
601 msg
->Input
.Shutdown
.Connection
->SendShutdown
= TRUE
;
605 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
609 LibTCPShutdown(PCONNECTION_ENDPOINT Connection
, const int shut_rx
, const int shut_tx
)
611 struct lwip_callback_msg
*msg
;
614 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
617 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
619 msg
->Input
.Shutdown
.Connection
= Connection
;
620 msg
->Input
.Shutdown
.shut_rx
= shut_rx
;
621 msg
->Input
.Shutdown
.shut_tx
= shut_tx
;
623 tcpip_callback_with_block(LibTCPShutdownCallback
, msg
, 1);
625 if (WaitForEventSafely(&msg
->Event
))
626 ret
= msg
->Output
.Shutdown
.Error
;
630 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
640 LibTCPCloseCallback(void *arg
)
642 struct lwip_callback_msg
*msg
= arg
;
643 PTCP_PCB pcb
= msg
->Input
.Close
.Connection
->SocketContext
;
645 /* Empty the queue even if we're already "closed" */
646 LibTCPEmptyQueue(msg
->Input
.Close
.Connection
);
648 if (!msg
->Input
.Close
.Connection
->SocketContext
)
650 msg
->Output
.Close
.Error
= ERR_OK
;
654 /* Clear the PCB pointer */
655 msg
->Input
.Close
.Connection
->SocketContext
= NULL
;
662 msg
->Output
.Close
.Error
= tcp_close(pcb
);
664 if (!msg
->Output
.Close
.Error
&& msg
->Input
.Close
.Callback
)
665 TCPFinEventHandler(msg
->Input
.Close
.Connection
, ERR_OK
);
669 if (msg
->Input
.Close
.Connection
->SendShutdown
&&
670 msg
->Input
.Close
.Connection
->ReceiveShutdown
)
672 /* Abort the connection */
675 /* Aborts always succeed */
676 msg
->Output
.Close
.Error
= ERR_OK
;
680 /* Start the graceful close process (or send RST for pending data) */
681 msg
->Output
.Close
.Error
= tcp_close(pcb
);
686 if (msg
->Output
.Close
.Error
)
688 /* Restore the PCB pointer */
689 msg
->Input
.Close
.Connection
->SocketContext
= pcb
;
693 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
697 LibTCPClose(PCONNECTION_ENDPOINT Connection
, const int safe
, const int callback
)
700 struct lwip_callback_msg
*msg
;
702 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
705 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
707 msg
->Input
.Close
.Connection
= Connection
;
708 msg
->Input
.Close
.Callback
= callback
;
711 LibTCPCloseCallback(msg
);
713 tcpip_callback_with_block(LibTCPCloseCallback
, msg
, 1);
715 if (WaitForEventSafely(&msg
->Event
))
716 ret
= msg
->Output
.Close
.Error
;
720 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
729 LibTCPAccept(PTCP_PCB pcb
, struct tcp_pcb
*listen_pcb
, void *arg
)
734 tcp_recv(pcb
, InternalRecvEventHandler
);
735 tcp_sent(pcb
, InternalSendEventHandler
);
736 tcp_err(pcb
, InternalErrorEventHandler
);
739 tcp_accepted(listen_pcb
);
743 LibTCPGetHostName(PTCP_PCB pcb
, struct ip_addr
*const ipaddr
, u16_t
*const port
)
748 *ipaddr
= pcb
->local_ip
;
749 *port
= pcb
->local_port
;
755 LibTCPGetPeerName(PTCP_PCB pcb
, struct ip_addr
* const ipaddr
, u16_t
* const port
)
760 *ipaddr
= pcb
->remote_ip
;
761 *port
= pcb
->remote_port
;