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
);
64 ExInterlockedInsertTailList(&Connection
->PacketQueue
, &qp
->ListEntry
, &Connection
->Lock
);
67 PQUEUE_ENTRY
LibTCPDequeuePacket(PCONNECTION_ENDPOINT Connection
)
70 PQUEUE_ENTRY qp
= NULL
;
72 if (IsListEmpty(&Connection
->PacketQueue
)) return NULL
;
74 Entry
= RemoveHeadList(&Connection
->PacketQueue
);
76 qp
= CONTAINING_RECORD(Entry
, QUEUE_ENTRY
, ListEntry
);
81 NTSTATUS
LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection
, PUCHAR RecvBuffer
, UINT RecvLen
, UINT
*Received
)
85 NTSTATUS Status
= STATUS_PENDING
;
86 UINT ReadLength
, ExistingDataLength
;
91 LockObject(Connection
, &OldIrql
);
93 if (!IsListEmpty(&Connection
->PacketQueue
))
95 while ((qp
= LibTCPDequeuePacket(Connection
)) != NULL
)
98 ExistingDataLength
= (*Received
);
100 Status
= STATUS_SUCCESS
;
102 ReadLength
= MIN(p
->tot_len
, RecvLen
);
103 if (ReadLength
!= p
->tot_len
)
105 if (ExistingDataLength
)
107 /* The packet was too big but we used some data already so give it another shot later */
108 InsertHeadList(&Connection
->PacketQueue
, &qp
->ListEntry
);
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.
116 Status
= STATUS_BUFFER_OVERFLOW
;
120 UnlockObject(Connection
, OldIrql
);
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
)
125 RtlCopyMemory(RecvBuffer
+ (*Received
), p
->payload
, p
->len
);
128 LockObject(Connection
, &OldIrql
);
130 RecvLen
-= ReadLength
;
132 /* Use this special pbuf free callback function because we're outside tcpip thread */
133 pbuf_free_callback(qp
->p
);
135 ExFreeToNPagedLookasideList(&QueueEntryLookasideList
, qp
);
140 if (Status
!= STATUS_SUCCESS
)
146 if (Connection
->ReceiveShutdown
)
147 Status
= STATUS_SUCCESS
;
149 Status
= STATUS_PENDING
;
152 UnlockObject(Connection
, OldIrql
);
159 WaitForEventSafely(PRKEVENT Event
)
161 PVOID WaitObjects
[] = {Event
, &TerminationEvent
};
163 if (KeWaitForMultipleObjects(2,
170 NULL
) == STATUS_WAIT_0
)
172 /* Signalled by the caller's event */
175 else /* if KeWaitForMultipleObjects() == STATUS_WAIT_1 */
177 /* Signalled by our termination event */
184 InternalSendEventHandler(void *arg
, PTCP_PCB pcb
, const u16_t space
)
186 /* Make sure the socket didn't get closed */
187 if (!arg
) return ERR_OK
;
189 TCPSendEventHandler(arg
, space
);
196 InternalRecvEventHandler(void *arg
, PTCP_PCB pcb
, struct pbuf
*p
, const err_t err
)
198 PCONNECTION_ENDPOINT Connection
= arg
;
200 /* Make sure the socket didn't get closed */
211 LibTCPEnqueuePacket(Connection
, p
);
213 tcp_recved(pcb
, p
->tot_len
);
215 TCPRecvEventHandler(arg
);
217 else if (err
== ERR_OK
)
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
223 Connection
->ReceiveShutdown
= TRUE
;
224 TCPFinEventHandler(arg
, ERR_OK
);
232 InternalAcceptEventHandler(void *arg
, PTCP_PCB newpcb
, const err_t err
)
234 /* Make sure the socket didn't get closed */
238 TCPAcceptEventHandler(arg
, newpcb
);
240 /* Set in LibTCPAccept (called from TCPAcceptEventHandler) */
241 if (newpcb
->callback_arg
)
249 InternalConnectEventHandler(void *arg
, PTCP_PCB pcb
, const err_t err
)
251 /* Make sure the socket didn't get closed */
255 TCPConnectEventHandler(arg
, err
);
262 InternalErrorEventHandler(void *arg
, const err_t err
)
264 /* Make sure the socket didn't get closed */
267 TCPFinEventHandler(arg
, err
);
272 LibTCPSocketCallback(void *arg
)
274 struct lwip_callback_msg
*msg
= arg
;
278 msg
->Output
.Socket
.NewPcb
= tcp_new();
280 if (msg
->Output
.Socket
.NewPcb
)
282 tcp_arg(msg
->Output
.Socket
.NewPcb
, msg
->Input
.Socket
.Arg
);
283 tcp_err(msg
->Output
.Socket
.NewPcb
, InternalErrorEventHandler
);
286 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
290 LibTCPSocket(void *arg
)
292 struct lwip_callback_msg
*msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
297 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
298 msg
->Input
.Socket
.Arg
= arg
;
300 tcpip_callback_with_block(LibTCPSocketCallback
, msg
, 1);
302 if (WaitForEventSafely(&msg
->Event
))
303 ret
= msg
->Output
.Socket
.NewPcb
;
307 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
317 LibTCPBindCallback(void *arg
)
319 struct lwip_callback_msg
*msg
= arg
;
323 if (!msg
->Input
.Bind
.Connection
->SocketContext
)
325 msg
->Output
.Bind
.Error
= ERR_CLSD
;
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
));
334 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
338 LibTCPBind(PCONNECTION_ENDPOINT Connection
, struct ip_addr
*const ipaddr
, const u16_t port
)
340 struct lwip_callback_msg
*msg
;
343 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
346 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
347 msg
->Input
.Bind
.Connection
= Connection
;
348 msg
->Input
.Bind
.IpAddress
= ipaddr
;
349 msg
->Input
.Bind
.Port
= port
;
351 tcpip_callback_with_block(LibTCPBindCallback
, msg
, 1);
353 if (WaitForEventSafely(&msg
->Event
))
354 ret
= msg
->Output
.Bind
.Error
;
358 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
368 LibTCPListenCallback(void *arg
)
370 struct lwip_callback_msg
*msg
= arg
;
374 if (!msg
->Input
.Listen
.Connection
->SocketContext
)
376 msg
->Output
.Listen
.NewPcb
= NULL
;
380 msg
->Output
.Listen
.NewPcb
= tcp_listen_with_backlog((PTCP_PCB
)msg
->Input
.Listen
.Connection
->SocketContext
, msg
->Input
.Listen
.Backlog
);
382 if (msg
->Output
.Listen
.NewPcb
)
384 tcp_accept(msg
->Output
.Listen
.NewPcb
, InternalAcceptEventHandler
);
388 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
392 LibTCPListen(PCONNECTION_ENDPOINT Connection
, const u8_t backlog
)
394 struct lwip_callback_msg
*msg
;
397 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
400 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
401 msg
->Input
.Listen
.Connection
= Connection
;
402 msg
->Input
.Listen
.Backlog
= backlog
;
404 tcpip_callback_with_block(LibTCPListenCallback
, msg
, 1);
406 if (WaitForEventSafely(&msg
->Event
))
407 ret
= msg
->Output
.Listen
.NewPcb
;
411 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
421 LibTCPSendCallback(void *arg
)
423 struct lwip_callback_msg
*msg
= arg
;
427 if (!msg
->Input
.Send
.Connection
->SocketContext
)
429 msg
->Output
.Send
.Error
= ERR_CLSD
;
433 if (msg
->Input
.Send
.Connection
->SendShutdown
)
435 msg
->Output
.Send
.Error
= ERR_CLSD
;
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
)
445 /* No buffer space so return pending */
446 msg
->Output
.Send
.Error
= ERR_INPROGRESS
;
448 else if (msg
->Output
.Send
.Error
== ERR_OK
)
450 /* Queued successfully so try to send it */
451 tcp_output((PTCP_PCB
)msg
->Input
.Send
.Connection
->SocketContext
);
455 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
459 LibTCPSend(PCONNECTION_ENDPOINT Connection
, void *const dataptr
, const u16_t len
, const int safe
)
462 struct lwip_callback_msg
*msg
;
464 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
467 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
468 msg
->Input
.Send
.Connection
= Connection
;
469 msg
->Input
.Send
.Data
= dataptr
;
470 msg
->Input
.Send
.DataLength
= len
;
473 LibTCPSendCallback(msg
);
475 tcpip_callback_with_block(LibTCPSendCallback
, msg
, 1);
477 if (WaitForEventSafely(&msg
->Event
))
478 ret
= msg
->Output
.Send
.Error
;
482 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
492 LibTCPConnectCallback(void *arg
)
494 struct lwip_callback_msg
*msg
= arg
;
499 if (!msg
->Input
.Connect
.Connection
->SocketContext
)
501 msg
->Output
.Connect
.Error
= ERR_CLSD
;
505 tcp_recv((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
, InternalRecvEventHandler
);
506 tcp_sent((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
, InternalSendEventHandler
);
508 Error
= tcp_connect((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
,
509 msg
->Input
.Connect
.IpAddress
, ntohs(msg
->Input
.Connect
.Port
),
510 InternalConnectEventHandler
);
512 msg
->Output
.Connect
.Error
= Error
== ERR_OK
? ERR_INPROGRESS
: Error
;
515 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
519 LibTCPConnect(PCONNECTION_ENDPOINT Connection
, struct ip_addr
*const ipaddr
, const u16_t port
)
521 struct lwip_callback_msg
*msg
;
524 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
527 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
528 msg
->Input
.Connect
.Connection
= Connection
;
529 msg
->Input
.Connect
.IpAddress
= ipaddr
;
530 msg
->Input
.Connect
.Port
= port
;
532 tcpip_callback_with_block(LibTCPConnectCallback
, msg
, 1);
534 if (WaitForEventSafely(&msg
->Event
))
536 ret
= msg
->Output
.Connect
.Error
;
541 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
551 LibTCPShutdownCallback(void *arg
)
553 struct lwip_callback_msg
*msg
= arg
;
554 PTCP_PCB pcb
= msg
->Input
.Shutdown
.Connection
->SocketContext
;
556 if (!msg
->Input
.Shutdown
.Connection
->SocketContext
)
558 msg
->Output
.Shutdown
.Error
= ERR_CLSD
;
562 if (pcb
->state
== CLOSE_WAIT
)
564 /* This case actually results in a socket closure later (lwIP bug?) */
565 msg
->Input
.Shutdown
.Connection
->SocketContext
= NULL
;
568 msg
->Output
.Shutdown
.Error
= tcp_shutdown(pcb
, msg
->Input
.Shutdown
.shut_rx
, msg
->Input
.Shutdown
.shut_tx
);
569 if (msg
->Output
.Shutdown
.Error
)
571 msg
->Input
.Shutdown
.Connection
->SocketContext
= pcb
;
575 if (msg
->Input
.Shutdown
.shut_rx
)
576 msg
->Input
.Shutdown
.Connection
->ReceiveShutdown
= TRUE
;
578 if (msg
->Input
.Shutdown
.shut_tx
)
579 msg
->Input
.Shutdown
.Connection
->SendShutdown
= TRUE
;
583 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
587 LibTCPShutdown(PCONNECTION_ENDPOINT Connection
, const int shut_rx
, const int shut_tx
)
589 struct lwip_callback_msg
*msg
;
592 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
595 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
597 msg
->Input
.Shutdown
.Connection
= Connection
;
598 msg
->Input
.Shutdown
.shut_rx
= shut_rx
;
599 msg
->Input
.Shutdown
.shut_tx
= shut_tx
;
601 tcpip_callback_with_block(LibTCPShutdownCallback
, msg
, 1);
603 if (WaitForEventSafely(&msg
->Event
))
604 ret
= msg
->Output
.Shutdown
.Error
;
608 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
618 LibTCPCloseCallback(void *arg
)
620 struct lwip_callback_msg
*msg
= arg
;
621 PTCP_PCB pcb
= msg
->Input
.Close
.Connection
->SocketContext
;
623 /* Empty the queue even if we're already "closed" */
624 LibTCPEmptyQueue(msg
->Input
.Close
.Connection
);
626 if (!msg
->Input
.Close
.Connection
->SocketContext
)
628 msg
->Output
.Close
.Error
= ERR_OK
;
632 /* Clear the PCB pointer */
633 msg
->Input
.Close
.Connection
->SocketContext
= NULL
;
640 msg
->Output
.Close
.Error
= tcp_close(pcb
);
642 if (!msg
->Output
.Close
.Error
&& msg
->Input
.Close
.Callback
)
643 TCPFinEventHandler(msg
->Input
.Close
.Connection
, ERR_OK
);
647 if (msg
->Input
.Close
.Connection
->SendShutdown
&&
648 msg
->Input
.Close
.Connection
->ReceiveShutdown
)
650 /* Abort the connection */
653 /* Aborts always succeed */
654 msg
->Output
.Close
.Error
= ERR_OK
;
658 /* Start the graceful close process (or send RST for pending data) */
659 msg
->Output
.Close
.Error
= tcp_close(pcb
);
664 if (msg
->Output
.Close
.Error
)
666 /* Restore the PCB pointer */
667 msg
->Input
.Close
.Connection
->SocketContext
= pcb
;
671 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
675 LibTCPClose(PCONNECTION_ENDPOINT Connection
, const int safe
, const int callback
)
678 struct lwip_callback_msg
*msg
;
680 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
683 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
685 msg
->Input
.Close
.Connection
= Connection
;
686 msg
->Input
.Close
.Callback
= callback
;
689 LibTCPCloseCallback(msg
);
691 tcpip_callback_with_block(LibTCPCloseCallback
, msg
, 1);
693 if (WaitForEventSafely(&msg
->Event
))
694 ret
= msg
->Output
.Close
.Error
;
698 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
707 LibTCPAccept(PTCP_PCB pcb
, struct tcp_pcb
*listen_pcb
, void *arg
)
712 tcp_recv(pcb
, InternalRecvEventHandler
);
713 tcp_sent(pcb
, InternalSendEventHandler
);
714 tcp_err(pcb
, InternalErrorEventHandler
);
717 tcp_accepted(listen_pcb
);
721 LibTCPGetHostName(PTCP_PCB pcb
, struct ip_addr
*const ipaddr
, u16_t
*const port
)
726 *ipaddr
= pcb
->local_ip
;
727 *port
= pcb
->local_port
;
733 LibTCPGetPeerName(PTCP_PCB pcb
, struct ip_addr
* const ipaddr
, u16_t
* const port
)
738 *ipaddr
= pcb
->remote_ip
;
739 *port
= pcb
->remote_port
;