mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4189 lines
148 KiB
4189 lines
148 KiB
/* asp.c, /appletalk/source, Garth Conboy, 04/01/89 */
|
|
/* Copyright (c) 1989 by Pacer Software Inc., La Jolla, CA */
|
|
|
|
/* GC - Initial coding.
|
|
GC - (12/08/89): AppleTalk phase II comes to town (not quite as good
|
|
as Santa Claus coming to town, but...)
|
|
GC - (07/08/90): Get session handlers should be terminated (with the new
|
|
ATaspSessionListenerDeleted error) when a session
|
|
listener is deleted.
|
|
GC - (08/18/90): New error logging mechanism.
|
|
GC - (03/24/92): Localize use of lastSessionRefNum to
|
|
GetNextSessionRefNum().
|
|
GC - (03/24/92): Removed "sessionListenerInfo->acceptingOpensNow".
|
|
GC - (03/24/92): Changed return type of AspGetSession to "long"; added
|
|
AspCancelGetSession();
|
|
GC - (03/24/92): Added FindSessionListenerInfoFor().
|
|
GC - (03/27/92): No longer use any "session buffering" for incoming
|
|
requests. Atp EnqueueRequestHandlers are not posted so
|
|
that Atp will simply pass up pointers into the Ddp
|
|
datagrams. Avoid a buffer copy.
|
|
GC - (03/29/92): Introduced GetAnyRequest(). This allows general get
|
|
requests on a session listener: any incoming requests
|
|
to any server session on the Sls will cause this call
|
|
to complete.
|
|
GC - (05/20/92): A much less compute intensive algorithm is now used
|
|
when we're looking for the next not-in-use session
|
|
reference number.
|
|
GC - (06/27/92): All buffers coming from user space are now "opaque," they
|
|
may or may not be "char *." We now use the new routines
|
|
MoveFromOpaque() and MoveToOpaque() to access these
|
|
"buffer"s rather than MemMove(). Note that "user bytes"
|
|
are left as "char *"s, ous caller's would be aware of this
|
|
and copy any "opaque" user bytes to some place "real"
|
|
before calling us!
|
|
GC - (06/27/92): Removed the various "copyRequired" arguments. Our
|
|
callers are now required to keep the various request and
|
|
response buffers around until the given transaction
|
|
completes.
|
|
GC - (06/28/92): Got rid of the "sendCloseSession" static, it's now an
|
|
argument to AspCloseSession... the way it should be.
|
|
"Works great, less code!"
|
|
GC - (07/10/92): Added AspSetCookieForSession() and
|
|
AspGetCookieForSession().
|
|
GC - (07/20/92): For Nikki at Microsoft: AspCreateSessionListener now can
|
|
optionally take an already open Atp socket on which to
|
|
create the session listener.
|
|
GC - (09/02/92): A Empty buffer may now be passed to AspGetAnyRequest and
|
|
AspGetRequest in which case a pointer to the actual
|
|
Atp/Ddp buffer will passed to the completion routine,
|
|
which must then copy the data before it returns.
|
|
GC - (09/02/92): Changed the return type and the argument list for
|
|
AspCreateSessionListenerOnNode to be a little more
|
|
reasonable.
|
|
GC - (11/07/92): Adjusted session close handling per Microsoft's requests;
|
|
two new error codes: ATaspLocalSessionClose and
|
|
ATaspRemoteSessionClose replace ATaspSessionClosed.
|
|
If "GetAnyRequest"s are being used on a listener, we
|
|
now can keep a queue of closes that we couldn't notify
|
|
anybody about, and we defer this notification until
|
|
the next GetAnyRequest comes in.
|
|
GC - (12/12/92): Locks and reference counts come to town.
|
|
GC - (12/22/92): Some corrections to the above changes. Also, now we
|
|
use the eventual sessionRefNum as the GetSession refNum.
|
|
|
|
*** Make the PVCS source control system happy:
|
|
$Header$
|
|
$Log$
|
|
***
|
|
|
|
Yep, this module contains all of the code for managing the AppleTalk
|
|
ASP protocol.
|
|
|
|
*/
|
|
|
|
#define IncludeAspErrors 1
|
|
|
|
#include "atalk.h"
|
|
|
|
#define VerboseMessages 0
|
|
|
|
ExternForVisibleFunction AtpIncomingRequestHandler IncomingSlsTransaction;
|
|
|
|
ExternForVisibleFunction AtpIncomingRequestHandler IncomingSssTransaction;
|
|
|
|
ExternForVisibleFunction AtpIncomingRequestHandler IncomingWssTransaction;
|
|
|
|
ExternForVisibleFunction AtpIncomingReleaseHandler IncomingRelease;
|
|
|
|
ExternForVisibleFunction AtpIncomingReleaseHandler SendStatusComplete;
|
|
|
|
ExternForVisibleFunction AtpIncomingResponseHandler IncomingWriteContinueResponse;
|
|
|
|
ExternForVisibleFunction AtpIncomingResponseHandler IncomingGetStatusResponse;
|
|
|
|
ExternForVisibleFunction AtpIncomingResponseHandler IncomingOpenSessionResponse;
|
|
|
|
ExternForVisibleFunction AtpIncomingResponseHandler IncomingWriteOrCommandComplete;
|
|
|
|
ExternForVisibleFunction long GetNextSessionRefNum(void);
|
|
|
|
ExternForVisibleFunction SessionInfo
|
|
FindSessionInfoForSocket(long socket,
|
|
unsigned char sessionId);
|
|
|
|
ExternForVisibleFunction SessionInfo FindSessionInfoFor(long sessionRefNum);
|
|
|
|
ExternForVisibleFunction AppleTalkErrorCode
|
|
SendAspErrorReturn(long sourceSocket,
|
|
AppleTalkAddress destination,
|
|
short unsigned transactionId,
|
|
Boolean exactlyOnce,
|
|
int errorCode);
|
|
|
|
ExternForVisibleFunction void
|
|
DecrementSocketUsage(SessionListenerInfo sessionListenerInfo,
|
|
long socket);
|
|
|
|
ExternForVisibleFunction TimerHandler SessionMaintenanceTimerExpired;
|
|
|
|
ExternForVisibleFunction AppleTalkErrorCode InitializeAsp(void);
|
|
|
|
ExternForVisibleFunction SessionListenerInfo
|
|
FindSessionListenerInfoFor(long sessionListenerRefNum);
|
|
|
|
ExternForVisibleFunction GetRequestInfo
|
|
FindGetRequestInfoFor(SessionListenerInfo sessionListenerInfo,
|
|
long getRequestRefNum);
|
|
|
|
ExternForVisibleFunction Boolean far
|
|
UnlinkGetRequestInfo(GetRequestInfo targetGetRequestInfo);
|
|
|
|
ExternForVisibleFunction Boolean far
|
|
UnlinkGetSessionHandler(GetSessionHandler getSessionHandler);
|
|
|
|
ExternForVisibleFunction void far
|
|
UnlinkSessionListenerInfo(SessionListenerInfo sessionListenerInfo);
|
|
|
|
ExternForVisibleFunction void far
|
|
UnlinkSessionInfo(SessionInfo sessionInfo);
|
|
|
|
ExternForVisibleFunction CloseCompletionRoutine AspServiceListenerCloseComplete;
|
|
|
|
static Boolean sessionMaintenanceTimerStarted = False;
|
|
|
|
static char getStatusResponseUserBytes[AtpUserBytesSize]; /* Read only */
|
|
|
|
/* *** External entry points *** */
|
|
|
|
void far ShutdownAsp(void)
|
|
{
|
|
|
|
sessionMaintenanceTimerStarted = False;
|
|
return;
|
|
|
|
} /* ShutdownAsp */
|
|
|
|
/* More of the protocol suite should be as simple as AspGetParams... */
|
|
|
|
AppleTalkErrorCode _near AspGetParams(
|
|
int far *maxCommandSize, /* Maximum supported command size. */
|
|
int far *quantumSize) /* Maximum data size of command reply or write. */
|
|
{
|
|
|
|
*maxCommandSize = AtpSinglePacketDataSize;
|
|
*quantumSize = AtpMaximumTotalResponseSize;
|
|
return(ATnoError);
|
|
|
|
} /* AspGetParams */
|
|
|
|
AppleTalkErrorCode far AspCreateSessionListenerOnNode(
|
|
int port, /* Port on which the socket should live. */
|
|
long existingAtpSocket, /* "-1" if we should open our own Atp socket
|
|
for the session listener; if ">= 0" this is
|
|
an already open Atp socket on which to create
|
|
the Asp session listener. */
|
|
int desiredSocket, /* Desired static socket or zero for dynamic.
|
|
Ignored if the above argument is ">= 0". */
|
|
long far *sessionListenerRefNum,
|
|
/* New session listener refNum. */
|
|
long *socket)
|
|
/* Full ATP socket we'll open (the socket that
|
|
the session listener will be open on). */
|
|
{
|
|
SessionListenerInfo sessionListenerInfo, currentSessionListenerInfo;
|
|
Boolean okay, wrapped = False;
|
|
int index;
|
|
AppleTalkErrorCode errorCode;
|
|
long tempSocket;
|
|
|
|
/* First call? */
|
|
|
|
if (not sessionMaintenanceTimerStarted)
|
|
if ((errorCode = InitializeAsp()) isnt ATnoError)
|
|
return(errorCode);
|
|
|
|
/* Create a session listener on an ATP socket; return the session listener
|
|
reference number. */
|
|
|
|
if (existingAtpSocket >= 0)
|
|
tempSocket = existingAtpSocket;
|
|
else
|
|
if ((errorCode = AtpOpenSocketOnNode(&tempSocket, port, empty,
|
|
desiredSocket, empty,
|
|
0)) isnt ATnoError)
|
|
return(errorCode);
|
|
if (socket isnt empty)
|
|
*socket = tempSocket;
|
|
|
|
/* Okay, allocate, fill in, and thread, a new session listener. */
|
|
|
|
sessionListenerInfo =
|
|
(SessionListenerInfo)Calloc(sizeof(*sessionListenerInfo), 1);
|
|
if (sessionListenerInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
ErrorLog("AspCreateSessionListenerOnNode", ISevError, __LINE__, port,
|
|
IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(tempSocket, Empty, (long unsigned)0);
|
|
return(AToutOfMemory);
|
|
}
|
|
|
|
/* Find a free session listener reference number. There souldn't be too
|
|
many of these session listeners going at a time, so don't bother hashing
|
|
here. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
okay = False;
|
|
TakeLock(AspLock);
|
|
while(not okay)
|
|
{
|
|
okay = True;
|
|
if ((lastSessionListenerRefNum += 1) < 0)
|
|
{
|
|
lastSessionListenerRefNum = 0;
|
|
if (wrapped)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
Free(sessionListenerInfo);
|
|
ErrorLog("AspCreateSessionListenerOnNode", ISevError, __LINE__, port,
|
|
IErrAspBadError, IMsgAspBadError,
|
|
Insert0());
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(tempSocket, Empty, (long unsigned)0);
|
|
return(ATinternalError);
|
|
}
|
|
wrapped = True;
|
|
}
|
|
for (currentSessionListenerInfo = sessionListenerInfoHead;
|
|
okay and currentSessionListenerInfo isnt empty;
|
|
currentSessionListenerInfo = currentSessionListenerInfo->next)
|
|
if (currentSessionListenerInfo->sessionListenerRefNum is
|
|
lastSessionListenerRefNum)
|
|
okay = False;
|
|
}
|
|
|
|
sessionListenerInfo->sessionListenerRefNum = lastSessionListenerRefNum;
|
|
sessionListenerInfo->ourSocket = tempSocket;
|
|
sessionListenerInfo->closeSocket = (existingAtpSocket < 0);
|
|
sessionListenerInfo->port = port;
|
|
sessionListenerInfo->next = sessionListenerInfoHead;
|
|
sessionListenerInfoHead = Link(sessionListenerInfo);
|
|
ReleaseLock(AspLock);
|
|
|
|
/* Enqueue a few ATP request handlers for this new session listener;
|
|
we'll need to be able to handle GetStatus, OpenSession and Tickle
|
|
request on the session listener socker (SLS) all other requests will
|
|
go to the server session socket (SSS). */
|
|
|
|
for (index = 0; index < OutstandingSlsHandlers; index += 1)
|
|
if ((errorCode = AtpEnqueueRequestHandler(empty, tempSocket, empty, 0,
|
|
empty, IncomingSlsTransaction,
|
|
(long unsigned)
|
|
lastSessionListenerRefNum))
|
|
isnt ATnoError)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
AspDeleteSessionListener(lastSessionListenerRefNum,
|
|
Empty, (long unsigned)0);
|
|
return(errorCode);
|
|
}
|
|
|
|
/* Set the refNum that we've used, and run away. */
|
|
|
|
*sessionListenerRefNum = sessionListenerInfo->sessionListenerRefNum;
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspCreateSessionListener */
|
|
|
|
AppleTalkErrorCode far AspDeleteSessionListener(
|
|
long sessionListenerRefNum, /* Who to delete. */
|
|
CloseCompletionRoutine far *closeCompletionRoutine,
|
|
/* Routine to call when the close completes. */
|
|
long unsigned closeUserData) /* User data for the above. */
|
|
{
|
|
SessionListenerInfo sessionListenerInfo;
|
|
SessionInfo sessionInfo, nextSessionInfo;
|
|
GetRequestInfo getRequestInfo, nextGetRequestInfo;
|
|
GetSessionHandler getSessionHandler, nextGetSessionHandler;
|
|
|
|
/* Find our target. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((sessionListenerInfo =
|
|
FindSessionListenerInfoFor(sessionListenerRefNum)) is Empty or
|
|
sessionListenerInfo->closing)
|
|
{
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSessionListener);
|
|
}
|
|
|
|
/* Set about closing. */
|
|
|
|
TakeLock(AspLock);
|
|
sessionListenerInfo->closing = True;
|
|
sessionListenerInfo->closeContext.closeCompletionRoutine =
|
|
closeCompletionRoutine;
|
|
sessionListenerInfo->closeContext.closeUserData = closeUserData;
|
|
ReleaseLock(AspLock);
|
|
|
|
/* Free any getSession handlers. */
|
|
|
|
TakeLock(AspLock);
|
|
getSessionHandler = Link(sessionListenerInfo->getSessionHandlers);
|
|
ReleaseLock(AspLock);
|
|
while (getSessionHandler isnt Empty)
|
|
{
|
|
getSessionHandler->openInProgress = True; /* Call completion rotuine. */
|
|
getSessionHandler->errorCode = ATaspSessionListenerDeleted;
|
|
TakeLock(AspLock);
|
|
nextGetSessionHandler = Link(getSessionHandler->next);
|
|
ReleaseLock(AspLock);
|
|
if (not UnlinkGetSessionHandler(getSessionHandler))
|
|
UnlinkGetSessionHandler(getSessionHandler);
|
|
getSessionHandler = nextGetSessionHandler;
|
|
}
|
|
|
|
/* Should also close all SSSs opened to this SLS. */
|
|
|
|
TakeLock(AspLock);
|
|
sessionInfo = Link(sessionListenerInfo->sessionList);
|
|
ReleaseLock(AspLock);
|
|
while (sessionInfo isnt Empty)
|
|
{
|
|
AspCloseSession(sessionInfo->sessionRefNum, Empty, (long unsigned)0,
|
|
False);
|
|
TakeLock(AspLock);
|
|
nextSessionInfo = Link(sessionInfo->nextForMySls);
|
|
ReleaseLock(AspLock);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
sessionInfo = nextSessionInfo;
|
|
}
|
|
|
|
/* Okay, unlink (twice) to set the rest of the operation into action. */
|
|
/* Terminate and free any pending GetAnyRequests. */
|
|
|
|
TakeLock(AspLock);
|
|
getRequestInfo = Link(sessionListenerInfo->getRequestInfoList);
|
|
ReleaseLock(AspLock);
|
|
while (getRequestInfo isnt Empty)
|
|
{
|
|
getRequestInfo->errorCode = ATaspSessionListenerDeleted;
|
|
TakeLock(AspLock);
|
|
nextGetRequestInfo = Link(getRequestInfo->next);
|
|
ReleaseLock(AspLock);
|
|
if (not UnlinkGetRequestInfo(getRequestInfo))
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
getRequestInfo = nextGetRequestInfo;
|
|
}
|
|
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
|
|
/* All set! */
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspDeleteSessionListener */
|
|
|
|
AppleTalkErrorCode far AspSetStatus(
|
|
long sessionListenerRefNum, /* What session listener? */
|
|
void far *serviceStatusOpaque, /* New server status "buffer" */
|
|
int serviceStatusSize) /* Size of block */
|
|
{
|
|
SessionListenerInfo sessionListenerInfo;
|
|
char far *newServiceStatus = Empty;
|
|
void far *newOpaqueServiceStatus = Empty;
|
|
Boolean newFreeOpaqueServiceStatus = False;
|
|
short newServiceStatusSize = (short)serviceStatusSize;
|
|
char far *freeServiceStatus = Empty;
|
|
void far *freeOpaqueServiceStatus = Empty;
|
|
|
|
if (serviceStatusSize > AtpMaximumTotalResponseSize)
|
|
return(ATaspStatusBufferTooBig);
|
|
|
|
/* Okay, find the session listener... */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((sessionListenerInfo = FindSessionListenerInfoFor(sessionListenerRefNum))
|
|
is Empty or sessionListenerInfo->closing)
|
|
{
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSessionListener);
|
|
}
|
|
|
|
/* Build up our new Status information */
|
|
|
|
if (serviceStatusSize > 0)
|
|
{
|
|
newServiceStatus = (char *)Malloc(serviceStatusSize);
|
|
if (newServiceStatus is Empty)
|
|
{
|
|
ErrorLog("AspSetStatus", ISevError, __LINE__, UnknownPort,
|
|
IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotSetStatus);
|
|
}
|
|
MoveFromOpaque(newServiceStatus, serviceStatusOpaque, 0,
|
|
serviceStatusSize);
|
|
|
|
/* Make a system dependent "opaque data descriptor" for our copy so
|
|
that it will be usefull to pass to Atp. */
|
|
|
|
if ((newOpaqueServiceStatus =
|
|
MakeOpaqueDataDescriptor(newServiceStatus, serviceStatusSize,
|
|
&newFreeOpaqueServiceStatus)) is Empty)
|
|
{
|
|
ErrorLog("AspSetStatus", ISevError, __LINE__, UnknownPort,
|
|
IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
Free(newServiceStatus);
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotSetStatus);
|
|
}
|
|
}
|
|
|
|
/* Okay, we're set to replace the current status buffer... */
|
|
|
|
TakeLock(AspLock);
|
|
if (not sessionListenerInfo->statusSendInProgress)
|
|
{
|
|
/* No send in progress, just replace. */
|
|
|
|
if (sessionListenerInfo->serviceStatusSize > 0)
|
|
freeServiceStatus = sessionListenerInfo->serviceStatus;
|
|
if (sessionListenerInfo->freeOpaqueServiceStatus)
|
|
freeOpaqueServiceStatus = sessionListenerInfo->opaqueServiceStatus;
|
|
sessionListenerInfo->serviceStatus = newServiceStatus;
|
|
sessionListenerInfo->opaqueServiceStatus = newOpaqueServiceStatus;
|
|
sessionListenerInfo->freeOpaqueServiceStatus = newFreeOpaqueServiceStatus;
|
|
sessionListenerInfo->serviceStatusSize = newServiceStatusSize;
|
|
}
|
|
else
|
|
{
|
|
/* A send is in progress, we must fill up the "next status slot" and
|
|
let it be copied to the "current status slot" when the send completes.
|
|
If the "nest status slot" is already in use, grab its info to free
|
|
when we've released our lock. */
|
|
|
|
if (sessionListenerInfo->newServiceStatusPending)
|
|
{
|
|
if (sessionListenerInfo->newServiceStatusSize > 0)
|
|
freeServiceStatus = sessionListenerInfo->newServiceStatus;
|
|
if (sessionListenerInfo->newFreeOpaqueServiceStatus)
|
|
freeOpaqueServiceStatus = sessionListenerInfo->newOpaqueServiceStatus;
|
|
}
|
|
|
|
/* Okay, now fill up the "next status slot" with our current info. */
|
|
|
|
sessionListenerInfo->newServiceStatus = newServiceStatus;
|
|
sessionListenerInfo->newOpaqueServiceStatus = newOpaqueServiceStatus;
|
|
sessionListenerInfo->newFreeOpaqueServiceStatus = newFreeOpaqueServiceStatus;
|
|
sessionListenerInfo->newServiceStatusSize = newServiceStatusSize;
|
|
|
|
sessionListenerInfo->newServiceStatusPending = True;
|
|
}
|
|
ReleaseLock(AspLock);
|
|
|
|
/* Free any remants there may be. */
|
|
|
|
if (freeServiceStatus isnt Empty)
|
|
Free(freeServiceStatus);
|
|
if (freeOpaqueServiceStatus isnt Empty)
|
|
FreeOpaqueDataDescriptor(freeOpaqueServiceStatus);
|
|
|
|
/* All set! */
|
|
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspSetStatus */
|
|
|
|
AppleTalkErrorCode far AspGetStatus(
|
|
long ourSocket, /* "Us"; who should the server respond to? */
|
|
AppleTalkAddress serverAddress, /* The server from which we should request
|
|
the status buffer (SLS). */
|
|
void far *opaqueBuffer, /* User "buffer" to place the status in! */
|
|
int bufferSize, /* Size of the above buffer. */
|
|
AspIncomingStatusHandler *completionRoutine,
|
|
/* User rotuine to call when that status
|
|
comes in. */
|
|
long unsigned userData) /* User data to pass along to the completion
|
|
rotuine. */
|
|
{
|
|
char userBytes[AtpUserBytesSize];
|
|
AppleTalkErrorCode errorCode;
|
|
CompletionInfo completionInfo;
|
|
|
|
/* We place a get status request and call a supplied completion routine when
|
|
the request completes. The completion routine is given the following
|
|
arguments:
|
|
|
|
errorCode - AppleTalkErrorCode; How did the request complete?
|
|
userData - long unsigned; as passed to this rotuine.
|
|
opaqueBuffer - void *; status "buffer," as passed to us.
|
|
bufferSize - int; actual length of status buffer.
|
|
|
|
*/
|
|
|
|
/* Set up userBytes for a status request. */
|
|
|
|
userBytes[AspCommandTypeOffset] = AspGetStatusCommand;
|
|
userBytes[AspCommandTypeOffset + 1] = 0;
|
|
userBytes[AspCommandTypeOffset + 2] = 0;
|
|
userBytes[AspCommandTypeOffset + 3] = 0;
|
|
|
|
/* Build up our completion info block. */
|
|
|
|
completionInfo = (CompletionInfo)Malloc(sizeof(*completionInfo));
|
|
if (completionInfo is empty)
|
|
return(ATaspCouldNotGetStatus);
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
|
|
/* Post the GetStatus request to the specified address... our caller
|
|
presumably found this with a prior NBP lookup. */
|
|
|
|
errorCode = AtpPostRequest(ourSocket, serverAddress,
|
|
Empty,
|
|
empty, 0, userBytes, False,
|
|
opaqueBuffer, bufferSize, empty,
|
|
AtpRetriesForAsp,
|
|
AtpIntervalSecondsForAsp,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingGetStatusResponse,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError)
|
|
Free(completionInfo);
|
|
|
|
return(errorCode);
|
|
|
|
} /* AspGetStatus */
|
|
|
|
AppleTalkErrorCode far AspGetSession(
|
|
long sessionListenerRefNum, /* What session listener? */
|
|
Boolean privateSocket, /* When we create the ASP connection
|
|
should it be on its own ATP socket? */
|
|
long *getSessionRefNum, /* Ref num of the created handler. */
|
|
AspIncomingSessionOpenHandler *completionRoutine,
|
|
/* Who do we call when the get session
|
|
command comes in and is completed. */
|
|
long unsigned userData) /* User data passed to the completion
|
|
routine. */
|
|
{
|
|
SessionListenerInfo sessionListenerInfo;
|
|
GetSessionHandler getSessionHandler;
|
|
|
|
/* Enqueue a handler for an incoming OpenSessionCommand for a specified
|
|
session listener. Return a unique identifier for the get-session, so
|
|
that it can be canceled later. When the OpenSession command comes in,
|
|
we'll call the supplied completion routine with the following arguments:
|
|
|
|
errorCode - AppleTalkErrorCode; how did the operation complete?
|
|
userData - long unsigned; as passed to this routine.
|
|
socket - long; the fully qualified AppleTalk socket on
|
|
which the ASP session is now open.
|
|
sessionRefNum - long; the session reference number for the new
|
|
ASP session.
|
|
|
|
The type of this routine is AspIncomingSessionOpenHandler.
|
|
*/
|
|
|
|
/* Okay, find the session listener... */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
sessionListenerInfo = FindSessionListenerInfoFor(sessionListenerRefNum);
|
|
if (sessionListenerInfo is Empty or sessionListenerInfo->closing)
|
|
{
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSessionListener);
|
|
}
|
|
|
|
/* Okay, build up and enqueue a new GetSession structure. */
|
|
|
|
getSessionHandler = (GetSessionHandler)Calloc(sizeof(*getSessionHandler), 1);
|
|
if (getSessionHandler is empty)
|
|
{
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotEnqueueHandler);
|
|
}
|
|
if ((getSessionHandler->sessionRefNum = GetNextSessionRefNum()) < 0)
|
|
{
|
|
ErrorLog("AspGetSession", ISevError, __LINE__, UnknownPort,
|
|
IErrAspBadError, IMsgAspBadError,
|
|
Insert0());
|
|
Free(getSessionHandler);
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotEnqueueHandler);
|
|
}
|
|
getSessionHandler->privateSocket = privateSocket;
|
|
getSessionHandler->userData = userData;
|
|
getSessionHandler->sessionOpenHandler = completionRoutine;
|
|
getSessionHandler->mySessionListenerInfo = Link(sessionListenerInfo);
|
|
TakeLock(AspLock);
|
|
if (sessionListenerInfo->closing)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
Free(getSessionHandler);
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSessionListener);
|
|
}
|
|
getSessionHandler->next = sessionListenerInfo->getSessionHandlers;
|
|
sessionListenerInfo->getSessionHandlers = Link(getSessionHandler);
|
|
*getSessionRefNum = getSessionHandler->sessionRefNum;
|
|
ReleaseLock(AspLock);
|
|
|
|
/* All set! */
|
|
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspGetSession */
|
|
|
|
AppleTalkErrorCode far AspCancelGetSession(
|
|
long sessionListenerRefNum, /* What session listener? */
|
|
long getSessionRefNum) /* What GetSession to cancel? */
|
|
{
|
|
SessionListenerInfo sessionListenerInfo;
|
|
GetSessionHandler getSessionHandler, previousGetSessionHandler = Empty;
|
|
|
|
/* Okay, find the session listener... */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
sessionListenerInfo = FindSessionListenerInfoFor(sessionListenerRefNum);
|
|
if (sessionListenerInfo is Empty or sessionListenerInfo->closing)
|
|
{
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSessionListener);
|
|
}
|
|
|
|
/* Okay, find the GetSession. */
|
|
|
|
TakeLock(AspLock);
|
|
for (getSessionHandler = sessionListenerInfo->getSessionHandlers;
|
|
getSessionHandler isnt Empty;
|
|
previousGetSessionHandler = getSessionHandler,
|
|
getSessionHandler = getSessionHandler->next)
|
|
if (getSessionHandler->sessionRefNum is getSessionRefNum)
|
|
break;
|
|
if (getSessionHandler is Empty)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchGetSession);
|
|
}
|
|
|
|
/* Okay, we've found the target GetSession handler, remove him from the
|
|
list. */
|
|
|
|
if (getSessionHandler->openInProgress)
|
|
getSessionHandler->canceled = True;
|
|
Link(getSessionHandler);
|
|
ReleaseLock(AspLock);
|
|
if (not UnlinkGetSessionHandler(getSessionHandler))
|
|
UnlinkGetSessionHandler(getSessionHandler);
|
|
|
|
/* All set! */
|
|
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspCancelGetSession */
|
|
|
|
AppleTalkErrorCode far AspOpenSessionOnNode(
|
|
int port, /* "Our" port number. */
|
|
long existingAtpSocket, /* "-1" if we should open our own Atp socket
|
|
for the new session; if ">= 0" this is
|
|
an already open Atp socket on which to
|
|
open the session. */
|
|
int desiredSocket, /* Desired socket; zero for dynamic. */
|
|
AppleTalkAddress serverAddress, /* Address of the server (SLS). */
|
|
long *ourSocket, /* The full address of the WSS that we
|
|
opened. */
|
|
AspIncomingOpenReplyHandler *completionRoutine,
|
|
/* Routine to call with the OpenReply. */
|
|
long unsigned userData) /* User data to pass to the completion
|
|
routine. */
|
|
{
|
|
AppleTalkErrorCode errorCode;
|
|
char userBytes[AtpUserBytesSize];
|
|
CompletionInfo completionInfo;
|
|
SessionInfo sessionInfo;
|
|
long index, tempSocket;
|
|
AppleTalkAddress ourAddress;
|
|
long sessionRefNum;
|
|
|
|
/* Post an OpenSession request. Call a supplied completion routine when the
|
|
request completes (or times-out). The arguments are as follows:
|
|
|
|
errorCode - AppleTalkErrorCode; how did the request complete?
|
|
userData - long unsigned; as passed to this routine.
|
|
sessionRefNum - long; session reference number of the new session.
|
|
*/
|
|
|
|
/* First call? */
|
|
|
|
if (not sessionMaintenanceTimerStarted)
|
|
if ((errorCode = InitializeAsp()) isnt ATnoError)
|
|
return(errorCode);
|
|
|
|
/* Try to open the WSS. */
|
|
|
|
if (existingAtpSocket < 0)
|
|
{
|
|
if ((errorCode = AtpOpenSocketOnNode(&tempSocket, port, empty,
|
|
desiredSocket, empty, 0)) isnt
|
|
ATnoError)
|
|
return(errorCode);
|
|
}
|
|
else
|
|
tempSocket = existingAtpSocket;
|
|
if (MapSocketToAddress(tempSocket, &ourAddress) isnt ATnoError)
|
|
{
|
|
ErrorLog("AspOpenSessionOnNode", ISevError, __LINE__, port,
|
|
IErrAspCouldntMapAddress, IMsgAspCouldntMapAddress,
|
|
Insert0());
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(tempSocket, Empty, (long unsigned)0);
|
|
return(ATinternalError);
|
|
}
|
|
if (ourSocket isnt empty)
|
|
*ourSocket = tempSocket;
|
|
|
|
/* Set up userBytes for an open-session request. */
|
|
|
|
userBytes[AspCommandTypeOffset] = AspOpenSessionCommand;
|
|
userBytes[AspWssNumberOffset] = ourAddress.socketNumber;
|
|
userBytes[AspVersionNumberOffset] = AspVersionBytes[0];
|
|
userBytes[AspVersionNumberOffset + 1] = AspVersionBytes[1];
|
|
|
|
/* Get a new sessionRefNum and allocate a new sessionInfo. */
|
|
|
|
if ((sessionRefNum = GetNextSessionRefNum()) < 0)
|
|
{
|
|
ErrorLog("AspOpenSessionOnNode", ISevError, __LINE__, port,
|
|
IErrAspBadError, IMsgAspBadError,
|
|
Insert0());
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(tempSocket, Empty, (long unsigned)0);
|
|
return(ATaspCouldNotOpenSession);
|
|
}
|
|
sessionInfo = (SessionInfo)Calloc(sizeof(*sessionInfo), 1);
|
|
if (sessionInfo is empty)
|
|
{
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(tempSocket, Empty, (long unsigned)0);
|
|
return(ATaspCouldNotOpenSession);
|
|
}
|
|
|
|
/* Fill in what we can... */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo->sessionRefNum = sessionRefNum;
|
|
sessionInfo->serverSession = False;
|
|
sessionInfo->waitingForOpenReply = True;
|
|
sessionInfo->ourPort = port;
|
|
sessionInfo->ourSocket = tempSocket;
|
|
sessionInfo->closeOurSocket = (existingAtpSocket < 0);
|
|
sessionInfo->slsAddress = serverAddress;
|
|
sessionInfo->lastContactTime = CurrentRelativeTime();
|
|
|
|
/* Build up our completion info block. */
|
|
|
|
completionInfo = (CompletionInfo)Malloc(sizeof(*completionInfo));
|
|
if (completionInfo is empty)
|
|
{
|
|
Free(sessionInfo);
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(tempSocket, Empty, (long unsigned)0);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotOpenSession);
|
|
}
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
completionInfo->sessionRefNum = sessionRefNum;
|
|
|
|
/* Thread the sessionInfo into the lookup table. */
|
|
|
|
CheckMod(index, sessionRefNum, NumberOfAspSessionHashBuckets,
|
|
"AspOpenSession");
|
|
TakeLock(AspLock);
|
|
sessionInfo->next = sessionInfoHashBuckets[index];
|
|
sessionInfoHashBuckets[index] = Link(sessionInfo);
|
|
ReleaseLock(AspLock);
|
|
|
|
/* Post the OpenSession request to the specified address... our caller
|
|
presumably found this with a prior NBP lookup. */
|
|
|
|
errorCode = AtpPostRequest(tempSocket, serverAddress,
|
|
Empty,
|
|
empty, 0, userBytes, True,
|
|
empty, 0,
|
|
sessionInfo->sessionUserBytes,
|
|
AtpRetriesForAsp,
|
|
AtpIntervalSecondsForAsp,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingOpenSessionResponse,
|
|
(long unsigned)completionInfo);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
AspCloseSession(sessionRefNum, Empty, (long unsigned)0, True);
|
|
Free(completionInfo);
|
|
}
|
|
|
|
return(errorCode);
|
|
|
|
} /* AspOpenSessionOnNode */
|
|
|
|
AppleTalkErrorCode far AspCloseSession(
|
|
long sessionRefNum, /* Session to close. */
|
|
CloseCompletionRoutine far *closeCompletionRoutine,
|
|
/* Close completion rotuine. */
|
|
long unsigned closeUserData,
|
|
/* User data for above. */
|
|
Boolean remoteClose) /* All external callers should pass "False."
|
|
If "True" we won't send a Close command
|
|
to the remote side -- we're closing due to
|
|
receiving such a close. */
|
|
{
|
|
SessionInfo sessionInfo;
|
|
AppleTalkErrorCode closeCode;
|
|
char userBytes[AtpUserBytesSize];
|
|
GetRequestInfo getRequestInfo, nextGetRequestInfo;
|
|
|
|
if (remoteClose)
|
|
closeCode = ATaspRemoteSessionClose;
|
|
else
|
|
closeCode = ATaspLocalSessionClose;
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
if ((sessionInfo = FindSessionInfoFor(sessionRefNum)) is Empty or
|
|
sessionInfo->closing)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
|
|
/* Set about closing. */
|
|
|
|
TakeLock(AspLock);
|
|
sessionInfo->closing = True;
|
|
sessionInfo->closeContext.closeCompletionRoutine = closeCompletionRoutine;
|
|
sessionInfo->closeContext.closeUserData = closeUserData;
|
|
sessionInfo->closeCode = closeCode;
|
|
ReleaseLock(AspLock);
|
|
|
|
if (not remoteClose)
|
|
{
|
|
/* Build up the user bytes for the ATP close request. */
|
|
|
|
userBytes[AspCommandTypeOffset] = AspCloseSessionCommand;
|
|
userBytes[AspSessionIdOffset] = sessionInfo->sessionId;
|
|
userBytes[AspAttentionWordOffset] = 0;
|
|
userBytes[AspAttentionWordOffset + 1] = 0;
|
|
|
|
/* Post the request... */
|
|
|
|
AtpPostRequest(sessionInfo->ourSocket, sessionInfo->theirAddress,
|
|
Empty, empty, 0, userBytes, False, empty, 0,
|
|
empty, AtpRetriesForAsp, AtpIntervalSecondsForAsp,
|
|
ThirtySecondsTRelTimer, empty, (long unsigned)0);
|
|
|
|
}
|
|
|
|
if (sessionInfo->serverSession)
|
|
{
|
|
/* We want to terminate any pending get request handlers. */
|
|
|
|
TakeLock(AspLock);
|
|
getRequestInfo = Link(sessionInfo->getRequestInfoList);
|
|
ReleaseLock(AspLock);
|
|
while (getRequestInfo isnt Empty)
|
|
{
|
|
getRequestInfo->errorCode = closeCode;
|
|
getRequestInfo->sessionRefNum = sessionRefNum;
|
|
getRequestInfo->usersCookie = sessionInfo->usersCookie;
|
|
|
|
/* Tag if the following Unlinks will really complete a GetRequest,
|
|
thus notifing the session's owner of the close (otherwise, if
|
|
we're doing GetAnyRequests, we'll use one of these later to
|
|
notify the owner). */
|
|
|
|
if (not getRequestInfo->inUse)
|
|
sessionInfo->notifiedOwnerOfClose = True;
|
|
TakeLock(AspLock);
|
|
nextGetRequestInfo = Link(getRequestInfo->next);
|
|
ReleaseLock(AspLock);
|
|
if (not UnlinkGetRequestInfo(getRequestInfo))
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
getRequestInfo = nextGetRequestInfo;
|
|
}
|
|
}
|
|
|
|
/* Lastly, Unlink (twice) to set the close into motion. */
|
|
|
|
#if VerboseMessages
|
|
printf("ASP SessionRefNum %d closeing.\n", sessionInfo->sessionRefNum);
|
|
#endif
|
|
UnlinkSessionInfo(sessionInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
return(ATnoError);
|
|
|
|
} /* AspCloseSession */
|
|
|
|
AppleTalkErrorCode AspSetCookieForSession(
|
|
long sessionRefNum, /* Session to set cookie for. */
|
|
long unsigned cookie) /* New cookie. */
|
|
{
|
|
SessionInfo sessionInfo;
|
|
|
|
/* Find the target session. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
|
|
/* Set the cookie and run away. */
|
|
|
|
sessionInfo->usersCookie = cookie;
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspSetCookieForSession */
|
|
|
|
AppleTalkErrorCode AspGetCookieForSession(
|
|
long sessionRefNum, /* Session to set cookie for. */
|
|
long unsigned far *cookie) /* Cookie return address. */
|
|
{
|
|
SessionInfo sessionInfo;
|
|
|
|
/* Find the target session. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
|
|
/* Get the cookie and run away. */
|
|
|
|
*cookie = sessionInfo->usersCookie;
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspGetCookieForSession */
|
|
|
|
AppleTalkErrorCode far AspGetRequest(
|
|
long sessionRefNum, /* Session to read from. */
|
|
void far *opaqueBuffer, /* "Buffer" to fill with request. May be
|
|
Empty. */
|
|
int bufferSize, /* Size of buffer. */
|
|
AspIncomingCommandHandler *completionRoutine,
|
|
/* Routine to call when the request comes
|
|
in. */
|
|
long unsigned userData) /* User data passed on to the completion
|
|
routine. */
|
|
{
|
|
SessionInfo sessionInfo;
|
|
GetRequestInfo getRequestInfo;
|
|
long getRequestRefNum, index;
|
|
|
|
/* We enqueue a handler for an incoming Write or Command on a particular
|
|
server session. When one comes in we call the supplied completion
|
|
routine with the following arguments:
|
|
|
|
errorCode - AppleTalkErrorCode; condition of request.
|
|
userData - long unsigned; as passed to us.
|
|
sessionRefNum - long; the session to respond to (as passed to us).
|
|
usersCookie - This session cookie.
|
|
opaqueBuffer - void *; "buffer" space for request data, as
|
|
passed to us; if Empty was passed in this is the
|
|
actual "char *" pointer to the real Atp/Ddp
|
|
buffer that contains the request.
|
|
bufferSize - int; ammount of buffer space actually used.
|
|
requestType - short; AspWriteCommand or AspCommandCommand.
|
|
getRequestRefNum - long unsigned; used for reply.
|
|
*/
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is empty or sessionInfo->closing)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (not sessionInfo->serverSession)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNotServerSession);
|
|
}
|
|
if (bufferSize > AtpSinglePacketDataSize)
|
|
bufferSize = AtpSinglePacketDataSize;
|
|
|
|
/* Build up a new GetRequestInfo node. First find a free GetRequestRefNum.
|
|
Don't worry, in 99.9999% of the cases, this loop will execute once! */
|
|
|
|
TakeLock(AspLock);
|
|
while(True)
|
|
{
|
|
sessionInfo->mySessionListener->lastGetRequestRefNum += 1;;
|
|
if ((sessionInfo->mySessionListener->lastGetRequestRefNum += 1) < 0)
|
|
sessionInfo->mySessionListener->lastGetRequestRefNum = 0;
|
|
getRequestRefNum = sessionInfo->mySessionListener->lastGetRequestRefNum;
|
|
ReleaseLock(AspLock);
|
|
if ((getRequestInfo = FindGetRequestInfoFor(sessionInfo->mySessionListener,
|
|
getRequestRefNum)) is Empty)
|
|
break;
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
TakeLock(AspLock);
|
|
}
|
|
|
|
getRequestInfo = (GetRequestInfo)Calloc(sizeof(*getRequestInfo), 1);
|
|
if (getRequestInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotGetRequest);
|
|
}
|
|
getRequestInfo->getRequestRefNum = getRequestRefNum;
|
|
getRequestInfo->mySessionInfo = Link(sessionInfo);
|
|
getRequestInfo->mySessionListener = Link(sessionInfo->mySessionListener);
|
|
getRequestInfo->opaqueBuffer = opaqueBuffer;
|
|
getRequestInfo->bufferSize = bufferSize;
|
|
getRequestInfo->completionRoutine = completionRoutine;
|
|
getRequestInfo->userData = userData;
|
|
|
|
/* Link it up! */
|
|
|
|
TakeLock(AspLock);
|
|
if (sessionInfo->closing)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
getRequestInfo->next = sessionInfo->getRequestInfoList;
|
|
sessionInfo->getRequestInfoList = getRequestInfo;
|
|
|
|
/* Link this guy into the per-Sls getRequestInfo hash list. */
|
|
|
|
CheckMod(index, getRequestRefNum, NumGetRequestInfoHashBuckets,
|
|
"AspGetRequest");
|
|
getRequestInfo->nextForMySessionListener =
|
|
sessionInfo->mySessionListener->getRequestInfoHashBuckets[index];
|
|
sessionInfo->mySessionListener->getRequestInfoHashBuckets[index] =
|
|
Link(getRequestInfo);
|
|
ReleaseLock(AspLock);
|
|
|
|
/* All set. */
|
|
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspGetRequest */
|
|
|
|
AppleTalkErrorCode far AspGetAnyRequest(
|
|
long sessionListenerRefNum, /* Session listener from whos sessions we
|
|
should read from. */
|
|
void far *opaqueBuffer, /* "Buffer" to fill with request. May be
|
|
Empty. */
|
|
int bufferSize, /* Size of buffer. */
|
|
AspIncomingCommandHandler *completionRoutine,
|
|
/* Routine to call when the request comes
|
|
in. */
|
|
long unsigned userData) /* User data passed on to the completion
|
|
routine. */
|
|
{
|
|
SessionListenerInfo sessionListenerInfo;
|
|
GetRequestInfo getRequestInfo;
|
|
long getRequestRefNum, index;
|
|
DeferredCloseNotify deferredCloseNotify;
|
|
|
|
/* We enqueue a handler for an incoming Write or Command targeted at any
|
|
server session to the specified session listener. When one comes in
|
|
we call the supplied completion routine with the following arguments:
|
|
|
|
errorCode - AppleTalkErrorCode; condition of request.
|
|
userData - long unsigned; as passed to us.
|
|
sessionRefNum - long; the session refNum of the target of the
|
|
incoming request.
|
|
usersCookie - long; this sessions cookie.
|
|
opaqueBuffer - void *; buffer space for request data, as passed
|
|
to us; if Empty was passed in this is the
|
|
actual "char *" pointer to the real Atp/Ddp
|
|
buffer that contains the request.
|
|
bufferSize - int; ammount of buffer space actually used.
|
|
requestType - short; AspWriteCommand or AspCommandCommand.
|
|
getRequestRefNum - long unsigned; used for reply.
|
|
*/
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
sessionListenerInfo = FindSessionListenerInfoFor(sessionListenerRefNum);
|
|
if (sessionListenerInfo is empty or sessionListenerInfo->closing)
|
|
{
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSessionListener);
|
|
}
|
|
if (bufferSize > AtpSinglePacketDataSize)
|
|
bufferSize = AtpSinglePacketDataSize;
|
|
|
|
/* If there is a deferred close on the session listener, complete this
|
|
this GetAnyRequest now with the old news. */
|
|
|
|
TakeLock(AspLock);
|
|
if (sessionListenerInfo->deferredCloseNotifyList isnt Empty)
|
|
{
|
|
deferredCloseNotify = sessionListenerInfo->deferredCloseNotifyList;
|
|
sessionListenerInfo->deferredCloseNotifyList = deferredCloseNotify->next;
|
|
ReleaseLock(AspLock);
|
|
|
|
(*completionRoutine)(deferredCloseNotify->closeCode, userData,
|
|
deferredCloseNotify->sessionRefNum,
|
|
deferredCloseNotify->usersCookie,
|
|
empty, 0, 0, (long)0);
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
}
|
|
|
|
/* Build up a new GetRequestInfo node. First find a free GetRequestRefNum.
|
|
Don't worry, in 99.9999% of the cases, this loop will execute once! */
|
|
|
|
while(True)
|
|
{
|
|
sessionListenerInfo->lastGetRequestRefNum += 1;;
|
|
if ((sessionListenerInfo->lastGetRequestRefNum += 1) < 0)
|
|
sessionListenerInfo->lastGetRequestRefNum = 0;
|
|
getRequestRefNum = sessionListenerInfo->lastGetRequestRefNum;
|
|
ReleaseLock(AspLock);
|
|
if ((getRequestInfo = FindGetRequestInfoFor(sessionListenerInfo,
|
|
getRequestRefNum)) is Empty)
|
|
break;
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
TakeLock(AspLock);
|
|
}
|
|
|
|
getRequestInfo = (GetRequestInfo)Calloc(sizeof(*getRequestInfo), 1);
|
|
if (getRequestInfo is empty)
|
|
{
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotGetRequest);
|
|
}
|
|
getRequestInfo->getRequestRefNum = getRequestRefNum;
|
|
getRequestInfo->mySessionListener = Link(sessionListenerInfo);
|
|
getRequestInfo->opaqueBuffer = opaqueBuffer;
|
|
getRequestInfo->bufferSize = bufferSize;
|
|
getRequestInfo->completionRoutine = completionRoutine;
|
|
getRequestInfo->userData = userData;
|
|
|
|
/* Link it up! */
|
|
|
|
TakeLock(AspLock);
|
|
if (sessionListenerInfo->closing)
|
|
{
|
|
Free(getRequestInfo);
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSessionListener);
|
|
}
|
|
getRequestInfo->next = sessionListenerInfo->getRequestInfoList;
|
|
sessionListenerInfo->getRequestInfoList = getRequestInfo;
|
|
sessionListenerInfo->getAnyRequestsSeen = True;
|
|
|
|
/* Link this guy into the per-Sls getRequestInfo hash list. */
|
|
|
|
CheckMod(index, getRequestRefNum, NumGetRequestInfoHashBuckets,
|
|
"AspGetAnyRequest");
|
|
getRequestInfo->nextForMySessionListener =
|
|
sessionListenerInfo->getRequestInfoHashBuckets[index];
|
|
sessionListenerInfo->getRequestInfoHashBuckets[index] = Link(getRequestInfo);
|
|
ReleaseLock(AspLock);
|
|
|
|
/* All set. */
|
|
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspGetAnyRequest */
|
|
|
|
AppleTalkErrorCode far AspReply(
|
|
long sessionRefNum, /* Session to respond from. */
|
|
long getRequestRefNum, /* Request we're responding to. */
|
|
short requestType, /* Write or Command; from request. */
|
|
char far *resultCode, /* Four byte reply result code. */
|
|
void far *opaqueBuffer, /* Response data. */
|
|
int bufferSize, /* Size of response data. */
|
|
AspReplyCompleteHandler *completionRoutine,
|
|
/* Routine to call when the response
|
|
completes (may be empty). */
|
|
long unsigned userData) /* User data to pass to completion
|
|
routine. */
|
|
{
|
|
SessionInfo sessionInfo;
|
|
GetRequestInfo getRequestInfo;
|
|
CompletionInfo completionInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
/* Reply to a specified ASP request. An optional completion routine should
|
|
be called when the reply completes. Its arguments are:
|
|
|
|
errorCode - AppleTalkErrorCode; how did the reply complete?
|
|
Okay or time-out?
|
|
userData - long unsigned; user data as passed to this
|
|
routine.
|
|
sessionRefNum - long; the session's identifier.
|
|
getRequestRefNum - long; the request's identifier.
|
|
*/
|
|
|
|
if (bufferSize > AtpMaximumTotalResponseSize)
|
|
return(ATaspBufferTooBig);
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is Empty or sessionInfo->closing)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (not sessionInfo->serverSession)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNotServerSession);
|
|
}
|
|
|
|
/* Can we find the specified request (that we're trying to respond to)? */
|
|
|
|
getRequestInfo = FindGetRequestInfoFor(sessionInfo->mySessionListener,
|
|
getRequestRefNum);
|
|
if (getRequestInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchRequest);
|
|
}
|
|
if (getRequestInfo->requestType isnt requestType or
|
|
not getRequestInfo->inUse)
|
|
{
|
|
if (not getRequestInfo->inUse)
|
|
errorCode = ATaspNoOperationInProgress;
|
|
else
|
|
errorCode = ATaspWrongRequestType;
|
|
getRequestInfo->inUse = True; /* Don't call completion routine. */
|
|
if (not UnlinkGetRequestInfo(getRequestInfo))
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
/* We'll need to handle a reply complete, see if we can get the memory
|
|
for the identifier. */
|
|
|
|
completionInfo = (CompletionInfo)Malloc(sizeof(*completionInfo));
|
|
if (completionInfo is empty)
|
|
{
|
|
if (not UnlinkGetRequestInfo(getRequestInfo))
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotPostReply);
|
|
}
|
|
|
|
/* Okay, post the reply; fill in the reply complete info first. */
|
|
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
completionInfo->sessionRefNum = sessionRefNum;
|
|
completionInfo->getRequestRefNum = getRequestRefNum;
|
|
Link(sessionInfo);
|
|
errorCode = AtpPostResponse(sessionInfo->ourSocket,
|
|
getRequestInfo->source,
|
|
getRequestInfo->transactionId,
|
|
opaqueBuffer, bufferSize,
|
|
resultCode,
|
|
getRequestInfo->exactlyOnce,
|
|
IncomingRelease,
|
|
(long unsigned)completionInfo);
|
|
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
Free(completionInfo);
|
|
}
|
|
|
|
/* Free the get request info. */
|
|
|
|
if (not UnlinkGetRequestInfo(getRequestInfo))
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
|
|
/* All set! */
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} /* AspReply */
|
|
|
|
#if Iam an OS2 /* Too many stack temporaries... */
|
|
#pragma optimize ("eg", off)
|
|
#endif
|
|
|
|
AppleTalkErrorCode far AspWriteContinue(
|
|
long sessionRefNum,
|
|
long getRequestRefNum,
|
|
void far *opaqueBuffer,
|
|
int bufferSize,
|
|
AspIncomingWriteDataHandler *completionRoutine,
|
|
long unsigned userData)
|
|
{
|
|
SessionInfo sessionInfo;
|
|
GetRequestInfo getRequestInfo;
|
|
CompletionInfo completionInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
char userBytes[AtpUserBytesSize];
|
|
|
|
/* Post a WriteContinue ATP request. We call a completion routine with the
|
|
following arguments when the request completes:
|
|
|
|
errorCode - AppleTalkErrorCode; how did the request complete?
|
|
Okay or time-out?
|
|
userData - long unsigned; user data as passed to this
|
|
routine.
|
|
sessionRefNum - long; the session's identifier.
|
|
getRequestRefNum - long; the request's identifier.
|
|
opaqueBuffer - void *; "buffer" with received data, as passed
|
|
to us.
|
|
bufferSize - int; how much data is in the buffer?
|
|
*/
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is empty or sessionInfo->closing)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (not sessionInfo->serverSession)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNotServerSession);
|
|
}
|
|
if (bufferSize > AtpMaximumTotalResponseSize)
|
|
bufferSize = AtpMaximumTotalResponseSize;
|
|
|
|
/* Can we find the specified request (a WriteCommand)? */
|
|
|
|
getRequestInfo = FindGetRequestInfoFor(sessionInfo->mySessionListener,
|
|
getRequestRefNum);
|
|
if (getRequestInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchRequest);
|
|
}
|
|
TakeLock(AspLock);
|
|
if (getRequestInfo->requestType isnt AspWriteCommand or
|
|
not getRequestInfo->inUse or
|
|
getRequestInfo->writeContinueInProgress)
|
|
{
|
|
if (getRequestInfo->writeContinueInProgress)
|
|
errorCode = ATaspOperationAlreadyInProgress;
|
|
else if (not getRequestInfo->inUse)
|
|
errorCode = ATaspNoOperationInProgress;
|
|
else
|
|
errorCode = ATaspWrongRequestType;
|
|
getRequestInfo->inUse = True; /* Don't call completion routine. */
|
|
if (not UnlinkGetRequestInfo(getRequestInfo))
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
getRequestInfo->writeContinueInProgress = True;
|
|
ReleaseLock(AspLock);
|
|
|
|
/* We'll need to handle a request complete, see if we can get the memory
|
|
for the identifier. */
|
|
|
|
completionInfo = (CompletionInfo)Malloc(sizeof(*completionInfo));
|
|
if (completionInfo is empty)
|
|
{
|
|
ErrorLog("AspWriteContinue", ISevError, __LINE__, UnknownPort,
|
|
IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
if (not UnlinkGetRequestInfo(getRequestInfo))
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotPostWriteContinue);
|
|
}
|
|
|
|
/* Build the writeContinue command. */
|
|
|
|
userBytes[AspCommandTypeOffset] = AspWriteDataCommand;
|
|
userBytes[AspSessionIdOffset] = sessionInfo->sessionId;
|
|
userBytes[AspSequenceNumberOffset] =
|
|
(char)(getRequestInfo->sequenceNumber >> 8);
|
|
userBytes[AspSequenceNumberOffset + 1] =
|
|
(char)(getRequestInfo->sequenceNumber & 0xFF);
|
|
|
|
/* Build the ATP data... two bytes of expected response size. We need to
|
|
pass Atp an "opaque" descriptor (not a "char *"), so build this too! */
|
|
|
|
getRequestInfo->writeContinueData[0] = (char)(bufferSize >> 8);
|
|
getRequestInfo->writeContinueData[1] = (char)(bufferSize & 0xFF);
|
|
if ((getRequestInfo->opaqueWriteContinueData =
|
|
MakeOpaqueDataDescriptor(getRequestInfo->writeContinueData, 2,
|
|
&getRequestInfo->
|
|
freeOpaqueWriteContinueData)) is Empty)
|
|
{
|
|
ErrorLog("AspWriteContinue", ISevError, __LINE__, UnknownPort,
|
|
IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
if (not UnlinkGetRequestInfo(getRequestInfo))
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotPostWriteContinue);
|
|
}
|
|
|
|
/* Okay, post the request; fill in the completion info first. */
|
|
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
completionInfo->sessionRefNum = sessionRefNum;
|
|
completionInfo->getRequestRefNum = getRequestRefNum;
|
|
Link(sessionInfo);
|
|
errorCode = AtpPostRequest(sessionInfo->ourSocket,
|
|
sessionInfo->theirAddress,
|
|
&getRequestInfo->writeContinueTransactionId,
|
|
getRequestInfo->opaqueWriteContinueData, 2,
|
|
userBytes, True, opaqueBuffer, bufferSize, empty,
|
|
AtpInfiniteRetries, AtpIntervalSecondsForAsp,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingWriteContinueResponse,
|
|
(long unsigned)completionInfo);
|
|
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
if (not UnlinkGetRequestInfo(getRequestInfo))
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
Free(completionInfo);
|
|
}
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} /* AspWriteContinue */
|
|
|
|
#if Iam an OS2
|
|
#pragma optimize ("eg", on)
|
|
#endif
|
|
|
|
AppleTalkErrorCode far AspCommand(
|
|
long sessionRefNum, /* Session to send the command to. */
|
|
void far *opaqueCommandBuffer, /* Command buffer to send. */
|
|
int commandBufferSize, /* Size of command. */
|
|
char far *resultCode, /* Location to store the result code into.
|
|
Maybe empty, in which case the completion
|
|
routine will be passed a pointer to where
|
|
it can copy the result code byte from. */
|
|
void far *opaqueReplyBuffer, /* Buffer to hold the command reply data. */
|
|
int replyBufferSize, /* Size of above. */
|
|
AspWriteOrCommCompleteHandler *completionRoutine,
|
|
/* Routine to call on completion. */
|
|
long unsigned userData) /* User data to pass to the above routine. */
|
|
{
|
|
SessionInfo sessionInfo;
|
|
WriteOrCommandInfo commandInfo;
|
|
CompletionInfo completionInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
char userBytes[AtpUserBytesSize];
|
|
|
|
/* Post an ASP command, call a completion routine with the following
|
|
arguments:
|
|
|
|
errorCode - AppleTalkErrorCode; how did the request complete?
|
|
userData - long unsigned; as passed to us.
|
|
sessionRefNum - long; the session that posted the request.
|
|
resultCode - char *; result returned from the server.
|
|
opaqueBuffer - void *; reply "buffer" from the server, as passed
|
|
to us.
|
|
bufferSize - int; size of the above buffer.
|
|
*/
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is empty or sessionInfo->closing)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (sessionInfo->serverSession)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNotWorkstationSession);
|
|
}
|
|
|
|
/* Get the two blocks of memory that we'll need to post this request. */
|
|
|
|
if ((commandInfo = (WriteOrCommandInfo)Calloc(sizeof(*commandInfo), 1))
|
|
is empty or
|
|
(completionInfo = (CompletionInfo)Malloc(sizeof(*completionInfo)))
|
|
is empty)
|
|
{
|
|
if (commandInfo isnt empty)
|
|
Free(commandInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotPostRequest);
|
|
}
|
|
|
|
/* Complete and thread the command info. We'll increment the sequence nubmer
|
|
when we know the request got out okay. */
|
|
|
|
commandInfo->resultCode = resultCode;
|
|
TakeLock(AspLock);
|
|
commandInfo->sequenceNumber = sessionInfo->nextSequenceNumber;
|
|
sessionInfo->nextSequenceNumber += 1;
|
|
commandInfo->next = sessionInfo->writeOrCommandInfoList;
|
|
sessionInfo->writeOrCommandInfoList = commandInfo;
|
|
ReleaseLock(AspLock);
|
|
|
|
/* Build the userBytes for an ASP command. */
|
|
|
|
userBytes[AspCommandTypeOffset] = AspCommandCommand;
|
|
userBytes[AspSessionIdOffset] = sessionInfo->sessionId;
|
|
userBytes[AspSequenceNumberOffset] =
|
|
(char)(commandInfo->sequenceNumber >> 8);
|
|
userBytes[AspSequenceNumberOffset + 1] =
|
|
(char)(commandInfo->sequenceNumber & 0xFF);
|
|
|
|
/* Build up the structure that we need to know what to do on ATP
|
|
completion. */
|
|
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
completionInfo->sessionRefNum = sessionRefNum;
|
|
completionInfo->sequenceNumber = commandInfo->sequenceNumber;
|
|
|
|
/* Okay, post the request. */
|
|
|
|
Link(sessionInfo);
|
|
errorCode = AtpPostRequest(sessionInfo->ourSocket,
|
|
sessionInfo->theirAddress,
|
|
Empty,
|
|
opaqueCommandBuffer, commandBufferSize,
|
|
userBytes, True, opaqueReplyBuffer,
|
|
replyBufferSize, resultCode, AtpInfiniteRetries,
|
|
AtpIntervalSecondsForAsp,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingWriteOrCommandComplete,
|
|
(long unsigned)completionInfo);
|
|
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
RemoveFromListNoUnlink(sessionInfo->writeOrCommandInfoList, commandInfo,
|
|
next);
|
|
sessionInfo->nextSequenceNumber -= 1;
|
|
ReleaseLock(AspLock);
|
|
Free(commandInfo);
|
|
Free(completionInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
/* Request out! */
|
|
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspCommand */
|
|
|
|
AppleTalkErrorCode far AspWrite(
|
|
long sessionRefNum, /* Session to send the command to. */
|
|
void far *opaqueCommandBuffer, /* Command "buffer" to send. */
|
|
int commandBufferSize, /* Size of command. */
|
|
void far *opaqueWriteBuffer, /* "Buffer" to write to the server. */
|
|
int writeBufferSize, /* Size of write buffer. */
|
|
char far *resultCode, /* Location to store the result code into.
|
|
Maybe empty, in which case the completion
|
|
routine will be passed a pointer to where
|
|
it can copy the result code byte from. */
|
|
void far *opaqueReplyBuffer, /* "Buffer" to hold the command reply data. */
|
|
int replyBufferSize, /* Size of above. */
|
|
AspWriteOrCommCompleteHandler *completionRoutine,
|
|
/* Routine to call on completion. */
|
|
long unsigned userData) /* User data to pass to the above routine. */
|
|
{
|
|
SessionInfo sessionInfo;
|
|
WriteOrCommandInfo writeInfo;
|
|
CompletionInfo completionInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
char userBytes[AtpUserBytesSize];
|
|
|
|
/* Post an ASP command, call a completion routine with the following
|
|
arguments:
|
|
|
|
errorCode - AppleTalkErrorCode; how did the request complete?
|
|
userData - long unsigned; as passed to us.
|
|
sessionRefNum - long; the session that posted the request.
|
|
resultCode - char *; result returned from the server.
|
|
opaqueBuffer - void *; reply "buffer" from the server, as passed
|
|
to us.
|
|
bufferSize - int; size of the above buffer.
|
|
*/
|
|
|
|
if (writeBufferSize > AtpMaximumTotalResponseSize)
|
|
return(ATaspSizeError);
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is empty or sessionInfo->closing)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (sessionInfo->serverSession)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNotWorkstationSession);
|
|
}
|
|
|
|
/* Get the two blocks of memory that we'll need to post this request. */
|
|
|
|
if ((writeInfo = (WriteOrCommandInfo)Calloc(sizeof(*writeInfo), 1))
|
|
is empty or
|
|
(completionInfo = (CompletionInfo)Malloc(sizeof(*completionInfo)))
|
|
is empty)
|
|
{
|
|
if (writeInfo isnt empty)
|
|
Free(writeInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotPostRequest);
|
|
}
|
|
|
|
/* Complete and thread the write info. We'll increment the next sequence
|
|
number when we know we're going to be okay. */
|
|
|
|
writeInfo->writeCommand = True;
|
|
writeInfo->resultCode = resultCode;
|
|
writeInfo->writeOpaqueBuffer = opaqueWriteBuffer;
|
|
writeInfo->writeBufferSize = writeBufferSize;
|
|
TakeLock(AspLock);
|
|
writeInfo->sequenceNumber = sessionInfo->nextSequenceNumber;
|
|
sessionInfo->nextSequenceNumber += 1;
|
|
writeInfo->next = sessionInfo->writeOrCommandInfoList;
|
|
sessionInfo->writeOrCommandInfoList = writeInfo;
|
|
ReleaseLock(AspLock);
|
|
|
|
/* Build the userBytes for an ASP write. */
|
|
|
|
userBytes[AspCommandTypeOffset] = AspWriteCommand;
|
|
userBytes[AspSessionIdOffset] = sessionInfo->sessionId;
|
|
userBytes[AspSequenceNumberOffset] =
|
|
(char)(writeInfo->sequenceNumber >> 8);
|
|
userBytes[AspSequenceNumberOffset + 1] =
|
|
(char)(writeInfo->sequenceNumber & 0xFF);
|
|
|
|
/* Build up the structure that we need to know what to do on ATP
|
|
completion. */
|
|
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
completionInfo->sessionRefNum = sessionRefNum;
|
|
completionInfo->sequenceNumber = writeInfo->sequenceNumber;
|
|
|
|
/* Okay, post the request. */
|
|
|
|
Link(sessionInfo);
|
|
errorCode = AtpPostRequest(sessionInfo->ourSocket,
|
|
sessionInfo->theirAddress,
|
|
Empty,
|
|
opaqueCommandBuffer, commandBufferSize,
|
|
userBytes, True, opaqueReplyBuffer,
|
|
replyBufferSize, resultCode, AtpInfiniteRetries,
|
|
AtpIntervalSecondsForAsp,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingWriteOrCommandComplete,
|
|
(long unsigned)completionInfo);
|
|
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
TakeLock(AspLock);
|
|
RemoveFromListNoUnlink(sessionInfo->writeOrCommandInfoList, writeInfo,
|
|
next);
|
|
sessionInfo->nextSequenceNumber -= 1;
|
|
ReleaseLock(AspLock);
|
|
Free(writeInfo);
|
|
Free(completionInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
/* Request is out! */
|
|
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspWrite */
|
|
|
|
AppleTalkErrorCode far AspGetAttention(
|
|
long sessionRefNum, /* Session to get attention from. */
|
|
AspIncomingAttentionHandler *handler,
|
|
/* Routine to call when an attention
|
|
comes in. */
|
|
long unsigned userData) /* User data to pass on to the handler when
|
|
an attention comes in. */
|
|
{
|
|
SessionInfo sessionInfo;
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is empty or sessionInfo->closing)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (sessionInfo->serverSession)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNotWorkstationSession);
|
|
}
|
|
|
|
TakeLock(AspLock);
|
|
sessionInfo->incomingAttentionHandler = handler;
|
|
sessionInfo->userDataForAttention = userData;
|
|
ReleaseLock(AspLock);
|
|
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspGetAttention */
|
|
|
|
AppleTalkErrorCode far AspSendAttention(
|
|
long sessionRefNum, /* Session to send attention to. */
|
|
short unsigned attentionData) /* Two bytes of attention data... */
|
|
{
|
|
SessionInfo sessionInfo;
|
|
char userBytes[AtpUserBytesSize];
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is empty or sessionInfo->closing)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (not sessionInfo->serverSession)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNotServerSession);
|
|
}
|
|
|
|
/* Build up the user bytes for the ATP request. */
|
|
|
|
userBytes[AspCommandTypeOffset] = AspAttentionCommand;
|
|
userBytes[AspSessionIdOffset] = sessionInfo->sessionId;
|
|
userBytes[AspAttentionWordOffset] = (char)(attentionData >> 8);
|
|
userBytes[AspAttentionWordOffset + 1] = (char)(attentionData & 0xFF);
|
|
|
|
/* Post the request... */
|
|
|
|
errorCode = AtpPostRequest(sessionInfo->ourSocket,
|
|
sessionInfo->theirAddress,
|
|
Empty,
|
|
empty, 0, userBytes, False, empty, 0,
|
|
empty, AtpRetriesForAsp,
|
|
AtpIntervalSecondsForAsp,
|
|
ThirtySecondsTRelTimer,
|
|
empty, (long unsigned)0);
|
|
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} /* AspSendAttention */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingSlsTransaction(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
AppleTalkAddress source,
|
|
void far *buffer, /* Really "char *." */
|
|
int bufferSize,
|
|
char far *userBytes,
|
|
Boolean exactlyOnce,
|
|
TRelTimerValue trelTimerValue,
|
|
short unsigned transactionId,
|
|
short unsigned bitmap)
|
|
{
|
|
StaticForSmallStack GetSessionHandler getSessionHandler;
|
|
StaticForSmallStack long serverSessionSocket;
|
|
StaticForSmallStack Boolean inUse;
|
|
StaticForSmallStack long id;
|
|
StaticForSmallStack SessionRefNumMap sessionRefNumMap;
|
|
StaticForSmallStack unsigned char sessionId;
|
|
StaticForSmallStack char outgoingUserBytes[AtpUserBytesSize];
|
|
StaticForSmallStack SessionListenerInfo sessionListenerInfo;
|
|
StaticForSmallStack SocketInfo socketInfo;
|
|
StaticForSmallStack AppleTalkAddress sssAddress;
|
|
StaticForSmallStack long index, sessionRefNum;
|
|
SessionInfo sessionInfo;
|
|
long ourSocket;
|
|
long sessionListenerRefNum = (long)userData;
|
|
AspIncomingSessionOpenHandler *sessionOpenHandler;
|
|
Boolean needToUndefer = True;
|
|
Boolean newSocket = False;
|
|
|
|
/* Touch all formals... */
|
|
|
|
buffer, bufferSize, bitmap, trelTimerValue;
|
|
|
|
if (errorCode is ATsocketClosed)
|
|
return; /* Session listener closed... */
|
|
else if (errorCode is ATatpTransactionAborted)
|
|
return; /* Somebody canceled our get request... */
|
|
|
|
/* Find the correct session listener: */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionListenerInfo = FindSessionListenerInfoFor(sessionListenerRefNum);
|
|
if (sessionListenerInfo is Empty or sessionListenerInfo->closing)
|
|
{
|
|
if (sessionListenerInfo is Empty)
|
|
ErrorLog("IncomingSlsTransaction", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspListenerInfoMissing, IMsgAspListenerInfoMissing,
|
|
Insert0());
|
|
else
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* If we have some sort of AppleTalk error, we could try to re-enqueue the
|
|
request handler but, more than likely, the error would happen again (and
|
|
quickly) and we'd end up hanging the driver. So, just drop the handler
|
|
on the floor... */
|
|
|
|
if (errorCode isnt ATnoError and errorCode isnt ATatpResponseBufferTooSmall)
|
|
{
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspLostSLSPacket, IMsgAspLostSLSPacket,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Okay, break up on ASP command type. */
|
|
|
|
switch(userBytes[AspCommandTypeOffset])
|
|
{
|
|
case AspOpenSessionCommand:
|
|
|
|
/* First, is there a free sessionId for a new ASP session? Only
|
|
go 'round this once [sessionId's are one byte beasts]. Also check
|
|
the getSessionHandlerList, for any session Ids that may be reserved
|
|
during an open that is currently in progress. */
|
|
|
|
TakeLock(AspLock);
|
|
for (sessionId = (unsigned char)(sessionListenerInfo->lastSessionId + 1);
|
|
sessionId isnt sessionListenerInfo->lastSessionId;
|
|
sessionId += 1)
|
|
{
|
|
inUse = False;
|
|
for (sessionInfo = sessionListenerInfo->sessionList;
|
|
not inUse and sessionInfo isnt empty;
|
|
sessionInfo = sessionInfo->nextForMySls)
|
|
if (sessionInfo->sessionId is sessionId)
|
|
inUse = True;
|
|
for (getSessionHandler = sessionListenerInfo->getSessionHandlers;
|
|
not inUse and getSessionHandler isnt Empty;
|
|
getSessionHandler = getSessionHandler->next)
|
|
if (getSessionHandler->openInProgress and
|
|
getSessionHandler->sessionId is sessionId)
|
|
inUse = True;
|
|
if (not inUse)
|
|
break;
|
|
}
|
|
|
|
/* Does the session listener have a GetSession pending? We should also
|
|
be busy if we couldn't find a free session ID. */
|
|
|
|
if (sessionListenerInfo->getSessionHandlers is empty or inUse)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
if (SendAspErrorReturn(sessionListenerInfo->ourSocket, source,
|
|
transactionId, exactlyOnce, ATaspServerBusy)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__,
|
|
UnknownPort, IErrAspBadSrvrBusySend, IMsgAspBadSrvrBusySend,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* Reserve the sessionId while weÕre processing the open! */
|
|
|
|
sessionListenerInfo->lastSessionId = sessionId;
|
|
getSessionHandler = sessionListenerInfo->getSessionHandlers;
|
|
getSessionHandler->sessionId = sessionId;
|
|
getSessionHandler->openInProgress = True;
|
|
Link(getSessionHandler);
|
|
ReleaseLock(AspLock);
|
|
|
|
/* Okay, do we like the version number? */
|
|
|
|
if (userBytes[AspVersionNumberOffset] isnt AspVersionBytes[0] or
|
|
userBytes[AspVersionNumberOffset + 1] isnt AspVersionBytes[1])
|
|
{
|
|
getSessionHandler->openInProgress = False;
|
|
UnlinkGetSessionHandler(getSessionHandler);
|
|
if (SendAspErrorReturn(sessionListenerInfo->ourSocket, source,
|
|
transactionId, exactlyOnce,
|
|
ATaspBadVersionNumber)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__,
|
|
UnknownPort, IErrAspBadBadVersionSend, IMsgAspBadBadVersionSend,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* We need to open (or find) a Server Session Socket (SSS) now. There
|
|
are two choices: first, if our GetSession call requested a "private"
|
|
we need to open a new one; second, if not, we should check to see if
|
|
we can overload a previous SSS. */
|
|
|
|
TakeLock(AspLock);
|
|
if (getSessionHandler->privateSocket)
|
|
socketInfo = empty;
|
|
else
|
|
for (socketInfo = sessionListenerInfo->socketList;
|
|
socketInfo isnt empty;
|
|
socketInfo = socketInfo->next)
|
|
if (not socketInfo->privateSocket and
|
|
socketInfo->activeSessions < MaximumAspSessionsPerSocket)
|
|
break;
|
|
|
|
/* Allocate a new socket, if needed. */
|
|
|
|
if (socketInfo is empty)
|
|
{
|
|
AppleTalkAddress slsAddress;
|
|
ExtendedAppleTalkNodeNumber slsNode;
|
|
|
|
/* The SSS must be on the same AppleTalk node as our SLS. */
|
|
|
|
ReleaseLock(AspLock);
|
|
if (MapSocketToAddress(sessionListenerInfo->ourSocket,
|
|
&slsAddress) isnt ATnoError)
|
|
{
|
|
getSessionHandler->openInProgress = False;
|
|
UnlinkGetSessionHandler(getSessionHandler);
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__,
|
|
UnknownPort, IErrAspCouldntMapAddress, IMsgAspCouldntMapAddress,
|
|
Insert0());
|
|
if (SendAspErrorReturn(sessionListenerInfo->ourSocket, source,
|
|
transactionId, exactlyOnce,
|
|
ATaspServerBusy) isnt ATnoError)
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__,
|
|
UnknownPort, IErrAspBadAddrNotMappedSend, IMsgAspBadAddrNotMappedSend,
|
|
Insert0());
|
|
break;
|
|
}
|
|
slsNode.networkNumber = slsAddress.networkNumber;
|
|
slsNode.nodeNumber = slsAddress.nodeNumber;
|
|
errorCode = AtpOpenSocketOnNode(&serverSessionSocket,
|
|
sessionListenerInfo->port,
|
|
&slsNode, 0, empty, 0);
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
getSessionHandler->openInProgress = False;
|
|
UnlinkGetSessionHandler(getSessionHandler);
|
|
if (SendAspErrorReturn(sessionListenerInfo->ourSocket, source,
|
|
transactionId, exactlyOnce,
|
|
ATaspServerBusy) isnt ATnoError)
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__,
|
|
UnknownPort, IErrAspBadNoSocketsSend, IMsgAspBadNoSocketsSend,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* Okay, build a new socket structure. */
|
|
|
|
socketInfo = (SocketInfo)Calloc(sizeof(*socketInfo), 1);
|
|
if (socketInfo is empty)
|
|
{
|
|
getSessionHandler->openInProgress = False;
|
|
UnlinkGetSessionHandler(getSessionHandler);
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__,
|
|
UnknownPort, IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
AtpCloseSocketOnNode(serverSessionSocket, Empty,
|
|
(long unsigned)0);
|
|
break; /* Just let the open OpenSession request time-out... */
|
|
}
|
|
socketInfo->socket = serverSessionSocket;
|
|
socketInfo->activeSessions = 1;
|
|
socketInfo->privateSocket =
|
|
getSessionHandler->privateSocket;
|
|
TakeLock(AspLock);
|
|
socketInfo->next = sessionListenerInfo->socketList;
|
|
sessionListenerInfo->socketList = socketInfo;
|
|
ReleaseLock(AspLock);
|
|
newSocket = True;
|
|
}
|
|
else
|
|
{
|
|
serverSessionSocket = socketInfo->socket;
|
|
socketInfo->activeSessions += 1;
|
|
ReleaseLock(AspLock);
|
|
}
|
|
|
|
/* Grab our session ref num. */
|
|
|
|
sessionRefNum = getSessionHandler->sessionRefNum;
|
|
|
|
/* Now we need to think about allocating a session info block. */
|
|
|
|
sessionInfo = (SessionInfo)Calloc(sizeof(*sessionInfo), 1);
|
|
sessionRefNumMap =
|
|
(SessionRefNumMap)Calloc(sizeof(*sessionRefNumMap), 1);
|
|
|
|
if (sessionInfo is empty or
|
|
sessionRefNumMap is empty)
|
|
{
|
|
getSessionHandler->openInProgress = False;
|
|
UnlinkGetSessionHandler(getSessionHandler);
|
|
if (sessionInfo isnt empty)
|
|
Free(sessionInfo);
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
DecrementSocketUsage(sessionListenerInfo,
|
|
serverSessionSocket);
|
|
break;
|
|
}
|
|
|
|
/* Can we get the address to send the eventual open reply too? */
|
|
|
|
if (MapSocketToAddress(serverSessionSocket, &sssAddress)
|
|
isnt ATnoError)
|
|
{
|
|
getSessionHandler->openInProgress = False;
|
|
UnlinkGetSessionHandler(getSessionHandler);
|
|
Free(sessionInfo);
|
|
Free(sessionRefNumMap);
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspCouldntMapAddress, IMsgAspCouldntMapAddress,
|
|
Insert0());
|
|
DecrementSocketUsage(sessionListenerInfo,
|
|
serverSessionSocket);
|
|
break;
|
|
}
|
|
|
|
/* Okay, start filling in the session node... */
|
|
|
|
sessionInfo->mySessionListener = Link(sessionListenerInfo);
|
|
sessionInfo->sessionRefNum = sessionRefNum;
|
|
sessionInfo->sessionId = sessionId;
|
|
sessionInfo->serverSession = True;
|
|
sessionInfo->ourPort = sessionListenerInfo->port;
|
|
sessionInfo->ourSocket = serverSessionSocket;
|
|
sessionInfo->theirAddress = source;
|
|
sessionInfo->theirAddress.socketNumber = userBytes[AspWssNumberOffset];
|
|
if (MapSocketToAddress(sessionListenerInfo->ourSocket,
|
|
&sessionInfo->slsAddress) isnt ATnoError)
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspCouldntMapAddress, IMsgAspCouldntMapAddress,
|
|
Insert0());
|
|
sessionInfo->lastContactTime = CurrentRelativeTime();
|
|
|
|
/* Pretty easy so far, but before we're finished, we need to thread
|
|
the node into three (yes three) lookup tables... Sigh. */
|
|
|
|
/* First the mapping table for ASP address to session ref number. */
|
|
|
|
TakeLock(AspLock);
|
|
if (sessionListenerInfo->closing or getSessionHandler->canceled)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
/* Undo above link, when filling sessionInfo. */
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
Free(sessionInfo);
|
|
Free(sessionRefNumMap);
|
|
getSessionHandler->openInProgress = False;
|
|
UnlinkGetSessionHandler(getSessionHandler);
|
|
DecrementSocketUsage(sessionListenerInfo,
|
|
serverSessionSocket);
|
|
break;
|
|
}
|
|
CheckMod(index, (((sessionInfo->ourSocket & 0xFFFF) << 8) +
|
|
sessionId),
|
|
NumberOfSessionRefNumBuckets, "IncomingSlsTransaction");
|
|
sessionRefNumMap->socket = sessionInfo->ourSocket;
|
|
sessionRefNumMap->sessionId = sessionId;
|
|
sessionRefNumMap->sessionRefNum = sessionRefNum;
|
|
sessionRefNumMap->next = sessionRefNumMapHashBuckets[index];
|
|
sessionRefNumMapHashBuckets[index] = sessionRefNumMap;
|
|
|
|
/* Now the session ref number hash table. */
|
|
|
|
CheckMod(index, sessionRefNum, NumberOfAspSessionHashBuckets,
|
|
"IncomingSlsTransaction");
|
|
sessionInfo->next = sessionInfoHashBuckets[index];
|
|
sessionInfoHashBuckets[index] = Link(sessionInfo);
|
|
|
|
/* Finaly onto the session per SLS list... */
|
|
|
|
sessionInfo->nextForMySls = sessionListenerInfo->sessionList;
|
|
sessionListenerInfo->sessionList = sessionInfo;
|
|
Link(sessionInfo);
|
|
ReleaseLock(AspLock);
|
|
|
|
/* Send the open reply. */
|
|
|
|
outgoingUserBytes[AspSssNumberOffset] = sssAddress.socketNumber;
|
|
outgoingUserBytes[AspSessionIdOffset] = sessionId;
|
|
outgoingUserBytes[AspErrorCodeOffset] =
|
|
(char)((unsigned short)ATnoError >> 8);
|
|
outgoingUserBytes[AspErrorCodeOffset + 1] = (char)(ATnoError & 0xFF);
|
|
if (AtpPostResponse(sessionListenerInfo->ourSocket, source,
|
|
transactionId, empty, 0, outgoingUserBytes,
|
|
exactlyOnce, empty, (long unsigned)0)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspBadOpenOkaySend, IMsgAspBadOpenOkaySend,
|
|
Insert0());
|
|
|
|
/* Now, we should start tickling on this session! */
|
|
|
|
outgoingUserBytes[AspCommandTypeOffset] = AspTickleCommand;
|
|
outgoingUserBytes[AspSessionIdOffset] = sessionId;
|
|
outgoingUserBytes[AspErrorCodeOffset] = 0;
|
|
outgoingUserBytes[AspErrorCodeOffset + 1] = 0;
|
|
if (AtpPostRequest(sessionInfo->ourSocket,
|
|
sessionInfo->theirAddress,
|
|
&sessionInfo->tickleTransactionId,
|
|
empty, 0, outgoingUserBytes, False,
|
|
empty, 0, empty, AtpInfiniteRetries,
|
|
AspTickleSeconds,
|
|
ThirtySecondsTRelTimer,
|
|
empty, (long unsigned)0)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspCouldntStartTickle, IMsgAspCouldntStartTickle,
|
|
Insert0());
|
|
|
|
/* If we're a new Atp socket, post a few GetRequest handlers to be
|
|
shared by all sessions operating on this socket. */
|
|
|
|
if (newSocket)
|
|
for (index = 0; index < GetRequestHandlersPerSocket; index += 1)
|
|
{
|
|
if (AtpEnqueueRequestHandler(&id, sessionInfo->ourSocket,
|
|
Empty, 0, Empty,
|
|
IncomingSssTransaction,
|
|
(long unsigned)sessionInfo->
|
|
ourSocket) isnt ATnoError)
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__,
|
|
UnknownPort, IErrAspCouldNotEnqueue,
|
|
IMsgAspCouldNotEnqueue, Insert0());
|
|
}
|
|
|
|
/* Lastly, we're ready to untread the session open handler and invoke
|
|
the completion routine! The latter comes for free by "fer sure"
|
|
unlinking the getRequestInfo. */
|
|
|
|
getSessionHandler->socket = sessionInfo->ourSocket;
|
|
getSessionHandler->sessionRefNum = sessionInfo->sessionRefNum;
|
|
if (not UnlinkGetSessionHandler(getSessionHandler))
|
|
UnlinkGetSessionHandler(getSessionHandler);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
break;
|
|
|
|
case AspTickleCommand:
|
|
|
|
/* Find the correct session info node, and note that we've heard
|
|
a peep from him recently. */
|
|
|
|
sessionId = (unsigned char)userBytes[AspSessionIdOffset];
|
|
|
|
TakeLock(AspLock);
|
|
for (sessionInfo = sessionListenerInfo->sessionList;
|
|
sessionInfo isnt empty;
|
|
sessionInfo = sessionInfo->nextForMySls)
|
|
if (sessionInfo->sessionId is sessionId)
|
|
break;
|
|
if (sessionInfo is empty)
|
|
{
|
|
/* This might be okay if tickling got started before the OpenSession
|
|
reply arrived. */
|
|
|
|
ReleaseLock(AspLock);
|
|
ErrorLog("IncomingSlsTransaction", ISevVerbose, __LINE__,
|
|
UnknownPort, IErrAspSessionInfoMissing, IMsgAspSessionInfoMissing,
|
|
Insert0());
|
|
break;
|
|
}
|
|
Link(sessionInfo);
|
|
ReleaseLock(AspLock);
|
|
|
|
#if VerboseMessages
|
|
printf("Tickle SSS (%d); sesRefNum = %d, sessionId = %d.\n",
|
|
sessionInfo->ourSocket,
|
|
sessionInfo->sessionRefNum, sessionId);
|
|
#endif
|
|
sessionInfo->lastContactTime = CurrentRelativeTime();
|
|
UnlinkSessionInfo(sessionInfo);
|
|
break;
|
|
|
|
case AspGetStatusCommand:
|
|
Link(sessionListenerInfo);
|
|
sessionListenerInfo->statusSendInProgress = True;
|
|
if (AtpPostResponse(sessionListenerInfo->ourSocket, source,
|
|
transactionId,
|
|
sessionListenerInfo->opaqueServiceStatus,
|
|
sessionListenerInfo->serviceStatusSize,
|
|
getStatusResponseUserBytes, exactlyOnce,
|
|
SendStatusComplete,
|
|
(long unsigned)sessionListenerInfo) isnt ATnoError)
|
|
|
|
{
|
|
SendStatusComplete(ATnoError, (long unsigned)sessionListenerInfo,
|
|
source, transactionId);
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspBadStatusSend, IMsgAspBadStatusSend,
|
|
Insert0());
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ErrorLog("IncomingSlsTransaction", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspBadCommandToSLS, IMsgAspBadCommandToSLS,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* Re-enqueue the request handler. */
|
|
|
|
if (AtpEnqueueRequestHandler(empty, sessionListenerInfo->ourSocket, empty, 0,
|
|
empty, IncomingSlsTransaction,
|
|
(long unsigned)sessionListenerInfo->
|
|
sessionListenerRefNum) isnt ATnoError)
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspCouldNotEnqueue, IMsgAspCouldNotEnqueue,
|
|
Insert0());
|
|
|
|
/* Now, see, that didn't hurt much... */
|
|
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
if (needToUndefer)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
return;
|
|
|
|
} /* IncomingSlsTransaction */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingSssTransaction(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
AppleTalkAddress source,
|
|
void far *buffer, /* Really "char *." */
|
|
int bufferSize,
|
|
char far *userBytes,
|
|
Boolean exactlyOnce,
|
|
TRelTimerValue trelTimerValue,
|
|
short unsigned transactionId,
|
|
short unsigned bitmap)
|
|
{
|
|
GetRequestInfo getRequestInfo;
|
|
long bufferingSessionRefNum = (long)userData;
|
|
SessionInfo sessionInfo;
|
|
long id;
|
|
short requestType;
|
|
Boolean needToUndefer = True;
|
|
short unsigned sequenceNumber;
|
|
void far *opaqueBuffer;
|
|
long ourSocket = (long)userData;
|
|
|
|
/* Touch unused formal... */
|
|
|
|
bitmap, trelTimerValue;
|
|
|
|
if (errorCode is ATatpResponseBufferTooSmall)
|
|
errorCode = ATaspBufferTooSmall;
|
|
else if (errorCode is ATsocketClosed)
|
|
return; /* Session closed... */
|
|
else if (errorCode is ATatpTransactionAborted)
|
|
return; /* Somebody canceled our get request... */
|
|
else if (errorCode isnt ATnoError)
|
|
{
|
|
ErrorLog("IncomingSssTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspIncomingError, IMsgAspIncomingError,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
/* Okay, using the socket (userData) and the packet's sessionId to find the
|
|
target sessionInfo. */
|
|
|
|
if ((sessionInfo =
|
|
FindSessionInfoForSocket(ourSocket,
|
|
(unsigned char)userBytes[AspSessionIdOffset]))
|
|
is empty or sessionInfo->closing)
|
|
{
|
|
if (sessionInfo is Empty)
|
|
ErrorLog("IncomingSssTransaction", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspTargetSessionMissing, IMsgAspTargetSessionMissing,
|
|
Insert0());
|
|
else
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Note that we've heard from this guy... so that session maintenance doesn't
|
|
close this session out from under us! */
|
|
|
|
sessionInfo->lastContactTime = CurrentRelativeTime();
|
|
|
|
/* Process the request. */
|
|
|
|
switch (requestType = userBytes[AspCommandTypeOffset])
|
|
{
|
|
case AspCommandCommand:
|
|
case AspWriteCommand:
|
|
|
|
/* Is there a getRequest handler for this session that not currently
|
|
being used? First check the per-session handler list, then check
|
|
the global list on the ServiceListenerInfo. */
|
|
|
|
TakeLock(AspLock);
|
|
for (getRequestInfo = sessionInfo->getRequestInfoList;
|
|
getRequestInfo isnt empty;
|
|
getRequestInfo = getRequestInfo->next)
|
|
if (not getRequestInfo->inUse)
|
|
break;
|
|
if (getRequestInfo is Empty)
|
|
for (getRequestInfo =
|
|
sessionInfo->mySessionListener->getRequestInfoList;
|
|
getRequestInfo isnt empty;
|
|
getRequestInfo = getRequestInfo->next)
|
|
if (not getRequestInfo->inUse)
|
|
break;
|
|
if (getRequestInfo is Empty)
|
|
{
|
|
/* We don't have a usable queued get request handler.
|
|
We should cancel the ATP transaction so that when the request
|
|
is resent, ATP will give it to us again and we can recheck for
|
|
a request handler at that time. Otherwise, ATP would think
|
|
that the given TID was already delived [it had been given to
|
|
ASP] and would not deliver it again, thus the ASP client would
|
|
never see the request and we would end up with a "hung"
|
|
transaction. */
|
|
|
|
ReleaseLock(AspLock);
|
|
if (AtpCancelResponse(sessionInfo->ourSocket, source,
|
|
transactionId, False) isnt ATnoError)
|
|
ErrorLog("IncomingSssTransaction", ISevError, __LINE__,
|
|
UnknownPort, IErrAspBadCancelResponse, IMsgAspBadCancelResponse,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* Verify sequence number. */
|
|
|
|
sequenceNumber =
|
|
(short unsigned)((userBytes[AspSequenceNumberOffset] << 8) +
|
|
(unsigned char)userBytes[AspSequenceNumberOffset + 1]);
|
|
if (sequenceNumber isnt sessionInfo->nextExpectedSequenceNumber)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
if (AtpCancelResponse(sessionInfo->ourSocket, source,
|
|
transactionId, False) isnt ATnoError)
|
|
ErrorLog("IncomingSssTransaction", ISevError, __LINE__,
|
|
UnknownPort, IErrAspBadCancelResponse, IMsgAspBadCancelResponse,
|
|
Insert0());
|
|
break;
|
|
}
|
|
else
|
|
sessionInfo->nextExpectedSequenceNumber += 1;
|
|
|
|
/* Tag the get request info as in-use. */
|
|
|
|
getRequestInfo->inUse = True;
|
|
Link(getRequestInfo);
|
|
ReleaseLock(AspLock);
|
|
|
|
/* Place the information from the ATP request into our getRequest
|
|
node that we'll need for the reply. */
|
|
|
|
getRequestInfo->requestType = requestType;
|
|
getRequestInfo->source = source;
|
|
getRequestInfo->exactlyOnce = exactlyOnce;
|
|
getRequestInfo->transactionId = transactionId;
|
|
getRequestInfo->sequenceNumber = sequenceNumber;
|
|
|
|
/* Move as much as we can of the ATP data into the ASP buffer. If
|
|
our caller didn't give us any place to put the data, just pass
|
|
a pointer to the actual "char *" Atp/Ddp incoming buffer. */
|
|
|
|
if (getRequestInfo->opaqueBuffer is Empty)
|
|
opaqueBuffer = (void *)buffer;
|
|
else
|
|
{
|
|
if (bufferSize > getRequestInfo->bufferSize)
|
|
{
|
|
errorCode = ATaspBufferTooSmall;
|
|
bufferSize = getRequestInfo->bufferSize;
|
|
}
|
|
MoveToOpaque(getRequestInfo->opaqueBuffer, 0, buffer, bufferSize);
|
|
opaqueBuffer = getRequestInfo->opaqueBuffer;
|
|
}
|
|
|
|
{
|
|
AspIncomingCommandHandler *completionRoutine =
|
|
getRequestInfo->completionRoutine;
|
|
long unsigned userData = getRequestInfo->userData;
|
|
long sessionRefNum = sessionInfo->sessionRefNum;
|
|
long getRequestRefNum = getRequestInfo->getRequestRefNum;
|
|
long unsigned cookie = sessionInfo->usersCookie;
|
|
|
|
#if IamNot a Primos
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
#endif
|
|
(*completionRoutine)(errorCode, userData, sessionRefNum, cookie,
|
|
opaqueBuffer, bufferSize, requestType,
|
|
getRequestRefNum);
|
|
#if Iam a Primos
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
#endif
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
needToUndefer = False;
|
|
}
|
|
break;
|
|
|
|
case AspCloseSessionCommand:
|
|
|
|
/* Send CloseSession reply & close the session. */
|
|
|
|
AtpPostResponse(sessionInfo->ourSocket, source, transactionId,
|
|
empty, 0, empty, exactlyOnce, empty,
|
|
(long unsigned)0);
|
|
|
|
/* Close the session! */
|
|
|
|
AspCloseSession(sessionInfo->sessionRefNum, Empty, (long unsigned)0,
|
|
True);
|
|
break;
|
|
|
|
default:
|
|
ErrorLog("IncomingSssTransaction", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspBadCommand, IMsgAspBadCommand,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* Re-enqueue the ATP read. */
|
|
|
|
if (AtpEnqueueRequestHandler(&id, sessionInfo->ourSocket,
|
|
Empty, 0, Empty, IncomingSssTransaction,
|
|
(long unsigned)sessionInfo->ourSocket)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingSssTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspCouldNotEnqueue, IMsgAspCouldNotEnqueue,
|
|
Insert0());
|
|
|
|
UnlinkSessionInfo(sessionInfo);
|
|
|
|
if (needToUndefer)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
|
|
return;
|
|
|
|
} /* IncomingSssTransaction */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingWssTransaction(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
AppleTalkAddress source,
|
|
void far *buffer,
|
|
/* Really "char *." */
|
|
int bufferSize,
|
|
char far *userBytes,
|
|
Boolean exactlyOnce,
|
|
TRelTimerValue trelTimerValue,
|
|
short unsigned transactionId,
|
|
short unsigned bitmap)
|
|
{
|
|
StaticForSmallStack SessionInfo sessionInfo;
|
|
StaticForSmallStack WriteOrCommandInfo writeInfo;
|
|
StaticForSmallStack long id;
|
|
StaticForSmallStack int writeSize;
|
|
long sessionRefNum = (long)userData;
|
|
Boolean callAttentionRoutine = False;
|
|
short unsigned attentionData;
|
|
short unsigned sequenceNumber;
|
|
AspIncomingAttentionHandler *incomingAttentionHandler;
|
|
long unsigned userDataForAttention;
|
|
|
|
/* Touch unused formal... */
|
|
|
|
bitmap, trelTimerValue;
|
|
|
|
if (errorCode is ATsocketClosed)
|
|
return; /* Session closed... */
|
|
else if (errorCode is ATatpTransactionAborted)
|
|
return; /* Somebody canceled our get request... */
|
|
else if (errorCode is ATatpResponseBufferTooSmall)
|
|
errorCode = ATnoError; /* We've got extra data, but who cares? */
|
|
else if (errorCode isnt ATnoError)
|
|
{
|
|
ErrorLog("IncomingWssTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspIncomingError, IMsgAspIncomingError,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* Find and verify our sessionInfo. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((sessionInfo = FindSessionInfoFor(sessionRefNum)) is empty
|
|
or sessionInfo->closing)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
ErrorLog("IncomingWssTransaction", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspSessionInfoMissing, IMsgAspSessionInfoMissing,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
if (sessionInfo->serverSession or
|
|
(unsigned char)userBytes[AspSessionIdOffset] isnt sessionInfo->sessionId)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
ErrorLog("IncomingWssTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspBadError, IMsgAspBadError,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Okay, handle the command. */
|
|
|
|
sessionInfo->lastContactTime = CurrentRelativeTime();
|
|
|
|
switch(userBytes[AspCommandTypeOffset])
|
|
{
|
|
case AspTickleCommand:
|
|
#if VerboseMessages
|
|
printf("Tickle WSS (%d); sesRefNum = %d, sessionId = %d.\n",
|
|
sessionInfo->ourSocket,
|
|
sessionInfo->sessionRefNum,
|
|
sessionInfo->sessionId);
|
|
#endif
|
|
break;
|
|
|
|
case AspCloseSessionCommand:
|
|
|
|
/* Send CloseSession reply & close the session. */
|
|
|
|
AtpPostResponse(sessionInfo->ourSocket, source, transactionId,
|
|
empty, 0, empty, exactlyOnce, empty,
|
|
(long unsigned)0);
|
|
AspCloseSession(sessionRefNum, Empty, (long unsigned)0, True);
|
|
break;
|
|
|
|
case AspAttentionCommand:
|
|
TakeLock(AspLock);
|
|
if (sessionInfo->incomingAttentionHandler isnt empty)
|
|
{
|
|
callAttentionRoutine = True;
|
|
incomingAttentionHandler = sessionInfo->incomingAttentionHandler;
|
|
userDataForAttention = sessionInfo->userDataForAttention;
|
|
attentionData =
|
|
(short unsigned)((userBytes[AspAttentionWordOffset] << 8) +
|
|
(unsigned char)userBytes[AspAttentionWordOffset + 1]);
|
|
}
|
|
ReleaseLock(AspLock);
|
|
|
|
/* Send AttentionReply */
|
|
|
|
AtpPostResponse(sessionInfo->ourSocket, source, transactionId,
|
|
empty, 0, empty, exactlyOnce, empty,
|
|
(long unsigned)0);
|
|
break;
|
|
|
|
case AspWriteDataCommand:
|
|
|
|
/* Try to find a matching write command. */
|
|
|
|
TakeLock(AspLock);
|
|
sequenceNumber =
|
|
(short unsigned)((userBytes[AspSequenceNumberOffset] << 8) +
|
|
(unsigned char)userBytes[AspSequenceNumberOffset + 1]);
|
|
for (writeInfo = sessionInfo->writeOrCommandInfoList;
|
|
writeInfo isnt empty;
|
|
writeInfo = writeInfo->next)
|
|
if (writeInfo->sequenceNumber is sequenceNumber)
|
|
break;
|
|
if (writeInfo is empty)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
break; /* No luck, ignore the request. */
|
|
}
|
|
if (not writeInfo->writeCommand)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
ErrorLog("IncomingWssTransaction", ISevWarning, __LINE__,
|
|
UnknownPort, IErrAspNotWriteCommand, IMsgAspNotWriteCommand,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* How much data can the server take? */
|
|
|
|
if (bufferSize < AspWriteDataSize)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
ErrorLog("IncomingWssTransaction", ISevWarning, __LINE__,
|
|
UnknownPort, IErrAspBadWritePacket, IMsgAspBadWritePacket,
|
|
Insert0());
|
|
break;
|
|
}
|
|
writeSize = (((unsigned char *)buffer)[0] << 8) +
|
|
((unsigned char *)buffer)[1];
|
|
writeInfo->acceptedBytes = writeSize;
|
|
if (writeSize > writeInfo->writeBufferSize)
|
|
writeSize = writeInfo->writeBufferSize;
|
|
|
|
/* Post the response... */
|
|
|
|
writeInfo->writeReplyPosted = True;
|
|
ReleaseLock(AspLock);
|
|
AtpPostResponse(sessionInfo->ourSocket, source, transactionId,
|
|
writeInfo->writeOpaqueBuffer, writeSize,
|
|
empty, exactlyOnce, empty, (long unsigned)0);
|
|
break;
|
|
|
|
default:
|
|
ErrorLog("IncomingWssTransaction", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspFunnyCommand, IMsgAspFunnyCommand,
|
|
Insert0());
|
|
break;
|
|
|
|
}
|
|
|
|
/* Re-enqueue the requestHandler. */
|
|
|
|
if (AtpEnqueueRequestHandler(&id, sessionInfo->ourSocket,
|
|
Empty, 0, Empty,
|
|
IncomingWssTransaction,
|
|
(long unsigned)sessionRefNum) isnt ATnoError)
|
|
ErrorLog("IncomingWssTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspCouldNotEnqueue, IMsgAspCouldNotEnqueue,
|
|
Insert0());
|
|
|
|
/* Call attention handler, if needed. */
|
|
|
|
if (callAttentionRoutine)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*incomingAttentionHandler)(ATnoError, userDataForAttention,
|
|
sessionInfo->sessionRefNum,
|
|
attentionData);
|
|
}
|
|
else
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
|
|
UnlinkSessionInfo(sessionInfo);
|
|
|
|
return;
|
|
|
|
} /* IncomingWssTransaction */
|
|
|
|
ExternForVisibleFunction SessionInfo
|
|
FindSessionInfoForSocket(long socket,
|
|
unsigned char sessionId)
|
|
{
|
|
long index;
|
|
SessionRefNumMap sessionRefNumMap;
|
|
SessionInfo sessionInfo;
|
|
long sessionRefNum;
|
|
|
|
/* First, given the socket and the session ID of an ASP session, we
|
|
need to find the session reference number. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
TakeLock(AspLock);
|
|
CheckMod(index, (((socket & 0xFFFF) << 8) + sessionId),
|
|
NumberOfSessionRefNumBuckets, "FindSessionInfoForSocket");
|
|
for (sessionRefNumMap = sessionRefNumMapHashBuckets[index];
|
|
sessionRefNumMap isnt empty;
|
|
sessionRefNumMap = sessionRefNumMap->next)
|
|
if (socket is sessionRefNumMap->socket and
|
|
sessionId is sessionRefNumMap->sessionId)
|
|
break;
|
|
if (sessionRefNumMap is empty)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(empty);
|
|
}
|
|
|
|
/* Okay, now we have the session reference number... find the correct
|
|
session info node. */
|
|
|
|
sessionRefNum = sessionRefNumMap->sessionRefNum;
|
|
ReleaseLock(AspLock);
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(sessionInfo);
|
|
|
|
} /* FindSessionInfoForSocket */
|
|
|
|
ExternForVisibleFunction AppleTalkErrorCode SendAspErrorReturn(
|
|
long sourceSocket,
|
|
AppleTalkAddress destination,
|
|
short unsigned transactionId,
|
|
Boolean exactlyOnce,
|
|
int errorCode)
|
|
{
|
|
char userBytes[AtpUserBytesSize];
|
|
|
|
userBytes[AspSssNumberOffset] = 0;
|
|
userBytes[AspSessionIdOffset] = 0;
|
|
userBytes[AspErrorCodeOffset] = (char)((unsigned short)errorCode >> 8);
|
|
userBytes[AspErrorCodeOffset + 1] = (char)(errorCode & 0xFF);
|
|
|
|
return(AtpPostResponse(sourceSocket, destination, transactionId, empty, 0,
|
|
userBytes, exactlyOnce, empty,
|
|
(long unsigned)0));
|
|
|
|
} /* SendAspErrorReturn */
|
|
|
|
ExternForVisibleFunction void DecrementSocketUsage(
|
|
SessionListenerInfo sessionListenerInfo,
|
|
long socket)
|
|
{
|
|
SocketInfo socketInfo, previousSocketInfo;
|
|
|
|
/* Walk a socket list looking for a given socket, if found decrement its
|
|
usage count; if zero, free the node. */
|
|
|
|
DeferAtpPackets();
|
|
DeferTimerChecking();
|
|
TakeLock(AspLock);
|
|
for (socketInfo = sessionListenerInfo->socketList,
|
|
previousSocketInfo = empty;
|
|
socketInfo isnt empty;
|
|
previousSocketInfo = socketInfo,
|
|
socketInfo = socketInfo->next)
|
|
if (socketInfo->socket is socket)
|
|
break;
|
|
|
|
if (socketInfo is empty)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
ErrorLog("DecrementSocketUsage", ISevError, __LINE__, UnknownPort,
|
|
IErrAspSocketNotFound, IMsgAspSocketNotFound,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
socketInfo->activeSessions -= 1;
|
|
if (socketInfo->activeSessions < 0)
|
|
{
|
|
ErrorLog("DecrementSocketUsage", ISevError, __LINE__, UnknownPort,
|
|
IErrAspBadUsageCount, IMsgAspBadUsageCount,
|
|
Insert0());
|
|
socketInfo->activeSessions = 0;
|
|
}
|
|
|
|
if (socketInfo->activeSessions is 0)
|
|
{
|
|
if (previousSocketInfo is empty)
|
|
sessionListenerInfo->socketList = socketInfo->next;
|
|
else
|
|
previousSocketInfo->next = socketInfo->next;
|
|
ReleaseLock(AspLock);
|
|
AtpCloseSocketOnNode(socketInfo->socket, Empty, (long unsigned)0);
|
|
Free(socketInfo);
|
|
}
|
|
else
|
|
ReleaseLock(AspLock);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* DecrementSocketUsage */
|
|
|
|
ExternForVisibleFunction AppleTalkErrorCode InitializeAsp(void)
|
|
{
|
|
|
|
/* Start the session maintenance timer... */
|
|
|
|
TakeLock(AspLock);
|
|
if (sessionMaintenanceTimerStarted)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
return(ATnoError);
|
|
}
|
|
sessionMaintenanceTimerStarted = True;
|
|
ReleaseLock(AspLock);
|
|
|
|
StartTimer(SessionMaintenanceTimerExpired, AspSessionMaintenanceSeconds,
|
|
0, empty);
|
|
|
|
return(ATnoError);
|
|
|
|
} /* InitializeAsp */
|
|
|
|
ExternForVisibleFunction void far
|
|
SessionMaintenanceTimerExpired(long unsigned timerId,
|
|
int dataSize,
|
|
char far *incomingAdditionalData)
|
|
{
|
|
SessionInfo sessionInfo, nextSessionInfo;
|
|
int index;
|
|
long unsigned now = CurrentRelativeTime();
|
|
|
|
/* Touch unused formals... */
|
|
|
|
timerId, dataSize, incomingAdditionalData;
|
|
|
|
/* Walk through all of the active session to see if any have died.
|
|
"Bring out your dead..." */
|
|
|
|
DeferAtpPackets();
|
|
for (index = 0; index < NumberOfAspSessionHashBuckets; index += 1)
|
|
{
|
|
TakeLock(AspLock);
|
|
sessionInfo = Link(sessionInfoHashBuckets[index]);
|
|
ReleaseLock(AspLock);
|
|
while (sessionInfo isnt empty)
|
|
{
|
|
nextSessionInfo = sessionInfo->next;
|
|
#if VerboseMessages
|
|
printf("SesRefNum = %d; Now = %u; LastContactTime = %u.\n",
|
|
sessionInfo->sessionRefNum, now,
|
|
sessionInfo->lastContactTime);
|
|
#endif
|
|
if (sessionInfo->lastContactTime + AspSessionMaintenanceSeconds < now)
|
|
{
|
|
if (AspCloseSession(sessionInfo->sessionRefNum, Empty,
|
|
(long unsigned)0, False) isnt
|
|
ATnoError)
|
|
ErrorLog("SessionMaintenanceTimerExpired", ISevError,
|
|
__LINE__, UnknownPort, IErrAspCouldNotCloseSess,
|
|
IMsgAspCouldNotCloseSess, Insert0());
|
|
#if VerboseMessages
|
|
printf("Session maintenance; closing sessionRefNum = %d.\n",
|
|
sessionInfo->sessionRefNum);
|
|
#endif
|
|
}
|
|
else
|
|
/* "I'm not dead yet..." */ ;
|
|
|
|
/* Move to next sesion on list. */
|
|
|
|
TakeLock(AspLock);
|
|
nextSessionInfo = Link(sessionInfo->next);
|
|
ReleaseLock(AspLock);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
sessionInfo = nextSessionInfo;
|
|
}
|
|
}
|
|
|
|
/* Restart the timer and exit. */
|
|
|
|
StartTimer(SessionMaintenanceTimerExpired, AspSessionMaintenanceSeconds,
|
|
0, empty);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* SessionMaintenanceTimerExpired */
|
|
|
|
ExternForVisibleFunction SessionInfo FindSessionInfoFor(long sessionRefNum)
|
|
{
|
|
SessionInfo sessionInfo;
|
|
long index;
|
|
|
|
/* Given a session ref num, return its session info structure. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
CheckMod(index, sessionRefNum, NumberOfAspSessionHashBuckets,
|
|
"FindSessionInfoFor");
|
|
|
|
TakeLock(AspLock);
|
|
for (sessionInfo = sessionInfoHashBuckets[index];
|
|
sessionInfo isnt empty;
|
|
sessionInfo = sessionInfo->next)
|
|
if (sessionInfo->sessionRefNum is sessionRefNum)
|
|
break;
|
|
|
|
if (sessionInfo isnt Empty)
|
|
Link(sessionInfo);
|
|
ReleaseLock(AspLock);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(sessionInfo);
|
|
|
|
} /* FindSessionInfoFor */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingRelease(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
short unsigned transactionId)
|
|
{
|
|
CompletionInfo completionInfo = (CompletionInfo)incomingUserData;
|
|
AspReplyCompleteHandler *completionRoutine;
|
|
long unsigned userData;
|
|
long sessionRefNum;
|
|
long getRequestRefNum;
|
|
SessionInfo sessionInfo;
|
|
|
|
/* Touch unused formals... */
|
|
|
|
source, transactionId;
|
|
|
|
completionRoutine =
|
|
(AspReplyCompleteHandler *)completionInfo->completionRoutine;
|
|
|
|
/* We have a completion routine to call, pull out the data, free the info
|
|
node, call the completion routine. */
|
|
|
|
userData = completionInfo->userData;
|
|
sessionRefNum = completionInfo->sessionRefNum;
|
|
getRequestRefNum = completionInfo->getRequestRefNum;
|
|
Free(completionInfo);
|
|
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
UnlinkSessionInfo(sessionInfo); /* Undo the link surrounding the
|
|
Atp transaction */
|
|
|
|
if (completionRoutine isnt Empty)
|
|
(*completionRoutine)(errorCode, userData, sessionRefNum,
|
|
getRequestRefNum);
|
|
|
|
UnlinkSessionInfo(sessionInfo); /* The implied Link, above. */
|
|
|
|
return;
|
|
|
|
} /* IncomingRelease */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingWriteContinueResponse(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
void far *opaqueResponseBuffer,
|
|
int responseBufferSize,
|
|
char far *responseUserBytes,
|
|
short unsigned transactionId)
|
|
{
|
|
CompletionInfo completionInfo = (CompletionInfo)incomingUserData;
|
|
AspIncomingWriteDataHandler *completionRoutine;
|
|
long unsigned userData;
|
|
long sessionRefNum;
|
|
long getRequestRefNum;
|
|
SessionInfo sessionInfo;
|
|
|
|
/* Touch unused formals... */
|
|
|
|
source, transactionId, responseUserBytes;
|
|
|
|
completionRoutine =
|
|
(AspIncomingWriteDataHandler *)completionInfo->completionRoutine;
|
|
userData = completionInfo->userData;
|
|
sessionRefNum = completionInfo->sessionRefNum;
|
|
getRequestRefNum = completionInfo->getRequestRefNum;
|
|
Free(completionInfo);
|
|
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
UnlinkSessionInfo(sessionInfo); /* Undo the link surrounding the
|
|
Atp transaction */
|
|
|
|
(*completionRoutine)(errorCode, userData, sessionRefNum, getRequestRefNum,
|
|
opaqueResponseBuffer, responseBufferSize);
|
|
|
|
UnlinkSessionInfo(sessionInfo); /* The implied Link, above. */
|
|
|
|
return;
|
|
|
|
} /* IncomingWriteContinueResponse */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingGetStatusResponse(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
void far *opaqueResponseBuffer,
|
|
int responseBufferSize,
|
|
char far *responseUserBytes,
|
|
short unsigned transactionId)
|
|
{
|
|
CompletionInfo completionInfo = (CompletionInfo)incomingUserData;
|
|
AspIncomingStatusHandler *completionRoutine;
|
|
long unsigned userData;
|
|
|
|
/* Touch unused formals... */
|
|
|
|
source, transactionId, responseUserBytes;
|
|
|
|
completionRoutine =
|
|
(AspIncomingStatusHandler *)completionInfo->completionRoutine;
|
|
userData = completionInfo->userData;
|
|
Free(completionInfo);
|
|
|
|
(*completionRoutine)(errorCode, userData, opaqueResponseBuffer,
|
|
responseBufferSize);
|
|
|
|
return;
|
|
|
|
} /* IncomingGetStatusResponse */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingOpenSessionResponse(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
void far *responseBuffer,
|
|
/* Really "char *." */
|
|
int responseBufferSize,
|
|
char far *responseUserBytes,
|
|
short unsigned transactionId)
|
|
{
|
|
CompletionInfo completionInfo = (CompletionInfo)incomingUserData;
|
|
AspIncomingOpenReplyHandler *completionRoutine;
|
|
long unsigned userData;
|
|
long sessionRefNum;
|
|
SessionInfo sessionInfo;
|
|
char tickleUserBytes[AtpUserBytesSize];
|
|
long id;
|
|
|
|
/* Touch unused formals... */
|
|
|
|
responseBuffer, responseBufferSize, transactionId;
|
|
|
|
/* Extract our completion information. */
|
|
|
|
completionRoutine =
|
|
(AspIncomingOpenReplyHandler *)completionInfo->completionRoutine;
|
|
userData = completionInfo->userData;
|
|
sessionRefNum = completionInfo->sessionRefNum;
|
|
Free(completionInfo);
|
|
|
|
/* See if we can find the sessionInfo, and make sure it's in a state that
|
|
we like (California, no doubt). */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((sessionInfo = FindSessionInfoFor(sessionRefNum)) is empty or
|
|
sessionInfo->closing)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
ErrorLog("IncomingOpenSessionResponse", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspSessionInfoMissing, IMsgAspSessionInfoMissing,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATaspCouldNotOpenSession, userData, (long)0);
|
|
return;
|
|
}
|
|
if (sessionInfo->serverSession or not sessionInfo->waitingForOpenReply)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
ErrorLog("IncomingOpenSessionResponse", ISevError, __LINE__, UnknownPort,
|
|
IErrAspSessionInfoBad, IMsgAspSessionInfoBad,
|
|
Insert0());
|
|
AspCloseSession(sessionRefNum, Empty, (long unsigned)0, False);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATaspCouldNotOpenSession, userData, (long)0);
|
|
return;
|
|
}
|
|
|
|
/* Okay, all set check error codes. */
|
|
|
|
if (errorCode is ATnoError)
|
|
errorCode = (responseUserBytes[AspErrorCodeOffset] << 8) +
|
|
(unsigned char)(responseUserBytes[AspErrorCodeOffset + 1]);
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
AspCloseSession(sessionRefNum, Empty, (long unsigned)0, False);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, (long)0);
|
|
return;
|
|
}
|
|
|
|
/* Okay fill in some more of the session info. */
|
|
|
|
sessionInfo->theirAddress = source;
|
|
sessionInfo->theirAddress.socketNumber =
|
|
responseUserBytes[AspSssNumberOffset];
|
|
sessionInfo->sessionId = (unsigned char)responseUserBytes[AspSessionIdOffset];
|
|
sessionInfo->lastContactTime = CurrentRelativeTime();
|
|
sessionInfo->waitingForOpenReply = False;
|
|
|
|
/* Well, all looks pretty good so far, start to tickle the SLS on the
|
|
other side of this session. */
|
|
|
|
tickleUserBytes[AspCommandTypeOffset] = AspTickleCommand;
|
|
tickleUserBytes[AspSessionIdOffset] = sessionInfo->sessionId;
|
|
tickleUserBytes[AspErrorCodeOffset] = 0;
|
|
tickleUserBytes[AspErrorCodeOffset+ 1] = 0;
|
|
errorCode = AtpPostRequest(sessionInfo->ourSocket,
|
|
sessionInfo->slsAddress,
|
|
Empty,
|
|
empty, 0, tickleUserBytes, False,
|
|
empty, 0, empty, AtpInfiniteRetries,
|
|
AspTickleSeconds,
|
|
ThirtySecondsTRelTimer,
|
|
empty, (long unsigned)0);
|
|
if (errorCode isnt ATnoError)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
AspCloseSession(sessionRefNum, Empty, (long unsigned)0, False);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, (long)0);
|
|
return;
|
|
}
|
|
|
|
/* Okay, enqueue a ReqeustHandler on the WSS. */
|
|
|
|
if ((errorCode = AtpEnqueueRequestHandler(&id, sessionInfo->ourSocket,
|
|
Empty, 0, Empty,
|
|
IncomingWssTransaction,
|
|
(long unsigned)sessionRefNum))
|
|
isnt ATnoError)
|
|
{
|
|
UnlinkSessionInfo(sessionInfo);
|
|
AspCloseSession(sessionRefNum, Empty, (long unsigned)0, False);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, (long)0);
|
|
return;
|
|
}
|
|
|
|
/* Okay, the workstation session is now in full operation. */
|
|
|
|
UnlinkSessionInfo(sessionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnoError, userData, sessionRefNum);
|
|
return;
|
|
|
|
} /* IncomingOpenSessionResponse */
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingWriteOrCommandComplete(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
void far *opaqueResponseBuffer,
|
|
int responseBufferSize,
|
|
char far *responseUserBytes,
|
|
short unsigned transactionId)
|
|
{
|
|
StaticForSmallStack SessionInfo sessionInfo;
|
|
StaticForSmallStack WriteOrCommandInfo writeOrCommandInfo, previousWriteOrCommandInfo;
|
|
CompletionInfo completionInfo = (CompletionInfo)incomingUserData;
|
|
AspWriteOrCommCompleteHandler *completionRoutine;
|
|
long unsigned userData;
|
|
long sessionRefNum;
|
|
short unsigned sequenceNumber;
|
|
int acceptedBytes = 0;
|
|
|
|
/* Touch unused formals... */
|
|
|
|
source, transactionId;
|
|
|
|
/* Extract our completion information. */
|
|
|
|
completionRoutine =
|
|
(AspWriteOrCommCompleteHandler *)completionInfo->completionRoutine;
|
|
userData = completionInfo->userData;
|
|
sessionRefNum = completionInfo->sessionRefNum;
|
|
sequenceNumber = completionInfo->sequenceNumber;
|
|
Free(completionInfo);
|
|
|
|
if (errorCode is ATatpResponseBufferTooSmall)
|
|
errorCode = ATaspSizeError;
|
|
|
|
/* Find our corresponding sessionInfo and write or command info. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is empty or (errorCode isnt ATnoError and
|
|
errorCode isnt ATaspSizeError and
|
|
errorCode isnt ATatpTransactionAborted))
|
|
{
|
|
UnlinkSessionInfo(sessionInfo); /* Possible above Link. */
|
|
UnlinkSessionInfo(sessionInfo); /* Possible Atp transaction Link. */
|
|
if (errorCode isnt ATatpTransactionAborted)
|
|
ErrorLog("IncomingWriteOrCommandComplete", ISevWarning, __LINE__,
|
|
UnknownPort, IErrAspSessionInfoMissing,
|
|
IMsgAspSessionInfoMissing, Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (errorCode isnt ATatpTransactionAborted)
|
|
errorCode = ATaspCouldNotPostRequest;
|
|
(*completionRoutine)(errorCode, userData, sessionRefNum,
|
|
empty, empty, 0, 0);
|
|
return;
|
|
}
|
|
UnlinkSessionInfo(sessionInfo); /* Undo the link surrounding the
|
|
Atp transaction */
|
|
TakeLock(AspLock);
|
|
for (writeOrCommandInfo = sessionInfo->writeOrCommandInfoList,
|
|
previousWriteOrCommandInfo = empty;
|
|
writeOrCommandInfo isnt empty;
|
|
previousWriteOrCommandInfo = writeOrCommandInfo,
|
|
writeOrCommandInfo = writeOrCommandInfo->next)
|
|
if (writeOrCommandInfo->sequenceNumber is sequenceNumber)
|
|
break;
|
|
if (writeOrCommandInfo is empty)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
ErrorLog("IncomingWriteOrCommandComplete", ISevWarning, __LINE__,
|
|
UnknownPort, IErrAspCommandInfoMissing, IMsgAspCommandInfoMissing,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATaspCouldNotPostRequest, userData, sessionRefNum,
|
|
empty, empty, 0, 0);
|
|
return;
|
|
}
|
|
|
|
/* Un-thread the write or command info... */
|
|
|
|
if (previousWriteOrCommandInfo is empty)
|
|
sessionInfo->writeOrCommandInfoList = writeOrCommandInfo->next;
|
|
else
|
|
previousWriteOrCommandInfo->next = writeOrCommandInfo->next;
|
|
ReleaseLock(AspLock);
|
|
|
|
/* Okay, call the completion routine... */
|
|
|
|
if (writeOrCommandInfo->writeCommand)
|
|
{
|
|
if (writeOrCommandInfo->writeReplyPosted and
|
|
writeOrCommandInfo->acceptedBytes <
|
|
writeOrCommandInfo->writeBufferSize)
|
|
errorCode = ATaspSizeError;
|
|
acceptedBytes = writeOrCommandInfo->acceptedBytes;
|
|
}
|
|
Free(writeOrCommandInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, sessionRefNum, responseUserBytes,
|
|
opaqueResponseBuffer, responseBufferSize, acceptedBytes);
|
|
UnlinkSessionInfo(sessionInfo);
|
|
return;
|
|
|
|
} /* IncomingWriteOrCommandComplete */
|
|
|
|
ExternForVisibleFunction long GetNextSessionRefNum(void)
|
|
{
|
|
Boolean inUse, wrapped = False;
|
|
int index;
|
|
SessionListenerInfo sessionListenerInfo;
|
|
GetSessionHandler getSessionHandler;
|
|
SessionInfo sessionInfo;
|
|
|
|
/* Pick a new session refernence number for this guy... Why I'm
|
|
bothering to check to see if the sessionRefNum is currently in
|
|
is use is a real mystery... it won't overflow for some 500 years
|
|
at a rate of 10,000 new connections per day... */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
inUse = True;
|
|
TakeLock(AspLock);
|
|
while(inUse)
|
|
{
|
|
inUse = False;
|
|
if ((lastSessionRefNum += 1) < 0)
|
|
{
|
|
lastSessionRefNum = 0;
|
|
if (wrapped)
|
|
{
|
|
inUse = True;
|
|
break;
|
|
}
|
|
wrapped = True;
|
|
}
|
|
|
|
/* Check the active sessions for the session refNum. */
|
|
|
|
for (index = 0;
|
|
not inUse and index < NumberOfAspSessionHashBuckets;
|
|
index += 1)
|
|
for (sessionInfo = sessionInfoHashBuckets[index];
|
|
not inUse and sessionInfo isnt empty;
|
|
sessionInfo = sessionInfo->next)
|
|
if (sessionInfo->sessionRefNum is lastSessionRefNum)
|
|
inUse = True;
|
|
|
|
/* Checking the pending GetSessions for the refNum. */
|
|
|
|
for (sessionListenerInfo = sessionListenerInfoHead;
|
|
not inUse and sessionListenerInfo isnt Empty;
|
|
sessionListenerInfo = sessionListenerInfo->next)
|
|
for (getSessionHandler = sessionListenerInfo->getSessionHandlers;
|
|
not inUse and getSessionHandler isnt Empty;
|
|
getSessionHandler = getSessionHandler->next)
|
|
if (getSessionHandler->sessionRefNum is lastSessionRefNum)
|
|
inUse = True;
|
|
}
|
|
ReleaseLock(AspLock);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
if (inUse)
|
|
return((long)-1);
|
|
else
|
|
return(lastSessionRefNum);
|
|
|
|
} /* GetNextSessionRefNum */
|
|
|
|
ExternForVisibleFunction SessionListenerInfo
|
|
FindSessionListenerInfoFor(long sessionListenerRefNum)
|
|
{
|
|
SessionListenerInfo sessionListenerInfo;
|
|
|
|
TakeLock(AspLock);
|
|
for (sessionListenerInfo = sessionListenerInfoHead;
|
|
sessionListenerInfo isnt empty;
|
|
sessionListenerInfo = sessionListenerInfo->next)
|
|
if (sessionListenerInfo->sessionListenerRefNum is sessionListenerRefNum)
|
|
break;
|
|
|
|
if (sessionListenerInfo isnt Empty)
|
|
Link(sessionListenerInfo);
|
|
|
|
ReleaseLock(AspLock);
|
|
|
|
return(sessionListenerInfo);
|
|
|
|
} /* FindSessionListenerInfoFor */
|
|
|
|
ExternForVisibleFunction GetRequestInfo
|
|
FindGetRequestInfoFor(SessionListenerInfo sessionListenerInfo,
|
|
long getRequestRefNum)
|
|
{
|
|
long index;
|
|
GetRequestInfo getRequestInfo;
|
|
|
|
/* Look in the per-Sls hash table for the specified getRequestRefNum. */
|
|
|
|
CheckMod(index, getRequestRefNum, NumGetRequestInfoHashBuckets,
|
|
"FindGetRequestInfoFor");
|
|
|
|
TakeLock(AspLock);
|
|
for (getRequestInfo = sessionListenerInfo->getRequestInfoHashBuckets[index];
|
|
getRequestInfo isnt Empty;
|
|
getRequestInfo = getRequestInfo->nextForMySessionListener)
|
|
if (getRequestInfo->getRequestRefNum is getRequestRefNum)
|
|
{
|
|
Link(getRequestInfo);
|
|
ReleaseLock(AspLock);
|
|
return(getRequestInfo);
|
|
}
|
|
|
|
ReleaseLock(AspLock);
|
|
return(Empty);
|
|
|
|
} /* FindGetRequestInfoFor */
|
|
|
|
ExternForVisibleFunction Boolean far
|
|
UnlinkGetRequestInfo(GetRequestInfo getRequestInfo)
|
|
{
|
|
long index;
|
|
|
|
if (getRequestInfo is Empty)
|
|
return(False);
|
|
|
|
/* Are we the last referant? */
|
|
|
|
TakeLock(AspLock);
|
|
if (not UnlinkNoFree(getRequestInfo))
|
|
{
|
|
ReleaseLock(AspLock);
|
|
return(False);
|
|
}
|
|
|
|
/* Yes. A GetRequestInfo lives on two lists:
|
|
|
|
1. Either a "next" chain off a SessionInfo or a SessionListenerInfo;
|
|
mySessionInfo will be Empty in the latter case.
|
|
|
|
2. The per-Sls hash list (off the SessionListenerInfo).
|
|
|
|
Remove the passed node from both lists, and then free the node. */
|
|
|
|
if (getRequestInfo->mySessionInfo is Empty)
|
|
{
|
|
/* Remove from the "next" (GetAnyRequest) list hanging off the session
|
|
listener info structure. */
|
|
|
|
if (RemoveFromListNoUnlink(getRequestInfo->mySessionListener->
|
|
getRequestInfoList, getRequestInfo,
|
|
next) is Empty)
|
|
{
|
|
/* We should always be found... if not we have data structure corruption
|
|
problems. */
|
|
|
|
ErrorLog("UnlinkGetRequestInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspGetRequestListBad, IMsgAspGetRequestListBad,
|
|
Insert0());
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
/* Remove from the "next" (GetRequest) list handing off the session
|
|
info structure. */
|
|
|
|
if (RemoveFromListNoUnlink(getRequestInfo->mySessionInfo->
|
|
getRequestInfoList, getRequestInfo,
|
|
next) is Empty)
|
|
{
|
|
/* We should always be found... if not we have data structure corruption
|
|
problems. */
|
|
|
|
ErrorLog("UnlinkGetRequestInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspGetRequestListBad, IMsgAspGetRequestListBad,
|
|
Insert0());
|
|
}
|
|
|
|
}
|
|
|
|
/* Remove from per-Sls hash list. */
|
|
|
|
CheckMod(index, getRequestInfo->getRequestRefNum,
|
|
NumGetRequestInfoHashBuckets, "UnlinkGetRequestInfo");
|
|
if (RemoveFromListNoUnlink(getRequestInfo->mySessionListener->
|
|
getRequestInfoHashBuckets[index],
|
|
getRequestInfo, nextForMySessionListener) is
|
|
Empty)
|
|
{
|
|
/* We should always be found... if not we have data structure corruption
|
|
problems. */
|
|
|
|
ErrorLog("UnlinkGetRequestInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspGetRequestListBad, IMsgAspGetRequestListBad,
|
|
Insert0());
|
|
}
|
|
ReleaseLock(AspLock);
|
|
|
|
/* If requested, call the completion routine. */
|
|
|
|
if (not getRequestInfo->inUse)
|
|
(*getRequestInfo->completionRoutine)(getRequestInfo->errorCode,
|
|
getRequestInfo->userData,
|
|
getRequestInfo->sessionRefNum,
|
|
getRequestInfo->usersCookie,
|
|
getRequestInfo->opaqueBuffer,
|
|
getRequestInfo->bufferSize,
|
|
getRequestInfo->requestType,
|
|
getRequestInfo->getRequestRefNum);
|
|
|
|
/* Okay, now we can undo the back links. */
|
|
|
|
if (getRequestInfo->mySessionInfo isnt Empty)
|
|
UnlinkSessionInfo(getRequestInfo->mySessionInfo);
|
|
UnlinkSessionListenerInfo(getRequestInfo->mySessionListener);
|
|
|
|
/* We're set, free the beast, and run away. */
|
|
|
|
if (getRequestInfo->freeOpaqueWriteContinueData)
|
|
FreeOpaqueDataDescriptor(getRequestInfo->opaqueWriteContinueData);
|
|
Free(getRequestInfo);
|
|
return(True);
|
|
|
|
} /* UnlinkGetRequestInfo */
|
|
|
|
ExternForVisibleFunction Boolean far
|
|
UnlinkGetSessionHandler(GetSessionHandler getSessionHandler)
|
|
{
|
|
/* Are we the last referant? */
|
|
|
|
TakeLock(AspLock);
|
|
if (not UnlinkNoFree(getSessionHandler))
|
|
{
|
|
ReleaseLock(AspLock);
|
|
return(False);
|
|
}
|
|
|
|
/* Yes, we are... remove from the list, call the completion routine, Unlink from
|
|
the session listener and free the beast. */
|
|
|
|
if (RemoveFromListNoUnlink(getSessionHandler->mySessionListenerInfo->
|
|
getSessionHandlers,
|
|
getSessionHandler, next) is Empty)
|
|
ErrorLog("UnlinkGetSessionHandler", ISevError, __LINE__, UnknownPort,
|
|
IErrAspGetSessionListBad, IMsgAspGetSessionListBad,
|
|
Insert0());
|
|
ReleaseLock(AspLock);
|
|
|
|
if (getSessionHandler->openInProgress)
|
|
(*getSessionHandler->sessionOpenHandler)(getSessionHandler->errorCode,
|
|
getSessionHandler->userData,
|
|
getSessionHandler->socket,
|
|
getSessionHandler->sessionRefNum);
|
|
UnlinkSessionListenerInfo(getSessionHandler->mySessionListenerInfo);
|
|
Free(getSessionHandler);
|
|
return(True);
|
|
|
|
} /* UnlinkGetSessionHandler */
|
|
|
|
ExternForVisibleFunction void far
|
|
UnlinkSessionListenerInfo(SessionListenerInfo sessionListenerInfo)
|
|
{
|
|
DeferredCloseNotify deferredCloseNotify, nextDeferredCloseNotify;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
if (sessionListenerInfo is Empty)
|
|
return;
|
|
|
|
/* Are we the last referant? */
|
|
|
|
TakeLock(AspLock);
|
|
if (not UnlinkNoFree(sessionListenerInfo))
|
|
{
|
|
ReleaseLock(AspLock);
|
|
return;
|
|
}
|
|
|
|
/* Remove him from our list. */
|
|
|
|
if (RemoveFromListNoUnlink(sessionListenerInfoHead, sessionListenerInfo,
|
|
next) is Empty)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
ErrorLog("UnlinkSessionListenerInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspListenerInfoMissing, IMsgAspListenerInfoMissing,
|
|
Insert0());
|
|
}
|
|
else
|
|
ReleaseLock(AspLock);
|
|
|
|
/* If we have any closes that we've deferred notification for... we're
|
|
out of luck now, so free 'em. */
|
|
|
|
for (deferredCloseNotify = sessionListenerInfo->deferredCloseNotifyList;
|
|
deferredCloseNotify isnt Empty;
|
|
deferredCloseNotify = nextDeferredCloseNotify)
|
|
{
|
|
nextDeferredCloseNotify = deferredCloseNotify->next;
|
|
Free(deferredCloseNotify);
|
|
}
|
|
|
|
/* Close the ATP socket that we're listening on. */
|
|
|
|
if (sessionListenerInfo->closeSocket)
|
|
{
|
|
if ((errorCode = AtpCloseSocketOnNode(sessionListenerInfo->ourSocket,
|
|
AspServiceListenerCloseComplete,
|
|
(long unsigned)sessionListenerInfo))
|
|
isnt ATnoError)
|
|
AspServiceListenerCloseComplete(ATnoError,
|
|
(long unsigned)sessionListenerInfo, 0);
|
|
}
|
|
else
|
|
AspServiceListenerCloseComplete(ATnoError,
|
|
(long unsigned)sessionListenerInfo, 0);
|
|
|
|
/* All set. */
|
|
|
|
return;
|
|
|
|
} /* UnlinkSessionListenerInfo */
|
|
|
|
ExternForVisibleFunction void far
|
|
AspServiceListenerCloseComplete(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
long cookie)
|
|
{
|
|
SessionListenerInfo sessionListenerInfo = (SessionListenerInfo)userData;
|
|
|
|
/* If there is a completion routine, call it. */
|
|
|
|
if (sessionListenerInfo->closeContext.closeCompletionRoutine isnt Empty)
|
|
(*(sessionListenerInfo->closeContext.closeCompletionRoutine))(
|
|
errorCode, sessionListenerInfo->closeContext.closeUserData,
|
|
sessionListenerInfo->sessionListenerRefNum);
|
|
|
|
/* Finaly, free the session listener... */
|
|
|
|
if (sessionListenerInfo->serviceStatusSize > 0)
|
|
Free(sessionListenerInfo->serviceStatus);
|
|
if (sessionListenerInfo->freeOpaqueServiceStatus)
|
|
FreeOpaqueDataDescriptor(sessionListenerInfo->opaqueServiceStatus);
|
|
Free(sessionListenerInfo);
|
|
return;
|
|
|
|
} /* AspServiceListenerCloseComplete */
|
|
|
|
ExternForVisibleFunction void far
|
|
UnlinkSessionInfo(SessionInfo sessionInfo)
|
|
{
|
|
SessionRefNumMap sessionRefNumMap, previousSessionRefNumMap;
|
|
GetRequestInfo getRequestInfo;
|
|
long index;
|
|
AppleTalkErrorCode errorCode = ATnoError;
|
|
|
|
if (sessionInfo is Empty)
|
|
return;
|
|
|
|
/* Are we the last referant. */
|
|
|
|
TakeLock(AspLock);
|
|
if (not UnlinkNoFree(sessionInfo))
|
|
{
|
|
ReleaseLock(AspLock);
|
|
return;
|
|
}
|
|
|
|
/* Yes, remove from the session ref num hash list. */
|
|
|
|
CheckMod(index, sessionInfo->sessionRefNum, NumberOfAspSessionHashBuckets,
|
|
"UnlinkSessionInfo");
|
|
if (RemoveFromListNoUnlink(sessionInfoHashBuckets[index], sessionInfo,
|
|
next) is Empty)
|
|
ErrorLog("UnlinkSessionInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspSessionInfoMissing, IMsgAspSessionInfoMissing,
|
|
Insert0());
|
|
|
|
/* Okay, now for server sessions, we need to unthread from the session list
|
|
hanging off the SLS. */
|
|
|
|
if (sessionInfo->serverSession)
|
|
{
|
|
if (RemoveFromListNoUnlink(sessionInfo->mySessionListener->sessionList,
|
|
sessionInfo, nextForMySls) is Empty)
|
|
ErrorLog("UnlinkSessionInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspNotOnSLSList, IMsgAspNotOnSLSList,
|
|
Insert0());
|
|
|
|
/* Remove this fellow from the SessionRefNumMap. */
|
|
|
|
CheckMod(index, (((sessionInfo->ourSocket & 0xFFFF) << 8) +
|
|
sessionInfo->sessionId), NumberOfSessionRefNumBuckets,
|
|
"UnlinkSessionInfo");
|
|
for (sessionRefNumMap = sessionRefNumMapHashBuckets[index],
|
|
previousSessionRefNumMap = empty;
|
|
sessionRefNumMap isnt empty;
|
|
previousSessionRefNumMap = sessionRefNumMap,
|
|
sessionRefNumMap = sessionRefNumMap->next)
|
|
if (sessionRefNumMap->sessionRefNum is sessionInfo->sessionRefNum)
|
|
break;
|
|
if (sessionRefNumMap is empty)
|
|
ErrorLog("UnlinkSessionInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspRefNumMapMissing, IMsgAspRefNumMapMissing,
|
|
Insert0());
|
|
else
|
|
{
|
|
if (previousSessionRefNumMap is empty)
|
|
sessionRefNumMapHashBuckets[index] = sessionRefNumMap->next;
|
|
else
|
|
previousSessionRefNumMap->next = sessionRefNumMap->next;
|
|
Free(sessionRefNumMap);
|
|
}
|
|
|
|
/* If the session on the current sessions listener have been dealt with
|
|
via the GetAnyRequest mechanism, and we haven't been able to notify
|
|
anybody about the close (above), we need to complete a GetAnyRequest
|
|
with the news. */
|
|
|
|
if (sessionInfo->mySessionListener->getAnyRequestsSeen and
|
|
not sessionInfo->notifiedOwnerOfClose)
|
|
{
|
|
/* If there is a GetAnyRequest handler, complete it. */
|
|
|
|
if (sessionInfo->mySessionListener->getRequestInfoList isnt Empty)
|
|
{
|
|
getRequestInfo = Link(sessionInfo->mySessionListener->
|
|
getRequestInfoList);
|
|
getRequestInfo->sessionRefNum = sessionInfo->sessionRefNum;
|
|
getRequestInfo->usersCookie = sessionInfo->usersCookie;
|
|
getRequestInfo->errorCode = sessionInfo->closeCode;
|
|
ReleaseLock(AspLock);
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
UnlinkGetRequestInfo(getRequestInfo);
|
|
}
|
|
else
|
|
{
|
|
DeferredCloseNotify deferredCloseNotify;
|
|
|
|
/* Okay, we haven't been able to notify anybody! Note the refNum
|
|
and the usersCookie in the sessionListener, we'll notify about
|
|
the close the next time a GetAnyRequest comes in. */
|
|
|
|
if ((deferredCloseNotify = Malloc(sizeof(*deferredCloseNotify))) is
|
|
Empty)
|
|
{
|
|
ReleaseLock(AspLock);
|
|
ErrorLog("UnlinkSessionInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
}
|
|
else
|
|
{
|
|
deferredCloseNotify->closeCode = sessionInfo->closeCode;
|
|
deferredCloseNotify->sessionRefNum = sessionInfo->sessionRefNum;
|
|
deferredCloseNotify->usersCookie = sessionInfo->usersCookie;
|
|
deferredCloseNotify->next =
|
|
sessionInfo->mySessionListener->deferredCloseNotifyList;
|
|
sessionInfo->mySessionListener->deferredCloseNotifyList =
|
|
deferredCloseNotify;
|
|
ReleaseLock(AspLock);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ReleaseLock(AspLock);
|
|
|
|
/* Cancel tickling to the other end. */
|
|
|
|
if (AtpCancelRequest(sessionInfo->ourSocket,
|
|
sessionInfo->tickleTransactionId,
|
|
False) isnt ATnoError)
|
|
ErrorLog("UnlinkSessionInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspCouldntCancelTickle, IMsgAspCouldntCancelTickle,
|
|
Insert0());
|
|
|
|
/* Also, we need to remove this guy's entry from the SLS's socket list. */
|
|
|
|
DecrementSocketUsage(sessionInfo->mySessionListener,
|
|
sessionInfo->ourSocket);
|
|
UnlinkSessionListenerInfo(sessionInfo->mySessionListener);
|
|
|
|
}
|
|
else
|
|
{
|
|
/* Okay, close the WSS. This will cancel tickling and cancel the
|
|
pending request handler. [Due to only one session per socket]. */
|
|
|
|
ReleaseLock(AspLock);
|
|
if (sessionInfo->closeOurSocket)
|
|
if (AtpCloseSocketOnNode(sessionInfo->ourSocket, Empty,
|
|
(long unsigned)0) isnt ATnoError)
|
|
ErrorLog("UnlinkSessionInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspBadSocketClose, IMsgAspBadSocketClose,
|
|
Insert0());
|
|
|
|
/* Closing the socket should have freed the list of pending write or
|
|
commands [due to the inherent canceling of pending requests].
|
|
Check this... */
|
|
|
|
if (sessionInfo->writeOrCommandInfoList isnt empty)
|
|
{
|
|
WriteOrCommandInfo writeOrCommandInfo, nextWriteOrCommandInfo;
|
|
|
|
ErrorLog("UnlinkSessionInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspListNotEmpty, IMsgAspListNotEmpty,
|
|
Insert0());
|
|
for (writeOrCommandInfo = sessionInfo->writeOrCommandInfoList;
|
|
writeOrCommandInfo isnt empty;
|
|
writeOrCommandInfo = nextWriteOrCommandInfo)
|
|
{
|
|
nextWriteOrCommandInfo = writeOrCommandInfo->next;
|
|
Free(writeOrCommandInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If there is a close completion routine, call it before we free the
|
|
sessionInfo. */
|
|
|
|
if (sessionInfo->closeContext.closeCompletionRoutine isnt Empty)
|
|
(*(sessionInfo->closeContext.closeCompletionRoutine))(
|
|
ATnoError, sessionInfo->closeContext.closeUserData,
|
|
sessionInfo->sessionRefNum);
|
|
Free(sessionInfo);
|
|
|
|
/* All set. */
|
|
|
|
return;
|
|
|
|
} /* UnlinkSessionInfo */
|
|
|
|
ExternForVisibleFunction void far
|
|
SendStatusComplete(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
AppleTalkAddress source,
|
|
short unsigned transactionId)
|
|
{
|
|
SessionListenerInfo sessionListenerInfo = (SessionListenerInfo)userData;
|
|
char far *freeServiceStatus = Empty;
|
|
void far *freeOpaqueServiceStatus = Empty;
|
|
|
|
/* A send status is complete, we may have switch to a new status for
|
|
the session listener. */
|
|
|
|
TakeLock(AspLock);
|
|
if (sessionListenerInfo->newServiceStatusPending)
|
|
{
|
|
if (sessionListenerInfo->serviceStatusSize > 0)
|
|
freeServiceStatus = sessionListenerInfo->serviceStatus;
|
|
if (sessionListenerInfo->freeOpaqueServiceStatus)
|
|
freeOpaqueServiceStatus = sessionListenerInfo->opaqueServiceStatus;
|
|
|
|
sessionListenerInfo->serviceStatus =
|
|
sessionListenerInfo->newServiceStatus;
|
|
sessionListenerInfo->opaqueServiceStatus =
|
|
sessionListenerInfo->newOpaqueServiceStatus;
|
|
sessionListenerInfo->freeOpaqueServiceStatus =
|
|
sessionListenerInfo->newFreeOpaqueServiceStatus;
|
|
sessionListenerInfo->serviceStatusSize =
|
|
sessionListenerInfo->newServiceStatusSize;
|
|
|
|
sessionListenerInfo->newServiceStatusPending = False;
|
|
}
|
|
|
|
/* Okay, the send is now really complete. */
|
|
|
|
sessionListenerInfo->statusSendInProgress = False;
|
|
ReleaseLock(AspLock);
|
|
|
|
/* If there is an "old status" free it. */
|
|
|
|
if (freeServiceStatus isnt Empty)
|
|
Free(freeServiceStatus);
|
|
if (freeOpaqueServiceStatus isnt Empty)
|
|
FreeOpaqueDataDescriptor(freeOpaqueServiceStatus);
|
|
|
|
/* Otherwise, we're all set, remove our link to the session listener. */
|
|
|
|
UnlinkSessionListenerInfo(sessionListenerInfo);
|
|
return;
|
|
|
|
} /* SendStatusComplete */
|
|
|
|
#if Verbose or (Iam a Primos)
|
|
void DumpAspInfo(void)
|
|
{
|
|
SessionListenerInfo sessionListenerInfo;
|
|
GetSessionHandler getSessionHandler;
|
|
SocketInfo socketInfo;
|
|
SessionInfo sessionInfo;
|
|
WriteOrCommandInfo writeOrCommandInfo;
|
|
GetRequestInfo getRequestInfo;
|
|
short numberOfSessionListeners = 0;
|
|
short numberOfGetSessions = 0;
|
|
short numberOfServerSessions = 0;
|
|
short numberOfServerSockets = 0;
|
|
short numberOfWorkstationSessions = 0;
|
|
short numberOfGetRequests = 0;
|
|
short numberOfGetAnyRequests = 0;
|
|
short numberOfCommands = 0;
|
|
short numberOfWrites = 0;
|
|
short numberOfWriteContinues = 0;
|
|
short serverSessionsForThisSls, count, totalSessions, index;
|
|
|
|
/* Verify structures and count interesting information [I'm sure somebody
|
|
will be interested]. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
/* Walk our list of session listeners. */
|
|
|
|
for (sessionListenerInfo = sessionListenerInfoHead;
|
|
sessionListenerInfo isnt empty;
|
|
sessionListenerInfo = sessionListenerInfo->next)
|
|
{
|
|
numberOfSessionListeners += 1;
|
|
|
|
/* Count the number of GetSession handlers pending on this session
|
|
listener. */
|
|
|
|
for (getSessionHandler = sessionListenerInfo->getSessionHandlers;
|
|
getSessionHandler isnt empty;
|
|
getSessionHandler = getSessionHandler->next)
|
|
numberOfGetSessions += 1;
|
|
|
|
/* Walk the socket list and count the number of sockets and sessions. */
|
|
|
|
serverSessionsForThisSls = 0;
|
|
for (socketInfo = sessionListenerInfo->socketList;
|
|
socketInfo isnt empty;
|
|
socketInfo = socketInfo->next)
|
|
{
|
|
numberOfServerSockets += 1;
|
|
numberOfServerSessions += socketInfo->activeSessions;
|
|
serverSessionsForThisSls += socketInfo->activeSessions;
|
|
}
|
|
|
|
/* Count the GetAnyRequests. */
|
|
|
|
for (getRequestInfo = sessionListenerInfo->getRequestInfoList;
|
|
getRequestInfo isnt empty;
|
|
getRequestInfo = getRequestInfo->next)
|
|
numberOfGetAnyRequests += 1;
|
|
|
|
/* Walk the sessionInfos hanging off this session listener. */
|
|
|
|
count = 0;
|
|
for (sessionInfo = sessionListenerInfo->sessionList;
|
|
sessionInfo isnt empty;
|
|
sessionInfo = sessionInfo->nextForMySls)
|
|
{
|
|
count += 1;
|
|
if (not sessionInfo->serverSession)
|
|
printf("Workstation session hanging off SLS list!\n");
|
|
|
|
/* For a server, there should be no WriteOrCommands... */
|
|
|
|
if (sessionInfo->writeOrCommandInfoList isnt empty)
|
|
printf("WriteOrCommands hanging off server session!\n");
|
|
|
|
/* Walk get request list. */
|
|
|
|
for (getRequestInfo = sessionInfo->getRequestInfoList;
|
|
getRequestInfo isnt empty;
|
|
getRequestInfo = getRequestInfo->next)
|
|
numberOfGetRequests += 1;
|
|
}
|
|
if (count isnt serverSessionsForThisSls)
|
|
printf("Consistency error. Server session count mismatch.\n");
|
|
}
|
|
|
|
/* Okay, now lets get another look at the same picture. Walk the session
|
|
hash table. */
|
|
|
|
totalSessions = 0;
|
|
for (index = 0; index < NumberOfAspSessionHashBuckets; index += 1)
|
|
for (sessionInfo = sessionInfoHashBuckets[index];
|
|
sessionInfo isnt empty;
|
|
sessionInfo = sessionInfo->next)
|
|
{
|
|
totalSessions += 1;
|
|
if (sessionInfo->serverSession)
|
|
continue;
|
|
numberOfWorkstationSessions += 1;
|
|
if (sessionInfo->getRequestInfoList isnt Empty)
|
|
printf("GetRequests hanging off workstation session!\n");
|
|
|
|
/* Walk pending writeOrCommand list. */
|
|
|
|
for (writeOrCommandInfo = sessionInfo->writeOrCommandInfoList;
|
|
writeOrCommandInfo isnt empty;
|
|
writeOrCommandInfo = writeOrCommandInfo->next)
|
|
{
|
|
if (writeOrCommandInfo->writeCommand)
|
|
numberOfWrites += 1;
|
|
else
|
|
numberOfCommands += 1;
|
|
if (writeOrCommandInfo->writeReplyPosted)
|
|
numberOfWriteContinues += 1;
|
|
}
|
|
}
|
|
if (totalSessions isnt (numberOfServerSessions + numberOfWorkstationSessions))
|
|
printf("Total sessions mismatch!.\n");
|
|
|
|
/* Print the report. */
|
|
|
|
printf("\n");
|
|
printf("%.3d session lisenters are currently active.\n",
|
|
numberOfSessionListeners);
|
|
printf("%.3d get session handlers are pending on the SLSs.\n",
|
|
numberOfGetSessions);
|
|
printf("%.3d server sessions are active on %.3d ATP sockets.\n",
|
|
numberOfServerSessions, numberOfServerSockets);
|
|
printf("%.3d get any request handlers are pending on the session listeners.\n",
|
|
numberOfGetAnyRequests);
|
|
printf("%.3d get request handlers are pending on the server sessions.\n",
|
|
numberOfGetRequests);
|
|
printf("%.3d workstations sessions are active.\n",
|
|
numberOfWorkstationSessions);
|
|
printf("%.3d commands and %.3d writes are pending on the workstation sessions.\n",
|
|
numberOfCommands, numberOfWrites);
|
|
printf("%.3d of the writes are processing write replies.\n",
|
|
numberOfWriteContinues);
|
|
printf("\n");
|
|
|
|
/* All set! */
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* DumpAspInfo */
|
|
#endif
|