windows-nt/Source/XPSP1/NT/net/sfm/afp/server/tcputil.c
2020-09-26 16:20:57 +08:00

1616 lines
39 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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 <afp.h>
#include <scavengr.h>
/*** 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; i<DSI_INIT_FREECONNLIST_SIZE; i++)
{
DsiScheduleWorkerEvent(DsiCreateTcpConn, pTcpAdptr);
}
ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql);
AfpServerBoundToTcp = TRUE;
RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql);
// start off tickle timer (monitor all connections to see who needs a tickle)
AfpScavengerScheduleEvent(DsiSendTickles, NULL, DSI_TICKLE_TIMER, False);
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR,("AFP/TCP bound and ready\n"));
//
// if we came this far, all went well: return success
//
return(STATUS_SUCCESS);
//
// Error path
//
DsiCreateAdapter_ErrExit:
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR,
("DsiCreateAdapter: couldn't create global adapter (%lx)\n",status));
ASSERT(0);
if (status != STATUS_ADDRESS_ALREADY_EXISTS)
{
ACQUIRE_SPIN_LOCK(&DsiAddressLock, &OldIrql);
DsiTcpAdapter = NULL;
RELEASE_SPIN_LOCK(&DsiAddressLock, OldIrql);
}
if (pTcpAdptr)
{
AfpFreeMemory(pTcpAdptr);
}
return(status);
}
/*** DsiCreateTcpConn
*
* This routine creates a connection object, creates a tdi connection for it
* and associates it with the tdi address object for the AFP port, and finally
* puts it on the free connections list of the adapter in question.
*
* Parm IN: pTcpAdptr - adapter context
*
* Returns: status of operation
*
*/
NTSTATUS FASTCALL
DsiCreateTcpConn(
IN PTCPADPTR pTcpAdptr
)
{
PTCPCONN pTcpConn;
NTSTATUS status;
KIRQL OldIrql;
ASSERT(pTcpAdptr->adp_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);
}