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
, SpaceLeft
;
92 LockObject(Connection
, &OldIrql
);
94 if (!IsListEmpty(&Connection
->PacketQueue
))
96 while ((qp
= LibTCPDequeuePacket(Connection
)) != NULL
)
99 ExistingDataLength
= (*Received
);
101 Status
= STATUS_SUCCESS
;
103 ReadLength
= MIN(p
->tot_len
, SpaceLeft
);
104 if (ReadLength
!= p
->tot_len
)
106 if (ExistingDataLength
)
108 /* The packet was too big but we used some data already so give it another shot later */
109 InsertHeadList(&Connection
->PacketQueue
, &qp
->ListEntry
);
114 /* The packet is just too big to fit fully in our buffer, even when empty so
115 * return an informative status but still copy all the data we can fit.
117 Status
= STATUS_BUFFER_OVERFLOW
;
121 UnlockObject(Connection
, OldIrql
);
123 /* Return to a lower IRQL because the receive buffer may be pageable memory */
124 for (; (*Received
) < ReadLength
+ ExistingDataLength
; (*Received
) += p
->len
, p
= p
->next
)
126 RtlCopyMemory(RecvBuffer
+ (*Received
), p
->payload
, p
->len
);
129 LockObject(Connection
, &OldIrql
);
131 SpaceLeft
-= 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
);
141 if (Status
!= STATUS_SUCCESS
)
147 if (Connection
->ReceiveShutdown
)
148 Status
= STATUS_SUCCESS
;
150 Status
= STATUS_PENDING
;
153 UnlockObject(Connection
, OldIrql
);
160 WaitForEventSafely(PRKEVENT Event
)
162 PVOID WaitObjects
[] = {Event
, &TerminationEvent
};
164 if (KeWaitForMultipleObjects(2,
171 NULL
) == STATUS_WAIT_0
)
173 /* Signalled by the caller's event */
176 else /* if KeWaitForMultipleObjects() == STATUS_WAIT_1 */
178 /* Signalled by our termination event */
185 InternalSendEventHandler(void *arg
, PTCP_PCB pcb
, const u16_t space
)
187 /* Make sure the socket didn't get closed */
188 if (!arg
) return ERR_OK
;
190 TCPSendEventHandler(arg
, space
);
197 InternalRecvEventHandler(void *arg
, PTCP_PCB pcb
, struct pbuf
*p
, const err_t err
)
199 PCONNECTION_ENDPOINT Connection
= arg
;
202 /* Make sure the socket didn't get closed */
213 len
= TCPRecvEventHandler(arg
, p
);
214 if (len
== p
->tot_len
)
216 tcp_recved(pcb
, len
);
224 DbgPrint("UNTESTED CASE: NOT ALL DATA TAKEN! EXTRA DATA MAY BE LOST!\n");
226 tcp_recved(pcb
, len
);
228 /* Possible memory leak of pbuf here? */
234 LibTCPEnqueuePacket(Connection
, p
);
236 tcp_recved(pcb
, p
->tot_len
);
241 else if (err
== ERR_OK
)
243 /* Complete pending reads with 0 bytes to indicate a graceful closure,
244 * but note that send is still possible in this state so we don't close the
245 * whole socket here (by calling tcp_close()) as that would violate TCP specs
247 Connection
->ReceiveShutdown
= TRUE
;
248 TCPFinEventHandler(arg
, ERR_OK
);
256 InternalAcceptEventHandler(void *arg
, PTCP_PCB newpcb
, const err_t err
)
258 /* Make sure the socket didn't get closed */
262 TCPAcceptEventHandler(arg
, newpcb
);
264 /* Set in LibTCPAccept (called from TCPAcceptEventHandler) */
265 if (newpcb
->callback_arg
)
273 InternalConnectEventHandler(void *arg
, PTCP_PCB pcb
, const err_t err
)
275 /* Make sure the socket didn't get closed */
279 TCPConnectEventHandler(arg
, err
);
286 InternalErrorEventHandler(void *arg
, const err_t err
)
288 /* Make sure the socket didn't get closed */
291 TCPFinEventHandler(arg
, err
);
296 LibTCPSocketCallback(void *arg
)
298 struct lwip_callback_msg
*msg
= arg
;
302 msg
->Output
.Socket
.NewPcb
= tcp_new();
304 if (msg
->Output
.Socket
.NewPcb
)
306 tcp_arg(msg
->Output
.Socket
.NewPcb
, msg
->Input
.Socket
.Arg
);
307 tcp_err(msg
->Output
.Socket
.NewPcb
, InternalErrorEventHandler
);
310 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
314 LibTCPSocket(void *arg
)
316 struct lwip_callback_msg
*msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
321 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
322 msg
->Input
.Socket
.Arg
= arg
;
324 tcpip_callback_with_block(LibTCPSocketCallback
, msg
, 1);
326 if (WaitForEventSafely(&msg
->Event
))
327 ret
= msg
->Output
.Socket
.NewPcb
;
331 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
341 LibTCPBindCallback(void *arg
)
343 struct lwip_callback_msg
*msg
= arg
;
347 if (!msg
->Input
.Bind
.Connection
->SocketContext
)
349 msg
->Output
.Bind
.Error
= ERR_CLSD
;
353 msg
->Output
.Bind
.Error
= tcp_bind((PTCP_PCB
)msg
->Input
.Bind
.Connection
->SocketContext
,
354 msg
->Input
.Bind
.IpAddress
,
355 ntohs(msg
->Input
.Bind
.Port
));
358 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
362 LibTCPBind(PCONNECTION_ENDPOINT Connection
, struct ip_addr
*const ipaddr
, const u16_t port
)
364 struct lwip_callback_msg
*msg
;
367 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
370 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
371 msg
->Input
.Bind
.Connection
= Connection
;
372 msg
->Input
.Bind
.IpAddress
= ipaddr
;
373 msg
->Input
.Bind
.Port
= port
;
375 tcpip_callback_with_block(LibTCPBindCallback
, msg
, 1);
377 if (WaitForEventSafely(&msg
->Event
))
378 ret
= msg
->Output
.Bind
.Error
;
382 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
392 LibTCPListenCallback(void *arg
)
394 struct lwip_callback_msg
*msg
= arg
;
398 if (!msg
->Input
.Listen
.Connection
->SocketContext
)
400 msg
->Output
.Listen
.NewPcb
= NULL
;
404 msg
->Output
.Listen
.NewPcb
= tcp_listen_with_backlog((PTCP_PCB
)msg
->Input
.Listen
.Connection
->SocketContext
, msg
->Input
.Listen
.Backlog
);
406 if (msg
->Output
.Listen
.NewPcb
)
408 tcp_accept(msg
->Output
.Listen
.NewPcb
, InternalAcceptEventHandler
);
412 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
416 LibTCPListen(PCONNECTION_ENDPOINT Connection
, const u8_t backlog
)
418 struct lwip_callback_msg
*msg
;
421 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
424 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
425 msg
->Input
.Listen
.Connection
= Connection
;
426 msg
->Input
.Listen
.Backlog
= backlog
;
428 tcpip_callback_with_block(LibTCPListenCallback
, msg
, 1);
430 if (WaitForEventSafely(&msg
->Event
))
431 ret
= msg
->Output
.Listen
.NewPcb
;
435 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
445 LibTCPSendCallback(void *arg
)
447 struct lwip_callback_msg
*msg
= arg
;
451 if (!msg
->Input
.Send
.Connection
->SocketContext
)
453 msg
->Output
.Send
.Error
= ERR_CLSD
;
457 if (msg
->Input
.Send
.Connection
->SendShutdown
)
459 msg
->Output
.Send
.Error
= ERR_CLSD
;
463 msg
->Output
.Send
.Error
= tcp_write((PTCP_PCB
)msg
->Input
.Send
.Connection
->SocketContext
,
464 msg
->Input
.Send
.Data
,
465 msg
->Input
.Send
.DataLength
,
466 TCP_WRITE_FLAG_COPY
);
467 if (msg
->Output
.Send
.Error
== ERR_MEM
)
469 /* No buffer space so return pending */
470 msg
->Output
.Send
.Error
= ERR_INPROGRESS
;
472 else if (msg
->Output
.Send
.Error
== ERR_OK
)
474 /* Queued successfully so try to send it */
475 tcp_output((PTCP_PCB
)msg
->Input
.Send
.Connection
->SocketContext
);
479 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
483 LibTCPSend(PCONNECTION_ENDPOINT Connection
, void *const dataptr
, const u16_t len
, const int safe
)
486 struct lwip_callback_msg
*msg
;
488 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
491 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
492 msg
->Input
.Send
.Connection
= Connection
;
493 msg
->Input
.Send
.Data
= dataptr
;
494 msg
->Input
.Send
.DataLength
= len
;
497 LibTCPSendCallback(msg
);
499 tcpip_callback_with_block(LibTCPSendCallback
, msg
, 1);
501 if (WaitForEventSafely(&msg
->Event
))
502 ret
= msg
->Output
.Send
.Error
;
506 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
516 LibTCPConnectCallback(void *arg
)
518 struct lwip_callback_msg
*msg
= arg
;
522 if (!msg
->Input
.Connect
.Connection
->SocketContext
)
524 msg
->Output
.Connect
.Error
= ERR_CLSD
;
528 tcp_recv((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
, InternalRecvEventHandler
);
529 tcp_sent((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
, InternalSendEventHandler
);
531 err_t Error
= tcp_connect((PTCP_PCB
)msg
->Input
.Connect
.Connection
->SocketContext
,
532 msg
->Input
.Connect
.IpAddress
, ntohs(msg
->Input
.Connect
.Port
),
533 InternalConnectEventHandler
);
535 msg
->Output
.Connect
.Error
= Error
== ERR_OK
? ERR_INPROGRESS
: Error
;
538 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
542 LibTCPConnect(PCONNECTION_ENDPOINT Connection
, struct ip_addr
*const ipaddr
, const u16_t port
)
544 struct lwip_callback_msg
*msg
;
547 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
550 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
551 msg
->Input
.Connect
.Connection
= Connection
;
552 msg
->Input
.Connect
.IpAddress
= ipaddr
;
553 msg
->Input
.Connect
.Port
= port
;
555 tcpip_callback_with_block(LibTCPConnectCallback
, msg
, 1);
557 if (WaitForEventSafely(&msg
->Event
))
559 ret
= msg
->Output
.Connect
.Error
;
564 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
574 LibTCPShutdownCallback(void *arg
)
576 struct lwip_callback_msg
*msg
= arg
;
577 PTCP_PCB pcb
= msg
->Input
.Shutdown
.Connection
->SocketContext
;
579 if (!msg
->Input
.Shutdown
.Connection
->SocketContext
)
581 msg
->Output
.Shutdown
.Error
= ERR_CLSD
;
585 if (pcb
->state
== CLOSE_WAIT
)
587 /* This case actually results in a socket closure later (lwIP bug?) */
588 msg
->Input
.Shutdown
.Connection
->SocketContext
= NULL
;
591 msg
->Output
.Shutdown
.Error
= tcp_shutdown(pcb
, msg
->Input
.Shutdown
.shut_rx
, msg
->Input
.Shutdown
.shut_tx
);
592 if (msg
->Output
.Shutdown
.Error
)
594 msg
->Input
.Shutdown
.Connection
->SocketContext
= pcb
;
598 if (msg
->Input
.Shutdown
.shut_rx
)
599 msg
->Input
.Shutdown
.Connection
->ReceiveShutdown
= TRUE
;
601 if (msg
->Input
.Shutdown
.shut_tx
)
602 msg
->Input
.Shutdown
.Connection
->SendShutdown
= TRUE
;
606 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
610 LibTCPShutdown(PCONNECTION_ENDPOINT Connection
, const int shut_rx
, const int shut_tx
)
612 struct lwip_callback_msg
*msg
;
615 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
618 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
620 msg
->Input
.Shutdown
.Connection
= Connection
;
621 msg
->Input
.Shutdown
.shut_rx
= shut_rx
;
622 msg
->Input
.Shutdown
.shut_tx
= shut_tx
;
624 tcpip_callback_with_block(LibTCPShutdownCallback
, msg
, 1);
626 if (WaitForEventSafely(&msg
->Event
))
627 ret
= msg
->Output
.Shutdown
.Error
;
631 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
641 LibTCPCloseCallback(void *arg
)
643 struct lwip_callback_msg
*msg
= arg
;
644 PTCP_PCB pcb
= msg
->Input
.Close
.Connection
->SocketContext
;
647 /* Empty the queue even if we're already "closed" */
648 LibTCPEmptyQueue(msg
->Input
.Close
.Connection
);
650 if (!msg
->Input
.Close
.Connection
->SocketContext
)
652 msg
->Output
.Close
.Error
= ERR_OK
;
656 /* Clear the PCB pointer */
657 msg
->Input
.Close
.Connection
->SocketContext
= NULL
;
659 /* Save the old PCB state */
662 msg
->Output
.Close
.Error
= tcp_close(pcb
);
663 if (!msg
->Output
.Close
.Error
)
665 if (msg
->Input
.Close
.Callback
)
667 /* Call the FIN handler in the cases where it will not be called by lwIP */
673 TCPFinEventHandler(msg
->Input
.Close
.Connection
, ERR_OK
);
683 /* Restore the PCB pointer */
684 msg
->Input
.Close
.Connection
->SocketContext
= pcb
;
688 KeSetEvent(&msg
->Event
, IO_NO_INCREMENT
, FALSE
);
692 LibTCPClose(PCONNECTION_ENDPOINT Connection
, const int safe
, const int callback
)
695 struct lwip_callback_msg
*msg
;
697 msg
= ExAllocateFromNPagedLookasideList(&MessageLookasideList
);
700 KeInitializeEvent(&msg
->Event
, NotificationEvent
, FALSE
);
702 msg
->Input
.Close
.Connection
= Connection
;
703 msg
->Input
.Close
.Callback
= callback
;
706 LibTCPCloseCallback(msg
);
708 tcpip_callback_with_block(LibTCPCloseCallback
, msg
, 1);
710 if (WaitForEventSafely(&msg
->Event
))
711 ret
= msg
->Output
.Close
.Error
;
715 ExFreeToNPagedLookasideList(&MessageLookasideList
, msg
);
724 LibTCPAccept(PTCP_PCB pcb
, struct tcp_pcb
*listen_pcb
, void *arg
)
729 tcp_recv(pcb
, InternalRecvEventHandler
);
730 tcp_sent(pcb
, InternalSendEventHandler
);
731 tcp_err(pcb
, InternalErrorEventHandler
);
734 tcp_accepted(listen_pcb
);
738 LibTCPGetHostName(PTCP_PCB pcb
, struct ip_addr
*const ipaddr
, u16_t
*const port
)
743 *ipaddr
= pcb
->local_ip
;
744 *port
= pcb
->local_port
;
750 LibTCPGetPeerName(PTCP_PCB pcb
, struct ip_addr
* const ipaddr
, u16_t
* const port
)
755 *ipaddr
= pcb
->remote_ip
;
756 *port
= pcb
->remote_port
;