diff --git a/reactos/drivers/network/tcpip/include/fileobjs.h b/reactos/drivers/network/tcpip/include/fileobjs.h index 33acb9c005a..5283ccdf590 100644 --- a/reactos/drivers/network/tcpip/include/fileobjs.h +++ b/reactos/drivers/network/tcpip/include/fileobjs.h @@ -35,4 +35,6 @@ NTSTATUS FileOpenControlChannel( NTSTATUS FileCloseControlChannel( PTDI_REQUEST Request); +VOID LogActiveObjects(VOID); + /* EOF */ diff --git a/reactos/drivers/network/tcpip/include/tcp.h b/reactos/drivers/network/tcpip/include/tcp.h index ce6f84bb341..c31293c0e0b 100644 --- a/reactos/drivers/network/tcpip/include/tcp.h +++ b/reactos/drivers/network/tcpip/include/tcp.h @@ -211,3 +211,6 @@ VOID FlushAllQueues(PCONNECTION_ENDPOINT Connection, NTSTATUS Status); VOID CompleteBucket(PCONNECTION_ENDPOINT Connection, PTDI_BUCKET Bucket, const BOOLEAN Synchronous); + +void +LibTCPDumpPcb(PVOID SocketContext); diff --git a/reactos/drivers/network/tcpip/include/titypes.h b/reactos/drivers/network/tcpip/include/titypes.h index e9380ffc030..70ba4ea0d9c 100644 --- a/reactos/drivers/network/tcpip/include/titypes.h +++ b/reactos/drivers/network/tcpip/include/titypes.h @@ -281,6 +281,7 @@ typedef struct _CONNECTION_ENDPOINT { BOOLEAN SendShutdown; BOOLEAN ReceiveShutdown; NTSTATUS ReceiveShutdownStatus; + BOOLEAN Closing; struct _CONNECTION_ENDPOINT *Next; /* Next connection in address file list */ } CONNECTION_ENDPOINT, *PCONNECTION_ENDPOINT; diff --git a/reactos/drivers/network/tcpip/tcpip/fileobjs.c b/reactos/drivers/network/tcpip/tcpip/fileobjs.c index 7ef8c7d558a..30a47136cea 100644 --- a/reactos/drivers/network/tcpip/tcpip/fileobjs.c +++ b/reactos/drivers/network/tcpip/tcpip/fileobjs.c @@ -10,6 +10,8 @@ #include "precomp.h" +/* Uncomment for logging of connections and address files every 10 seconds */ +#define LOG_OBJECTS /* List of all address file objects managed by this driver */ LIST_ENTRY AddressFileListHead; @@ -99,6 +101,88 @@ BOOLEAN AddrReceiveMatch( return FALSE; } +VOID +LogActiveObjects(VOID) +{ +#ifdef LOG_OBJECTS + PLIST_ENTRY CurrentEntry; + KIRQL OldIrql; + PADDRESS_FILE AddrFile; + PCONNECTION_ENDPOINT Conn; + + DbgPrint("----------- TCP/IP Active Object Dump -------------\n"); + + TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql); + + CurrentEntry = AddressFileListHead.Flink; + while (CurrentEntry != &AddressFileListHead) + { + AddrFile = CONTAINING_RECORD(CurrentEntry, ADDRESS_FILE, ListEntry); + + DbgPrint("Address File (%s, %d, %d) @ 0x%p | Ref count: %d | Sharers: %d\n", + A2S(&AddrFile->Address), WN2H(AddrFile->Port), AddrFile->Protocol, + AddrFile, AddrFile->RefCount, AddrFile->Sharers); + DbgPrint("\tListener: "); + if (AddrFile->Listener == NULL) + DbgPrint("\n"); + else + DbgPrint("0x%p\n", AddrFile->Listener); + DbgPrint("\tAssociated endpoints: "); + if (AddrFile->Connection == NULL) + DbgPrint("\n"); + else + { + Conn = AddrFile->Connection; + while (Conn) + { + DbgPrint("0x%p ", Conn); + Conn = Conn->Next; + } + DbgPrint("\n"); + } + + CurrentEntry = CurrentEntry->Flink; + } + + TcpipReleaseSpinLock(&AddressFileListLock, OldIrql); + + TcpipAcquireSpinLock(&ConnectionEndpointListLock, &OldIrql); + + CurrentEntry = ConnectionEndpointListHead.Flink; + while (CurrentEntry != &ConnectionEndpointListHead) + { + Conn = CONTAINING_RECORD(CurrentEntry, CONNECTION_ENDPOINT, ListEntry); + + DbgPrint("Connection @ 0x%p | Ref count: %d\n", Conn, Conn->RefCount); + DbgPrint("\tPCB: "); + if (Conn->SocketContext == NULL) + DbgPrint("\n"); + else + { + DbgPrint("0x%p\n", Conn->SocketContext); + LibTCPDumpPcb(Conn->SocketContext); + } + DbgPrint("\tPacket queue status: %s\n", IsListEmpty(&Conn->PacketQueue) ? "Empty" : "Not Empty"); + DbgPrint("\tRequest lists: Connect: %s | Recv: %s | Send: %s | Shutdown: %s | Listen: %s\n", + IsListEmpty(&Conn->ConnectRequest) ? "Empty" : "Not Empty", + IsListEmpty(&Conn->ReceiveRequest) ? "Empty" : "Not Empty", + IsListEmpty(&Conn->SendRequest) ? "Empty" : "Not Empty", + IsListEmpty(&Conn->ShutdownRequest) ? "Empty" : "Not Empty", + IsListEmpty(&Conn->ListenRequest) ? "Empty" : "Not Empty"); + DbgPrint("\tSend shutdown: %s\n", Conn->SendShutdown ? "Yes" : "No"); + DbgPrint("\tReceive shutdown: %s\n", Conn->ReceiveShutdown ? "Yes" : "No"); + if (Conn->ReceiveShutdown) DbgPrint("\tReceive shutdown status: 0x%x\n", Conn->ReceiveShutdownStatus); + DbgPrint("\tClosing: %s\n", Conn->Closing ? "Yes" : "No"); + + CurrentEntry = CurrentEntry->Flink; + } + + TcpipReleaseSpinLock(&ConnectionEndpointListLock, OldIrql); + + DbgPrint("---------------------------------------------------\n"); +#endif +} + PADDRESS_FILE AddrFindShared( PIP_ADDRESS BindAddress, USHORT Port, diff --git a/reactos/lib/drivers/ip/network/ip.c b/reactos/lib/drivers/ip/network/ip.c index 14a36f5d8c8..ac26f16225f 100644 --- a/reactos/lib/drivers/ip/network/ip.c +++ b/reactos/lib/drivers/ip/network/ip.c @@ -24,6 +24,8 @@ BOOLEAN IpWorkItemQueued = FALSE; IP_PROTOCOL_HANDLER ProtocolTable[IP_PROTOCOL_TABLE_SIZE]; +ULONG IpTimerExpirations; + VOID TCPRegisterInterface(PIP_INTERFACE IF); @@ -119,9 +121,16 @@ VOID NTAPI IPTimeoutDpcFn(PKDPC Dpc, * SystemArgument1 = Unused * SystemArgument2 = Unused * NOTES: - * This routine is dispatched once in a while to do maintainance jobs + * This routine is dispatched once in a while to do maintenance jobs */ { + IpTimerExpirations++; + + if ((IpTimerExpirations % 10) == 0) + { + LogActiveObjects(); + } + /* Check if datagram fragments have taken too long to assemble */ IPDatagramReassemblyTimeout(); diff --git a/reactos/lib/drivers/lwip/src/rostcp.c b/reactos/lib/drivers/lwip/src/rostcp.c index 57c8876c29d..55f929684bf 100755 --- a/reactos/lib/drivers/lwip/src/rostcp.c +++ b/reactos/lib/drivers/lwip/src/rostcp.c @@ -35,6 +35,21 @@ extern NPAGED_LOOKASIDE_LIST QueueEntryLookasideList; /* Required for ERR_T to NTSTATUS translation in receive error handling */ NTSTATUS TCPTranslateError(const err_t err); +void +LibTCPDumpPcb(PVOID SocketContext) +{ + struct tcp_pcb *pcb = (struct tcp_pcb*)SocketContext; + unsigned int addr = ntohl(pcb->remote_ip.addr); + + DbgPrint("\tState: %s\n", tcp_state_str[pcb->state]); + DbgPrint("\tRemote: (%d.%d.%d.%d, %d)\n", + (addr >> 24) & 0xFF, + (addr >> 16) & 0xFF, + (addr >> 8) & 0xFF, + addr & 0xFF, + pcb->remote_port); +} + static void LibTCPEmptyQueue(PCONNECTION_ENDPOINT Connection) @@ -231,18 +246,15 @@ InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t er Connection->ReceiveShutdown = TRUE; Connection->ReceiveShutdownStatus = STATUS_SUCCESS; - /* This code path executes for both remotely and locally initiated closures, - * and we need to distinguish between them */ - if (Connection->SocketContext) + /* If we already did a send shutdown, we're in TIME_WAIT so we can't use this PCB anymore */ + if (Connection->SendShutdown) { - /* Remotely initiated close */ - TCPRecvEventHandler(arg); - } - else - { - /* Locally initated close */ - TCPFinEventHandler(arg, ERR_CLSD); + Connection->SocketContext = NULL; + tcp_arg(pcb, NULL); } + + /* Remotely initiated close */ + TCPRecvEventHandler(arg); } return ERR_OK; @@ -285,30 +297,18 @@ void InternalErrorEventHandler(void *arg, const err_t err) { PCONNECTION_ENDPOINT Connection = arg; - KIRQL OldIrql; /* Make sure the socket didn't get closed */ if (!arg) return; - /* Check if data is left to be read */ - LockObject(Connection, &OldIrql); - if (IsListEmpty(&Connection->PacketQueue)) - { - UnlockObject(Connection, OldIrql); + /* The PCB is dead now */ + Connection->SocketContext = NULL; - /* Deliver the error now */ - TCPFinEventHandler(arg, err); - } - else - { - UnlockObject(Connection, OldIrql); + /* Defer the error delivery until all data is gone */ + Connection->ReceiveShutdown = TRUE; + Connection->ReceiveShutdownStatus = TCPTranslateError(err); - /* Defer the error delivery until all data is gone */ - Connection->ReceiveShutdown = TRUE; - Connection->ReceiveShutdownStatus = TCPTranslateError(err); - - TCPRecvEventHandler(arg); - } + TCPRecvEventHandler(arg); } static @@ -633,7 +633,12 @@ LibTCPShutdownCallback(void *arg) goto done; } - /* These need to be called separately, otherwise we get a tcp_close() */ + /* LwIP makes the (questionable) assumption that SHUTDOWN_RDWR is equivalent to tcp_close(). + * This assumption holds even if the shutdown calls are done separately (even through multiple + * WinSock shutdown() calls). This assumption means that lwIP has the right to deallocate our + * PCB without telling us if we shutdown TX and RX. To avoid these problems, we'll clear the + * socket context if we have called shutdown for TX and RX. + */ if (msg->Input.Shutdown.shut_rx) { msg->Output.Shutdown.Error = tcp_shutdown(pcb, TRUE, FALSE); } @@ -651,6 +656,14 @@ LibTCPShutdownCallback(void *arg) if (msg->Input.Shutdown.shut_tx) msg->Input.Shutdown.Connection->SendShutdown = TRUE; + + if (msg->Input.Shutdown.Connection->ReceiveShutdown && + msg->Input.Shutdown.Connection->SendShutdown) + { + /* The PCB is not ours anymore */ + msg->Input.Shutdown.Connection->SocketContext = NULL; + tcp_arg(pcb, NULL); + } } done: @@ -697,37 +710,43 @@ LibTCPCloseCallback(void *arg) /* Empty the queue even if we're already "closed" */ LibTCPEmptyQueue(msg->Input.Close.Connection); - if (!msg->Input.Close.Connection->SocketContext) + /* Check if we've already been closed */ + if (msg->Input.Close.Connection->Closing) { msg->Output.Close.Error = ERR_OK; goto done; } - /* Clear the PCB pointer */ - msg->Input.Close.Connection->SocketContext = NULL; + /* Enter "closing" mode if we're doing a normal close */ + if (msg->Input.Close.Callback) + msg->Input.Close.Connection->Closing = TRUE; - switch (pcb->state) + /* Check if the PCB was already "closed" but the client doesn't know it yet */ + if (!msg->Input.Close.Connection->SocketContext) { - case CLOSED: - case LISTEN: - case SYN_SENT: - msg->Output.Close.Error = tcp_close(pcb); - - if (!msg->Output.Close.Error && msg->Input.Close.Callback) - TCPFinEventHandler(msg->Input.Close.Connection, ERR_CLSD); - break; - - default: - /* Abort the socket */ - tcp_abort(pcb); - msg->Output.Close.Error = ERR_OK; - break; + if (msg->Input.Close.Callback) + TCPFinEventHandler(msg->Input.Close.Connection, ERR_CLSD); + msg->Output.Close.Error = ERR_OK; + goto done; } + /* Clear the PCB pointer and stop callbacks */ + msg->Input.Close.Connection->SocketContext = NULL; + tcp_arg(pcb, NULL); + + /* This may generate additional callbacks but we don't care, + * because they're too inconsistent to rely on */ + msg->Output.Close.Error = tcp_close(pcb); + if (msg->Output.Close.Error) { /* Restore the PCB pointer */ msg->Input.Close.Connection->SocketContext = pcb; + msg->Input.Close.Connection->Closing = FALSE; + } + else if (msg->Input.Close.Callback) + { + TCPFinEventHandler(msg->Input.Close.Connection, ERR_CLSD); } done: