/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: spxcutil.c Abstract: This module contains code which implements the CONNECTION object. Routines are provided to create, destroy, reference, and dereference, transport connection objects. Author: Nikhil Kamkolkar (nikhilk) 11-November-1993 Environment: Kernel mode Revision History: Sanjay Anand (SanjayAn) 5-July-1995 Bug fixes - tagged [SA] --*/ #include "precomp.h" #pragma hdrstop #ifndef __PREFAST__ #pragma warning(disable:4068) #endif #pragma prefast(disable:276, "The assignments are harmless") // Define module number for event logging entries #define FILENUM SPXCUTIL // // Minor utility routines // BOOLEAN spxConnCheckNegSize( IN PUSHORT pNegSize ) /*++ Routine Description: Arguments: Return Value: --*/ { int i; // We go thru table and see if this is the minimum size or if it // can go down further. Return true if it is not the minimum size. DBGPRINT(CONNECT, INFO, ("spxConnCheckNegSize: Current %lx Check Val %lx\n", (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE), SpxMaxPktSize[0])); if ((ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE) <= SpxMaxPktSize[0]) return(FALSE); for (i = SpxMaxPktSizeIndex-1; i > 0; i--) { DBGPRINT(CONNECT, INFO, ("spxConnCheckNegSize: Current %lx Check Val %lx\n", (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE), SpxMaxPktSize[i])); if (SpxMaxPktSize[i] < (ULONG)(*pNegSize - MIN_IPXSPX2_HDRSIZE)) break; } *pNegSize = (USHORT)(SpxMaxPktSize[i] + MIN_IPXSPX2_HDRSIZE); DBGPRINT(CONNECT, ERR, ("spxConnCheckNegSize: Trying Size %lx Min size possible %lx\n", *pNegSize, SpxMaxPktSize[0] + MIN_IPXSPX2_HDRSIZE)); return(TRUE); } VOID spxConnSetNegSize( IN OUT PNDIS_PACKET pPkt, IN ULONG Size ) /*++ Routine Description: Arguments: Return Value: --*/ { PNDIS_BUFFER pNdisBuffer; UINT bufCount; PSPX_SEND_RESD pSendResd; PIPXSPX_HDR pIpxSpxHdr; CTEAssert(Size > 0); NdisQueryPacket(pPkt, NULL, &bufCount, &pNdisBuffer, NULL); CTEAssert (bufCount == 3); NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer); NdisQueryBuffer(pNdisBuffer, &pIpxSpxHdr, &bufCount); NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer); NdisAdjustBufferLength(pNdisBuffer, Size); // Change it in send reserved pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); pSendResd->sr_Len = (Size + MIN_IPXSPX2_HDRSIZE); #if SPX_OWN_PACKETS // Change in ipx header pIpxSpxHdr = (PIPXSPX_HDR)((PBYTE)pPkt + NDIS_PACKET_SIZE + sizeof(SPX_SEND_RESD) + IpxInclHdrOffset); #endif PUTSHORT2SHORT((PUSHORT)&pIpxSpxHdr->hdr_PktLen, (Size + MIN_IPXSPX2_HDRSIZE)); // Change in the neg packet field of the header. PUTSHORT2SHORT( &pIpxSpxHdr->hdr_NegSize, (Size + MIN_IPXSPX2_HDRSIZE)); DBGPRINT(CONNECT, DBG, ("spxConnSetNegSize: Setting size to %lx Hdr %lx\n", Size, (Size + MIN_IPXSPX2_HDRSIZE))); return; } BOOLEAN SpxConnDequeueSendPktLock( IN PSPX_CONN_FILE pSpxConnFile, IN PNDIS_PACKET pPkt ) /*++ Routine Description: Arguments: Return Value: --*/ { PSPX_SEND_RESD pSr, pListHead, pListTail; PSPX_SEND_RESD pSendResd; BOOLEAN removed = TRUE; // If we are sequenced or not decides which list we choose. pSendResd = (PSPX_SEND_RESD)(pPkt->ProtocolReserved); if ((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0) { pListHead = pSpxConnFile->scf_SendSeqListHead; pListTail = pSpxConnFile->scf_SendSeqListTail; } else { pListHead = pSpxConnFile->scf_SendListHead; pListTail = pSpxConnFile->scf_SendListTail; } // Most often, we will be at the head of the list. if (pListHead == pSendResd) { if ((pListHead = pSendResd->sr_Next) == NULL) { DBGPRINT(SEND, INFO, ("SpxConnDequeuePktLock: %lx first in list\n", pSendResd)); pListTail = NULL; } } else { DBGPRINT(SEND, INFO, ("SpxConnDequeuePktLock: %lx !first in list\n", pSendResd)); pSr = pListHead; while (pSr != NULL) { if (pSr->sr_Next == pSendResd) { if ((pSr->sr_Next = pSendResd->sr_Next) == NULL) { pListTail = pSr; } break; } pSr = pSr->sr_Next; } if (pSr == NULL) removed = FALSE; } if (removed) { if ((pSendResd->sr_State & SPX_SENDPKT_SEQ) != 0) { pSpxConnFile->scf_SendSeqListHead = pListHead; pSpxConnFile->scf_SendSeqListTail = pListTail; } else { pSpxConnFile->scf_SendListHead = pListHead; pSpxConnFile->scf_SendListTail = pListTail; } } return(removed); } BOOLEAN SpxConnDequeueRecvPktLock( IN PSPX_CONN_FILE pSpxConnFile, IN PNDIS_PACKET pPkt ) /*++ Routine Description: Arguments: Return Value: --*/ { PSPX_RECV_RESD pSr, pListHead, pListTail; PSPX_RECV_RESD pRecvResd; BOOLEAN removed = TRUE; pRecvResd = (PSPX_RECV_RESD)(pPkt->ProtocolReserved); pListHead = pSpxConnFile->scf_RecvListHead; pListTail = pSpxConnFile->scf_RecvListTail; // Most often, we will be at the head of the list. if (pListHead == pRecvResd) { DBGPRINT(RECEIVE, INFO, ("SpxConnDequeuePktLock: %lx first in list\n", pRecvResd)); if ((pListHead = pRecvResd->rr_Next) == NULL) { pListTail = NULL; } } else { DBGPRINT(RECEIVE, INFO, ("SpxConnDequeuePktLock: %lx !first in list\n", pRecvResd)); pSr = pListHead; while (pSr != NULL) { if (pSr->rr_Next == pRecvResd) { if ((pSr->rr_Next = pRecvResd->rr_Next) == NULL) { pListTail = pSr; } break; } pSr = pSr->rr_Next; } if (pSr == NULL) removed = FALSE; } if (removed) { pSpxConnFile->scf_RecvListHead = pListHead; pSpxConnFile->scf_RecvListTail = pListTail; } return(removed); } BOOLEAN spxConnGetPktByType( IN PSPX_CONN_FILE pSpxConnFile, IN ULONG PktType, IN BOOLEAN fSeqList, IN PNDIS_PACKET * ppPkt ) /*++ Routine Description: Arguments: Return Value: --*/ { PSPX_SEND_RESD pSr, *ppSr; // Most often, we will be at the head of the list. ppSr = (fSeqList ? &pSpxConnFile->scf_SendSeqListHead : &pSpxConnFile->scf_SendListHead); for (; (pSr = *ppSr) != NULL; ) { if (pSr->sr_Type == PktType) { *ppPkt = (PNDIS_PACKET)CONTAINING_RECORD( pSr, NDIS_PACKET, ProtocolReserved); DBGPRINT(SEND, INFO, ("SpxConnFindByType: %lx.%lx.%d\n", pSr,*ppPkt, fSeqList)); break; } ppSr = &pSr->sr_Next; } return(pSr != NULL); } BOOLEAN spxConnGetPktBySeqNum( IN PSPX_CONN_FILE pSpxConnFile, IN USHORT SeqNum, IN PNDIS_PACKET * ppPkt ) /*++ Routine Description: Arguments: Return Value: --*/ { PSPX_SEND_RESD pSr, *ppSr; // Most often, we will be at the head of the list. ppSr = &pSpxConnFile->scf_SendSeqListHead; for (; (pSr = *ppSr) != NULL; ) { if (pSr->sr_SeqNum == SeqNum) { *ppPkt = (PNDIS_PACKET)CONTAINING_RECORD( pSr, NDIS_PACKET, ProtocolReserved); DBGPRINT(SEND, DBG, ("SpxConnFindBySeq: %lx.%lx.%d\n", pSr,*ppPkt, SeqNum)); break; } ppSr = &pSr->sr_Next; } return(pSr != NULL); } USHORT spxConnGetId( VOID ) /*++ Routine Description: This must be called with the device lock held. Arguments: Return Value: --*/ { PSPX_CONN_FILE pSpxConnFile; BOOLEAN wrapped = FALSE; USHORT startConnId, retConnId; startConnId = SpxDevice->dev_NextConnId; // Search the global active list. do { if ((SpxDevice->dev_NextConnId >= startConnId) && wrapped) { retConnId = 0; break; } if (SpxDevice->dev_NextConnId == 0xFFFF) { wrapped = TRUE; SpxDevice->dev_NextConnId = 1; continue; } // Later this be a tree. for (pSpxConnFile = SpxDevice->dev_GlobalActiveConnList[ SpxDevice->dev_NextConnId & NUM_SPXCONN_HASH_MASK]; pSpxConnFile != NULL; pSpxConnFile = pSpxConnFile->scf_GlobalActiveNext) { if (pSpxConnFile->scf_LocalConnId == SpxDevice->dev_NextConnId) { break; } } // Increment for next time. retConnId = SpxDevice->dev_NextConnId++; // Ensure we are still legal. We could return if connfile is null. if (SpxDevice->dev_NextConnId == 0xFFFF) { wrapped = TRUE; SpxDevice->dev_NextConnId = 1; } if (pSpxConnFile != NULL) { continue; } break; } while (TRUE); return(retConnId); } NTSTATUS spxConnRemoveFromList( IN PSPX_CONN_FILE * ppConnListHead, IN PSPX_CONN_FILE pConnRemove ) /*++ Routine Description: This routine must be called with the address lock (and the lock of the remove connection will usually also be, but is not needed) held. Arguments: Return Value: --*/ { PSPX_CONN_FILE pRemConn, *ppRemConn; NTSTATUS status = STATUS_SUCCESS; // Dequeue the connection file from the address list. It must be // in the inactive list. for (ppRemConn = ppConnListHead; (pRemConn = *ppRemConn) != NULL;) { if (pRemConn == pConnRemove) { *ppRemConn = pRemConn->scf_Next; break; } ppRemConn = &pRemConn->scf_Next; } if (pRemConn == NULL) { DBGBRK(FATAL); CTEAssert(0); status = STATUS_INVALID_CONNECTION; } return(status); } NTSTATUS spxConnRemoveFromAssocList( IN PSPX_CONN_FILE * ppConnListHead, IN PSPX_CONN_FILE pConnRemove ) /*++ Routine Description: This routine must be called with the address lock (and the lock of the remove connection will usually also be, but is not needed) held. Arguments: Return Value: --*/ { PSPX_CONN_FILE pRemConn, *ppRemConn; NTSTATUS status = STATUS_SUCCESS; // Dequeue the connection file from the address list. It must be // in the inactive list. for (ppRemConn = ppConnListHead; (pRemConn = *ppRemConn) != NULL;) { if (pRemConn == pConnRemove) { *ppRemConn = pRemConn->scf_AssocNext; break; } ppRemConn = &pRemConn->scf_AssocNext; } if (pRemConn == NULL) { CTEAssert(0); status = STATUS_INVALID_CONNECTION; } return(status); } VOID spxConnInsertIntoGlobalActiveList( IN PSPX_CONN_FILE pSpxConnFile ) /*++ Routine Description: This routine must be called with the device lock held. Arguments: Return Value: --*/ { int index = (int)(pSpxConnFile->scf_LocalConnId & NUM_SPXCONN_HASH_MASK); // For now, its just a linear list. pSpxConnFile->scf_GlobalActiveNext = SpxDevice->dev_GlobalActiveConnList[index]; SpxDevice->dev_GlobalActiveConnList[index] = pSpxConnFile; return; } NTSTATUS spxConnRemoveFromGlobalActiveList( IN PSPX_CONN_FILE pSpxConnFile ) /*++ Routine Description: This routine must be called with the device lock held. Arguments: Return Value: --*/ { PSPX_CONN_FILE pC, *ppC; int index = (int)(pSpxConnFile->scf_LocalConnId & NUM_SPXCONN_HASH_MASK); NTSTATUS status = STATUS_SUCCESS; // For now, its just a linear list. for (ppC = &SpxDevice->dev_GlobalActiveConnList[index]; (pC = *ppC) != NULL;) { if (pC == pSpxConnFile) { DBGPRINT(SEND, INFO, ("SpxConnRemoveFromGlobal: %lx\n", pSpxConnFile)); // Remove from list *ppC = pC->scf_GlobalActiveNext; break; } ppC = &pC->scf_GlobalActiveNext; } if (pC == NULL) status = STATUS_INVALID_CONNECTION; return(status); } VOID spxConnInsertIntoGlobalList( IN PSPX_CONN_FILE pSpxConnFile ) /*++ Routine Description: Arguments: Return Value: --*/ { CTELockHandle lockHandle; // Get the global q lock CTEGetLock(&SpxGlobalQInterlock, &lockHandle); pSpxConnFile->scf_GlobalNext = SpxGlobalConnList; SpxGlobalConnList = pSpxConnFile; CTEFreeLock(&SpxGlobalQInterlock, lockHandle); return; } NTSTATUS spxConnRemoveFromGlobalList( IN PSPX_CONN_FILE pSpxConnFile ) /*++ Routine Description: Arguments: Return Value: --*/ { CTELockHandle lockHandle; PSPX_CONN_FILE pC, *ppC; NTSTATUS status = STATUS_SUCCESS; // Get the global q lock CTEGetLock(&SpxGlobalQInterlock, &lockHandle); for (ppC = &SpxGlobalConnList; (pC = *ppC) != NULL;) { if (pC == pSpxConnFile) { DBGPRINT(SEND, DBG, ("SpxConnRemoveFromGlobal: %lx\n", pSpxConnFile)); // Remove from list *ppC = pC->scf_GlobalNext; break; } ppC = &pC->scf_GlobalNext; } CTEFreeLock(&SpxGlobalQInterlock, lockHandle); if (pC == NULL) status = STATUS_INVALID_CONNECTION; return(status); } #if 0 VOID spxConnPushIntoPktList( IN PSPX_CONN_FILE pSpxConnFile ) /*++ Routine Description: !!!MACROIZE!!! Arguments: Return Value: --*/ { CTELockHandle lockHandle; // Get the global q lock CTEGetLock(&SpxGlobalQInterlock, &lockHandle); pSpxConnFile->scf_PktNext = SpxPktConnList; SpxPktConnList = pSpxConnFile; CTEFreeLock(&SpxGlobalQInterlock, lockHandle); return; } VOID spxConnPopFromPktList( IN PSPX_CONN_FILE * ppSpxConnFile ) /*++ Routine Description: !!!MACROIZE!!! Arguments: Return Value: --*/ { CTELockHandle lockHandle; // Get the global q lock CTEGetLock(&SpxGlobalQInterlock, &lockHandle); if ((*ppSpxConnFile = SpxPktConnList) != NULL) { SpxPktConnList = SpxPktConnList->scf_PktNext; DBGPRINT(SEND, DBG, ("SpxConnRemoveFromPkt: %lx\n", *ppSpxConnFile)); } CTEFreeLock(&SpxGlobalQInterlock, lockHandle); return; } VOID spxConnPushIntoRecvList( IN PSPX_CONN_FILE pSpxConnFile ) /*++ Routine Description: !!!MACROIZE!!! Arguments: Return Value: --*/ { CTELockHandle lockHandle; // Get the global q lock CTEGetLock(&SpxGlobalQInterlock, &lockHandle); pSpxConnFile->scf_ProcessRecvNext = SpxRecvConnList; SpxRecvConnList = pSpxConnFile; CTEFreeLock(&SpxGlobalQInterlock, lockHandle); return; } VOID spxConnPopFromRecvList( IN PSPX_CONN_FILE * ppSpxConnFile ) /*++ Routine Description: !!!MACROIZE!!! Arguments: Return Value: --*/ { CTELockHandle lockHandle; // Get the global q lock CTEGetLock(&SpxGlobalQInterlock, &lockHandle); if ((*ppSpxConnFile = SpxRecvConnList) != NULL) { SpxRecvConnList = SpxRecvConnList->scf_ProcessRecvNext; DBGPRINT(SEND, INFO, ("SpxConnRemoveFromRecv: %lx\n", *ppSpxConnFile)); } CTEFreeLock(&SpxGlobalQInterlock, lockHandle); return; } #endif // // Reference/Dereference routines // #if DBG VOID SpxConnFileRef( IN PSPX_CONN_FILE pSpxConnFile ) /*++ Routine Description: This routine increments the reference count on an address file. Arguments: pSpxConnFile - Pointer to a transport address file object. Return Value: none. --*/ { CTEAssert ((LONG)pSpxConnFile->scf_RefCount >= 0); // not perfect, but... (VOID)SPX_ADD_ULONG ( &pSpxConnFile->scf_RefCount, 1, &pSpxConnFile->scf_Lock); } // SpxRefConnectionFile VOID SpxConnFileLockRef( IN PSPX_CONN_FILE pSpxConnFile ) /*++ Routine Description: This routine increments the reference count on an address file. IT IS CALLED WITH THE CONNECTION LOCK HELD. Arguments: pSpxConnFile - Pointer to a transport address file object. Return Value: none. --*/ { CTEAssert ((LONG)pSpxConnFile->scf_RefCount >= 0); // not perfect, but... (VOID)SPX_ADD_ULONG ( &pSpxConnFile->scf_RefCount, 1, &pSpxConnFile->scf_Lock); } // SpxRefConnectionFileLock #endif VOID SpxConnFileRefByIdLock ( IN USHORT ConnId, OUT PSPX_CONN_FILE * ppSpxConnFile, OUT PNTSTATUS pStatus ) /*++ Routine Description: !!!MUST BE CALLED WITH THE DEVICE LOCK HELD!!! All active connections should be on the device active list. Later, this data structure will be a tree, caching the last accessed connection. Arguments: Return Value: STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise --*/ { PSPX_CONN_FILE pSpxChkConn; *pStatus = STATUS_SUCCESS; for (pSpxChkConn = SpxDevice->dev_GlobalActiveConnList[ConnId & NUM_SPXCONN_HASH_MASK]; pSpxChkConn != NULL; pSpxChkConn = pSpxChkConn->scf_GlobalActiveNext) { if (pSpxChkConn->scf_LocalConnId == ConnId) { SpxConnFileReference(pSpxChkConn, CFREF_BYID); *ppSpxConnFile = pSpxChkConn; break; } } if (pSpxChkConn == NULL) { *pStatus = STATUS_INVALID_CONNECTION; } return; } VOID SpxConnFileRefByCtxLock( IN PSPX_ADDR_FILE pSpxAddrFile, IN CONNECTION_CONTEXT Ctx, OUT PSPX_CONN_FILE * ppSpxConnFile, OUT PNTSTATUS pStatus ) /*++ Routine Description: !!!MUST BE CALLED WITH THE ADDRESS LOCK HELD!!! Returns a referenced connection file with the associated context and address file desired. Arguments: Return Value: --*/ { PSPX_CONN_FILE pSpxChkConn = NULL; BOOLEAN Found = FALSE; *pStatus = STATUS_SUCCESS; for (pSpxChkConn = pSpxAddrFile->saf_Addr->sa_InactiveConnList; pSpxChkConn != NULL; pSpxChkConn = pSpxChkConn->scf_Next) { if ((pSpxChkConn->scf_ConnCtx == Ctx) && (pSpxChkConn->scf_AddrFile == pSpxAddrFile)) { SpxConnFileReference(pSpxChkConn, CFREF_BYCTX); *ppSpxConnFile = pSpxChkConn; Found = TRUE; break; } } if (!Found) { *pStatus = STATUS_INVALID_CONNECTION; } return; } NTSTATUS SpxConnFileVerify ( IN PSPX_CONN_FILE pConnFile ) /*++ Routine Description: This routine is called to verify that the pointer given us in a file object is in fact a valid address file object. We also verify that the address object pointed to by it is a valid address object, and reference it to keep it from disappearing while we use it. Arguments: Return Value: STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise --*/ { CTELockHandle LockHandle; NTSTATUS status = STATUS_SUCCESS; try { if ((pConnFile->scf_Size == sizeof (SPX_CONN_FILE)) && (pConnFile->scf_Type == SPX_CONNFILE_SIGNATURE)) { CTEGetLock (&pConnFile->scf_Lock, &LockHandle); if (!SPX_CONN_FLAG(pConnFile, SPX_CONNFILE_CLOSING)) { SpxConnFileLockReference(pConnFile, CFREF_VERIFY); } else { DBGPRINT(TDI, ERR, ("StVerifyConnFile: A %lx closing\n", pConnFile)); status = STATUS_INVALID_CONNECTION; } CTEFreeLock (&pConnFile->scf_Lock, LockHandle); } else { DBGPRINT(TDI, ERR, ("StVerifyAddressFile: AF %lx bad signature\n", pConnFile)); status = STATUS_INVALID_CONNECTION; } } except(EXCEPTION_EXECUTE_HANDLER) { DBGPRINT(TDI, ERR, ("SpxVerifyConnFile: AF %lx exception\n", pConnFile)); return GetExceptionCode(); } return status; } // SpxVerifyConnFile VOID SpxConnFileDeref( IN PSPX_CONN_FILE pSpxConnFile ) /*++ Routine Description: This routine dereferences an address file by decrementing the reference count contained in the structure. If, after being decremented, the reference count is zero, then this routine calls SpxDestroyConnectionFile to remove it from the system. Arguments: pSpxConnFile - Pointer to a transport address file object. Return Value: none. --*/ { ULONG oldvalue; BOOLEAN fDiscNotIndicated = FALSE; BOOLEAN fIDiscFlag = FALSE; BOOLEAN fSpx2; CTEAssert(pSpxConnFile->scf_RefCount > 0); oldvalue = SPX_ADD_ULONG ( &pSpxConnFile->scf_RefCount, (ULONG)-1, &pSpxConnFile->scf_Lock); CTEAssert (oldvalue > 0); if (oldvalue == 1) { CTELockHandle lockHandleConn, lockHandleAddr, lockHandleDev; LIST_ENTRY discReqList, *p; PREQUEST pDiscReq; PSPX_ADDR_FILE pSpxAddrFile = NULL; PREQUEST pCloseReq = NULL, pCleanupReq = NULL, pConnectReq = NULL; BOOLEAN fDisassoc = FALSE; InitializeListHead(&discReqList); // We may not be associated at this point. Note: When we are active we // always have a reference. So its not like we execute this code very often. CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) { pSpxAddrFile = pSpxConnFile->scf_AddrFile; } else { if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) { DBGPRINT(TDI, DBG, ("SpxDerefConnectionFile: Conn cleanup %lx.%lx\n", pSpxConnFile, pSpxConnFile->scf_CleanupReq)); // Save this for later completion. pCleanupReq = pSpxConnFile->scf_CleanupReq; pSpxConnFile->scf_CleanupReq = NULL; } if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING)) { DBGPRINT(TDI, DBG, ("SpxDerefConnectionFile: Conn closing %lx\n", pSpxConnFile)); // Save this for later completion. pCloseReq = pSpxConnFile->scf_CloseReq; // // Null this out so on a re-entrant case, we dont try to complete this again. // pSpxConnFile->scf_CloseReq = NULL; CTEAssert(pCloseReq != NULL); } } CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); if (pSpxAddrFile) { CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); //if (pSpxConnFile->scf_RefCount == 0) // // ** The lock passed here is a dummy - it is pre-compiled out. // if (SPX_ADD_ULONG(&pSpxConnFile->scf_RefCount, 0, &pSpxConnFile->scf_Lock) == 0) { DBGPRINT(TDI, INFO, ("SpxDerefConnectionFile: Conn is 0 %lx.%lx\n", pSpxConnFile, pSpxConnFile->scf_Flags)); // All pending requests on this connection are done. See if we // need to complete the disconnect phase etc. switch (SPX_MAIN_STATE(pSpxConnFile)) { case SPX_CONNFILE_DISCONN: // Disconnect is done. Move connection out of all the lists // it is on, reset states etc. DBGPRINT(TDI, INFO, ("SpxDerefConnectionFile: Conn being inactivated %lx\n", pSpxConnFile)); // Time to complete disc requests if present. // There could be multiple of them. p = pSpxConnFile->scf_DiscLinkage.Flink; while (p != &pSpxConnFile->scf_DiscLinkage) { pDiscReq = LIST_ENTRY_TO_REQUEST(p); p = p->Flink; DBGPRINT(TDI, DBG, ("SpxDerefConnectionFile: Disc on %lx.%lx\n", pSpxConnFile, pDiscReq)); RemoveEntryList(REQUEST_LINKAGE(pDiscReq)); if (REQUEST_STATUS(pDiscReq) == STATUS_PENDING) { REQUEST_STATUS(pDiscReq) = STATUS_SUCCESS; } InsertTailList( &discReqList, REQUEST_LINKAGE(pDiscReq)); } // // Note the state here, and check after the conn has been inactivated. // // // Bug #14354 - odisc and idisc cross each other, leading to double disc to AFD // if (!SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC) && !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_ODISC)) { fDiscNotIndicated = TRUE; } if (SPX_CONN_FLAG2(pSpxConnFile, SPX_CONNFILE2_IDISC)) { fIDiscFlag = TRUE; } fSpx2 = (SPX2_CONN(pSpxConnFile)) ? TRUE : FALSE; // // [SA] Bug #14655 // Do not try to inactivate an already inactivated connection // if (!(SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED)) { spxConnInactivate(pSpxConnFile); } else { // // This is an SPXI connection which has got the local disconnect. // Reset the flags now. // CTEAssert(!fDiscNotIndicated); SPX_MAIN_SETSTATE(pSpxConnFile, 0); SPX_DISC_SETSTATE(pSpxConnFile, 0); SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); } // // [SA] If we were waiting for sends to be aborted and did not indicate this // disconnect to AFD; and AFD did not call a disconnect on this connection, // then call the disonnect handler now. // if (fDiscNotIndicated) { PVOID pDiscHandlerCtx; PTDI_IND_DISCONNECT pDiscHandler = NULL; ULONG discCode = 0; pDiscHandler = pSpxConnFile->scf_AddrFile->saf_DiscHandler; pDiscHandlerCtx = pSpxConnFile->scf_AddrFile->saf_DiscHandlerCtx; // Indicate disconnect to afd. if (pDiscHandler != NULL) { // // If this was an SPXI connection, the disconnect state is still // DISCONN, so if this routine is re-entered, we need to prevent // a re-indicate to AFD. // Also, we need to wait for a local disconnect from AFD since // we indicated a TDI_DISCONNECT_RELEASE. We bump up the ref count // in this case. // if (!fSpx2) { CTEAssert( (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_INACTIVATED) ); SPX_CONN_SETFLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC); if (fIDiscFlag) { SpxConnFileLockReference(pSpxConnFile, CFREF_DISCWAITSPX); SPX_CONN_SETFLAG2(pSpxConnFile, SPX_CONNFILE2_DISC_WAIT); } } CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); DBGPRINT(CONNECT, INFO, ("spxDerefConnectionFile: Indicating to afd On %lx when %lx\n", pSpxConnFile, SPX_MAIN_STATE(pSpxConnFile))); // First complete all requests waiting for receive completion on // this conn before indicating disconnect. spxConnCompletePended(pSpxConnFile); if (fIDiscFlag) { // // Indicate DISCONNECT_RELEASE to AFD so it allows receive of packets // it has buffered before the remote disconnect took place. // discCode = TDI_DISCONNECT_RELEASE; } else { // // [SA] bug #15249 // If not Informed disconnect, indicate DISCONNECT_ABORT to AFD // discCode = TDI_DISCONNECT_ABORT; } (*pDiscHandler)( pDiscHandlerCtx, pSpxConnFile->scf_ConnCtx, 0, // Disc data NULL, 0, // Disc info NULL, discCode); CTEGetLock(&SpxDevice->dev_Lock, &lockHandleDev); CTEGetLock(pSpxAddrFile->saf_AddrLock, &lockHandleAddr); CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandleConn); } } --SpxDevice->dev_Stat.OpenConnections; break; case SPX_CONNFILE_CONNECTING: case SPX_CONNFILE_LISTENING: // Get connect/accept request if present. pConnectReq = pSpxConnFile->scf_ConnectReq; pSpxConnFile->scf_ConnectReq = NULL; spxConnInactivate(pSpxConnFile); break; case SPX_CONNFILE_ACTIVE: KeBugCheck(0); default: CTEAssert(SPX_MAIN_STATE(pSpxConnFile) == 0); break; } // If stopping, disassociate from the address file. Complete // cleanup request. if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING)) { DBGPRINT(TDI, DBG, ("SpxDerefConnectionFile: Conn cleanup %lx.%lx\n", pSpxConnFile, pSpxConnFile->scf_CleanupReq)); // Save this for later completion. pCleanupReq = pSpxConnFile->scf_CleanupReq; pSpxConnFile->scf_CleanupReq = NULL; SPX_CONN_RESETFLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_ASSOC)) { DBGPRINT(TDI, INFO, ("SpxDerefConnectionFile: Conn stopping %lx\n", pSpxConnFile)); pSpxAddrFile = pSpxConnFile->scf_AddrFile; SPX_CONN_RESETFLAG(pSpxConnFile,SPX_CONNFILE_ASSOC); // Dequeue the connection from the address file spxConnRemoveFromAssocList( &pSpxAddrFile->saf_AssocConnList, pSpxConnFile); // Dequeue the connection file from the address list. It must // be in the inactive list. spxConnRemoveFromList( &pSpxAddrFile->saf_Addr->sa_InactiveConnList, pSpxConnFile); DBGPRINT(CREATE, INFO, ("SpxConnDerefDisAssociate: %lx from addr file %lx\n", pSpxConnFile, pSpxAddrFile)); fDisassoc = TRUE; } } if (SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING)) { DBGPRINT(TDI, DBG, ("SpxDerefConnectionFile: Conn closing %lx\n", pSpxConnFile)); // Save this for later completion. pCloseReq = pSpxConnFile->scf_CloseReq; // // Null this out so on a re-entrant case, we dont try to complete this again. // pSpxConnFile->scf_CloseReq = NULL; CTEAssert(pCloseReq != NULL); } CTEAssert(IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); CTEAssert(IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); } CTEFreeLock (&pSpxConnFile->scf_Lock, lockHandleConn); CTEFreeLock (pSpxAddrFile->saf_AddrLock, lockHandleAddr); CTEFreeLock (&SpxDevice->dev_Lock, lockHandleDev); } if (fDisassoc) { // Remove reference on address for this association. SpxAddrFileDereference(pSpxAddrFile, AFREF_CONN_ASSOC); } if (pConnectReq != (PREQUEST)NULL) { DBGPRINT(TDI, DBG, ("SpxDerefConnectionFile: Connect on %lx req %lx\n", pSpxConnFile, pConnectReq)); // Status will already be set in here. We should be here only if // connect is being aborted. SpxCompleteRequest(pConnectReq); } while (!IsListEmpty(&discReqList)) { p = RemoveHeadList(&discReqList); pDiscReq = LIST_ENTRY_TO_REQUEST(p); DBGPRINT(CONNECT, DBG, ("SpxConnFileDeref: DISC REQ %lx.%lx Completing\n", pSpxConnFile, pDiscReq)); SpxCompleteRequest(pDiscReq); } if (pCleanupReq != (PREQUEST)NULL) { DBGPRINT(TDI, DBG, ("SpxDerefConnectionFile: Cleanup complete %lx req %lx\n", pSpxConnFile, pCleanupReq)); REQUEST_INFORMATION(pCleanupReq) = 0; REQUEST_STATUS(pCleanupReq) = STATUS_SUCCESS; SpxCompleteRequest (pCleanupReq); } if (pCloseReq != (PREQUEST)NULL) { DBGPRINT(TDI, DBG, ("SpxDerefConnectionFile: Freed %lx close req %lx\n", pSpxConnFile, pCloseReq)); CTEAssert(pSpxConnFile->scf_RefCount == 0); // Remove from the global list if (!NT_SUCCESS(spxConnRemoveFromGlobalList(pSpxConnFile))) { KeBugCheck(0); } // Free it up. SpxFreeMemory (pSpxConnFile); REQUEST_INFORMATION(pCloseReq) = 0; REQUEST_STATUS(pCloseReq) = STATUS_SUCCESS; SpxCompleteRequest (pCloseReq); } } return; } // SpxDerefConnectionFile VOID spxConnReInit( IN PSPX_CONN_FILE pSpxConnFile ) /*++ Routine Description: Arguments: Return Value: --*/ { // Reinit all variables. pSpxConnFile->scf_Flags2 = 0; pSpxConnFile->scf_GlobalActiveNext = NULL; pSpxConnFile->scf_PktNext = NULL; pSpxConnFile->scf_CRetryCount = 0; pSpxConnFile->scf_WRetryCount = 0; pSpxConnFile->scf_RRetryCount = 0; pSpxConnFile->scf_RRetrySeqNum = 0; pSpxConnFile->scf_CTimerId = pSpxConnFile->scf_RTimerId = pSpxConnFile->scf_WTimerId = pSpxConnFile->scf_TTimerId = pSpxConnFile->scf_ATimerId = 0; pSpxConnFile->scf_LocalConnId = pSpxConnFile->scf_SendSeqNum = pSpxConnFile->scf_SentAllocNum = pSpxConnFile->scf_RecvSeqNum = pSpxConnFile->scf_RetrySeqNum = pSpxConnFile->scf_RecdAckNum = pSpxConnFile->scf_RemConnId = pSpxConnFile->scf_RecdAllocNum = 0; #if DBG // Initialize so we dont hit breakpoint on seq 0 pSpxConnFile->scf_PktSeqNum = 0xFFFF; #endif pSpxConnFile->scf_DataType = 0; CTEAssert(IsListEmpty(&pSpxConnFile->scf_ReqLinkage)); CTEAssert(IsListEmpty(&pSpxConnFile->scf_DiscLinkage)); CTEAssert(IsListEmpty(&pSpxConnFile->scf_RecvLinkage)); CTEAssert(pSpxConnFile->scf_RecvListHead == NULL); CTEAssert(pSpxConnFile->scf_RecvListTail == NULL); CTEAssert(pSpxConnFile->scf_SendListHead == NULL); CTEAssert(pSpxConnFile->scf_SendListTail == NULL); CTEAssert(pSpxConnFile->scf_SendSeqListHead == NULL); CTEAssert(pSpxConnFile->scf_SendSeqListTail == NULL); pSpxConnFile->scf_CurRecvReq = NULL; pSpxConnFile->scf_CurRecvOffset = 0; pSpxConnFile->scf_CurRecvSize = 0; pSpxConnFile->scf_ReqPkt = NULL; return; } VOID spxConnInactivate( IN PSPX_CONN_FILE pSpxConnFile ) /*++ Routine Description: !!! Called with dev/addr/connection lock held !!! Arguments: This gets us back to associate SAVING the state of the STOPPING and CLOSING flags so that dereference can go ahead and finish those. Return Value: --*/ { BOOLEAN fStopping, fClosing, fAborting; fStopping = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_STOPPING); fClosing = SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_CLOSING); // // [SA] Bug #14655 // Save the disconnect states so that a proper error can be given in the case of // a send after a remote disconnection. // // // Bug #17729 // Dont retain these flags if a local disconnect has already occured. // fAborting = (!SPX2_CONN(pSpxConnFile) && !SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_IND_IDISC) && (SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_DISCONN) && (SPX_DISC_STATE(pSpxConnFile) == SPX_DISC_ABORT)); #if DBG pSpxConnFile->scf_GhostFlags = pSpxConnFile->scf_Flags; pSpxConnFile->scf_GhostFlags2 = pSpxConnFile->scf_Flags2; pSpxConnFile->scf_GhostRefCount = pSpxConnFile->scf_RefCount; #endif // Clear all flags, go back to the assoc state. Restore stop/close pSpxConnFile->scf_Flags = SPX_CONNFILE_ASSOC; SPX_CONN_SETFLAG(pSpxConnFile, ((fStopping ? SPX_CONNFILE_STOPPING : 0) | (fClosing ? SPX_CONNFILE_CLOSING : 0))); // // [SA] bug #14655 // In order to avoid a re-entry, mark connection as SPX_DISC_INACTIVATED // if (fAborting) { SPX_MAIN_SETSTATE(pSpxConnFile, SPX_CONNFILE_DISCONN); SPX_DISC_SETSTATE(pSpxConnFile, SPX_DISC_INACTIVATED); } // Remove connection from global list on device if (!NT_SUCCESS(spxConnRemoveFromGlobalActiveList( pSpxConnFile))) { KeBugCheck(0); } // Remove connection from active list on address if (!NT_SUCCESS(spxConnRemoveFromList( &pSpxConnFile->scf_AddrFile->saf_Addr->sa_ActiveConnList, pSpxConnFile))) { KeBugCheck(0); } // Put connection in inactive list on address SPX_INSERT_ADDR_INACTIVE( pSpxConnFile->scf_AddrFile->saf_Addr, pSpxConnFile); spxConnReInit(pSpxConnFile); return; }