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.
3869 lines
119 KiB
3869 lines
119 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
asp.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the ASP protocol code.
|
|
|
|
Author:
|
|
|
|
Garth Conboy Initial Coding
|
|
Nikhil Kamkolkar Rewritten for microsoft coding style. mpized
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
|
|
#define IncludeAspErrors 1
|
|
#include "atalk.h"
|
|
|
|
#define VerboseMessages 0
|
|
|
|
ExternForVisibleFunction
|
|
AtpIncomingRequestHandler IncomingSlsTransaction;
|
|
|
|
ExternForVisibleFunction
|
|
AtpIncomingRequestHandler IncomingSssTransaction;
|
|
|
|
ExternForVisibleFunction
|
|
AtpIncomingRequestHandler IncomingWssTransaction;
|
|
|
|
ExternForVisibleFunction
|
|
AtpIncomingReleaseHandler IncomingRelease;
|
|
|
|
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
|
|
void
|
|
RemoveGetRequestInfo(GetRequestInfo targetGetRequestInfo);
|
|
|
|
ExternForVisibleFunction
|
|
void
|
|
FreeGetRequestInfo(GetRequestInfo targetGetRequestInfo);
|
|
|
|
// GLOBALS
|
|
static Boolean sessionMaintenanceTimerStarted = False;
|
|
static char getStatusResponseUserBytes[AtpUserBytesSize]; // Read only
|
|
|
|
|
|
|
|
|
|
void ShutdownAsp(void)
|
|
{
|
|
|
|
sessionMaintenanceTimerStarted = False;
|
|
return;
|
|
|
|
} // ShutdownAsp
|
|
|
|
|
|
|
|
|
|
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. This socket will be
|
|
// closed when the session listener is deleted, or
|
|
// if any errors occur while creating the 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;
|
|
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;
|
|
|
|
//
|
|
// 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;
|
|
while(not okay) {
|
|
okay = True;
|
|
if ((lastSessionListenerRefNum += 1) < 0) {
|
|
lastSessionListenerRefNum = 0;
|
|
if (wrapped) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
ErrorLog("AspCreateSessionListenerOnNode", ISevError, __LINE__, port,
|
|
IErrAspBadError, IMsgAspBadError,
|
|
Insert0());
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(tempSocket);
|
|
return(ATinternalError);
|
|
}
|
|
wrapped = True;
|
|
}
|
|
for (sessionListenerInfo = sessionListenerInfoHead;
|
|
okay and sessionListenerInfo isnt empty;
|
|
sessionListenerInfo = sessionListenerInfo->next)
|
|
if (sessionListenerInfo->sessionListenerRefNum is
|
|
lastSessionListenerRefNum)
|
|
okay = False;
|
|
}
|
|
|
|
// 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);
|
|
return(AToutOfMemory);
|
|
}
|
|
sessionListenerInfo->sessionListenerRefNum = lastSessionListenerRefNum;
|
|
sessionListenerInfo->ourSocket = tempSocket;
|
|
sessionListenerInfo->closeSocket = (existingAtpSocket < 0);
|
|
sessionListenerInfo->port = port;
|
|
sessionListenerInfo->next = sessionListenerInfoHead;
|
|
sessionListenerInfoHead = sessionListenerInfo;
|
|
|
|
//
|
|
// 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);
|
|
return(errorCode);
|
|
}
|
|
|
|
// Set the refNum that we've used, and run away.
|
|
|
|
*sessionListenerRefNum = lastSessionListenerRefNum;
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} // AspCreateSessionListener
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far AspDeleteSessionListener(
|
|
long sessionListenerRefNum) // Who to delete.
|
|
{
|
|
SessionListenerInfo sessionListenerInfo, previousSessionListenerInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
SessionInfo sessionInfo, nextSessionInfo;
|
|
GetSessionHandler getSessionHandler, nextGetSessionHandler;
|
|
GetRequestInfo getRequestInfo, nextGetRequestInfo;
|
|
DeferredCloseNotify deferredCloseNotify, nextDeferredCloseNotify;
|
|
|
|
// Find our target.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
for (previousSessionListenerInfo = empty,
|
|
sessionListenerInfo = sessionListenerInfoHead;
|
|
sessionListenerInfo isnt empty;
|
|
previousSessionListenerInfo = sessionListenerInfo,
|
|
sessionListenerInfo = sessionListenerInfo->next)
|
|
if (sessionListenerInfo->sessionListenerRefNum is sessionListenerRefNum)
|
|
break;
|
|
|
|
if (sessionListenerInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSessionListener);
|
|
}
|
|
|
|
// Close the ATP socket that we're listening on.
|
|
|
|
if (not sessionListenerInfo->closeSocket)
|
|
errorCode = ATnoError; // Close not requested.
|
|
else
|
|
errorCode = AtpCloseSocketOnNode(sessionListenerInfo->ourSocket);
|
|
|
|
// Should also close all SSSs opened to this SLS.
|
|
|
|
for (sessionInfo = sessionListenerInfo->sessionList;
|
|
sessionInfo isnt empty;
|
|
sessionInfo = nextSessionInfo) {
|
|
nextSessionInfo = sessionInfo->nextForMySls;
|
|
AspCloseSession(sessionInfo->sessionRefNum, False);
|
|
}
|
|
|
|
// Remove him from our list.
|
|
|
|
if (previousSessionListenerInfo is empty)
|
|
sessionListenerInfoHead = sessionListenerInfo->next;
|
|
else
|
|
previousSessionListenerInfo->next = sessionListenerInfo->next;
|
|
|
|
// Free any getSession handlers.
|
|
|
|
for (getSessionHandler = sessionListenerInfo->getSessionHandlers;
|
|
getSessionHandler isnt empty;
|
|
getSessionHandler = nextGetSessionHandler) {
|
|
nextGetSessionHandler = getSessionHandler->next;
|
|
(*getSessionHandler->sessionOpenHandler)(ATaspSessionListenerDeleted,
|
|
getSessionHandler->userData,
|
|
(long)0, (long)0);
|
|
Free(getSessionHandler);
|
|
}
|
|
|
|
// Terminate and free any pending GetAnyRequests.
|
|
|
|
for (getRequestInfo = sessionListenerInfo->getRequestInfoList;
|
|
getRequestInfo isnt empty;
|
|
getRequestInfo = nextGetRequestInfo) {
|
|
nextGetRequestInfo = getRequestInfo->next;
|
|
RemoveGetRequestInfo(getRequestInfo);
|
|
if (not getRequestInfo->inUse)
|
|
(*getRequestInfo->completionRoutine)(ATaspSessionListenerDeleted,
|
|
getRequestInfo->userData,
|
|
(long)0, (long)0, empty, 0, 0,
|
|
getRequestInfo->
|
|
getRequestRefNum);
|
|
FreeGetRequestInfo(getRequestInfo);
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
// Finaly, free the session listener...
|
|
|
|
if (sessionListenerInfo->serviceStatusSize > 0)
|
|
Free(sessionListenerInfo->serviceStatus);
|
|
if (sessionListenerInfo->freeOpaqueServiceStatus)
|
|
FreeOpaqueDataDescriptor(sessionListenerInfo->opaqueServiceStatus);
|
|
Free(sessionListenerInfo);
|
|
|
|
// All set!
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} // AspDeleteSessionListener
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far AspSetStatus(
|
|
long sessionListenerRefNum, // What session listener?
|
|
void far *serviceStatusOpaque, // New server status "buffer"
|
|
int serviceStatusSize) // Size of block
|
|
{
|
|
SessionListenerInfo sessionListenerInfo;
|
|
|
|
if (serviceStatusSize > AtpMaximumTotalResponseSize)
|
|
return(ATaspStatusBufferTooBig);
|
|
|
|
// Okay, find the session listener...
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
for (sessionListenerInfo = sessionListenerInfoHead;
|
|
sessionListenerInfo isnt empty;
|
|
sessionListenerInfo = sessionListenerInfo->next)
|
|
if (sessionListenerInfo->sessionListenerRefNum is sessionListenerRefNum)
|
|
break;
|
|
if (sessionListenerInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSessionListener);
|
|
}
|
|
|
|
// Okay, we're set to replace the current status buffer...
|
|
|
|
if (sessionListenerInfo->serviceStatusSize > 0)
|
|
Free(sessionListenerInfo->serviceStatus);
|
|
if (sessionListenerInfo->freeOpaqueServiceStatus)
|
|
FreeOpaqueDataDescriptor(sessionListenerInfo->opaqueServiceStatus);
|
|
sessionListenerInfo->freeOpaqueServiceStatus = False;
|
|
if (serviceStatusSize > 0) {
|
|
sessionListenerInfo->serviceStatus = (char *)Malloc(serviceStatusSize);
|
|
if (sessionListenerInfo->serviceStatus is empty) {
|
|
ErrorLog("AspSetStatus", ISevError, __LINE__, UnknownPort,
|
|
IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
sessionListenerInfo->serviceStatusSize = 0;
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotSetStatus);
|
|
}
|
|
MoveFromOpaque(sessionListenerInfo->serviceStatus, serviceStatusOpaque, 0,
|
|
serviceStatusSize);
|
|
|
|
//
|
|
// Make a system dependent "opaque data descriptor" for our copy so
|
|
// that it will be usefull to pass to Atp.
|
|
//
|
|
|
|
if ((sessionListenerInfo->opaqueServiceStatus =
|
|
MakeOpaqueDataDescriptor(sessionListenerInfo->serviceStatus,
|
|
serviceStatusSize,
|
|
&sessionListenerInfo->
|
|
freeOpaqueServiceStatus)) is Empty) {
|
|
ErrorLog("AspSetStatus", ISevError, __LINE__, UnknownPort,
|
|
IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
Free(sessionListenerInfo->serviceStatus);
|
|
sessionListenerInfo->serviceStatusSize = 0;
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotSetStatus);
|
|
}
|
|
}
|
|
sessionListenerInfo->serviceStatusSize = (short)serviceStatusSize;
|
|
|
|
// All set!
|
|
|
|
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,
|
|
AtpGetNextTransactionId(ourSocket),
|
|
empty, 0, userBytes, True,
|
|
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) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSessionListener);
|
|
}
|
|
|
|
// Okay, build up and enqueue a new GetSession structure.
|
|
|
|
getSessionHandler = (GetSessionHandler)Calloc(sizeof(*getSessionHandler), 1);
|
|
if (getSessionHandler is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotEnqueueHandler);
|
|
}
|
|
getSessionHandler->refNum = UniqueNumber();
|
|
getSessionHandler->privateSocket = privateSocket;
|
|
getSessionHandler->userData = userData;
|
|
getSessionHandler->sessionOpenHandler = completionRoutine;
|
|
getSessionHandler->next = sessionListenerInfo->getSessionHandlers;
|
|
sessionListenerInfo->getSessionHandlers = getSessionHandler;
|
|
|
|
// All set!
|
|
|
|
*getSessionRefNum = getSessionHandler->refNum;
|
|
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) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSessionListener);
|
|
}
|
|
|
|
// Okay, find the GetSession.
|
|
|
|
for (getSessionHandler = sessionListenerInfo->getSessionHandlers;
|
|
getSessionHandler isnt Empty;
|
|
previousGetSessionHandler = getSessionHandler,
|
|
getSessionHandler = getSessionHandler->next)
|
|
if (getSessionHandler->refNum is getSessionRefNum)
|
|
break;
|
|
if (getSessionHandler is Empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchGetSession);
|
|
}
|
|
|
|
//
|
|
// Okay, we've found the target GetSession handler, remove him from the
|
|
// list.
|
|
//
|
|
|
|
if (previousGetSessionHandler is Empty)
|
|
sessionListenerInfo->getSessionHandlers = getSessionHandler->next;
|
|
else
|
|
previousGetSessionHandler->next = getSessionHandler->next;
|
|
Free(getSessionHandler);
|
|
|
|
// All set!
|
|
|
|
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);
|
|
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);
|
|
return(ATaspCouldNotOpenSession);
|
|
}
|
|
sessionInfo = (SessionInfo)Calloc(sizeof(*sessionInfo), 1);
|
|
if (sessionInfo is empty) {
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(tempSocket);
|
|
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();
|
|
|
|
// Thread the sessionInfo into the lookup table.
|
|
|
|
CheckMod(index, sessionRefNum, NumberOfAspSessionHashBuckets,
|
|
"AspOpenSession");
|
|
sessionInfo->next = sessionInfoHashBuckets[index];
|
|
sessionInfoHashBuckets[index] = sessionInfo;
|
|
|
|
// Build up our completion info block.
|
|
|
|
completionInfo = (CompletionInfo)Malloc(sizeof(*completionInfo));
|
|
if (completionInfo is empty) {
|
|
Free(sessionInfo);
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(tempSocket);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotOpenSession);
|
|
}
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
completionInfo->sessionRefNum = sessionRefNum;
|
|
|
|
//
|
|
// Post the OpenSession request to the specified address... our caller
|
|
// presumably found this with a prior NBP lookup.
|
|
//
|
|
|
|
errorCode = AtpPostRequest(tempSocket, serverAddress,
|
|
AtpGetNextTransactionId(tempSocket),
|
|
empty, 0, userBytes, True,
|
|
empty, 0,
|
|
sessionInfo->sessionUserBytes,
|
|
AtpRetriesForAsp,
|
|
AtpIntervalSecondsForAsp,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingOpenSessionResponse,
|
|
(long unsigned)completionInfo);
|
|
|
|
if (errorCode isnt ATnoError)
|
|
Free(completionInfo);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} // AspOpenSessionOnNode
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far AspCloseSession(
|
|
long sessionRefNum, // Session to close.
|
|
//
|
|
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.
|
|
//
|
|
{
|
|
SessionListenerInfo sessionListenerInfo;
|
|
SessionRefNumMap sessionRefNumMap, previousSessionRefNumMap;
|
|
SessionInfo sessionInfo, previousSessionInfo;
|
|
GetRequestInfo getRequestInfo, nextGetRequestInfo;
|
|
long index;
|
|
char userBytes[AtpUserBytesSize];
|
|
AppleTalkErrorCode errorCode = ATnoError;
|
|
AppleTalkErrorCode closeCode;
|
|
Boolean notifiedOwnerOfClose = False;
|
|
|
|
if (remoteClose)
|
|
closeCode = ATaspRemoteSessionClose;
|
|
else
|
|
closeCode = ATaspLocalSessionClose;
|
|
|
|
// First, unthread this guy from the sessionInfoHash buckets.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
CheckMod(index, sessionRefNum, NumberOfAspSessionHashBuckets,
|
|
"FindSessionInfoFor");
|
|
for (sessionInfo = sessionInfoHashBuckets[index],
|
|
previousSessionInfo = empty;
|
|
sessionInfo isnt empty;
|
|
previousSessionInfo = sessionInfo,
|
|
sessionInfo = sessionInfo->next)
|
|
if (sessionInfo->sessionRefNum is sessionRefNum)
|
|
break;
|
|
if (sessionInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (previousSessionInfo is empty)
|
|
sessionInfoHashBuckets[index] = sessionInfo->next;
|
|
else
|
|
previousSessionInfo->next = sessionInfo->next;
|
|
|
|
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...
|
|
|
|
errorCode = AtpPostRequest(sessionInfo->ourSocket,
|
|
sessionInfo->theirAddress,
|
|
AtpGetNextTransactionId(sessionInfo->ourSocket),
|
|
empty, 0, userBytes, False, empty, 0,
|
|
empty, AtpRetriesForAsp,
|
|
AtpIntervalSecondsForAsp,
|
|
ThirtySecondsTRelTimer,
|
|
empty, (long unsigned)0);
|
|
|
|
}
|
|
|
|
//
|
|
// Okay, now for server sessions, we need to unthread from the session list
|
|
// hanging off the SLS.
|
|
//
|
|
|
|
if (sessionInfo->serverSession) {
|
|
sessionListenerInfo = sessionInfo->mySessionListener;
|
|
for (sessionInfo = sessionListenerInfo->sessionList,
|
|
previousSessionInfo = empty;
|
|
sessionInfo isnt empty;
|
|
previousSessionInfo = sessionInfo,
|
|
sessionInfo = sessionInfo->nextForMySls)
|
|
if (sessionInfo->sessionRefNum is sessionRefNum)
|
|
break;
|
|
if (sessionInfo is empty) {
|
|
ErrorLog("AspCloseSession", ISevError, __LINE__, UnknownPort,
|
|
IErrAspNotOnSLSList, IMsgAspNotOnSLSList,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATinternalError);
|
|
}
|
|
if (previousSessionInfo is empty)
|
|
sessionListenerInfo->sessionList = sessionInfo->nextForMySls;
|
|
else
|
|
previousSessionInfo->nextForMySls = sessionInfo->nextForMySls;
|
|
|
|
// Cancel tickling to the other end.
|
|
|
|
if (AtpCancelRequest(sessionInfo->ourSocket,
|
|
sessionInfo->tickleTransactionId,
|
|
False) isnt ATnoError)
|
|
ErrorLog("AspCloseSession", ISevError, __LINE__, UnknownPort,
|
|
IErrAspCouldntCancelTickle, IMsgAspCouldntCancelTickle,
|
|
Insert0());
|
|
|
|
//
|
|
// Cancel the pending request handler. Don't bother checking for error
|
|
// here; we may have been called from IncomingSssTransaction (processing
|
|
// a Close command), in which case no new request handlers would have
|
|
// been posted.
|
|
//
|
|
|
|
AtpCancelRequestHandler(sessionInfo->ourSocket,
|
|
sessionInfo->atpRequestHandlerId, False);
|
|
|
|
// Also, we need to remove this guy's entry from the SLS's socket list.
|
|
|
|
DecrementSocketUsage(sessionListenerInfo, sessionInfo->ourSocket);
|
|
|
|
// Remove this fellow from the SessionRefNumMap.
|
|
|
|
CheckMod(index, (((sessionInfo->ourSocket & 0xFFFF) << 8) +
|
|
sessionInfo->sessionId), NumberOfSessionRefNumBuckets,
|
|
"AspCloseSession");
|
|
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("AspCloseSession", ISevError, __LINE__, UnknownPort,
|
|
IErrAspRefNumMapMissing, IMsgAspRefNumMapMissing,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATinternalError);
|
|
}
|
|
if (previousSessionRefNumMap is empty)
|
|
sessionRefNumMapHashBuckets[index] = sessionRefNumMap->next;
|
|
else
|
|
previousSessionRefNumMap->next = sessionRefNumMap->next;
|
|
Free(sessionRefNumMap);
|
|
|
|
// Lastly, we want to terminate any pending get request handlers.
|
|
|
|
for (getRequestInfo = sessionInfo->getRequestInfoList;
|
|
getRequestInfo isnt empty;
|
|
getRequestInfo = nextGetRequestInfo) {
|
|
nextGetRequestInfo = getRequestInfo->next;
|
|
RemoveGetRequestInfo(getRequestInfo);
|
|
if (not getRequestInfo->inUse) {
|
|
(*getRequestInfo->completionRoutine)(closeCode,
|
|
getRequestInfo->userData,
|
|
sessionInfo->sessionRefNum,
|
|
sessionInfo->usersCookie,
|
|
empty, 0, 0,
|
|
getRequestInfo->
|
|
getRequestRefNum);
|
|
notifiedOwnerOfClose = True;
|
|
}
|
|
FreeGetRequestInfo(getRequestInfo);
|
|
}
|
|
|
|
//
|
|
// If the session on the current session 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 notifiedOwnerOfClose) {
|
|
// If there is a GetAnyRequest handler, complete it. (only !inUse)
|
|
for (getRequestInfo = sessionInfo->mySessionListener->getRequestInfoList;
|
|
getRequestInfo isnt empty;
|
|
getRequestInfo = nextGetRequestInfo) {
|
|
nextGetRequestInfo = getRequestInfo->next;
|
|
if (not getRequestInfo->inUse) {
|
|
notifiedOwnerOfClose = True;
|
|
RemoveGetRequestInfo(getRequestInfo);
|
|
(*getRequestInfo->completionRoutine)(closeCode,
|
|
getRequestInfo->userData,
|
|
sessionInfo->sessionRefNum,
|
|
sessionInfo->usersCookie,
|
|
empty, 0, 0,
|
|
getRequestInfo->
|
|
getRequestRefNum);
|
|
FreeGetRequestInfo(getRequestInfo);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (not notifiedOwnerOfClose) {
|
|
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)
|
|
ErrorLog("AspCloseSession", ISevError, __LINE__, UnknownPort,
|
|
IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
else {
|
|
deferredCloseNotify->closeCode = closeCode;
|
|
deferredCloseNotify->sessionRefNum = sessionInfo->sessionRefNum;
|
|
deferredCloseNotify->usersCookie = sessionInfo->usersCookie;
|
|
deferredCloseNotify->next =
|
|
sessionInfo->mySessionListener->deferredCloseNotifyList;
|
|
sessionInfo->mySessionListener->deferredCloseNotifyList =
|
|
deferredCloseNotify;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Okay, close the WSS. This will cancel tickling and cancel the
|
|
// pending request handler. [Due to only one session per socket].
|
|
//
|
|
|
|
if (sessionInfo->closeOurSocket)
|
|
if (AtpCloseSocketOnNode(sessionInfo->ourSocket) isnt ATnoError)
|
|
ErrorLog("AspCloseSession", 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("AspCloseSession", ISevError, __LINE__, UnknownPort,
|
|
IErrAspListNotEmpty, IMsgAspListNotEmpty,
|
|
Insert0());
|
|
for (writeOrCommandInfo = sessionInfo->writeOrCommandInfoList;
|
|
writeOrCommandInfo isnt empty;
|
|
writeOrCommandInfo = nextWriteOrCommandInfo) {
|
|
nextWriteOrCommandInfo = writeOrCommandInfo->next;
|
|
Free(writeOrCommandInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Lastly, free the sessionInfo.
|
|
|
|
#if VerboseMessages
|
|
printf("ASP SessionRefNum %d closed.\n", sessionInfo->sessionRefNum);
|
|
#endif
|
|
Free(sessionInfo);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
return(errorCode);
|
|
|
|
} // 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;
|
|
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;
|
|
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) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (not sessionInfo->serverSession) {
|
|
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!
|
|
//
|
|
|
|
getRequestRefNum = sessionInfo->mySessionListener->lastGetRequestRefNum;
|
|
while(True) {
|
|
if ((getRequestRefNum += 1) < 0)
|
|
getRequestRefNum = 0;
|
|
if (FindGetRequestInfoFor(sessionInfo->mySessionListener,
|
|
getRequestRefNum) is Empty)
|
|
break;
|
|
}
|
|
sessionInfo->mySessionListener->lastGetRequestRefNum = getRequestRefNum;
|
|
|
|
getRequestInfo = (GetRequestInfo)Calloc(sizeof(*getRequestInfo), 1);
|
|
if (getRequestInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotGetRequest);
|
|
}
|
|
getRequestInfo->getRequestRefNum = getRequestRefNum;
|
|
getRequestInfo->mySessionInfo = sessionInfo;
|
|
getRequestInfo->mySessionListener = sessionInfo->mySessionListener;
|
|
getRequestInfo->opaqueBuffer = opaqueBuffer;
|
|
getRequestInfo->bufferSize = bufferSize;
|
|
getRequestInfo->completionRoutine = completionRoutine;
|
|
getRequestInfo->userData = userData;
|
|
|
|
// Link it up!
|
|
|
|
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] =
|
|
getRequestInfo;
|
|
|
|
// All set.
|
|
|
|
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) {
|
|
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.
|
|
//
|
|
|
|
if (sessionListenerInfo->deferredCloseNotifyList isnt Empty) {
|
|
deferredCloseNotify = sessionListenerInfo->deferredCloseNotifyList;
|
|
sessionListenerInfo->deferredCloseNotifyList = deferredCloseNotify->next;
|
|
|
|
(*completionRoutine)(deferredCloseNotify->closeCode, userData,
|
|
deferredCloseNotify->sessionRefNum,
|
|
deferredCloseNotify->usersCookie,
|
|
empty, 0, 0, (long)0);
|
|
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!
|
|
//
|
|
|
|
getRequestRefNum = sessionListenerInfo->lastGetRequestRefNum;
|
|
while(True) {
|
|
if ((getRequestRefNum += 1) < 0)
|
|
getRequestRefNum = 0;
|
|
if (FindGetRequestInfoFor(sessionListenerInfo,
|
|
getRequestRefNum) is Empty)
|
|
break;
|
|
}
|
|
sessionListenerInfo->lastGetRequestRefNum = getRequestRefNum;
|
|
|
|
getRequestInfo = (GetRequestInfo)Calloc(sizeof(*getRequestInfo), 1);
|
|
if (getRequestInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspCouldNotGetRequest);
|
|
}
|
|
getRequestInfo->getRequestRefNum = getRequestRefNum;
|
|
getRequestInfo->mySessionListener = sessionListenerInfo;
|
|
getRequestInfo->opaqueBuffer = opaqueBuffer;
|
|
getRequestInfo->bufferSize = bufferSize;
|
|
getRequestInfo->completionRoutine = completionRoutine;
|
|
getRequestInfo->userData = userData;
|
|
|
|
// Link it up!
|
|
|
|
getRequestInfo->next = sessionListenerInfo->getRequestInfoList;
|
|
sessionListenerInfo->getRequestInfoList = getRequestInfo;
|
|
sessionListenerInfo->getAnyRequestsSeen = True;
|
|
|
|
// Link this guy into the per-Sls getRequestInfo hash list.
|
|
|
|
CheckMod(index, getRequestRefNum, NumGetRequestInfoHashBuckets,
|
|
"AspGetRequest");
|
|
getRequestInfo->nextForMySessionListener =
|
|
sessionListenerInfo->getRequestInfoHashBuckets[index];
|
|
sessionListenerInfo->getRequestInfoHashBuckets[index] = getRequestInfo;
|
|
|
|
// All set.
|
|
|
|
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.
|
|
//
|
|
//
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (not sessionInfo->serverSession) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNotServerSession);
|
|
}
|
|
if (bufferSize > AtpMaximumTotalResponseSize) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspBufferTooBig);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
RemoveGetRequestInfo(getRequestInfo);
|
|
if (getRequestInfo->requestType isnt requestType or
|
|
not getRequestInfo->inUse) {
|
|
if (not getRequestInfo->inUse)
|
|
errorCode = ATaspNoOperationInProgress;
|
|
else
|
|
errorCode = ATaspWrongRequestType;
|
|
FreeGetRequestInfo(getRequestInfo);
|
|
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) {
|
|
FreeGetRequestInfo(getRequestInfo);
|
|
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;
|
|
errorCode = AtpPostResponse(sessionInfo->ourSocket,
|
|
getRequestInfo->source,
|
|
getRequestInfo->transactionId,
|
|
opaqueBuffer, bufferSize,
|
|
resultCode,
|
|
getRequestInfo->exactlyOnce,
|
|
IncomingRelease,
|
|
(long unsigned)completionInfo);
|
|
|
|
// Free the get request info.
|
|
FreeGetRequestInfo(getRequestInfo);
|
|
|
|
// All set!
|
|
|
|
if (errorCode isnt ATnoError)
|
|
Free(completionInfo);
|
|
|
|
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) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (not sessionInfo->serverSession) {
|
|
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);
|
|
}
|
|
|
|
RemoveGetRequestInfo(getRequestInfo);
|
|
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;
|
|
FreeGetRequestInfo(getRequestInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
//
|
|
// 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());
|
|
FreeGetRequestInfo(getRequestInfo);
|
|
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());
|
|
FreeGetRequestInfo(getRequestInfo);
|
|
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;
|
|
getRequestInfo->writeContinueInProgress = True;
|
|
getRequestInfo->writeContinueTransactionId =
|
|
AtpGetNextTransactionId(sessionInfo->ourSocket);
|
|
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) {
|
|
Free(completionInfo);
|
|
}
|
|
|
|
FreeGetRequestInfo(getRequestInfo);
|
|
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) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (sessionInfo->serverSession) {
|
|
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);
|
|
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->sequenceNumber = sessionInfo->nextSequenceNumber;
|
|
commandInfo->resultCode = resultCode;
|
|
commandInfo->next = sessionInfo->writeOrCommandInfoList;
|
|
sessionInfo->writeOrCommandInfoList = commandInfo;
|
|
|
|
// 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.
|
|
|
|
errorCode = AtpPostRequest(sessionInfo->ourSocket,
|
|
sessionInfo->theirAddress,
|
|
AtpGetNextTransactionId(sessionInfo->ourSocket),
|
|
opaqueCommandBuffer, commandBufferSize,
|
|
userBytes, True, opaqueReplyBuffer,
|
|
replyBufferSize, resultCode, AtpInfiniteRetries,
|
|
AtpIntervalSecondsForAsp,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingWriteOrCommandComplete,
|
|
(long unsigned)completionInfo);
|
|
|
|
if (errorCode isnt ATnoError) {
|
|
sessionInfo->writeOrCommandInfoList = commandInfo->next;
|
|
Free(commandInfo);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
// Request out, sequence number used!
|
|
|
|
sessionInfo->nextSequenceNumber += 1;
|
|
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.
|
|
//
|
|
//
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (sessionInfo->serverSession) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNotWorkstationSession);
|
|
}
|
|
if (writeBufferSize > AtpMaximumTotalResponseSize) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspSizeError);
|
|
}
|
|
|
|
// 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);
|
|
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->sequenceNumber = sessionInfo->nextSequenceNumber;
|
|
writeInfo->writeCommand = True;
|
|
writeInfo->resultCode = resultCode;
|
|
writeInfo->writeOpaqueBuffer = opaqueWriteBuffer;
|
|
writeInfo->writeBufferSize = writeBufferSize;
|
|
writeInfo->next = sessionInfo->writeOrCommandInfoList;
|
|
sessionInfo->writeOrCommandInfoList = writeInfo;
|
|
|
|
// 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.
|
|
|
|
errorCode = AtpPostRequest(sessionInfo->ourSocket,
|
|
sessionInfo->theirAddress,
|
|
AtpGetNextTransactionId(sessionInfo->ourSocket),
|
|
opaqueCommandBuffer, commandBufferSize,
|
|
userBytes, True, opaqueReplyBuffer,
|
|
replyBufferSize, resultCode, AtpInfiniteRetries,
|
|
AtpIntervalSecondsForAsp,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingWriteOrCommandComplete,
|
|
(long unsigned)completionInfo);
|
|
|
|
if (errorCode isnt ATnoError) {
|
|
sessionInfo->writeOrCommandInfoList = writeInfo->next;
|
|
Free(writeInfo);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
// Request is out, so the sequence number is used!
|
|
|
|
sessionInfo->nextSequenceNumber += 1;
|
|
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) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (sessionInfo->serverSession) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNotWorkstationSession);
|
|
}
|
|
|
|
sessionInfo->incomingAttentionHandler = handler;
|
|
sessionInfo->userDataForAttention = userData;
|
|
|
|
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) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATaspNoSuchSession);
|
|
}
|
|
if (not sessionInfo->serverSession) {
|
|
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,
|
|
AtpGetNextTransactionId(sessionInfo->ourSocket),
|
|
empty, 0, userBytes, False, empty, 0,
|
|
empty, AtpRetriesForAsp,
|
|
AtpIntervalSecondsForAsp,
|
|
ThirtySecondsTRelTimer,
|
|
empty, (long unsigned)0);
|
|
|
|
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;
|
|
|
|
// 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();
|
|
for (sessionListenerInfo = sessionListenerInfoHead;
|
|
sessionListenerInfo isnt empty;
|
|
sessionListenerInfo = sessionListenerInfo->next)
|
|
if (sessionListenerInfo->sessionListenerRefNum is sessionListenerRefNum)
|
|
break;
|
|
|
|
if (sessionListenerInfo is empty) {
|
|
ErrorLog("IncomingSlsTransaction", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspListenerInfoMissing, IMsgAspListenerInfoMissing,
|
|
Insert0());
|
|
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) {
|
|
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].
|
|
//
|
|
|
|
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;
|
|
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) {
|
|
if (SendAspErrorReturn(sessionListenerInfo->ourSocket, source,
|
|
transactionId, exactlyOnce, ATaspServerBusy)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__,
|
|
UnknownPort, IErrAspBadSrvrBusySend, IMsgAspBadSrvrBusySend,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
sessionListenerInfo->lastSessionId = sessionId;
|
|
|
|
// Okay, do we like the version number?
|
|
|
|
if (userBytes[AspVersionNumberOffset] isnt AspVersionBytes[0] or
|
|
userBytes[AspVersionNumberOffset + 1] isnt AspVersionBytes[1]) {
|
|
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.
|
|
//
|
|
|
|
if (sessionListenerInfo->getSessionHandlers->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.
|
|
|
|
if (MapSocketToAddress(sessionListenerInfo->ourSocket,
|
|
&slsAddress) isnt ATnoError) {
|
|
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) {
|
|
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) {
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__,
|
|
UnknownPort, IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
AtpCloseSocketOnNode(serverSessionSocket);
|
|
break; // Just let the open OpenSession request time-out...
|
|
}
|
|
socketInfo->socket = serverSessionSocket;
|
|
socketInfo->activeSessions = 1;
|
|
socketInfo->privateSocket =
|
|
sessionListenerInfo->getSessionHandlers->privateSocket;
|
|
socketInfo->next = sessionListenerInfo->socketList;
|
|
sessionListenerInfo->socketList = socketInfo;
|
|
}
|
|
else {
|
|
serverSessionSocket = socketInfo->socket;
|
|
socketInfo->activeSessions += 1;
|
|
}
|
|
|
|
// Get a new session ref num.
|
|
|
|
if ((sessionRefNum = GetNextSessionRefNum()) < 0) {
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspBadError, IMsgAspBadError,
|
|
Insert0());
|
|
DecrementSocketUsage(sessionListenerInfo,
|
|
serverSessionSocket);
|
|
break;
|
|
}
|
|
|
|
// 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) {
|
|
if (sessionInfo isnt empty)
|
|
Free(sessionInfo);
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspOutOfMemory, IMsgAspOutOfMemory,
|
|
Insert0());
|
|
DecrementSocketUsage(sessionListenerInfo,
|
|
serverSessionSocket);
|
|
break;
|
|
}
|
|
|
|
// Okay, start filling in the session node...
|
|
|
|
sessionInfo->mySessionListener = 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.
|
|
|
|
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] = sessionInfo;
|
|
|
|
// Finaly onto the session per SLS list...
|
|
|
|
sessionInfo->nextForMySls = sessionListenerInfo->sessionList;
|
|
sessionListenerInfo->sessionList = sessionInfo;
|
|
|
|
// All looks good; we can send back a OpenSessionReply.
|
|
|
|
if (MapSocketToAddress(serverSessionSocket, &sssAddress)
|
|
isnt ATnoError) {
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspCouldntMapAddress, IMsgAspCouldntMapAddress,
|
|
Insert0());
|
|
AtpCloseSocketOnNode(serverSessionSocket);
|
|
break; // Just let the open OpenSession request time-out...
|
|
}
|
|
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;
|
|
sessionInfo->tickleTransactionId =
|
|
AtpGetNextTransactionId(sessionInfo->ourSocket);
|
|
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());
|
|
|
|
//
|
|
// Post an ATP read on behalf of this session [not really for THIS
|
|
// particular session, but another read on the shared ATP socket...
|
|
// these are really demultiplexed in IncomingSssTransaction].
|
|
//
|
|
|
|
if (AtpEnqueueRequestHandler(&id, sessionInfo->ourSocket,
|
|
Empty, 0, Empty,
|
|
IncomingSssTransaction,
|
|
(long unsigned)sessionRefNum)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingSlsTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspCouldNotEnqueue, IMsgAspCouldNotEnqueue,
|
|
Insert0());
|
|
else
|
|
sessionInfo->atpRequestHandlerId = id;
|
|
|
|
//
|
|
// Lastly, we're ready to untread the session open handler and invoke
|
|
// the completion routine!
|
|
//
|
|
|
|
getSessionHandler = sessionListenerInfo->getSessionHandlers;
|
|
sessionOpenHandler = getSessionHandler->sessionOpenHandler;
|
|
userData = getSessionHandler->userData;
|
|
sessionListenerInfo->getSessionHandlers = getSessionHandler->next;
|
|
ourSocket = sessionInfo->ourSocket;
|
|
Free(getSessionHandler);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*sessionOpenHandler)(ATnoError, userData, ourSocket,
|
|
sessionInfo->sessionRefNum);
|
|
needToUndefer = False;
|
|
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];
|
|
|
|
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.
|
|
//
|
|
|
|
ErrorLog("IncomingSlsTransaction", ISevVerbose, __LINE__,
|
|
UnknownPort, IErrAspSessionInfoMissing, IMsgAspSessionInfoMissing,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
#if VerboseMessages
|
|
printf("Tickle SSS (%d); sesRefNum = %d, sessionId = %d.\n",
|
|
sessionInfo->ourSocket,
|
|
sessionInfo->sessionRefNum, sessionId);
|
|
#endif
|
|
sessionInfo->lastContactTime = CurrentRelativeTime();
|
|
break;
|
|
|
|
case AspGetStatusCommand:
|
|
if (AtpPostResponse(sessionListenerInfo->ourSocket, source,
|
|
transactionId,
|
|
sessionListenerInfo->opaqueServiceStatus,
|
|
sessionListenerInfo->serviceStatusSize,
|
|
getStatusResponseUserBytes, exactlyOnce, empty,
|
|
(long unsigned)0) isnt ATnoError)
|
|
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...
|
|
|
|
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 bufferingSessionInfo, sessionInfo;
|
|
long id;
|
|
Boolean enqueueNewAtpRead = True;
|
|
short requestType;
|
|
Boolean needToUndefer = True;
|
|
short unsigned sequenceNumber;
|
|
void far *opaqueBuffer;
|
|
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// Okay, this is a little murcky... The sessionRefNum that we've gotten
|
|
// as "userData" is probably not the sessionRefNum of the session that
|
|
// really wants the incoming ATP request. This session will, however be
|
|
// on the same socket of the session that really wants the request.
|
|
// Remember that ATP doesn't have any concept of an ASP session, so all
|
|
// of the ATP reads posted (one for each ASP session) are the same to ATP
|
|
// (on the same socket).
|
|
//
|
|
// Now, to find the session that really wants the data, we know the
|
|
// socket (it will be the same as the above session), and by looking in
|
|
// the incoming packet we can find the sessionId. With these two, we can
|
|
// find the actual target sessionRefNum (sessionInfo). It is in this
|
|
// session that we check to see if there are any pending higher level
|
|
// ASP GetRequests. If so, we move the data out, call the designated
|
|
// completion routine. If not we check to see if there are any "any
|
|
// session GetRequest handlers" hanging off the ServiceListenerInfo,
|
|
// if so we move the data out. Failing this, else we ignore the incoming
|
|
// request. Sigh.
|
|
//
|
|
|
|
// Find the sessionInfo of the session that caught this request.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((bufferingSessionInfo = FindSessionInfoFor(bufferingSessionRefNum))
|
|
is empty) {
|
|
ErrorLog("IncomingSssTransaction", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspSessionBufMissing, IMsgAspSessionBufMissing,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Okay, now use the socket and the packet's sessionId to find the
|
|
// target sessionInfo.
|
|
//
|
|
|
|
if ((sessionInfo =
|
|
FindSessionInfoForSocket(bufferingSessionInfo->ourSocket,
|
|
(unsigned char)userBytes[AspSessionIdOffset]))
|
|
is empty) {
|
|
ErrorLog("IncomingSssTransaction", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspTargetSessionMissing, IMsgAspTargetSessionMissing,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
if (sessionInfo->mySessionListener isnt
|
|
bufferingSessionInfo->mySessionListener) {
|
|
ErrorLog("IncomingSssTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspWrongSLS, IMsgAspWrongSLS,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
if (sessionInfo->ourSocket isnt bufferingSessionInfo->ourSocket) {
|
|
//
|
|
// Yes, all ASP sessions that share GetRequest handlers will be on
|
|
// the same ATP (Ddp) socket!
|
|
//
|
|
|
|
ErrorLog("IncomingSssTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspWrongAddress, IMsgAspWrongAddress,
|
|
Insert0());
|
|
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.
|
|
//
|
|
|
|
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.
|
|
//
|
|
|
|
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) {
|
|
if (AtpCancelResponse(sessionInfo->ourSocket, source,
|
|
transactionId, False) isnt ATnoError)
|
|
ErrorLog("IncomingSssTransaction", ISevError, __LINE__,
|
|
UnknownPort, IErrAspBadCancelResponse, IMsgAspBadCancelResponse,
|
|
Insert0());
|
|
break;
|
|
}
|
|
else
|
|
sessionInfo->nextExpectedSequenceNumber += 1;
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// Tag the get request info as in-use and call the user's handler
|
|
// routine.
|
|
//
|
|
|
|
getRequestInfo->inUse = True; {
|
|
AspIncomingCommandHandler *completionRoutine =
|
|
getRequestInfo->completionRoutine;
|
|
long unsigned userData = getRequestInfo->userData;
|
|
long sessionRefNum = sessionInfo->sessionRefNum;
|
|
long getRequestRefNum = getRequestInfo->getRequestRefNum;
|
|
long unsigned cookie = sessionInfo->usersCookie;
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, sessionRefNum, cookie,
|
|
opaqueBuffer, bufferSize, requestType,
|
|
getRequestRefNum);
|
|
needToUndefer = False;
|
|
}
|
|
break;
|
|
|
|
case AspCloseSessionCommand:
|
|
|
|
//
|
|
// Only re-enqueue the ATP read if the GetRequestHandler that was used
|
|
// for the "current" request was NOT owned by the session we're
|
|
// closing.
|
|
//
|
|
|
|
if (bufferingSessionInfo->sessionRefNum is
|
|
sessionInfo->sessionRefNum)
|
|
enqueueNewAtpRead = False;
|
|
|
|
// 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, True);
|
|
break;
|
|
|
|
default:
|
|
ErrorLog("IncomingSssTransaction", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspBadCommand, IMsgAspBadCommand,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
// Re-enqueue the ATP read.
|
|
|
|
if (enqueueNewAtpRead)
|
|
if (AtpEnqueueRequestHandler(&id, sessionInfo->ourSocket,
|
|
Empty, 0, Empty, IncomingSssTransaction,
|
|
(long unsigned)bufferingSessionInfo->
|
|
sessionRefNum) isnt ATnoError)
|
|
ErrorLog("IncomingSssTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspCouldNotEnqueue, IMsgAspCouldNotEnqueue,
|
|
Insert0());
|
|
else
|
|
bufferingSessionInfo->atpRequestHandlerId = id;
|
|
|
|
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 enqueueNewAtpRead = True;
|
|
Boolean callAttentionRoutine = False;
|
|
short unsigned attentionData;
|
|
short unsigned sequenceNumber;
|
|
|
|
// 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) {
|
|
ErrorLog("IncomingWssTransaction", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspSessionInfoMissing, IMsgAspSessionInfoMissing,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
if (sessionInfo->serverSession or
|
|
(unsigned char)userBytes[AspSessionIdOffset] isnt sessionInfo->sessionId) {
|
|
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, True);
|
|
enqueueNewAtpRead = False;
|
|
break;
|
|
|
|
case AspAttentionCommand:
|
|
if (sessionInfo->incomingAttentionHandler isnt empty) {
|
|
callAttentionRoutine = True;
|
|
attentionData =
|
|
(short unsigned)((userBytes[AspAttentionWordOffset] << 8) +
|
|
(unsigned char)userBytes[AspAttentionWordOffset + 1]);
|
|
}
|
|
|
|
// 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.
|
|
|
|
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)
|
|
break; // No luck, ignore the request.
|
|
if (not writeInfo->writeCommand) {
|
|
ErrorLog("IncomingWssTransaction", ISevWarning, __LINE__,
|
|
UnknownPort, IErrAspNotWriteCommand, IMsgAspNotWriteCommand,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
// How much data can the server take?
|
|
|
|
if (bufferSize < AspWriteDataSize) {
|
|
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;
|
|
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 needed.
|
|
|
|
if (enqueueNewAtpRead)
|
|
if (AtpEnqueueRequestHandler(&id, sessionInfo->ourSocket,
|
|
Empty, 0, Empty,
|
|
IncomingWssTransaction,
|
|
(long unsigned)sessionRefNum) isnt ATnoError)
|
|
ErrorLog("IncomingWssTransaction", ISevError, __LINE__, UnknownPort,
|
|
IErrAspCouldNotEnqueue, IMsgAspCouldNotEnqueue,
|
|
Insert0());
|
|
else
|
|
sessionInfo->atpRequestHandlerId = id;
|
|
|
|
// Call attention handler, if needed.
|
|
|
|
if (callAttentionRoutine) {
|
|
AspIncomingAttentionHandler *incomingAttentionHandler =
|
|
sessionInfo->incomingAttentionHandler;
|
|
long unsigned userData = sessionInfo->userDataForAttention;
|
|
long sessionRefNum = sessionInfo->sessionRefNum;
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*incomingAttentionHandler)(ATnoError, userData, sessionRefNum,
|
|
attentionData);
|
|
}
|
|
else {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
|
|
return;
|
|
|
|
} // IncomingWssTransaction
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction SessionInfo
|
|
FindSessionInfoForSocket(long socket,
|
|
unsigned char sessionId)
|
|
{
|
|
long index;
|
|
SessionRefNumMap sessionRefNumMap;
|
|
SessionInfo sessionInfo;
|
|
|
|
//
|
|
// First, given the socket and the session ID of an ASP session, we
|
|
// need to find the session reference number.
|
|
//
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
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) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(empty);
|
|
}
|
|
|
|
//
|
|
// Okay, now we have the session reference number... find the correct
|
|
// session info node.
|
|
//
|
|
|
|
sessionInfo = FindSessionInfoFor(sessionRefNumMap->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();
|
|
for (socketInfo = sessionListenerInfo->socketList,
|
|
previousSocketInfo = empty;
|
|
socketInfo isnt empty;
|
|
previousSocketInfo = socketInfo,
|
|
socketInfo = socketInfo->next)
|
|
if (socketInfo->socket is socket)
|
|
break;
|
|
|
|
if (socketInfo is empty) {
|
|
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;
|
|
AtpCloseSocketOnNode(socketInfo->socket);
|
|
Free(socketInfo);
|
|
}
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} // DecrementSocketUsage
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction AppleTalkErrorCode InitializeAsp(void)
|
|
{
|
|
|
|
// Start the session maintenance timer...
|
|
|
|
StartTimer(SessionMaintenanceTimerExpired, AspSessionMaintenanceSeconds,
|
|
0, empty);
|
|
|
|
sessionMaintenanceTimerStarted = True;
|
|
|
|
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)
|
|
for (sessionInfo = sessionInfoHashBuckets[index];
|
|
sessionInfo isnt empty;
|
|
sessionInfo = nextSessionInfo) {
|
|
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, False) isnt
|
|
ATnoError)
|
|
ErrorLog("SessionMaintenanceTimerExpired", ISevError,
|
|
__LINE__, UnknownPort, IErrAspCouldNotCloseSess,
|
|
IMsgAspCouldNotCloseSess, Insert0());
|
|
#if VerboseMessages
|
|
printf("Session maintenance; closing sessionRefNum = %d.\n",
|
|
sessionInfo->sessionRefNum);
|
|
#endif
|
|
sessionInfo = sessionInfoHashBuckets[index];
|
|
}
|
|
else
|
|
// "I'm not dead yet..." ;
|
|
}
|
|
|
|
// 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");
|
|
for (sessionInfo = sessionInfoHashBuckets[index];
|
|
sessionInfo isnt empty;
|
|
sessionInfo = sessionInfo->next)
|
|
if (sessionInfo->sessionRefNum is sessionRefNum)
|
|
break;
|
|
|
|
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;
|
|
|
|
// Touch unused formals...
|
|
|
|
source, transactionId;
|
|
|
|
completionRoutine =
|
|
(AspReplyCompleteHandler *)completionInfo->completionRoutine;
|
|
if (completionRoutine is empty) {
|
|
Free(completionInfo);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
|
|
(*completionRoutine)(errorCode, userData, sessionRefNum, getRequestRefNum);
|
|
|
|
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;
|
|
|
|
// Touch unused formals...
|
|
|
|
source, transactionId, responseUserBytes;
|
|
|
|
completionRoutine =
|
|
(AspIncomingWriteDataHandler *)completionInfo->completionRoutine;
|
|
userData = completionInfo->userData;
|
|
sessionRefNum = completionInfo->sessionRefNum;
|
|
getRequestRefNum = completionInfo->getRequestRefNum;
|
|
Free(completionInfo);
|
|
|
|
(*completionRoutine)(errorCode, userData, sessionRefNum, getRequestRefNum,
|
|
opaqueResponseBuffer, responseBufferSize);
|
|
|
|
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) {
|
|
ErrorLog("IncomingOpenSessionResponse", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAspSessionInfoMissing, IMsgAspSessionInfoMissing,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATaspCouldNotOpenSession, userData, (long)0);
|
|
return;
|
|
}
|
|
if (sessionInfo->serverSession or not sessionInfo->waitingForOpenReply) {
|
|
ErrorLog("IncomingOpenSessionResponse", ISevError, __LINE__, UnknownPort,
|
|
IErrAspSessionInfoBad, IMsgAspSessionInfoBad,
|
|
Insert0());
|
|
AspCloseSession(sessionRefNum, 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) {
|
|
AspCloseSession(sessionRefNum, 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,
|
|
AtpGetNextTransactionId(sessionInfo->ourSocket),
|
|
empty, 0, tickleUserBytes, False,
|
|
empty, 0, empty, AtpInfiniteRetries,
|
|
AspTickleSeconds,
|
|
ThirtySecondsTRelTimer,
|
|
empty, (long unsigned)0);
|
|
f (errorCode isnt ATnoError)
|
|
|
|
AspCloseSession(sessionRefNum, 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) {
|
|
AspCloseSession(sessionRefNum, False);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, (long)0);
|
|
return;
|
|
}
|
|
else
|
|
sessionInfo->atpRequestHandlerId = id;
|
|
|
|
// Okay, the workstation session is now in full operation.
|
|
|
|
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;
|
|
else if (errorCode is ATatpTransactionAborted)
|
|
;
|
|
else if (errorCode isnt ATnoError) {
|
|
(*completionRoutine)(errorCode, userData, sessionRefNum,
|
|
empty, empty, 0, 0);
|
|
return;
|
|
}
|
|
|
|
// Find our corresponding sessionInfo and write or command info.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
sessionInfo = FindSessionInfoFor(sessionRefNum);
|
|
if (sessionInfo is empty) {
|
|
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;
|
|
}
|
|
for (writeOrCommandInfo = sessionInfo->writeOrCommandInfoList,
|
|
previousWriteOrCommandInfo = empty;
|
|
writeOrCommandInfo isnt empty;
|
|
previousWriteOrCommandInfo = writeOrCommandInfo,
|
|
writeOrCommandInfo = writeOrCommandInfo->next)
|
|
if (writeOrCommandInfo->sequenceNumber is sequenceNumber)
|
|
break;
|
|
if (writeOrCommandInfo is empty) {
|
|
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;
|
|
|
|
// 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);
|
|
return;
|
|
|
|
} // IncomingWriteOrCommandComplete
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction long GetNextSessionRefNum(void)
|
|
{
|
|
Boolean inUse, wrapped = False;
|
|
|
|
//
|
|
// 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;
|
|
while(inUse) {
|
|
inUse = False;
|
|
if ((lastSessionRefNum += 1) < 0) {
|
|
lastSessionRefNum = 0;
|
|
if (wrapped) {
|
|
inUse = True;
|
|
break;
|
|
}
|
|
wrapped = True;
|
|
}
|
|
if (FindSessionInfoFor(lastSessionRefNum) isnt Empty)
|
|
inUse = True;
|
|
}
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
if (inUse)
|
|
return((long)-1);
|
|
else
|
|
return(lastSessionRefNum);
|
|
|
|
} // GetNextSessionRefNum
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction SessionListenerInfo
|
|
FindSessionListenerInfoFor(long sessionListenerRefNum)
|
|
{
|
|
SessionListenerInfo sessionListenerInfo;
|
|
|
|
for (sessionListenerInfo = sessionListenerInfoHead;
|
|
sessionListenerInfo isnt empty;
|
|
sessionListenerInfo = sessionListenerInfo->next)
|
|
if (sessionListenerInfo->sessionListenerRefNum is sessionListenerRefNum)
|
|
break;
|
|
|
|
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");
|
|
|
|
for (getRequestInfo = sessionListenerInfo->getRequestInfoHashBuckets[index];
|
|
getRequestInfo isnt Empty;
|
|
getRequestInfo = getRequestInfo->nextForMySessionListener)
|
|
if (getRequestInfo->getRequestRefNum is getRequestRefNum)
|
|
break;
|
|
|
|
return(getRequestInfo);
|
|
|
|
} // FindGetRequestInfoFor
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void
|
|
RemoveGetRequestInfo(GetRequestInfo targetGetRequestInfo)
|
|
{
|
|
GetRequestInfo getRequestInfo, previousGetRequestInfo;
|
|
long index;
|
|
|
|
//
|
|
// 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 (targetGetRequestInfo->mySessionInfo is Empty) {
|
|
//
|
|
// Remove from the "next" (GetAnyRequest) list hanging off the session
|
|
// listener info structure.
|
|
//
|
|
|
|
for (previousGetRequestInfo = Empty,
|
|
getRequestInfo = targetGetRequestInfo->mySessionListener->
|
|
getRequestInfoList;
|
|
getRequestInfo isnt Empty;
|
|
previousGetRequestInfo = getRequestInfo,
|
|
getRequestInfo = getRequestInfo->next)
|
|
if (getRequestInfo is targetGetRequestInfo)
|
|
break;
|
|
|
|
//
|
|
// We should always be found... if not we have data structure corruption
|
|
// problems.
|
|
//
|
|
|
|
if (getRequestInfo is Empty) {
|
|
ErrorLog("RemoveGetRequestInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspGetRequestListBad, IMsgAspGetRequestListBad,
|
|
Insert0());
|
|
Free(targetGetRequestInfo);
|
|
return;
|
|
}
|
|
|
|
// Okay, remove 'im from the list.
|
|
|
|
if (previousGetRequestInfo is Empty)
|
|
targetGetRequestInfo->mySessionListener->getRequestInfoList =
|
|
targetGetRequestInfo->next;
|
|
else
|
|
previousGetRequestInfo->next = targetGetRequestInfo->next;
|
|
}
|
|
else {
|
|
//
|
|
// Remove from the "next" (GetRequest) list handing off the session
|
|
// info structure.
|
|
//
|
|
|
|
for (previousGetRequestInfo = Empty,
|
|
getRequestInfo = targetGetRequestInfo->mySessionInfo->
|
|
getRequestInfoList;
|
|
getRequestInfo isnt Empty;
|
|
previousGetRequestInfo = getRequestInfo,
|
|
getRequestInfo = getRequestInfo->next)
|
|
if (getRequestInfo is targetGetRequestInfo)
|
|
break;
|
|
|
|
//
|
|
// We should always be found... if not we have data structure corruption
|
|
// problems.
|
|
//
|
|
|
|
if (getRequestInfo is Empty) {
|
|
ErrorLog("RemoveGetRequestInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspGetRequestListBad, IMsgAspGetRequestListBad,
|
|
Insert0());
|
|
Free(targetGetRequestInfo);
|
|
return;
|
|
}
|
|
|
|
// Okay, remove 'im from the list.
|
|
|
|
if (previousGetRequestInfo is Empty)
|
|
targetGetRequestInfo->mySessionInfo->getRequestInfoList =
|
|
targetGetRequestInfo->next;
|
|
else
|
|
previousGetRequestInfo->next = targetGetRequestInfo->next;
|
|
}
|
|
|
|
// Remove from per-Sls hash list.
|
|
|
|
CheckMod(index, targetGetRequestInfo->getRequestRefNum,
|
|
NumGetRequestInfoHashBuckets, "RemoveGetRequestInfo");
|
|
for (previousGetRequestInfo = Empty,
|
|
getRequestInfo = targetGetRequestInfo->mySessionListener->
|
|
getRequestInfoHashBuckets[index];
|
|
getRequestInfo isnt Empty;
|
|
previousGetRequestInfo = getRequestInfo,
|
|
getRequestInfo = getRequestInfo->nextForMySessionListener)
|
|
if (getRequestInfo is targetGetRequestInfo)
|
|
break;
|
|
|
|
//
|
|
// We should always be found... if not we have data structure corruption
|
|
// problems.
|
|
//
|
|
|
|
if (getRequestInfo is Empty) {
|
|
ErrorLog("RemoveGetRequestInfo", ISevError, __LINE__, UnknownPort,
|
|
IErrAspGetRequestListBad, IMsgAspGetRequestListBad,
|
|
Insert0());
|
|
Free(targetGetRequestInfo);
|
|
return;
|
|
}
|
|
|
|
// Okay, remove from the hash chain.
|
|
|
|
if (previousGetRequestInfo is Empty)
|
|
targetGetRequestInfo->mySessionListener->getRequestInfoHashBuckets[index] =
|
|
targetGetRequestInfo->nextForMySessionListener;
|
|
else
|
|
previousGetRequestInfo->nextForMySessionListener =
|
|
targetGetRequestInfo->nextForMySessionListener;
|
|
|
|
return;
|
|
|
|
} // RemoveGetRequestInfo
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void
|
|
FreeGetRequestInfo(GetRequestInfo targetGetRequestInfo)
|
|
{
|
|
// We're set, free the beast, and run away.
|
|
|
|
if (targetGetRequestInfo->freeOpaqueWriteContinueData)
|
|
FreeOpaqueDataDescriptor(targetGetRequestInfo->opaqueWriteContinueData);
|
|
Free(targetGetRequestInfo);
|
|
return;
|
|
|
|
} // FreeGetRequestInfo
|
|
|
|
|
|
|
|
|
|
#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
|