windows-nt/Source/XPSP1/NT/net/sfm/atalk/sys/asp.c
2020-09-26 16:20:57 +08:00

2778 lines
68 KiB
C
Raw Permalink 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) 1992 Microsoft Corporation
Module Name:
asp.c
Abstract:
This module implements the ASP protocol.
Author:
Jameel Hyder (jameelh@microsoft.com)
Nikhil Kamkolkar (nikhilk@microsoft.com)
Revision History:
30 Mar 1993 Initial Version
Notes: Tab stop: 4
--*/
#include <atalk.h>
#pragma hdrstop
#define FILENUM ASP
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, AtalkInitAspInitialize)
#pragma alloc_text(PAGE, AtalkAspCreateAddress)
#pragma alloc_text(PAGE_ASP, AtalkAspCloseAddress)
#pragma alloc_text(PAGE_ASP, AtalkAspSetStatus)
#pragma alloc_text(PAGE_ASP, AtalkAspListenControl)
#pragma alloc_text(PAGE_ASP, AtalkAspCloseConnection)
#pragma alloc_text(PAGE_ASP, AtalkAspFreeConnection)
#pragma alloc_text(PAGE_ASP, AtalkAspCleanupConnection)
#pragma alloc_text(PAGE_ASP, AtalkAspWriteContinue)
#pragma alloc_text(PAGE_ASP, AtalkAspReply)
#pragma alloc_text(PAGE_ASP, atalkAspPostWriteContinue)
#pragma alloc_text(PAGE_ASP, AtalkAspSendAttention)
#pragma alloc_text(PAGE_ASP, AtalkAspReferenceAddr)
#pragma alloc_text(PAGE_ASP, atalkAspReferenceConnBySrcAddr)
#pragma alloc_text(PAGE_ASP, AtalkAspDereferenceConn)
#pragma alloc_text(PAGE_ASP, atalkAspSlsXHandler)
#pragma alloc_text(PAGE_ASP, atalkAspSssXHandler)
#pragma alloc_text(PAGE_ASP, atalkAspReplyRelease)
#pragma alloc_text(PAGE_ASP, atalkAspWriteContinueResp)
#pragma alloc_text(PAGE_ASP, atalkAspSendAttentionResp)
#pragma alloc_text(PAGE_ASP, atalkAspSessionClose)
#pragma alloc_text(PAGE_ASP, atalkAspReturnResp)
#pragma alloc_text(PAGE_ASP, atalkAspRespComplete)
#pragma alloc_text(PAGE_ASP, atalkAspCloseComplete)
#endif
/*
* The model for ASP calls in this module is as follows:
*
* - For create calls (CreateAddress & CreateSession), a pointer to the created
* object is returned. This structure is referenced for creation.
* - For all other calls, it expects a referenced pointer to the object.
*/
VOID
AtalkInitAspInitialize(
VOID
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
LONG i;
INITIALIZE_SPIN_LOCK(&atalkAspLock);
for (i = 0; i < NUM_ASP_CONN_LISTS; i++)
{
AtalkTimerInitialize(&atalkAspConnMaint[i].ascm_SMTTimer,
atalkAspSessionMaintenanceTimer,
(SHORT)(ASP_SESSION_MAINTENANCE_TIMER - i*ASP_SESSION_TIMER_STAGGER));
AtalkTimerScheduleEvent(&atalkAspConnMaint[i].ascm_SMTTimer);
}
}
ATALK_ERROR
AtalkAspCreateAddress(
OUT PASP_ADDROBJ * ppAspAddr
)
/*++
Routine Description:
Create an ASP address object (aka listener). This object is associated with
two seperate Atp sockets, one each for the Sls and the Sss. The Sls accepts
the tickle, getstatus and opensession requests from the client end. The
Sss accepts requests.
Currently only the server side ASP is implemented and hence the ASP address
object is only a listener.
Arguments:
Return Value:
--*/
{
PASP_ADDROBJ pAspAddr = NULL;
ATALK_ERROR Status;
int i;
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspCreateAddr: Entered\n"));
do
{
// Allocate memory for the Asp address object
if ((pAspAddr = AtalkAllocZeroedMemory(sizeof(ASP_ADDROBJ))) == NULL)
{
Status = ATALK_RESR_MEM;
break;
}
// Initialize the Asp address object
#if DBG
pAspAddr->aspao_Signature = ASPAO_SIGNATURE;
#endif
INITIALIZE_SPIN_LOCK(&pAspAddr->aspao_Lock);
// Refcounts for creation, Sls & Sss sockets and request handlers
pAspAddr->aspao_RefCount = 1 + 2 + 2;
pAspAddr->aspao_NextSessionId = 1;
pAspAddr->aspao_EnableNewConnections = TRUE;
// Create an Atp Socket on the port for the Sls
Status = AtalkAtpOpenAddress(AtalkDefaultPort,
0,
NULL,
ATP_DEF_MAX_SINGLE_PKT_SIZE,
ATP_DEF_SEND_USER_BYTES_ALL,
NULL,
TRUE, // CACHE this address
&pAspAddr->aspao_pSlsAtpAddr);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspCreateAddress: AtalkAtpOpenAddress for Sls failed %ld\n", Status));
break;
}
// Set Request handler for the SLS to handle GetStatus, OpenSession and Tickle
AtalkAtpSetReqHandler(pAspAddr->aspao_pSlsAtpAddr,
atalkAspSlsXHandler,
pAspAddr);
// Create the Atp Socket on the port for the Sss
Status = AtalkAtpOpenAddress(AtalkDefaultPort,
0,
NULL,
ATP_DEF_MAX_SINGLE_PKT_SIZE,
ATP_DEF_SEND_USER_BYTES_ALL,
NULL,
TRUE, // CACHE this address
&pAspAddr->aspao_pSssAtpAddr);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspCreateAddress: AtalkAtpOpenAddress for Sss failed %ld\n", Status));
break;
}
// Set Request handler for the SSS to handle Cmd/Write/Close
AtalkAtpSetReqHandler(pAspAddr->aspao_pSssAtpAddr,
atalkAspSssXHandler,
pAspAddr);
} while (FALSE);
if (!ATALK_SUCCESS(Status))
{
if (pAspAddr != NULL)
{
if (pAspAddr->aspao_pSlsAtpAddr != NULL)
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspCreateAddress: Closing SLS Atp Address %lx\n",
pAspAddr->aspao_pSlsAtpAddr));
AtalkAtpCloseAddress(pAspAddr->aspao_pSlsAtpAddr, NULL, NULL);
}
if (pAspAddr->aspao_pSssAtpAddr != NULL)
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspCreateAddress: Closing SSS Atp Address %lx\n",
pAspAddr->aspao_pSssAtpAddr));
AtalkAtpCloseAddress(pAspAddr->aspao_pSssAtpAddr, NULL, NULL);
}
AtalkFreeMemory(pAspAddr);
}
}
else
{
*ppAspAddr = pAspAddr;
}
return Status;
}
ATALK_ERROR
AtalkAspCloseAddress(
IN PASP_ADDROBJ pAspAddr,
IN GENERIC_COMPLETION CompletionRoutine,
IN PVOID CloseContext
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PASP_CONNOBJ pAspConn;
KIRQL OldIrql;
int i;
ATALK_ERROR Status = ATALK_PENDING;
PBYTE pStatusBuf;
ASSERT(VALID_ASPAO(pAspAddr));
ASSERT(pAspAddr->aspao_RefCount > 1);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspCloseAddr: Entered for Addr %lx\n", pAspAddr));
ACQUIRE_SPIN_LOCK(&pAspAddr->aspao_Lock, &OldIrql);
pAspAddr->aspao_Flags |= ASPAO_CLOSING;
pAspAddr->aspao_CloseCompletion = CompletionRoutine;
pAspAddr->aspao_CloseContext = CloseContext;
pStatusBuf = pAspAddr->aspao_pStatusBuf;
pAspAddr->aspao_pStatusBuf = NULL;
RELEASE_SPIN_LOCK(&pAspAddr->aspao_Lock, OldIrql);
// Close down the atp sockets for Sls and Sss
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("AtalkAspCloseAddress: Closing SLS Atp Address %lx\n",
pAspAddr->aspao_pSlsAtpAddr));
AtalkAtpCloseAddress(pAspAddr->aspao_pSlsAtpAddr,
atalkAspCloseComplete,
pAspAddr);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("AtalkAspCloseAddress: Closing SSS Atp Address %lx\n",
pAspAddr->aspao_pSssAtpAddr));
AtalkAtpCloseAddress(pAspAddr->aspao_pSssAtpAddr,
atalkAspCloseComplete,
pAspAddr);
// Free the status buffer if any
if (pStatusBuf != NULL)
{
AtalkFreeMemory(pStatusBuf);
}
// Shut down the active sessions now.
for (i = 0; i < ASP_CONN_HASH_BUCKETS; i++)
{
ACQUIRE_SPIN_LOCK(&pAspAddr->aspao_Lock, &OldIrql);
pAspConn = pAspAddr->aspao_pSessions[i];
while (pAspConn != NULL)
{
ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
// if we have visited this guy, skip it
if (pAspConn->aspco_Flags & ASPCO_SHUTDOWN)
{
RELEASE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspCloseAddress: VISITED: skipping conn %lx Flags %lx RefCount %d\n",
pAspConn,pAspConn->aspco_Flags,pAspConn->aspco_RefCount));
// we still have the pAspAddr->aspao_Lock spinlock held!
pAspConn = pAspConn->aspco_NextOverflow;
continue;
}
pAspConn->aspco_Flags |= (ASPCO_LOCAL_CLOSE | ASPCO_SHUTDOWN);
// Reference this since atalkAspSessionClose() expects it.
pAspConn->aspco_RefCount ++;
RELEASE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
RELEASE_SPIN_LOCK(&pAspAddr->aspao_Lock, OldIrql);
atalkAspSessionClose(pAspConn);
ACQUIRE_SPIN_LOCK(&pAspAddr->aspao_Lock, &OldIrql);
}
RELEASE_SPIN_LOCK(&pAspAddr->aspao_Lock, OldIrql);
}
ASSERT(KeGetCurrentIrql() == LOW_LEVEL);
// Let remaining cleanup happen during the Derefernce
AtalkAspDereferenceAddr(pAspAddr); // Remove the creation reference
return Status;
}
ATALK_ERROR
AtalkAspBind(
IN PASP_ADDROBJ pAspAddr,
IN PASP_BIND_PARAMS pBindParms,
IN PACTREQ pActReq
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
ASSERT (VALID_ASPAO(pAspAddr));
// copy network addr
ACQUIRE_SPIN_LOCK(&pAspAddr->aspao_Lock, &OldIrql);
pBindParms->pXportEntries->asp_AtalkAddr.Network =
pAspAddr->aspao_pSlsAtpAddr->atpao_DdpAddr->ddpao_Addr.ata_Network;
pBindParms->pXportEntries->asp_AtalkAddr.Node =
pAspAddr->aspao_pSlsAtpAddr->atpao_DdpAddr->ddpao_Addr.ata_Node;
pBindParms->pXportEntries->asp_AtalkAddr.Socket =
pAspAddr->aspao_pSlsAtpAddr->atpao_DdpAddr->ddpao_Addr.ata_Socket;
RELEASE_SPIN_LOCK(&pAspAddr->aspao_Lock, OldIrql);
// Fill in our entry points into the client buffer
pBindParms->pXportEntries->asp_AspCtxt = pAspAddr;
pBindParms->pXportEntries->asp_SetStatus = AtalkAspSetStatus;
pBindParms->pXportEntries->asp_CloseConn = AtalkAspCloseConnection;
pBindParms->pXportEntries->asp_FreeConn = AtalkAspFreeConnection;
pBindParms->pXportEntries->asp_ListenControl = AtalkAspListenControl;
pBindParms->pXportEntries->asp_WriteContinue = AtalkAspWriteContinue;
pBindParms->pXportEntries->asp_Reply = AtalkAspReply;
pBindParms->pXportEntries->asp_SendAttention = AtalkAspSendAttention;
// Get the clients entry points
pAspAddr->aspao_ClientEntries = pBindParms->ClientEntries;
// Call the completion routine before returning.
(*pActReq->ar_Completion)(ATALK_NO_ERROR, pActReq);
return ATALK_PENDING;
}
NTSTATUS
AtalkAspSetStatus(
IN PASP_ADDROBJ pAspAddr,
IN PUCHAR pStatusBuf,
IN USHORT StsBufSize
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
NTSTATUS Status = STATUS_SUCCESS;
PUCHAR pOldBuf = NULL, pNewBuf = NULL;
ASSERT(VALID_ASPAO(pAspAddr));
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspSetStatus: Entered for Addr %lx\n", pAspAddr));
do
{
if (pStatusBuf != NULL)
{
// Allocate a buffer and copy the contents of the passed in
// buffer descriptor in it. Free an existing status buffer if one exists
if (StsBufSize >= ASP_MAX_STATUS_SIZE)
{
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
if ((pNewBuf = AtalkAllocMemory(StsBufSize)) == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
RtlCopyMemory(pNewBuf, pStatusBuf, StsBufSize);
}
ACQUIRE_SPIN_LOCK(&pAspAddr->aspao_Lock, &OldIrql);
if (pAspAddr->aspao_pStatusBuf != NULL)
{
ASSERT(pAspAddr->aspao_StsBufSize != 0);
pOldBuf = pAspAddr->aspao_pStatusBuf;
}
pAspAddr->aspao_pStatusBuf = pNewBuf;
pAspAddr->aspao_StsBufSize = StsBufSize;
RELEASE_SPIN_LOCK(&pAspAddr->aspao_Lock, OldIrql);
if (pOldBuf != NULL)
AtalkFreeMemory(pOldBuf);
} while (FALSE);
return Status;
}
NTSTATUS FASTCALL
AtalkAspListenControl(
IN PASP_ADDROBJ pAspAddr,
IN BOOLEAN Enable
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
if (AtalkAspReferenceAddr(pAspAddr))
{
ACQUIRE_SPIN_LOCK(&pAspAddr->aspao_Lock, &OldIrql);
pAspAddr->aspao_EnableNewConnections = Enable;
RELEASE_SPIN_LOCK(&pAspAddr->aspao_Lock, OldIrql);
AtalkAspDereferenceAddr(pAspAddr);
Status = STATUS_SUCCESS;
}
return Status;
}
NTSTATUS
AtalkAspCloseConnection(
IN PASP_CONNOBJ pAspConn
)
/*++
Routine Description:
Shutdown a session.
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
BOOLEAN CompListen = FALSE;
ASSERT(VALID_ASPCO(pAspConn));
ASSERT(pAspConn->aspco_RefCount > 0);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspCloseConn: Entered for Conn %lx\n", pAspConn));
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("AtalkCloseConnection: Close session from %d.%d for Session %d\n",
pAspConn->aspco_WssRemoteAddr.ata_Network,
pAspConn->aspco_WssRemoteAddr.ata_Node,
pAspConn->aspco_SessionId));
AtalkAspCleanupConnection(pAspConn);
ACQUIRE_SPIN_LOCK(&pAspConn->aspco_Lock, &OldIrql);
pAspConn->aspco_Flags |= (ASPCO_CLOSING | ASPCO_LOCAL_CLOSE);
RELEASE_SPIN_LOCK(&pAspConn->aspco_Lock, OldIrql);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspCloseConnection: Done for %lx (%ld)\n",
pAspConn, pAspConn->aspco_RefCount));
// Let remaining cleanup happen during the Derefernce
AtalkAspDereferenceConn(pAspConn); // Remove the creation reference
#ifdef PROFILING
INTERLOCKED_DECREMENT_LONG( &AtalkStatistics.stat_CurAspSessions,
&AtalkStatsLock.SpinLock);
#endif
return STATUS_PENDING;
}
NTSTATUS
AtalkAspFreeConnection(
IN PASP_CONNOBJ pAspConn
)
/*++
Routine Description:
Shutdown a session.
Arguments:
Return Value:
--*/
{
return STATUS_SUCCESS;
}
ATALK_ERROR
AtalkAspCleanupConnection(
IN PASP_CONNOBJ pAspConn
)
/*++
Routine Description:
Cancel all I/O on this session. Complete pending replies and write
continues with error.
Arguments:
Return Value:
--*/
{
PASP_REQUEST pAspReq;
PASP_ADDROBJ pAspAddr;
ATALK_ERROR Status;
KIRQL OldIrql;
USHORT XactId;
BYTE UserBytes[ATP_USERBYTES_SIZE];
ATALK_ADDR RemoteAddr;
BOOLEAN CancelTickle, AlreadyCleaning, fConnActive;
ATALK_ERROR error;
ASSERT(VALID_ASPCO(pAspConn));
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspCleanupConnection: For %lx\n", pAspConn));
ACQUIRE_SPIN_LOCK(&pAspConn->aspco_Lock, &OldIrql);
CancelTickle = ((pAspConn->aspco_Flags & ASPCO_TICKLING) != 0);
AlreadyCleaning = (pAspConn->aspco_Flags & ASPCO_CLEANING_UP) ? TRUE : FALSE;
if (AlreadyCleaning)
{
pAspConn->aspco_Flags &= ~ASPCO_TICKLING;
}
fConnActive = (pAspConn->aspco_Flags & ASPCO_ACTIVE) ? TRUE : FALSE;
pAspConn->aspco_Flags &= ~ASPCO_ACTIVE;
pAspConn->aspco_Flags |= ASPCO_CLEANING_UP;
RELEASE_SPIN_LOCK(&pAspConn->aspco_Lock, OldIrql);
if (AlreadyCleaning)
return ATALK_NO_ERROR;
pAspAddr = pAspConn->aspco_pAspAddr;
ASSERT(VALID_ASPAO(pAspAddr));
// Send a session close request, if this is an active connection
if (fConnActive)
{
UserBytes[ASP_CMD_OFF] = ASP_CLOSE_SESSION;
UserBytes[ASP_SESSIONID_OFF] = pAspConn->aspco_SessionId;
PUTSHORT2SHORT(UserBytes + ASP_ATTN_WORD_OFF, 0);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspCleanupConnection: Sending close req for %lx\n",
pAspConn));
Status = AtalkAtpPostReq(pAspAddr->aspao_pSssAtpAddr,
&pAspConn->aspco_WssRemoteAddr,
&XactId,
ATP_REQ_REMOTE, // Close session request is ALO
NULL,
0,
UserBytes,
NULL,
0,
ATP_RETRIES_FOR_ASP,
ATP_MAX_INTERVAL_FOR_ASP,
THIRTY_SEC_TIMER,
NULL,
NULL);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspCleanupConn: AtalkAtpPostReq %ld\n", Status));
}
}
// Cancel tickle packets
if (CancelTickle)
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspCleanupConnection: Cancel tickle for %lx\n", pAspConn));
Status = AtalkAtpCancelReq(pAspAddr->aspao_pSlsAtpAddr,
pAspConn->aspco_TickleXactId,
&pAspConn->aspco_WssRemoteAddr);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspCleanupConn: AtalkAtpCancelReq %ld\n", Status));
}
}
do
{
BOOLEAN CancelReply = FALSE;
ACQUIRE_SPIN_LOCK(&pAspConn->aspco_Lock, &OldIrql);
for (pAspReq = pAspConn->aspco_pActiveReqs;
pAspReq != NULL;
pAspReq = pAspReq->asprq_Next)
{
ASSERT (VALID_ASPRQ(pAspReq));
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspCleanupConnection: Found req %lx (%lx) for %lx\n",
pAspReq, pAspReq->asprq_Flags, pAspConn));
CancelReply = FALSE;
if ((pAspReq->asprq_Flags & (ASPRQ_WRTCONT | ASPRQ_WRTCONT_CANCELLED)) == ASPRQ_WRTCONT)
{
pAspReq->asprq_Flags |= ASPRQ_WRTCONT_CANCELLED;
RemoteAddr = pAspConn->aspco_WssRemoteAddr;
break;
}
if ((pAspReq->asprq_Flags & (ASPRQ_REPLY | ASPRQ_REPLY_CANCELLED)) == ASPRQ_REPLY)
{
CancelReply = TRUE;
pAspReq->asprq_Flags |= ASPRQ_REPLY_CANCELLED;
break;
}
}
RELEASE_SPIN_LOCK(&pAspConn->aspco_Lock, OldIrql);
if (pAspReq != NULL)
{
if (CancelReply)
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspCleanupConnection: Cancel reply for %lx, flag=%lx\n",
pAspReq,pAspReq->asprq_Flags));
error = AtalkAtpCancelResp(pAspReq->asprq_pAtpResp);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspCleanupConnection: AtalkAtpCancelResp failed %lx\n",error));
}
}
else
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspCleanupConnection: Cancel wrtcont for %lx, flag=%lx\n",
pAspReq,pAspReq->asprq_Flags));
error = AtalkAtpCancelReq(pAspConn->aspco_pAspAddr->aspao_pSssAtpAddr,
pAspReq->asprq_WCXactId,
&RemoteAddr);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspCleanupConnection: AtalkAtpCancelReq failed %lx\n",error));
}
}
}
else
{
break;
}
} while (TRUE);
return ATALK_NO_ERROR;
}
NTSTATUS FASTCALL
AtalkAspWriteContinue(
IN PREQUEST pRequest
)
/*++
Routine Description:
The response buffer is in the request itself.
Arguments:
Return Value:
--*/
{
PASP_REQUEST pAspReq;
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("AtalkAspWriteContinue: Entered with pRequest %lx\n", pRequest));
pAspReq = CONTAINING_RECORD(pRequest, ASP_REQUEST, asprq_Request);
ASSERT (VALID_ASPRQ(pAspReq));
if (pRequest->rq_WriteMdl != NULL)
{
atalkAspPostWriteContinue(pAspReq);
return(STATUS_SUCCESS);
}
else
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspWriteContinue: buffer alloc failed, completing write with error\n"));
atalkAspWriteContinueResp(ATALK_RESR_MEM, pAspReq, NULL, NULL, 0, NULL);
}
return(STATUS_SUCCESS);
}
NTSTATUS FASTCALL
AtalkAspReply(
IN PREQUEST pRequest, // Pointer to request
IN PBYTE pResultCode // Pointer to the 4-byte result
)
/*++
Routine Description:
The response buffer is in the request itself.
Arguments:
Return Value:
--*/
{
PASP_REQUEST pAspReq, *ppAspReq;
PASP_CONNOBJ pAspConn;
PASP_ADDROBJ pAspAddr;
ATALK_ERROR error;
KIRQL OldIrql;
USHORT ReplySize;
pAspReq = CONTAINING_RECORD(pRequest, ASP_REQUEST, asprq_Request);
ASSERT (VALID_ASPRQ(pAspReq));
pAspConn = pAspReq->asprq_pAspConn;
ASSERT(VALID_ASPCO(pAspConn));
pAspAddr = pAspConn->aspco_pAspAddr;
ASSERT(VALID_ASPAO(pAspAddr));
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspReply: Entered for session %lx\n", pAspConn));
ASSERT ((pAspReq->asprq_Flags & (ASPRQ_WRTCONT | ASPRQ_REPLY)) == 0);
do
{
// Find and de-queue this request from the list
ACQUIRE_SPIN_LOCK(&pAspConn->aspco_Lock, &OldIrql);
for (ppAspReq = &pAspConn->aspco_pActiveReqs;
*ppAspReq != NULL;
ppAspReq = &(*ppAspReq)->asprq_Next)
{
if (pAspReq == *ppAspReq)
{
*ppAspReq = pAspReq->asprq_Next;
pAspConn->aspco_cReqsInProcess --;
pAspReq->asprq_Flags |= ASPRQ_REPLY;
break;
}
}
ASSERT(*ppAspReq == pAspReq->asprq_Next);
if (pAspConn->aspco_Flags & (ASPCO_CLEANING_UP |
ASPCO_CLOSING |
ASPCO_LOCAL_CLOSE |
ASPCO_REMOTE_CLOSE))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspReply: Session Closing - session %x\n", pAspConn->aspco_SessionId));
RELEASE_SPIN_LOCK(&pAspConn->aspco_Lock, OldIrql);
pAspReq->asprq_Flags &= ~ASPRQ_REPLY;
pAspReq->asprq_Flags |= ASPRQ_REPLY_ABORTED;
error = ATALK_LOCAL_CLOSE;
break;
}
RELEASE_SPIN_LOCK(&pAspConn->aspco_Lock, OldIrql);
ReplySize = (USHORT)AtalkSizeMdlChain(pAspReq->asprq_Request.rq_ReplyMdl);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspReply: Posting AtalkAtpPostResp for request %lx\n", pAspReq));
error = AtalkAtpPostResp(pAspReq->asprq_pAtpResp,
&pAspReq->asprq_RemoteAddr,
pAspReq->asprq_Request.rq_ReplyMdl,
ReplySize,
pResultCode,
atalkAspReplyRelease,
pAspReq);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspReply: AtalkAtpPostResp %ld\n", error));
}
} while (FALSE);
if (!ATALK_SUCCESS(error))
{
if (error != ATALK_ATP_RESP_TOOMANY)
{
atalkAspReplyRelease(error, pAspReq);
}
}
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspReply: Completing request %lx, Status %ld\n",
pAspReq, error));
return STATUS_PENDING;
}
LOCAL ATALK_ERROR FASTCALL
atalkAspPostWriteContinue(
IN PASP_REQUEST pAspReq
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ATALK_ERROR error;
PASP_CONNOBJ pAspConn;
PAMDL pAMdl = NULL;
BYTE UserBytes[ATP_USERBYTES_SIZE];
USHORT RespSize;
ASSERT (VALID_ASPRQ(pAspReq));
pAspConn = pAspReq->asprq_pAspConn;
ASSERT(VALID_ASPCO(pAspConn));
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspPostWriteContinue: Entered for session %lx\n", pAspConn));
RespSize = (USHORT)AtalkSizeMdlChain(pAspReq->asprq_Request.rq_WriteMdl);
ASSERT (RespSize <= ATP_MAX_TOTAL_RESPONSE_SIZE);
if (RespSize > ATP_MAX_TOTAL_RESPONSE_SIZE)
RespSize = ATP_MAX_TOTAL_RESPONSE_SIZE;
ASSERT (!(pAspReq->asprq_Flags & (ASPRQ_WRTCONT | ASPRQ_WRTCONT_CANCELLED)));
pAspReq->asprq_Flags |= ASPRQ_WRTCONT;
do
{
// We need to build an AMdl for two bytes of response which
// indicates how much data we are expecting !!!
if ((pAMdl = AtalkAllocAMdl(pAspReq->asprq_WrtContRespBuf,
ASP_WRITE_DATA_SIZE)) == NULL)
{
error = ATALK_RESR_MEM;
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspWriteContinue: AtalkAllocMdl failed for 2 bytes !!\n"));
}
else
{
PBYTE pWrtData;
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspWriteContinue: Posting AtalkAtpPostReq for request %lx\n",
pAspReq));
pWrtData = AtalkGetAddressFromMdlSafe(pAMdl, NormalPagePriority);
if (pWrtData == NULL)
{
if (pAMdl != NULL)
{
AtalkFreeAMdl(pAMdl);
}
error = ATALK_RESR_MEM;
break;
}
UserBytes[ASP_CMD_OFF] = ASP_WRITE_DATA;
UserBytes[ASP_SESSIONID_OFF] = pAspConn->aspco_SessionId;
PUTSHORT2SHORT(UserBytes+ASP_SEQUENCE_NUM_OFF, pAspReq->asprq_SeqNum);
PUTSHORT2SHORT(pWrtData, RespSize);
// Snapshot the current tick count. We use this to adjust the retry times on
// write continue.
pAspConn->aspco_RT.rt_New = AtalkGetCurrentTick();
error = AtalkAtpPostReq(pAspConn->aspco_pAspAddr->aspao_pSssAtpAddr,
&pAspConn->aspco_WssRemoteAddr,
&pAspReq->asprq_WCXactId,
ATP_REQ_EXACTLY_ONCE | ATP_REQ_REMOTE,
pAMdl,
ASP_WRITE_DATA_SIZE,
UserBytes,
pAspReq->asprq_Request.rq_WriteMdl,
RespSize,
ATP_INFINITE_RETRIES,
pAspConn->aspco_RT.rt_Base,
THIRTY_SEC_TIMER,
atalkAspWriteContinueResp,
pAspReq);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspWriteContinue: AtalkAtpPostReq %ld\n", error));
}
}
if (!ATALK_SUCCESS(error))
{
if (pAMdl != NULL)
AtalkFreeAMdl(pAMdl);
}
} while (FALSE);
return error;
}
NTSTATUS
AtalkAspSendAttention(
IN PASP_CONNOBJ pAspConn,
IN USHORT AttentionWord,
IN PVOID pContext
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ATALK_ERROR error;
NTSTATUS Status = STATUS_SUCCESS;
KIRQL OldIrql;
PAMDL pAMdl = NULL;
BYTE UserBytes[ATP_USERBYTES_SIZE];
USHORT XactId, RespSize = 16; // Some small number (see comment below)
ASSERT(VALID_ASPCO(pAspConn));
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspSendAttention: Entered for session %lx\n", pAspConn));
// Reference by src addr here instead of by pointer since the former will
// fail when the session is in one of the stages of death whereas the
// latter will not. Also this assumes that it is called at dispatch so raise irql.
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
pAspConn = atalkAspReferenceConnBySrcAddr(pAspConn->aspco_pAspAddr,
&pAspConn->aspco_WssRemoteAddr,
pAspConn->aspco_SessionId);
KeLowerIrql(OldIrql);
if (pAspConn == NULL)
return STATUS_REQUEST_NOT_ACCEPTED;
UserBytes[ASP_CMD_OFF] = ASP_ATTENTION;
UserBytes[ASP_SESSIONID_OFF] = pAspConn->aspco_SessionId;
PUTSHORT2SHORT(UserBytes+ASP_ATTN_WORD_OFF, AttentionWord);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspSendAttention: Posting AtalkAtpPostReq for Conn %lx\n", pAspConn));
// We need to build an AMdl for a dummy buffer to hold the response.
// There is no real response but some clients fry their
// machines if we don't !!! If we cannot allocate the mdl we we go
// ahead anyway.
if ((pAMdl = AtalkAllocAMdl(NULL, RespSize)) == NULL)
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspSendAttention: AtalkAllocMdl failed for dummy buffer !!\n"));
RespSize = 0;
}
pAspConn->aspco_AttentionContext = pContext;
error = AtalkAtpPostReq(pAspConn->aspco_pAspAddr->aspao_pSssAtpAddr,
&pAspConn->aspco_WssRemoteAddr,
&XactId,
ATP_REQ_REMOTE, // SendAttention is ALO
NULL,
0,
UserBytes,
pAMdl,
RespSize,
ATP_RETRIES_FOR_ASP,
ATP_MAX_INTERVAL_FOR_ASP,
THIRTY_SEC_TIMER,
atalkAspSendAttentionResp,
pAspConn);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspSendAttention: AtalkAtpPostReq %ld\n", Status));
Status = AtalkErrorToNtStatus(error);
atalkAspSendAttentionResp(error,
pAspConn,
NULL,
pAMdl,
RespSize,
UserBytes);
}
return Status;
}
PASP_ADDROBJ FASTCALL
AtalkAspReferenceAddr(
IN PASP_ADDROBJ pAspAddr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
PASP_ADDROBJ pRefAddr;
ASSERT(VALID_ASPAO(pAspAddr));
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspReferenceAddr: Addr %lx, PreCount %ld\n",
pAspAddr, pAspAddr->aspao_RefCount));
pRefAddr = pAspAddr;
ACQUIRE_SPIN_LOCK(&pAspAddr->aspao_Lock, &OldIrql);
ASSERT(pAspAddr->aspao_RefCount > 1);
if (pAspAddr->aspao_Flags & ASPAO_CLOSING)
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("AtalkAspReferenceAddr: Referencing closing object %lx!!\n",
pAspAddr));
pRefAddr = NULL;
}
else pAspAddr->aspao_RefCount ++;
RELEASE_SPIN_LOCK(&pAspAddr->aspao_Lock, OldIrql);
return pRefAddr;
}
VOID FASTCALL
AtalkAspDereferenceAddr(
IN PASP_ADDROBJ pAspAddr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
BOOLEAN Cleanup;
ASSERT(VALID_ASPAO(pAspAddr));
ASSERT (pAspAddr->aspao_RefCount > 0);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspDereferenceAddr: Addr %lx, PreCount %ld\n",
pAspAddr, pAspAddr->aspao_RefCount));
ACQUIRE_SPIN_LOCK(&pAspAddr->aspao_Lock, &OldIrql);
pAspAddr->aspao_RefCount --;
Cleanup = FALSE;
if (pAspAddr->aspao_RefCount == 0)
{
ASSERT (pAspAddr->aspao_Flags & ASPAO_CLOSING);
Cleanup = TRUE;
}
RELEASE_SPIN_LOCK(&pAspAddr->aspao_Lock, OldIrql);
// Check if this address object is history. Do all the processing needed to make this go
// away. When all is done, clear the event to signal close is complete
if (Cleanup)
{
// At this point we are sure that no active sessions exist on this
// address.
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("AtalkAspDereferenceAddr: Cleaning up addr %lx\n", pAspAddr));
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("AtalkAspDereferenceAddr: Indicating close for %lx\n", pAspAddr));
ASSERT(KeGetCurrentIrql() == LOW_LEVEL);
// Call the completion routine to indicate close is successfull
if (pAspAddr->aspao_CloseCompletion != NULL)
(*pAspAddr->aspao_CloseCompletion)(ATALK_NO_ERROR,
pAspAddr->aspao_CloseContext);
// Finally free the memory
AtalkFreeMemory(pAspAddr);
AtalkUnlockAspIfNecessary();
}
}
LOCAL PASP_CONNOBJ
atalkAspReferenceConnBySrcAddr(
IN PASP_ADDROBJ pAspAddr,
IN PATALK_ADDR pSrcAddr,
IN BYTE SessionId
)
/*++
Routine Description:
ASP has the concept of 8-bit session ids which uniquely identifies a
session on a listener. This effectively restricts the number of sessions
to 255 (0 is invalid). To eliminate the restriction, the following
strategy is used.
a, Atp is modified to isolate the transaction ids on a per addr basis
i.e. it monotonically increases for each <net,node,socket> combination.
b, We create session ids on a per <net,node> basis.
Given the following observed facts:
1, That macintoshes use the sockets starting from the top of the range.
2, Most network addresses have the same high byte - macintoshes tend
to start from the bottom of the range.
3, We allocate session ids starting from 1 and most (all) clients will
not have more than one session with us.
It does not make any sense to take either the socket, session id or the
high byte of the network number into account. That leaves only the low
byte of the network, and node id - a nice 16-bit number to hash.
Arguments:
Return Value:
--*/
{
PASP_CONNOBJ pAspConn, pRefConn = NULL;
int index;
ASSERT(VALID_ASPAO(pAspAddr));
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspReferenceConnBySrcAddr: Addr %lx, Source %x.%x SessionId %d\n",
pAspAddr, pSrcAddr->ata_Network, pSrcAddr->ata_Node, SessionId));
index = HASH_SRCADDR(pSrcAddr);
ACQUIRE_SPIN_LOCK_DPC(&pAspAddr->aspao_Lock);
for (pAspConn = pAspAddr->aspao_pSessions[index];
pAspConn != NULL;
pAspConn = pAspConn->aspco_NextOverflow)
{
if ((pSrcAddr->ata_Network == pAspConn->aspco_WssRemoteAddr.ata_Network) &&
(pSrcAddr->ata_Node == pAspConn->aspco_WssRemoteAddr.ata_Node) &&
(pAspConn->aspco_SessionId == SessionId))
{
ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
if ((pAspConn->aspco_Flags & (ASPCO_CLOSING |
ASPCO_CLEANING_UP |
ASPCO_LOCAL_CLOSE |
ASPCO_REMOTE_CLOSE)) == 0)
{
ASSERT(pAspConn->aspco_RefCount > 0);
pAspConn->aspco_RefCount ++;
pRefConn = pAspConn;
}
RELEASE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
break;
}
}
RELEASE_SPIN_LOCK_DPC(&pAspAddr->aspao_Lock);
return pRefConn;
}
VOID FASTCALL
AtalkAspDereferenceConn(
IN PASP_CONNOBJ pAspConn
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PASP_ADDROBJ pAspAddr = pAspConn->aspco_pAspAddr;
KIRQL OldIrql;
PASP_REQUEST pAspReq;
BOOLEAN Cleanup = FALSE;
ASSERT(VALID_ASPCO(pAspConn));
ASSERT (pAspConn->aspco_RefCount > 0);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("AtalkAspDereferenceConn: Conn %lx, PreCount %ld\n",
pAspConn, pAspConn->aspco_RefCount));
ACQUIRE_SPIN_LOCK(&pAspConn->aspco_Lock, &OldIrql);
pAspConn->aspco_RefCount --;
if (pAspConn->aspco_RefCount == 0)
{
Cleanup = TRUE;
}
RELEASE_SPIN_LOCK(&pAspConn->aspco_Lock, OldIrql);
if (!Cleanup)
{
return;
}
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("AtalkAspDereferenceConn: Last for %lx\n", pAspConn));
// The connection is all but dead. Perform the last rites. If its an
// active session that we're about to shut down, send a close notification
// to the other side. If it is a remote close, we've already responded to it.
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("AtalkAspDereferenceConn: Cleaning up Conn %lx\n", pAspConn));
ASSERT(VALID_ASPAO(pAspAddr));
// The connection is in one of the following states:
// a, Closed remotely - idle
// b, Active
//
// In either case it is in the hash bucket so unlink it
{
PASP_CONNOBJ * ppAspConn;
int index;
// The connection was active. This is linked into two different
// lists. Unlink from the hash table here and from the global
// list later.
ASSERT(pAspConn->aspco_pActiveReqs == NULL);
index = HASH_SRCADDR(&pAspConn->aspco_WssRemoteAddr);
ACQUIRE_SPIN_LOCK(&pAspAddr->aspao_Lock, &OldIrql);
for (ppAspConn = &pAspAddr->aspao_pSessions[index];
*ppAspConn != NULL;
ppAspConn = &(*ppAspConn)->aspco_NextOverflow)
{
if (pAspConn == *ppAspConn)
{
*ppAspConn = pAspConn->aspco_NextOverflow;
break;
}
}
RELEASE_SPIN_LOCK(&pAspAddr->aspao_Lock, OldIrql);
ASSERT (*ppAspConn == pAspConn->aspco_NextOverflow);
}
ACQUIRE_SPIN_LOCK(&atalkAspLock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
AtalkUnlinkDouble(pAspConn,
aspco_NextSession,
aspco_PrevSession)
// Free any requests on the free list
while ((pAspReq = pAspConn->aspco_pFreeReqs) != NULL)
{
pAspConn->aspco_pFreeReqs = pAspReq->asprq_Next;
AtalkBPFreeBlock(pAspReq);
}
RELEASE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
RELEASE_SPIN_LOCK(&atalkAspLock, OldIrql);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("AtalkAspDereferenceConn: Indicating close for Conn %lx\n", pAspConn));
// Call the completion routine to indicate close is successful.
(*pAspAddr->aspao_ClientEntries.clt_CloseCompletion)(STATUS_SUCCESS,
pAspConn->aspco_ConnContext);
// Now Dereference the address object, before we are history
AtalkAspDereferenceAddr(pAspAddr);
// Finally free the memory.
AtalkFreeMemory(pAspConn);
}
LOCAL VOID
atalkAspSlsXHandler(
IN ATALK_ERROR ErrorCode,
IN PASP_ADDROBJ pAspAddr, // Listener (our context)
IN PATP_RESP pAtpResp, // Atp Response context
IN PATALK_ADDR pSrcAddr, // Address of requestor
IN USHORT PktLen,
IN PBYTE pPkt,
IN PBYTE pUserBytes
)
/*++
Routine Description:
Handler for incoming requests on the Sls. It handles session opens, tickles
and get status on the session.
Arguments:
Return Value:
--*/
{
PASP_CONNOBJ pAspConn;
ATALK_ERROR Status;
PASP_POSTSTAT_CTX pStsCtx;
int index;
USHORT StsBufSize;
BYTE AspCmd, SessionId, StartId;
BOOLEAN fAddrRefed=FALSE;
if (!ATALK_SUCCESS(ErrorCode))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("atalkAspSlsXHandler: Error %ld\n", ErrorCode));
// Take away the reference on the Sls now that the atp address is closing
if (ErrorCode == ATALK_ATP_CLOSING)
AtalkAspDereferenceAddr(pAspAddr);
return;
}
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspSlsXHandler: Entered for Function %x from %x.%x\n",
pUserBytes[ASP_CMD_OFF], pSrcAddr->ata_Network, pSrcAddr->ata_Node));
switch (AspCmd = pUserBytes[ASP_CMD_OFF])
{
case ASP_OPEN_SESSION:
// Is the version number ok ?
if ((pUserBytes[ASP_VERSION_OFF] != ASP_VERSION[0]) ||
(pUserBytes[ASP_VERSION_OFF+1] != ASP_VERSION[1]))
{
atalkAspReturnResp( pAtpResp,
pSrcAddr,
0, // SSS
0, // SessionId
ASP_BAD_VERSION); // ErrorCode
break;
}
// Create a connection object corres. to this listen and then notify
// the client that it needs to handle a new session
// Allocate memory for a connection object
if ((pAspConn = AtalkAllocZeroedMemory(sizeof(ASP_CONNOBJ))) != NULL)
{
#if DBG
pAspConn->aspco_Signature = ASPCO_SIGNATURE;
#endif
INITIALIZE_SPIN_LOCK(&pAspConn->aspco_Lock);
pAspConn->aspco_RefCount = 1; // Creation reference
pAspConn->aspco_pAspAddr = pAspAddr; // Owning address object
AtalkInitializeRT(&pAspConn->aspco_RT,
ATP_INITIAL_INTERVAL_FOR_ASP,
ATP_MIN_INTERVAL_FOR_ASP,
ATP_MAX_INTERVAL_FOR_ASP);
}
ACQUIRE_SPIN_LOCK_DPC(&pAspAddr->aspao_Lock);
if (pAspConn != NULL)
{
PASP_CONNOBJ pTmp;
// Find a session id that we can use for this session. We use
// the next assignable id, if that is not in use. Otherwise we
// use the next id not in use by that session. In most cases
// we have only one session from any client.
index = HASH_SRCADDR(pSrcAddr);
// If we do not find any, we use this
SessionId = StartId = pAspAddr->aspao_NextSessionId++;
ASSERT (SessionId != 0);
if (pAspAddr->aspao_NextSessionId == 0)
pAspAddr->aspao_NextSessionId = 1;
for (pTmp = pAspAddr->aspao_pSessions[index];
pTmp != NULL;
NOTHING)
{
if ((pTmp->aspco_WssRemoteAddr.ata_Node == pSrcAddr->ata_Node) &&
(pTmp->aspco_WssRemoteAddr.ata_Network == pSrcAddr->ata_Network))
{
if (pTmp->aspco_SessionId == SessionId)
{
// if we have cycled through all, get out!
if (SessionId == (StartId - 1))
{
break;
}
SessionId ++;
if (SessionId == 0)
{
// all sessions are taken: quit here!
if (StartId == 1)
{
break;
}
SessionId = 1;
}
pTmp = pAspAddr->aspao_pSessions[index];
continue;
}
}
pTmp = pTmp->aspco_NextOverflow;
}
// if there are 255 sessions already from this address, then
// we can't have any more, sorry !!!
if (SessionId != (StartId - 1))
{
// Link it into the hash table
pAspAddr->aspao_RefCount ++;
fAddrRefed = TRUE;
pAspConn->aspco_SessionId = SessionId;
pAspConn->aspco_cReqsInProcess = 0;
pAspConn->aspco_WssRemoteAddr.ata_Address = pSrcAddr->ata_Address;
pAspConn->aspco_WssRemoteAddr.ata_Socket = pUserBytes[ASP_WSS_OFF];
pAspConn->aspco_LastContactTime = AtalkGetCurrentTick();
pAspConn->aspco_NextExpectedSeqNum = 0;
pAspConn->aspco_Flags |= (ASPCO_ACTIVE | ASPCO_TICKLING);
// The session should be linked *after* all of the above
// are initialized
pAspConn->aspco_NextOverflow = pAspAddr->aspao_pSessions[index];
pAspAddr->aspao_pSessions[index] = pAspConn;
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_CurAspSessions,
&AtalkStatsLock.SpinLock);
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_TotalAspSessions,
&AtalkStatsLock.SpinLock);
#endif
}
else
{
AtalkFreeMemory(pAspConn);
pAspConn = NULL;
}
}
RELEASE_SPIN_LOCK_DPC(&pAspAddr->aspao_Lock);
if (pAspConn != NULL)
{
BYTE Socket;
BYTE UserBytes[ATP_USERBYTES_SIZE];
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspSlsXHandler: Opening session from %d.%d for Session %d\n",
pSrcAddr->ata_Network, pSrcAddr->ata_Node, SessionId));
// Call the open completion routine and get a context. This is needed
// before we do anything else. Once we send out an open success it'll
// be too late.
// FALSE says this is not over TCP/IP
pAspConn->aspco_ConnContext =
(*pAspAddr->aspao_ClientEntries.clt_SessionNotify)(pAspConn,FALSE);
if (pAspConn->aspco_ConnContext != NULL)
{
// Now link the session into the global list
ACQUIRE_SPIN_LOCK_DPC(&atalkAspLock);
AtalkLinkDoubleAtHead(atalkAspConnMaint[SessionId & (NUM_ASP_CONN_LISTS-1)].ascm_ConnList,
pAspConn,
aspco_NextSession,
aspco_PrevSession)
RELEASE_SPIN_LOCK_DPC(&atalkAspLock);
// Send an open session response - XO
Socket = pAspAddr->aspao_pSssAtpAddr->atpao_DdpAddr->ddpao_Addr.ata_Socket;
atalkAspReturnResp( pAtpResp,
pSrcAddr,
Socket,
pAspConn->aspco_SessionId,
0); // Success
// Send a tickle out every ASP_TICKLE_INTERVAL seconds
UserBytes[ASP_CMD_OFF] = ASP_TICKLE;
UserBytes[ASP_SESSIONID_OFF] = pAspConn->aspco_SessionId;
PUTSHORT2SHORT(UserBytes + ASP_ERRORCODE_OFF, 0);
Status = AtalkAtpPostReq(pAspAddr->aspao_pSlsAtpAddr,
&pAspConn->aspco_WssRemoteAddr,
&pAspConn->aspco_TickleXactId,
ATP_REQ_REMOTE, // Tickle packets are ALO
NULL,
0,
UserBytes,
NULL,
0,
ATP_INFINITE_RETRIES,
ASP_TICKLE_INTERVAL,
THIRTY_SEC_TIMER,
NULL,
NULL);
if (!ATALK_SUCCESS(Status))
{
pAspConn->aspco_Flags &= ~ASPCO_TICKLING;
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSlsXHandler: AtalkAtpPostReq %ld\n", Status));
}
}
else
{
PASP_CONNOBJ * ppAspConn;
// Unlink it from the hash table
ACQUIRE_SPIN_LOCK_DPC(&pAspAddr->aspao_Lock);
for (ppAspConn = &pAspAddr->aspao_pSessions[index];
*ppAspConn != NULL;
ppAspConn = &(*ppAspConn)->aspco_NextOverflow)
{
if (*ppAspConn == pAspConn)
{
*ppAspConn = pAspConn->aspco_NextOverflow;
break;
}
}
ASSERT (*ppAspConn == pAspConn->aspco_NextOverflow);
RELEASE_SPIN_LOCK_DPC(&pAspAddr->aspao_Lock);
AtalkFreeMemory(pAspConn);
pAspConn = NULL;
#ifdef PROFILING
INTERLOCKED_DECREMENT_LONG_DPC(&AtalkStatistics.stat_CurAspSessions,
&AtalkStatsLock.SpinLock);
INTERLOCKED_DECREMENT_LONG_DPC(&AtalkStatistics.stat_TotalAspSessions,
&AtalkStatsLock.SpinLock);
#endif
}
}
// If we are set to disable listens or could not allocate memory, drop it
if (pAspConn == NULL)
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSlsXHandler: No conn objects available\n"));
atalkAspReturnResp( pAtpResp,
pSrcAddr,
0,
0,
ASP_SERVER_BUSY);
// remove that refcount if we put it in hoping afp would accept the request
if (fAddrRefed)
{
AtalkAspDereferenceAddr(pAspAddr);
}
}
break;
case ASP_GET_STATUS:
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspSssXHandler: Received GetStat from %x.%x\n",
pSrcAddr->ata_Network, pSrcAddr->ata_Node));
// Create an Mdl to describe the status buffer and post a response
// to the GetStatus request
StsBufSize = 0;
pStsCtx = NULL;
ACQUIRE_SPIN_LOCK_DPC(&pAspAddr->aspao_Lock);
if (pAspAddr->aspao_pStatusBuf != NULL)
{
pStsCtx = (PASP_POSTSTAT_CTX)AtalkAllocMemory(sizeof(ASP_POSTSTAT_CTX) +
pAspAddr->aspao_StsBufSize);
if (pStsCtx != NULL)
{
pStsCtx->aps_pAMdl = AtalkAllocAMdl((PBYTE)pStsCtx + sizeof(ASP_POSTSTAT_CTX),
pAspAddr->aspao_StsBufSize);
if (pStsCtx->aps_pAMdl != NULL)
{
pStsCtx->aps_pAtpResp = pAtpResp;
StsBufSize = pAspAddr->aspao_StsBufSize;
RtlCopyMemory((PBYTE)pStsCtx + sizeof(ASP_POSTSTAT_CTX),
pAspAddr->aspao_pStatusBuf,
StsBufSize);
}
else
{
AtalkFreeMemory(pStsCtx);
pStsCtx = NULL;
StsBufSize = 0;
}
}
}
RELEASE_SPIN_LOCK_DPC(&pAspAddr->aspao_Lock);
Status = AtalkAtpPostResp(pAtpResp,
pSrcAddr,
(pStsCtx != NULL) ?
pStsCtx->aps_pAMdl : NULL,
StsBufSize,
NULL,
atalkAspRespComplete,
pStsCtx);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSlsXHandler: AtalkAtpPostResp %ld\n", Status));
atalkAspRespComplete(Status, pStsCtx);
}
break;
case ASP_TICKLE:
SessionId = pUserBytes[ASP_SESSIONID_OFF];
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspSssXHandler: Received tickle from %x.%x Session %d\n",
pSrcAddr->ata_Network, pSrcAddr->ata_Node, SessionId));
if ((pAspConn = atalkAspReferenceConnBySrcAddr(pAspAddr, pSrcAddr, SessionId)) != NULL)
{
ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
pAspConn->aspco_LastContactTime = AtalkGetCurrentTick();
RELEASE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
AtalkAspDereferenceConn(pAspConn);
}
else
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSssXHandler: Conn not found for addr %d.%d Session %d\n",
pSrcAddr->ata_Network, pSrcAddr->ata_Node, SessionId));
}
// Fall through to the default case
default:
// Cancel this response since we never respond to it and we want this to go away
AtalkAtpCancelResp(pAtpResp);
if (AspCmd != ASP_TICKLE)
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSlsXHandler: Invalid command\n"));
}
break;
}
}
LOCAL VOID
atalkAspSssXHandler(
IN ATALK_ERROR ErrorCode,
IN PASP_ADDROBJ pAspAddr, // Listener (our context)
IN PATP_RESP pAtpResp, // Atp Response context
IN PATALK_ADDR pSrcAddr, // Address of requestor
IN USHORT PktLen,
IN PBYTE pPkt,
IN PBYTE pUserBytes
)
/*++
Routine Description:
Handler for incoming requests on the Sss. It handles incoming requests, close
and write continue.
Arguments:
Return Value:
--*/
{
PASP_CONNOBJ pAspConn; // Session which will handle this request
PASP_REQUEST pAspReq; // The request that will be satisfied
ATALK_ERROR Status;
NTSTATUS retStatus;
USHORT SequenceNum; // From the incoming packet
BYTE SessionId; // -- ditto --
BYTE RequestType; // -- ditto --
BOOLEAN CancelResp = FALSE,
CancelTickle;
BOOLEAN fTellAfp=TRUE;
do
{
if (!ATALK_SUCCESS(ErrorCode))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("atalkAspSssXHandler: Error %ld\n", ErrorCode));
// Take away the reference on the Sls now that the atp address is closing
if (ErrorCode == ATALK_ATP_CLOSING)
AtalkAspDereferenceAddr(pAspAddr);
break;
}
// Get the session id out of the packet and reference the session that this
// request is targeted to.
SessionId = pUserBytes[ASP_SESSIONID_OFF];
RequestType = pUserBytes[ASP_CMD_OFF];
GETSHORT2SHORT(&SequenceNum, pUserBytes+ASP_SEQUENCE_NUM_OFF);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspSssXHandler: Entered for Request %x from %x.%x\n",
RequestType, pSrcAddr->ata_Network, pSrcAddr->ata_Node));
// The reference for this connection is passed down to the request
// for the ASP_CMD & ASP_WRITE case.
pAspConn = atalkAspReferenceConnBySrcAddr(pAspAddr, pSrcAddr, SessionId);
if (pAspConn == NULL)
{
CancelResp = TRUE;
break;
}
ASSERT (pAspConn->aspco_pAspAddr == pAspAddr);
ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
pAspConn->aspco_LastContactTime = AtalkGetCurrentTick();
switch (RequestType)
{
case ASP_CMD:
case ASP_WRITE:
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspSssXHandler: %s\n",
(RequestType == ASP_CMD) ? "Command" : "Write"));
// Create a request for this and notify the client to handle this
// Validate the incoming sequence number. Reject if out of sequence
if (SequenceNum == pAspConn->aspco_NextExpectedSeqNum)
{
// We now have a request to be handled.
// The reference to the connection above will be passed on
// to the request. This will get de-referenced when the
// request is replied to. See if we have a free request to pick up
// Allocate a request structure if not and link it in the listener object
if ((pAspReq = pAspConn->aspco_pFreeReqs) != NULL)
pAspConn->aspco_pFreeReqs = pAspReq->asprq_Next;
else pAspReq = AtalkBPAllocBlock(BLKID_ASPREQ);
if (pAspReq != NULL)
{
pAspConn->aspco_NextExpectedSeqNum ++;
#if DBG
pAspReq->asprq_Signature = ASPRQ_SIGNATURE;
#endif
pAspReq->asprq_pAtpResp = pAtpResp;
pAspReq->asprq_pAspConn = pAspConn;
pAspReq->asprq_ReqType = RequestType;
pAspReq->asprq_SeqNum = SequenceNum;
pAspReq->asprq_RemoteAddr = *pSrcAddr;
pAspReq->asprq_Flags = 0;
pAspReq->asprq_Request.rq_WriteMdl = NULL;
pAspReq->asprq_Request.rq_CacheMgrContext = NULL;
pAspReq->asprq_Request.rq_RequestSize = PktLen;
pAspReq->asprq_Next = pAspConn->aspco_pActiveReqs;
pAspConn->aspco_cReqsInProcess ++;
pAspConn->aspco_pActiveReqs = pAspReq;
ASSERT ((pAspConn->aspco_Flags & (ASPCO_CLEANING_UP |
ASPCO_CLOSING |
ASPCO_LOCAL_CLOSE |
ASPCO_REMOTE_CLOSE)) == 0);
}
}
else
{
pAspReq = NULL;
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSssXHandler: Sequence mismatch exp %x, act %x\n",
pAspConn->aspco_NextExpectedSeqNum, SequenceNum));
}
RELEASE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
// If we do not have an request to handle this, cancel the
// response. Otherwise the client will keep retrying and atp
// will not tell us since it already has.
if (pAspReq == NULL)
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSssXHandler: Dropping request for session %d from %d.%d\n",
SessionId, pSrcAddr->ata_Network, pSrcAddr->ata_Node));
CancelResp = TRUE;
AtalkAspDereferenceConn(pAspConn);
break;
}
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspSssXHandler: Indicating Request %lx\n", pAspReq));
if (RequestType == ASP_WRITE)
{
if (PktLen > MAX_WRITE_REQ_SIZE)
{
PASP_REQUEST *ppTmpAspReq;
ASSERT(0);
ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
for (ppTmpAspReq = &pAspConn->aspco_pActiveReqs;
*ppTmpAspReq != NULL; ppTmpAspReq = &(*ppTmpAspReq)->asprq_Next )
{
if (pAspReq == *ppTmpAspReq)
{
*ppTmpAspReq = pAspReq->asprq_Next;
break;
}
}
RELEASE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
AtalkAspDereferenceConn(pAspConn);
AtalkBPFreeBlock(pAspReq);
pAspReq = NULL;
CancelResp = TRUE;
break;
}
RtlCopyMemory(pAspReq->asprq_ReqBuf, pPkt, PktLen);
pAspReq->asprq_Request.rq_RequestBuf = pAspReq->asprq_ReqBuf;
retStatus = (*pAspAddr->aspao_ClientEntries.clt_GetWriteBuffer)
(pAspConn->aspco_ConnContext,&pAspReq->asprq_Request);
//
// most common case: file server will pend it so it can go to cache mgr
//
if (retStatus == STATUS_PENDING)
{
fTellAfp = FALSE;
break;
}
else if (retStatus == STATUS_SUCCESS)
{
if (pAspReq->asprq_Request.rq_WriteMdl != NULL)
{
atalkAspPostWriteContinue(pAspReq);
// we informed (or will inform) AFP about this request: don't
// inform again below!
fTellAfp = FALSE;
}
}
else
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSssXHandler: GetWriteBuffer returned %lx on %lx\n",
retStatus,pAspConn));
}
}
// TRUE for CMD as well
if ((pAspReq->asprq_Request.rq_WriteMdl == NULL) &&
(fTellAfp))
{
pAspReq->asprq_Request.rq_RequestBuf = pPkt;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
// Notify the client that it has a request to handle
retStatus = (*pAspAddr->aspao_ClientEntries.clt_RequestNotify)
(STATUS_SUCCESS,
pAspConn->aspco_ConnContext,
&pAspReq->asprq_Request);
if (!NT_SUCCESS(retStatus))
{
PASP_REQUEST *ppTmpAspReq;
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSssXHandler: Afp didn't accept request %lx on conn %lx\n",
pAspReq,pAspConn));
ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
for (ppTmpAspReq = &pAspConn->aspco_pActiveReqs;
*ppTmpAspReq != NULL; ppTmpAspReq = &(*ppTmpAspReq)->asprq_Next )
{
if (pAspReq == *ppTmpAspReq)
{
*ppTmpAspReq = pAspReq->asprq_Next;
break;
}
}
RELEASE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
AtalkAspDereferenceConn(pAspConn);
AtalkBPFreeBlock(pAspReq);
pAspReq = NULL;
CancelResp = TRUE;
}
}
break;
case ASP_CLOSE_SESSION:
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("atalkAspSssXHandler: Close request from %d.%d for Session %d\n",
pSrcAddr->ata_Network, pSrcAddr->ata_Node, SessionId));
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AspSessionsClosed,
&AtalkStatsLock.SpinLock);
#endif
CancelTickle = ((pAspConn->aspco_Flags &ASPCO_TICKLING) != 0);
pAspConn->aspco_Flags &= ~(ASPCO_ACTIVE | ASPCO_TICKLING);
pAspConn->aspco_Flags |= ASPCO_REMOTE_CLOSE;
RELEASE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
// Send a CloseSession reply and close the session
Status = AtalkAtpPostResp(pAtpResp,
pSrcAddr,
NULL,
0,
NULL,
AtalkAtpGenericRespComplete,
pAtpResp);
if (!ATALK_SUCCESS(Status))
{
AtalkAtpGenericRespComplete(Status, pAtpResp);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSssXHandler: AtalkAtpPostResp failed %ld\n", Status));
}
// Cancel the tickle requests for this session
if (CancelTickle)
{
Status = AtalkAtpCancelReq(pAspAddr->aspao_pSlsAtpAddr,
pAspConn->aspco_TickleXactId,
&pAspConn->aspco_WssRemoteAddr);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSssXHandler: AtalkAtpCancelReq %ld\n", Status));
}
}
// Shut down this session, well almost ... Note that we have a reference
// to this connection which will be Dereferenced by atalkAspSessionClose.
atalkAspSessionClose(pAspConn);
break;
default:
RELEASE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
CancelResp = TRUE;
AtalkAspDereferenceConn(pAspConn);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSssXHandler: Invalid command %d\n", RequestType));
break;
}
} while (FALSE);
if (CancelResp)
{
Status = AtalkAtpCancelResp(pAtpResp);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
("atalkAspSssXHandler: AtalkAspCancelResp %ld\n", Status));
}
}
}
LOCAL VOID FASTCALL
atalkAspReplyRelease(
IN ATALK_ERROR ErrorCode,
IN PASP_REQUEST pAspReq
)
/*++
Routine Description:
Handler for incoming release for reply
Arguments:
Return Value:
--*/
{
PASP_CONNOBJ pAspConn;
PASP_ADDROBJ pAspAddr;
KIRQL OldIrql;
NTSTATUS Status = STATUS_SUCCESS;
ASSERT (VALID_ASPRQ(pAspReq));
pAspConn = pAspReq->asprq_pAspConn;
ASSERT (VALID_ASPCO(pAspConn));
pAspAddr = pAspConn->aspco_pAspAddr;
ASSERT (VALID_ASPAO(pAspAddr));
ASSERT ((pAspReq->asprq_Flags & ASPRQ_REPLY) || !ATALK_SUCCESS(ErrorCode));
if (!NT_SUCCESS(ErrorCode))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("atalkAspReplyRelease: Failure %ld\n", ErrorCode));
Status = AtalkErrorToNtStatus(ErrorCode);
}
// We complete here
(*pAspAddr->aspao_ClientEntries.clt_ReplyCompletion)(Status,
pAspConn->aspco_ConnContext,
&pAspReq->asprq_Request);
// Based on whether a reply was actually sent or not, either deref the response
// or cancel it.
if (pAspReq->asprq_Flags & ASPRQ_REPLY)
{
AtalkAtpRespDereference(pAspReq->asprq_pAtpResp);
}
else
{
AtalkAtpCancelResp(pAspReq->asprq_pAtpResp);
}
// make sure we aren't hanging on to cache mgr's mdl!
ASSERT(pAspReq->asprq_Request.rq_CacheMgrContext == NULL);
#if DBG
pAspReq->asprq_Signature = 0x28041998;
pAspReq->asprq_pAtpResp = (PATP_RESP)(pAspReq->asprq_Request.rq_WriteMdl);
pAspReq->asprq_pAspConn = (PASP_CONNOBJ)(pAspReq->asprq_Request.rq_CacheMgrContext);
pAspReq->asprq_Request.rq_WriteMdl = (PMDL)0x44556677;
pAspReq->asprq_Request.rq_CacheMgrContext = (PVOID)66778899;
#endif
// Free this as we are done with this request now
ACQUIRE_SPIN_LOCK(&pAspConn->aspco_Lock, &OldIrql);
if (pAspConn->aspco_pFreeReqs == NULL)
{
pAspReq->asprq_Next = NULL;
pAspConn->aspco_pFreeReqs = pAspReq;
}
else AtalkBPFreeBlock(pAspReq);
RELEASE_SPIN_LOCK(&pAspConn->aspco_Lock, OldIrql);
// We are done with this request.
AtalkAspDereferenceConn(pAspConn);
}
LOCAL VOID
atalkAspWriteContinueResp(
IN ATALK_ERROR ErrorCode,
IN PASP_REQUEST pAspReq,
IN PAMDL pReqAMdl,
IN PAMDL pRespAMdl,
IN USHORT RespSize,
IN PBYTE RespUserBytes
)
/*++
Routine Description:
Handler for incoming write continue response.
Arguments:
Return Value:
--*/
{
PASP_CONNOBJ pAspConn;
PASP_ADDROBJ pAspAddr;
NTSTATUS Status;
NTSTATUS retStatus;
KIRQL OldIrql;
PASP_REQUEST * ppAspReq;
PVOID pClientContxt;
ASSERT (VALID_ASPRQ(pAspReq));
ASSERT(pAspReq->asprq_Flags & ASPRQ_WRTCONT);
pAspConn = pAspReq->asprq_pAspConn;
ASSERT(VALID_ASPCO(pAspConn));
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspWriteContinueResp: Entered for request %lx\n", pAspReq));
pAspAddr = pAspConn->aspco_pAspAddr;
ASSERT(VALID_ASPAO(pAspAddr));
pAspReq->asprq_Flags &= ~ASPRQ_WRTCONT;
pClientContxt = pAspConn->aspco_ConnContext;
if (ATALK_SUCCESS(ErrorCode))
{
pAspConn->aspco_RT.rt_New = AtalkGetCurrentTick() - pAspConn->aspco_RT.rt_New;
// Estimate the retry interval for next time.
AtalkCalculateNewRT(&pAspConn->aspco_RT);
Status = STATUS_SUCCESS;
}
else
{
Status = AtalkErrorToNtStatus(ErrorCode);
}
#ifdef PROFILING
{
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&AtalkStatsLock, &OldIrql);
AtalkStatistics.stat_LastAspRTT = (ULONG)(pAspConn->aspco_RT.rt_Base);
if ((ULONG)(pAspConn->aspco_RT.rt_Base) > AtalkStatistics.stat_MaxAspRTT)
AtalkStatistics.stat_MaxAspRTT = (ULONG)(pAspConn->aspco_RT.rt_Base);
RELEASE_SPIN_LOCK(&AtalkStatsLock, OldIrql);
}
#endif
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspWriteContinueResp: Indicating request %lx\n", pAspReq));
// Notify the client that it has a request to handle
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
retStatus = (*pAspAddr->aspao_ClientEntries.clt_RequestNotify)
(Status,
pClientContxt,
&pAspReq->asprq_Request);
KeLowerIrql(OldIrql);
//
// In case the writecontinue returned an error, this request needs to go away
// since there will never be a call to AtalkAspReply(). Also deref the conn.
// Alternately, if the response came in fine, but the server didn't want to accept
// it, it's the same deal
//
if ( (!NT_SUCCESS(Status)) || (!NT_SUCCESS(retStatus)) )
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspWriteContinueResp: incoming %lx, Afp %lx req: %lx on %lx, cancelling\n",
Status,retStatus,pAspReq,pAspConn));
ACQUIRE_SPIN_LOCK(&pAspConn->aspco_Lock, &OldIrql);
for (ppAspReq = &pAspConn->aspco_pActiveReqs;
*ppAspReq != NULL;
ppAspReq = &(*ppAspReq)->asprq_Next)
{
if (pAspReq == *ppAspReq)
{
*ppAspReq = pAspReq->asprq_Next;
pAspConn->aspco_cReqsInProcess --;
break;
}
}
ASSERT (*ppAspReq == pAspReq->asprq_Next);
RELEASE_SPIN_LOCK(&pAspConn->aspco_Lock, OldIrql);
AtalkAspDereferenceConn(pAspConn);
// Cancel the response for the original request that caused wrtcont to be posted
AtalkAtpCancelResp(pAspReq->asprq_pAtpResp);
// Free this request as well
AtalkBPFreeBlock(pAspReq);
}
if (pReqAMdl)
{
ASSERT (AtalkGetAddressFromMdlSafe(pReqAMdl, NormalPagePriority) == pAspReq->asprq_WrtContRespBuf);
ASSERT (AtalkSizeMdlChain(pReqAMdl) == ASP_WRITE_DATA_SIZE);
AtalkFreeAMdl(pReqAMdl);
}
}
LOCAL VOID
atalkAspSendAttentionResp(
IN ATALK_ERROR ErrorCode,
IN PVOID pContext,
IN PAMDL pReqAMdl,
IN PAMDL pRespAMdl,
IN USHORT RespSize,
IN PBYTE RespUserBytes
)
/*++
Routine Description:
Handler for incoming write continue response.
Arguments:
Return Value:
--*/
{
PBYTE pBuf;
PASP_CONNOBJ pAspConn = (PASP_CONNOBJ)pContext;
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspSendAttentionResp: Entered for conn %lx\n", pAspConn));
if (!ATALK_SUCCESS(ErrorCode))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSendAttentionResp: Failure %ld\n", ErrorCode));
}
if (pRespAMdl != NULL)
{
pBuf = AtalkGetAddressFromMdlSafe(
pRespAMdl,
NormalPagePriority);
if (pBuf != NULL)
{
AtalkFreeMemory(pBuf);
}
AtalkFreeAMdl(pRespAMdl);
}
// Call the completion routine
(*pAspConn->aspco_pAspAddr->aspao_ClientEntries.clt_AttnCompletion)(pAspConn->aspco_AttentionContext);
pAspConn->aspco_AttentionContext = NULL;
// Finally Dereference the connection
AtalkAspDereferenceConn(pAspConn);
}
LOCAL LONG FASTCALL
atalkAspSessionMaintenanceTimer(
IN PTIMERLIST pTimer,
IN BOOLEAN TimerShuttingDown
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PASP_CONNOBJ pAspConn, pAspConnNext;
PASP_CONN_MAINT pAspCM;
BOOLEAN Close = FALSE;
LONG CurrentTick = AtalkGetCurrentTick();
#ifdef PROFILING
TIME TimeS, TimeE, TimeD;
TimeS = KeQueryPerformanceCounter(NULL);
#endif
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspSessionMaintenanceTimer: Entered\n"));
if (TimerShuttingDown)
return ATALK_TIMER_NO_REQUEUE;
pAspCM = CONTAINING_RECORD(pTimer, ASP_CONN_MAINT, ascm_SMTTimer);
ACQUIRE_SPIN_LOCK_DPC(&atalkAspLock);
// Walk the list of sessions on the global list and shut down
// sessions that have not tickle'd for a while
for (pAspConn = pAspCM->ascm_ConnList; pAspConn != NULL; pAspConn = pAspConnNext)
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspSessionMaintenanceTimer: Checking out session %d from %x.%x\n",
pAspConn->aspco_SessionId,
pAspConn->aspco_WssRemoteAddr.ata_Network,
pAspConn->aspco_WssRemoteAddr.ata_Node));
pAspConnNext = pAspConn->aspco_NextSession;
Close = FALSE;
ASSERT (VALID_ASPCO(pAspConn));
ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
if ((pAspConn->aspco_Flags & ASPCO_ACTIVE) &&
((CurrentTick - pAspConn->aspco_LastContactTime) > ASP_MAX_SESSION_IDLE_TIME))
{
pAspConn->aspco_Flags |= (ASPCO_REMOTE_CLOSE | ASPCO_DROPPED);
pAspConn->aspco_Flags &= ~ASPCO_ACTIVE;
pAspConn->aspco_RefCount ++; // Since atalkAspSessionClose Derefs it
Close = TRUE;
}
RELEASE_SPIN_LOCK_DPC(&pAspConn->aspco_Lock);
if (Close)
{
PASP_ADDROBJ pAspAddr;
ATALK_ERROR Status;
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AspSessionsDropped,
&AtalkStatsLock.SpinLock);
#endif
pAspAddr = pAspConn->aspco_pAspAddr;
ASSERT (VALID_ASPAO(pAspAddr));
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSessionMaintenanceTimer: Shutting down session %d from %x.%x\n",
pAspConn->aspco_SessionId,
pAspConn->aspco_WssRemoteAddr.ata_Network,
pAspConn->aspco_WssRemoteAddr.ata_Node));
RELEASE_SPIN_LOCK_DPC(&atalkAspLock);
// This session is being punted. Cancel tickles on this and notify the
// server that this session is history.
Status = AtalkAtpCancelReq(pAspAddr->aspao_pSlsAtpAddr,
pAspConn->aspco_TickleXactId,
&pAspConn->aspco_WssRemoteAddr);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspSessionMaintenanceTimer: AtalkAtpCancelReq %ld\n", Status));
}
// Shut down this session, well almost ...
atalkAspSessionClose(pAspConn);
ACQUIRE_SPIN_LOCK_DPC(&atalkAspLock);
pAspConnNext = pAspCM->ascm_ConnList;
}
}
#ifdef PROFILING
TimeE = KeQueryPerformanceCounter(NULL);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR_DPC(&AtalkStatistics.stat_AspSmtProcessTime,
TimeD,
&AtalkStatsLock.SpinLock);
INTERLOCKED_INCREMENT_LONG_DPC( &AtalkStatistics.stat_AspSmtCount,
&AtalkStatsLock.SpinLock);
#endif
RELEASE_SPIN_LOCK_DPC(&atalkAspLock);
// Reschedule ourselves
return ATALK_TIMER_REQUEUE;
}
LOCAL VOID
atalkAspSessionClose(
IN PASP_CONNOBJ pAspConn
)
/*++
Routine Description:
This should be called with a reference to the connection which is Dereferenced
here.
Arguments:
Return Value:
--*/
{
PASP_REQUEST pAspReq, pAspReqNext;
PASP_ADDROBJ pAspAddr = pAspConn->aspco_pAspAddr;
REQUEST Request;
KIRQL OldIrql;
NTSTATUS Status = STATUS_REMOTE_DISCONNECT;
ACQUIRE_SPIN_LOCK(&pAspConn->aspco_Lock, &OldIrql);
pAspConn->aspco_Flags &= ~ASPCO_ACTIVE;
// Cancel any Write-continues pending. Do not bother cancelling
// replies as they will time out anyway. Also atalkAspReplyRelease()
// will attempt to acquire the connection lock and we're already
// holding it
for (pAspReq = pAspConn->aspco_pActiveReqs;
pAspReq != NULL;
pAspReq = pAspReqNext)
{
pAspReqNext = pAspReq->asprq_Next;
if ((pAspReq->asprq_Flags & (ASPRQ_WRTCONT | ASPRQ_WRTCONT_CANCELLED)) == ASPRQ_WRTCONT)
{
pAspReq->asprq_Flags |= ASPRQ_WRTCONT_CANCELLED;
RELEASE_SPIN_LOCK(&pAspConn->aspco_Lock, OldIrql);
AtalkAtpCancelReq(pAspAddr->aspao_pSssAtpAddr,
pAspReq->asprq_WCXactId,
&pAspConn->aspco_WssRemoteAddr);
ACQUIRE_SPIN_LOCK(&pAspConn->aspco_Lock, &OldIrql);
pAspReqNext = pAspConn->aspco_pActiveReqs;
}
}
RELEASE_SPIN_LOCK(&pAspConn->aspco_Lock, OldIrql);
if (pAspConn->aspco_Flags & ASPCO_DROPPED)
{
Status = STATUS_LOCAL_DISCONNECT;
}
// Indicate a request with an error to indicate that the session closed remotely.
// Pass the remote address of the client so that the server can log an event if
// the session did not shutdown gracefully.
Request.rq_RequestSize = (LONG)(pAspConn->aspco_WssRemoteAddr.ata_Address);
Request.rq_RequestBuf = NULL;
Request.rq_WriteMdl = NULL;
Request.rq_CacheMgrContext = NULL;
(*pAspAddr->aspao_ClientEntries.clt_RequestNotify)(Status,
pAspConn->aspco_ConnContext,
&Request);
// Finally Dereference the session
AtalkAspDereferenceConn(pAspConn);
}
LOCAL VOID
atalkAspReturnResp(
IN PATP_RESP pAtpResp,
IN PATALK_ADDR pDstAddr,
IN BYTE Byte0,
IN BYTE Byte1,
IN USHORT Word2
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
BYTE UserBytes[ATP_USERBYTES_SIZE];
ATALK_ERROR Status;
UserBytes[0] = Byte0;
UserBytes[1] = Byte1;
PUTSHORT2SHORT(UserBytes+2, Word2);
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspReturnResp: For Resp %lx\n", pAtpResp));
Status = AtalkAtpPostResp(pAtpResp,
pDstAddr,
NULL,
0,
UserBytes,
AtalkAtpGenericRespComplete,
pAtpResp);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspReturnResp: AtalkAtpPostResp failed %ld\n", Status));
AtalkAtpGenericRespComplete(Status, pAtpResp);
}
}
LOCAL VOID FASTCALL
atalkAspRespComplete(
IN ATALK_ERROR ErrorCode,
IN PASP_POSTSTAT_CTX pStsCtx
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
("atalkAspRespComplete: Entered pStsCtx %lx\n", pStsCtx));
if (!ATALK_SUCCESS(ErrorCode))
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
("atalkAspRespComplete: Failed %ld, pStsCtx %lx\n", ErrorCode, pStsCtx));
}
if (pStsCtx != NULL)
{
AtalkFreeAMdl(pStsCtx->aps_pAMdl);
AtalkAtpRespDereferenceDpc(pStsCtx->aps_pAtpResp);
AtalkFreeMemory(pStsCtx);
}
}
LOCAL VOID
atalkAspCloseComplete(
IN ATALK_ERROR Status,
IN PASP_ADDROBJ pAspAddr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_WARN,
("atalkAspCloseComplete: AtalkAtpCloseAddr returned %ld\n", Status));
AtalkAspDereferenceAddr(pAspAddr);
}
#if DBG
VOID
AtalkAspDumpSessions(
VOID
)
{
PASP_CONNOBJ pAspConn;
KIRQL OldIrql;
LONG i;
DBGPRINT(DBG_COMP_DUMP, DBG_LEVEL_FATAL, ("ASP SESSION LIST:\n"));
ACQUIRE_SPIN_LOCK(&atalkAspLock, &OldIrql);
for (i = 0; i < NUM_ASP_CONN_LISTS; i++)
{
for (pAspConn = atalkAspConnMaint[i].ascm_ConnList;
pAspConn != NULL;
pAspConn = pAspConn->aspco_NextSession)
{
DBGPRINT(DBG_COMP_DUMP, DBG_LEVEL_FATAL,
("\tRemote Addr %4d.%3d.%2d SessionId %2d Flags %4x RefCount %ld\n",
pAspConn->aspco_WssRemoteAddr.ata_Network,
pAspConn->aspco_WssRemoteAddr.ata_Node,
pAspConn->aspco_WssRemoteAddr.ata_Socket,
pAspConn->aspco_SessionId,
pAspConn->aspco_Flags,
pAspConn->aspco_RefCount));
}
}
RELEASE_SPIN_LOCK(&atalkAspLock, OldIrql);
}
#endif