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.
5361 lines
187 KiB
5361 lines
187 KiB
/* adsp.c, /atalk-ii/source, Garth Conboy, 03/30/90 */
|
|
/* Copyright (c) 1989 by Pacer Software Inc., La Jolla, CA */
|
|
|
|
/* GC - Initial coding.
|
|
GC - (04/01/90): April fools!
|
|
GC - (05/14/90): Back to testing...
|
|
GC - (06/11/90): Added, at PFC's request, a little better error checking
|
|
in incoming attention handling.
|
|
GC - (06/12/90): In AdspPacketIn() an ATsocketClosed should be handled
|
|
AFTER packet defer logic has been executed.
|
|
GC - (06/19/90): One call to AdspGetAttention will yield only one call
|
|
to the completion routine... must re-arm for the next
|
|
attention.
|
|
GC - (06/28/90): AdspGetAttention should take a user buffer, and should
|
|
"complete" if the connection closes.
|
|
GC - (06/28/90): AdspSendAttention should be async, and should "complete"
|
|
if the connection closes.
|
|
GC - (08/18/90): New error logging mechanism.
|
|
GC - (11/03/90): Some fixes from OS/2.
|
|
GC - (07/29/91): Use AdspCloseConnection rather than RemoveConnectionEnd
|
|
in ProbeTimerExpired.
|
|
GC - (08/07/91): Ack request in SendData when send window closes to zero.
|
|
GC - (12/17/91): Corrected a couple of off-by-one errors when comparing
|
|
SendSeqNum with sendWindowSeqNum; could have caused
|
|
extra sends. Fix from GMP at Iris.
|
|
GC - (12/17/91): Send "ack" *after* calculating receiveSeqNum and
|
|
receiveWindowSize. Fix from GMP at Iris.
|
|
GC - (03/30/92): Updated for BufferDescriptors.
|
|
GC - (06/27/92): All buffers coming from user space are now "opaque," they
|
|
may or may not be "char *." We now use the new routines
|
|
MoveFromOpaque() and MoveToOpaque() to access these
|
|
"buffer"s rather than MemMove().
|
|
GC - (09/02/92): When creating a new connection listener, allow an already
|
|
open Ddp socket to be passed in. Changes from Nikki at
|
|
Microsoft.
|
|
GC - (09/02/92): Added AdspGetAnything to have a single completion
|
|
routine that can handle both incoming data and incoming
|
|
attentions. A GetAnything will take precidence over
|
|
and AdspReceive and an AdspGetAttention. Also, check
|
|
buffer size at entry to AspGetAttention.
|
|
GC - (10/06/92): On an AdspSend(), try to flush the data onto the wire
|
|
(regardless of the setting of the flushFlag) if we've
|
|
completely filled our send queue.
|
|
GC - (11/08/92): AdspSend may now complete partial sends, and will return
|
|
the number of bytes enqueued.
|
|
GC - (11/15/92): Integrated Nikki's (Microsoft) changes for Adsp event
|
|
handler support. See "adsp.h" and "socket.h" for more
|
|
information.
|
|
GC - (11/15/92): Corrected a bug that could cause BufferQueueSize() and
|
|
MaxNextReadSizeOfBufferQueue() to return "-1" (or be off
|
|
by one of the low side) if a chunk in a BufferQueue was
|
|
completly processed (i.e. "startIndex is (dataSize +
|
|
endOfMessage)"). This would eventually cause recoverable
|
|
BufferQueue corruption. Tracked down with the help of
|
|
Eric Smith at Telebit.
|
|
GC - (11/15/92): Added support for incoming attention event handlers.
|
|
GC - (11/15/92): Added AdspSetCookieForConnection() and
|
|
AdspGetCookieForConnection().
|
|
GC - (11/15/92): A AdspGetAnything handler will now return the attention
|
|
code as the first two bytes of the buffer rather than
|
|
as a unqiue argument.
|
|
GC - (11/17/92): Added AdspCancelGetConnectionRequest();
|
|
GC - (11/24/92): Added "send okay event" support.
|
|
GC - (12/10/92): Added AdspPeek(), a rather cool idea from Microsoft
|
|
(Nikki).
|
|
|
|
*** Make the PVCS source control system happy:
|
|
$Header$
|
|
$Log$
|
|
***
|
|
|
|
The Adsp portion of PacerTalk.
|
|
|
|
*/
|
|
|
|
#define IncludeAdspErrors 1
|
|
|
|
#include "atalk.h"
|
|
|
|
#define VerboseMessages 0 /* 0 for quiet */
|
|
#define DebugCode 1 /* 0 for none */
|
|
|
|
/* ************************************ */
|
|
/* *** Static data and definitions. *** */
|
|
/* ************************************ */
|
|
|
|
#if not defined(DeferAdspPackets)
|
|
/* Deferred ADSP packet queue: */
|
|
|
|
#define MaximumDeferredPackets 10
|
|
typedef struct dp { struct dp far *next;
|
|
Boolean forConnectionListener;
|
|
AppleTalkErrorCode errorCode;
|
|
long unsigned userData;
|
|
int port;
|
|
AppleTalkAddress source;
|
|
long destinationSocket;
|
|
short datagramLength;
|
|
AppleTalkAddress actualDestination;
|
|
char datagram[1];
|
|
} far *DeferredPacket;
|
|
static volatile DeferredPacket headOfDeferredPacketList = empty;
|
|
static volatile DeferredPacket tailOfDeferredPacketList = empty;
|
|
static volatile short currentDeferredPacketCount = 0;
|
|
|
|
static volatile short handleAdspPacketsNesting = 0;
|
|
static volatile short deferIncomingAdspPacketsCount = 0;
|
|
#endif
|
|
|
|
static AppleTalkAddress unknownAddress;
|
|
|
|
/* ********************************* */
|
|
/* *** Internal static routines. *** */
|
|
/* ********************************* */
|
|
|
|
ExternForVisibleFunction IncomingDdpHandler AdspPacketIn;
|
|
ExternForVisibleFunction IncomingDdpHandler AdspConnectionListenerPacketIn;
|
|
|
|
ExternForVisibleFunction long MaxSendSize(ConnectionEnd connectionEnd);
|
|
|
|
ExternForVisibleFunction Boolean UnsignedBetweenWithWrap(long unsigned low,
|
|
long unsigned high,
|
|
long unsigned target);
|
|
|
|
ExternForVisibleFunction Boolean UnsignedGreaterWithWrap(long unsigned high,
|
|
long unsigned low);
|
|
|
|
ExternForVisibleFunction void
|
|
DecodeAdspHeader(char far *datagram,
|
|
short unsigned far *connectionId,
|
|
long unsigned far *firstByteSeqNum,
|
|
long unsigned far *nextReceiveSeqNum,
|
|
long far *receiveWindowSize,
|
|
int far *descriptor);
|
|
|
|
ExternForVisibleFunction void BuildAdspHeader(ConnectionEnd connectionEnd,
|
|
char *datagram,
|
|
int descriptor);
|
|
|
|
ExternForVisibleFunction void SendOpenControl(ConnectionEnd connectionEnd);
|
|
|
|
ExternForVisibleFunction void SendAttention(ConnectionEnd connectionEnd);
|
|
|
|
ExternForVisibleFunction void SendData(ConnectionEnd connectionEnd);
|
|
|
|
ExternForVisibleFunction AppleTalkErrorCode
|
|
ReadData(ConnectionEnd connectionEnd,
|
|
Boolean peekOnly,
|
|
long far *peekBytesReturned,
|
|
Boolean far *peekEndOfMessage);
|
|
|
|
ExternForVisibleFunction TimerHandler OpenTimerExpired;
|
|
ExternForVisibleFunction TimerHandler SendAttentionTimerExpired;
|
|
ExternForVisibleFunction TimerHandler ProbeTimerExpired;
|
|
ExternForVisibleFunction TimerHandler RetransmitTimerExpired;
|
|
ExternForVisibleFunction TimerHandler ForwardResetTimerExpired;
|
|
|
|
ExternForVisibleFunction void RemoveConnectionEnd(ConnectionEnd target);
|
|
|
|
ExternForVisibleFunction short unsigned
|
|
GetNextConnectionIdForSocket(long socket);
|
|
|
|
ExternForVisibleFunction long GetNextRefNum(void);
|
|
|
|
ExternForVisibleFunction ConnectionListenerInfo
|
|
FindConnectionListenerByRefNum(long refNum);
|
|
|
|
ExternForVisibleFunction ConnectionEnd
|
|
FindConnectionEndByLocalInfo(long socket,
|
|
short unsigned connectionId);
|
|
|
|
ExternForVisibleFunction ConnectionEnd
|
|
FindConnectionEndByRemoteInfo(AppleTalkAddress remoteAddress,
|
|
short unsigned remoteConnectionId);
|
|
|
|
ExternForVisibleFunction ConnectionEnd FindConnectionEndByRefNum(long refNum);
|
|
|
|
ExternForVisibleFunction Boolean
|
|
AddToBufferQueue(BufferQueue far *bufferQueue, void far *data,
|
|
long offset, long dataSize, Boolean dataIsOpaque,
|
|
Boolean endOfMessage, BufferQueue *auxBufferQueue);
|
|
|
|
ExternForVisibleFunction long
|
|
ReadFromBufferQueue(BufferQueue far *bufferQueue, void far *data,
|
|
long offset, long dataSize, Boolean dataIsOpaque,
|
|
Boolean far *endOfMessage);
|
|
|
|
ExternForVisibleFunction long
|
|
ReadAndDiscardFromBufferQueue(BufferQueue far *bufferQueue,
|
|
void far *data,
|
|
long offset, long dataSize,
|
|
Boolean dataIsOpaque,
|
|
Boolean far *endOfMessage);
|
|
|
|
ExternForVisibleFunction Boolean
|
|
DiscardFromBufferQueue(BufferQueue far *bufferQueue, long dataSize,
|
|
BufferQueue far *auxBufferQueue);
|
|
|
|
ExternForVisibleFunction long
|
|
MaxNextReadSizeOfBufferQueue(BufferQueue far *bufferQueue,
|
|
Boolean far *endOfMessage);
|
|
|
|
ExternForVisibleFunction long BufferQueueSize(BufferQueue far *bufferQueue);
|
|
|
|
ExternForVisibleFunction void FreeBufferQueue(BufferQueue far *bufferQueue);
|
|
|
|
ExternForVisibleFunction char far
|
|
*GetLookaheadPointer(BufferQueue far *bufferQueue, long far *size);
|
|
|
|
#if not defined(DeferAdspPackets)
|
|
ExternForVisibleFunction void DeferAdspPackets(void);
|
|
ExternForVisibleFunction void HandleAdspPackets(void);
|
|
#endif
|
|
|
|
/* ************************** */
|
|
/* *** External routines. *** */
|
|
/* ************************** */
|
|
|
|
AppleTalkErrorCode far AdspSetWindowSizes(
|
|
long newSendWindow, /* Max send window in bytes. */
|
|
long newReceiveWindow) /* Max receive window in bytes. */
|
|
{
|
|
/* Validate args... */
|
|
|
|
if (newSendWindow < 0 or
|
|
newReceiveWindow < 0 or
|
|
newSendWindow > MaxSendReceiveWindowSize or
|
|
newReceiveWindow > MaxSendReceiveWindowSize)
|
|
return(ATadspBadWindowSize);
|
|
|
|
/* Set new values; zero means use default. */
|
|
|
|
if (newSendWindow is 0)
|
|
newSendWindow = DefaultSendReceiveWindowSize;
|
|
if (newReceiveWindow is 0)
|
|
newReceiveWindow = DefaultSendReceiveWindowSize;
|
|
|
|
maxSendWindowSize = newSendWindow;
|
|
maxReceiveWindowSize = newReceiveWindow;
|
|
return(ATnoError);
|
|
|
|
} /* AdspSetWindowSizes */
|
|
|
|
AppleTalkErrorCode far AdspCreateConnectionListener(
|
|
int port, /* What port? */
|
|
ExtendedAppleTalkNodeNumber *desiredNode, /* On specified node? */
|
|
long existingDdpSocket, /* If >= 0, existing ddp socket to use; -1
|
|
to open a new one. */
|
|
int desiredSocket, /* Specified socket; 0 = dynamic; ignored if
|
|
above arg is -1. */
|
|
long far *listenerRefNum, /* New connection listener ref num. */
|
|
long far *socketHandle, /* If non-empty, the socket that we've pened. */
|
|
AdspConnectionEventHandler far *eventHandler,
|
|
/* Optional incoming connections
|
|
event handler. */
|
|
long unsigned eventContext) /* Context for above. */
|
|
{
|
|
AppleTalkErrorCode returnCode;
|
|
long socket;
|
|
ConnectionListenerInfo connectionListenerInfo;
|
|
|
|
/* Okay, set up to create the session listener node... privacy please! */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
|
|
/* Find a new connectionListenerRefNum */
|
|
|
|
while (True)
|
|
{
|
|
for (connectionListenerInfo = connectionListenerList;
|
|
connectionListenerInfo isnt empty;
|
|
connectionListenerInfo = connectionListenerInfo->next)
|
|
if (connectionListenerInfo->connectionListenerRefNum is
|
|
nextConnectionListenerRefNum)
|
|
break;
|
|
if (connectionListenerInfo is empty)
|
|
break;
|
|
nextConnectionListenerRefNum += 1;
|
|
}
|
|
*listenerRefNum = nextConnectionListenerRefNum;
|
|
nextConnectionListenerRefNum += 1;
|
|
|
|
/* Can we open a socket for the listener? */
|
|
|
|
if (existingDdpSocket >= 0)
|
|
{
|
|
/* Set the handler for this socket to be the Adsp handler. */
|
|
|
|
socket = existingDdpSocket;
|
|
returnCode = NewHandlerForSocket(socket, AdspConnectionListenerPacketIn,
|
|
(long unsigned)*listenerRefNum, False);
|
|
}
|
|
else
|
|
returnCode = OpenSocketOnNode(&socket, port, desiredNode, desiredSocket,
|
|
AdspConnectionListenerPacketIn,
|
|
(long unsigned)*listenerRefNum, False,
|
|
empty, 0, empty);
|
|
|
|
if (returnCode < ATnoError)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(returnCode);
|
|
}
|
|
|
|
if (socketHandle isnt empty)
|
|
*socketHandle = socket;
|
|
|
|
/* Okay, build a new connectionListenerInfo node. */
|
|
|
|
if ((connectionListenerInfo =
|
|
Calloc(sizeof(*connectionListenerInfo), 1)) is empty)
|
|
{
|
|
if (existingDdpSocket < 0)
|
|
CloseSocketOnNode(socket, Empty, (long)0);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
connectionListenerInfo->connectionListenerRefNum = *listenerRefNum;
|
|
connectionListenerInfo->socket = socket;
|
|
connectionListenerInfo->closeSocket = (existingDdpSocket < 0);
|
|
|
|
/* Set the event handler info. */
|
|
|
|
connectionListenerInfo->connectionEventHandler = eventHandler;
|
|
connectionListenerInfo->connectionEventContext = eventContext;
|
|
|
|
connectionListenerInfo->next = connectionListenerList;
|
|
connectionListenerList = connectionListenerInfo;
|
|
|
|
/* All set. */
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspCreateConnectionListener */
|
|
|
|
AppleTalkErrorCode far AdspDeleteConnectionListener(
|
|
long listenerRefNum) /* Connection listener to delete. */
|
|
{
|
|
ConnectionListenerInfo connectionListenerInfo;
|
|
|
|
/* Can we find the guy? */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
|
|
connectionListenerInfo = FindConnectionListenerByRefNum(listenerRefNum);
|
|
if (connectionListenerInfo is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnectionListener);
|
|
}
|
|
|
|
/* Closing the socket will cause an ATsocketClosed to arrive at
|
|
AdspConnectionListenerPacketIn() and he will free up all of our
|
|
resources. */
|
|
|
|
if (connectionListenerInfo->closeSocket)
|
|
CloseSocketOnNode(connectionListenerInfo->socket, Empty, (long)0);
|
|
else
|
|
{
|
|
AppleTalkAddress dummy;
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
AdspConnectionListenerPacketIn(ATsocketClosed,
|
|
(long unsigned)listenerRefNum, 0, dummy,
|
|
0, DdpProtocolAdsp, Empty, 0, dummy);
|
|
return(ATnoError);
|
|
}
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspDeleteConnectionListener */
|
|
|
|
AppleTalkErrorCode far AdspSetConnectionEventHandler(
|
|
long listenerRefNum, /* Connection listener ref num. */
|
|
AdspConnectionEventHandler eventHandler, /* Incoming connections event
|
|
handler. */
|
|
long unsigned eventContext) /* Context for above. */
|
|
{
|
|
ConnectionListenerInfo connectionListenerInfo;
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionListenerInfo = FindConnectionListenerByRefNum(listenerRefNum))
|
|
is Empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnectionListener);
|
|
}
|
|
|
|
/* Set the event handler info. */
|
|
|
|
EnterCriticalSection();
|
|
connectionListenerInfo->connectionEventHandler = eventHandler;
|
|
connectionListenerInfo->connectionEventContext = eventContext;
|
|
LeaveCriticalSection();
|
|
|
|
/* All set. */
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspSetConnectionEventHandler */
|
|
|
|
AppleTalkErrorCode far AdspGetConnectionRequest(
|
|
long listenerRefNum, /* Connection listener to get request from */
|
|
long far *getConnectionRequestRefNum,
|
|
/* RefNum for this get connection request;
|
|
so we can cancel it. */
|
|
AdspIncomingOpenRequestHandler *completionRoutine,
|
|
/* Routine to call with the request */
|
|
long unsigned userData) /* To be passed to above routine */
|
|
{
|
|
ConnectionListenerInfo connectionListenerInfo;
|
|
OpenRequestHandler openRequestHandler;
|
|
long refNum;
|
|
Boolean match;
|
|
|
|
/* Register a handler for an incoming open request, when a open request comes
|
|
in the completionRoutine will be called with the following arguments:
|
|
|
|
errorCode - AppleTalkErrorCode; status of operation.
|
|
userData - long unsigned; as passed to us.
|
|
source - AppleTalkAddress; source of the open request.
|
|
listenerRefNum
|
|
- long; as passed to us - the connection listener that the
|
|
open request has come in on.
|
|
refNum - long; refNum for connection if it is accepted; this value
|
|
must be passed to AdspAcceptConnectionRequest() and
|
|
AdspDenyConnectionRequest() to identify the request.
|
|
*/
|
|
|
|
/* Find our connection listener */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
|
|
connectionListenerInfo = FindConnectionListenerByRefNum(listenerRefNum);
|
|
if (connectionListenerInfo is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnectionListener);
|
|
}
|
|
|
|
/* Find a new get connection request ref num. */
|
|
|
|
match = True;
|
|
while(match)
|
|
{
|
|
match = False;
|
|
for (openRequestHandler = connectionListenerInfo->openRequestHandlers;
|
|
not match and openRequestHandler isnt Empty;
|
|
openRequestHandler = openRequestHandler->next)
|
|
if (openRequestHandler->getConnectionRequestRefNum is
|
|
nextGetConnectionRequestRefNum)
|
|
match = True;
|
|
if (not match)
|
|
break;
|
|
nextGetConnectionRequestRefNum += 1;
|
|
}
|
|
refNum = nextGetConnectionRequestRefNum;
|
|
nextGetConnectionRequestRefNum += 1;
|
|
if (getConnectionRequestRefNum isnt Empty)
|
|
*getConnectionRequestRefNum = refNum;
|
|
|
|
/* Build a new open request handler node. */
|
|
|
|
if ((openRequestHandler = Calloc(sizeof(*openRequestHandler), 1)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
|
|
/* Copy in the info, thread into the listener request handler list, and
|
|
we're set! */
|
|
|
|
openRequestHandler->getConnectionRequestRefNum = refNum;
|
|
openRequestHandler->completionRoutine = completionRoutine;
|
|
openRequestHandler->userData = userData;
|
|
openRequestHandler->next = connectionListenerInfo->openRequestHandlers;
|
|
connectionListenerInfo->openRequestHandlers = openRequestHandler;
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspGetConnectionRequest */
|
|
|
|
AppleTalkErrorCode far AdspCancelGetConnectionRequest(
|
|
long listenerRefNum, /* Connection listener on which to cancel
|
|
a GetConnectionRequest. */
|
|
long getConnectionRequestRefNum)
|
|
/* Request handler to cancel. */
|
|
{
|
|
ConnectionListenerInfo connectionListenerInfo;
|
|
OpenRequestHandler openRequestHandler, previousOpenRequestHandler;
|
|
|
|
/* Find our connection listener */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
|
|
connectionListenerInfo = FindConnectionListenerByRefNum(listenerRefNum);
|
|
if (connectionListenerInfo is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnectionListener);
|
|
}
|
|
|
|
/* Find the specified GetConnectionRequest. */
|
|
|
|
for (previousOpenRequestHandler = Empty,
|
|
openRequestHandler = connectionListenerInfo->openRequestHandlers;
|
|
openRequestHandler isnt Empty;
|
|
previousOpenRequestHandler = openRequestHandler,
|
|
openRequestHandler = openRequestHandler->next)
|
|
if (openRequestHandler->getConnectionRequestRefNum is
|
|
getConnectionRequestRefNum)
|
|
break;
|
|
|
|
/* We better have found one, and he better not be in use. */
|
|
|
|
if (openRequestHandler is Empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchGetConnectionReq);
|
|
}
|
|
if (openRequestHandler->inUse)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspGetConnectionRequestInUse);
|
|
}
|
|
|
|
/* Okay, remove our target. */
|
|
|
|
if (previousOpenRequestHandler is Empty)
|
|
connectionListenerInfo->openRequestHandlers = openRequestHandler->next;
|
|
else
|
|
previousOpenRequestHandler->next = openRequestHandler->next;
|
|
Free(openRequestHandler);
|
|
|
|
/* All set. */
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspCancelGetConnectionRequest */
|
|
|
|
AppleTalkErrorCode AdspDenyConnectionRequest(
|
|
long listenerRefNum, /* Listener that to open request that we're
|
|
denying was targeted at. */
|
|
long refNum) /* The request that we're denying. */
|
|
{
|
|
ConnectionListenerInfo connectionListenerInfo;
|
|
OpenRequestHandler previousOpenRequestHandler, openRequestHandler;
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* Find the listener and the inUse handler. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
|
|
connectionListenerInfo = FindConnectionListenerByRefNum(listenerRefNum);
|
|
if (connectionListenerInfo is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnectionListener);
|
|
}
|
|
for (previousOpenRequestHandler = empty,
|
|
openRequestHandler = connectionListenerInfo->openRequestHandlers;
|
|
openRequestHandler isnt empty;
|
|
previousOpenRequestHandler = openRequestHandler,
|
|
openRequestHandler = openRequestHandler->next)
|
|
if (openRequestHandler->inUse and
|
|
openRequestHandler->refNum is refNum)
|
|
break;
|
|
if (openRequestHandler is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
/* Go ahead and unthread the open request handler -- we'll free it before we
|
|
flee. */
|
|
|
|
if (previousOpenRequestHandler is empty)
|
|
connectionListenerInfo->openRequestHandlers = openRequestHandler->next;
|
|
else
|
|
previousOpenRequestHandler->next = openRequestHandler->next;
|
|
|
|
/* Okay, I admit it, this is going to sound a little stange: We want to set
|
|
up a dummy connection end so that we can use SendOpenControl() to really
|
|
send the deny request -- we pretend for this single send that "this" end
|
|
of the connection is really the connection lister. Yes, it's a little bit
|
|
easier this way. */
|
|
|
|
|
|
if ((connectionEnd = Calloc(sizeof(*connectionEnd), 1)) is empty)
|
|
{
|
|
Free(openRequestHandler);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
connectionEnd->connectionState = AdspClosed; /* This will cause deny */
|
|
connectionEnd->refNum = openRequestHandler->refNum;
|
|
connectionEnd->socket = connectionListenerInfo->socket;
|
|
connectionEnd->remoteConnectionId = openRequestHandler->remoteConnectionId;
|
|
connectionEnd->remoteAddress = openRequestHandler->remoteAddress;
|
|
connectionEnd->seenRemoteOpenRequest = True;
|
|
|
|
/* Okay, deny the connection. */
|
|
|
|
SendOpenControl(connectionEnd);
|
|
Free(connectionEnd);
|
|
|
|
/* We're set now. */
|
|
|
|
Free(openRequestHandler);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspDenyConnectionRequest */
|
|
|
|
AppleTalkErrorCode AdspAcceptConnectionRequest(
|
|
long listenerRefNum, /* Listener that to open request that we're
|
|
accepting was targeted at. */
|
|
long refNum, /* The request that we're accepting. */
|
|
long far *socketHandle, /* >= 0 if new Adsp session should be
|
|
opened on specified handle; < 0 or
|
|
empty ignored as input; non-empty
|
|
socket handle used will be returned. */
|
|
int port, /* If new socket, what port? */
|
|
ExtendedAppleTalkNodeNumber *desiredNode,
|
|
/* If new socket, on specified node? */
|
|
int desiredSocket, /* If new socket, 0 = dynamic. */
|
|
AdspOpenCompleteHandler *completionRoutine,
|
|
/* Who to call when open completes. */
|
|
long unsigned userData, /* User data to pass to above routine. */
|
|
AdspReceiveEventHandler far *receiveEventHandler,
|
|
/* Optional receive data event handler. */
|
|
long unsigned receiveEventContext,
|
|
/* Context to pass to above. */
|
|
AdspReceiveAttnEventHandler far *receiveAttentionEventHandler,
|
|
/* Optional receive attention event handler. */
|
|
long unsigned receiveAttentionEventContext,
|
|
/* Context for the above. */
|
|
AdspSendOkayEventHandler far *sendOkayEventHandler,
|
|
/* Send window now non-zero event handler. */
|
|
long unsigned sendOkayEventContext,
|
|
/* Context for the above. */
|
|
AdspDisconnectEventHandler far *disconnectEventHandler,
|
|
/* Optional disconnect event handler. */
|
|
long unsigned disconnectEventContext)
|
|
/* Context for the above. */
|
|
{
|
|
ConnectionListenerInfo connectionListenerInfo;
|
|
OpenRequestHandler previousOpenRequestHandler, openRequestHandler;
|
|
ConnectionEnd connectionEnd;
|
|
long ourSocket;
|
|
short unsigned connectionId;
|
|
long index;
|
|
AppleTalkErrorCode errorCode;
|
|
AppleTalkAddress dummyAddress;
|
|
Boolean closeSocket = True;
|
|
|
|
/* Find the listener and the inUse handler. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
|
|
connectionListenerInfo = FindConnectionListenerByRefNum(listenerRefNum);
|
|
if (connectionListenerInfo is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnectionListener);
|
|
}
|
|
for (previousOpenRequestHandler = empty,
|
|
openRequestHandler = connectionListenerInfo->openRequestHandlers;
|
|
openRequestHandler isnt empty;
|
|
previousOpenRequestHandler = openRequestHandler,
|
|
openRequestHandler = openRequestHandler->next)
|
|
if (openRequestHandler->inUse and
|
|
openRequestHandler->refNum is refNum)
|
|
break;
|
|
if (openRequestHandler is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
/* Go ahead and unthread the open request handler -- we'll free it before we
|
|
flee. */
|
|
|
|
if (previousOpenRequestHandler is empty)
|
|
connectionListenerInfo->openRequestHandlers = openRequestHandler->next;
|
|
else
|
|
previousOpenRequestHandler->next = openRequestHandler->next;
|
|
|
|
/* If socket handle is valid, it be must a currently open. If it's already
|
|
an Adsp socket, we replicate the "closeSocket" info, if it's a Ddp
|
|
socket we set "closeSocket" to False. */
|
|
|
|
if (socketHandle isnt empty and *socketHandle >= 0)
|
|
{
|
|
/* First check to see if it's already an Adsp socket. */
|
|
|
|
CheckMod(index, *socketHandle, NumberOfConnectionEndHashBkts,
|
|
"AdspAcceptConnectionRequest");
|
|
for (connectionEnd = connectionEndLocalHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
connectionEnd = connectionEnd->nextByLocalInfo)
|
|
if (connectionEnd->socket is *socketHandle)
|
|
break;
|
|
if (connectionEnd isnt Empty)
|
|
closeSocket = connectionEnd->closeSocket;
|
|
else
|
|
{
|
|
/* Now check to make sure it's a Ddp socket... if so, switch handlers
|
|
to Adsp. */
|
|
|
|
if ((errorCode = MapSocketToAddress(*socketHandle, &dummyAddress)) isnt
|
|
ATnoError or
|
|
(errorCode = NewHandlerForSocket(*socketHandle, AdspPacketIn,
|
|
(long)0, False)) isnt ATnoError)
|
|
{
|
|
Free(openRequestHandler);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
closeSocket = False;
|
|
}
|
|
ourSocket = *socketHandle;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise use the requested "new socket info" to open one. */
|
|
|
|
if ((errorCode = OpenSocketOnNode(&ourSocket, port, desiredNode,
|
|
desiredSocket, AdspPacketIn,
|
|
(long)0, False, empty, 0,
|
|
empty)) isnt ATnoError)
|
|
{
|
|
Free(openRequestHandler);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
}
|
|
if (socketHandle isnt empty)
|
|
*socketHandle = ourSocket;
|
|
|
|
/* Find a local connectionId. */
|
|
|
|
if ((connectionId = GetNextConnectionIdForSocket(ourSocket)) is 0)
|
|
{
|
|
if (closeSocket)
|
|
CloseSocketOnNode(ourSocket, Empty, (long)0);
|
|
Free(openRequestHandler);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATinternalError);
|
|
}
|
|
|
|
/* Okay, start building a connection end. */
|
|
|
|
if ((connectionEnd = Calloc(sizeof(*connectionEnd), 1)) is empty)
|
|
{
|
|
if (closeSocket)
|
|
CloseSocketOnNode(ourSocket, Empty, (long)0);
|
|
Free(openRequestHandler);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
|
|
/* Fill in the skeleton, so that, as least, we can use RemoveConnectionEnd
|
|
if things go wrong from here on in. */
|
|
|
|
connectionEnd->connectionState = AdspHalfOpen;
|
|
connectionEnd->passiveOpen = True;
|
|
connectionEnd->refNum = refNum;
|
|
connectionEnd->connectionId = connectionId;
|
|
connectionEnd->socket = ourSocket;
|
|
connectionEnd->closeSocket = closeSocket;
|
|
connectionEnd->openCompletionRoutine = completionRoutine;
|
|
connectionEnd->openUserData = userData;
|
|
connectionEnd->receiveWindowSize = maxReceiveWindowSize;
|
|
connectionEnd->sendQueueMax = maxSendWindowSize;
|
|
connectionEnd->receiveQueueMax = maxReceiveWindowSize;
|
|
|
|
/* Setup the event handlers - they could all be empty. */
|
|
|
|
connectionEnd->receiveEventHandler = receiveEventHandler;
|
|
connectionEnd->receiveEventContext = receiveEventContext;
|
|
connectionEnd->receiveAttentionEventHandler = receiveAttentionEventHandler;
|
|
connectionEnd->receiveAttentionEventContext = receiveAttentionEventContext;
|
|
connectionEnd->sendOkayEventHandler = sendOkayEventHandler;
|
|
connectionEnd->sendOkayEventContext = sendOkayEventContext;
|
|
connectionEnd->disconnectEventHandler = disconnectEventHandler;
|
|
connectionEnd->disconnectEventContext = disconnectEventContext;
|
|
|
|
/* Use the information stored in our open request handler to build the
|
|
remote side of the connection end. */
|
|
|
|
connectionEnd->seenRemoteOpenRequest = True;
|
|
connectionEnd->remoteAddress = openRequestHandler->remoteAddress;
|
|
connectionEnd->remoteConnectionId = openRequestHandler->remoteConnectionId;
|
|
connectionEnd->sendSeqNum = openRequestHandler->remoteNextReceiveSeqNum;
|
|
connectionEnd->retransmitSeqNum = openRequestHandler->remoteNextReceiveSeqNum;
|
|
connectionEnd->sendWindowSeqNum =
|
|
openRequestHandler->remoteNextReceiveSeqNum +
|
|
(long unsigned)openRequestHandler->remoteReceiveWindowSize -
|
|
(long unsigned)1;
|
|
connectionEnd->receiveAttentionSeqNum =
|
|
openRequestHandler->receiveAttentionSeqNum;
|
|
|
|
/* Start our open timer, and send our first ReqAck... */
|
|
|
|
connectionEnd->openTimerId = StartTimer(OpenTimerExpired,
|
|
AdspOpenIntervalSeconds,
|
|
sizeof(connectionEnd->refNum),
|
|
(char *)&connectionEnd->refNum);
|
|
SendOpenControl(connectionEnd);
|
|
|
|
/* Thread this guy into the two of the three lookup tables... we'll fill
|
|
in the RemoteInfo lookup table when we actually get the Ack. */
|
|
|
|
CheckMod(index, connectionEnd->refNum, NumberOfConnectionEndHashBkts,
|
|
"AdspAcceptConnectionRequest");
|
|
connectionEnd->next = connectionEndRefNumHashBuckets[index];
|
|
connectionEndRefNumHashBuckets[index] = connectionEnd;
|
|
CheckMod(index, connectionEnd->socket, NumberOfConnectionEndHashBkts,
|
|
"AdspAcceptConnectionRequest");
|
|
connectionEnd->nextByLocalInfo = connectionEndLocalHashBuckets[index];
|
|
connectionEndLocalHashBuckets[index] = connectionEnd;
|
|
|
|
/* Such a deal! */
|
|
|
|
Free(openRequestHandler);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspAcceptConnectionRequest */
|
|
|
|
AppleTalkErrorCode far AdspOpenConnectionOnNode(
|
|
AdspOpenType openType, /* Active or passive */
|
|
long far *socketHandle, /* >= 0 if new Adsp session should be
|
|
opened on specified handle; < 0 or
|
|
empty ignored as input; non-empty
|
|
socket handle used will be returned. */
|
|
int port, /* If new socket, what port? */
|
|
ExtendedAppleTalkNodeNumber *desiredNode,
|
|
/* If new socket, on specified node? */
|
|
int desiredSocket, /* If new socket, 0 = dynamic. */
|
|
AppleTalkAddress remoteAddress, /* Target of open (ignored for passive) */
|
|
long far *refNum, /* New connection end refNum. */
|
|
AdspOpenCompleteHandler *completionRoutine,
|
|
/* Who to call when open completes. */
|
|
long unsigned userData, /* User data to pass to above routine. */
|
|
AdspReceiveEventHandler far *receiveEventHandler,
|
|
/* Optional receive data event handler. */
|
|
long unsigned receiveEventContext,
|
|
/* Context to pass to above. */
|
|
AdspReceiveAttnEventHandler far *receiveAttentionEventHandler,
|
|
/* Optional receive attention event handler. */
|
|
long unsigned receiveAttentionEventContext,
|
|
/* Context for the above. */
|
|
AdspSendOkayEventHandler far *sendOkayEventHandler,
|
|
/* Send window now non-zero event handler. */
|
|
long unsigned sendOkayEventContext,
|
|
/* Context for the above. */
|
|
AdspDisconnectEventHandler far *disconnectEventHandler,
|
|
/* Optional disconnect event handler. */
|
|
long unsigned disconnectEventContext)
|
|
/* Context for the above. */
|
|
{
|
|
/* Start an Adsp open to the specifed remote address, when complete call
|
|
the completion routine with the following arguments:
|
|
|
|
errorCode - AppleTalkErrorCode; Completion status.
|
|
userData - long unsigned; as passed to us.
|
|
refNum - long; refNum of connection (as returned in *refNum).
|
|
socket - long; socket on which connection is now open.
|
|
remoteAddress - AppleTalkAddress; real remote address.
|
|
*/
|
|
|
|
long index;
|
|
long ourSocket;
|
|
ConnectionEnd connectionEnd;
|
|
short unsigned connectionId;
|
|
AppleTalkErrorCode errorCode;
|
|
AppleTalkAddress dummyAddress;
|
|
Boolean closeSocket = True;
|
|
|
|
/* Privacy please. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
|
|
/* If socket handle is valid, it be must a currently open. If it's already
|
|
an Adsp socket, we replicate the "closeSocket" info, if it's a Ddp
|
|
socket we set "closeSocket" to False. */
|
|
|
|
if (socketHandle isnt empty and *socketHandle >= 0)
|
|
{
|
|
/* First check to see if it's already an Adsp socket. */
|
|
|
|
CheckMod(index, *socketHandle, NumberOfConnectionEndHashBkts,
|
|
"AdspAcceptConnectionRequest");
|
|
for (connectionEnd = connectionEndLocalHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
connectionEnd = connectionEnd->nextByLocalInfo)
|
|
if (connectionEnd->socket is *socketHandle)
|
|
break;
|
|
if (connectionEnd isnt Empty)
|
|
closeSocket = connectionEnd->closeSocket;
|
|
else
|
|
{
|
|
/* Now check to make sure it's a Ddp socket... if so, switch handlers
|
|
to Adsp. */
|
|
|
|
if ((errorCode = MapSocketToAddress(*socketHandle, &dummyAddress)) isnt
|
|
ATnoError or
|
|
(errorCode = NewHandlerForSocket(*socketHandle, AdspPacketIn,
|
|
(long)0, False)) isnt ATnoError)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
closeSocket = False;
|
|
}
|
|
ourSocket = *socketHandle;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise use the requested "new socket info" to open one. */
|
|
|
|
if ((errorCode = OpenSocketOnNode(&ourSocket, port, desiredNode,
|
|
desiredSocket, AdspPacketIn,
|
|
(long)0, False, empty, 0,
|
|
empty)) isnt ATnoError)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
}
|
|
if (socketHandle isnt empty)
|
|
*socketHandle = ourSocket;
|
|
|
|
/* Find a new refNum and connectionId. */
|
|
|
|
if ((*refNum = GetNextRefNum()) < 0 or
|
|
(connectionId = GetNextConnectionIdForSocket(ourSocket)) is 0)
|
|
{
|
|
if (closeSocket)
|
|
CloseSocketOnNode(ourSocket, Empty, (long)0);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATinternalError);
|
|
}
|
|
|
|
/* Okay, start building a connection end. */
|
|
|
|
if ((connectionEnd = Calloc(sizeof(*connectionEnd), 1)) is empty)
|
|
{
|
|
if (closeSocket)
|
|
CloseSocketOnNode(ourSocket, Empty, (long)0);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
|
|
/* Fill in the skeleton, so that, as least, we can use RemoveConnectionEnd
|
|
if things go wrong from here on in. */
|
|
|
|
connectionEnd->connectionState = AdspHalfOpen;
|
|
if (openType is AdspPassiveOpen)
|
|
connectionEnd->passiveOpen = True;
|
|
connectionEnd->refNum = *refNum;
|
|
connectionEnd->connectionId = connectionId;
|
|
connectionEnd->socket = ourSocket;
|
|
connectionEnd->closeSocket = closeSocket;
|
|
if (openType is AdspActiveOpen)
|
|
connectionEnd->remoteAddress = remoteAddress;
|
|
connectionEnd->openCompletionRoutine = completionRoutine;
|
|
connectionEnd->openUserData = userData;
|
|
connectionEnd->receiveWindowSize = maxReceiveWindowSize;
|
|
connectionEnd->sendQueueMax = maxSendWindowSize;
|
|
connectionEnd->receiveQueueMax = maxReceiveWindowSize;
|
|
|
|
/* Setup the event handlers - they could all be empty. */
|
|
|
|
connectionEnd->receiveEventHandler = receiveEventHandler;
|
|
connectionEnd->receiveEventContext = receiveEventContext;
|
|
connectionEnd->receiveAttentionEventHandler = receiveAttentionEventHandler;
|
|
connectionEnd->receiveAttentionEventContext = receiveAttentionEventContext;
|
|
connectionEnd->sendOkayEventHandler = sendOkayEventHandler;
|
|
connectionEnd->sendOkayEventContext = sendOkayEventContext;
|
|
connectionEnd->disconnectEventHandler = disconnectEventHandler;
|
|
connectionEnd->disconnectEventContext = disconnectEventContext;
|
|
|
|
/* Thread this guy into the two lookup tables that we know enough about
|
|
now (by refNum and by localInfo). */
|
|
|
|
CheckMod(index, connectionEnd->refNum, NumberOfConnectionEndHashBkts,
|
|
"AdspOpenConnectionOnNode");
|
|
connectionEnd->next = connectionEndRefNumHashBuckets[index];
|
|
connectionEndRefNumHashBuckets[index] = connectionEnd;
|
|
CheckMod(index, connectionEnd->socket, NumberOfConnectionEndHashBkts,
|
|
"AdspOpenConnectionOnNode");
|
|
connectionEnd->nextByLocalInfo = connectionEndLocalHashBuckets[index];
|
|
connectionEndLocalHashBuckets[index] = connectionEnd;
|
|
|
|
/* Okay, send the first open request and start the retry timer. */
|
|
|
|
if (openType is AdspActiveOpen)
|
|
{
|
|
SendOpenControl(connectionEnd);
|
|
connectionEnd->openTimerId = StartTimer(OpenTimerExpired,
|
|
AdspOpenIntervalSeconds,
|
|
sizeof(connectionEnd->refNum),
|
|
(char *)&connectionEnd->refNum);
|
|
}
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspOpenConnectionOnNode */
|
|
|
|
AppleTalkErrorCode AdspSetDataEventHandlers(
|
|
long refNum, /* The request that we're accepting. */
|
|
AdspReceiveEventHandler far *receiveEventHandler,
|
|
/* Optional receive data event handler. */
|
|
long unsigned receiveEventContext,
|
|
/* Context to pass to above. */
|
|
AdspReceiveAttnEventHandler far *receiveAttentionEventHandler,
|
|
/* Optional receive attention event handler. */
|
|
long unsigned receiveAttentionEventContext,
|
|
/* Context for the above. */
|
|
AdspSendOkayEventHandler far *sendOkayEventHandler,
|
|
/* Send window now non-zero event handler. */
|
|
long unsigned sendOkayEventContext,
|
|
/* Context for the above. */
|
|
AdspDisconnectEventHandler far *disconnectEventHandler,
|
|
/* Optional disconnect event handler. */
|
|
long unsigned disconnectEventContext)
|
|
/* Context for the above. */
|
|
{
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* Find the connection end. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is Empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
/* Setup the event handlers - they could all be empty. */
|
|
|
|
EnterCriticalSection();
|
|
connectionEnd->receiveEventHandler = receiveEventHandler;
|
|
connectionEnd->receiveEventContext = receiveEventContext;
|
|
connectionEnd->receiveAttentionEventHandler = receiveAttentionEventHandler;
|
|
connectionEnd->receiveAttentionEventContext = receiveAttentionEventContext;
|
|
connectionEnd->sendOkayEventHandler = sendOkayEventHandler;
|
|
connectionEnd->sendOkayEventContext = sendOkayEventContext;
|
|
connectionEnd->disconnectEventHandler = disconnectEventHandler;
|
|
connectionEnd->disconnectEventContext = disconnectEventContext;
|
|
LeaveCriticalSection();
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspSetDataEventHandlers */
|
|
|
|
AppleTalkErrorCode far AdspCloseConnection(
|
|
long refNum, /* The connection refNum to close. */
|
|
Boolean remoteClose) /* All external callers should pass
|
|
pass "False;" Adsp internally will use
|
|
either True or False as required. */
|
|
{
|
|
ConnectionEnd connectionEnd;
|
|
BufferDescriptor datagram;
|
|
|
|
/* Find our connection. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
/* Should we send a CloseAdvice to the other side? */
|
|
|
|
if (not remoteClose and connectionEnd->connectionState is AdspOpen)
|
|
{
|
|
if ((datagram = NewBufferDescriptor(AdspDataOffset)) is Empty)
|
|
{
|
|
ErrorLog("AdspCloseConnection", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
BuildAdspHeader(connectionEnd, datagram->data, AdspControlFlag +
|
|
AdspCloseConnectionCode);
|
|
if (DeliverDdp(connectionEnd->socket,
|
|
connectionEnd->remoteAddress,
|
|
DdpProtocolAdsp,
|
|
datagram,
|
|
AdspDataOffset,
|
|
Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("AdspCloseConnection", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadCloseAdviseSend, IMsgAdspBadCloseAdviseSend,
|
|
Insert0());
|
|
}
|
|
|
|
/* If we're in the process of opening, call the open completion routine. */
|
|
|
|
/* BUGBUG: We're still an active connection here, so what if this completion
|
|
routine calls AdspCloseConnection... it'll get invoked again!
|
|
Temporarilly solve this with resetting the flag first... MP
|
|
issue here, though. */
|
|
|
|
if (connectionEnd->connectionState is AdspHalfOpen)
|
|
{
|
|
connectionEnd->connectionState = AdspClosed;
|
|
(*connectionEnd->openCompletionRoutine)(ATadspConnectionClosed,
|
|
connectionEnd->openUserData,
|
|
refNum, (long)0,
|
|
unknownAddress);
|
|
connectionEnd->connectionState = AdspHalfOpen;
|
|
if (not connectionEnd->passiveOpen or
|
|
connectionEnd->seenRemoteOpenRequest)
|
|
CancelTimer(connectionEnd->openTimerId);
|
|
}
|
|
|
|
/* If we have a read pending, terminate it. */
|
|
|
|
if (connectionEnd->receivePending)
|
|
{
|
|
connectionEnd->receivePending = False;
|
|
(*connectionEnd->receiveCompletionRoutine)(ATadspConnectionClosed,
|
|
connectionEnd->receiveUserData,
|
|
connectionEnd->refNum,
|
|
empty, (long)0, False);
|
|
}
|
|
if (connectionEnd->getAnythingPending)
|
|
{
|
|
connectionEnd->getAnythingPending = False;
|
|
(*connectionEnd->getAnythingCompletionRoutine)(ATadspConnectionClosed,
|
|
connectionEnd->
|
|
getAnythingUserData,
|
|
connectionEnd->refNum,
|
|
False, Empty, 0, False);
|
|
}
|
|
|
|
/* If we have a forward reset pending close it. */
|
|
|
|
if (connectionEnd->outgoingForwardReset)
|
|
{
|
|
connectionEnd->outgoingForwardReset = False;
|
|
(*connectionEnd->forwardResetAckHandler)(ATadspConnectionClosed,
|
|
connectionEnd->
|
|
forwardResetAckUserData,
|
|
connectionEnd->refNum);
|
|
CancelTimer(connectionEnd->forwardResetTimerId);
|
|
}
|
|
|
|
/* If we have an incoming attention handler, notify it of the close. */
|
|
|
|
if (connectionEnd->incomingAttentionHandler isnt empty)
|
|
{
|
|
AdspIncomingAttentionRoutine *incomingAttentionHandler;
|
|
incomingAttentionHandler = connectionEnd->incomingAttentionHandler;
|
|
connectionEnd->incomingAttentionHandler = Empty;
|
|
(*incomingAttentionHandler)(ATadspConnectionClosed,
|
|
connectionEnd-> incomingAttentionUserData,
|
|
connectionEnd->refNum, 0, empty, 0);
|
|
}
|
|
|
|
/* If we're waiting for an attention to complete, notify it of the close.
|
|
Stop the retry timer too. */
|
|
|
|
if (connectionEnd->waitingForAttentionAck)
|
|
{
|
|
connectionEnd->waitingForAttentionAck = False;
|
|
if (connectionEnd->outgoingAttentionAckHandler isnt empty)
|
|
(*connectionEnd->outgoingAttentionAckHandler)
|
|
(ATadspConnectionClosed,
|
|
connectionEnd->outgoingAttentionAckUserData,
|
|
connectionEnd->refNum);
|
|
CancelTimer(connectionEnd->outgoingAttentionTimerId);
|
|
if (connectionEnd->outgoingAttentionBuffer isnt empty)
|
|
Free(connectionEnd->outgoingAttentionBuffer);
|
|
}
|
|
|
|
/* If we're open, cancel the connection maintenance and retransmit timers. */
|
|
|
|
if (connectionEnd->connectionState is AdspOpen)
|
|
{
|
|
CancelTimer(connectionEnd->probeTimerId);
|
|
CancelTimer(connectionEnd->retransmitTimerId);
|
|
}
|
|
|
|
/* If there is a disconnect event handler, and if this was a remote or a
|
|
non-client disconnect, call it with the error. */
|
|
|
|
if (remoteClose and (connectionEnd->disconnectEventHandler isnt Empty))
|
|
{
|
|
(*connectionEnd->disconnectEventHandler)(connectionEnd->refNum,
|
|
connectionEnd->disconnectEventContext,
|
|
ATadspConnectionClosed);
|
|
connectionEnd->disconnectEventHandler = empty;
|
|
}
|
|
|
|
/* All set, remove the beast. */
|
|
|
|
RemoveConnectionEnd(connectionEnd);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspCloseConnection */
|
|
|
|
AppleTalkErrorCode far AdspSetCookieForConnection(
|
|
long refNum, /* The connection on which to set the cookie. */
|
|
long unsigned cookie) /* The new cookie. */
|
|
{
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* Find our connection. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
/* Do the deed. */
|
|
|
|
connectionEnd->usersCookie = cookie;
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspSetCookieForConnection */
|
|
|
|
AppleTalkErrorCode far AdspGetCookieForConnection(
|
|
long refNum, /* The connection from which to get the cookie. */
|
|
long unsigned far *cookie) /* Where to stick the cookie. */
|
|
{
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* Find our connection. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
/* Do the deed. */
|
|
|
|
*cookie = connectionEnd->usersCookie;
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspGetCookieForConnection */
|
|
|
|
AppleTalkErrorCode far AdspForwardReset(
|
|
long refNum, /* The connection refNum to reset. */
|
|
AdspForwardResetAckHandler *completionRoutine,
|
|
/* Routine to call when forward reset is
|
|
Acked; may be empty. */
|
|
long unsigned userData) /* User data for above call. */
|
|
{
|
|
/* Call the specified completion routine (which may be empty) when our
|
|
forward reset is Acked:
|
|
|
|
errorCode - AppleTalkErrorCode; status of operation.
|
|
userData - long unsigned; as passed to us.
|
|
refNum - long; our connection ref num.
|
|
*/
|
|
|
|
ConnectionEnd connectionEnd;
|
|
AppleTalkErrorCode errorCode;
|
|
BufferDescriptor datagram;
|
|
|
|
/* Find our connection. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
/* Only one at a time. */
|
|
|
|
if (connectionEnd->outgoingForwardReset)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspFwdResetAlreadyPending);
|
|
}
|
|
|
|
/* Discard any unsent and unacked data in the send queue. */
|
|
|
|
FreeBufferQueue(&connectionEnd->sendQueue);
|
|
connectionEnd->nextSendQueue = connectionEnd->sendQueue;
|
|
connectionEnd->retransmitSeqNum = connectionEnd->sendSeqNum;
|
|
|
|
/* Build the ForwardReset packet and send it on its way. */
|
|
|
|
if ((datagram = NewBufferDescriptor(AdspDataOffset)) is Empty)
|
|
{
|
|
ErrorLog("AdspForwardReset", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
BuildAdspHeader(connectionEnd, datagram->data, AdspControlFlag +
|
|
AdspForwardResetCode);
|
|
if ((errorCode = DeliverDdp(connectionEnd->socket,
|
|
connectionEnd->remoteAddress,
|
|
DdpProtocolAdsp,
|
|
datagram,
|
|
AdspDataOffset,
|
|
Empty, Empty, 0)) isnt ATnoError)
|
|
{
|
|
ErrorLog("AdspForwardReset", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadFrwdResetSend, IMsgAdspBadFrwdResetSend,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
/* Save the info in the connection end, and start the retry timer. */
|
|
|
|
connectionEnd->outgoingForwardReset = True;
|
|
connectionEnd->forwardResetAckHandler = completionRoutine;
|
|
connectionEnd->forwardResetAckUserData = userData;
|
|
connectionEnd->forwardResetTimerId =
|
|
StartTimer(ForwardResetTimerExpired,
|
|
ForwardResetTimerIntenvalSecs,
|
|
sizeof(connectionEnd->refNum),
|
|
(char *)&connectionEnd->refNum);
|
|
|
|
/* All set. */
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspForwardReset */
|
|
|
|
AppleTalkErrorCode far AdspGetAttention(
|
|
long refNum, /* Connection to handle attentions for. */
|
|
void far *opaqueBuffer, /* "Buffer" to fill in with attention data;
|
|
must be able to hold 570 bytes. */
|
|
long bufferSize, /* Size of buffer. */
|
|
AdspIncomingAttentionRoutine *completionRoutine,
|
|
/* Routine to call when attentions come in */
|
|
long unsigned userData) /* Data to be passed to above routine. */
|
|
{
|
|
ConnectionEnd connectionEnd;
|
|
|
|
if (opaqueBuffer is empty or bufferSize < AdspMaxAttentionDataSize)
|
|
return(ATadspBadBufferSize);
|
|
|
|
/* Find our connection. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
if (connectionEnd->incomingAttentionHandler isnt empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspHandlerAlreadyQueued);
|
|
}
|
|
|
|
/* Copy in the suppiled data. */
|
|
|
|
connectionEnd->incomingAttentionHandler = completionRoutine;
|
|
connectionEnd->incomingAttentionUserData = userData;
|
|
connectionEnd->incomingAttentionOpaqueBuffer = opaqueBuffer;
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspGetAttention */
|
|
|
|
AppleTalkErrorCode far AdspSendAttention(
|
|
long refNum, /* Connection to send attention on. */
|
|
short unsigned attentionCode, /* Attention code to send. */
|
|
void far *attentionOpaqueBuffer,
|
|
/* Attention "buffer" to send. */
|
|
int attentionBufferSize, /* Size of above. */
|
|
AdspAttentionAckHandler *completionRoutine,
|
|
/* Routine to call when Ack comes in
|
|
(may be empty). */
|
|
long unsigned userData) /* To be passed to the above. */
|
|
{
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* Good attention code? MinAdspAttentionCode is zero, so there's no need
|
|
to test for "attentionCode < MinAdspAttentionCode" */
|
|
|
|
if (attentionCode > MaxAdspAttentionCode)
|
|
return(ATadspBadAttentionCode);
|
|
if (attentionBufferSize < 0 or
|
|
attentionBufferSize > AdspMaxAttentionDataSize)
|
|
return(ATadspBadAttentionBufferSize);
|
|
|
|
/* Find our connection. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
if (attentionOpaqueBuffer is empty)
|
|
attentionBufferSize = 0;
|
|
if (attentionBufferSize is 0)
|
|
attentionOpaqueBuffer = empty;
|
|
|
|
/* Do we already have an attention pending? */
|
|
|
|
if (connectionEnd->waitingForAttentionAck)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspAttentionAlreadyPending);
|
|
}
|
|
|
|
/* Copy the needed fields into the connection end. */
|
|
|
|
connectionEnd->waitingForAttentionAck = True;
|
|
connectionEnd->outgoingAttentionCode = attentionCode;
|
|
connectionEnd->outgoingAttentionBufferSize = attentionBufferSize;
|
|
connectionEnd->outgoingAttentionAckHandler = completionRoutine;
|
|
connectionEnd->outgoingAttentionAckUserData = userData;
|
|
if (attentionBufferSize isnt 0)
|
|
{
|
|
if ((connectionEnd->outgoingAttentionBuffer = Malloc(attentionBufferSize))
|
|
is empty)
|
|
{
|
|
connectionEnd->waitingForAttentionAck = False;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
MoveFromOpaque(connectionEnd->outgoingAttentionBuffer,
|
|
attentionOpaqueBuffer, 0, attentionBufferSize);
|
|
}
|
|
|
|
/* Send the Attention and start the retry timer. */
|
|
|
|
SendAttention(connectionEnd);
|
|
connectionEnd->outgoingAttentionTimerId =
|
|
StartTimer(SendAttentionTimerExpired,
|
|
AttentionTimerIntervalSeconds,
|
|
sizeof(connectionEnd->refNum),
|
|
(char far *)&connectionEnd->refNum);
|
|
|
|
/* All set! */
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspSendAttention */
|
|
|
|
AppleTalkErrorCode far AdspMaxCurrentSendSize(
|
|
long refNum, /* Connection to query. */
|
|
long far *size) /* Returned size. */
|
|
{
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* Find our connection. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
*size = MaxSendSize(connectionEnd);
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspMaxCurrentSendSize */
|
|
|
|
AppleTalkErrorCode far AdspMaxCurrentReceiveSize(
|
|
long refNum, /* Connection to query. */
|
|
long far *size, /* Returned size. */
|
|
Boolean *endOfMessage) /* Is an EOM pending? */
|
|
{
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* Find our connection. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
*size = MaxNextReadSizeOfBufferQueue(&connectionEnd->receiveQueue,
|
|
endOfMessage);
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AdspMaxCurrentReceiveSize */
|
|
|
|
AppleTalkErrorCode far AdspSend(
|
|
long refNum, /* Connection to send data on. */
|
|
void far *opaqueBuffer, /* Data "buffer." */
|
|
long bufferSize, /* Its size. */
|
|
Boolean endOfMessage, /* End of logical message? */
|
|
Boolean flushFlag, /* Flush before we return? */
|
|
long far *bytesSent) /* How may bytes did we enqueue? The
|
|
EOM bit counts as one byte. */
|
|
{
|
|
ConnectionEnd connectionEnd;
|
|
long sendSize;
|
|
AppleTalkErrorCode errorCode = ATnoError;
|
|
|
|
/* Nop? */
|
|
|
|
if (bufferSize is 0 and not endOfMessage and not flushFlag)
|
|
return(ATnoError);
|
|
if (bufferSize < 0)
|
|
return(ATadspBadBufferSize);
|
|
|
|
/* Find our connection. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
/* If we can't fit any of the buffer, give up. If it might do some good,
|
|
try a flush -- maybe that will dislodge things. */
|
|
|
|
sendSize = MaxSendSize(connectionEnd);
|
|
if (sendSize is 0)
|
|
{
|
|
if (BufferQueueSize(&connectionEnd->nextSendQueue) isnt 0 and
|
|
connectionEnd->sendSeqNum isnt connectionEnd->sendWindowSeqNum + 1)
|
|
SendData(connectionEnd);
|
|
connectionEnd->sendWindowHasClosed = True;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspCouldNotEnqueueSend);
|
|
}
|
|
|
|
/* If we can't process the entire send, do what we can. */
|
|
|
|
if (bufferSize + endOfMessage > sendSize)
|
|
{
|
|
bufferSize = sendSize;
|
|
endOfMessage = False;
|
|
errorCode = ATadspCouldNotFullyEnqueueSend;
|
|
}
|
|
|
|
/* Add the data to the send queue. */
|
|
|
|
if (not AddToBufferQueue(&connectionEnd->sendQueue, opaqueBuffer, 0,
|
|
bufferSize, True, endOfMessage,
|
|
&connectionEnd->nextSendQueue))
|
|
{
|
|
ErrorLog("AdspSend", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATinternalError);
|
|
}
|
|
|
|
/* Flush if requested (or we're full), (and it would do some good), else,
|
|
we're done! */
|
|
|
|
if ((flushFlag or MaxSendSize(connectionEnd) is 0) and
|
|
BufferQueueSize(&connectionEnd->nextSendQueue) isnt 0 and
|
|
connectionEnd->sendSeqNum isnt connectionEnd->sendWindowSeqNum + 1)
|
|
SendData(connectionEnd);
|
|
|
|
if (bytesSent isnt Empty)
|
|
*bytesSent = bufferSize + endOfMessage;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} /* AdspSend */
|
|
|
|
AppleTalkErrorCode far AdspReceive(
|
|
long refNum, /* Session to read from. */
|
|
void far *opaqueBuffer, /* "Buffer" to read into. */
|
|
long bufferSize, /* Size of above. */
|
|
AdspReceiveHandler *completionRoutine,
|
|
/* Routine to call with data. */
|
|
long unsigned userData) /* UserData to pass to above. */
|
|
{
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* When the read completes call our completion routine with the following
|
|
arguments:
|
|
|
|
errorCode - AppleTalkErrorCode; completion code.
|
|
userData - long unsigned; as passed to us.
|
|
refNum - long; as passed to us.
|
|
opaqueBuffer - void far *; as passed to us.
|
|
bufferSize - long; actual returned buffer length.
|
|
endOfMessage - Boolean; logical end of message?
|
|
*/
|
|
|
|
/* Buffer size of zero if okay... justing looking for EOM. */
|
|
|
|
if (bufferSize < 0)
|
|
return(ATadspBadBufferSize);
|
|
|
|
/* Find our connection. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
/* Too many? */
|
|
|
|
if (connectionEnd->receivePending)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspReadAlreadyPending);
|
|
}
|
|
|
|
/* Okay, copy the information into the connection end. */
|
|
|
|
connectionEnd->receiveCompletionRoutine = completionRoutine;
|
|
connectionEnd->receiveUserData = userData;
|
|
connectionEnd->receiveOpaqueBuffer = opaqueBuffer;
|
|
connectionEnd->receiveBufferSize = bufferSize;
|
|
connectionEnd->receivePending = True;
|
|
|
|
/* Try to return any data that's waiting now.
|
|
|
|
** ReadData() will undefer **
|
|
*/
|
|
|
|
if (not connectionEnd->dataEventInProgress)
|
|
ReadData(connectionEnd, False, Empty, Empty);
|
|
else
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
return(ATnoError);
|
|
|
|
} /* AdspReceive */
|
|
|
|
AppleTalkErrorCode far AdspPeek(
|
|
long refNum, /* Session to peek into. */
|
|
void far *opaqueBuffer, /* Buffer to fill. */
|
|
long bufferSize, /* Size of buffer. */
|
|
long far *bytesReturned, /* Bytes filled in. */
|
|
Boolean *endOfMessage) /* EOM too? */
|
|
{
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* Buffer size of zero if okay... justing peeking for EOM. */
|
|
|
|
if (bufferSize < 0)
|
|
return(ATadspBadBufferSize);
|
|
|
|
/* Find our connection. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
/* Too many? */
|
|
|
|
if (connectionEnd->receivePending)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspReadAlreadyPending);
|
|
}
|
|
|
|
/* Okay, copy the information into the connection end. */
|
|
|
|
connectionEnd->receiveOpaqueBuffer = opaqueBuffer;
|
|
connectionEnd->receiveBufferSize = bufferSize;
|
|
|
|
/* Try to return any data that's waiting now.
|
|
|
|
** ReadData() will undefer **
|
|
*/
|
|
|
|
return(ReadData(connectionEnd, True, bytesReturned, endOfMessage));
|
|
|
|
} /* AdspPeek */
|
|
|
|
AppleTalkErrorCode far AdspGetAnything(
|
|
long refNum, /* Session to read from. */
|
|
void far *opaqueBuffer, /* "Buffer" to read into. */
|
|
long bufferSize, /* Size of above. */
|
|
AdspGetAnythingHandler *completionRoutine,
|
|
/* Routine to call with data. */
|
|
long unsigned userData) /* UserData to pass to above. */
|
|
{
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* When either real data comes in or an attention comes in, call our
|
|
completion routine with the following arguments:
|
|
|
|
errorCode - AppleTalkErrorCode; completion code.
|
|
userData - long unsigned; as passed to us.
|
|
refNum - long; as passed to us.
|
|
attentionData
|
|
- Boolean; is the "stuff" attention data?
|
|
attentionCode
|
|
- short unsigned; if above is True, this is the
|
|
attention code.
|
|
opaqueBuffer - void far *; as passed to us.
|
|
bufferSize - long; actual returned buffer length.
|
|
endOfMessage - Boolean; if "attentionData" is False, does this
|
|
"real buffer" include an end-of-message?
|
|
|
|
A "GetAnything" takes precidence over a AdspRead or an AdspGetAttention.
|
|
*/
|
|
|
|
/* Buffer size must be able to hold a full attention, since we don't
|
|
know what we're going to get! */
|
|
|
|
if (opaqueBuffer is Empty or bufferSize < 0)
|
|
return(ATadspBadBufferSize);
|
|
|
|
/* Find our connection. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspNoSuchConnection);
|
|
}
|
|
|
|
/* Too many? */
|
|
|
|
if (connectionEnd->getAnythingPending)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATadspGetAnythingAlreadyPending);
|
|
}
|
|
|
|
/* Okay, copy the information into the connection end. */
|
|
|
|
connectionEnd->getAnythingCompletionRoutine = completionRoutine;
|
|
connectionEnd->getAnythingUserData = userData;
|
|
connectionEnd->getAnythingOpaqueBuffer = opaqueBuffer;
|
|
connectionEnd->getAnythingBufferSize = bufferSize;
|
|
connectionEnd->getAnythingPending = True;
|
|
|
|
/* Try to return any data that's waiting now.
|
|
|
|
** ReadData() will undefer **
|
|
*/
|
|
|
|
if (not connectionEnd->dataEventInProgress)
|
|
ReadData(connectionEnd, False, Empty, Empty);
|
|
else
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
return(ATnoError);
|
|
|
|
} /* AdspGetAnything */
|
|
|
|
/* @Add new external routines here@ */
|
|
|
|
/* ************************ */
|
|
/* *** Static routines. *** */
|
|
/* ************************ */
|
|
|
|
ExternForVisibleFunction long far
|
|
AdspConnectionListenerPacketIn(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
int port,
|
|
AppleTalkAddress source,
|
|
long destinationSocket,
|
|
int protocolType,
|
|
char far *datagram,
|
|
int datagramLength,
|
|
AppleTalkAddress actualDestination)
|
|
{
|
|
ConnectionListenerInfo connectionListenerInfo,
|
|
previousConnectionListenerInfo;
|
|
OpenRequestHandler openRequestHandler, nextOpenRequestHandler;
|
|
long listenerRefNum = (long)userData;
|
|
#if not defined(DeferAdspPackets)
|
|
DeferredPacket deferredPacket;
|
|
#endif
|
|
short unsigned remoteConnectionId;
|
|
long unsigned remoteFirstByteSeqNum;
|
|
long unsigned remoteNextReceiveSeqNum;
|
|
long remoteReceiveWindowSize;
|
|
int descriptor, controlCode;
|
|
short unsigned destinationConnectionId;
|
|
short unsigned adspVersionStamp;
|
|
long unsigned receiveAttentionSeqNum;
|
|
AdspIncomingOpenRequestHandler *completionRoutine;
|
|
AdspConnectionEventHandler far *connectionEventHandler;
|
|
long unsigned connectionEventContext;
|
|
long newRefNum;
|
|
|
|
/* Do we like the error code? */
|
|
|
|
if (errorCode isnt ATnoError and
|
|
errorCode isnt ATsocketClosed)
|
|
{
|
|
ErrorLog("AdspConnectionListenerPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspFunnyErrorCode, IMsgAdspFunnyErrorCode,
|
|
Insert0());
|
|
return((long)True);
|
|
}
|
|
|
|
#if not defined(DeferAdspPackets)
|
|
/* Should we be defering incoming packets? */
|
|
|
|
EnterCriticalSection();
|
|
if (deferIncomingAdspPacketsCount > 0)
|
|
{
|
|
#if VerboseMessages & 0
|
|
printf("AdspConnectionListenerPacketIn: from %d:%d:%d to %d; Deferred.\n",
|
|
source.networkNumber, source.nodeNumber, source.socketNumber,
|
|
destinationSocket);
|
|
#endif
|
|
if (currentDeferredPacketCount is MaximumDeferredPackets)
|
|
{
|
|
LeaveCriticalSection();
|
|
ErrorLog("AdspConnectionListenerPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspLosingData, IMsgAdspLosingData,
|
|
Insert0());
|
|
return((long)True);
|
|
}
|
|
LeaveCriticalSection();
|
|
deferredPacket = (DeferredPacket)Malloc(sizeof(*deferredPacket) +
|
|
datagramLength);
|
|
if (deferredPacket is empty)
|
|
{
|
|
ErrorLog("AdspConnectionListenerPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
return((long)True);
|
|
}
|
|
|
|
/* Fill in the data strcuture, and place the packet at the end of the
|
|
queue. */
|
|
|
|
deferredPacket->forConnectionListener = True;
|
|
deferredPacket->errorCode = errorCode;
|
|
deferredPacket->userData = userData;
|
|
deferredPacket->next = empty;
|
|
deferredPacket->port = port;
|
|
deferredPacket->source = source;
|
|
deferredPacket->destinationSocket = destinationSocket;
|
|
deferredPacket->datagramLength = (short)datagramLength;
|
|
deferredPacket->actualDestination = actualDestination;
|
|
if (datagram isnt empty)
|
|
MoveMem(deferredPacket->datagram, datagram, datagramLength);
|
|
|
|
EnterCriticalSection();
|
|
if (tailOfDeferredPacketList is empty)
|
|
tailOfDeferredPacketList = headOfDeferredPacketList = deferredPacket;
|
|
else
|
|
{
|
|
tailOfDeferredPacketList->next = deferredPacket;
|
|
tailOfDeferredPacketList = deferredPacket;
|
|
}
|
|
|
|
/* All set... return. */
|
|
|
|
currentDeferredPacketCount += 1;
|
|
LeaveCriticalSection();
|
|
|
|
return((long)True);
|
|
}
|
|
else
|
|
LeaveCriticalSection();
|
|
#endif
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
|
|
/* If we get a "socket closed", we should remove the connection listener
|
|
and notify any queued open request handlers. */
|
|
|
|
if (errorCode is ATsocketClosed)
|
|
{
|
|
for (previousConnectionListenerInfo = empty,
|
|
connectionListenerInfo = connectionListenerList;
|
|
connectionListenerInfo isnt empty;
|
|
previousConnectionListenerInfo = connectionListenerInfo,
|
|
connectionListenerInfo = connectionListenerInfo->next)
|
|
if (connectionListenerInfo->connectionListenerRefNum is
|
|
listenerRefNum)
|
|
break;
|
|
if (connectionListenerInfo is empty)
|
|
{
|
|
ErrorLog("AdspConnectionListenerPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspListenerMissing, IMsgAdspListenerMissing,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Remove the connection listener from the master list. */
|
|
|
|
if (previousConnectionListenerInfo is empty)
|
|
connectionListenerList = connectionListenerInfo->next;
|
|
else
|
|
previousConnectionListenerInfo->next = connectionListenerInfo->next;
|
|
|
|
/* If we have any open handlers, close 'em out. */
|
|
|
|
for (openRequestHandler = connectionListenerInfo->openRequestHandlers;
|
|
openRequestHandler isnt empty;
|
|
openRequestHandler = nextOpenRequestHandler)
|
|
{
|
|
nextOpenRequestHandler = openRequestHandler->next;
|
|
if (not openRequestHandler->inUse)
|
|
openRequestHandler->completionRoutine(ATadspConnectionListenerDeleted,
|
|
openRequestHandler->userData,
|
|
source, listenerRefNum,
|
|
(long)0);
|
|
Free(openRequestHandler);
|
|
}
|
|
|
|
/* Do we have any deferred connection events? */
|
|
|
|
EnterCriticalSection();
|
|
openRequestHandler = connectionListenerInfo->deferredConnectionEvents;
|
|
connectionListenerInfo->deferredConnectionEvents = Empty;
|
|
LeaveCriticalSection();
|
|
while (openRequestHandler isnt Empty)
|
|
{
|
|
nextOpenRequestHandler = openRequestHandler->next;
|
|
Free(openRequestHandler);
|
|
openRequestHandler = nextOpenRequestHandler;
|
|
}
|
|
|
|
/* Free the listener and we're finished! */
|
|
|
|
Free(connectionListenerInfo);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
|
|
}
|
|
|
|
/* Okay, find our connection listener. */
|
|
|
|
if ((connectionListenerInfo = FindConnectionListenerByRefNum(listenerRefNum))
|
|
is empty)
|
|
{
|
|
ErrorLog("AdspConnectionListenerPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspListenerMissing, IMsgAdspListenerMissing,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
if (protocolType isnt DdpProtocolAdsp)
|
|
{
|
|
ErrorLog("AdspConnectionListenerPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspOddProtocol, IMsgAdspOddProtocol,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Decode the header, if it's long enough. */
|
|
|
|
if (datagramLength < AdspDataOffset)
|
|
{
|
|
ErrorLog("AdspConnectionListenerPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspMissingHeader, IMsgAdspMissingHeader,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
DecodeAdspHeader(datagram, &remoteConnectionId, &remoteFirstByteSeqNum,
|
|
&remoteNextReceiveSeqNum, &remoteReceiveWindowSize,
|
|
&descriptor);
|
|
|
|
/* As a connection listener, we only care about open requests. */
|
|
|
|
controlCode = (descriptor & AdspControlCodeMask);
|
|
if (not (descriptor & AdspControlFlag) or
|
|
controlCode isnt AdspOpenConnectionReqCode)
|
|
{
|
|
ErrorLog("AdspConnectionListenerPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspFunnyRequest, IMsgAdspFunnyRequest,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Get the rest of the Open header. */
|
|
|
|
if (datagramLength < AdspNextAttentionSeqNumOffset + sizeof(long))
|
|
{
|
|
ErrorLog("AdspConnectionListenerPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspMissingHeader, IMsgAdspMissingHeader,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
MoveShortWireToMachine(datagram + AdspVersionStampOffset,
|
|
adspVersionStamp);
|
|
MoveShortWireToMachine(datagram + AdspDestConnectionIdOffset,
|
|
destinationConnectionId);
|
|
MoveLongWireToMachine(datagram + AdspNextAttentionSeqNumOffset,
|
|
receiveAttentionSeqNum);
|
|
|
|
/* Version okay? */
|
|
|
|
if (adspVersionStamp isnt AdspVersionStamp)
|
|
{
|
|
ErrorLog("AdspConnectionListenerPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspBadVersion, IMsgAdspBadVersion,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Is this a duplicate openRequest (same remoteID and same remote address)?
|
|
That is, have we already passed this guy up to the user to decide whether
|
|
to accept or deny the request? If so, ignore this open request. */
|
|
|
|
for (openRequestHandler = connectionListenerInfo->openRequestHandlers;
|
|
openRequestHandler isnt empty;
|
|
openRequestHandler = openRequestHandler->next)
|
|
if (openRequestHandler->inUse and
|
|
openRequestHandler->remoteConnectionId is remoteConnectionId and
|
|
AppleTalkAddressesEqual(&openRequestHandler->remoteAddress,
|
|
&source))
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Also go through the deferred events queue. */
|
|
|
|
for (openRequestHandler = connectionListenerInfo->deferredConnectionEvents;
|
|
openRequestHandler isnt empty;
|
|
openRequestHandler = openRequestHandler->next)
|
|
if (openRequestHandler->remoteConnectionId is remoteConnectionId and
|
|
AppleTalkAddressesEqual(&openRequestHandler->remoteAddress,
|
|
&source))
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Okay we're not a duplicate, do we have a pending non-in-use handler?
|
|
If not, ignore the request. NOTE: For events indicated, inUse will
|
|
always be true */
|
|
|
|
for (openRequestHandler = connectionListenerInfo->openRequestHandlers;
|
|
openRequestHandler isnt empty;
|
|
openRequestHandler = openRequestHandler->next)
|
|
if (not openRequestHandler->inUse)
|
|
break;
|
|
|
|
/* No GetConnection and no event handler? */
|
|
|
|
connectionEventHandler = connectionListenerInfo->connectionEventHandler;
|
|
connectionEventContext = connectionListenerInfo->connectionEventContext;
|
|
if (openRequestHandler is empty and
|
|
connectionEventHandler is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Copy the fields that we'll later need to accept the request into the
|
|
openRequest node. Tag the guy as InUse too. */
|
|
|
|
if ((newRefNum = GetNextRefNum()) < 0)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
if (openRequestHandler is empty)
|
|
{
|
|
/* We need to indicate this incoming connection - make an event handler. */
|
|
|
|
if ((openRequestHandler = Calloc(sizeof(*openRequestHandler), 1)) is empty)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
ErrorLog("AdspConnectionListenerPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
return((long)True);
|
|
}
|
|
openRequestHandler->eventHandler = True;
|
|
}
|
|
|
|
openRequestHandler->inUse = True;
|
|
openRequestHandler->refNum = newRefNum;
|
|
openRequestHandler->remoteAddress = source;
|
|
openRequestHandler->remoteConnectionId = remoteConnectionId;
|
|
openRequestHandler->remoteNextReceiveSeqNum = remoteNextReceiveSeqNum;
|
|
openRequestHandler->remoteReceiveWindowSize = remoteReceiveWindowSize;
|
|
openRequestHandler->receiveAttentionSeqNum = receiveAttentionSeqNum;
|
|
|
|
if (openRequestHandler->eventHandler)
|
|
{
|
|
EnterCriticalSection();
|
|
if (connectionListenerInfo->connectEventInProgress)
|
|
{
|
|
/* There is already an incoming connection event in progress. Queue
|
|
this event into the deferred queue. */
|
|
|
|
openRequestHandler->next =
|
|
connectionListenerInfo->deferredConnectionEvents;
|
|
connectionListenerInfo->deferredConnectionEvents = openRequestHandler;
|
|
LeaveCriticalSection();
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
connectionListenerInfo->connectEventInProgress = True;
|
|
LeaveCriticalSection();
|
|
|
|
/* Enter openRequestHandler into the connectionListener structure. */
|
|
|
|
openRequestHandler->next = connectionListenerInfo->openRequestHandlers;
|
|
connectionListenerInfo->openRequestHandlers = openRequestHandler;
|
|
|
|
/* Indicate the event, the accept()/deny() will happen whenever and we
|
|
don't have to worry about it as the thing is queued up in the
|
|
openRequestHandler queue anyway. */
|
|
|
|
while (True)
|
|
{
|
|
(*connectionEventHandler)(connectionListenerInfo->
|
|
connectionListenerRefNum,
|
|
connectionEventContext,
|
|
openRequestHandler->remoteAddress,
|
|
openRequestHandler->refNum);
|
|
EnterCriticalSection();
|
|
openRequestHandler = connectionListenerInfo->deferredConnectionEvents;
|
|
if (openRequestHandler is empty)
|
|
{
|
|
LeaveCriticalSection();
|
|
break;
|
|
}
|
|
|
|
/* Dequeue from deferred, put into the openRequest queue. */
|
|
|
|
connectionListenerInfo->deferredConnectionEvents =
|
|
openRequestHandler->next;
|
|
openRequestHandler->next = connectionListenerInfo->openRequestHandlers;
|
|
connectionListenerInfo->openRequestHandlers = openRequestHandler;
|
|
LeaveCriticalSection();
|
|
}
|
|
|
|
connectionListenerInfo->connectEventInProgress = False;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
else
|
|
{
|
|
/* Okay, set up to call the completion routine... let the user know there
|
|
is a new open request to deal with. */
|
|
|
|
completionRoutine = openRequestHandler->completionRoutine;
|
|
userData = openRequestHandler->userData;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnoError, userData, source, listenerRefNum,
|
|
newRefNum);
|
|
}
|
|
|
|
return((long)True);
|
|
|
|
} /* AdspConnectionListenerPacketIn */
|
|
|
|
ExternForVisibleFunction long far
|
|
AdspPacketIn(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
int port,
|
|
AppleTalkAddress source,
|
|
long destinationSocket,
|
|
int protocolType,
|
|
char far *datagram,
|
|
int datagramLength,
|
|
AppleTalkAddress actualDestination)
|
|
{
|
|
ConnectionEnd connectionEnd, nextConnectionEnd;
|
|
long index;
|
|
short unsigned remoteConnectionId;
|
|
long unsigned remoteFirstByteSeqNum;
|
|
long unsigned remoteNextReceiveSeqNum;
|
|
long remoteReceiveWindowSize;
|
|
int descriptor, controlCode;
|
|
short unsigned destinationConnectionId;
|
|
short unsigned adspVersionStamp;
|
|
long unsigned receiveAttentionSeqNum;
|
|
long unsigned newSendWindowSeqNum;
|
|
AdspOpenCompleteHandler *openCompletionRoutine;
|
|
AdspForwardResetAckHandler *forwardResetAckHandler;
|
|
AdspIncomingAttentionRoutine *incomingAttentionHandler;
|
|
AdspAttentionAckHandler *outgoingAttentionAckHandler = Empty;
|
|
AdspGetAnythingHandler *getAnythingRoutine = Empty;
|
|
AdspReceiveAttnEventHandler far *receiveAttentionEventHandler = Empty;
|
|
long unsigned receiveAttentionEventContext;
|
|
long unsigned outgoingAttentionAckUserData;
|
|
long refNum, bytesAccepted = 0;
|
|
#if not defined(DeferAdspPackets)
|
|
DeferredPacket deferredPacket;
|
|
#endif
|
|
Boolean endOfMessage;
|
|
long dataSize;
|
|
BufferDescriptor packet;
|
|
|
|
/* Do we like the error code? */
|
|
|
|
if (errorCode isnt ATnoError and
|
|
errorCode isnt ATsocketClosed)
|
|
{
|
|
ErrorLog("AdspPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspFunnyErrorCode, IMsgAdspFunnyErrorCode,
|
|
Insert0());
|
|
return((long)True);
|
|
}
|
|
|
|
#if not defined(DeferAdspPackets)
|
|
/* Should we be defering incoming packets? */
|
|
|
|
EnterCriticalSection();
|
|
if (deferIncomingAdspPacketsCount > 0)
|
|
{
|
|
#if VerboseMessages & 0
|
|
printf("AdspPacketIn: from %d:%d:%d to %d; Deferred.\n",
|
|
source.networkNumber, source.nodeNumber, source.socketNumber,
|
|
destinationSocket);
|
|
#endif
|
|
if (currentDeferredPacketCount is MaximumDeferredPackets)
|
|
{
|
|
LeaveCriticalSection();
|
|
ErrorLog("AdspPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspLosingData, IMsgAdspLosingData,
|
|
Insert0());
|
|
return((long)True);
|
|
}
|
|
LeaveCriticalSection();
|
|
deferredPacket = (DeferredPacket)Malloc(sizeof(*deferredPacket) +
|
|
datagramLength);
|
|
if (deferredPacket is empty)
|
|
{
|
|
ErrorLog("AdspPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
return((long)True);
|
|
}
|
|
|
|
/* Fill in the data strcuture, and place the packet at the end of the
|
|
queue. */
|
|
|
|
deferredPacket->forConnectionListener = False;
|
|
deferredPacket->errorCode = errorCode;
|
|
deferredPacket->userData = userData;
|
|
deferredPacket->next = empty;
|
|
deferredPacket->port = port;
|
|
deferredPacket->source = source;
|
|
deferredPacket->destinationSocket = destinationSocket;
|
|
deferredPacket->datagramLength = (short)datagramLength;
|
|
deferredPacket->actualDestination = actualDestination;
|
|
if (datagram isnt empty)
|
|
MoveMem(deferredPacket->datagram, datagram, datagramLength);
|
|
|
|
EnterCriticalSection();
|
|
if (tailOfDeferredPacketList is empty)
|
|
tailOfDeferredPacketList = headOfDeferredPacketList = deferredPacket;
|
|
else
|
|
{
|
|
tailOfDeferredPacketList->next = deferredPacket;
|
|
tailOfDeferredPacketList = deferredPacket;
|
|
}
|
|
|
|
/* All set... return. */
|
|
|
|
currentDeferredPacketCount += 1;
|
|
LeaveCriticalSection();
|
|
|
|
return((long)True);
|
|
}
|
|
else
|
|
LeaveCriticalSection();
|
|
#endif
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
|
|
/* If we get a "socket closed", we should close all Adsp sessions operating
|
|
on the incoming socket (regardless of connection ID). */
|
|
|
|
if (errorCode is ATsocketClosed)
|
|
{
|
|
CheckMod(index, destinationSocket, NumberOfConnectionEndHashBkts,
|
|
"AdspPacketIn");
|
|
for (connectionEnd = connectionEndLocalHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
connectionEnd = nextConnectionEnd)
|
|
{
|
|
nextConnectionEnd = connectionEnd->nextByLocalInfo;
|
|
if (connectionEnd->socket isnt destinationSocket)
|
|
continue;
|
|
connectionEnd->socket = -1; /* Already closed, obviously */
|
|
AdspCloseConnection(connectionEnd->refNum, True);
|
|
}
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
if (protocolType isnt DdpProtocolAdsp)
|
|
{
|
|
ErrorLog("AdspPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspOddProtocol, IMsgAdspOddProtocol,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Decode the header, if it's long enough. */
|
|
|
|
if (datagramLength < AdspDataOffset)
|
|
{
|
|
ErrorLog("AdspPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspMissingHeader, IMsgAdspMissingHeader,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
DecodeAdspHeader(datagram, &remoteConnectionId, &remoteFirstByteSeqNum,
|
|
&remoteNextReceiveSeqNum, &remoteReceiveWindowSize,
|
|
&descriptor);
|
|
|
|
/* Process Open oriented requests. */
|
|
|
|
controlCode = (descriptor & AdspControlCodeMask);
|
|
if ((descriptor & AdspControlFlag) and
|
|
controlCode >= AdspOpenConnectionReqCode and
|
|
controlCode <= AdspOpenConnectionDenyCode)
|
|
{
|
|
/* Get the rest of the Open header. */
|
|
|
|
if (datagramLength < AdspNextAttentionSeqNumOffset + sizeof(long))
|
|
{
|
|
ErrorLog("AdspPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspMissingHeader, IMsgAdspMissingHeader,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
MoveShortWireToMachine(datagram + AdspVersionStampOffset,
|
|
adspVersionStamp);
|
|
MoveShortWireToMachine(datagram + AdspDestConnectionIdOffset,
|
|
destinationConnectionId);
|
|
MoveLongWireToMachine(datagram + AdspNextAttentionSeqNumOffset,
|
|
receiveAttentionSeqNum);
|
|
|
|
/* Version okay? */
|
|
|
|
if (adspVersionStamp isnt AdspVersionStamp)
|
|
{
|
|
ErrorLog("AdspPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspBadVersion, IMsgAdspBadVersion,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Find our connection ID; if just "Req" we must have a HalfOpen
|
|
targetted at the source address; otherwise we can use the destination
|
|
connectionId and the source address for a normal local lookup. */
|
|
|
|
if (controlCode is AdspOpenConnectionReqCode)
|
|
{
|
|
CheckMod(index, destinationSocket, NumberOfConnectionEndHashBkts,
|
|
"AdspPacketIn");
|
|
for (connectionEnd = connectionEndLocalHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
connectionEnd = connectionEnd->nextByLocalInfo)
|
|
if (connectionEnd->socket is destinationSocket and
|
|
connectionEnd->connectionState is AdspHalfOpen and
|
|
not connectionEnd->seenRemoteOpenRequest and
|
|
(connectionEnd->passiveOpen or
|
|
AppleTalkAddressesEqual(&connectionEnd->remoteAddress,
|
|
&source)))
|
|
break;
|
|
}
|
|
else
|
|
connectionEnd = FindConnectionEndByLocalInfo(destinationSocket,
|
|
destinationConnectionId);
|
|
if (connectionEnd is empty)
|
|
{
|
|
/* Nobody to connect to. */
|
|
|
|
#if VerboseMessages
|
|
printf("Open control code = %d; no live target.\n", controlCode);
|
|
#endif
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
refNum = connectionEnd->refNum;
|
|
#if VerboseMessages
|
|
printf("Open control code %d to target RefNum = %d.\n", controlCode,
|
|
connectionEnd->refNum);
|
|
#endif
|
|
|
|
/* Handle easy deny case. */
|
|
|
|
if (controlCode is AdspOpenConnectionDenyCode)
|
|
{
|
|
if (connectionEnd->connectionState is AdspOpen)
|
|
{
|
|
/* The other end can't change its mind... let connection age
|
|
away if the other guy is serious. */
|
|
|
|
ErrorLog("AdspPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspCantDeny, IMsgAdspCantDeny,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* If a passive open was denied, just get back to the passive open
|
|
state. */
|
|
|
|
if (connectionEnd->passiveOpen)
|
|
{
|
|
connectionEnd->seenRemoteOpenRequest = False;
|
|
CancelTimer(connectionEnd->openTimerId);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Otherwise, complete our Open request with the bad news. */
|
|
|
|
CancelTimer(connectionEnd->openTimerId);
|
|
openCompletionRoutine = connectionEnd->openCompletionRoutine;
|
|
userData = connectionEnd->openUserData;
|
|
|
|
RemoveConnectionEnd(connectionEnd);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*openCompletionRoutine)(ATadspConnectionDenied, userData, refNum,
|
|
(long)0, unknownAddress);
|
|
return((long)True);
|
|
}
|
|
|
|
/* If we're HalfOpen, and we have a request, accept all the interesting
|
|
fields from the header (assuming we have't already done that). */
|
|
|
|
if (not connectionEnd->seenRemoteOpenRequest and
|
|
(controlCode is AdspOpenConnectionReqCode or
|
|
controlCode is AdspOpenConnectionReqAndAckCode))
|
|
{
|
|
connectionEnd->seenRemoteOpenRequest = True;
|
|
|
|
connectionEnd->remoteConnectionId = remoteConnectionId;
|
|
connectionEnd->remoteAddress = source;
|
|
|
|
connectionEnd->sendSeqNum = remoteNextReceiveSeqNum;
|
|
connectionEnd->retransmitSeqNum = remoteNextReceiveSeqNum;
|
|
connectionEnd->sendWindowSeqNum =
|
|
remoteNextReceiveSeqNum +
|
|
(long unsigned)remoteReceiveWindowSize -
|
|
(long unsigned)1;
|
|
|
|
connectionEnd->receiveAttentionSeqNum = receiveAttentionSeqNum;
|
|
|
|
/* If we're a passive open, start the open timer now. */
|
|
|
|
if (connectionEnd->passiveOpen)
|
|
connectionEnd->openTimerId = StartTimer(OpenTimerExpired,
|
|
AdspOpenIntervalSeconds,
|
|
sizeof(connectionEnd->
|
|
refNum),
|
|
(char *)&connectionEnd->
|
|
refNum);
|
|
}
|
|
|
|
/* If we're any flavour of Ack, and we're not open yet, we can make
|
|
the connection that way now. */
|
|
|
|
if (connectionEnd->connectionState is AdspHalfOpen and
|
|
(controlCode is AdspOpenConnectionAckCode or
|
|
controlCode is AdspOpenConnectionReqAndAckCode))
|
|
{
|
|
connectionEnd->connectionState = AdspOpen;
|
|
CancelTimer(connectionEnd->openTimerId);
|
|
connectionEnd->lastContactTime = CurrentRelativeTime();
|
|
connectionEnd->probeTimerId = StartTimer(ProbeTimerExpired,
|
|
ProbeTimerIntervalSeconds,
|
|
sizeof(refNum),
|
|
(char *)&refNum);
|
|
connectionEnd->retransmitTimerId =
|
|
StartTimer(RetransmitTimerExpired,
|
|
RetransmitTimerIntervalSeconds,
|
|
sizeof(refNum),
|
|
(char *)&refNum);
|
|
|
|
/* We're now open, add the connection end to the remote info lookup
|
|
table. */
|
|
|
|
CheckMod(index, connectionEnd->remoteConnectionId,
|
|
NumberOfConnectionEndHashBkts, "AdspPacketIn");
|
|
connectionEnd->nextByRemoteInfo = connectionEndRemoteHashBuckets[index];
|
|
connectionEndRemoteHashBuckets[index] = connectionEnd;
|
|
|
|
/* Is the other end still looking for an Ack? */
|
|
|
|
if (controlCode is AdspOpenConnectionReqAndAckCode)
|
|
SendOpenControl(connectionEnd);
|
|
|
|
/* We're set, call the completion routine with the good news. */
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*connectionEnd->openCompletionRoutine)(ATnoError,
|
|
connectionEnd->openUserData,
|
|
connectionEnd->refNum,
|
|
destinationSocket,
|
|
source);
|
|
return((long)True);
|
|
}
|
|
|
|
/* Okay, we're basically set, if the other end is looking for an Ack,
|
|
give it to him, else we just got a stale Ack from the other side,
|
|
which we can ignore. */
|
|
|
|
if (controlCode is AdspOpenConnectionReqCode or
|
|
controlCode is AdspOpenConnectionReqAndAckCode)
|
|
SendOpenControl(connectionEnd);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
|
|
} /* Open control packet handling */
|
|
|
|
/* Find our connection end. */
|
|
|
|
if ((connectionEnd = FindConnectionEndByRemoteInfo(source,
|
|
remoteConnectionId)) is
|
|
empty)
|
|
{
|
|
#if VerboseMessages
|
|
ErrorLog("AdspPacketIn", ISevVerbose, __LINE__, port,
|
|
IErrAdspDeadDest, IMsgAdspDeadDest,
|
|
Insert0());
|
|
#endif
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
connectionEnd->lastContactTime = CurrentRelativeTime();
|
|
|
|
/* Handle Attention packets. */
|
|
|
|
if (descriptor & AdspAttentionFlag)
|
|
{
|
|
long unsigned remoteAttentionSendSeq = remoteFirstByteSeqNum;
|
|
long unsigned remoteAttentionReceiveSeq = remoteNextReceiveSeqNum;
|
|
short unsigned attentionCode;
|
|
int attentionDataSize;
|
|
char far *attentionOpaqueBuffer;
|
|
|
|
if (controlCode isnt 0)
|
|
{
|
|
ErrorLog("AdspPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspFunnyValue, IMsgAdspFunnyValue,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Validate sequence numbers. */
|
|
|
|
if (remoteAttentionReceiveSeq is connectionEnd->sendAttentionSeqNum + 1)
|
|
connectionEnd->sendAttentionSeqNum += 1; /* Ack of our last Attn */
|
|
if (remoteAttentionSendSeq isnt connectionEnd->receiveAttentionSeqNum)
|
|
{
|
|
#if VerboseMessages
|
|
ErrorLog("AdspPacketIn", ISevVerbose, __LINE__, port,
|
|
IErrAdspAttnOutOfSeq, IMsgAdspAttnOutOfSeq,
|
|
Insert0());
|
|
#endif
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* If we're waiting for an attention acknowledgement, any attention
|
|
packet will do. */
|
|
|
|
if (connectionEnd->waitingForAttentionAck)
|
|
{
|
|
/* Save what we need to call completion routine when packets get
|
|
undeferred. */
|
|
|
|
outgoingAttentionAckHandler =
|
|
connectionEnd->outgoingAttentionAckHandler;
|
|
outgoingAttentionAckUserData =
|
|
connectionEnd->outgoingAttentionAckUserData;
|
|
refNum = connectionEnd->refNum;
|
|
|
|
CancelTimer(connectionEnd->outgoingAttentionTimerId);
|
|
connectionEnd->waitingForAttentionAck = False;
|
|
if (connectionEnd->outgoingAttentionBuffer isnt empty)
|
|
Free(connectionEnd->outgoingAttentionBuffer);
|
|
}
|
|
|
|
/* If the packet is a "Control", then all it can be is an Ack, so
|
|
no data to handle. */
|
|
|
|
if (descriptor & AdspControlFlag)
|
|
{
|
|
#if 0
|
|
connectionEnd->receiveAttentionSeqNum += 1; /* "accept" Ack */
|
|
#endif
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (outgoingAttentionAckHandler isnt empty)
|
|
(*outgoingAttentionAckHandler)(ATnoError,
|
|
outgoingAttentionAckUserData,
|
|
connectionEnd->refNum);
|
|
return((long)True);
|
|
}
|
|
|
|
/* If we don't have a handler or if there's no attention code, don't
|
|
accept the attention. Note that we only try to use the event
|
|
handler if we don't have an attention handler and there is no
|
|
GetAnythingPending and we're not already doing an attention
|
|
event. */
|
|
|
|
EnterCriticalSection();
|
|
if (connectionEnd->incomingAttentionHandler is Empty and
|
|
not connectionEnd->getAnythingPending)
|
|
{
|
|
receiveAttentionEventHandler =
|
|
connectionEnd->receiveAttentionEventHandler;
|
|
receiveAttentionEventContext =
|
|
connectionEnd->receiveAttentionEventContext;
|
|
}
|
|
if ((connectionEnd->incomingAttentionHandler is Empty and
|
|
not connectionEnd->getAnythingPending and
|
|
receiveAttentionEventHandler is Empty) or
|
|
connectionEnd->attentionEventInProgress or
|
|
datagramLength < AdspAttentionDataOffset or
|
|
(connectionEnd->getAnythingPending and
|
|
connectionEnd->getAnythingBufferSize <
|
|
datagramLength - AdspAttentionCodeOffset))
|
|
{
|
|
LeaveCriticalSection();
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (outgoingAttentionAckHandler isnt empty)
|
|
(*outgoingAttentionAckHandler)(ATnoError,
|
|
outgoingAttentionAckUserData,
|
|
connectionEnd->refNum);
|
|
return((long)True);
|
|
}
|
|
if (receiveAttentionEventHandler isnt Empty)
|
|
connectionEnd->attentionEventInProgress = True;
|
|
LeaveCriticalSection();
|
|
|
|
/* Okay, the following is a little tricky and it's after 11:00pm on
|
|
a Sunday night and I'm not sure I should be thinking this hard.
|
|
Anyhow, if we're using an attention handler we only want to "ack"
|
|
the attention if it is accepted by the handler -- that is only if
|
|
the handler returns that it accepted some data or if when we get
|
|
back and there is a posted GetAttention or GetAnything. In the latter
|
|
case we'll go ahead and supply THIS attention message to the newly
|
|
posted handler (or GetAnything). If the event handler accepted
|
|
any bytes, we don't supply this attention to a handler that may
|
|
have been posted by the attention handler. */
|
|
|
|
if (receiveAttentionEventHandler isnt Empty)
|
|
{
|
|
/* Note for indications, the attention code is just the first two
|
|
bytes of the attention buffer. */
|
|
|
|
bytesAccepted = (*receiveAttentionEventHandler)
|
|
(connectionEnd->refNum,
|
|
receiveAttentionEventContext,
|
|
datagram + AdspAttentionCodeOffset,
|
|
datagramLength - AdspAttentionCodeOffset,
|
|
datagramLength - AdspAttentionCodeOffset);
|
|
if (bytesAccepted is 0 and
|
|
connectionEnd->incomingAttentionHandler is Empty and
|
|
(not connectionEnd->getAnythingPending or
|
|
(connectionEnd->getAnythingPending and
|
|
connectionEnd->getAnythingBufferSize < datagramLength -
|
|
AdspAttentionCodeOffset)))
|
|
{
|
|
/* No apparent interest, don't accept the attention. */
|
|
|
|
connectionEnd->attentionEventInProgress = False;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (outgoingAttentionAckHandler isnt empty)
|
|
(*outgoingAttentionAckHandler)(ATnoError,
|
|
outgoingAttentionAckUserData,
|
|
connectionEnd->refNum);
|
|
return((long)True);
|
|
|
|
}
|
|
}
|
|
|
|
/* Okay, Ack and accept the attention. */
|
|
|
|
if ((packet = NewBufferDescriptor(AdspDataOffset)) is Empty)
|
|
{
|
|
ErrorLog("AdspPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
connectionEnd->attentionEventInProgress = False;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (outgoingAttentionAckHandler isnt empty)
|
|
(*outgoingAttentionAckHandler)(ATnoError,
|
|
outgoingAttentionAckUserData,
|
|
connectionEnd->refNum);
|
|
return((long)True);
|
|
}
|
|
connectionEnd->receiveAttentionSeqNum += 1;
|
|
BuildAdspHeader(connectionEnd, packet->data, AdspControlFlag +
|
|
AdspAttentionFlag);
|
|
if (DeliverDdp(connectionEnd->socket,
|
|
connectionEnd->remoteAddress,
|
|
DdpProtocolAdsp,
|
|
packet,
|
|
AdspDataOffset,
|
|
Empty, Empty, 0) isnt ATnoError)
|
|
{
|
|
connectionEnd->receiveAttentionSeqNum -= 1; /* Unaccept it. */
|
|
ErrorLog("AdspPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspBadAckSend, IMsgAdspBadAckSend,
|
|
Insert0());
|
|
connectionEnd->attentionEventInProgress = False;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (outgoingAttentionAckHandler isnt empty)
|
|
(*outgoingAttentionAckHandler)(ATnoError,
|
|
outgoingAttentionAckUserData,
|
|
connectionEnd->refNum);
|
|
return((long)True);
|
|
}
|
|
|
|
/* If the attention was already accepted by an event handler, we're
|
|
finished now. */
|
|
|
|
if (bytesAccepted isnt 0)
|
|
{
|
|
connectionEnd->attentionEventInProgress = False;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (outgoingAttentionAckHandler isnt empty)
|
|
(*outgoingAttentionAckHandler)(ATnoError,
|
|
outgoingAttentionAckUserData,
|
|
connectionEnd->refNum);
|
|
return((long)True);
|
|
}
|
|
|
|
/* Move the data into user space, call the user's handler and we're
|
|
set. */
|
|
|
|
attentionDataSize = datagramLength - AdspAttentionDataOffset;
|
|
if (connectionEnd->getAnythingPending)
|
|
{
|
|
/* For a GetAnything, the attention code is just the first two
|
|
bytes of the data. */
|
|
|
|
attentionOpaqueBuffer = connectionEnd->getAnythingOpaqueBuffer;
|
|
MoveToOpaque(attentionOpaqueBuffer, 0,
|
|
datagram + AdspAttentionCodeOffset,
|
|
sizeof(short unsigned));
|
|
MoveToOpaque(attentionOpaqueBuffer, sizeof(short unsigned),
|
|
datagram + AdspAttentionDataOffset,
|
|
attentionDataSize);
|
|
attentionDataSize += sizeof(short unsigned);
|
|
getAnythingRoutine = connectionEnd->getAnythingCompletionRoutine;
|
|
userData = connectionEnd->getAnythingUserData;
|
|
connectionEnd->getAnythingPending = False;
|
|
}
|
|
else
|
|
{
|
|
attentionOpaqueBuffer = connectionEnd->incomingAttentionOpaqueBuffer;
|
|
MoveShortWireToMachine(datagram + AdspAttentionCodeOffset,
|
|
attentionCode);
|
|
MoveToOpaque(attentionOpaqueBuffer, 0,
|
|
datagram + AdspAttentionDataOffset,
|
|
attentionDataSize);
|
|
incomingAttentionHandler = connectionEnd->incomingAttentionHandler;
|
|
userData = connectionEnd->incomingAttentionUserData;
|
|
connectionEnd->incomingAttentionHandler = Empty; /* Been used now. */
|
|
}
|
|
refNum = connectionEnd->refNum;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (outgoingAttentionAckHandler isnt empty)
|
|
(*outgoingAttentionAckHandler)(ATnoError,
|
|
outgoingAttentionAckUserData,
|
|
connectionEnd->refNum);
|
|
if (getAnythingRoutine isnt Empty)
|
|
(*getAnythingRoutine)(ATnoError, userData, refNum, True,
|
|
attentionOpaqueBuffer,
|
|
attentionDataSize, False);
|
|
else
|
|
(*incomingAttentionHandler)(ATnoError, userData, refNum,
|
|
attentionCode, attentionOpaqueBuffer,
|
|
attentionDataSize);
|
|
return((long)True);
|
|
|
|
} /* Handle Attention packets */
|
|
|
|
/* Can we use "remoteNextReceiveSeqNum" to "Ack" any pending data? */
|
|
|
|
if (UnsignedBetweenWithWrap(connectionEnd->retransmitSeqNum,
|
|
connectionEnd->sendSeqNum,
|
|
remoteNextReceiveSeqNum))
|
|
{
|
|
long sendSize;
|
|
|
|
#if VerboseMessages
|
|
if (remoteNextReceiveSeqNum isnt connectionEnd->retransmitSeqNum)
|
|
printf("AsdpPacketIn: have ack for %u to %u for refNum %d.\n",
|
|
connectionEnd->retransmitSeqNum,
|
|
remoteNextReceiveSeqNum - (long unsigned)1,
|
|
connectionEnd->refNum);
|
|
#endif
|
|
if (not DiscardFromBufferQueue(&connectionEnd->sendQueue,
|
|
(long)(remoteNextReceiveSeqNum -
|
|
connectionEnd->retransmitSeqNum),
|
|
&connectionEnd->nextSendQueue))
|
|
ErrorLog("AdspPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
connectionEnd->retransmitSeqNum = remoteNextReceiveSeqNum;
|
|
|
|
/* If the send window opened up some and it was zero before indicate the
|
|
"sendOkay" event. */
|
|
|
|
if ((sendSize = MaxSendSize(connectionEnd)) isnt 0 and
|
|
connectionEnd->sendWindowHasClosed and
|
|
connectionEnd->sendOkayEventHandler isnt Empty)
|
|
{
|
|
connectionEnd->sendWindowHasClosed = False;
|
|
(*connectionEnd->sendOkayEventHandler)(connectionEnd->refNum,
|
|
connectionEnd->
|
|
sendOkayEventContext,
|
|
sendSize);
|
|
}
|
|
}
|
|
|
|
/* We almost always can use the header values to update sendWindowSeqNum. */
|
|
|
|
newSendWindowSeqNum = remoteNextReceiveSeqNum +
|
|
(long unsigned)remoteReceiveWindowSize -
|
|
(long unsigned)1;
|
|
if (UnsignedGreaterWithWrap(newSendWindowSeqNum,
|
|
connectionEnd->sendWindowSeqNum))
|
|
connectionEnd->sendWindowSeqNum = newSendWindowSeqNum;
|
|
else
|
|
if (connectionEnd->sendWindowSeqNum isnt newSendWindowSeqNum)
|
|
#if VerboseMessages
|
|
ErrorLog("AdspPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspShrinkingWindow, IMsgAdspShrinkingWindow,
|
|
Insert0());
|
|
#else
|
|
;
|
|
#endif
|
|
|
|
/* Don't handle "ack" requests now... do it after we've processed any
|
|
data in the incoming packet, so we can ack that too. */
|
|
|
|
/* Handle the remainder of the Control packets (we've already taken care
|
|
of the various Opens). */
|
|
|
|
if (descriptor & AdspControlFlag)
|
|
{
|
|
/* Ack if requested, send any data too. */
|
|
|
|
if (descriptor & AdspAckRequestFlag)
|
|
SendData(connectionEnd);
|
|
|
|
switch(controlCode)
|
|
{
|
|
case AdspProbeOrAckCode:
|
|
|
|
/* If it was a "probe" it would have had its AckRequest flag set,
|
|
so we've handled it above; if it was an "ack", we've already set
|
|
lastContactTime -- so, we're set. Otherwise, maybe some data was
|
|
really acked, and we can send some more data. */
|
|
|
|
if (not (descriptor & AdspAckRequestFlag) and
|
|
BufferQueueSize(&connectionEnd->nextSendQueue) isnt 0 and
|
|
connectionEnd->sendSeqNum isnt
|
|
connectionEnd->sendWindowSeqNum + 1)
|
|
SendData(connectionEnd);
|
|
break;
|
|
|
|
case AdspCloseConnectionCode:
|
|
AdspCloseConnection(connectionEnd->refNum, True);
|
|
break;
|
|
|
|
case AdspForwardResetCode:
|
|
|
|
/* Is the forward reset within range? */
|
|
|
|
if (UnsignedBetweenWithWrap(connectionEnd->receiveSeqNum,
|
|
connectionEnd->receiveSeqNum +
|
|
(long unsigned)
|
|
(connectionEnd->receiveWindowSize),
|
|
remoteFirstByteSeqNum))
|
|
{
|
|
/* Yes, do the reset. */
|
|
|
|
connectionEnd->receiveSeqNum = remoteFirstByteSeqNum;
|
|
connectionEnd->receiveWindowSize = connectionEnd->receiveQueueMax;
|
|
FreeBufferQueue(&connectionEnd->receiveQueue);
|
|
connectionEnd->incomingForwardReset = True;
|
|
}
|
|
|
|
/* Ack regardless of whether we accepted the reset... */
|
|
|
|
if ((packet = NewBufferDescriptor(AdspDataOffset)) is Empty)
|
|
{
|
|
ErrorLog("AdspPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
BuildAdspHeader(connectionEnd, packet->data, AdspControlFlag +
|
|
AdspForwardResetAckCode);
|
|
if (DeliverDdp(connectionEnd->socket,
|
|
connectionEnd->remoteAddress,
|
|
DdpProtocolAdsp,
|
|
packet,
|
|
AdspDataOffset,
|
|
Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("AdspPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspBadFrwdResetAckSend, IMsgAdspBadFrwdResetAckSend,
|
|
Insert0());
|
|
|
|
/* Complete a read if we can... ReadData will undefer. */
|
|
|
|
ReadData(connectionEnd, False, Empty, Empty);
|
|
return((long)True);
|
|
|
|
case AdspForwardResetAckCode:
|
|
|
|
/* Do we want an Ack and is it a "valid" Ack? */
|
|
|
|
if (not connectionEnd->outgoingForwardReset)
|
|
break;
|
|
if (not UnsignedBetweenWithWrap(connectionEnd->sendSeqNum,
|
|
connectionEnd->sendWindowSeqNum +
|
|
(long unsigned)1,
|
|
remoteNextReceiveSeqNum))
|
|
break;
|
|
|
|
/* Okay, note the Ack -- if no completion routine, we're
|
|
finished. */
|
|
|
|
connectionEnd->outgoingForwardReset = False;
|
|
CancelTimer(connectionEnd->forwardResetTimerId);
|
|
if (connectionEnd->forwardResetAckHandler is empty)
|
|
break;
|
|
|
|
/* Let the user know that the forward reset took. */
|
|
|
|
userData = connectionEnd->forwardResetAckUserData;
|
|
forwardResetAckHandler = connectionEnd->forwardResetAckHandler;
|
|
refNum = connectionEnd->refNum;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*forwardResetAckHandler)(ATnoError, userData, refNum);
|
|
return((long)True);
|
|
|
|
case AdspRetransmitCode:
|
|
|
|
/* We've modified retransmitSeqNum to match remoteNextReceiveSeqNum
|
|
if the remote side acked any data, now if remoteNextReceiveSeqNum
|
|
is within range we should back up retransmitSeqNum and the
|
|
nextSendQueue to the start of the retransmit queue and try again
|
|
to send the data. */
|
|
|
|
if (UnsignedBetweenWithWrap(connectionEnd->retransmitSeqNum,
|
|
connectionEnd->sendSeqNum,
|
|
remoteNextReceiveSeqNum))
|
|
{
|
|
connectionEnd->nextSendQueue = connectionEnd->sendQueue;
|
|
connectionEnd->sendSeqNum = connectionEnd->retransmitSeqNum;
|
|
SendData(connectionEnd);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ErrorLog("AdspPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAdspFunnyValue, IMsgAdspFunnyValue,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* All set. */
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
|
|
} /* The rest of the Control codes */
|
|
|
|
/* Okay, lastly, we might have some data! Check it out. */
|
|
|
|
if (connectionEnd->receiveSeqNum isnt remoteFirstByteSeqNum)
|
|
{
|
|
#if VerboseMessages
|
|
printf("Rejecting an out of seq packet for refNum %d.\n",
|
|
connectionEnd->refNum);
|
|
#endif
|
|
if ((connectionEnd->outOfSequencePackets += 1) >= OutOfSequencePacketsMax)
|
|
{
|
|
#if VerboseMessages
|
|
printf("Sending retransmit advice control packet for refNum %d.\n",
|
|
connectionEnd->refNum);
|
|
#endif
|
|
if ((packet = NewBufferDescriptor(AdspDataOffset)) is Empty)
|
|
{
|
|
ErrorLog("AdspPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
BuildAdspHeader(connectionEnd, packet->data, AdspControlFlag +
|
|
AdspRetransmitCode);
|
|
if (DeliverDdp(connectionEnd->socket,
|
|
connectionEnd->remoteAddress,
|
|
DdpProtocolAdsp,
|
|
packet,
|
|
AdspDataOffset,
|
|
Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("AdspPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspBadRetranSend, IMsgAdspBadRetranSend,
|
|
Insert0());
|
|
connectionEnd->outOfSequencePackets = 0;
|
|
}
|
|
|
|
/* Ack if requested, send any data too. */
|
|
|
|
if (descriptor & AdspAckRequestFlag)
|
|
SendData(connectionEnd);
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
connectionEnd->outOfSequencePackets = 0;
|
|
endOfMessage = ((descriptor & AdspEndOfMessageFlag) isnt 0);
|
|
dataSize = datagramLength - AdspDataOffset;
|
|
if (dataSize + endOfMessage is 0)
|
|
{
|
|
#if VerboseMessages
|
|
printf("Rejecting a no-data packet for refNum %d.\n",
|
|
connectionEnd->refNum);
|
|
#endif
|
|
|
|
/* Ack if requested, send any data too. */
|
|
|
|
if (descriptor & AdspAckRequestFlag)
|
|
SendData(connectionEnd);
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
if (dataSize + endOfMessage > connectionEnd->receiveWindowSize)
|
|
{
|
|
#if VerboseMessages
|
|
printf("Rejecting a packet due to too much data for refNum %d.\n",
|
|
connectionEnd->refNum);
|
|
#endif
|
|
|
|
/* Ack if requested, send any data too. */
|
|
|
|
if (descriptor & AdspAckRequestFlag)
|
|
SendData(connectionEnd);
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Accept the data. */
|
|
|
|
if (not AddToBufferQueue(&connectionEnd->receiveQueue,
|
|
datagram, AdspDataOffset, dataSize, False,
|
|
endOfMessage, empty))
|
|
ErrorLog("AdspPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
#if VerboseMessages
|
|
printf("Accepting %u to %u for refNum %d.\n",
|
|
connectionEnd->receiveSeqNum,
|
|
connectionEnd->receiveSeqNum + (long unsigned)dataSize +
|
|
(long unsigned)endOfMessage - (long unsigned)1,
|
|
connectionEnd->refNum);
|
|
#endif
|
|
connectionEnd->receiveSeqNum += (long unsigned)(dataSize + endOfMessage);
|
|
connectionEnd->receiveWindowSize -= (dataSize + endOfMessage);
|
|
if (connectionEnd->receiveWindowSize < 0)
|
|
{
|
|
ErrorLog("AdspPacketIn", ISevError, __LINE__, port,
|
|
IErrAdspBadWindowSize, IMsgAdspBadWindowSize,
|
|
Insert0());
|
|
connectionEnd->receiveWindowSize = 0;
|
|
}
|
|
|
|
/* Ack if requested, send any data too. */
|
|
|
|
if (descriptor & AdspAckRequestFlag)
|
|
SendData(connectionEnd);
|
|
|
|
/* Lastly, try to handle any outstanding AdspRead().
|
|
|
|
** ReadData() will undefer **
|
|
*/
|
|
|
|
ReadData(connectionEnd, False, Empty, Empty);
|
|
return((long)True);
|
|
|
|
} /* AdspPacketIn */
|
|
|
|
/* @Add new static routines here@ */
|
|
|
|
ExternForVisibleFunction ConnectionListenerInfo
|
|
FindConnectionListenerByRefNum(long refNum)
|
|
{
|
|
ConnectionListenerInfo connectionListenerInfo;
|
|
|
|
/* Walk the list and find the guy. */
|
|
|
|
for (connectionListenerInfo = connectionListenerList;
|
|
connectionListenerInfo isnt empty;
|
|
connectionListenerInfo = connectionListenerInfo->next)
|
|
if (connectionListenerInfo->connectionListenerRefNum is refNum)
|
|
break;
|
|
|
|
return(connectionListenerInfo);
|
|
|
|
} /* FindConnectionListenerByRefNum */
|
|
|
|
ExternForVisibleFunction Boolean UnsignedGreaterWithWrap(long unsigned high,
|
|
long unsigned low)
|
|
{
|
|
/* Do we have a presumed wrap? */
|
|
|
|
if (high < 0x80000 and low > 0x10000)
|
|
return(True);
|
|
else
|
|
return(high > low);
|
|
|
|
} /* UnsignedGreatWithWrap */
|
|
|
|
ExternForVisibleFunction Boolean UnsignedBetweenWithWrap(long unsigned low,
|
|
long unsigned high,
|
|
long unsigned target)
|
|
{
|
|
|
|
if (low <= high)
|
|
return(target >= low and target <= high);
|
|
|
|
/* Otherwise, assume an unsigned wrap at zero lies between high and low. */
|
|
|
|
return(target >= low or target <= high);
|
|
|
|
} /* UnsignedBetweenWithWrap */
|
|
|
|
ExternForVisibleFunction long MaxSendSize(ConnectionEnd connectionEnd)
|
|
{
|
|
long sendSize;
|
|
|
|
/* The answer is the remaining available (to fill) space in the retransmit
|
|
queue -- this includes data we're saving for possible retransmit as well
|
|
as data we haven't sent yet. Actually, this could go negative because
|
|
BufferQueueSize counts EOMs and sendQueueMax doesn't -- answer with zero
|
|
if this happens. */
|
|
|
|
sendSize = connectionEnd->sendQueueMax -
|
|
BufferQueueSize(&connectionEnd->sendQueue);
|
|
if (sendSize < 0)
|
|
sendSize = (long)0;
|
|
|
|
return(sendSize);
|
|
|
|
} /* MaxSendSize */
|
|
|
|
ExternForVisibleFunction void
|
|
DecodeAdspHeader(char far *datagram,
|
|
short unsigned far *connectionId,
|
|
long unsigned far *firstByteSeqNum,
|
|
long unsigned far *nextReceiveSeqNum,
|
|
long far *receiveWindowSize,
|
|
int far *descriptor)
|
|
{
|
|
short unsigned windowSizeTemp;
|
|
|
|
MoveShortWireToMachine(datagram + AdspSourceConnectionIdOffset,
|
|
*connectionId);
|
|
MoveLongWireToMachine(datagram + AdspFirstByteSeqNumOffset,
|
|
*firstByteSeqNum);
|
|
MoveLongWireToMachine(datagram + AdspNextReceiveByteSeqNumOffset,
|
|
*nextReceiveSeqNum);
|
|
MoveShortWireToMachine(datagram + AdspReceiveWindowSizeOffset,
|
|
windowSizeTemp);
|
|
*receiveWindowSize = windowSizeTemp;
|
|
*descriptor = (unsigned char)datagram[AdspDescriptorOffset];
|
|
return;
|
|
|
|
} /* DecodeAdspHeader */
|
|
|
|
ExternForVisibleFunction void far
|
|
ForwardResetTimerExpired(long unsigned timerId,
|
|
int dataSize,
|
|
char far *additionalData)
|
|
{
|
|
long refNum;
|
|
ConnectionEnd connectionEnd;
|
|
BufferDescriptor datagram;
|
|
|
|
/* Validate and verify our additional data. */
|
|
|
|
if (dataSize isnt sizeof(refNum))
|
|
{
|
|
ErrorLog("ForwardResetTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadData, IMsgAdspBadData,
|
|
Insert0());
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
DeferAdspPackets();
|
|
refNum = *(long *)additionalData;
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
ErrorLog("RetransmitTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspConnectionLost, IMsgAdspConnectionLost,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
if (not connectionEnd->outgoingForwardReset)
|
|
{
|
|
ErrorLog("RetransmitTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspResetNotPending, IMsgAdspResetNotPending,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Build the Forward reset packet and send it on its way. */
|
|
|
|
if ((datagram = NewBufferDescriptor(AdspDataOffset)) is Empty)
|
|
{
|
|
ErrorLog("RetransmitTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
BuildAdspHeader(connectionEnd, datagram->data, AdspControlFlag +
|
|
AdspForwardResetCode);
|
|
if (DeliverDdp(connectionEnd->socket,
|
|
connectionEnd->remoteAddress,
|
|
DdpProtocolAdsp,
|
|
datagram,
|
|
AdspDataOffset,
|
|
Empty, Empty, 0) isnt ATnoError)
|
|
{
|
|
ErrorLog("ForwardResetTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadFrwdResetSend, IMsgAdspBadFrwdResetSend,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Restart the retry timer. */
|
|
|
|
connectionEnd->forwardResetTimerId =
|
|
StartTimer(ForwardResetTimerExpired,
|
|
ForwardResetTimerIntenvalSecs,
|
|
sizeof(connectionEnd->refNum),
|
|
(char *)&connectionEnd->refNum);
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* ForwardResetTimerExpired */
|
|
|
|
ExternForVisibleFunction void far
|
|
RetransmitTimerExpired(long unsigned timerId,
|
|
int dataSize,
|
|
char far *additionalData)
|
|
{
|
|
long refNum;
|
|
ConnectionEnd connectionEnd;
|
|
BufferDescriptor datagram;
|
|
|
|
/* Validate and verify our additional data. */
|
|
|
|
if (dataSize isnt sizeof(refNum))
|
|
{
|
|
ErrorLog("RetransmitTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadData, IMsgAdspBadData,
|
|
Insert0());
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
DeferAdspPackets();
|
|
refNum = *(long *)additionalData;
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
ErrorLog("RetransmitTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspConnectionLost, IMsgAdspConnectionLost,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* We only have work to do if the remote side has not accepted any data
|
|
since last time we were here AND we have previously sent but still
|
|
un-acked data pending. */
|
|
|
|
if (connectionEnd->retransmitSeqNum is
|
|
connectionEnd->lastRetransmitSeqNum and
|
|
BufferQueueSize(&connectionEnd->sendQueue) isnt
|
|
BufferQueueSize(&connectionEnd->nextSendQueue))
|
|
{
|
|
/* Okay, we may have a problem, on the first time, just request an Ack --
|
|
maybe the data was received but just not acked. */
|
|
|
|
if (not connectionEnd->retransmitAckRequestSent)
|
|
{
|
|
#if VerboseMessages
|
|
printf("RetransmitTimerExpired: requesting Ack for refNum %d.\n",
|
|
connectionEnd->refNum);
|
|
#endif
|
|
if ((datagram = NewBufferDescriptor(AdspDataOffset)) is Empty)
|
|
ErrorLog("RetransmitTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
else
|
|
{
|
|
BuildAdspHeader(connectionEnd, datagram->data, AdspControlFlag +
|
|
AdspAckRequestFlag + AdspProbeOrAckCode);
|
|
if (DeliverDdp(connectionEnd->socket,
|
|
connectionEnd->remoteAddress,
|
|
DdpProtocolAdsp,
|
|
datagram,
|
|
AdspDataOffset,
|
|
Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("RetransmitTimerExpired", ISevError, __LINE__,
|
|
UnknownPort, IErrAdspBadProbeSend, IMsgAdspBadProbeSend,
|
|
Insert0());
|
|
connectionEnd->retransmitAckRequestSent = True;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, rewind sendSeqNum and try to resend. */
|
|
|
|
#if VerboseMessages
|
|
printf("RetransmitTimerExpired: rewinding send queue for "
|
|
"refNum %d.\n", connectionEnd->refNum);
|
|
#endif
|
|
connectionEnd->sendSeqNum = connectionEnd->retransmitSeqNum;
|
|
connectionEnd->nextSendQueue = connectionEnd->sendQueue;
|
|
SendData(connectionEnd);
|
|
connectionEnd->retransmitAckRequestSent = False;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Tag that all's well. */
|
|
|
|
connectionEnd->lastRetransmitSeqNum = connectionEnd->retransmitSeqNum;
|
|
connectionEnd->retransmitAckRequestSent = False;
|
|
}
|
|
|
|
/* Restart the retransmit timer. */
|
|
|
|
connectionEnd->retransmitTimerId = StartTimer(RetransmitTimerExpired,
|
|
RetransmitTimerIntervalSeconds,
|
|
sizeof(refNum),
|
|
(char *)&refNum);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* RetransmitTimerExpired */
|
|
|
|
ExternForVisibleFunction void far ProbeTimerExpired(long unsigned timerId,
|
|
int dataSize,
|
|
char far *additionalData)
|
|
{
|
|
long refNum;
|
|
ConnectionEnd connectionEnd;
|
|
long unsigned now = CurrentRelativeTime();
|
|
BufferDescriptor datagram;
|
|
|
|
/* Validate and verify our additional data. */
|
|
|
|
if (dataSize isnt sizeof(refNum))
|
|
{
|
|
ErrorLog("ProbeTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadData, IMsgAdspBadData,
|
|
Insert0());
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
DeferAdspPackets();
|
|
refNum = *(long *)additionalData;
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
ErrorLog("ProbeTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspConnectionLost, IMsgAdspConnectionLost,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Has the connection died? */
|
|
|
|
if (connectionEnd->lastContactTime + ConnectionDeadSeconds <= now)
|
|
{
|
|
#if VerboseMessages
|
|
printf("Removing dead connection RefNum = %d.\n",
|
|
connectionEnd->refNum);
|
|
#endif
|
|
AdspCloseConnection(refNum, True);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* If we haven't heard from the other side recently, send out a probe. */
|
|
|
|
if (connectionEnd->lastContactTime + ProbeTimerIntervalSeconds <= now)
|
|
{
|
|
#if VerboseMessages
|
|
printf("Sending probe for RefNum = %d to %d.%d.%d.\n",
|
|
connectionEnd->refNum,
|
|
connectionEnd->remoteAddress.networkNumber,
|
|
connectionEnd->remoteAddress.nodeNumber,
|
|
connectionEnd->remoteAddress.socketNumber);
|
|
#endif
|
|
if ((datagram = NewBufferDescriptor(AdspDataOffset)) is Empty)
|
|
ErrorLog("ProbeTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
else
|
|
{
|
|
BuildAdspHeader(connectionEnd, datagram->data, AdspControlFlag +
|
|
AdspAckRequestFlag + AdspProbeOrAckCode);
|
|
if (DeliverDdp(connectionEnd->socket,
|
|
connectionEnd->remoteAddress,
|
|
DdpProtocolAdsp,
|
|
datagram,
|
|
AdspDataOffset,
|
|
Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("ProbeTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadProbeSend, IMsgAdspBadProbeSend,
|
|
Insert0());
|
|
}
|
|
}
|
|
|
|
/* Restart the probe timer. */
|
|
|
|
connectionEnd->probeTimerId = StartTimer(ProbeTimerExpired,
|
|
ProbeTimerIntervalSeconds,
|
|
sizeof(refNum),
|
|
(char *)&refNum);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* ProbeTimerExpired */
|
|
|
|
ExternForVisibleFunction void far
|
|
SendAttentionTimerExpired(long unsigned timerId,
|
|
int dataSize,
|
|
char far *additionalData)
|
|
{
|
|
long refNum;
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* Validate and verify our additional data. */
|
|
|
|
if (dataSize isnt sizeof(refNum))
|
|
{
|
|
ErrorLog("SendAttentionTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadData, IMsgAdspBadData,
|
|
Insert0());
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
DeferAdspPackets();
|
|
refNum = *(long *)additionalData;
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
ErrorLog("SendAttentionTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspConnectionLost, IMsgAdspConnectionLost,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Are we here for a good reason? */
|
|
|
|
if (not connectionEnd->waitingForAttentionAck)
|
|
{
|
|
ErrorLog("SendAttentionTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspTimerNotCanceled, IMsgAdspTimerNotCanceled,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Okay, send the attention and restart the timer. */
|
|
|
|
SendAttention(connectionEnd);
|
|
connectionEnd->outgoingAttentionTimerId =
|
|
StartTimer(SendAttentionTimerExpired,
|
|
AttentionTimerIntervalSeconds,
|
|
sizeof(connectionEnd->refNum),
|
|
(char far *)&connectionEnd->refNum);
|
|
|
|
/* All set! */
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* SendAttentionTimerExpired */
|
|
|
|
ExternForVisibleFunction void far OpenTimerExpired(long unsigned timerId,
|
|
int dataSize,
|
|
char far *additionalData)
|
|
{
|
|
long refNum;
|
|
ConnectionEnd connectionEnd;
|
|
AdspOpenCompleteHandler *completionRoutine;
|
|
long unsigned userData;
|
|
|
|
/* Validate and verify our additional data. */
|
|
|
|
if (dataSize isnt sizeof(refNum))
|
|
{
|
|
ErrorLog("OpenTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadData, IMsgAdspBadData,
|
|
Insert0());
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
DeferAdspPackets();
|
|
refNum = *(long *)additionalData;
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
ErrorLog("OpenTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspConnectionLost, IMsgAdspConnectionLost,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Did we get fully open? */
|
|
|
|
if (connectionEnd->connectionState is AdspOpen)
|
|
{
|
|
ErrorLog("OpenTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspTimerNotCanceled, IMsgAdspTimerNotCanceled,
|
|
Insert0());
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* If we're not out of attemps, try it again. */
|
|
|
|
connectionEnd->openAttemptsCount += 1;
|
|
if (connectionEnd->openAttemptsCount < AdspMaxOpenAttempts)
|
|
{
|
|
SendOpenControl(connectionEnd);
|
|
connectionEnd->openTimerId = StartTimer(OpenTimerExpired,
|
|
AdspOpenIntervalSeconds,
|
|
sizeof(connectionEnd->refNum),
|
|
(char *)&connectionEnd->refNum);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Okay, we're out of tries... if passive open just get back to that
|
|
state. */
|
|
|
|
if (connectionEnd->passiveOpen)
|
|
{
|
|
connectionEnd->seenRemoteOpenRequest = False;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Otherwise, remove the connection end, call the completion routine and
|
|
give it up. */
|
|
|
|
completionRoutine = connectionEnd->openCompletionRoutine;
|
|
userData = connectionEnd->openUserData;
|
|
RemoveConnectionEnd(connectionEnd);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATadspOpenFailed, userData, refNum, (long)0,
|
|
unknownAddress);
|
|
return;
|
|
|
|
} /* OpenTimerExpired */
|
|
|
|
ExternForVisibleFunction void SendOpenControl(ConnectionEnd connectionEnd)
|
|
{
|
|
BufferDescriptor datagram;
|
|
int descriptor;
|
|
|
|
/* Pick what kind of descriptor we want: */
|
|
|
|
descriptor = AdspControlFlag;
|
|
if (connectionEnd->connectionState is AdspClosed)
|
|
descriptor += AdspOpenConnectionDenyCode;
|
|
else if (connectionEnd->connectionState is AdspOpen)
|
|
descriptor += AdspOpenConnectionAckCode;
|
|
else if (connectionEnd->seenRemoteOpenRequest)
|
|
descriptor += AdspOpenConnectionReqAndAckCode;
|
|
else
|
|
descriptor += AdspOpenConnectionReqCode;
|
|
|
|
#if VerboseMessages
|
|
printf("Sending open control 0x%x for RefNum = %d to %d.%d.%d.\n",
|
|
descriptor, connectionEnd->refNum,
|
|
connectionEnd->remoteAddress.networkNumber,
|
|
connectionEnd->remoteAddress.nodeNumber,
|
|
connectionEnd->remoteAddress.socketNumber);
|
|
#endif
|
|
|
|
/* Build an OpenRequest datagram and send it on its way. */
|
|
|
|
if ((datagram = NewBufferDescriptor(AdspNextAttentionSeqNumOffset +
|
|
sizeof(long))) is Empty)
|
|
{
|
|
ErrorLog("SendOpenControl", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
return;
|
|
}
|
|
BuildAdspHeader(connectionEnd, datagram->data, descriptor);
|
|
MoveShortMachineToWire(datagram->data + AdspVersionStampOffset,
|
|
AdspVersionStamp);
|
|
MoveShortMachineToWire(datagram->data + AdspDestConnectionIdOffset,
|
|
connectionEnd->remoteConnectionId);
|
|
MoveLongMachineToWire(datagram->data + AdspNextAttentionSeqNumOffset,
|
|
connectionEnd->receiveAttentionSeqNum);
|
|
|
|
if (DeliverDdp(connectionEnd->socket,
|
|
connectionEnd->remoteAddress,
|
|
DdpProtocolAdsp,
|
|
datagram,
|
|
AdspNextAttentionSeqNumOffset + sizeof(long),
|
|
Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("SendOpenControl", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadOpenSend, IMsgAdspBadOpenSend,
|
|
Insert0());
|
|
return;
|
|
|
|
} /* SendOpenControl */
|
|
|
|
ExternForVisibleFunction void SendAttention(ConnectionEnd connectionEnd)
|
|
{
|
|
BufferDescriptor datagram;
|
|
|
|
#if VerboseMessages
|
|
printf("Sending attention (0x%X) for RefNum = %d to %d.%d.%d.\n",
|
|
connectionEnd->outgoingAttentionCode,
|
|
connectionEnd->refNum,
|
|
connectionEnd->remoteAddress.networkNumber,
|
|
connectionEnd->remoteAddress.nodeNumber,
|
|
connectionEnd->remoteAddress.socketNumber);
|
|
#endif
|
|
|
|
/* Build an Attention datagram and send it on its way. */
|
|
|
|
if ((datagram = NewBufferDescriptor(AdspAttentionDataOffset +
|
|
connectionEnd->outgoingAttentionBufferSize)) is Empty)
|
|
{
|
|
ErrorLog("SendAttention", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
return;
|
|
}
|
|
BuildAdspHeader(connectionEnd, datagram->data, AdspAttentionFlag +
|
|
AdspAckRequestFlag);
|
|
MoveShortMachineToWire(datagram->data + AdspAttentionCodeOffset,
|
|
connectionEnd->outgoingAttentionCode);
|
|
if (connectionEnd->outgoingAttentionBuffer isnt empty)
|
|
MoveMem(datagram->data + AdspAttentionDataOffset,
|
|
connectionEnd->outgoingAttentionBuffer,
|
|
connectionEnd->outgoingAttentionBufferSize);
|
|
|
|
if (DeliverDdp(connectionEnd->socket,
|
|
connectionEnd->remoteAddress,
|
|
DdpProtocolAdsp,
|
|
datagram,
|
|
AdspAttentionDataOffset +
|
|
connectionEnd->outgoingAttentionBufferSize,
|
|
Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("SendAttention", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadAttnSend, IMsgAdspBadAttnSend,
|
|
Insert0());
|
|
return;
|
|
|
|
} /* SendAttntion */
|
|
|
|
ExternForVisibleFunction void SendData(ConnectionEnd connectionEnd)
|
|
{
|
|
BufferDescriptor datagram;
|
|
long windowSize;
|
|
long dataSize, tempDataSize;
|
|
Boolean endOfMessage;
|
|
int descriptor;
|
|
|
|
/* If there is no data to send (or the remote can't handle any more data),
|
|
just send an Ack. */
|
|
|
|
dataSize = BufferQueueSize(&connectionEnd->nextSendQueue);
|
|
windowSize = (long)(connectionEnd->sendWindowSeqNum -
|
|
connectionEnd->sendSeqNum +
|
|
(long unsigned)1);
|
|
if (windowSize < 0)
|
|
{
|
|
ErrorLog("SendData", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
if (dataSize is 0 or windowSize is 0)
|
|
{
|
|
#if VerboseMessages
|
|
printf("Sending non-data Ack for RefNum = %d to %d.%d.%d.\n",
|
|
connectionEnd->refNum,
|
|
connectionEnd->remoteAddress.networkNumber,
|
|
connectionEnd->remoteAddress.nodeNumber,
|
|
connectionEnd->remoteAddress.socketNumber);
|
|
#endif
|
|
|
|
/* Build an Ack datagram and send it on its way. */
|
|
|
|
if ((datagram = NewBufferDescriptor(AdspDataOffset)) is Empty)
|
|
{
|
|
ErrorLog("SendData", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
return;
|
|
}
|
|
descriptor = AdspControlFlag + AdspProbeOrAckCode +
|
|
((windowSize is 0) ? AdspAckRequestFlag : 0);
|
|
BuildAdspHeader(connectionEnd, datagram->data, descriptor);
|
|
|
|
if (DeliverDdp(connectionEnd->socket,
|
|
connectionEnd->remoteAddress,
|
|
DdpProtocolAdsp,
|
|
datagram,
|
|
AdspDataOffset,
|
|
Empty, Empty, Empty) isnt ATnoError)
|
|
ErrorLog("SendData", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadNonDataAckSend, IMsgAdspBadNonDataAckSend,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* Okay, we have some data to send. */
|
|
|
|
windowSize = Min(windowSize, dataSize);
|
|
while (windowSize > 0)
|
|
{
|
|
/* Compute how much data we can send. */
|
|
|
|
dataSize = MaxNextReadSizeOfBufferQueue(&connectionEnd->nextSendQueue,
|
|
&endOfMessage);
|
|
if (dataSize > AdspMaxDataSize)
|
|
dataSize = AdspMaxDataSize;
|
|
if (dataSize > windowSize)
|
|
dataSize = windowSize;
|
|
|
|
/* Get a buffer descriptor for our write. */
|
|
|
|
if ((datagram = NewBufferDescriptor(AdspDataOffset + dataSize)) is Empty)
|
|
{
|
|
ErrorLog("SendData", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* Send either up to the next EOM, or all remaining data, or 572
|
|
bytes... */
|
|
|
|
tempDataSize = ReadFromBufferQueue(&connectionEnd->nextSendQueue,
|
|
datagram->data, AdspDataOffset,
|
|
dataSize, False, &endOfMessage);
|
|
if (tempDataSize isnt dataSize or
|
|
dataSize + endOfMessage is 0 or
|
|
dataSize > AdspMaxDataSize)
|
|
{
|
|
ErrorLog("SendData", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
FreeBufferChain(datagram);
|
|
return;
|
|
}
|
|
|
|
/* Send over EOM and our ack requirements, if any. */
|
|
|
|
descriptor = endOfMessage ? AdspEndOfMessageFlag : 0;
|
|
if (windowSize is (dataSize + endOfMessage))
|
|
descriptor += AdspAckRequestFlag;
|
|
BuildAdspHeader(connectionEnd, datagram->data, descriptor);
|
|
|
|
#if VerboseMessages
|
|
printf("SendData: sending %u to %u for refNum %d.\n",
|
|
connectionEnd->sendSeqNum,
|
|
connectionEnd->sendSeqNum +
|
|
(long unsigned)dataSize +
|
|
(long unsigned)endOfMessage -
|
|
(long unsigned)1,
|
|
connectionEnd->refNum);
|
|
#endif
|
|
if (DeliverDdp(connectionEnd->socket,
|
|
connectionEnd->remoteAddress,
|
|
DdpProtocolAdsp,
|
|
datagram,
|
|
(int)(AdspDataOffset + dataSize),
|
|
Empty, Empty, 0) isnt ATnoError)
|
|
ErrorLog("SendData", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadNonDataAckSend, IMsgAdspBadNonDataAckSend,
|
|
Insert0());
|
|
|
|
/* Move our send seqNum up. */
|
|
|
|
connectionEnd->sendSeqNum += (long unsigned)(dataSize + endOfMessage);
|
|
windowSize -= (dataSize + endOfMessage);
|
|
}
|
|
if (windowSize isnt 0)
|
|
ErrorLog("SendData", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspWindowSizeErrorToo, IMsgAdspWindowSizeErrorToo,
|
|
Insert0());
|
|
|
|
/* The deed is done. */
|
|
|
|
return;
|
|
|
|
} /* SendData */
|
|
|
|
ExternForVisibleFunction AppleTalkErrorCode
|
|
ReadData(ConnectionEnd connectionEnd,
|
|
Boolean peekOnly,
|
|
long far *peekBytesReturned,
|
|
Boolean far *peekEndOfMessage)
|
|
{
|
|
Boolean endOfMessage;
|
|
long readSize, lookaheadSize, bytesAccepted, bytesRead;
|
|
char far *lookaheadData;
|
|
AdspReceiveHandler *completionRoutine;
|
|
AdspGetAnythingHandler *getAnythingRoutine = Empty;
|
|
long unsigned userData;
|
|
void far *opaqueBuffer;
|
|
long bufferSize, queuedData;
|
|
AdspReceiveEventHandler far *receiveEventHandler;
|
|
long unsigned receiveEventContext;
|
|
|
|
/* Move any data from the receive queue into user space.
|
|
|
|
** We undefer **
|
|
*/
|
|
|
|
if (peekOnly)
|
|
{
|
|
*peekBytesReturned = 0;
|
|
*peekEndOfMessage = False;
|
|
}
|
|
|
|
/* If we've gotten a forward reset, inform the user -- no data is passed up
|
|
in this case. */
|
|
|
|
if ((peekOnly or
|
|
connectionEnd->receivePending or
|
|
connectionEnd->getAnythingPending) and
|
|
connectionEnd->incomingForwardReset)
|
|
{
|
|
if (connectionEnd->getAnythingPending)
|
|
{
|
|
getAnythingRoutine = connectionEnd->getAnythingCompletionRoutine;
|
|
userData = connectionEnd->getAnythingUserData;
|
|
connectionEnd->getAnythingPending = False;
|
|
}
|
|
else
|
|
{
|
|
completionRoutine = connectionEnd->receiveCompletionRoutine;
|
|
userData = connectionEnd->receiveUserData;
|
|
connectionEnd->receivePending = False;
|
|
}
|
|
connectionEnd->incomingForwardReset = False;
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (peekOnly)
|
|
return(ATadspForwardReset);
|
|
else if (getAnythingRoutine isnt Empty)
|
|
(*getAnythingRoutine)(ATadspForwardReset, userData,
|
|
connectionEnd->refNum, False,
|
|
Empty, 0, False);
|
|
else
|
|
(*completionRoutine)(ATadspForwardReset, userData,
|
|
connectionEnd->refNum, empty,
|
|
0, False);
|
|
return(ATnoError);
|
|
}
|
|
|
|
/* Any read request pending? Any data to supply? We do this in a loop
|
|
to handle the cases such as packets coming in during a read and
|
|
additional reads or indications need to be done. Also of a whole big
|
|
blob of messages has just come in and our read completion routine
|
|
posts a new read, we can process a number of messages here. */
|
|
|
|
receiveEventHandler = connectionEnd->receiveEventHandler;
|
|
receiveEventContext = connectionEnd->receiveEventContext;
|
|
queuedData = BufferQueueSize(&connectionEnd->receiveQueue);
|
|
|
|
if (peekOnly and queuedData is 0)
|
|
{
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
}
|
|
|
|
bytesRead = 1;
|
|
while (queuedData > 0 and bytesRead > 0)
|
|
{
|
|
bytesRead = 0;
|
|
|
|
/* Check for no pending reads but an unblocked event handler. */
|
|
|
|
if (not peekOnly and
|
|
not connectionEnd->receivePending and
|
|
not connectionEnd->getAnythingPending and
|
|
receiveEventHandler isnt Empty and
|
|
connectionEnd->previouslyIndicatedData is 0)
|
|
{
|
|
/* Okay, there is data to be received and an event handler is
|
|
present and receive data indications are unblocked
|
|
(previouslyIndicatedData is 0). */
|
|
|
|
connectionEnd->previouslyIndicatedData = queuedData;
|
|
|
|
/* Call the indication handler with pointer to buffer chunk with data,
|
|
length of data in buffer chunk as the lookahead size, and the size
|
|
of the buffer queue as available bytes.
|
|
|
|
The lookahead size is the number of bytes that can be pulled from
|
|
the "first" buffer chunk on the queue; it may be zero if there is
|
|
just an endOfMessage. The last argument is the "available data;"
|
|
each EOM in the queue does count as a "byte." If this routine
|
|
accepts the data, it should indicate so by returning
|
|
"lookaheadSize + endOfMessage." The "+ endOfMessage" is critical
|
|
in the case where there is only an EOM, and we need to know
|
|
something was accepted (if it was)!
|
|
|
|
Further, we are indicating that all remaining data is immediately
|
|
available (we're at the last buffer chunk) if "queuedData" is
|
|
equal to "lookaheadSize + endOfMessage."
|
|
|
|
We set "dataEventInProgress" so that any Reads or GetAnythings
|
|
posted during the indication will just be queued (not fullfiled)
|
|
until we return, we'll process them imediately after the indication
|
|
returns. The allows the user to accept some data, and then read
|
|
data beyond the accepted bytes. */
|
|
|
|
lookaheadData = GetLookaheadPointer(&connectionEnd->receiveQueue,
|
|
&lookaheadSize);
|
|
connectionEnd->dataEventInProgress = True;
|
|
bytesAccepted = (*receiveEventHandler)(connectionEnd->refNum,
|
|
receiveEventContext,
|
|
lookaheadData, lookaheadSize,
|
|
endOfMessage, queuedData);
|
|
connectionEnd->dataEventInProgress = False;
|
|
|
|
/* The user can't accept more than the lookahead! */
|
|
|
|
if (bytesAccepted > lookaheadSize + endOfMessage)
|
|
bytesAccepted = lookaheadSize + endOfMessage;
|
|
bytesRead = bytesAccepted;
|
|
|
|
if (bytesAccepted isnt 0)
|
|
{
|
|
/* Adjust the buffer queue to account for any accepted data;
|
|
note that if the user accepts some data and also places a
|
|
read for more data, the read will complete with data that
|
|
is logically "after" the accepted data. */
|
|
|
|
/* Consume bytesAccepted amount of data. Adjust our window
|
|
size too, and SendData to notify the other side that we've
|
|
consumed the data. */
|
|
|
|
DiscardFromBufferQueue(&connectionEnd->receiveQueue,
|
|
bytesAccepted, empty);
|
|
connectionEnd->receiveWindowSize += bytesAccepted;
|
|
if (connectionEnd->receiveWindowSize >
|
|
connectionEnd->receiveQueueMax)
|
|
{
|
|
ErrorLog("ReadData", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspWindowSizeOverMax, IMsgAdspWindowSizeOverMax,
|
|
Insert0());
|
|
connectionEnd->receiveWindowSize = connectionEnd->receiveQueueMax;
|
|
}
|
|
SendData(connectionEnd);
|
|
}
|
|
}
|
|
|
|
/* Now check for pending reads, either an originally posted read, or
|
|
maybe one posted by the event handler. */
|
|
|
|
if (peekOnly or
|
|
connectionEnd->getAnythingPending or
|
|
connectionEnd->receivePending)
|
|
{
|
|
if (peekOnly or connectionEnd->receivePending)
|
|
{
|
|
opaqueBuffer = connectionEnd->receiveOpaqueBuffer;
|
|
bufferSize = connectionEnd->receiveBufferSize;
|
|
}
|
|
else
|
|
{
|
|
opaqueBuffer = connectionEnd->getAnythingOpaqueBuffer;
|
|
bufferSize = connectionEnd->getAnythingBufferSize;
|
|
}
|
|
|
|
/* Read the data into user space; if we're just peeking make sure
|
|
we don't really alter the buffer queue. */
|
|
|
|
if (peekOnly)
|
|
{
|
|
BufferQueue originalBufferQueue;
|
|
|
|
originalBufferQueue = connectionEnd->receiveQueue;
|
|
readSize = ReadFromBufferQueue(&originalBufferQueue,
|
|
opaqueBuffer, 0,
|
|
bufferSize, True,
|
|
&endOfMessage);
|
|
}
|
|
else
|
|
readSize = ReadAndDiscardFromBufferQueue(&connectionEnd->
|
|
receiveQueue,
|
|
opaqueBuffer, 0,
|
|
bufferSize, True,
|
|
&endOfMessage);
|
|
if (readSize is 0 and not endOfMessage)
|
|
{
|
|
ErrorLog("ReadData", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspNoData, IMsgAdspNoData,
|
|
Insert0());
|
|
connectionEnd->previouslyIndicatedData = 0;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
break;
|
|
}
|
|
bytesRead += (readSize + endOfMessage);
|
|
|
|
/* If we're jsut peeking, return what we know, and flee. */
|
|
|
|
if (peekOnly)
|
|
{
|
|
*peekBytesReturned = readSize;
|
|
*peekEndOfMessage = endOfMessage;
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
}
|
|
|
|
/* Increase our window size, and return the data to user space. Do a
|
|
SendData so that the other side knows about the change to our receive
|
|
window size. */
|
|
|
|
connectionEnd->receiveWindowSize += (readSize + endOfMessage);
|
|
if (connectionEnd->receiveWindowSize >
|
|
connectionEnd->receiveQueueMax)
|
|
{
|
|
ErrorLog("ReadData", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspWindowSizeOverMax, IMsgAdspWindowSizeOverMax,
|
|
Insert0());
|
|
connectionEnd->receiveWindowSize = connectionEnd->receiveQueueMax;
|
|
}
|
|
|
|
SendData(connectionEnd);
|
|
if (connectionEnd->getAnythingPending)
|
|
{
|
|
getAnythingRoutine = connectionEnd->getAnythingCompletionRoutine;
|
|
userData = connectionEnd->getAnythingUserData;
|
|
connectionEnd->getAnythingPending = False;
|
|
}
|
|
else
|
|
{
|
|
completionRoutine = connectionEnd->receiveCompletionRoutine;
|
|
userData = connectionEnd->receiveUserData;
|
|
connectionEnd->receivePending = False;
|
|
}
|
|
|
|
/* Call the read completion routine. */
|
|
|
|
if (getAnythingRoutine isnt Empty)
|
|
(*getAnythingRoutine)(ATnoError, userData, connectionEnd->refNum,
|
|
False, opaqueBuffer, readSize, endOfMessage);
|
|
else
|
|
(*completionRoutine)(ATnoError, userData, connectionEnd->refNum,
|
|
opaqueBuffer, readSize, endOfMessage);
|
|
}
|
|
|
|
/* Now we need to adjust the previouslyIndicatedBytes and see if we need
|
|
to go again... */
|
|
|
|
connectionEnd->previouslyIndicatedData -=
|
|
Min(bytesRead, connectionEnd->previouslyIndicatedData);
|
|
queuedData = BufferQueueSize(&connectionEnd->receiveQueue);
|
|
}
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* ReadData */
|
|
|
|
ExternForVisibleFunction void BuildAdspHeader(ConnectionEnd connectionEnd,
|
|
char *datagram,
|
|
int descriptor)
|
|
{
|
|
/* Format a standard ADSP header or an attention ADSP header (based on the
|
|
flags in "descriptor". */
|
|
|
|
MoveShortMachineToWire(datagram + AdspSourceConnectionIdOffset,
|
|
connectionEnd->connectionId);
|
|
if (descriptor & AdspAttentionFlag)
|
|
MoveLongMachineToWire(datagram + AdspThisAttentionSeqNumOffset,
|
|
connectionEnd->sendAttentionSeqNum);
|
|
else
|
|
MoveLongMachineToWire(datagram + AdspFirstByteSeqNumOffset,
|
|
connectionEnd->sendSeqNum);
|
|
if (descriptor & AdspAttentionFlag)
|
|
MoveLongMachineToWire(datagram + AdspNextReceiveAttnSeqNumOffset,
|
|
connectionEnd->receiveAttentionSeqNum);
|
|
else
|
|
MoveLongMachineToWire(datagram + AdspNextReceiveByteSeqNumOffset,
|
|
connectionEnd->receiveSeqNum);
|
|
if (descriptor & AdspAttentionFlag)
|
|
MoveShortMachineToWire(datagram + AdspReceiveAttentionSizeOffset, 0);
|
|
else
|
|
MoveShortMachineToWire(datagram + AdspReceiveWindowSizeOffset,
|
|
connectionEnd->receiveWindowSize);
|
|
|
|
/* Lastly, move the descriptor too. */
|
|
|
|
datagram[AdspDescriptorOffset] = (char)descriptor;
|
|
return;
|
|
|
|
} /* BuildAdspHeader */
|
|
|
|
ExternForVisibleFunction void RemoveConnectionEnd(ConnectionEnd target)
|
|
{
|
|
ConnectionEnd connectionEnd, previousConnectionEnd;
|
|
long index;
|
|
|
|
#if VerboseMessages
|
|
printf("Removing connection end for RefNum = %d.\n", target->refNum);
|
|
#endif
|
|
|
|
/* Remove the target from the RefNum lookup table. */
|
|
|
|
if (target->connectionState is AdspOpen or
|
|
target->connectionState is AdspHalfOpen)
|
|
{
|
|
CheckMod(index, target->refNum, NumberOfConnectionEndHashBkts,
|
|
"RemoveConnectionEnd");
|
|
for (previousConnectionEnd = empty,
|
|
connectionEnd = connectionEndRefNumHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
previousConnectionEnd = connectionEnd,
|
|
connectionEnd = connectionEnd->next)
|
|
if (connectionEnd is target)
|
|
break;
|
|
if (connectionEnd is empty)
|
|
ErrorLog("RemoveConnectionEnd", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspNotOnRefNumList, IMsgAdspNotOnRefNumList,
|
|
Insert0());
|
|
else if (previousConnectionEnd is empty)
|
|
connectionEndRefNumHashBuckets[index] = target->next;
|
|
else
|
|
previousConnectionEnd->next = target->next;
|
|
}
|
|
|
|
/* Remove the target from the LocalInfo lookup table. */
|
|
|
|
if (target->connectionState is AdspOpen or
|
|
target->connectionState is AdspHalfOpen)
|
|
{
|
|
CheckMod(index, target->socket, NumberOfConnectionEndHashBkts,
|
|
"RemoveConnectionEnd");
|
|
for (previousConnectionEnd = empty,
|
|
connectionEnd = connectionEndLocalHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
previousConnectionEnd = connectionEnd,
|
|
connectionEnd = connectionEnd->nextByLocalInfo)
|
|
if (connectionEnd is target)
|
|
break;
|
|
if (connectionEnd is empty)
|
|
ErrorLog("RemoveConnectionEnd", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspNotOnRefNumList, IMsgAdspNotOnRefNumList,
|
|
Insert0());
|
|
else if (previousConnectionEnd is empty)
|
|
connectionEndLocalHashBuckets[index] = target->nextByLocalInfo;
|
|
else
|
|
previousConnectionEnd->nextByLocalInfo = target->nextByLocalInfo;
|
|
}
|
|
|
|
/* Remove the target from the RemoteInfo lookup table. */
|
|
|
|
if (target->connectionState is AdspOpen)
|
|
{
|
|
CheckMod(index, target->remoteConnectionId,
|
|
NumberOfConnectionEndHashBkts, "RemoveConnectionEnd");
|
|
for (previousConnectionEnd = empty,
|
|
connectionEnd = connectionEndRemoteHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
previousConnectionEnd = connectionEnd,
|
|
connectionEnd = connectionEnd->nextByRemoteInfo)
|
|
if (connectionEnd is target)
|
|
break;
|
|
if (connectionEnd is empty)
|
|
ErrorLog("RemoveConnectionEnd", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspNotOnRefNumList, IMsgAdspNotOnRefNumList,
|
|
Insert0());
|
|
else if (previousConnectionEnd is empty)
|
|
connectionEndRemoteHashBuckets[index] = target->nextByRemoteInfo;
|
|
else
|
|
previousConnectionEnd->nextByRemoteInfo = target->nextByRemoteInfo;
|
|
}
|
|
|
|
/* Now we need to know if this was/is the last guy using a specified
|
|
socket. If so, close the socket. Socket may have been tagged to "-1"
|
|
if AdspPacketIn gets a ATsocketClosed errorcode. */
|
|
|
|
if (target->socket >= 0 and target->closeSocket)
|
|
{
|
|
CheckMod(index, target->socket, NumberOfConnectionEndHashBkts,
|
|
"RemoveConnectionEnd");
|
|
for (connectionEnd = connectionEndLocalHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
connectionEnd = connectionEnd->nextByLocalInfo)
|
|
if (connectionEnd->socket is target->socket)
|
|
break;
|
|
if (connectionEnd is empty)
|
|
{
|
|
#if VerboseMessages
|
|
printf("Closing socket %d for RefNum = %d.\n", target->socket,
|
|
target->refNum);
|
|
#endif
|
|
if (CloseSocketOnNode(target->socket, Empty, (long)0) isnt ATnoError)
|
|
ErrorLog("RemoveConnectionEnd", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadSocketClose, IMsgAdspBadSocketClose,
|
|
Insert0());
|
|
}
|
|
}
|
|
|
|
/* Okay, free the two buffer queues. */
|
|
|
|
FreeBufferQueue(&target->sendQueue);
|
|
FreeBufferQueue(&target->receiveQueue);
|
|
|
|
/* Free the actual connection and and we're all set! */
|
|
|
|
Free(target);
|
|
return;
|
|
|
|
} /* RemoveConnectionEnd */
|
|
|
|
ExternForVisibleFunction short unsigned
|
|
GetNextConnectionIdForSocket(long socket)
|
|
{
|
|
short unsigned newConnectionId;
|
|
ConnectionEnd connectionEnd;
|
|
long index;
|
|
Boolean wrapped = False;
|
|
|
|
/* Pick a new connection ID - make sure it's not in use on the given
|
|
socket. */
|
|
|
|
while(True)
|
|
{
|
|
newConnectionId = nextConnectionId;
|
|
if ((nextConnectionId += 1) is 0)
|
|
{
|
|
nextConnectionId = 1;
|
|
if (wrapped)
|
|
{
|
|
ErrorLog("GetNextConnectionIdForSocket", ISevError, __LINE__,
|
|
UnknownPort, IErrAdspOutOfIds, IMsgAdspOutOfIds,
|
|
Insert0());
|
|
return(0);
|
|
}
|
|
wrapped = True;
|
|
}
|
|
CheckMod(index, socket, NumberOfConnectionEndHashBkts,
|
|
"GetNextConnectionIdForSocket");
|
|
for (connectionEnd = connectionEndLocalHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
connectionEnd = connectionEnd->nextByLocalInfo)
|
|
if (connectionEnd->socket is socket and
|
|
connectionEnd->connectionId is newConnectionId)
|
|
break;
|
|
if (connectionEnd is empty)
|
|
return(newConnectionId);
|
|
}
|
|
|
|
} /* GetNextConnectionIdForSocket */
|
|
|
|
ExternForVisibleFunction long GetNextRefNum(void)
|
|
{
|
|
long newRefNum;
|
|
long index;
|
|
ConnectionEnd connectionEnd;
|
|
Boolean wrapped = False;
|
|
ConnectionListenerInfo connectionListenerInfo;
|
|
OpenRequestHandler openRequestHandler;
|
|
|
|
/* Pick out a new refNum; make sure it's not in use. */
|
|
|
|
while(True)
|
|
{
|
|
newRefNum = nextConnectionRefNum;
|
|
if ((nextConnectionRefNum += 1) < 0)
|
|
{
|
|
nextConnectionRefNum = (long)0;
|
|
if (wrapped)
|
|
{
|
|
ErrorLog("GetNextRefNum", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspOutOfRefNums, IMsgAdspOutOfRefNums,
|
|
Insert0());
|
|
return(-1);
|
|
}
|
|
wrapped = True;
|
|
}
|
|
|
|
/* Check the open connection ends: */
|
|
|
|
CheckMod(index, newRefNum, NumberOfConnectionEndHashBkts,
|
|
"GetNextRefNum");
|
|
for (connectionEnd = connectionEndRefNumHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
connectionEnd = connectionEnd->next)
|
|
if (connectionEnd->refNum is newRefNum)
|
|
break;
|
|
if (connectionEnd isnt empty)
|
|
continue;
|
|
|
|
/* Check the inUse connection open request handlers (the've got a refNum
|
|
reserved in them). */
|
|
|
|
for (connectionListenerInfo = connectionListenerList,
|
|
openRequestHandler = empty;
|
|
connectionListenerInfo isnt empty;
|
|
connectionListenerInfo = connectionListenerInfo->next)
|
|
{
|
|
for (openRequestHandler = connectionListenerInfo->openRequestHandlers;
|
|
openRequestHandler isnt empty;
|
|
openRequestHandler = openRequestHandler->next)
|
|
if (openRequestHandler->inUse and
|
|
openRequestHandler->refNum is newRefNum)
|
|
break;
|
|
if (openRequestHandler isnt empty)
|
|
break;
|
|
}
|
|
if (openRequestHandler is empty)
|
|
return(newRefNum);
|
|
}
|
|
|
|
} /* GetNextRefNum */
|
|
|
|
ExternForVisibleFunction ConnectionEnd
|
|
FindConnectionEndByLocalInfo(long socket,
|
|
short unsigned connectionId)
|
|
{
|
|
long index;
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* Hash connectionId and walk the list matching both it and the socket
|
|
handle. */
|
|
|
|
CheckMod(index, socket, NumberOfConnectionEndHashBkts,
|
|
"FindConnectionEndByLocalInfo");
|
|
for (connectionEnd = connectionEndLocalHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
connectionEnd = connectionEnd->nextByLocalInfo)
|
|
if (connectionEnd->socket is socket and
|
|
connectionEnd->connectionId is connectionId)
|
|
return(connectionEnd);
|
|
|
|
return(empty);
|
|
|
|
} /* FindConnectionEndByLocalInfo */
|
|
|
|
ExternForVisibleFunction ConnectionEnd
|
|
FindConnectionEndByRemoteInfo(AppleTalkAddress remoteAddress,
|
|
short unsigned remoteConnectionId)
|
|
{
|
|
long index;
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* Hash remote connectionId and walk list matching both it and the remote
|
|
AppleTalk address. */
|
|
|
|
CheckMod(index, remoteConnectionId, NumberOfConnectionEndHashBkts,
|
|
"FindConnectionEndByRemoteInfo");
|
|
for (connectionEnd = connectionEndRemoteHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
connectionEnd = connectionEnd->nextByRemoteInfo)
|
|
if (connectionEnd->remoteConnectionId is remoteConnectionId and
|
|
AppleTalkAddressesEqual(&connectionEnd->remoteAddress,
|
|
&remoteAddress))
|
|
return(connectionEnd);
|
|
|
|
return(empty);
|
|
|
|
} /* FindConnectionEndByRemoteInfo */
|
|
|
|
ExternForVisibleFunction ConnectionEnd FindConnectionEndByRefNum(long refNum)
|
|
{
|
|
long index;
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* Hash refNum and walk the list looking for it. */
|
|
|
|
CheckMod(index, refNum, NumberOfConnectionEndHashBkts,
|
|
"FindConnectionEndByRefNum");
|
|
for (connectionEnd = connectionEndRefNumHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
connectionEnd = connectionEnd->next)
|
|
if (connectionEnd->refNum is refNum)
|
|
return(connectionEnd);
|
|
|
|
return(empty);
|
|
|
|
} /* FindConnectionEndByRefNum */
|
|
|
|
#if not defined(DeferAdspPackets)
|
|
ExternForVisibleFunction void DeferAdspPackets(void)
|
|
{
|
|
|
|
EnterCriticalSection();
|
|
deferIncomingAdspPacketsCount += 1;
|
|
LeaveCriticalSection();
|
|
|
|
return;
|
|
|
|
} /* DeferAdspPackets */
|
|
|
|
ExternForVisibleFunction void HandleAdspPackets(void)
|
|
{
|
|
DeferredPacket deferredPacket;
|
|
IncomingDdpHandler *handler;
|
|
|
|
if (deferIncomingAdspPacketsCount is 0)
|
|
{
|
|
ErrorLog("HandleAdspPackets", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspZeroDeferCount, IMsgAdspZeroDeferCount,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* Decrement defer count. */
|
|
|
|
EnterCriticalSection();
|
|
deferIncomingAdspPacketsCount -= 1;
|
|
|
|
/* This routine can be called indirectly recursively via the call to
|
|
AdspPacketIn... we don't want to let our stack frame get too big, so
|
|
if we're already trying to handle deferred packets higher on the
|
|
stack, just ignore it here. */
|
|
|
|
if (handleAdspPacketsNesting isnt 0)
|
|
{
|
|
LeaveCriticalSection();
|
|
return;
|
|
}
|
|
handleAdspPacketsNesting += 1;
|
|
|
|
/* If we're no longer defering packets, handle any queued ones. */
|
|
|
|
if (deferIncomingAdspPacketsCount is 0)
|
|
{
|
|
while(headOfDeferredPacketList isnt empty)
|
|
{
|
|
deferredPacket = headOfDeferredPacketList;
|
|
headOfDeferredPacketList = headOfDeferredPacketList->next;
|
|
if (headOfDeferredPacketList is empty)
|
|
tailOfDeferredPacketList = empty;
|
|
if ((currentDeferredPacketCount -= 1) < 0)
|
|
{
|
|
ErrorLog("HandleAdspPackets", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBadDeferCount, IMsgAdspBadDeferCount,
|
|
Insert0());
|
|
currentDeferredPacketCount = 0;
|
|
}
|
|
LeaveCriticalSection();
|
|
if (deferredPacket->forConnectionListener)
|
|
handler = AdspConnectionListenerPacketIn;
|
|
else
|
|
handler = AdspPacketIn;
|
|
(*handler)(deferredPacket->errorCode, deferredPacket->userData,
|
|
deferredPacket->port, deferredPacket->source,
|
|
deferredPacket->destinationSocket, DdpProtocolAdsp,
|
|
deferredPacket->datagram, deferredPacket->datagramLength,
|
|
deferredPacket->actualDestination);
|
|
Free(deferredPacket);
|
|
EnterCriticalSection();
|
|
}
|
|
}
|
|
|
|
handleAdspPacketsNesting -= 1;
|
|
LeaveCriticalSection();
|
|
return;
|
|
|
|
} /* HandleAdspPackets */
|
|
#endif
|
|
|
|
/* **************************************** */
|
|
/* *** Buffer queue management routines *** */
|
|
/* **************************************** */
|
|
|
|
ExternForVisibleFunction Boolean
|
|
AddToBufferQueue(BufferQueue far *bufferQueue, void far *data,
|
|
long offset, long dataSize, Boolean dataIsOpaque,
|
|
Boolean endOfMessage, BufferQueue *auxBufferQueue)
|
|
{
|
|
BufferChunk newChunk;
|
|
|
|
/* "auxBufferQueue" is optional, if present and bufferQueue was initially
|
|
empty, "auxBufferQueue" will be set to "bufferQueue" before returning. */
|
|
|
|
/* Make sure the queue looks basically okay. */
|
|
|
|
if ((bufferQueue->head is empty and bufferQueue->tail isnt empty) or
|
|
(bufferQueue->tail is empty and bufferQueue->head isnt empty))
|
|
{
|
|
ErrorLog("AddToBufferQueue", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
if (dataSize is 0 and not endOfMessage)
|
|
return(True);
|
|
|
|
/* If we just adding an EOM and the last chunk in the queue does not have
|
|
its EOM bit set, we can just set it, rather that adding a new "empty"
|
|
chunk. If we're here and dataSize is zero, we can assume an EOM, due to
|
|
the above test. */
|
|
|
|
if (dataSize is 0 and
|
|
bufferQueue->tail isnt empty and
|
|
not bufferQueue->tail->endOfMessage)
|
|
{
|
|
bufferQueue->tail->endOfMessage = True;
|
|
return(True);
|
|
}
|
|
|
|
/* Build a new buffer chunk and fill it in. */
|
|
|
|
if ((newChunk = Malloc(sizeof(*newChunk) + dataSize)) is empty)
|
|
{
|
|
ErrorLog("AddToBufferQueue", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspOutOfMemory, IMsgAdspOutOfMemory,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
if (dataSize isnt 0)
|
|
if (dataIsOpaque)
|
|
MoveFromOpaque(newChunk->data, data, offset, dataSize);
|
|
else
|
|
MoveMem(newChunk->data, (char far *)data + offset, dataSize);
|
|
newChunk->dataSize = dataSize;
|
|
newChunk->endOfMessage = endOfMessage;
|
|
|
|
/* Link 'er up (at the end of the chunk list). */
|
|
|
|
if (auxBufferQueue isnt empty and
|
|
bufferQueue->head is empty)
|
|
{
|
|
auxBufferQueue->head = auxBufferQueue->tail = newChunk;
|
|
auxBufferQueue->startIndex = (long)0;
|
|
}
|
|
newChunk->next = empty;
|
|
if (bufferQueue->tail isnt empty)
|
|
bufferQueue->tail->next = newChunk;
|
|
bufferQueue->tail = newChunk;
|
|
if (bufferQueue->head is empty)
|
|
{
|
|
bufferQueue->head = newChunk;
|
|
bufferQueue->startIndex = (long)0;
|
|
}
|
|
|
|
return(True);
|
|
|
|
} /* AddToBufferQueue */
|
|
|
|
ExternForVisibleFunction long
|
|
ReadFromBufferQueue(BufferQueue far *bufferQueue, void far *data,
|
|
long offset, long dataSize, Boolean dataIsOpaque,
|
|
Boolean far *endOfMessage)
|
|
{
|
|
/* Read from a buffer queue; return the total bytes read; stop at:
|
|
end of queue; buffer filled; end of message. Return the total
|
|
bytes placed in the buffer; the EOM does NOT count as a byte.
|
|
Move the queue along, but do NOT free any buffer chunks that have
|
|
been read. */
|
|
|
|
BufferChunk currentChunk, previousChunk;
|
|
long startIndex = bufferQueue->startIndex;
|
|
long copyChunkSize;
|
|
long lastReadIndex;
|
|
long dataIndex = 0;
|
|
|
|
/* Does the queue look okay. */
|
|
|
|
if ((bufferQueue->head is empty and bufferQueue->tail isnt empty) or
|
|
(bufferQueue->tail is empty and bufferQueue->head isnt empty))
|
|
{
|
|
ErrorLog("ReadFromBufferQueue", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
return((long)0);
|
|
}
|
|
|
|
/* Check for the two "no data to read" cases. */
|
|
|
|
*endOfMessage = False;
|
|
if (bufferQueue->head is empty or
|
|
(bufferQueue->head->next is empty and
|
|
startIndex is bufferQueue->head->dataSize +
|
|
bufferQueue->head->endOfMessage))
|
|
return((long)0);
|
|
|
|
/* Copy chunks of data until either we exhust the target buffer or we hit
|
|
an end of message. */
|
|
|
|
for (previousChunk = empty, currentChunk = bufferQueue->head;
|
|
currentChunk isnt empty;
|
|
previousChunk = currentChunk, currentChunk = currentChunk->next)
|
|
{
|
|
/* Check for nothing to read in current chunk. */
|
|
|
|
if (startIndex is currentChunk->dataSize +
|
|
currentChunk->endOfMessage)
|
|
{
|
|
startIndex = 0;
|
|
continue;
|
|
}
|
|
|
|
/* Copy as much as we can; either whole chunk or full buffer. */
|
|
|
|
copyChunkSize = Min(currentChunk->dataSize - startIndex, dataSize);
|
|
if (copyChunkSize < 0)
|
|
{
|
|
ErrorLog("ReadFromBufferQueue", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
return((long)0);
|
|
}
|
|
if (dataIsOpaque)
|
|
MoveToOpaque(data, offset + dataIndex, currentChunk->data + startIndex,
|
|
copyChunkSize);
|
|
else
|
|
MoveMem((char far *)data + offset + dataIndex, (char far *)currentChunk->data + startIndex,
|
|
copyChunkSize);
|
|
dataIndex += copyChunkSize;
|
|
dataSize -= copyChunkSize;
|
|
lastReadIndex = startIndex + copyChunkSize;
|
|
|
|
/* Check terminating conditions. */
|
|
|
|
startIndex = 0; /* startIndex only counts for the first chunk */
|
|
if (dataSize is 0) /* Buffer full? */
|
|
{
|
|
if (lastReadIndex is currentChunk->dataSize and
|
|
currentChunk->endOfMessage)
|
|
*endOfMessage = True; /* Are we really returning the eom? */
|
|
break;
|
|
}
|
|
if (currentChunk->endOfMessage) /* Hit eom? */
|
|
{
|
|
*endOfMessage = True;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Update the head of the list. */
|
|
|
|
if (currentChunk is empty)
|
|
{
|
|
/* We've really run out of data (no EOM). */
|
|
|
|
bufferQueue->head = previousChunk;
|
|
bufferQueue->startIndex = previousChunk->dataSize +
|
|
previousChunk->endOfMessage;
|
|
}
|
|
else if (*endOfMessage and currentChunk->next isnt empty)
|
|
{
|
|
/* We've hit EOM but there are additional chunks, move to start of
|
|
next one. */
|
|
|
|
bufferQueue->head = currentChunk->next;
|
|
bufferQueue->startIndex = (long)0;
|
|
}
|
|
else if (*endOfMessage)
|
|
{
|
|
/* We've hit EOM of the last chunk, signify by moving to the end
|
|
of the current chunk (data plus eom size) -- we've aways logically
|
|
read the EOM. */
|
|
|
|
bufferQueue->head = currentChunk;
|
|
bufferQueue->startIndex = currentChunk->dataSize + 1;
|
|
}
|
|
else if (lastReadIndex is currentChunk->dataSize and
|
|
currentChunk->next isnt empty)
|
|
{
|
|
/* Filled buffer at end of current chunk, more chunks remain. */
|
|
|
|
bufferQueue->head = currentChunk->next;
|
|
bufferQueue->startIndex = (long)0;
|
|
}
|
|
else if (lastReadIndex is currentChunk->dataSize)
|
|
{
|
|
/* Filled buffer at end of current chunk, no more chunks. */
|
|
|
|
bufferQueue->head = currentChunk;
|
|
bufferQueue->startIndex = currentChunk->dataSize;
|
|
}
|
|
else
|
|
{
|
|
/* We've filled the buffer in the middle of a chunk. */
|
|
|
|
bufferQueue->head = currentChunk;
|
|
bufferQueue->startIndex = lastReadIndex;
|
|
}
|
|
|
|
/* All set. */
|
|
|
|
return(dataIndex);
|
|
|
|
} /* ReadFromBufferQueue */
|
|
|
|
ExternForVisibleFunction long
|
|
ReadAndDiscardFromBufferQueue(BufferQueue far *bufferQueue,
|
|
void far *data,
|
|
long offset, long dataSize,
|
|
Boolean dataIsOpaque,
|
|
Boolean far *endOfMessage)
|
|
{
|
|
/* Read from a buffer queue; return the total bytes read; stop at:
|
|
end of queue; buffer filled; end of message. Return the total
|
|
bytes placed in the buffer; the EOM does NOT count as a byte.
|
|
Move the queue along, free any complete buffer chunks that have
|
|
been read. */
|
|
|
|
long totalBytesRead;
|
|
BufferChunk currentChunk, nextChunk;
|
|
BufferChunk originalHead = bufferQueue->head;
|
|
|
|
/* Okay, first do the read. */
|
|
|
|
if ((totalBytesRead = ReadFromBufferQueue(bufferQueue, data, offset, dataSize,
|
|
dataIsOpaque, endOfMessage))
|
|
is (long)0 and not *endOfMessage)
|
|
return((long)0);
|
|
|
|
/* If we've completely processed any chunks, free them. */
|
|
|
|
for (currentChunk = originalHead;
|
|
currentChunk isnt bufferQueue->head;
|
|
currentChunk = nextChunk)
|
|
{
|
|
nextChunk = currentChunk->next;
|
|
Free(currentChunk);
|
|
}
|
|
|
|
/* Okay, if we're at the end of the last chunk, free it and set the
|
|
queue to empty. */
|
|
|
|
if (bufferQueue->startIndex is bufferQueue->head->dataSize +
|
|
bufferQueue->head->endOfMessage)
|
|
{
|
|
if (bufferQueue->head->next isnt empty)
|
|
{
|
|
/* ReadFromBufferQueue should never leave us in this state, it should
|
|
move head up to the next chunk... but check anyway. */
|
|
|
|
ErrorLog("ReadAndDiscardFromBufferQueue", ISevError, __LINE__,
|
|
UnknownPort, IErrAdspBadStartIndex, IMsgAdspBadStartIndex,
|
|
Insert0());
|
|
bufferQueue->head = bufferQueue->head->next;
|
|
bufferQueue->startIndex = (long)0;
|
|
Free(currentChunk);
|
|
return(totalBytesRead);
|
|
}
|
|
|
|
/* Free the last chunk and set the queue to empty. */
|
|
|
|
Free(bufferQueue->head);
|
|
bufferQueue->head = bufferQueue->tail = empty;
|
|
bufferQueue->startIndex = 0;
|
|
return(totalBytesRead);
|
|
}
|
|
|
|
/* More data ramaining... */
|
|
|
|
return(totalBytesRead);
|
|
|
|
} /* ReadAndDiscardFromBufferQueue */
|
|
|
|
ExternForVisibleFunction Boolean
|
|
DiscardFromBufferQueue(BufferQueue far *bufferQueue, long dataSize,
|
|
BufferQueue far *auxBufferQueue)
|
|
{
|
|
/* Discard data from a buffer queue. "dataSize" is the amount of data
|
|
to discard; each EOM DOES count a single byte. "auxBufferQueue" is
|
|
optional; if present it points somewhere within "bufferQueue" -- it
|
|
is invalid to discard beyond this point. If "auxBufferQueue" is
|
|
present AND points to the end of "bufferQueue" AND all data in
|
|
"bufferQueue" is discarded, "auxBufferQueue" will also be set to empty
|
|
at completion. For managing the "ADSP sendQueue" the values for
|
|
"bufferQueue" and "auxBufferQueue" would be "sendQueue" and
|
|
"nextSendQueue" respectively. */
|
|
|
|
BufferChunk currentChunk, nextChunk;
|
|
long startIndex = bufferQueue->startIndex;
|
|
long chunkSize;
|
|
|
|
/* A little error checking. */
|
|
|
|
if (dataSize > 0 and bufferQueue->head is empty)
|
|
{
|
|
ErrorLog("DiscardFromBufferQueue", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspQueueIsEmpty, IMsgAdspQueueIsEmpty,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
if ((bufferQueue->head is empty and bufferQueue->tail isnt empty) or
|
|
(bufferQueue->tail is empty and bufferQueue->head isnt empty) or
|
|
dataSize < 0)
|
|
{
|
|
ErrorLog("DiscardFromBufferQueue", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
if (auxBufferQueue isnt empty)
|
|
if ((auxBufferQueue->head is empty and auxBufferQueue->tail isnt empty) or
|
|
(auxBufferQueue->tail is empty and auxBufferQueue->head isnt empty))
|
|
{
|
|
ErrorLog("DiscardFromBufferQueue", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspAuxQueueError, IMsgAdspAuxQueueError,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
|
|
/* Walk along our buffer queue discarding what we can. */
|
|
|
|
for (currentChunk = bufferQueue->head;
|
|
currentChunk isnt empty;
|
|
currentChunk = nextChunk)
|
|
{
|
|
nextChunk = currentChunk->next;
|
|
|
|
/* How much data is in the current chunk? */
|
|
|
|
chunkSize = currentChunk->dataSize - startIndex;
|
|
if (currentChunk->endOfMessage)
|
|
chunkSize += 1;
|
|
|
|
/* Are we finished discarding while data still remains in the current
|
|
chunk? If so, reset the start index and we're finished. */
|
|
|
|
if (dataSize < chunkSize)
|
|
{
|
|
bufferQueue->head = currentChunk;
|
|
bufferQueue->startIndex = startIndex + dataSize;
|
|
if (auxBufferQueue isnt empty and
|
|
bufferQueue->head is auxBufferQueue->head and
|
|
bufferQueue->startIndex > auxBufferQueue->startIndex)
|
|
{
|
|
ErrorLog("DiscardFromBufferQueue", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspTooMuchDiscard, IMsgAdspTooMuchDiscard,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
return(True);
|
|
}
|
|
|
|
/* Otherwise, we discarded a whole chunk? */
|
|
|
|
if (auxBufferQueue isnt empty and
|
|
auxBufferQueue->head is currentChunk and
|
|
(auxBufferQueue->head->next isnt empty or
|
|
auxBufferQueue->startIndex < auxBufferQueue->head->dataSize +
|
|
auxBufferQueue->head->endOfMessage))
|
|
{
|
|
ErrorLog("DiscardFromBufferQueue", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspTooMuchFree, IMsgAdspTooMuchFree,
|
|
Insert0());
|
|
auxBufferQueue->head = auxBufferQueue->tail = empty;
|
|
auxBufferQueue->startIndex = (long)0;
|
|
}
|
|
Free(currentChunk);
|
|
|
|
/* Move on to the next chunk. */
|
|
|
|
dataSize -= chunkSize;
|
|
startIndex = 0; /* StartIndex only counts for first chunk */
|
|
}
|
|
|
|
/* If we exit out this way, the whole queue has been discarded -- we should
|
|
mark it as empty. */
|
|
|
|
if (dataSize isnt 0)
|
|
ErrorLog("DiscardFromBufferQueue", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspCantDiscard, IMsgAdspCantDiscard,
|
|
Insert0());
|
|
bufferQueue->head = bufferQueue->tail = empty;
|
|
bufferQueue->startIndex = (long)0;
|
|
if (auxBufferQueue isnt empty)
|
|
{
|
|
auxBufferQueue->head = auxBufferQueue->tail = empty;
|
|
auxBufferQueue->startIndex = (long)0;
|
|
}
|
|
|
|
/* All set. */
|
|
|
|
return(True);
|
|
|
|
} /* DiscardFromBufferQueue */
|
|
|
|
ExternForVisibleFunction long
|
|
MaxNextReadSizeOfBufferQueue(BufferQueue far *bufferQueue,
|
|
Boolean far *endOfMessage)
|
|
{
|
|
/* Return the data size in a buffer queue up to the end or next EOM. The
|
|
EOM is NOT counted in the return value. */
|
|
|
|
BufferChunk currentChunk;
|
|
long nextReadSize = (long)0;
|
|
long startIndex = bufferQueue->startIndex;
|
|
|
|
/* Error check. */
|
|
|
|
*endOfMessage = False;
|
|
if ((bufferQueue->head is empty and bufferQueue->tail isnt empty) or
|
|
(bufferQueue->tail is empty and bufferQueue->head isnt empty))
|
|
{
|
|
ErrorLog("MaxNextReadSizeOfBufferQueue", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
return((long)0);
|
|
}
|
|
|
|
/* Walk the queue. */
|
|
|
|
for (currentChunk = bufferQueue->head;
|
|
currentChunk isnt empty;
|
|
currentChunk = currentChunk->next)
|
|
{
|
|
/* Check for nothing in current chunk. */
|
|
|
|
if (startIndex is currentChunk->dataSize +
|
|
currentChunk->endOfMessage)
|
|
{
|
|
startIndex = 0;
|
|
continue;
|
|
}
|
|
|
|
nextReadSize += currentChunk->dataSize - startIndex;
|
|
if (currentChunk->endOfMessage)
|
|
{
|
|
*endOfMessage = True;
|
|
return(nextReadSize);
|
|
}
|
|
startIndex = 0; /* StartIndex only counts in first chunk */
|
|
}
|
|
|
|
/* Well, we could read the whole thing -- return that length. */
|
|
|
|
return(nextReadSize);
|
|
|
|
} /* MaxNextReadSizeOfBufferQueue */
|
|
|
|
ExternForVisibleFunction long BufferQueueSize(BufferQueue far *bufferQueue)
|
|
{
|
|
/* Return the total size of a buffer queue; each EOM counts as a single
|
|
byte. */
|
|
|
|
BufferChunk currentChunk;
|
|
long queueSize = (long)0;
|
|
long startIndex = bufferQueue->startIndex;
|
|
|
|
/* Error check. */
|
|
|
|
if ((bufferQueue->head is empty and bufferQueue->tail isnt empty) or
|
|
(bufferQueue->tail is empty and bufferQueue->head isnt empty))
|
|
{
|
|
ErrorLog("BufferQueueSize", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
return((long)0);
|
|
}
|
|
|
|
/* Walk the queue. */
|
|
|
|
for (currentChunk = bufferQueue->head;
|
|
currentChunk isnt empty;
|
|
currentChunk = currentChunk->next)
|
|
{
|
|
/* Check for nothing in current chunk. */
|
|
|
|
if (startIndex is currentChunk->dataSize +
|
|
currentChunk->endOfMessage)
|
|
{
|
|
startIndex = 0;
|
|
continue;
|
|
}
|
|
|
|
queueSize += currentChunk->dataSize - startIndex;
|
|
if (currentChunk->endOfMessage)
|
|
queueSize += 1;
|
|
startIndex = 0; /* StartIndex only counts in first chunk */
|
|
}
|
|
|
|
/* Return the size. */
|
|
|
|
return(queueSize);
|
|
|
|
} /* BufferQueueSize */
|
|
|
|
ExternForVisibleFunction void FreeBufferQueue(BufferQueue far *bufferQueue)
|
|
{
|
|
BufferChunk currentChunk, nextChunk;
|
|
|
|
if ((bufferQueue->head is empty and bufferQueue->tail isnt empty) or
|
|
(bufferQueue->tail is empty and bufferQueue->head isnt empty))
|
|
ErrorLog("FreeBufferQueue", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
|
|
for (currentChunk = bufferQueue->head;
|
|
currentChunk isnt empty;
|
|
currentChunk = nextChunk)
|
|
{
|
|
nextChunk = currentChunk->next;
|
|
Free(currentChunk);
|
|
}
|
|
|
|
bufferQueue->head = bufferQueue->tail = empty;
|
|
bufferQueue->startIndex = (long)0;
|
|
|
|
return;
|
|
|
|
} /* FreeBufferQueue */
|
|
|
|
ExternForVisibleFunction char far
|
|
*GetLookaheadPointer(BufferQueue far *bufferQueue, long far *size)
|
|
{
|
|
/* Return the number of bytes that can be read from the next buffer chunk
|
|
to be processed without crossing buffer chunk boundries. An available
|
|
EOM flag does NOT count as a byte. */
|
|
|
|
BufferChunk firstChunk;
|
|
long startIndex = bufferQueue->startIndex;
|
|
|
|
if ((bufferQueue->head is empty and bufferQueue->tail isnt empty) or
|
|
(bufferQueue->tail is empty and bufferQueue->head isnt empty))
|
|
ErrorLog("GetLookaheadPointer", ISevError, __LINE__, UnknownPort,
|
|
IErrAdspBufferQueueError, IMsgAdspBufferQueueError,
|
|
Insert0());
|
|
|
|
/* Queue empty? */
|
|
|
|
if (bufferQueue->head is Empty)
|
|
{
|
|
*size = 0;
|
|
return(Empty);
|
|
}
|
|
|
|
/* "Past" end of current chunk? */
|
|
|
|
firstChunk = bufferQueue->head;
|
|
if (startIndex is firstChunk->dataSize + firstChunk->endOfMessage)
|
|
{
|
|
firstChunk = firstChunk->next;
|
|
startIndex = 0;
|
|
}
|
|
if (firstChunk is Empty)
|
|
{
|
|
*size = 0;
|
|
return(Empty);
|
|
}
|
|
|
|
/* Okay, there is some data, return the information. */
|
|
|
|
*size = firstChunk->dataSize - startIndex;
|
|
return((char far *)(firstChunk->data + startIndex));
|
|
|
|
} /* GetLookaheadPointer */
|
|
|
|
/* ****************** */
|
|
/* *** Debug code *** */
|
|
/* ****************** */
|
|
|
|
#if DebugCode
|
|
|
|
void DumpAdspConnectionEnd(ConnectionEnd connectionEnd)
|
|
{
|
|
printf("\n**** Start connection end dump; RefNum = %d.\n",
|
|
connectionEnd->refNum);
|
|
printf(" Connection state = ");
|
|
switch(connectionEnd->connectionState)
|
|
{
|
|
case AdspClosed:
|
|
printf("Closed.\n");
|
|
break;
|
|
case AdspHalfOpen:
|
|
printf("HalfOpen (%s; %s seen remote OpenReq).\n",
|
|
(connectionEnd->passiveOpen ? "passive" : "active"),
|
|
(connectionEnd->seenRemoteOpenRequest ? "have" : "haven't"));
|
|
break;
|
|
case AdspOpen:
|
|
printf("Open.\n");
|
|
break;
|
|
default:
|
|
printf("Unknown.\n");
|
|
break;
|
|
}
|
|
printf(" socket = %d, localConnId = 0%o, sendMax = %d, receiveMax = %d,\n",
|
|
connectionEnd->socket, connectionEnd->connectionId,
|
|
connectionEnd->sendQueueMax, connectionEnd->receiveQueueMax);
|
|
printf(" toAddr = %d.%d.%d, remConnId = 0%o, sendASN = %d, recvASN = %d,\n",
|
|
connectionEnd->remoteAddress.networkNumber,
|
|
connectionEnd->remoteAddress.nodeNumber,
|
|
connectionEnd->remoteAddress.socketNumber,
|
|
connectionEnd->remoteConnectionId,
|
|
connectionEnd->sendAttentionSeqNum,
|
|
connectionEnd->receiveAttentionSeqNum);
|
|
printf(" sendSQ = %d, sendWSN = %d, resendSN = %d, tosend = %d.\n",
|
|
connectionEnd->sendSeqNum, connectionEnd->sendWindowSeqNum,
|
|
connectionEnd->retransmitSeqNum,
|
|
BufferQueueSize(&connectionEnd->nextSendQueue));
|
|
printf(" recvSN = %d, recvWN = %d, undeliveredWN = %d; recvP = %d.\n",
|
|
connectionEnd->receiveSeqNum, connectionEnd->receiveWindowSize,
|
|
BufferQueueSize(&connectionEnd->receiveQueue),
|
|
connectionEnd->receivePending);
|
|
printf("**** End connection end dump.\n");
|
|
|
|
} /* DumpAdspConnectionEnd */
|
|
|
|
void DumpAdspInfo(long refNum)
|
|
{
|
|
int index;
|
|
ConnectionEnd connectionEnd;
|
|
|
|
/* If RefNum specified, dump that one, else dump 'em all. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAdspPackets();
|
|
if (refNum >= 0)
|
|
{
|
|
if ((connectionEnd = FindConnectionEndByRefNum(refNum)) is empty)
|
|
{
|
|
printf("**** Could not find RefNum %d.\n", refNum);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
DumpAdspConnectionEnd(connectionEnd);
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
for (index = 0; index < NumberOfConnectionEndHashBkts; index += 1)
|
|
for (connectionEnd = connectionEndRefNumHashBuckets[index];
|
|
connectionEnd isnt empty;
|
|
connectionEnd = connectionEnd->next)
|
|
DumpAdspConnectionEnd(connectionEnd);
|
|
|
|
HandleAdspPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* DumpAdspInfo */
|
|
|
|
#endif
|