Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

5507 lines
168 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
adsp.c
Abstract:
This module contains the ADSP protocol code.
Author:
Garth Conboy Initial Coding
Nikhil Kamkolkar Rewritten for microsoft coding style. mpized
Revision History:
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 - (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.
--*/
#define IncludeAdspErrors 1
#include "atalk.h"
#define VerboseMessages 0 // 0 for quiet
#define DebugCode 0 // 0 for none
// ************************************
// *** Static data and definitions. ***
// ************************************
// 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;
static AppleTalkAddress unknownAddress;
// Primos has no conecpt of "critical sections" so simulate it.
#if Iam a Primos
#undef EnterCriticalSection
#undef LeaveCriticalSection
#define EnterCriticalSection DeferTimerChecking
#define LeaveCriticalSection HandleDeferredTimerChecks
#endif
// *********************************
// *** 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 void ReadData(ConnectionEnd connectionEnd);
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
PeekFromBufferQueue(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);
ExternForVisibleFunction void DeferAdspPackets(void);
ExternForVisibleFunction void HandleAdspPackets(void);
// **************************
// *** 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);
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);
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);
Free(openRequestHandler);
HandleAdspPackets();
HandleDeferredTimerChecks();
return(ATinternalError);
}
// Okay, start building a connection end.
if ((connectionEnd = Calloc(sizeof(*connectionEnd), 1)) is empty) {
if (closeSocket)
CloseSocketOnNode(ourSocket);
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);
HandleAdspPackets();
HandleDeferredTimerChecks();
return(ATinternalError);
}
// Okay, start building a connection end.
if ((connectionEnd = Calloc(sizeof(*connectionEnd), 1)) is empty) {
if (closeSocket)
CloseSocketOnNode(ourSocket);
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.
if (connectionEnd->connectionState is AdspHalfOpen) {
(*connectionEnd->openCompletionRoutine)(ATadspConnectionClosed,
connectionEnd->openUserData,
refNum, (long)0,
unknownAddress);
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) {
(*connectionEnd->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) {
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 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);
else {
HandleAdspPackets();
HandleDeferredTimerChecks();
}
return(ATnoError);
} // AdspReceive
AppleTalkErrorCode far AdspPeek(
long refNum, // Session to read from.
void far *opaqueBuffer, // "Buffer" to read into.
long *bufferSize, // Size of above., on return number read
Boolean *endOfMessage) // was an eom also there
{
ConnectionEnd connectionEnd;
// 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);
}
// If a receive is pending, we do not want to mess around with a peek
if (connectionEnd->receivePending) {
HandleAdspPackets();
HandleDeferredTimerChecks();
return(ATadspReadAlreadyPending);
}
// Try to return any data that's waiting now without consuming it
*bufferSize = PeekFromBufferQueue(&connectionEnd->receiveQueue, opaqueBuffer,
0, *bufferSize, True, endOfMessage);
HandleAdspPackets();
HandleDeferredTimerChecks();
return(ATnoError);
} // 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);
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;
DeferredPacket deferredPacket;
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);
}
// 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();
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;
DeferredPacket deferredPacket;
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);
}
// 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();
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 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) {
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) {
// 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);
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);
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 void ReadData(ConnectionEnd connectionEnd)
{
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 we've gotten a forward reset, inform the user -- no data is passed up
// in this case.
//
if ((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 (getAnythingRoutine isnt Empty)
(*getAnythingRoutine)(ATadspForwardReset, userData,
connectionEnd->refNum, False,
Empty, 0, False);
else
(*completionRoutine)(ATadspForwardReset, userData,
connectionEnd->refNum, empty,
0, False);
return;
}
//
// 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);
bytesRead = 1;
while (queuedData > 0 and bytesRead > 0) {
bytesRead = 0;
// Check for no pending reads but an unblocked event handler.
if (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 (connectionEnd->getAnythingPending or
connectionEnd->receivePending) {
if (connectionEnd->receivePending)
{
opaqueBuffer = connectionEnd->receiveOpaqueBuffer;
bufferSize = connectionEnd->receiveBufferSize;
}
else {
opaqueBuffer = connectionEnd->getAnythingOpaqueBuffer;
bufferSize = connectionEnd->getAnythingBufferSize;
}
if ((readSize = ReadAndDiscardFromBufferQueue(&connectionEnd->
receiveQueue,
opaqueBuffer, 0,
bufferSize, True,
&endOfMessage)) is 0 and
not endOfMessage) {
ErrorLog("ReadData", ISevError, __LINE__, UnknownPort,
IErrAdspNoData, IMsgAdspNoData,
Insert0());
connectionEnd->previouslyIndicatedData = 0;
break;
}
bytesRead += (readSize + endOfMessage);
//
// 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;
} // 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) 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
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
// ****************************************
// *** 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
PeekFromBufferQueue(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.
// Do not move the queue along, or 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;
}
}
return(dataIndex);
}
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