NT4/private/ntos/tdi/tcpip/tcp/tcpconn.c
2020-09-30 17:12:29 +02:00

2345 lines
72 KiB
C

/********************************************************************/
/** 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