/* Copyright (c) 1998 Microsoft Corporation Module Name: tcputil.c Abstract: This module contains utility routines that used to implement the AFP/TCP interface Author: Shirish Koti Revision History: 22 Jan 1998 Initial Version --*/ #define FILENUM FILE_TCPUTIL #include #include /*** DsiInit * * This routine initialization of DSI related globals * * Returns: nothing * */ VOID DsiInit( IN VOID ) { DsiTcpAdapter = NULL; INITIALIZE_SPIN_LOCK(&DsiAddressLock); INITIALIZE_SPIN_LOCK(&DsiResourceLock); InitializeListHead(&DsiFreeRequestList); InitializeListHead(&DsiIpAddrList); KeInitializeEvent(&DsiShutdownEvent, NotificationEvent, False); // // initialize the function table of entry points into DSI // AfpDsiEntries.asp_AtalkAddr.Address = 0; AfpDsiEntries.asp_AspCtxt = NULL; AfpDsiEntries.asp_SetStatus = DsiAfpSetStatus; AfpDsiEntries.asp_CloseConn = DsiAfpCloseConn; AfpDsiEntries.asp_FreeConn = DsiAfpFreeConn; AfpDsiEntries.asp_ListenControl = DsiAfpListenControl; AfpDsiEntries.asp_WriteContinue = DsiAfpWriteContinue; AfpDsiEntries.asp_Reply = DsiAfpReply; AfpDsiEntries.asp_SendAttention = DsiAfpSendAttention; } /*** DsiCreateAdapter * * This routine creates an adapter object. It's called whenever TDI tells us that * a tcpip interface became available * * Returns: status of operation * */ NTSTATUS FASTCALL DsiCreateAdapter( IN VOID ) { NTSTATUS status; PTCPADPTR pTcpAdptr; PTCPADPTR pCurrTcpAdptr; KIRQL OldIrql; HANDLE FileHandle; PFILE_OBJECT pFileObject; DWORD i; KeClearEvent(&DsiShutdownEvent); pTcpAdptr = (PTCPADPTR)AfpAllocZeroedNonPagedMemory(sizeof(TCPADPTR)); if (pTcpAdptr == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateInterface: alloc for PTCPADPTR failed\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto DsiCreateAdapter_ErrExit; } pTcpAdptr->adp_Signature = DSI_ADAPTER_SIGNATURE; pTcpAdptr->adp_RefCount = 1; // creation refcount pTcpAdptr->adp_State = TCPADPTR_STATE_INIT; InitializeListHead(&pTcpAdptr->adp_ActiveConnHead); InitializeListHead(&pTcpAdptr->adp_FreeConnHead); INITIALIZE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock); pTcpAdptr->adp_FileHandle = INVALID_HANDLE_VALUE; pTcpAdptr->adp_pFileObject = NULL; // // ok, save this adapter as our global adapter (there can only be one // "adapter" active at any time. // ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql); ASSERT(DsiTcpAdapter == NULL); if (DsiTcpAdapter == NULL) { DsiTcpAdapter = pTcpAdptr; status = STATUS_SUCCESS; RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); } else { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateAdapter: DsiTcpAdapter is not NULL!\n")); ASSERT(0); status = STATUS_ADDRESS_ALREADY_EXISTS; RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); goto DsiCreateAdapter_ErrExit; } // // create TDI address for the AFP port // status = DsiOpenTdiAddress(pTcpAdptr, &FileHandle, &pFileObject); if (!NT_SUCCESS(status)) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateAdapter: ...TdiAddr.. failed %lx on %lx!\n", status,pTcpAdptr)); goto DsiCreateAdapter_ErrExit; } ACQUIRE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, &OldIrql); pTcpAdptr->adp_FileHandle = FileHandle; pTcpAdptr->adp_pFileObject = pFileObject; // mark that we now have opened the tdi address object pTcpAdptr->adp_State |= TCPADPTR_STATE_BOUND; // we are going to create DSI_INIT_FREECONNLIST_SIZE connections to put // on the free list. Idea is at any time, so many (currently 2) connections // should be on the free list. pTcpAdptr->adp_RefCount += DSI_INIT_FREECONNLIST_SIZE; RELEASE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, OldIrql); // // now, schedule an event to create those connections for the free list // for (i=0; iadp_Signature == DSI_ADAPTER_SIGNATURE); pTcpConn = (PTCPCONN)AfpAllocZeroedNonPagedMemory(sizeof(TCPCONN)); if (pTcpConn == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateTcpConn: alloc for TCPCONN failed\n")); // remove the CONN refcount (we put before calling this routine) DsiDereferenceAdapter(pTcpAdptr); return(STATUS_INSUFFICIENT_RESOURCES); } pTcpConn->con_pTcpAdptr = pTcpAdptr; pTcpConn->con_Signature = DSI_CONN_SIGNATURE; pTcpConn->con_State = TCPCONN_STATE_INIT; pTcpConn->con_RcvState = DSI_NEW_REQUEST; pTcpConn->con_DestIpAddr = 0; pTcpConn->con_RefCount = 1; pTcpConn->con_pDsiReq = NULL; pTcpConn->con_FileHandle = INVALID_HANDLE_VALUE; pTcpConn->con_pFileObject = NULL; DBGREFCOUNT(("DsiCreateTcpConn: CREATION inc %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState)); InitializeListHead(&pTcpConn->con_PendingReqs); // // initialize the TDI stuff for this connection and open handles // status = DsiOpenTdiConnection(pTcpConn); if (!NT_SUCCESS(status)) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateTcpConn: ..TdiConn.. failed %lx\n",status)); // remove the CONN refcount (we put before calling this routine) DsiDereferenceAdapter(pTcpAdptr); AfpFreeMemory(pTcpConn); return(status); } // // associate this connection with the addr object // status = DsiAssociateTdiConnection(pTcpConn); if (!NT_SUCCESS(status)) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateTcpConn: ..AssociateTdiConn.. failed %lx\n",status)); DsiCloseTdiConnection(pTcpConn); AfpFreeMemory(pTcpConn); // remove the CONN refcount (we put before calling this routine) DsiDereferenceAdapter(pTcpAdptr); return(status); } // // the connection is ready to be queued to the Free list. Make sure the // addr object isn't closing before putting this puppy on the list // ACQUIRE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, &OldIrql); if (!(pTcpAdptr->adp_State & TCPADPTR_STATE_CLOSING)) { InsertTailList(&pTcpAdptr->adp_FreeConnHead,&pTcpConn->con_Linkage); pTcpAdptr->adp_NumFreeConnections++; status = STATUS_SUCCESS; } else { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateTcpConn: Failed #2, pTcpAdptr %lx is closing\n",pTcpAdptr)); status = STATUS_INVALID_ADDRESS; } RELEASE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, OldIrql); // // if something went wrong, undo everything // if (!NT_SUCCESS(status)) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiCreateTcpConn: something went wrong status=%lx, conn not created\n",status)); // close the TDI handles DsiCloseTdiConnection(pTcpConn); AfpFreeMemory(pTcpConn); // remove the CONN refcount (we put before calling this routine) DsiDereferenceAdapter(pTcpAdptr); } else { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_INFO, ("DsiCreateTcpConn: put new connection %lx on free list\n",pTcpConn)); } return(status); } /*** DsiAddIpaddressToList * * This routine saves an "active" ipaddress in our list of ipaddresses * * Parm IN: IpAddress - the ipaddress to save * * Returns: result of the operation * */ NTSTATUS DsiAddIpaddressToList( IN IPADDRESS IpAddress ) { KIRQL OldIrql; PLIST_ENTRY pList; PIPADDRENTITY pIpaddrEntity; PIPADDRENTITY pTmpIpaddrEntity; BOOLEAN fAlreadyPresent=FALSE; pIpaddrEntity = (PIPADDRENTITY)AfpAllocZeroedNonPagedMemory(sizeof(IPADDRENTITY)); if (pIpaddrEntity == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiAddIpaddressToList: malloc failed! (%lx)\n",IpAddress)); return(STATUS_INSUFFICIENT_RESOURCES); } pIpaddrEntity->IpAddress = IpAddress; ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql); pList = DsiIpAddrList.Flink; while (pList != &DsiIpAddrList) { pTmpIpaddrEntity = CONTAINING_RECORD(pList, IPADDRENTITY, Linkage); if (pTmpIpaddrEntity->IpAddress == IpAddress) { fAlreadyPresent = TRUE; break; } pList = pList->Flink; } if (fAlreadyPresent) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiAddIpaddressToList: %d.%d.%d.%d already present!\n", (IpAddress>>24)&0xFF,(IpAddress>>16)&0xFF,(IpAddress>>8)&0xFF,IpAddress&0xFF)); ASSERT(0); RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); return(STATUS_ADDRESS_ALREADY_EXISTS); } InsertTailList(&DsiIpAddrList, &pIpaddrEntity->Linkage); RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); return(STATUS_SUCCESS); } /*** DsiRemoveIpaddressFromList * * This routine remove ipaddress from our list of ipaddresses * * Parm IN: IpAddress - the ipaddress to remove * * Returns: TRUE if we removed the ipaddress, FALSE if we didn't * */ BOOLEAN DsiRemoveIpaddressFromList( IN IPADDRESS IpAddress ) { KIRQL OldIrql; PLIST_ENTRY pList; PIPADDRENTITY pIpaddrEntity; BOOLEAN fFoundInList=FALSE; ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql); pList = DsiIpAddrList.Flink; while (pList != &DsiIpAddrList) { pIpaddrEntity = CONTAINING_RECORD(pList, IPADDRENTITY, Linkage); if (pIpaddrEntity->IpAddress == IpAddress) { fFoundInList = TRUE; break; } pList = pList->Flink; } if (!fFoundInList) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiRemoveIpaddressFromList: %d.%d.%d.%d not in the list!\n", (IpAddress>>24)&0xFF,(IpAddress>>16)&0xFF,(IpAddress>>8)&0xFF,IpAddress&0xFF)); ASSERT(0); RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); return(FALSE); } RemoveEntryList(&pIpaddrEntity->Linkage); RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); AfpFreeMemory(pIpaddrEntity); return(TRUE); } /*** DsiGetRequest * * This routine allocates a DSI Request structure and returns. For performance * reasons, we don't alloc new memory each time, but save a list of these * * Parm IN: nothin' * * Returns: pointer to a DSIREQ strucutre (NULL on failure) * */ PDSIREQ DsiGetRequest( IN VOID ) { PDSIREQ pDsiReq=NULL; PLIST_ENTRY pList; KIRQL OldIrql; ACQUIRE_SPIN_LOCK(&DsiResourceLock, &OldIrql); if (!IsListEmpty(&DsiFreeRequestList)) { pList = RemoveHeadList(&DsiFreeRequestList); pDsiReq = CONTAINING_RECORD(pList, DSIREQ, dsi_Linkage); ASSERT(DsiFreeRequestListSize > 0); DsiFreeRequestListSize--; RtlZeroMemory(pDsiReq, sizeof(DSIREQ)); } RELEASE_SPIN_LOCK(&DsiResourceLock, OldIrql); if (pDsiReq == NULL) { pDsiReq = (PDSIREQ)AfpAllocZeroedNonPagedMemory(sizeof(DSIREQ)); } if (pDsiReq != NULL) { pDsiReq->dsi_Signature = DSI_REQUEST_SIGNATURE; InitializeListHead(&pDsiReq->dsi_Linkage); } else { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetRequest: malloc failed!\n")); } return(pDsiReq); } /*** DsiGetReqBuffer * * This routine allocates a buffer to hold either a header or a command * The likelihood of this function getting called is pretty slim (basically * if a packet is fragmented by TCP). So we simply make a call to alloc * * Parm IN: BufLen - length of the buffer requested * * Returns: pointer to a buffer (NULL on failure) * */ PBYTE DsiGetReqBuffer( IN DWORD BufLen ) { PBYTE pBuffer=NULL; pBuffer = AfpAllocNonPagedMemory(BufLen); #if DBG if (pBuffer == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetReqBuffer: malloc failed!\n")); } #endif return(pBuffer); } /*** DsiFreeRequest * * This routine frees up a previously allocated DSI Request structure. Again, * for performance reasons, we don't free up the memory but put this in a list * * Parm IN: pDsiReq - the request to be freed * * Returns: nothing * */ VOID DsiFreeRequest( PDSIREQ pDsiReq ) { KIRQL OldIrql; BOOLEAN fQueueTooBig = TRUE; if ((pDsiReq->dsi_PartialBuf != NULL) && (pDsiReq->dsi_PartialBuf != &pDsiReq->dsi_RespHeader[0])) { ASSERT(pDsiReq->dsi_PartialBufSize > 0); DsiFreeReqBuffer(pDsiReq->dsi_PartialBuf); pDsiReq->dsi_PartialBuf = NULL; pDsiReq->dsi_PartialBufSize = 0; } // if there was an Mdl we got via cache mgr, it had better be returned to system ASSERT(pDsiReq->dsi_AfpRequest.rq_CacheMgrContext == NULL); // // if we came here via abnormal disconnect, this could be non-null // if (pDsiReq->dsi_pDsiAllocedMdl) { AfpFreeMdl(pDsiReq->dsi_pDsiAllocedMdl); } #if DBG RtlFillMemory(pDsiReq, sizeof(DSIREQ), 'f'); #endif ACQUIRE_SPIN_LOCK(&DsiResourceLock, &OldIrql); if (DsiFreeRequestListSize < DsiNumTcpConnections) { InsertTailList(&DsiFreeRequestList, &pDsiReq->dsi_Linkage); DsiFreeRequestListSize++; fQueueTooBig = FALSE; } RELEASE_SPIN_LOCK(&DsiResourceLock, OldIrql); if (fQueueTooBig) { AfpFreeMemory(pDsiReq); } return; } /*** DsiFreeReqBuffer * * This routine allocates a buffer to hold either a header or a command * The likelihood of this function getting called is pretty slim (basically * if a packet is fragmented by TCP). So we simply make a call to alloc * * Parm IN: BufLen - length of the buffer requested * * Returns: pointer to a buffer (NULL on failure) * */ VOID DsiFreeReqBuffer( IN PBYTE pBuffer ) { ASSERT(pBuffer != NULL); AfpFreeMemory(pBuffer); return; } /*** DsiDereferenceAdapter * * This routine dereferences the adapter object. When refcount goes to 0, it * removes it from the global list of adapters. If at task time, it calls a * routine to close tcp handles and free the memory. If at dpc, it schedules * an event to do the same. * * Parm IN: pTcpAdptr - adapter context * * Returns: nothin' * */ VOID DsiDereferenceAdapter( IN PTCPADPTR pTcpAdptr ) { KIRQL OldIrql; BOOLEAN fDone=FALSE; ASSERT(pTcpAdptr->adp_Signature == DSI_ADAPTER_SIGNATURE); ACQUIRE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, &OldIrql); pTcpAdptr->adp_RefCount--; if (pTcpAdptr->adp_RefCount == 0) { fDone = TRUE; ASSERT(pTcpAdptr->adp_State & TCPADPTR_STATE_CLOSING); } RELEASE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, OldIrql); if (!fDone) { return; } // // this dude's life is over: do the needful to bid goodbye // // if we are at DPC, we need to do all the cleanup (file handle closing etc.) // at task time: queue an event if (KeGetCurrentIrql() == DISPATCH_LEVEL) { // queue an event, since we are at dpc DsiScheduleWorkerEvent(DsiFreeAdapter, pTcpAdptr); } else { DsiFreeAdapter(pTcpAdptr); } return; } /*** DsiDereferenceConnection * * This routine dereferences the connection object. When refcount goes to 0, it * removes it from the list of connections. If at task time, it calls a * routine to close tcp handles and free the memory. If at dpc, it schedules * an event to do the same. * * Parm IN: pTcpConn - connection context * * Returns: nothin' * */ VOID DsiDereferenceConnection( IN PTCPCONN pTcpConn ) { KIRQL OldIrql; BOOLEAN fDone=FALSE; ASSERT(VALID_TCPCONN(pTcpConn)); ACQUIRE_SPIN_LOCK(&pTcpConn->con_SpinLock, &OldIrql); pTcpConn->con_RefCount--; if (pTcpConn->con_RefCount == 0) { fDone = TRUE; ASSERT(pTcpConn->con_State & TCPCONN_STATE_CLOSING); } RELEASE_SPIN_LOCK(&pTcpConn->con_SpinLock, OldIrql); if (!fDone) { return; } // // this dude's life is over: do the needful to bid goodbye // #if 0 // if we are at DPC, we need to do all the cleanup (file handle closing etc.) // at task time: queue an event if (KeGetCurrentIrql() == DISPATCH_LEVEL) { // queue an event, since we are at dpc DsiScheduleWorkerEvent(DsiFreeConnection, pTcpConn); } else { DsiFreeConnection(pTcpConn); } #endif // schedule a worker event to free this connection DsiScheduleWorkerEvent(DsiFreeConnection, pTcpConn); return; } /*** DsiDestroyAdapter * * This routine destroys the global adapter object. * * Returns: status of operation * */ NTSTATUS DsiDestroyAdapter( IN VOID ) { KIRQL OldIrql; PLIST_ENTRY pFreeList; PLIST_ENTRY pActiveList; PTCPCONN pTcpConn; BOOLEAN fAlreadyCleanedUp=FALSE; if (DsiTcpAdapter == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiDestroyAdapter: adapter gone! How did this happen!!\n")); // unblock the event! KeSetEvent(&DsiShutdownEvent, IO_NETWORK_INCREMENT, False); return(STATUS_SUCCESS); } // stop the tickle timer if (!AfpScavengerKillEvent(DsiSendTickles, NULL)) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiDestroyAdapter: TickleTimer not running or hit timing window!!\n")); } ACQUIRE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, &OldIrql); if (DsiTcpAdapter->adp_State & TCPADPTR_STATE_CLEANED_UP) { fAlreadyCleanedUp = TRUE; } DsiTcpAdapter->adp_State |= TCPADPTR_STATE_CLOSING; DsiTcpAdapter->adp_State |= TCPADPTR_STATE_CLEANED_UP; if (fAlreadyCleanedUp) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiDestroyAdapter: already destroyed!\n")); ASSERT(IsListEmpty(&DsiTcpAdapter->adp_FreeConnHead)); ASSERT(IsListEmpty(&DsiTcpAdapter->adp_ActiveConnHead)); ASSERT(DsiTcpAdapter->adp_NumFreeConnections == 0); RELEASE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, OldIrql); return(STATUS_SUCCESS); } // // free up all the connections from the Free list // while (!IsListEmpty(&DsiTcpAdapter->adp_FreeConnHead)) { pFreeList = DsiTcpAdapter->adp_FreeConnHead.Flink; pTcpConn = CONTAINING_RECORD(pFreeList, TCPCONN, con_Linkage); RemoveEntryList(&pTcpConn->con_Linkage); ASSERT(DsiTcpAdapter->adp_NumFreeConnections > 0); DsiTcpAdapter->adp_NumFreeConnections--; InitializeListHead(&pTcpConn->con_Linkage); RELEASE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, OldIrql); pTcpConn->con_State |= TCPCONN_STATE_CLOSING; DsiDereferenceConnection(pTcpConn); DBGREFCOUNT(("DsiDestroyAdapter: Creation dec %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState)); ACQUIRE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, &OldIrql); } // // kill all the connections from the Active list // pActiveList = DsiTcpAdapter->adp_ActiveConnHead.Flink; while (pActiveList != &DsiTcpAdapter->adp_ActiveConnHead) { pTcpConn = CONTAINING_RECORD(pActiveList, TCPCONN, con_Linkage); pActiveList = pActiveList->Flink; ACQUIRE_SPIN_LOCK_AT_DPC(&pTcpConn->con_SpinLock); // if this connection is already closing, skip it if (pTcpConn->con_State & TCPCONN_STATE_CLOSING) { RELEASE_SPIN_LOCK_FROM_DPC(&pTcpConn->con_SpinLock); continue; } // put ABORT refcount for now pTcpConn->con_RefCount++; DBGREFCOUNT(("DsiDestroyAdapter: ABORT inc %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState)); RemoveEntryList(&pTcpConn->con_Linkage); InitializeListHead(&pTcpConn->con_Linkage); RELEASE_SPIN_LOCK_FROM_DPC(&pTcpConn->con_SpinLock); RELEASE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, OldIrql); DsiAbortConnection(pTcpConn); // remove that ABORT refcount DsiDereferenceConnection(pTcpConn); DBGREFCOUNT(("DsiDestroyAdapter: ABORT dec %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState)); ACQUIRE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, &OldIrql); // since we released the lock, things could have changed: start over pActiveList = DsiTcpAdapter->adp_ActiveConnHead.Flink; } RELEASE_SPIN_LOCK(&DsiTcpAdapter->adp_SpinLock, OldIrql); // remove the creation refcount DsiDereferenceAdapter(DsiTcpAdapter); return(STATUS_SUCCESS); } /*** DsiKillConnection * * This routine kills an active connection. * * Parm IN: pTcpConn - the connection to kill * * Returns: TRUE if we killed it, FALSE if we couldn't * */ BOOLEAN DsiKillConnection( IN PTCPCONN pTcpConn, IN DWORD DiscFlag ) { KIRQL OldIrql; NTSTATUS status; PDSIREQ pPartialDsiReq=NULL; BOOLEAN fFirstVisit=TRUE; ACQUIRE_SPIN_LOCK(&pTcpConn->con_SpinLock, &OldIrql); if (pTcpConn->con_State & TCPCONN_STATE_CLEANED_UP) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_WARN, ("DsiKillConnection: connection %lx already cleaned up\n",pTcpConn)); fFirstVisit = FALSE; } pTcpConn->con_State &= ~TCPCONN_STATE_CONNECTED; pTcpConn->con_State |= (TCPCONN_STATE_CLOSING | TCPCONN_STATE_CLEANED_UP); // // if a request is waiting for an mdl to become available, don't touch it here. // When afp returns with mdl (or null mdl), we'll clean up this request // if (pTcpConn->con_RcvState != DSI_AWAITING_WRITE_MDL) { pPartialDsiReq = pTcpConn->con_pDsiReq; pTcpConn->con_pDsiReq = NULL; } RELEASE_SPIN_LOCK(&pTcpConn->con_SpinLock, OldIrql); if (pPartialDsiReq) { // if we had allocated an mdl, let afp know so afp can free it if ((pPartialDsiReq->dsi_Command == DSI_COMMAND_WRITE) && (pPartialDsiReq->dsi_AfpRequest.rq_WriteMdl != NULL)) { AfpCB_RequestNotify(STATUS_REMOTE_DISCONNECT, pTcpConn->con_pSda, &pPartialDsiReq->dsi_AfpRequest); } DsiFreeRequest(pPartialDsiReq); // remove the REQUEST refcount DsiDereferenceConnection(pTcpConn); DBGREFCOUNT(("DsiKillConnection: REQUEST dec %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState)); } status = (DiscFlag == TDI_DISCONNECT_ABORT)? STATUS_LOCAL_DISCONNECT: STATUS_REMOTE_DISCONNECT; // give AFP the bad news DsiDisconnectWithAfp(pTcpConn, status); // give TCP the bad news DsiDisconnectWithTcp(pTcpConn, DiscFlag); // remove the Creation refcount if this is the first time we're visiting // this routine if (fFirstVisit) { DsiDereferenceConnection(pTcpConn); DBGREFCOUNT(("DsiKillConnection: Creation dec %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState)); } return(TRUE); } /*** DsiFreeAdapter * * This routine frees the adapter object after closing the tcp handles * * Parm IN: pTcpAdptr - adapter object * * Returns: result of the operation * */ NTSTATUS FASTCALL DsiFreeAdapter( IN PTCPADPTR pTcpAdptr ) { BOOLEAN fRecreateAdapter=FALSE; KIRQL OldIrql; ASSERT(KeGetCurrentIrql() != DISPATCH_LEVEL); ASSERT(pTcpAdptr->adp_Signature == DSI_ADAPTER_SIGNATURE); ASSERT(pTcpAdptr->adp_State & TCPADPTR_STATE_CLOSING); ASSERT(pTcpAdptr->adp_RefCount == 0); // close file handles DsiCloseTdiAddress(pTcpAdptr); ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql); DsiTcpAdapter = NULL; AfpServerBoundToTcp = FALSE; // // it's possible that by the time we did all the cleanup and everything, // an ipaddress(es) became available. If that has happened, go ahead and // create the global adapter again! // if (!IsListEmpty(&DsiIpAddrList)) { fRecreateAdapter = TRUE; } // if we are shutting down, don't create the adapter again! if ((AfpServerState == AFP_STATE_STOP_PENDING) || (AfpServerState == AFP_STATE_SHUTTINGDOWN) || (AfpServerState == AFP_STATE_STOPPED)) { fRecreateAdapter = FALSE; } RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); ASSERT(pTcpAdptr->adp_pFileObject == NULL); ASSERT(pTcpAdptr->adp_FileHandle == INVALID_HANDLE_VALUE); DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiFreeAdapter: freeing adapter %lx\n",pTcpAdptr)); AfpFreeMemory(pTcpAdptr); // wake up that blocked thread! KeSetEvent(&DsiShutdownEvent, IO_NETWORK_INCREMENT, False); if (fRecreateAdapter) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiFreeAdapter: ipaddress came in, so recreating global adapter\n")); DsiCreateAdapter(); } return(STATUS_SUCCESS); } /*** DsiFreeConnection * * This routine frees the connection object after closing the tcp handles * * Parm IN: pTcpConn - connection object * * Returns: result of the operation * */ NTSTATUS FASTCALL DsiFreeConnection( IN PTCPCONN pTcpConn ) { KIRQL OldIrql; PTCPADPTR pTcpAdptr; IPADDRESS IpAddress; ASSERT(KeGetCurrentIrql() != DISPATCH_LEVEL); ASSERT(pTcpConn->con_Signature == DSI_CONN_SIGNATURE); ASSERT(pTcpConn->con_State & TCPCONN_STATE_CLOSING); ASSERT(pTcpConn->con_RefCount == 0); pTcpAdptr = pTcpConn->con_pTcpAdptr; ASSERT(pTcpAdptr->adp_Signature == DSI_ADAPTER_SIGNATURE); // close file handles DsiCloseTdiConnection(pTcpConn); // remove this puppy from the list ACQUIRE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, &OldIrql); RemoveEntryList(&pTcpConn->con_Linkage); RELEASE_SPIN_LOCK(&pTcpAdptr->adp_SpinLock, OldIrql); // remove the CONN refcount for this connection DsiDereferenceAdapter(pTcpConn->con_pTcpAdptr); ASSERT(pTcpConn->con_pFileObject == NULL); ASSERT(pTcpConn->con_FileHandle == INVALID_HANDLE_VALUE); #if DBG IpAddress = pTcpConn->con_DestIpAddr; if (IpAddress) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_WARN, ("DsiFreeConnection: freeing connection on %d.%d.%d.%d (%lx)\n", (IpAddress>>24)&0xFF,(IpAddress>>16)&0xFF,(IpAddress>>8)&0xFF, IpAddress&0xFF,pTcpConn)); } pTcpConn->con_Signature = 0xDEADBEEF; #endif AfpFreeMemory(pTcpConn); return(STATUS_SUCCESS); } /*** DsiGetIpAddrBlob * * This routine generates a 'blob' that gets plugged into the ServerInfo buffer. * Here we walk the ipaddr list and generate a blob with all the available * ipaddresses (6-byte-per-ipaddress_ * * Parm OUT: pIpAddrCount - how many ipaddresses there are in the system * ppIpAddrBlob - pointer to a pointer to a buffer * * Returns: status of operation * */ NTSTATUS DsiGetIpAddrBlob( OUT DWORD *pIpAddrCount, OUT PBYTE *ppIpAddrBlob ) { KIRQL OldIrql; PLIST_ENTRY pList; DWORD AddrCount=0; DWORD TmpCount=0; PBYTE AddrBlob; PBYTE pCurrentBlob; PIPADDRENTITY pIpaddrEntity; *pIpAddrCount = 0; *ppIpAddrBlob = NULL; ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql); if (!DsiTcpEnabled) { RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetIpAddrBlob: Server is disabled\n")); return(STATUS_SUCCESS); } // // find out how many ipaddresses are there on the list // AddrCount = 0; pList = DsiIpAddrList.Flink; while (pList != &DsiIpAddrList) { AddrCount++; pList = pList->Flink; } if (AddrCount == 0) { RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetIpAddrBlob: calling AfpSetServerStatus with 0 addrs\n")); return(STATUS_SUCCESS); } if (AddrCount > DSI_MAX_IPADDR_COUNT) { AddrCount = DSI_MAX_IPADDR_COUNT; } AddrBlob = AfpAllocZeroedNonPagedMemory(AddrCount * DSI_NETWORK_ADDR_LEN); if (AddrBlob == NULL) { RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetIpAddrBlob: malloc failed\n")); return(STATUS_INSUFFICIENT_RESOURCES); } // // create a "blob" that AfpSetServerStatus can directly copy // TmpCount = 0; pCurrentBlob = AddrBlob; pList = DsiIpAddrList.Flink; while (pList != &DsiIpAddrList) { pIpaddrEntity = CONTAINING_RECORD(pList, IPADDRENTITY, Linkage); pCurrentBlob[0] = DSI_NETWORK_ADDR_LEN; pCurrentBlob[1] = DSI_NETWORK_ADDR_IPTAG; PUTDWORD2DWORD(&pCurrentBlob[2], pIpaddrEntity->IpAddress); pCurrentBlob += DSI_NETWORK_ADDR_LEN; pList = pList->Flink; TmpCount++; if (TmpCount == AddrCount) { break; } } RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); *pIpAddrCount = AddrCount; *ppIpAddrBlob = AddrBlob; return(STATUS_SUCCESS); } /*** DsiGetIrpForTcp * * This routine is called when we need to pass an irp back to TCP to get the * remainint data (when it has more data than it has indicated to us). * Here we allocate an mdl if there isn't one already, and allocate and * initialize an irp ready to be sent to TCP * * Parm IN: pTcpConn - the connection object * pBuffer - buffer that TCP will copy data in * pInputMdl - if non-null, then we don't allocate a new mdl * ReadSize - how many bytes do we need * * Returns: pIrp if successful, NULL otherwise * * NOTE: pTcpConn spinlock is held on entry * */ PIRP DsiGetIrpForTcp( IN PTCPCONN pTcpConn, IN PBYTE pBuffer, IN PMDL pInputMdl, IN DWORD ReadSize ) { PDEVICE_OBJECT pDeviceObject; PIRP pIrp=NULL; PTDI_REQUEST_KERNEL_RECEIVE pRequest; PMDL pMdl; pDeviceObject = IoGetRelatedDeviceObject(pTcpConn->con_pFileObject); pIrp = AfpAllocIrp(pDeviceObject->StackSize); if (pIrp == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetIrpForTcp: alloc irp failed!\n")); return(NULL); } if (pInputMdl) { pMdl = pInputMdl; } else { pMdl = AfpAllocMdl(pBuffer, ReadSize, NULL); if (pMdl == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiGetIrpForTcp: alloc mdl failed!\n")); AfpFreeIrp(pIrp); return(NULL); } } pTcpConn->con_pRcvIrp = pIrp; pTcpConn->con_State |= TCPCONN_STATE_TCP_HAS_IRP; // put TcpIRP refcount, removed when the irp completes pTcpConn->con_RefCount++; DBGREFCOUNT(("DsiGetIrpForTcp: TcpIRP inc %lx (%d %d,%d)\n", pTcpConn,pTcpConn->con_RefCount,pTcpConn->con_State,pTcpConn->con_RcvState)); TdiBuildReceive(pIrp, pDeviceObject, pTcpConn->con_pFileObject, DsiTcpRcvIrpCompletion, (PVOID)pTcpConn, pMdl, TDI_RECEIVE_NORMAL, ReadSize); // // this irp will be returned to TCP, so do what IoSubSystem // would have done if we had called IoCallDriver // IoSetNextIrpStackLocation(pIrp); return(pIrp); } /*** DsiMakePartialMdl * * This routine is called when we need to reissue an Mdl (via irp) back to TCP * because TCP prematurely completed the previous irp (i.e. all the requested * bytes haven't come in yet, but say Push bit was set or something). In such * a case, we need to give a new mdl which accounts for the bytes we have got * so far (i.e. the offset has changed) * * Parm IN: pOrgMdl - the original Mdl we gave to TCp * dwOffset - what offset we want the new partial Mdl to describe * * Returns: the new partial Mdl if successful, NULL otherwise * */ PMDL DsiMakePartialMdl( IN PMDL pOrgMdl, IN DWORD dwOffset ) { PMDL pSubMdl; PMDL pPartialMdl=NULL; DWORD dwNewMdlLen; PVOID vAddr; ASSERT(pOrgMdl != NULL); ASSERT(dwOffset > 0); ASSERT(dwOffset < AfpMdlChainSize(pOrgMdl)); pSubMdl = pOrgMdl; // // get to the Mdl that is going to have this offset // while (dwOffset >= MmGetMdlByteCount(pSubMdl)) { dwOffset -= MmGetMdlByteCount(pSubMdl); pSubMdl = pSubMdl->Next; ASSERT(pSubMdl != NULL); } ASSERT(MmGetMdlByteCount(pSubMdl) > dwOffset); vAddr = (PVOID)((PUCHAR)MmGetMdlVirtualAddress( pSubMdl ) + dwOffset); dwNewMdlLen = MmGetMdlByteCount(pSubMdl) - dwOffset; pPartialMdl = IoAllocateMdl(vAddr, dwNewMdlLen, FALSE, FALSE, NULL); if (pPartialMdl == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiMakePartialMdl: IoAllocateMdl failed\n")); return(NULL); } AFP_DBG_INC_COUNT(AfpDbgMdlsAlloced); IoBuildPartialMdl(pSubMdl, pPartialMdl, vAddr, dwNewMdlLen); // if there are further Mdl's down the original, link them on pPartialMdl->Next = pSubMdl->Next; return(pPartialMdl); } /*** DsiUpdateAfpStatus * * This routine is just a wrapper function so that we can schedule an event to * call the real function AfpSetServerStatus * * Returns: status of operation * */ NTSTATUS FASTCALL DsiUpdateAfpStatus( IN PVOID Unused ) { NTSTATUS status; status = AfpSetServerStatus(); return(status); } /*** DsiScheduleWorkerEvent * * This routine schedules an event for a later time. This routine is called * typically when we are at dpc but something (e.g. file handle operations) * needs to be done at passive level. This routine puts the request on the * worker queue. * * Parm IN: WorkerRoutine - the routine to be exececuted by the worker thread * Context - parameter for that routine * * Returns: result of the operation * */ NTSTATUS DsiScheduleWorkerEvent( IN DSI_WORKER WorkerRoutine, IN PVOID Context ) { PTCPWORKITEM pTWItem; pTWItem = (PTCPWORKITEM)AfpAllocZeroedNonPagedMemory(sizeof(TCPWORKITEM)); if (pTWItem == NULL) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiScheduleWorkerEvent: alloc failed!\n")); return(STATUS_INSUFFICIENT_RESOURCES); } AfpInitializeWorkItem(&pTWItem->tcp_WorkItem, DsiWorker, pTWItem); pTWItem->tcp_Worker = WorkerRoutine; pTWItem->tcp_Context = Context; AfpQueueWorkItem(&pTWItem->tcp_WorkItem); return(STATUS_SUCCESS); } /*** DsiScheduleWorkerEvent * * This routine gets called by the worker thread when DsiScheduleWorkerEvent * schedules an event. This routine then calls the actual routine that was * scheduled for later time. * * Parm IN: Context - the work item * * Returns: result of the operation * */ VOID FASTCALL DsiWorker( IN PVOID Context ) { PTCPWORKITEM pTWItem; NTSTATUS status; pTWItem = (PTCPWORKITEM)Context; ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL); (*pTWItem->tcp_Worker)(pTWItem->tcp_Context); AfpFreeMemory(pTWItem); } /*** DsiShutdown * * This routine is called when sfm is shutting down. We basically make sure * that all the resources are freed up, file handles closed etc. * * Returns: Nothing * */ VOID DsiShutdown( IN VOID ) { KIRQL OldIrql; PLIST_ENTRY pList; PIPADDRENTITY pIpaddrEntity; PDSIREQ pDsiReq=NULL; ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql); while (!IsListEmpty(&DsiIpAddrList)) { pList = RemoveHeadList(&DsiIpAddrList); pIpaddrEntity = CONTAINING_RECORD(pList, IPADDRENTITY, Linkage); AfpFreeMemory(pIpaddrEntity); } if (DsiStatusBuffer != NULL) { AfpFreeMemory(DsiStatusBuffer); DsiStatusBuffer = NULL; } RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql); // kill the global adapter if it's around if (DsiTcpAdapter) { KeClearEvent(&DsiShutdownEvent); DsiDestroyAdapter(); // if the "adapter" is still hanging around, wait till it's gone if (DsiTcpAdapter) { DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiShutdown: waiting for the TCP interface to go away...\n")); AfpIoWait(&DsiShutdownEvent, NULL); DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR, ("DsiShutdown: ... and the wait is over!\n")); } } ACQUIRE_SPIN_LOCK(&DsiResourceLock, &OldIrql); while (!IsListEmpty(&DsiFreeRequestList)) { pList = RemoveHeadList(&DsiFreeRequestList); pDsiReq = CONTAINING_RECORD(pList, DSIREQ, dsi_Linkage); AfpFreeMemory(pDsiReq); DsiFreeRequestListSize--; } RELEASE_SPIN_LOCK(&DsiResourceLock, OldIrql); ASSERT(DsiFreeRequestListSize == 0); }