diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/tcpip/tcp/tcpconn.c | 2344 |
1 files changed, 2344 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/tcp/tcpconn.c b/private/ntos/tdi/tcpip/tcp/tcpconn.c new file mode 100644 index 000000000..daeadf85c --- /dev/null +++ b/private/ntos/tdi/tcpip/tcp/tcpconn.c @@ -0,0 +1,2344 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** TCPCONN.C - TCP connection mgmt code. +// +// This file contains the code handling TCP connection related requests, +// such as connecting and disconnecting. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "tdi.h" +#ifdef VXD +#include "tdivxd.h" +#include "tdistat.h" +#endif +#ifdef NT +#include "tdint.h" +#include "tdistat.h" +#endif +#include "queue.h" +#include "addr.h" +#include "tcp.h" +#include "tcb.h" +#include "tcpconn.h" +#include "tcpsend.h" +#include "tcprcv.h" +#include "tcpdeliv.h" +#include "tlcommon.h" +#include "info.h" +#include "tcpcfg.h" + +#define CONN_INDEX(c) ((c) & 0xffffff) +#define CONN_INST(c) ((uchar)((c) >> 24)) +#define MAKE_CONN_ID(i, s) ((((uint)(s)) << 24) | ((uint)(i))) +#define GROW_DELTA 16 +#define INVALID_CONN_ID MAKE_CONN_ID(INVALID_CONN_INDEX, 0xff) + + +#ifndef NT +TCPConnReq *ConnReqFree; // Connection request free list. +#else +SLIST_HEADER ConnReqFree; // Connection request free list. +extern PDRIVER_OBJECT TCPDriverObject; +#endif + +DEFINE_LOCK_STRUCTURE(ConnReqFreeLock) // Lock to protect conn req free list. +uint NumConnReq; // Current number of ConnReqs in system. +uint MaxConnReq = 0xffffffff; // Maximum allowed number of ConnReqs. + +TCPConnTable *ConnTable = NULL; // The current connection table. + +uint ConnTableSize; // Current number of entries in the + // ConnTable. +uchar ConnInst; // Current conn inst in use. +uint NextConnIndex; // Next conn. index to use. + +DEFINE_LOCK_STRUCTURE(ConnTableLock) +EXTERNAL_LOCK(AddrObjTableLock) +EXTERNAL_LOCK(TCBTableLock) + +TCPAddrCheckElement *AddrCheckTable = NULL; // The current check table + +extern IPInfo LocalNetInfo; +extern void RemoveConnFromAO(AddrObj *AO, TCPConn *Conn); + + +// +// All of the init code can be discarded. +// +#ifdef NT +#ifdef ALLOC_PRAGMA + +int InitTCPConn(void); +void UnInitTCPConn(void); +void NotifyConnLimitProc(CTEEvent *Event, void *Context); + +typedef struct ConnLimitExceededStruct { + CTEEvent cle_event; + IPAddr cle_addr; + ulong cle_port; +} ConnLimitExceededStruct; + + +#pragma alloc_text(INIT, InitTCPConn) +#pragma alloc_text(INIT, UnInitTCPConn) +#pragma alloc_text(PAGE, NotifyConnLimitProc) + +#endif // ALLOC_PRAGMA +#endif + +void CompleteConnReq(TCB *CmpltTCB, IPOptInfo *OptInfo, TDI_STATUS Status); + + +//** Routines for handling conn refcount going to 0. + +//* DummyDone - Called when nothing to do. +// +// Input: Conn - Conn goint to 0. +// Handle - Lock handle for conn table lock. +// +// Returns: Nothing. +// +void +DummyDone(TCPConn *Conn, CTELockHandle Handle) +{ + CTEFreeLock(&ConnTableLock, Handle); +} + +//* DummyCmplt - Dummy close completion routine. +void +DummyCmplt(PVOID Dummy1, uint Dummy2, uint Dummy3) +{ +} + +//* CloseDone - Called when we need to complete a close. +// +// Input: Conn - Conn going to 0. +// Handle - Lock handle for conn table lock. +// +// Returns: Nothing. +// +void +CloseDone(TCPConn *Conn, CTELockHandle Handle) +{ + CTEReqCmpltRtn Rtn; // Completion routine. + PVOID Context; // User context for completion routine. + CTELockHandle AOTableHandle, ConnTableHandle, AOHandle; + AddrObj *AO; + + CTEAssert(Conn->tc_flags & CONN_CLOSING); + + Rtn = Conn->tc_rtn; + Context = Conn->tc_rtncontext; + CTEFreeLock(&ConnTableLock, Handle); + + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + CTEGetLock(&ConnTableLock, &ConnTableHandle); + + if ((AO = Conn->tc_ao) != NULL) { + + CTEStructAssert(AO, ao); + + // It's associated. + CTEGetLock(&AO->ao_lock, &AOHandle); + RemoveConnFromAO(AO, Conn); + // We've pulled him from the AO, we can free the lock now. + CTEFreeLock(&AO->ao_lock, AOHandle); + } + + CTEFreeLock(&ConnTableLock, ConnTableHandle); + CTEFreeLock(&AddrObjTableLock, AOTableHandle); + + CTEFreeMem(Conn); + + (*Rtn)(Context, TDI_SUCCESS, 0); + +} + +//* DisassocDone - Called when we need to complete a disassociate. +// +// Input: Conn - Conn going to 0. +// Handle - Lock handle for conn table lock. +// +// Returns: Nothing. +// +void +DisassocDone(TCPConn *Conn, CTELockHandle Handle) +{ + CTEReqCmpltRtn Rtn; // Completion routine. + PVOID Context; // User context for completion routine. + AddrObj *AO; + CTELockHandle AOTableHandle, ConnTableHandle, AOHandle; + uint NeedClose = FALSE; + + CTEAssert(Conn->tc_flags & CONN_DISACC); + CTEAssert(!(Conn->tc_flags & CONN_CLOSING)); + CTEAssert(Conn->tc_refcnt == 0); + + Rtn = Conn->tc_rtn; + Context = Conn->tc_rtncontext; + Conn->tc_refcnt = 1; + CTEFreeLock(&ConnTableLock, Handle); + + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + CTEGetLock(&ConnTableLock, &ConnTableHandle); + if (!(Conn->tc_flags & CONN_CLOSING)) { + + AO = Conn->tc_ao; + if (AO != NULL) { + CTEGetLock(&AO->ao_lock, &AOHandle); + RemoveConnFromAO(AO, Conn); + CTEFreeLock(&AO->ao_lock, AOHandle); + } + + CTEAssert(Conn->tc_refcnt == 1); + Conn->tc_flags &= ~CONN_DISACC; + } else + NeedClose = TRUE; + + Conn->tc_refcnt = 0; + CTEFreeLock(&AddrObjTableLock, ConnTableHandle); + + if (NeedClose) { + CloseDone(Conn, AOTableHandle); + } else { + CTEFreeLock(&ConnTableLock, AOTableHandle); + (*Rtn)(Context, TDI_SUCCESS, 0); + } + +} + + +//* FreeConnReq - Free a connection request structure. +// +// Called to free a connection request structure. +// +// Input: FreedReq - Connection request structure to be freed. +// +// Returns: Nothing. +// +void +FreeConnReq(TCPConnReq *FreedReq) +{ +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; + + CTEStructAssert(FreedReq, tcr); + + BufferLink = STRUCT_OF( + SINGLE_LIST_ENTRY, + &(FreedReq->tcr_req.tr_q.q_next), + Next + ); + + ExInterlockedPushEntrySList( + &ConnReqFree, + BufferLink, + &ConnReqFreeLock + ); + +#else // NT + TCPConnReq **Temp; + + CTEStructAssert(FreedReq, tcr); + + Temp = (TCPConnReq **)&FreedReq->tcr_req.tr_q.q_next; + *Temp = ConnReqFree; + ConnReqFree = FreedReq; + +#endif // NT +} + +//* GetConnReq - Get a connection request structure. +// +// Called to get a connection request structure. +// +// Input: Nothing. +// +// Returns: Pointer to ConnReq structure, or NULL if none. +// +TCPConnReq * +GetConnReq(void) +{ + TCPConnReq *Temp; + +#ifdef NT + PSINGLE_LIST_ENTRY BufferLink; + Queue *QueuePtr; + TCPReq *ReqPtr; + + BufferLink = ExInterlockedPopEntrySList( + &ConnReqFree, + &ConnReqFreeLock + ); + + if (BufferLink != NULL) { + QueuePtr = STRUCT_OF(Queue, BufferLink, q_next); + ReqPtr = STRUCT_OF(TCPReq, QueuePtr, tr_q); + Temp = STRUCT_OF(TCPConnReq, ReqPtr, tcr_req); + CTEStructAssert(Temp, tcr); + } + else { + if (NumConnReq < MaxConnReq) + Temp = CTEAllocMem(sizeof(TCPConnReq)); + else + Temp = NULL; + + if (Temp != NULL) { + ExInterlockedAddUlong(&NumConnReq, 1, &ConnReqFreeLock); +#ifdef DEBUG + Temp->tcr_req.tr_sig = tr_signature; + Temp->tcr_sig = tcr_signature; +#endif + } + } + +#else // NT + + Temp = ConnReqFree; + if (Temp != NULL) + ConnReqFree = (TCPConnReq *)Temp->tcr_req.tr_q.q_next; + else { + if (NumConnReq < MaxConnReq) + Temp = CTEAllocMem(sizeof(TCPConnReq)); + else + Temp = NULL; + + if (Temp != NULL) { + NumConnReq++; +#ifdef DEBUG + Temp->tcr_req.tr_sig = tr_signature; + Temp->tcr_sig = tcr_signature; +#endif + } + } + +#endif // NT + + return Temp; +} + +//* GetConnFromConnID - Get a Connection from a connection ID. +// +// Called to obtain a Connection pointer from a ConnID. We don't actually +// check the connection pointer here, but we do bounds check the input ConnID +// and make sure the instance fields match. +// We assume the caller has taken the ConnTable lock. +// +// Input: ConnID - Connection ID to find a pointer for. +// +// Returns: Pointer to the TCPConn, or NULL. +// +TCPConn * +GetConnFromConnID(uint ConnID) +{ + uint ConnIndex = CONN_INDEX(ConnID); + TCPConn *MatchingConn; + + if (ConnIndex < ConnTableSize) { + MatchingConn = (*ConnTable)[ConnIndex]; + if (MatchingConn != NULL) { + CTEStructAssert(MatchingConn, tc); + if (MatchingConn->tc_inst != CONN_INST(ConnID)) + MatchingConn = NULL; + } + } else + MatchingConn = NULL; + + return MatchingConn; + + +} + +//* GetConnID - Get a ConnTable slot. +// +// Called during OpenConnection to find a free slot in the ConnTable and +// set it up with a connection. We assume the caller holds the lock on the +// TCB ConnTable when we are called. +// +// Input: NewConn - Connection to enter into slot.. +// +// Returns: A ConnId to use. +// +uint +GetConnID(TCPConn *NewConn) +{ + uint CurrConnID; + uint i; // Index variable. + + // Keep doing this until it works. + for (;;) { + CurrConnID = NextConnIndex; + + for (i = 0; i < ConnTableSize; i++ ) { + if (CurrConnID == ConnTableSize) + CurrConnID = 0; // Wrapped, start at 0. + + if ((*ConnTable)[CurrConnID] == NULL) + break; // Found a free one. + + ++CurrConnID; + } + + if (i < ConnTableSize) { + // We found a free slot. + (*ConnTable)[CurrConnID] = NewConn; + NextConnIndex = CurrConnID + 1; + ConnInst++; + NewConn->tc_inst = ConnInst; + return MAKE_CONN_ID(CurrConnID, ConnInst); + } + + // Didn't find a free slot. Grow the table. + if (ConnTableSize != MaxConnections) { + uint NewTableSize; + TCPConnTable *NewTable; + + NewTableSize = MIN(ConnTableSize + GROW_DELTA, MaxConnections); + NewTable = CTEAllocMem(NewTableSize * sizeof(TCPConn *)); + if (NewTable != NULL) { + TCPConnTable *OldTable; + // We allocated it. Copy the old table in, and update ptrs and + // size. + CTEMemSet(NewTable, 0, NewTableSize * sizeof(TCPConn *)); + CTEMemCopy(NewTable, ConnTable, ConnTableSize * + (sizeof (TCPConn *))); + OldTable = ConnTable; + ConnTable = NewTable; + ConnTableSize = NewTableSize; + if (OldTable != NULL) + CTEFreeMem(OldTable); + // Try it again, from the top. + continue; + } else { + // Couldn't grow the table. + return INVALID_CONN_ID; + } + + } else { + // Table's already at the maximum allowable size. + return INVALID_CONN_ID; + } + } + + +} + +//* FreeConnID - Free a ConnTable slot. +// +// Called when we're done with a ConnID. We assume the caller holds the lock +// on the TCB ConnTable when we are called. +// +// Input: ConnID - Connection ID to be freed. +// +// Returns: Nothing. +// +void +FreeConnID(uint ConnID) +{ + uint Index = CONN_INDEX(ConnID); // Index into conn table. + + CTEAssert(Index < ConnTableSize); + CTEAssert((*ConnTable)[Index] != NULL); + CTEStructAssert((*ConnTable)[Index], tc); + + FREE_CONN_INDEX(Index); + +} + +//* MapIPError - Map an IP error to a TDI error. +// +// Called to map an input IP error code to a TDI error code. If we can't, +// we return the provided default. +// +// Input: IPError - Error code to be mapped. +// Default - Default error code to return. +// +// Returns: Mapped TDI error. +// +TDI_STATUS +MapIPError(IP_STATUS IPError, TDI_STATUS Default) +{ + switch (IPError) { + + case IP_DEST_NET_UNREACHABLE: + return TDI_DEST_NET_UNREACH; + case IP_DEST_HOST_UNREACHABLE: + return TDI_DEST_HOST_UNREACH; + case IP_DEST_PROT_UNREACHABLE: + return TDI_DEST_PROT_UNREACH; + case IP_DEST_PORT_UNREACHABLE: + return TDI_DEST_PORT_UNREACH; + default: + return Default; + } +} + +//* FinishRemoveTCBFromConn - Finish removing a TCB from a conn structure. +// +// Called when we have the locks we need and we just want to pull the +// TCB off the connection. The caller must hold the ConnTableLock before +// calling this. +// +// Input: RemovedTCB - TCB to be removed. +// +// Returns: Nothing. +// +void +FinishRemoveTCBFromConn(TCB *RemovedTCB) +{ + TCPConn *Conn; + CTELockHandle AOHandle, TCBHandle; + AddrObj *AO; + + if ((( Conn = RemovedTCB->tcb_conn ) != NULL ) && + ( Conn->tc_tcb == RemovedTCB ) ) { + CTEStructAssert(Conn, tc); + + AO = Conn->tc_ao; + + if (AO != NULL) { + CTEGetLockAtDPC(&AO->ao_lock, &AOHandle); + CTEGetLockAtDPC(&RemovedTCB->tcb_lock, &TCBHandle); + + // Need to double check this is still correct. + + if (Conn == RemovedTCB->tcb_conn) { + // Everything still looks good. + REMOVEQ(&Conn->tc_q); + ENQUEUE(&AO->ao_idleq, &Conn->tc_q); + } else + Conn = RemovedTCB->tcb_conn; + + CTEFreeLockFromDPC(&AO->ao_lock, TCBHandle); + } else { + CTEGetLockAtDPC(&RemovedTCB->tcb_lock, &AOHandle); + Conn = RemovedTCB->tcb_conn; + } + + if (Conn != NULL) { + if (Conn->tc_tcb == RemovedTCB) + Conn->tc_tcb = NULL; + else + CTEAssert(Conn->tc_tcb == NULL); + } + + CTEFreeLockFromDPC(&RemovedTCB->tcb_lock, AOHandle); + } +} + +//* RemoveTCBFromConn - Remove a TCB from a Conn structure. +// +// Called when we need to disassociate a TCB from a connection structure. +// All we do is get the appropriate locks and call FinishRemoveTCBFromConn. +// +// Input: RemovedTCB - TCB to be removed. +// +// Returns: Nothing. +// +void +RemoveTCBFromConn(TCB *RemovedTCB) +{ + CTELockHandle ConnHandle, TCBHandle; + + CTEStructAssert(RemovedTCB, tcb); + + CTEGetLock(&ConnTableLock, &ConnHandle); + + FinishRemoveTCBFromConn(RemovedTCB); + + CTEFreeLock(&ConnTableLock, ConnHandle); + +} + +//* RemoveConnFromTCB - Remove a conn from a TCB. +// +// Called when we want to break the final association between a connection +// and a TCB. +// +// Input: RemoveTCB - TCB to be removed. +// +// Returns: Nothing. +// +void +RemoveConnFromTCB(TCB *RemoveTCB) +{ + ConnDoneRtn DoneRtn = NULL; + CTELockHandle ConnHandle, TCBHandle; + TCPConn *Conn; + + CTEGetLock(&ConnTableLock, &ConnHandle); + CTEGetLock(&RemoveTCB->tcb_lock, &TCBHandle); + + + if ((Conn = RemoveTCB->tcb_conn) != NULL) { + + CTEStructAssert(Conn, tc); + + if (--(Conn->tc_refcnt) == 0) + DoneRtn = Conn->tc_donertn; + + RemoveTCB->tcb_conn = NULL; + } + + CTEFreeLock(&RemoveTCB->tcb_lock, TCBHandle); + + if (DoneRtn != NULL) + (*DoneRtn)(Conn, ConnHandle); + else + CTEFreeLock(&ConnTableLock, ConnHandle); +} + + +//* CloseTCB - Close a TCB. +// +// Called when we are done with a TCB, and want to free it. We'll remove +// him from any tables that he's in, and destroy any outstanding requests. +// +// Input: ClosedTCB - TCB to be closed. +// Handle - Lock handle for TCB. +// +// Returns: Nothing. +// +void +CloseTCB(TCB *ClosedTCB, CTELockHandle Handle) +{ + CTELockHandle ConnTableHandle, TCBTableHandle; + uchar OrigState = ClosedTCB->tcb_state; + TDI_STATUS Status; + uint OKToFree; + + + CTEStructAssert(ClosedTCB, tcb); + CTEAssert(ClosedTCB->tcb_refcnt == 0); + CTEAssert(ClosedTCB->tcb_state != TCB_CLOSED); + CTEAssert(ClosedTCB->tcb_pending & DEL_PENDING); + + CTEFreeLock(&ClosedTCB->tcb_lock, Handle); + + // We need to get the ConnTable, TCBTable, and TCB locks to pull + // this guy from all the appropriate tables. + CTEGetLock(&ConnTableLock, &ConnTableHandle); + + // We'll check to make sure that our state isn't CLOSED. This should never + // happen, since nobody should call TryToCloseTCB when the state is + // closed, or take the reference count if we're closing. Nevertheless, + // we'll double check as a safety measure. + if (ClosedTCB->tcb_state == TCB_CLOSED) { + DEBUGCHK; + CTEFreeLock(&ConnTableLock, ConnTableHandle); + return; + } + + // Update SNMP counters. If we're in SYN-SENT or SYN-RCVD, this is a failed + // connection attempt. If we're in ESTABLISED or CLOSE-WAIT, treat this + // as an 'Established Reset' event. + if (ClosedTCB->tcb_state == TCB_SYN_SENT || + ClosedTCB->tcb_state == TCB_SYN_RCVD) + TStats.ts_attemptfails++; + else + if (ClosedTCB->tcb_state == TCB_ESTAB || + ClosedTCB->tcb_state == TCB_CLOSE_WAIT) { + TStats.ts_estabresets++; + TStats.ts_currestab--; + CTEAssert(*(int *)&TStats.ts_currestab >= 0); + } + + ClosedTCB->tcb_state = TCB_CLOSED; + + + // Remove the TCB from it's associated TCPConn structure, if it has one. + FinishRemoveTCBFromConn(ClosedTCB); + + CTEGetLockAtDPC(&TCBTableLock, &TCBTableHandle); + CTEGetLockAtDPC(&ClosedTCB->tcb_lock, &Handle); + + OKToFree = RemoveTCB(ClosedTCB); + + // He's been pulled from the appropriate places so nobody can find him. + // Free the locks, and proceed to destroy any requests, etc. + CTEFreeLockFromDPC(&ClosedTCB->tcb_lock, Handle); + CTEFreeLockFromDPC(&TCBTableLock, TCBTableHandle); + CTEFreeLock(&ConnTableLock, ConnTableHandle); + + if (SYNC_STATE(OrigState) && !GRACEFUL_CLOSED_STATE(OrigState)) { + if (ClosedTCB->tcb_flags & NEED_RST) + SendRSTFromTCB(ClosedTCB); + } + + (*LocalNetInfo.ipi_freeopts)(&ClosedTCB->tcb_opt); + (*LocalNetInfo.ipi_closerce)(ClosedTCB->tcb_rce); + + if (ClosedTCB->tcb_closereason & TCB_CLOSE_RST) + Status = TDI_CONNECTION_RESET; + else if (ClosedTCB->tcb_closereason & TCB_CLOSE_ABORTED) + Status = TDI_CONNECTION_ABORTED; + else if (ClosedTCB->tcb_closereason & TCB_CLOSE_TIMEOUT) + Status = MapIPError(ClosedTCB->tcb_error, TDI_TIMED_OUT); + else if (ClosedTCB->tcb_closereason & TCB_CLOSE_REFUSED) + Status = TDI_CONN_REFUSED; + else if (ClosedTCB->tcb_closereason & TCB_CLOSE_UNREACH) + Status = MapIPError(ClosedTCB->tcb_error, TDI_DEST_UNREACHABLE); + else + Status = TDI_SUCCESS; + + // Now complete any outstanding requests on the TCB. + if (ClosedTCB->tcb_connreq != NULL) { + TCPConnReq *ConnReq = ClosedTCB->tcb_connreq; + CTEStructAssert(ConnReq, tcr); + + (*ConnReq->tcr_req.tr_rtn)(ConnReq->tcr_req.tr_context, Status, 0); + FreeConnReq(ConnReq); + } + + if (ClosedTCB->tcb_discwait != NULL) { + TCPConnReq *ConnReq = ClosedTCB->tcb_discwait; + CTEStructAssert(ConnReq, tcr); + + (*ConnReq->tcr_req.tr_rtn)(ConnReq->tcr_req.tr_context, Status, 0); + FreeConnReq(ConnReq); + } + + while (!EMPTYQ(&ClosedTCB->tcb_sendq)) { + TCPReq *Req; + TCPSendReq *SendReq; + long Result; + + DEQUEUE(&ClosedTCB->tcb_sendq, Req, TCPReq, tr_q); + + CTEStructAssert(Req, tr); + SendReq = (TCPSendReq *)Req; + CTEStructAssert(SendReq, tsr); + + // Decrement the initial reference put on the buffer when it was + // allocated. This reference would have been decremented if the + // send had been acknowledged, but then the send would not still + // be on the tcb_sendq. + Result = CTEInterlockedDecrementLong( + &(SendReq->tsr_refcnt) + ); + + CTEAssert(Result >= 0); + + if (Result <= 0) { + // If we've sent directly from this send, NULL out the next + // pointer for the last buffer in the chain. + if (SendReq->tsr_lastbuf != NULL) { + NDIS_BUFFER_LINKAGE(SendReq->tsr_lastbuf) = NULL; + SendReq->tsr_lastbuf = NULL; + } + + (*Req->tr_rtn)(Req->tr_context, Status, 0); + FreeSendReq(SendReq); + } else { + // The send request will be freed when all outstanding references + // to it have completed. + SendReq->tsr_req.tr_status = Status; + } + } + + while (ClosedTCB->tcb_rcvhead != NULL) { + TCPRcvReq *RcvReq; + + RcvReq = ClosedTCB->tcb_rcvhead; + CTEStructAssert(RcvReq, trr); + ClosedTCB->tcb_rcvhead = RcvReq->trr_next; + (*RcvReq->trr_rtn)(RcvReq->trr_context, Status, 0); + FreeRcvReq(RcvReq); + } + + while (ClosedTCB->tcb_exprcv != NULL) { + TCPRcvReq *RcvReq; + + RcvReq = ClosedTCB->tcb_exprcv; + CTEStructAssert(RcvReq, trr); + ClosedTCB->tcb_exprcv = RcvReq->trr_next; + (*RcvReq->trr_rtn)(RcvReq->trr_context, Status, 0); + FreeRcvReq(RcvReq); + } + + if (ClosedTCB->tcb_pendhead != NULL) + FreeRBChain(ClosedTCB->tcb_pendhead); + + if (ClosedTCB->tcb_urgpending != NULL) + FreeRBChain(ClosedTCB->tcb_urgpending); + + while (ClosedTCB->tcb_raq != NULL) { + TCPRAHdr *Hdr; + + Hdr = ClosedTCB->tcb_raq; + CTEStructAssert(Hdr, trh); + ClosedTCB->tcb_raq = Hdr->trh_next; + if (Hdr->trh_buffer != NULL) + FreeRBChain(Hdr->trh_buffer); + + CTEFreeMem(Hdr); + } + + RemoveConnFromTCB(ClosedTCB); + + if (OKToFree) { + FreeTCB(ClosedTCB); + } else { + CTEGetLock(&TCBTableLock, &TCBTableHandle); + ClosedTCB->tcb_walkcount--; + if (ClosedTCB->tcb_walkcount == 0) { + FreeTCB(ClosedTCB); + } + CTEFreeLock(&TCBTableLock, TCBTableHandle); + } + +} + +//* TryToCloseTCB - Try to close a TCB. +// +// Called when we need to close a TCB, but don't know if we can. If +// the reference count is 0, we'll call CloseTCB to deal with it. +// Otherwise we'll set the DELETE_PENDING bit and deal with it when +// the ref. count goes to 0. We assume the TCB is locked when we are called. +// +// Input: ClosedTCB - TCB to be closed. +// Reason - Reason we're closing. +// Handle - Lock handle for TCB. +// +// Returns: Nothing. +// +void +TryToCloseTCB(TCB *ClosedTCB, uchar Reason, CTELockHandle Handle) +{ + CTEStructAssert(ClosedTCB, tcb); + CTEAssert(ClosedTCB->tcb_state != TCB_CLOSED); + + ClosedTCB->tcb_closereason |= Reason; + + if (ClosedTCB->tcb_pending & DEL_PENDING) { + DEBUGCHK; + CTEFreeLock(&ClosedTCB->tcb_lock, Handle); + return; + } + + ClosedTCB->tcb_pending |= DEL_PENDING; + ClosedTCB->tcb_slowcount++; + ClosedTCB->tcb_fastchk |= TCP_FLAG_SLOW; + + if (ClosedTCB->tcb_refcnt == 0) + CloseTCB(ClosedTCB, Handle); + else { + CTEFreeLock(&ClosedTCB->tcb_lock, Handle); + } +} + + +//* DerefTCB - Dereference a TCB. +// +// Called when we're done with a TCB, and want to let exclusive user +// have a shot. We dec. the refcount, and if it goes to zero and there +// are pending actions, we'll perform one of the pending actions. +// +// Input: DoneTCB - TCB to be dereffed. +// Handle - Lock handle to be used when freeing TCB lock. +// +// Returns: Nothing. +// +void +DerefTCB(TCB *DoneTCB, CTELockHandle Handle) +{ + + CTEAssert(DoneTCB->tcb_refcnt != 0); + if (--DoneTCB->tcb_refcnt == 0) { + if (DoneTCB->tcb_pending == 0) { + CTEFreeLock(&DoneTCB->tcb_lock, Handle); + return; + } else { + // BUGBUG handle pending actions. + if (DoneTCB->tcb_pending & DEL_PENDING) + CloseTCB(DoneTCB, Handle); + else + DEBUGCHK; + return; + } + } + + CTEFreeLock(&DoneTCB->tcb_lock, Handle); + return; +} + + +//** TdiOpenConnection - Open a connection. +// +// This is the TDI Open Connection entry point. We open a connection, +// and save the caller's connection context. A TCPConn structure is allocated +// here, but a TCB isn't allocated until the Connect or Listen is done. +// +// Input: Request - Pointed to a TDI request structure. +// Context - Connection context to be saved for connection. +// +// Returns: Status of attempt to open the connection. +// +TDI_STATUS +TdiOpenConnection(PTDI_REQUEST Request, PVOID Context) +{ + TCPConn *NewConn; // The newly opened connection. + CTELockHandle Handle; // Lock handle for TCPConnTable. + uint ConnID; // New ConnID. + TDI_STATUS Status; // Status of this request. + + NewConn = CTEAllocMem(sizeof(TCPConn)); + + if (NewConn != NULL) { // We allocated a connection. + CTEMemSet(NewConn, 0, sizeof(TCPConn)); +#ifdef DEBUG + NewConn->tc_sig = tc_signature; +#endif + NewConn->tc_tcb = NULL; + NewConn->tc_ao = NULL; + NewConn->tc_context = Context; + + CTEGetLock(&ConnTableLock, &Handle); + ConnID = GetConnID(NewConn); + if (ConnID != INVALID_CONN_ID) { + // We successfully got a ConnID. + Request->Handle.ConnectionContext = (CONNECTION_CONTEXT)ConnID; + NewConn->tc_refcnt = 0; + NewConn->tc_flags = 0; + NewConn->tc_tcbflags = NAGLING | (BSDUrgent ? BSD_URGENT : 0); + if (DefaultRcvWin != 0) { + NewConn->tc_window = DefaultRcvWin; + NewConn->tc_flags |= CONN_WINSET; + } else + NewConn->tc_window = DEFAULT_RCV_WIN; + + NewConn->tc_donertn = DummyDone; + Status = TDI_SUCCESS; + } else { + CTEFreeMem(NewConn); + Status = TDI_NO_RESOURCES; + } + + CTEFreeLock(&ConnTableLock, Handle); + return Status; + } + + // Couldn't get a connection. + return TDI_NO_RESOURCES; + +} + +//* RemoveConnFromAO - Remove a connection from an AddrObj. +// +// A little utility routine to remove a connection from an AddrObj. +// We run down the connections on the AO, and when we find him we splice +// him out. We assume the caller holds the locks on the AddrObj and the +// TCPConnTable lock. +// +// Input: AO - AddrObj to remove from. +// Conn - Conn to remove. +// +// Returns: Nothing. +// +void +RemoveConnFromAO(AddrObj *AO, TCPConn *Conn) +{ + + CTEStructAssert(AO, ao); + CTEStructAssert(Conn, tc); + + REMOVEQ(&Conn->tc_q); + Conn->tc_ao = NULL; + + +} + +//* TdiCloseConnection - Close a connection. +// +// Called when the user is done with a connection, and wants to close it. +// We look the connection up in our table, and if we find it we'll remove +// the connection from the AddrObj it's associate with (if any). If there's +// a TCB associated with the connection we'll close it also. +// +// There are some interesting wrinkles related to closing while a TCB +// is still referencing the connection (i.e. tc_refcnt != 0) or while a +// disassociate address is in progress. See below for more details. +// +// Input: Request - Request identifying connection to be closed. +// +// Returns: Status of attempt to close. +// +TDI_STATUS +TdiCloseConnection(PTDI_REQUEST Request) +{ + uint ConnID = (uint)Request->Handle.ConnectionContext; + CTELockHandle TableHandle; + TCPConn *Conn; + TDI_STATUS Status; + + CTEGetLock(&ConnTableLock, &TableHandle); + + // We have the locks we need. Try to find a connection. + Conn = GetConnFromConnID(ConnID); + + if (Conn != NULL) { + CTELockHandle TCBHandle; + TCB *ConnTCB; + + // We found the connection. Free the ConnID and mark the connection + // as closing. + + CTEStructAssert(Conn, tc); + + FreeConnID(ConnID); + + Conn->tc_flags |= CONN_CLOSING; + + // See if there's a TCB referencing this connection. + // If there is, we'll need to wait until he's done before closing him. + // We'll hurry the process along if we still have a pointer to him. + + if (Conn->tc_refcnt != 0) { + CTEReqCmpltRtn Rtn; + PVOID Context; + + // A connection still references him. Save the current rtn stuff + // in case we are in the middle of disassociating him from an + // address, and store the caller's callback routine and our done + // routine. + Rtn = Conn->tc_rtn; + Context = Conn->tc_rtncontext; + + Conn->tc_rtn = Request->RequestNotifyObject; + Conn->tc_rtncontext = Request->RequestContext; + Conn->tc_donertn = CloseDone; + + // See if we're in the middle of disassociating him + if (Conn->tc_flags & CONN_DISACC) { + + // We are disassociating him. We'll free the conn table lock + // now and fail the disassociate request. Note that when + // we free the lock the refcount could go to zero. This is + // OK, because we've already stored the neccessary info. in + // the connection so the caller will get called back if it + // does. From this point out we return PENDING, so a callback + // is OK. We've marked him as closing, so the disassoc done + // routine will bail out if we've interrupted him. If the ref. + // count does go to zero, Conn->tc_tcb would have to be NULL, + // so in that case we'll just fall out of this routine. + + CTEFreeLock(&ConnTableLock, TableHandle); + (*Rtn)(Context, (uint) TDI_REQ_ABORTED, 0); + CTEGetLock(&ConnTableLock, &TableHandle); + } + + + ConnTCB = Conn->tc_tcb; + if (ConnTCB != NULL) { + CTEStructAssert(ConnTCB, tcb); + // We have a TCB. Take the lock on him and get ready to + // close him. + CTEGetLock(&ConnTCB->tcb_lock, &TCBHandle); + if (ConnTCB->tcb_state != TCB_CLOSED) { + ConnTCB->tcb_flags |= NEED_RST; + CTEFreeLock(&ConnTableLock, TCBHandle); + if (!CLOSING(ConnTCB)) + TryToCloseTCB(ConnTCB, TCB_CLOSE_ABORTED, TableHandle); + else + CTEFreeLock(&ConnTCB->tcb_lock, TableHandle); + return TDI_PENDING; + } else { + // He's already closing. This should be harmless, but check + // this case. + DEBUGCHK; + CTEFreeLock(&ConnTCB->tcb_lock, TCBHandle); + } + } + Status = TDI_PENDING; + + } else { + // We have a connection that we can close. Finish the close. + Conn->tc_rtn = DummyCmplt; + CloseDone(Conn, TableHandle); + return TDI_SUCCESS; + } + + + } else + Status = TDI_INVALID_CONNECTION; + + // We're done with the connection. Go ahead and free him. + CTEFreeLock(&ConnTableLock, TableHandle); + + return Status; + +} + + +//* TdiAssociateAddress - Associate an address with a connection. +// +// Called to associate an address with a connection. We do a minimal +// amount of sanity checking, and then put the connection on the AddrObj's +// list. +// +// Input: Request - Pointer to request structure for this request. +// AddrHandle - Address handle to associate connection with. +// +// Returns: Status of attempt to associate. +// +TDI_STATUS +TdiAssociateAddress(PTDI_REQUEST Request, HANDLE AddrHandle) +{ + CTELockHandle TableHandle, AOHandle; + AddrObj *AO; + uint ConnID = (uint)Request->Handle.ConnectionContext; + TCPConn *Conn; + TDI_STATUS Status; + +#ifdef VXD + AO = GetIndexedAO((uint)AddrHandle); + if (AO == NULL) + return TDI_ADDR_INVALID; +#else + AO = (AddrObj *)AddrHandle; +#endif + CTEStructAssert(AO, ao); + + CTEGetLock(&ConnTableLock, &TableHandle); + CTEGetLock(&AO->ao_lock, &AOHandle); + + Conn = GetConnFromConnID(ConnID); + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + if (Conn->tc_ao != NULL) { + // It's already associated. Error out. + DEBUGCHK; + Status = TDI_ALREADY_ASSOCIATED; + } else { + Conn->tc_ao = AO; + CTEAssert(Conn->tc_tcb == NULL); + ENQUEUE(&AO->ao_idleq, &Conn->tc_q); + Status = TDI_SUCCESS; + } + } else + Status = TDI_INVALID_CONNECTION; + + CTEFreeLock(&AO->ao_lock, AOHandle); + CTEFreeLock(&ConnTableLock, TableHandle); + return Status; +} + +//* TdiDisAssociateAddress - Disassociate a connection from an address. +// +// The TDI entry point to disassociate a connection from an address. The +// connection must actually be associated and not connected to anything. +// +// Input: Request - Pointer to the request structure for this +// command. +// +// Returns: Status of request. +// +TDI_STATUS +TdiDisAssociateAddress(PTDI_REQUEST Request) +{ + uint ConnID = (uint)Request->Handle.ConnectionContext; + CTELockHandle AOTableHandle, ConnTableHandle, AOHandle; + TCPConn *Conn; + AddrObj *AO; + TDI_STATUS Status; + + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + CTEGetLock(&ConnTableLock, &ConnTableHandle); + Conn = GetConnFromConnID(ConnID); + + if (Conn != NULL) { + // The connection actually exists! + + CTEStructAssert(Conn, tc); + AO = Conn->tc_ao; + if (AO != NULL) { + CTEStructAssert(AO, ao); + // And it's associated. + CTEGetLock(&AO->ao_lock, &AOHandle); + // If there's no connection currently active, go ahead and remove + // him from the AddrObj. If a connection is active error the + // request out. + if (Conn->tc_tcb == NULL) { + if (Conn->tc_refcnt == 0) { + RemoveConnFromAO(AO, Conn); + Status = TDI_SUCCESS; + } else { + // He shouldn't be closing, or we couldn't have found him. + CTEAssert(!(Conn->tc_flags & CONN_CLOSING)); + + Conn->tc_rtn = Request->RequestNotifyObject; + Conn->tc_rtncontext = Request->RequestContext; + Conn->tc_donertn = DisassocDone; + Conn->tc_flags |= CONN_DISACC; + Status = TDI_PENDING; + } + + } else + Status = TDI_CONNECTION_ACTIVE; + CTEFreeLock(&AO->ao_lock, AOHandle); + } else + Status = TDI_NOT_ASSOCIATED; + } else + Status = TDI_INVALID_CONNECTION; + + CTEFreeLock(&ConnTableLock, ConnTableHandle); + CTEFreeLock(&AddrObjTableLock, AOTableHandle); + + return Status; + +} + +//* ProcessUserOptions - Process options from the user. +// +// A utility routine to process options from the user. We fill in the +// optinfo structure, and if we have options we call ip to check on them. +// +// Input: Info - Info structure containing options to be processed. +// OptInfo - Info structure to be filled in. +// +// Returns: TDI_STATUS of attempt. +// +TDI_STATUS +ProcessUserOptions(PTDI_CONNECTION_INFORMATION Info, IPOptInfo *OptInfo) +{ + TDI_STATUS Status; + + (*LocalNetInfo.ipi_initopts)(OptInfo); + + if (Info != NULL && Info->Options != NULL) { + IP_STATUS OptStatus; + + OptStatus = (*LocalNetInfo.ipi_copyopts)(Info->Options, + Info->OptionsLength, OptInfo); + if (OptStatus != IP_SUCCESS) { + if (OptStatus == IP_NO_RESOURCES) + Status = TDI_NO_RESOURCES; + else + Status = TDI_BAD_OPTION; + } else + Status = TDI_SUCCESS; + } else { + Status = TDI_SUCCESS; + } + + return Status; + + +} + +//* InitTCBFromConn - Initialize a TCB from information in a Connection. +// +// Called from Connect and Listen processing to initialize a new TCB from +// information in the connection. We assume the AddrObjTableLock and +// ConnTableLocks are held when we are called, or that the caller has some +// other way of making sure that the referenced AO doesn't go away in the middle +// of operation. +// +// Input: Conn - Connection to initialize from. +// NewTCB - TCB to be initialized. +// Addr - Remote addressing and option info for NewTCB. +// AOLocked - True if the called has the address object locked. +// +// Returns: TDI_STATUS of init attempt. +// +TDI_STATUS +InitTCBFromConn(TCPConn *Conn, TCB *NewTCB, + PTDI_CONNECTION_INFORMATION Addr, uint AOLocked) +{ + CTELockHandle AOHandle; + TDI_STATUS Status; + + CTEStructAssert(Conn, tc); + + // We have a connection. Make sure it's associated with an address and + // doesn't already have a TCB attached. + + if (Conn->tc_flags & CONN_INVALID) + return TDI_INVALID_CONNECTION; + + if (Conn->tc_tcb == NULL) { + AddrObj *ConnAO; + + ConnAO = Conn->tc_ao; + if (ConnAO != NULL) { + CTEStructAssert(ConnAO, ao); + + if (!AOLocked) { + CTEGetLock(&ConnAO->ao_lock, &AOHandle); + } + NewTCB->tcb_saddr = ConnAO->ao_addr; + NewTCB->tcb_sport = ConnAO->ao_port; + NewTCB->tcb_rcvind = ConnAO->ao_rcv; + NewTCB->tcb_ricontext = ConnAO->ao_rcvcontext; + if (NewTCB->tcb_rcvind == NULL) + NewTCB->tcb_rcvhndlr = PendData; + else + NewTCB->tcb_rcvhndlr = IndicateData; + + NewTCB->tcb_conncontext = Conn->tc_context; + NewTCB->tcb_flags |= Conn->tc_tcbflags; + NewTCB->tcb_defaultwin = Conn->tc_window; + NewTCB->tcb_rcvwin = Conn->tc_window; + + if (Conn->tc_flags & CONN_WINSET) + NewTCB->tcb_flags |= WINDOW_SET; + + if (NewTCB->tcb_flags & KEEPALIVE) { + NewTCB->tcb_alive = TCPTime; + NewTCB->tcb_kacount = 0; + } + + if (!AOLocked) { + CTEFreeLock(&ConnAO->ao_lock, AOHandle); + } + + // If we've been given options, we need to process them now. + if (Addr != NULL && Addr->Options != NULL) + NewTCB->tcb_flags |= CLIENT_OPTIONS; + Status = ProcessUserOptions(Addr, &NewTCB->tcb_opt); + + return Status; + } else + return TDI_NOT_ASSOCIATED; + } else + return TDI_CONNECTION_ACTIVE; + +} + +//* TdiConnect - Establish a connection. +// +// The TDI connection establishment routine. Called when the client wants to +// establish a connection, we validate his incoming parameters and kick +// things off by sending a SYN. +// +// Input: Request - The request structure for this command. +// Timeout - How long to wait for the request. The format +// of this time is system specific - we use +// a macro to convert to ticks. +// RequestAddr - Pointer to a TDI_CONNECTION_INFORMATION +// structure describing the destination. +// ReturnAddr - Pointer to where to return information. +// +// Returns: Status of attempt to connect. +// +TDI_STATUS +TdiConnect(PTDI_REQUEST Request, void *TO, + PTDI_CONNECTION_INFORMATION RequestAddr, + PTDI_CONNECTION_INFORMATION ReturnAddr) +{ + TCPConnReq *ConnReq; // Connection request to use. + IPAddr DestAddr; + ushort DestPort; + uchar AddrType; + TCPConn *Conn; + TCB *NewTCB; + uint ConnID = (uint)Request->Handle.ConnectionContext; + CTELockHandle AOTableHandle, ConnTableHandle, AOHandle; + AddrObj *AO; + TDI_STATUS Status; + CTELockHandle TCBHandle; + IPAddr SrcAddr; + ushort MSS; + TCP_TIME *Timeout; + + // First, get and validate the remote address. + if (RequestAddr == NULL || RequestAddr->RemoteAddress == NULL || + !GetAddress((PTRANSPORT_ADDRESS)RequestAddr->RemoteAddress, &DestAddr, + &DestPort)) + return TDI_BAD_ADDR; + + AddrType = (*LocalNetInfo.ipi_getaddrtype)(DestAddr); + + if (AddrType == DEST_INVALID || IS_BCAST_DEST(AddrType) || DestPort == 0) + return TDI_BAD_ADDR; + + // Now get a connection request. If we can't, bail out now. + ConnReq = GetConnReq(); + if (ConnReq == NULL) + return TDI_NO_RESOURCES; + + // Get a TCB, assuming we'll need one. + NewTCB = AllocTCB(); + if (NewTCB == NULL) { + // Couldn't get a TCB. + FreeConnReq(ConnReq); + return TDI_NO_RESOURCES; + } + + Timeout = (TCP_TIME *)TO; + + if (Timeout != NULL && !INFINITE_CONN_TO(*Timeout)) { + ulong Ticks = TCP_TIME_TO_TICKS(*Timeout); + if (Ticks > MAX_CONN_TO_TICKS) + Ticks = MAX_CONN_TO_TICKS; + else + Ticks++; + ConnReq->tcr_timeout = (ushort)Ticks; + } else + ConnReq->tcr_timeout = 0; + + ConnReq->tcr_flags = 0; + ConnReq->tcr_conninfo = ReturnAddr; + ConnReq->tcr_req.tr_rtn = Request->RequestNotifyObject; + ConnReq->tcr_req.tr_context = Request->RequestContext; + NewTCB->tcb_daddr = DestAddr; + NewTCB->tcb_dport = DestPort; + + // Now find the real connection. If we find it, we'll make sure it's + // associated. + CTEGetLock(&AddrObjTableLock, &AOTableHandle); + CTEGetLock(&ConnTableLock, &ConnTableHandle); + Conn = GetConnFromConnID(ConnID); + if (Conn != NULL) { + uint Inserted; + + CTEStructAssert(Conn, tc); + + AO = Conn->tc_ao; + + if (AO != NULL) { + CTEGetLock(&AO->ao_lock, &AOHandle); + + CTEStructAssert(AO, ao); + Status = InitTCBFromConn(Conn, NewTCB, RequestAddr, TRUE); + if (Status == TDI_SUCCESS) { + + // We've processed the options, and we know the destination + // address is good, and we have all the resources we need, + // so we can go ahead and open an RCE. If this works we'll + // put the TCB into the Connection and send a SYN. + + // We're done with the AddrObjTable now, so we can free it's + // lock. + NewTCB->tcb_flags |= ACTIVE_OPEN; + + CTEFreeLock(&AddrObjTableLock, AOHandle); + + SrcAddr = (*LocalNetInfo.ipi_openrce)(DestAddr, + NewTCB->tcb_saddr, &NewTCB->tcb_rce, &AddrType, &MSS, + &NewTCB->tcb_opt); + + if (IP_ADDR_EQUAL(SrcAddr, NULL_IP_ADDR)) { + // The request failed. We know the destination is good + // (we verified it above), so it must be unreachable. + CTEFreeLock(&AO->ao_lock, ConnTableHandle); + Status = TDI_DEST_UNREACHABLE; + goto error; + } + + // OK, the RCE open worked. Enter the TCB into the connection. + CTEGetLock(&NewTCB->tcb_lock, &TCBHandle); + Conn->tc_tcb = NewTCB; + Conn->tc_refcnt++; + NewTCB->tcb_conn = Conn; + REMOVEQ(&Conn->tc_q); + ENQUEUE(&AO->ao_activeq, &Conn->tc_q); + CTEFreeLock(&ConnTableLock, TCBHandle); + CTEFreeLock(&AO->ao_lock, ConnTableHandle); + + + // If the caller didn't specify a local address, use what + // IP provided. + if (IP_ADDR_EQUAL(NewTCB->tcb_saddr, NULL_IP_ADDR)) + NewTCB->tcb_saddr = SrcAddr; + + // Until we have MTU discovery in place, hold the MSS down + // to 536 if we're going off net. + MSS -= sizeof(TCPHeader); + + if (!PMTUDiscovery && IS_OFFNET_DEST(AddrType)) { + NewTCB->tcb_mss = MIN(MSS, MAX_REMOTE_MSS) - + NewTCB->tcb_opt.ioi_optlength; + + CTEAssert(NewTCB->tcb_mss > 0); + } + else { + if (PMTUDiscovery) + NewTCB->tcb_opt.ioi_flags = IP_FLAG_DF; + NewTCB->tcb_mss = MSS - NewTCB->tcb_opt.ioi_optlength; + + CTEAssert(NewTCB->tcb_mss > 0); + } + + // + // Initialize the remote mss in case we receive an MTU change + // from IP before the remote SYN arrives. The remmms will + // be replaced when the remote SYN is processed. + // + NewTCB->tcb_remmss = NewTCB->tcb_mss; + + // Now initialize our send state. + InitSendState(NewTCB); + NewTCB->tcb_refcnt = 1; + NewTCB->tcb_state = TCB_SYN_SENT; + TStats.ts_activeopens++; + + // Need to put the ConnReq on the TCB now, in case the timer fires + // after we've inserted. + + NewTCB->tcb_connreq = ConnReq; + CTEFreeLock(&NewTCB->tcb_lock, AOTableHandle); + + Inserted = InsertTCB(NewTCB); + CTEGetLock(&NewTCB->tcb_lock, &TCBHandle); + + if (!Inserted) { + // Insert failed. We must already have a connection. Pull + // the connreq from the TCB first, so we can return the correct + // error code for it. + NewTCB->tcb_connreq = NULL; + NewTCB->tcb_refcnt--; + TryToCloseTCB(NewTCB, TCB_CLOSE_ABORTED, TCBHandle); + FreeConnReq(ConnReq); + return TDI_ADDR_IN_USE; + } + + // If it's closing somehow, stop now. It can't have gone to + // closed, as we hold a reference on it. It could have gone + // to some other state (for example SYN-RCVD) so we need to + // check that now too. + if (!CLOSING(NewTCB) && NewTCB->tcb_state == TCB_SYN_SENT) { + SendSYN(NewTCB, TCBHandle); + CTEGetLock(&NewTCB->tcb_lock, &TCBHandle); + } + DerefTCB(NewTCB, TCBHandle); + + return TDI_PENDING; + } else + CTEFreeLock(&AO->ao_lock, AOHandle); + + } else + Status = TDI_NOT_ASSOCIATED; + } else + Status = TDI_INVALID_CONNECTION; + + CTEFreeLock(&AddrObjTableLock, ConnTableHandle); +error: + CTEFreeLock(&ConnTableLock, AOTableHandle); + FreeConnReq(ConnReq); + FreeTCB(NewTCB); + return Status; + + +} + +//* TdiListen - Listen for a connection. +// +// The TDI listen handling routine. Called when the client wants to +// post a listen, we validate his incoming parameters, allocate a TCB +// and return. +// +// Input: Request - The request structure for this command. +// Flags - Listen flags for the listen. +// AcceptableAddr - Pointer to a TDI_CONNECTION_INFORMATION +// structure describing acceptable remote +// addresses. +// ConnectedAddr - Pointer to where to return information +// about the address we connected to. +// +// Returns: Status of attempt to connect. +// +TDI_STATUS +TdiListen(PTDI_REQUEST Request, ushort Flags, + PTDI_CONNECTION_INFORMATION AcceptableAddr, + PTDI_CONNECTION_INFORMATION ConnectedAddr) +{ + TCPConnReq *ConnReq; // Connection request to use. + IPAddr RemoteAddr; // Remote address to take conn. from. + ushort RemotePort; // Acceptable remote port. + uchar AddrType; // Type of remote address. + TCPConn *Conn; // Pointer to the Connection being + // listened upon. + TCB *NewTCB; // Pointer to the new TCB we'll use. + uint ConnID = (uint)Request->Handle.ConnectionContext; + CTELockHandle AOTableHandle, ConnTableHandle; + TDI_STATUS Status; + + // If we've been given remote addressing criteria, check it out. + if (AcceptableAddr != NULL && AcceptableAddr->RemoteAddress != NULL) { + if (!GetAddress((PTRANSPORT_ADDRESS)AcceptableAddr->RemoteAddress, + &RemoteAddr, &RemotePort)) + return TDI_BAD_ADDR; + + if (!IP_ADDR_EQUAL(RemoteAddr, NULL_IP_ADDR)) { + AddrType = (*LocalNetInfo.ipi_getaddrtype)(RemoteAddr); + + if (AddrType == DEST_INVALID || IS_BCAST_DEST(AddrType)) + return TDI_BAD_ADDR; + } + } else { + RemoteAddr = NULL_IP_ADDR; + RemotePort = 0; + } + + // The remote address is valid. Get a ConnReq, and maybe a TCB. + ConnReq = GetConnReq(); + if (ConnReq == NULL) + return TDI_NO_RESOURCES; // Couldn't get one. + + // Now try to get a TCB. + NewTCB = AllocTCB(); + if (NewTCB == NULL) { + // Couldn't get a TCB. Return an error. + FreeConnReq(ConnReq); + return TDI_NO_RESOURCES; + } + + // We have the resources we need. Initialize them, and then check the + // state of the connection. + ConnReq->tcr_flags = Flags; + ConnReq->tcr_conninfo = ConnectedAddr; + ConnReq->tcr_req.tr_rtn = Request->RequestNotifyObject; + ConnReq->tcr_req.tr_context = Request->RequestContext; + NewTCB->tcb_connreq = ConnReq; + NewTCB->tcb_daddr = RemoteAddr; + NewTCB->tcb_dport = RemotePort; + NewTCB->tcb_state = TCB_LISTEN; + + // Now find the real connection. If we find it, we'll make sure it's + // associated. + CTEGetLock(&ConnTableLock, &ConnTableHandle); + Conn = GetConnFromConnID(ConnID); + if (Conn != NULL) { + CTELockHandle AddrHandle; + AddrObj *ConnAO; + + CTEStructAssert(Conn, tc); + // We have a connection. Make sure it's associated with an address and + // doesn't already have a TCB attached. + ConnAO = Conn->tc_ao; + + if (ConnAO != NULL) { + CTEStructAssert(ConnAO, ao); + CTEGetLockAtDPC(&ConnAO->ao_lock, &AddrHandle); + + Status = InitTCBFromConn(Conn, NewTCB, AcceptableAddr, TRUE); + + if (Status == TDI_SUCCESS) { + + // The initialization worked. Assign the new TCB to the connection, + // and return. + + REMOVEQ(&Conn->tc_q); + ENQUEUE(&ConnAO->ao_listenq, &Conn->tc_q); + + Conn->tc_tcb = NewTCB; + NewTCB->tcb_conn = Conn; + Conn->tc_refcnt++; + + ConnAO->ao_listencnt++; + CTEFreeLockFromDPC(&ConnAO->ao_lock, AddrHandle); + + Status = TDI_PENDING; + } else { + FreeTCB(NewTCB); + CTEFreeLockFromDPC(&ConnAO->ao_lock, AddrHandle); + } + } else { + FreeTCB(NewTCB); + Status = TDI_NOT_ASSOCIATED; + } + } else { + FreeTCB(NewTCB); + Status = TDI_INVALID_CONNECTION; + } + + // We're all done. Free the locks and get out. + CTEFreeLock(&ConnTableLock, ConnTableHandle); + return Status; + +} + +//* InitRCE - Initialize an RCE. +// +// A utility routine to open and RCE and determine the maximum segment size +// for a connections. This function is called with the TCB lock held +// when transitioning out of the SYN_SENT or LISTEN states. +// +// Input: NewTCB - TCB for which an RCE is to be opened. +// +// Returns: Nothing. +// +void +InitRCE(TCB *NewTCB) +{ + uchar DType; + ushort MSS; + + // Open an RCE for this connection. + (*LocalNetInfo.ipi_openrce)(NewTCB->tcb_daddr, NewTCB->tcb_saddr, + &NewTCB->tcb_rce, &DType, &MSS, &NewTCB->tcb_opt); + + // Until we have Dynamic MTU discovery in place, force MTU down. + MSS -= sizeof(TCPHeader); + if (!PMTUDiscovery && (DType & DEST_OFFNET_BIT)) { + NewTCB->tcb_mss = MIN(NewTCB->tcb_remmss, MIN(MSS, MAX_REMOTE_MSS) + - NewTCB->tcb_opt.ioi_optlength); + + CTEAssert(NewTCB->tcb_mss > 0); + } + else { + if (PMTUDiscovery) + NewTCB->tcb_opt.ioi_flags = IP_FLAG_DF; + MSS -= NewTCB->tcb_opt.ioi_optlength; + NewTCB->tcb_mss = MIN(NewTCB->tcb_remmss, MSS); + + CTEAssert(NewTCB->tcb_mss > 0); + + } + + +} + +//* AcceptConn - Accept a connection on a TCB. +// +// Called to accept a connection on a TCB, either from an incoming +// receive segment or via a user's accept. We initialize the RCE +// and the send state, and send out a SYN. We assume the TCB is locked +// and referenced when we get it. +// +// Input: AcceptTCB - TCB to accept on. +// Handle - Lock handle for TCB. +// +// Returns: Nothing. +// +void +AcceptConn(TCB *AcceptTCB, CTELockHandle Handle) +{ + CTEStructAssert(AcceptTCB, tcb); + CTEAssert(AcceptTCB->tcb_refcnt != 0); + + InitRCE(AcceptTCB); + InitSendState(AcceptTCB); + + AdjustRcvWin(AcceptTCB); + SendSYN(AcceptTCB, Handle); + + CTEGetLock(&AcceptTCB->tcb_lock, &Handle); + + DerefTCB(AcceptTCB, Handle); +} + +//* TdiAccept - Accept a connection. +// +// The TDI accept routine. Called when the client wants to +// accept a connection for which a listen had previously completed. We +// examine the state of the connection - it has to be in SYN-RCVD, with +// a TCB, with no pending connreq, etc. +// +// Input: Request - The request structure for this command. +// AcceptInfo - Pointer to a TDI_CONNECTION_INFORMATION +// structure describing option information +// for this accept. +// ConnectedIndo - Pointer to where to return information +// about the address we connected to. +// +// Returns: Status of attempt to connect. +// +TDI_STATUS +TdiAccept(PTDI_REQUEST Request, PTDI_CONNECTION_INFORMATION AcceptInfo, + PTDI_CONNECTION_INFORMATION ConnectedInfo) +{ + TCPConnReq *ConnReq; // ConnReq we'll use for this connection. + uint ConnID = (uint)Request->Handle.ConnectionContext; + TCPConn *Conn; // Connection being accepted upon. + TCB *AcceptTCB; // TCB for Conn. + CTELockHandle ConnTableHandle; // Lock handle for connection table. + CTELockHandle TCBHandle; // Lock handle for TCB. + TDI_STATUS Status; + + // First, get the ConnReq we'll need. + ConnReq = GetConnReq(); + if (ConnReq == NULL) + return TDI_NO_RESOURCES; + + ConnReq->tcr_conninfo = ConnectedInfo; + ConnReq->tcr_req.tr_rtn = Request->RequestNotifyObject; + ConnReq->tcr_req.tr_context = Request->RequestContext; + + // Now look up the connection. + CTEGetLock(&ConnTableLock, &ConnTableHandle); + Conn = GetConnFromConnID(ConnID); + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + // We have the connection. Make sure is has a TCB, and that the + // TCB is in the SYN-RCVD state, etc. + AcceptTCB = Conn->tc_tcb; + + if (AcceptTCB != NULL) { + CTEStructAssert(AcceptTCB, tcb); + + CTEGetLock(&AcceptTCB->tcb_lock, &TCBHandle); + CTEFreeLock(&ConnTableLock, TCBHandle); + + if (!CLOSING(AcceptTCB) && AcceptTCB->tcb_state == TCB_SYN_RCVD) { + // State is valid. Make sure this TCB had a delayed accept on + // it, and that there is currently no connect request pending. + if (!(AcceptTCB->tcb_flags & CONN_ACCEPTED) && + AcceptTCB->tcb_connreq == NULL) { + + // If the caller gave us options, they'll override any + // that are already present, if they're valid. + if (AcceptInfo != NULL && AcceptInfo->Options != NULL) { + IPOptInfo TempOptInfo; + + // We have options. Copy them to make sure they're valid. + Status = ProcessUserOptions(AcceptInfo, &TempOptInfo); + if (Status == TDI_SUCCESS) { + (*LocalNetInfo.ipi_freeopts)(&AcceptTCB->tcb_opt); + AcceptTCB->tcb_opt = TempOptInfo; + AcceptTCB->tcb_flags |= CLIENT_OPTIONS; + } else + goto connerror; + } + + AcceptTCB->tcb_connreq = ConnReq; + AcceptTCB->tcb_flags |= CONN_ACCEPTED; + AcceptTCB->tcb_refcnt++; + // Everything's set. Accept the connection now. + AcceptConn(AcceptTCB, ConnTableHandle); + return TDI_PENDING; + } + } +connerror: + CTEFreeLock(&AcceptTCB->tcb_lock, ConnTableHandle); + Status = TDI_INVALID_CONNECTION; + goto error; + } + } + Status = TDI_INVALID_CONNECTION; + CTEFreeLock(&ConnTableLock, ConnTableHandle); + +error: + FreeConnReq(ConnReq); + return Status; + +} + +//* TdiDisConnect - Disconnect a connection. +// +// The TDI disconnection routine. Called when the client wants to disconnect +// a connection. There are two types of disconnection we support, graceful +// and abortive. A graceful close will cause us to send a FIN and not complete +// the request until we get the ACK back. An abortive close causes us to send +// a RST. In that case we'll just get things going and return immediately. +// +// Input: Request - The request structure for this command. +// Timeout - How long to wait for the request. The format +// of this time is system specific - we use +// a macro to convert to ticks. +// Flags - Flags indicating type of disconnect. +// DiscConnInfo - Pointer to a TDI_CONNECTION_INFORMATION +// structure giving disconnection info. Ignored +// for this request. +// ReturnInfo - Pointer to where to return information. +// Ignored for this request. +// +// Returns: Status of attempt to disconnect. +// +TDI_STATUS +TdiDisconnect(PTDI_REQUEST Request, void *TO, ushort Flags, + PTDI_CONNECTION_INFORMATION DiscConnInfo, + PTDI_CONNECTION_INFORMATION ReturnInfo) +{ + TCPConnReq *ConnReq; // Connection request to use. + TCPConn *Conn; + TCB *DiscTCB; + CTELockHandle ConnTableHandle, TCBHandle; + TDI_STATUS Status; + TCP_TIME *Timeout; + + CTEGetLock(&ConnTableLock, &ConnTableHandle); + + Conn = GetConnFromConnID((uint)Request->Handle.ConnectionContext); + + if (Conn != NULL) { + CTEStructAssert(Conn, tc); + + DiscTCB = Conn->tc_tcb; + if (DiscTCB != NULL) { + CTEStructAssert(DiscTCB, tcb); + CTEGetLock(&DiscTCB->tcb_lock, &TCBHandle); + + // We have the TCB. See what kind of disconnect this is. + if (Flags & TDI_DISCONNECT_ABORT) { + // This is an abortive disconnect. If we're not already + // closed or closing, blow the connection away. + if (DiscTCB->tcb_state != TCB_CLOSED) { + CTEFreeLock(&ConnTableLock, TCBHandle); + + if (!CLOSING(DiscTCB)) { + DiscTCB->tcb_flags |= NEED_RST; + TryToCloseTCB(DiscTCB, TCB_CLOSE_ABORTED, + ConnTableHandle); + } else + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + + return TDI_SUCCESS; + } else { + // The TCB isn't connected. + CTEFreeLock(&ConnTableLock, TCBHandle); + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + DEBUGCHK; + return TDI_INVALID_STATE; + } + } else { + // This is not an abortive close. For graceful close we'll need + // a ConnReq. + CTEFreeLock(&ConnTableLock, TCBHandle); + + // Make sure we aren't in the middle of an abortive close. + if (CLOSING(DiscTCB)) { + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + return TDI_INVALID_CONNECTION; + } + + ConnReq = GetConnReq(); + if (ConnReq != NULL) { + // Got the ConnReq. See if this is a DISCONNECT_WAIT + // primitive or not. + + ConnReq->tcr_flags = 0; + ConnReq->tcr_conninfo = NULL; + ConnReq->tcr_req.tr_rtn = Request->RequestNotifyObject; + ConnReq->tcr_req.tr_context = Request->RequestContext; + + if (!(Flags & TDI_DISCONNECT_WAIT)) { + Timeout = (TCP_TIME *)TO; + + if (Timeout != NULL && !INFINITE_CONN_TO(*Timeout)) { + ulong Ticks = TCP_TIME_TO_TICKS(*Timeout); + if (Ticks > MAX_CONN_TO_TICKS) + Ticks = MAX_CONN_TO_TICKS; + else + Ticks++; + ConnReq->tcr_timeout = (ushort)Ticks; + } else + ConnReq->tcr_timeout = 0; + + // OK, we're just about set. We need to update the TCB + // state, and send the FIN. + if (DiscTCB->tcb_state == TCB_ESTAB) { + DiscTCB->tcb_state = TCB_FIN_WAIT1; + + // Since we left established, we're off the fast + // receive path. + DiscTCB->tcb_slowcount++; + DiscTCB->tcb_fastchk |= TCP_FLAG_SLOW; + } else + if (DiscTCB->tcb_state == TCB_CLOSE_WAIT) + DiscTCB->tcb_state = TCB_LAST_ACK; + else { + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + FreeConnReq(ConnReq); + return TDI_INVALID_STATE; + } + + TStats.ts_currestab--; // Update SNMP info. + CTEAssert(*(int *)&TStats.ts_currestab >= 0); + + CTEAssert(DiscTCB->tcb_connreq == NULL); + DiscTCB->tcb_connreq = ConnReq; + DiscTCB->tcb_flags |= FIN_NEEDED; + DiscTCB->tcb_refcnt++; +#ifdef VXD + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + TCPSend(DiscTCB); +#else + TCPSend(DiscTCB, ConnTableHandle); +#endif + return TDI_PENDING; + } else { + // This is a DISC_WAIT request. + ConnReq->tcr_timeout = 0; + if (DiscTCB->tcb_discwait == NULL) { + DiscTCB->tcb_discwait = ConnReq; + Status = TDI_PENDING; + } else + Status = TDI_INVALID_STATE; + + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + return Status; + } + } else { + // Couldn't get a ConnReq. + CTEFreeLock(&DiscTCB->tcb_lock, ConnTableHandle); + return TDI_NO_RESOURCES; + } + } + } + } + + // No Conn, or no TCB on conn. Return an error. + CTEFreeLock(&ConnTableLock, ConnTableHandle); + return TDI_INVALID_CONNECTION; +} + +//* OKToNotify - See if it's OK to notify about a DISC. +// +// A little utility function, called to see it it's OK to notify the client +// of an incoming FIN. +// +// Input: NotifyTCB - TCB to check. +// +// Returns: TRUE if it's OK, False otherwise. +// +uint +OKToNotify(TCB *NotifyTCB) +{ + CTEStructAssert(NotifyTCB, tcb); + if (NotifyTCB->tcb_pendingcnt == 0 && NotifyTCB->tcb_urgcnt == 0 && + NotifyTCB->tcb_rcvhead == NULL && NotifyTCB->tcb_exprcv == NULL) + return TRUE; + else + return FALSE; +} + +//* NotifyOfDisc - Notify a client that a TCB is being disconnected. +// +// Called when we're disconnecting a TCB because we've received a FIN or +// RST from the remote peer, or because we're aborting for some reason. +// We'll complete a DISCONNECT_WAIT request if we have one, or try and +// issue an indication otherwise. This is only done if we're in a synchronized +// state and not in TIMED-WAIT. +// +// Input: DiscTCB - Pointer to TCB we're notifying. +// Status - Status code for notification. +// +// Returns: Nothing. +// +void +NotifyOfDisc(TCB *DiscTCB, IPOptInfo *DiscInfo, TDI_STATUS Status) +{ + CTELockHandle TCBHandle, AOTHandle, ConnTHandle; + TCPConnReq *DiscReq; + TCPConn *Conn; + AddrObj *DiscAO; + PVOID ConnContext; + + CTEStructAssert(DiscTCB, tcb); + CTEAssert(DiscTCB->tcb_refcnt != 0); + + CTEGetLock(&DiscTCB->tcb_lock, &TCBHandle); + if (SYNC_STATE(DiscTCB->tcb_state) && + !(DiscTCB->tcb_flags & DISC_NOTIFIED)) { + + // We can't notify him if there's still data to be taken. + if (Status == TDI_GRACEFUL_DISC && !OKToNotify(DiscTCB)) { + DiscTCB->tcb_flags |= DISC_PENDING; + CTEFreeLock(&DiscTCB->tcb_lock, TCBHandle); + return; + } + + DiscTCB->tcb_flags |= DISC_NOTIFIED; + DiscTCB->tcb_flags &= ~DISC_PENDING; + + // We're in a state where a disconnect is meaningful, and we haven't + // already notified the client. + + // See if we have a DISC-WAIT request pending. + if ((DiscReq = DiscTCB->tcb_discwait) != NULL) { + // We have a disconnect wait request. Complete it and we're done. + DiscTCB->tcb_discwait = NULL; + CTEFreeLock(&DiscTCB->tcb_lock, TCBHandle); + (*DiscReq->tcr_req.tr_rtn)(DiscReq->tcr_req.tr_context, Status, 0); + FreeConnReq(DiscReq); + return; + + } + + // No DISC-WAIT. Find the AddrObj for the connection, and see if there + // is a disconnect handler registered. + + ConnContext = DiscTCB->tcb_conncontext; + CTEFreeLock(&DiscTCB->tcb_lock, TCBHandle); + + CTEGetLock(&AddrObjTableLock, &AOTHandle); + CTEGetLock(&ConnTableLock, &ConnTHandle); + if ((Conn = DiscTCB->tcb_conn) != NULL) { + CTEStructAssert(Conn, tc); + + DiscAO = Conn->tc_ao; + if (DiscAO != NULL) { + CTELockHandle AOHandle; + PDisconnectEvent DiscEvent; + PVOID DiscContext; + + + CTEStructAssert(DiscAO, ao); + CTEGetLock(&DiscAO->ao_lock, &AOHandle); + CTEFreeLock(&ConnTableLock, AOHandle); + CTEFreeLock(&AddrObjTableLock, ConnTHandle); + + DiscEvent = DiscAO->ao_disconnect; + DiscContext = DiscAO->ao_disconncontext; + + if (DiscEvent != NULL) { + uint InfoLength; + PVOID Info; + + REF_AO(DiscAO); + CTEFreeLock(&DiscAO->ao_lock, AOTHandle); + + if (DiscInfo != NULL) { + InfoLength = (uint)DiscInfo->ioi_optlength; + Info = DiscInfo->ioi_options; + } else { + InfoLength = 0; + Info = NULL; + } + + IF_TCPDBG(TCP_DEBUG_CLOSE) { + TCPTRACE(("TCP: indicating %s disconnect\n", + (Status == TDI_GRACEFUL_DISC) ? "graceful" : + "abortive" + )); + } + + (*DiscEvent)(DiscContext, + ConnContext, 0, + NULL, InfoLength, Info, (Status == TDI_GRACEFUL_DISC) ? + TDI_DISCONNECT_RELEASE : TDI_DISCONNECT_ABORT); + + DELAY_DEREF_AO(DiscAO); + return; + } else { + CTEFreeLock(&DiscAO->ao_lock, AOTHandle); + return; + } + } + } + + CTEFreeLock(&ConnTableLock, ConnTHandle); + CTEFreeLock(&AddrObjTableLock, AOTHandle); + return; + + } + CTEFreeLock(&DiscTCB->tcb_lock, TCBHandle); + +} + +//* GracefulClose - Complete the transition to a gracefully closed state. +// +// Called when we need to complete the transition to a gracefully closed +// state, either TIME_WAIT or CLOSED. This completion involves removing +// the TCB from it's associated connection (if it has one), notifying the +// upper layer client either via completing a request or calling a disc. +// notification handler, and actually doing the transition. +// +// The tricky part here is if we need to notify him (instead of completing +// a graceful disconnect request). We can't notify him if there is pending +// data on the connection, so in that case we have to pend the disconnect +// notification until we deliver the data. +// +// Input: CloseTCB - TCB to transition. +// ToTimeWait - True if we're going to TIME_WAIT, False if +// we're going to close the TCB. +// Notify - True if we're going to transition via notification, +// False if we're going to transition by completing +// a disconnect request. +// Handle - Lock handle for TCB. +// +// Returns: Nothing. +// +void +GracefulClose(TCB *CloseTCB, uint ToTimeWait, uint Notify, CTELockHandle Handle) +{ + + CTEStructAssert(CloseTCB, tcb); + + CTEAssert(CloseTCB->tcb_refcnt != 0); + + // First, see if we need to notify the client of a FIN. + if (Notify) { + // We do need to notify him. See if it's OK to do so. + if (OKToNotify(CloseTCB)) { + // We can notify him. Change his state, pull him from the conn., + // and notify him. + if (ToTimeWait) { + // Save the time we went into time wait, in case we need to + // scavenge. + CloseTCB->tcb_alive = CTESystemUpTime(); + CloseTCB->tcb_state = TCB_TIME_WAIT; + CTEFreeLock(&CloseTCB->tcb_lock, Handle); + } else { + // He's going to close. Mark him as closing with TryToCloseTCB + // (he won't actually close since we have a ref. on him). We + // do this so that anyone touching him after we free the + // lock will fail. + TryToCloseTCB(CloseTCB, TDI_SUCCESS, Handle); + } + + + RemoveTCBFromConn(CloseTCB); + NotifyOfDisc(CloseTCB, NULL, TDI_GRACEFUL_DISC); + + } else { + // Can't notify him now. Set the appropriate flags, and return. + CloseTCB->tcb_flags |= (GC_PENDING | (ToTimeWait ? TW_PENDING : 0)); + DerefTCB(CloseTCB, Handle); + return; + } + } else { + // We're not notifying this guy, we just need to complete a conn. req. + // We need to check and see if he's been notified, and if not + // we'll complete the request and notify him later. + if (CloseTCB->tcb_flags & DISC_NOTIFIED) { + // He's been notified. + if (ToTimeWait) { + // Save the time we went into time wait, in case we need to + // scavenge. + CloseTCB->tcb_alive = CTESystemUpTime(); + CloseTCB->tcb_state = TCB_TIME_WAIT; + CTEFreeLock(&CloseTCB->tcb_lock, Handle); + } else { + // Mark him as closed. See comments above. + TryToCloseTCB(CloseTCB, TDI_SUCCESS, Handle); + } + + RemoveTCBFromConn(CloseTCB); + + CTEGetLock(&CloseTCB->tcb_lock, &Handle); + CompleteConnReq(CloseTCB, NULL, TDI_SUCCESS); + CTEFreeLock(&CloseTCB->tcb_lock, Handle); + } else { + // He hasn't been notified. He should be pending already. + CTEAssert(CloseTCB->tcb_flags & DISC_PENDING); + CloseTCB->tcb_flags |= (GC_PENDING | (ToTimeWait ? TW_PENDING : 0)); + + CompleteConnReq(CloseTCB, NULL, TDI_SUCCESS); + + DerefTCB(CloseTCB, Handle); + return; + } + } + + // If we're going to TIME_WAIT, start the TIME_WAIT timer now. + // Otherwise close the TCB. + CTEGetLock(&CloseTCB->tcb_lock, &Handle); + if (!CLOSING(CloseTCB) && ToTimeWait) { + START_TCB_TIMER(CloseTCB->tcb_rexmittimer, MAX_REXMIT_TO); + CTEFreeLock(&CloseTCB->tcb_lock, Handle); + RemoveConnFromTCB(CloseTCB); + CTEGetLock(&CloseTCB->tcb_lock, &Handle); + + } + + DerefTCB(CloseTCB, Handle); + +} + +//* ConnCheckPassed - Check to see if we have exceeded the connect limit +// +// Called when a SYN is received to determine whether we will accept +// the incoming connection. If the is an empty slot or if the IPAddr +// is already in the table, we accept it. +// +// Input: Source Address of incoming connection +// Destination port of incoming connection +// +// Returns: TRUE is connect is to be accepted +// FALSE if connection is rejected +// +int ConnCheckPassed(IPAddr Src, ulong Prt) + +{ + UNREFERENCED_PARAMETER(Src); + UNREFERENCED_PARAMETER(Prt); + + return TRUE; +} + +void InitAddrChecks() +{ + return; +} + +//* EnumerateConnectionList - Enumerate Connection List database. +// +// This routine enumerates the contents of the connection limit database +// +// Input: +// +// Buffer - A pointer to a buffer into which to put +// the returned connection list entries. +// +// BufferSize - On input, the size in bytes of Buffer. +// On output, the number of bytes written. +// +// EntriesAvailable - On output, the total number of connection entries +// available in the database. +// +// Returns: A TDI status code: +// +// TDI_SUCCESS otherwise. +// +// NOTES: +// +// This routine acquires AddrObjTableLock. +// +// Entries written to output buffer are in host byte order. +// +void +EnumerateConnectionList(uchar *Buffer, ulong BufferSize, + ulong *EntriesReturned, ulong *EntriesAvailable) +{ + + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(BufferSize); + + *EntriesAvailable = 0; + *EntriesReturned = 0; + + return; +} + + +#pragma BEGIN_INIT + +//* InitTCPConn - Initialize TCP connection management code. +// +// Called during init time to initialize our TCP connection mgmt.. +// +// Input: Nothing. +// +// Returns: TRUE. +// +int +InitTCPConn(void) +{ +#ifdef NT + ExInitializeSListHead(&ConnReqFree); +#endif + + CTEInitLock(&ConnReqFreeLock); + return TRUE; +} + +//* UnInitTCPConn - Uninitialize our connection management code. +// +// Called if initialization fails to uninitialize our conn mgmet. +// +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +UnInitTCPConn(void) +{ + +} + +#pragma END_INIT + |