mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2476 lines
83 KiB
2476 lines
83 KiB
/* atp.c, /appletalk/source, Garth Conboy, 02/20/89 */
|
|
/* Copyright (c) 1988 by Pacer Software Inc., La Jolla, CA */
|
|
|
|
/* GC - Initial coding.
|
|
GC - (12/08/89): AppleTalk phase II comes to town.
|
|
GC - (01/09/90): Don't allocate TWO copies of the response buffer!
|
|
Bug from Peter Caswell.
|
|
GC - (06/12/90): In AtpPacketIn() an ATsocketClosed should be handled
|
|
AFTER packet defer logic has been executed.
|
|
GC - (08/18/90): New error logging mechanism.
|
|
GC - (11/03/90): For OS/2 only: Added some "static"s to AtpPostResponse
|
|
to save stack space; this ends up making the routine
|
|
non-reentrant (which is evil), but I don't think it's
|
|
ever called that way under OS/2... we'll see!
|
|
GC - (11/06/90): Still more OS/2 stack usage strinking, to the point of
|
|
Mallocing our dynamic data in AtpPacketIn(). Sigh.
|
|
GC - (03/24/92): Changed the meaning of empty pointers being passed as
|
|
"buffer" and "userBytes" to AtpEnqueueRequestHandler;
|
|
if these arguments are empty, the completion routine is
|
|
passed actual pointers into the Ddp datagram, offset
|
|
correct to be the Atp data and user bytes respectively.
|
|
This allows higher level protocol callers (e.g. Asp) to
|
|
potentially save a buffer copy.
|
|
GC - (03/31/92): Updated for BufferDescriptors.
|
|
GC - (06/27/92): All buffers coming from user space are now "opaque," they
|
|
may or may not be "char *." We now use the new routines
|
|
MoveFromOpaque() and MoveToOpaque() to access these
|
|
"buffer"s rather than MemMove(). Note that "user bytes"
|
|
are left as "char *"s, our caller's would be aware of this
|
|
and copy any "opaque" user bytes to some place "real"
|
|
before calling us!
|
|
GC - (06/27/92): Removed the various "copyRequired" arguments. Our
|
|
callers are now required to keep the various request and
|
|
response buffers around until the given transaction
|
|
completes.
|
|
GC - (06/28/92): Much better code for "synching-up" repsonse buffers.
|
|
GC - (09/17/92): AtpOpenSocketOnNode() now returns an AppleTalkErrorCode
|
|
and passed back the opened socket via a by-reference
|
|
parameter.
|
|
GC - (09/17/92): Ditto for AtpEnqueueRequestHandler().
|
|
GC - (11/14/92): Added a "cancelDueToClose" argument to the various
|
|
cancel routines... replaced a static Boolean of this
|
|
name that could give us trouble in reentrant or Multi-
|
|
processor environments.
|
|
GC - (11/14/92): Added AtpSetCookieForSocket() and AtpGetCookieForSocket().
|
|
|
|
*** Make the PVCS source control system happy:
|
|
$Header$
|
|
$Log$
|
|
***
|
|
|
|
Yep, you got it, this module contains all of the code for managing the
|
|
AppleTalk ATP protocol.
|
|
|
|
*/
|
|
|
|
#define IncludeAtpErrors 1
|
|
|
|
#include "atalk.h"
|
|
|
|
/* Debug information. */
|
|
|
|
#define Debug 0
|
|
#define VerboseMessages 0
|
|
#if Debug
|
|
#define DropEveryNthRequest 53
|
|
#define DropEveryNthResponse 53
|
|
#define DropEveryNthRelease 53
|
|
#endif
|
|
|
|
/* 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 AtpPacketIn;
|
|
|
|
ExternForVisibleFunction TimerHandler AtpReleaseTimerExpired;
|
|
ExternForVisibleFunction TimerHandler AtpRequestTimerExpired;
|
|
|
|
ExternForVisibleFunction void AtpTransmitRelease(AtpSendRequest request);
|
|
|
|
ExternForVisibleFunction void AtpTransmitRequest(AtpSendRequest request);
|
|
|
|
ExternForVisibleFunction void
|
|
AtpTransmitResponse(long sourceSocket,
|
|
AppleTalkAddress destination,
|
|
short unsigned transactionId,
|
|
void far *responseOpaqueBuffer,
|
|
int responseBufferSize,
|
|
char far *responseUserBytes,
|
|
short unsigned bitmap,
|
|
Boolean explicitZero,
|
|
short maximumSinglePacketDataSize);
|
|
|
|
ExternForVisibleFunction short
|
|
AtpBitmapToBufferSize(short unsigned bitmap,
|
|
short maximumSinglePacketDataSize);
|
|
|
|
ExternForVisibleFunction short unsigned
|
|
AtpBufferSizeToBitmap(int bufferSize,
|
|
short maximumSinglePacketDataSize);
|
|
|
|
ExternForVisibleFunction short
|
|
AtpSynchUpResponseBuffer(AtpSendRequest request,
|
|
short maximumSinglePacketDataSize);
|
|
|
|
ExternForVisibleFunction AtpInfo FindAtpInfoFor(long mySocket);
|
|
|
|
/* Deferred ATP packet queue: */
|
|
|
|
#define MaximumDeferredPackets 50
|
|
typedef struct dp { struct dp far *next;
|
|
AppleTalkErrorCode errorCode;
|
|
int port;
|
|
AppleTalkAddress source;
|
|
long destinationSocket;
|
|
short datagramLength;
|
|
AppleTalkAddress actualDestination;
|
|
char datagram[1];
|
|
} far *DeferredPacket;
|
|
volatile DeferredPacket headOfDeferredPacketList = empty;
|
|
volatile DeferredPacket tailOfDeferredPacketList = empty;
|
|
volatile short currentDeferredPacketCount = 0;
|
|
|
|
volatile short deferIncomingAtpPacketsCount = 0;
|
|
BOOLEAN handleInProgress = FALSE;
|
|
|
|
/* An item on the send-request or send-response lists can be removed either
|
|
due to incoming packets OR a timer going off. When a timer goes off the
|
|
handler is passed the address of the structure. It is not sufficient to
|
|
walk the given list looking for the socket; a packet could have come in
|
|
just before the timer handler got around to defering incoming packets, and
|
|
that packet could have removed the item from the specified list, and if
|
|
we're very unlucky, the address could have been reused, and we could get
|
|
very confused. So, we identify each member of these lists with both their
|
|
address's as well as a 32 bit ID. */
|
|
|
|
typedef struct {long id;
|
|
long socket;
|
|
char far *pointer;
|
|
} AdditionalData;
|
|
static long nextId = 0;
|
|
|
|
AppleTalkErrorCode far AtpOpenSocketOnNode(
|
|
long far *socketHandle, /* The Atp/Ddp socket opended; returned. */
|
|
int port, /* Port on which the socket should live. */
|
|
ExtendedAppleTalkNodeNumber *desiredNode,
|
|
/* Desired node for socket, empty if none. */
|
|
int desiredSocket, /* Desired static socket or zero for dynamic. */
|
|
char far *datagramBuffers, /* DDP datagram buffers. */
|
|
int totalBufferSize) /* How many of these guys. */
|
|
{
|
|
long socket, index;
|
|
AtpInfo atpInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
/* Open the requested DDP socket. */
|
|
|
|
if ((errorCode = OpenSocketOnNode(&socket, port, desiredNode, desiredSocket,
|
|
AtpPacketIn, (long)0, False,
|
|
datagramBuffers,
|
|
totalBufferSize, empty)) isnt ATnoError)
|
|
return(errorCode);
|
|
|
|
/* We have to create and thread a new AtpInfo structure. */
|
|
|
|
if ((atpInfo = (AtpInfo)Calloc(sizeof(*atpInfo), 1)) is empty)
|
|
{
|
|
ErrorLog("AtpOpenSocketOnNode", ISevError, __LINE__, port,
|
|
IErrAtpOutOfMemory, IMsgAtpOutOfMemory,
|
|
Insert0());
|
|
CloseSocketOnNode(socket);
|
|
return(AToutOfMemory);
|
|
}
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
CheckMod(index, socket, MaxAtpInfoHashBuckets, "AtpOpenSocketOnNode");
|
|
atpInfo->mySocket = socket;
|
|
atpInfo->maximumSinglePacketDataSize = AtpSinglePacketDataSize;
|
|
atpInfo->next = atpInfoHashBuckets[index];
|
|
atpInfoHashBuckets[index] = atpInfo;
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
if (socketHandle isnt empty)
|
|
*socketHandle = socket;
|
|
return(ATnoError);
|
|
|
|
} /* AtpOpenSocketOnNode */
|
|
|
|
AppleTalkErrorCode far AtpCloseSocketOnNode(
|
|
long socket) /* The socket to close. */
|
|
{
|
|
AtpInfo atpInfo, previousAtpInfo;
|
|
AtpReceiveRequest receiveRequest, nextReceiveRequest;
|
|
AtpSendRequest sendRequest, nextSendRequest;
|
|
AtpSendResponse sendResponse, nextSendResponse;
|
|
AppleTalkErrorCode errorCode;
|
|
long index;
|
|
|
|
/* We're going to muck with the live ATP databases, defer incoming packets
|
|
and hold off the timers... */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
/* Find the correct ATP strcuture, so we can tear the beast down. */
|
|
|
|
if ((atpInfo = FindAtpInfoFor(socket)) is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
|
|
/* Cancel all pending requests and responses, and free the data structures
|
|
in the OpenSocket node. */
|
|
|
|
for (receiveRequest = atpInfo->receiveRequestQueue;
|
|
receiveRequest isnt empty;
|
|
receiveRequest = nextReceiveRequest)
|
|
{
|
|
nextReceiveRequest = receiveRequest->next;
|
|
AtpCancelRequestHandler(socket, receiveRequest->requestHandlerId, True);
|
|
}
|
|
for (sendRequest = atpInfo->sendRequestQueue;
|
|
sendRequest isnt empty;
|
|
sendRequest = nextSendRequest)
|
|
{
|
|
nextSendRequest = sendRequest->next;
|
|
AtpCancelRequest(socket, sendRequest->transactionId, True);
|
|
}
|
|
for (sendResponse = atpInfo->sendResponseQueue;
|
|
sendResponse isnt empty;
|
|
sendResponse = nextSendResponse)
|
|
{
|
|
nextSendResponse = sendResponse->next;
|
|
AtpCancelResponse(socket, sendResponse->destination,
|
|
sendResponse->transactionId, True);
|
|
}
|
|
|
|
/* Release the AtpInfo structure. */
|
|
|
|
CheckMod(index, socket, MaxAtpInfoHashBuckets, "AtpCloseSocketOnNode");
|
|
for (previousAtpInfo = empty,
|
|
atpInfo = atpInfoHashBuckets[index];
|
|
atpInfo isnt empty;
|
|
previousAtpInfo = atpInfo,
|
|
atpInfo = atpInfo->next)
|
|
if (atpInfo->mySocket is socket)
|
|
break;
|
|
if (atpInfo is empty)
|
|
ErrorLog("AtpCloseSocketOnNode", ISevError, __LINE__, UnknownPort,
|
|
IErrAtpAtpInfoMissing, IMsgAtpAtpInfoMissing,
|
|
Insert0());
|
|
else
|
|
{
|
|
if (previousAtpInfo is empty)
|
|
atpInfoHashBuckets[index] = atpInfo->next;
|
|
else
|
|
previousAtpInfo->next = atpInfo->next;
|
|
Free(atpInfo);
|
|
}
|
|
|
|
/* Close the DDP socket! */
|
|
|
|
errorCode = CloseSocketOnNode(socket);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} /* AtpCloseSocketOnNode */
|
|
|
|
AppleTalkErrorCode AtpSetCookieForSocket(
|
|
long socket, /* The Atp socket for which to set the cookie. */
|
|
long unsigned cookie) /* The new cookie's value. */
|
|
{
|
|
AtpInfo atpInfo;
|
|
|
|
/* We're going to muck with the live ATP databases, defer incoming packets
|
|
and hold off the timers... */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
/* Find the correct ATP strcuture, in which to set the cookie. */
|
|
|
|
if ((atpInfo = FindAtpInfoFor(socket)) is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
|
|
/* Do the deed. */
|
|
|
|
atpInfo->usersCookie = cookie;
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspSetCookieForSession */
|
|
|
|
AppleTalkErrorCode AtpGetCookieForSocket(
|
|
long socket, /* The Atp socket for which to set the cookie. */
|
|
long unsigned far *cookie) /* Where to stick the cookie. */
|
|
{
|
|
AtpInfo atpInfo;
|
|
|
|
/* We're going to muck with the live ATP databases, defer incoming packets
|
|
and hold off the timers... */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
/* Find the correct ATP strcuture, from which to get the cookie. */
|
|
|
|
if ((atpInfo = FindAtpInfoFor(socket)) is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
|
|
/* Do the deed. */
|
|
|
|
*cookie = atpInfo->usersCookie;
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AspGetCookieForSession */
|
|
|
|
AppleTalkErrorCode near AtpMaximumSinglePacketDataSize(
|
|
long socket, /* The address to adjust the send/recieve
|
|
quantum for. */
|
|
short maximumSinglePacketDataSize)
|
|
/* New quantum size. */
|
|
{
|
|
AtpInfo atpInfo;
|
|
|
|
if (maximumSinglePacketDataSize < 0 or
|
|
maximumSinglePacketDataSize > AtpSinglePacketDataSize)
|
|
return(ATatpBadBufferSize);
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
/* Find our ATP info and set the new quantum size. */
|
|
|
|
atpInfo = FindAtpInfoFor(socket);
|
|
if (atpInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
atpInfo->maximumSinglePacketDataSize = maximumSinglePacketDataSize;
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AtpMaximumSinglePacketDataSize */
|
|
|
|
short unsigned _near AtpGetNextTransactionId(
|
|
long socket) /* The AppleTalk address that we're going
|
|
to post the transaction from. */
|
|
{
|
|
AtpInfo atpInfo;
|
|
short unsigned transactionId;
|
|
Boolean inUse = True;
|
|
AtpSendRequest request;
|
|
|
|
/* Guaranteed to be called with packets and timers deferred, and out
|
|
side of a critical section */
|
|
|
|
EnterCriticalSection();
|
|
atpInfo = FindAtpInfoFor(socket);
|
|
if (atpInfo is empty)
|
|
{
|
|
/* There is no real way to return an error here, because our return
|
|
type is "short unsigned"... but, so what? The only thing that can
|
|
be done with a "next transaction id" is to call AtpPostRequest()
|
|
will it... this socket will be the same one as was just passed to
|
|
us, so the user will get the real "socket not open" error at that
|
|
time. So, don't worry about the following "return(0)". */
|
|
|
|
LeaveCriticalSection();
|
|
return(0);
|
|
}
|
|
|
|
/* Okay, find one that's not currently in use. */
|
|
|
|
transactionId = atpInfo->lastTransactionId;
|
|
while(inUse)
|
|
{
|
|
transactionId += 1; /* Will wrap in 16 bits... */
|
|
inUse = False;
|
|
for (request = atpInfo->sendRequestQueue;
|
|
request isnt empty;
|
|
request = request->next)
|
|
if (transactionId is request->transactionId)
|
|
{
|
|
inUse = True;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Okay, we've got one, note it as last used and return. */
|
|
|
|
atpInfo->lastTransactionId = transactionId;
|
|
LeaveCriticalSection();
|
|
|
|
return(transactionId);
|
|
|
|
} /* AtpGetNextTransactionId */
|
|
|
|
#if Iam an OS2 /* Too many stack temporaries... */
|
|
#pragma optimize ("eg", off)
|
|
#endif
|
|
|
|
AppleTalkErrorCode far AtpPostRequest(
|
|
long sourceSocket, /* "Our" AppleTalk address -- source of the
|
|
request. */
|
|
AppleTalkAddress destination, /* Destination AppleTalk address. Who are
|
|
we asking? */
|
|
short unsigned transactionId, /* The transaction ID for this request as
|
|
returned from AtpGetNextTransactionId. */
|
|
void far *requestOpaqueBuffer, /* Request data. */
|
|
int requestBufferSize, /* Request data size, in bytes. */
|
|
char far *requestUserBytes, /* "UserBytes" for request (may be empty). */
|
|
Boolean exactlyOnce, /* Transaction type. */
|
|
void far *responseOpaqueBuffer, /* Buffer to store response in (may be
|
|
empty). */
|
|
int responseBufferSize, /* Expected response size, in bytes. */
|
|
char far *responseUserBytes, /* Buffer to store "userBytes" from first
|
|
response packet in (may be empty). */
|
|
int retryCount, /* How many times should we retry the
|
|
request (-1 = forever). */
|
|
int retryInterval, /* How often should we retry the request
|
|
in seconds (0 -> 2). */
|
|
TRelTimerValue trelTimerValue, /* How long should the other side wait
|
|
for a release? (0 = 30 seconds). */
|
|
AtpIncomingResponseHandler *completionRoutine,
|
|
/* Routine to call when the request
|
|
completes; may be empty. */
|
|
long unsigned userData) /* Arbitrary data passed on to the completion
|
|
routine. */
|
|
{
|
|
/* We post an ATP request to a specified destination. When a complete
|
|
response arrives (or when the request times out) we call the supplied
|
|
completion routine as follows:
|
|
|
|
(*completionRoutine)(errorCode, userData, source, opaqueBuffer,
|
|
bufferSize, userBytes, transactionId);
|
|
|
|
The arguments are:
|
|
|
|
errorCode - an AppleTalkErrorCode; either ATnoError,
|
|
ATatpRequestTimedOut or ATatpResponseBufferTooSmall
|
|
[arguments following "source" will only be valid
|
|
if errorCode is ATnoError].
|
|
|
|
userData - a longword; the "userData" that was passed to the
|
|
corresponding AtpPostRequest call.
|
|
|
|
source - an AppleTalkAddress; the internet source of the ATP
|
|
response (destination of the request).
|
|
|
|
opaqueBuffer - a void*; as passed to AtpPostRequest where the ATP
|
|
response data has been written.
|
|
|
|
bufferSize - an int; the number of bytes actually stored in the
|
|
location pointed to by "buffer".
|
|
|
|
userBytes - a char*; as passed to AtpPostRequest where the ATP
|
|
response "user bytes" have been written. If an
|
|
Empty responseUserBytes buffer was passed into this
|
|
routine, this argument will point to a temporary
|
|
copy of the user bytes, which the handler can copy
|
|
out.
|
|
|
|
transactionId - a short unsigned int; the ATP transaction ID of the
|
|
request.
|
|
*/
|
|
|
|
AtpInfo atpInfo;
|
|
AtpSendRequest request;
|
|
short index;
|
|
AdditionalData additionalData;
|
|
|
|
/* Okay we're ready to start threading a new request data structure... lets
|
|
be private about it! */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
/* Validate socket. */
|
|
|
|
atpInfo = FindAtpInfoFor(sourceSocket);
|
|
if (atpInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
|
|
/* Validate arguments. */
|
|
|
|
if (requestBufferSize < 0 or
|
|
requestBufferSize > atpInfo->maximumSinglePacketDataSize)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpBadBufferSize);
|
|
}
|
|
if (responseBufferSize < 0 or
|
|
responseBufferSize > (atpInfo->maximumSinglePacketDataSize *
|
|
AtpMaximumResponsePackets))
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpBadBufferSize);
|
|
}
|
|
if (retryInterval is 0)
|
|
retryInterval = 2;
|
|
if (retryInterval < 0)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpBadRetryInfo);
|
|
}
|
|
if (retryCount isnt AtpInfiniteRetries and retryCount < 0)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpBadRetryInfo);
|
|
}
|
|
if (trelTimerValue < FirstValidTRelTimerValue or
|
|
trelTimerValue > LastValidTRelTimerValue)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpBadTRelTimer);
|
|
}
|
|
|
|
/* Okay, allocate a request handler (or TCB as Apple would say) so we can
|
|
start filling it in. */
|
|
|
|
request = (AtpSendRequest)Calloc(sizeof(*request), 1);
|
|
if (request is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpCouldNotPostRequest);
|
|
}
|
|
|
|
/* Fill 'er up! */
|
|
|
|
request->sourceSocket = sourceSocket;
|
|
request->destination = destination;
|
|
request->transactionId = transactionId;
|
|
request->exactlyOnce = exactlyOnce;
|
|
request->completionRoutine = completionRoutine;
|
|
request->userData = userData;
|
|
request->requestOpaqueBuffer = requestOpaqueBuffer;
|
|
request->requestBufferSize = (short)requestBufferSize;
|
|
if (requestUserBytes isnt empty)
|
|
MoveMem(request->requestUserBytes, requestUserBytes, AtpUserBytesSize);
|
|
else
|
|
FillMem(request->requestUserBytes, 0, AtpUserBytesSize);
|
|
request->responseOpaqueBuffer = responseOpaqueBuffer;
|
|
request->responseBufferSize = (short)responseBufferSize;
|
|
request->responseUserBytes = responseUserBytes;
|
|
request->bitmap = AtpBufferSizeToBitmap(responseBufferSize,
|
|
atpInfo->maximumSinglePacketDataSize);
|
|
for (index = 0; index < AtpMaximumResponsePackets; index += 1)
|
|
request->packetsIn[index].received = False;
|
|
request->retryInterval = (short)retryInterval;
|
|
request->retryCount = (short)retryCount;
|
|
request->trelTimerValue = trelTimerValue;
|
|
|
|
/* Enqueue it. */
|
|
|
|
request->next = atpInfo->sendRequestQueue;
|
|
atpInfo->sendRequestQueue = request;
|
|
|
|
/* Arm the retry timer, and we're finished. */
|
|
|
|
request->id = additionalData.id = nextId++;
|
|
additionalData.socket = request->sourceSocket;
|
|
additionalData.pointer = (char *)request;
|
|
request->timerId = StartTimer(AtpRequestTimerExpired,
|
|
retryInterval,
|
|
sizeof(AdditionalData),
|
|
(char *)&additionalData);
|
|
|
|
/* Okay, send the request. */
|
|
|
|
AtpTransmitRequest(request);
|
|
|
|
#if VerboseMessages
|
|
printf("AtpPostRequest: from %d to %d:%d:%d; tid = %u.\n",
|
|
sourceSocket,
|
|
destination.networkNumber, destination.nodeNumber,
|
|
destination.socketNumber, transactionId);
|
|
#endif
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} /* AtpPostRequest */
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far AtpEnqueueRequestHandler(
|
|
long far *requestHandlerId,
|
|
long socket, /* "Our" AppleTalk address. */
|
|
void far *opaqueBuffer, /* User "buffer" for request data (may be
|
|
empty). */
|
|
int bufferSize, /* Size, in bytes, of buffer (ignored if
|
|
buffer is empty). */
|
|
char far *userBytes, /* User buffer for ATP "userBytes" (may be
|
|
empty). */
|
|
AtpIncomingRequestHandler *completionRoutine,
|
|
/* Routine to call when request comes in. */
|
|
long unsigned userData) /* Arbitrary data passed on to the completion
|
|
routine. */
|
|
{
|
|
/* Our job in life is simply to enqueue a handler for an incoming ATP
|
|
request to "our" (destination) socket. When a matching request arrives,
|
|
we invoke the supplied completion routine as follows:
|
|
|
|
(*completionRoutine)(errorCode, userData, source, opaqueBuffer,
|
|
bufferSize, userBytes, exactlyOnce,
|
|
trelTimerValue, transactionId, bitmap);
|
|
|
|
The arguments are:
|
|
|
|
errorCode - an AppleTalkErrorCode; either ATnoError or
|
|
ATatpRequestBufferTooSmall. The latter if the
|
|
buffer size specified in the AtpEnqueueRequestHandler
|
|
call could not hold all of the ATP request data. In
|
|
this case, as much data as will fit is returned.
|
|
This also can be ATatpTransactionAborted.
|
|
|
|
userData - a longword; the "userData" that was passed to the
|
|
corresponding AtpEnqueueRequestHandler call.
|
|
|
|
source - an AppleTalkAddress; the internet source of the ATP
|
|
request.
|
|
|
|
buffer - a void*; as passed to AtpEnqueueRequestHandler where
|
|
the ATP request data has been written. If an empty
|
|
pointer is passed to AtpEnqueueRequestHandler, when
|
|
the completion routine is called this argument will
|
|
simply point at the Atp data within the Ddp datagram;
|
|
thus, potential removing a buffer copy. Note that
|
|
if a non-Empty pointer was supplied, "opaque" data
|
|
will be written into user space; an Empty pointer will
|
|
cause a real "char *" to be passed to the completion
|
|
routine.
|
|
|
|
bufferSize - an int; the number of bytes stored in the location
|
|
pointed to by "buffer".
|
|
|
|
userBytes - a char*; as passed to AtpEnqueueRequestHandler where
|
|
the ATP "user bytes" have been written. If an empty
|
|
pointer is passed to AtpEnqueueRequestHandler, when
|
|
the completion routine is called this argument will
|
|
simply point at the Atp user bytes within the Ddp
|
|
datagram; thus, potentially removing a small buffer
|
|
copy.
|
|
|
|
exactlyOnce - an int; "1" if exactly-once mode was requested, "0"
|
|
otherwise.
|
|
|
|
trelTimerValue- TRelTimerValue; how long should we expect to wait for
|
|
a release.
|
|
|
|
transactionId - a short unsigned int; the ATP transaction ID of the
|
|
request.
|
|
|
|
bitmap - a short unsigned int; the ATP response bitmap of the
|
|
request.
|
|
*/
|
|
|
|
AtpInfo atpInfo;
|
|
AtpReceiveRequest requestHandler;
|
|
Boolean inUse;
|
|
AppleTalkErrorCode error = ATnoError;
|
|
|
|
|
|
/* Okay, we're going to enqueue the request handler, defer packets... */
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
do
|
|
{
|
|
/* Find out atpInfo. */
|
|
atpInfo = FindAtpInfoFor(socket);
|
|
if (atpInfo is empty)
|
|
{
|
|
error = ATsocketNotOpen;
|
|
break;
|
|
}
|
|
|
|
if (bufferSize < 0)
|
|
{
|
|
error = ATatpBadBufferSize;
|
|
break;
|
|
}
|
|
|
|
if (completionRoutine is empty)
|
|
{
|
|
error = ATatpCompletionRoutineRequired;
|
|
break;
|
|
}
|
|
|
|
/* Pick a new requestHandlerId. */
|
|
inUse = True;
|
|
while(inUse)
|
|
{
|
|
inUse = False;
|
|
if ((atpInfo->lastRequestHandlerId += 1) < 0)
|
|
{
|
|
atpInfo->lastRequestHandlerId = 0;
|
|
}
|
|
|
|
for (requestHandler = atpInfo->receiveRequestQueue;
|
|
requestHandler isnt empty;
|
|
requestHandler = requestHandler->next)
|
|
{
|
|
if (requestHandler->requestHandlerId is atpInfo->lastRequestHandlerId)
|
|
inUse = True;
|
|
}
|
|
}
|
|
|
|
/* Allocate and enqueue the request handler. */
|
|
requestHandler = (AtpReceiveRequest)Malloc(sizeof(*requestHandler));
|
|
if (requestHandler is empty)
|
|
{
|
|
error = ATatpCouldNotEnqueueRequest;
|
|
break;
|
|
}
|
|
|
|
requestHandler->requestHandlerId = atpInfo->lastRequestHandlerId;
|
|
requestHandler->completionRoutine = completionRoutine;
|
|
requestHandler->userData = userData;
|
|
requestHandler->opaqueBuffer = opaqueBuffer;
|
|
requestHandler->bufferSize = (short)bufferSize;
|
|
requestHandler->userBytes = userBytes;
|
|
requestHandler->next = atpInfo->receiveRequestQueue;
|
|
atpInfo->receiveRequestQueue = requestHandler;
|
|
if (requestHandlerId isnt empty)
|
|
*requestHandlerId = requestHandler->requestHandlerId;
|
|
|
|
} while (FALSE);
|
|
|
|
// Assert that the error is right.
|
|
ASSERT(error == ATnoError);
|
|
|
|
/* Okay, the deed is done. */
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(error);
|
|
|
|
} /* AtpEnqueueRequestHandler */
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far AtpPostResponse(
|
|
long sourceSocket, /* "Our" AppleTalk address. */
|
|
AppleTalkAddress destination, /* Who are sending the response too? */
|
|
short unsigned transactionId, /* What request are we responding too? */
|
|
void far *responseOpaqueBuffer, /* Response data. */
|
|
int responseBufferSize, /* Size, in bytes, of buffer. */
|
|
char far *responseUserBytes, /* Response "user bytes", may be empty. */
|
|
Boolean exactlyOnce, /* Response mode. */
|
|
AtpIncomingReleaseHandler *completionRoutine,
|
|
/* Completion routine; called after xmit
|
|
in non-exactly once mode; after release
|
|
for exactly once mode; may be empty. */
|
|
long unsigned userData) /* User data passed to completionRoutine. */
|
|
{
|
|
/* If the completion rotuine is supplied, it is called as follows:
|
|
|
|
(*completionRoutine)(errorCode, userData, source, transactionId);
|
|
|
|
The arguments are:
|
|
|
|
errorCode - an AppleTalkErrorCode; either ATnoError or
|
|
ATatpNoRelease.
|
|
|
|
userData - a longword; the "userData" that was passed to the
|
|
corresponding AtpSendResponse call.
|
|
|
|
source - an AppleTalkAddress; where did the release come
|
|
from? The source of the request.
|
|
|
|
transactionId - a short unsinged int; as passed to AtpSendResponse.
|
|
|
|
The "completionRotuine" (if supplied) will be called before AtpSendResponse
|
|
returns in non-exactly once mode. */
|
|
|
|
StaticForSmallStack AtpSendResponse response;
|
|
StaticForSmallStack AtpInfo atpInfo;
|
|
StaticForSmallStack short unsigned bitmap;
|
|
StaticForSmallStack AdditionalData additionalData;
|
|
StaticForSmallStack Boolean explicitZero;
|
|
|
|
/* We're mucking with the databases... */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
atpInfo = FindAtpInfoFor(sourceSocket);
|
|
if (atpInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
if (responseBufferSize < 0 or
|
|
responseBufferSize > (atpInfo->maximumSinglePacketDataSize *
|
|
AtpMaximumResponsePackets))
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpResponseTooBig);
|
|
}
|
|
|
|
/* If we're exactly once mode, we should have a pending sendResponse structure
|
|
created when the request came in. */
|
|
|
|
if (exactlyOnce)
|
|
{
|
|
for (response = atpInfo->sendResponseQueue;
|
|
response isnt empty;
|
|
response = response->next)
|
|
if (transactionId is response->transactionId and
|
|
sourceSocket is response->sourceSocket and
|
|
AppleTalkAddressesEqual(&destination, &response->destination))
|
|
break;
|
|
if (response is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpNoMatchingTransaction);
|
|
}
|
|
if (responseBufferSize >
|
|
AtpBitmapToBufferSize(response->bitmap,
|
|
atpInfo->maximumSinglePacketDataSize))
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpResponseTooBig); /* In this case, we'll let our caller
|
|
try again with a smaller buffer,
|
|
if that doesn't happen the response
|
|
will get timed out when the release
|
|
timer goes off. */
|
|
}
|
|
if (response->responseBufferSize isnt AtpNoResponseKnownYet)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpAlreadyRespondedTo);
|
|
}
|
|
|
|
/* Okay, all looks to be fine, fill in the needed field of the response
|
|
structure. */
|
|
|
|
response->completionRoutine = completionRoutine;
|
|
response->userData = userData;
|
|
bitmap = response->bitmap;
|
|
explicitZero = response->explicitZero;
|
|
if (responseUserBytes isnt empty)
|
|
MoveMem(response->responseUserBytes, responseUserBytes,
|
|
AtpUserBytesSize);
|
|
else
|
|
FillMem(response->responseUserBytes, 0, AtpUserBytesSize);
|
|
response->responseOpaqueBuffer = responseOpaqueBuffer;
|
|
response->responseBufferSize = (short)responseBufferSize;
|
|
}
|
|
else
|
|
{
|
|
bitmap = AtpBufferSizeToBitmap(responseBufferSize,
|
|
atpInfo->maximumSinglePacketDataSize);
|
|
explicitZero = (bitmap is 0);
|
|
}
|
|
|
|
/* If exactly once mode, restart the release timer. */
|
|
|
|
if (exactlyOnce)
|
|
{
|
|
CancelTimer(response->timerId);
|
|
additionalData.id = response->id;
|
|
additionalData.socket = response->sourceSocket;
|
|
additionalData.pointer = (char *)response;
|
|
response->timerId = StartTimer(AtpReleaseTimerExpired,
|
|
trelTimerSeconds[response->
|
|
trelTimerValue],
|
|
sizeof(AdditionalData),
|
|
(char *)&additionalData);
|
|
}
|
|
|
|
/* Okay, send the response. */
|
|
|
|
AtpTransmitResponse(sourceSocket, destination, transactionId,
|
|
responseOpaqueBuffer, responseBufferSize,
|
|
responseUserBytes, bitmap, explicitZero,
|
|
atpInfo->maximumSinglePacketDataSize);
|
|
|
|
#if VerboseMessages
|
|
printf("AtpPostResponse: from %d to %d:%d:%d; tid = %u.\n",
|
|
sourceSocket,
|
|
destination.networkNumber, destination.nodeNumber,
|
|
destination.socketNumber, transactionId);
|
|
#endif
|
|
|
|
/* If we're not exactly once mode, and we have a completion routine, call
|
|
it. */
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (not exactlyOnce and completionRoutine isnt empty)
|
|
(*completionRoutine)(ATnoError, userData, destination, transactionId);
|
|
|
|
/* All set... */
|
|
|
|
return(ATnoError);
|
|
|
|
} /* AtpPostResponse */
|
|
|
|
#if Iam an OS2
|
|
#pragma optimize ("eg", on)
|
|
#endif
|
|
|
|
AppleTalkErrorCode far AtpCancelRequestHandler(
|
|
long socket, /* AppleTalk address on which the request
|
|
handler originated. */
|
|
long requestHandlerId, /* Transaction ID of the request. */
|
|
Boolean cancelDueToClose) /* All external caller's should pass "False;"
|
|
Atp will internally use True or False. */
|
|
{
|
|
AtpReceiveRequest requestHandler, previousRequestHandler;
|
|
AtpInfo atpInfo;
|
|
AtpIncomingRequestHandler *incomingRequest;
|
|
long unsigned userData;
|
|
static AppleTalkAddress dummyAddress;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
/* Validate socket. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
atpInfo = FindAtpInfoFor(socket);
|
|
if (atpInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
|
|
/* See if we can find the specified receive request handler. */
|
|
|
|
for (requestHandler = atpInfo->receiveRequestQueue,
|
|
previousRequestHandler = empty;
|
|
requestHandler isnt empty;
|
|
previousRequestHandler = requestHandler,
|
|
requestHandler = requestHandler->next)
|
|
if (requestHandler->requestHandlerId is requestHandlerId)
|
|
break;
|
|
if (requestHandler is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpNoMatchingTransaction);
|
|
}
|
|
|
|
/* Okay, dequeue the request handler. */
|
|
|
|
if (previousRequestHandler is empty)
|
|
atpInfo->receiveRequestQueue = requestHandler->next;
|
|
else
|
|
previousRequestHandler->next = requestHandler->next;
|
|
|
|
/* We want to call the completion routine, so pull out the data before
|
|
freeing the request handler. */
|
|
|
|
incomingRequest = requestHandler->completionRoutine;
|
|
userData = requestHandler->userData;
|
|
Free(requestHandler);
|
|
|
|
/* All set! */
|
|
|
|
errorCode = (cancelDueToClose ? ATsocketClosed : ATatpTransactionAborted);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*incomingRequest)(errorCode, userData, dummyAddress, empty,
|
|
0, empty, False, 0, 0, 0);
|
|
return(ATnoError);
|
|
|
|
} /* AtpCancelRequestHandler */
|
|
|
|
AppleTalkErrorCode far AtpCancelRequest(
|
|
long socket, /* AppleTalk address on which the request
|
|
originated. */
|
|
short unsigned transactionId, /* Transaction ID of the request. */
|
|
Boolean cancelDueToClose) /* All external caller's should pass "False;"
|
|
Atp will internally use True or False. */
|
|
{
|
|
AtpSendRequest request, previousRequest;
|
|
AtpInfo atpInfo;
|
|
AtpIncomingResponseHandler *incomingResponse;
|
|
long unsigned userData;
|
|
AppleTalkAddress destination;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
/* Validate socket. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
atpInfo = FindAtpInfoFor(socket);
|
|
if (atpInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
|
|
/* Can we find the specified response control block? */
|
|
|
|
for (previousRequest = empty,
|
|
request = atpInfo->sendRequestQueue;
|
|
request isnt empty;
|
|
previousRequest = request,
|
|
request = request->next)
|
|
if (request->transactionId is transactionId)
|
|
break;
|
|
if (request is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpNoMatchingTransaction);
|
|
}
|
|
|
|
/* Unthread the request from the queue. */
|
|
|
|
if (previousRequest is empty)
|
|
atpInfo->sendRequestQueue = request->next;
|
|
else
|
|
previousRequest->next = request->next;
|
|
CancelTimer(request->timerId);
|
|
|
|
/* Call the completion routine with an abort. */
|
|
|
|
incomingResponse = request->completionRoutine;
|
|
userData = request->userData;
|
|
destination = request->destination;
|
|
Free(request);
|
|
errorCode = (cancelDueToClose ? ATsocketClosed : ATatpTransactionAborted);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (incomingResponse isnt empty)
|
|
(*incomingResponse)(errorCode, userData, destination,
|
|
empty, 0, empty, 0);
|
|
|
|
/* All set. */
|
|
|
|
return(ATnoError);
|
|
|
|
} /* AtpCancelRequest */
|
|
|
|
AppleTalkErrorCode far AtpCancelResponse(
|
|
long socket, /* AppleTalk address on which the response
|
|
originated (or should originate);
|
|
destination of request. */
|
|
AppleTalkAddress destination, /* AppleTalk address of the source of the
|
|
request; destination of the response. */
|
|
short unsigned transactionId, /* Transaction ID to cancel. */
|
|
Boolean cancelDueToClose) /* All external caller's should pass "False;"
|
|
Atp will internally use True or False. */
|
|
{
|
|
AtpSendResponse response, previousResponse;
|
|
AtpInfo atpInfo;
|
|
AtpIncomingReleaseHandler *incomingRelease;
|
|
long unsigned userData;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
/* Validate socket. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
atpInfo = FindAtpInfoFor(socket);
|
|
if (atpInfo is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATsocketNotOpen);
|
|
}
|
|
|
|
/* Can we find the specified response control block? */
|
|
|
|
for (previousResponse = empty,
|
|
response = atpInfo->sendResponseQueue;
|
|
response isnt empty;
|
|
previousResponse = response,
|
|
response = response->next)
|
|
if (response->transactionId is transactionId and
|
|
AppleTalkAddressesEqual(&destination, &response->destination))
|
|
break;
|
|
if (response is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATatpNoMatchingTransaction);
|
|
}
|
|
|
|
/* Unthread the response from the queue. */
|
|
|
|
if (previousResponse is empty)
|
|
atpInfo->sendResponseQueue = response->next;
|
|
else
|
|
previousResponse->next = response->next;
|
|
CancelTimer(response->timerId);
|
|
|
|
/* Call the completion routine with an abort. */
|
|
|
|
incomingRelease = response->completionRoutine;
|
|
userData = response->userData;
|
|
Free(response);
|
|
errorCode = (cancelDueToClose ? ATsocketClosed : ATatpTransactionAborted);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (incomingRelease isnt empty)
|
|
(*incomingRelease)(errorCode, userData, destination, 0);
|
|
|
|
/* All set. */
|
|
|
|
return(ATnoError);
|
|
|
|
} /* AtpCancelResponse */
|
|
|
|
void _near DeferAtpPackets(void)
|
|
{
|
|
|
|
EnterCriticalSection();
|
|
deferIncomingAtpPacketsCount += 1;
|
|
LeaveCriticalSection();
|
|
|
|
return;
|
|
|
|
} /* DeferAtpPackets */
|
|
|
|
void _near HandleAtpPackets(void)
|
|
{
|
|
DeferredPacket deferredPacket;
|
|
|
|
EnterCriticalSection();
|
|
ASSERT(deferIncomingAtpPacketsCount != 0);
|
|
|
|
/* Decrement defer count. */
|
|
|
|
deferIncomingAtpPacketsCount -= 1;
|
|
|
|
/* If we're no longer defering packets, handle any queued ones. */
|
|
|
|
do {
|
|
if (deferIncomingAtpPacketsCount is 0)
|
|
{
|
|
if (handleInProgress)
|
|
break;
|
|
else
|
|
{
|
|
handleInProgress = TRUE;
|
|
}
|
|
|
|
while(headOfDeferredPacketList isnt empty)
|
|
{
|
|
deferredPacket = headOfDeferredPacketList;
|
|
headOfDeferredPacketList = headOfDeferredPacketList->next;
|
|
if (headOfDeferredPacketList is empty)
|
|
tailOfDeferredPacketList = empty;
|
|
if ((currentDeferredPacketCount -= 1) < 0)
|
|
{
|
|
ErrorLog("HandleAtpPackets", ISevError, __LINE__, UnknownPort,
|
|
IErrAtpBadDeferCount, IMsgAtpBadDeferCount,
|
|
Insert0());
|
|
currentDeferredPacketCount = 0;
|
|
}
|
|
LeaveCriticalSection();
|
|
AtpPacketIn(deferredPacket->errorCode, (long)0,
|
|
deferredPacket->port, deferredPacket->source,
|
|
deferredPacket->destinationSocket, DdpProtocolAtp,
|
|
deferredPacket->datagram, deferredPacket->datagramLength,
|
|
deferredPacket->actualDestination);
|
|
Free(deferredPacket);
|
|
EnterCriticalSection();
|
|
}
|
|
|
|
handleInProgress = FALSE;
|
|
break;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
LeaveCriticalSection();
|
|
return;
|
|
|
|
} /* HandleAtpPackets */
|
|
|
|
ExternForVisibleFunction long far
|
|
AtpPacketIn(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
int port,
|
|
AppleTalkAddress source,
|
|
long destinationSocket,
|
|
int protocolType,
|
|
char far *datagram,
|
|
int datagramLength,
|
|
AppleTalkAddress actualDestination)
|
|
{
|
|
StaticForSmallStack AtpInfo atpInfo;
|
|
StaticForSmallStack short unsigned functionCode, sequenceNumber;
|
|
StaticForSmallStack Boolean endOfMessage, sendTransactionStatus;
|
|
StaticForSmallStack AtpReceiveRequest requestHandler;
|
|
StaticForSmallStack AtpSendResponse response, previousResponse;
|
|
StaticForSmallStack AtpSendRequest request, previousRequest;
|
|
StaticForSmallStack Boolean bufferTooSmall;
|
|
StaticForSmallStack short expectedResponseSize;
|
|
StaticForSmallStack short unsigned index;
|
|
StaticForSmallStack short unsigned correctBit, bitToReset;
|
|
StaticForSmallStack short startOffset, totalBufferSpaceNeeded;
|
|
StaticForSmallStack AdditionalData additionalData;
|
|
StaticForSmallStack DeferredPacket deferredPacket;
|
|
short unsigned bitmap;
|
|
unsigned short transactionId;
|
|
short atpDataSize;
|
|
#if Iam an OS2
|
|
/* Can you spell "real hack"? Good... I knew you could. We really
|
|
need stack space here (in stupid OS/2 land). And, this routine
|
|
really needs to be reentrant... so malloc our dynamic data. Sigh. */
|
|
|
|
struct { AtpIncomingRequestHandler *incomingRequest;
|
|
AtpIncomingResponseHandler *incomingResponse;
|
|
AtpIncomingReleaseHandler *incomingRelease;
|
|
char far *localBuffer, far *localUserBytes;
|
|
} far *localData = Malloc(sizeof(*localData));
|
|
|
|
#define incomingRequest (localData->incomingRequest)
|
|
#define incomingResponse (localData->incomingResponse)
|
|
#define incomingRelease (localData->incomingRelease)
|
|
#define localBuffer (localData->localBuffer)
|
|
#define localUserBytes (localData->localUserBytes)
|
|
|
|
#define FreeTempSpace() Free(localData)
|
|
#else
|
|
AtpIncomingRequestHandler *incomingRequest;
|
|
AtpIncomingResponseHandler *incomingResponse;
|
|
AtpIncomingReleaseHandler *incomingRelease;
|
|
char far *localBuffer, far *localUserBytes;
|
|
|
|
#define FreeTempSpace()
|
|
#endif
|
|
char tempUserBytes[AtpUserBytesSize];
|
|
short actualSize;
|
|
Boolean exactlyOnce;
|
|
TRelTimerValue trelTimerValue;
|
|
|
|
/* Check for incoming errors... */
|
|
|
|
bufferTooSmall = False;
|
|
if (errorCode isnt ATnoError and
|
|
errorCode isnt ATsocketClosed)
|
|
{
|
|
ErrorLog("AtpPacketIn", ISevError, __LINE__, port,
|
|
IErrAtpFunnyIncomingError, IMsgAtpFunnyIncomingError,
|
|
Insert0());
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Should we be defering incoming packets? */
|
|
|
|
EnterCriticalSection();
|
|
if (deferIncomingAtpPacketsCount > 0)
|
|
{
|
|
#if VerboseMessages
|
|
printf("AtpPacketIn: from %d:%d:%d to %d; Deferred.\n",
|
|
source.networkNumber, source.nodeNumber, source.socketNumber,
|
|
destinationSocket);
|
|
#endif
|
|
if (currentDeferredPacketCount is MaximumDeferredPackets)
|
|
{
|
|
LeaveCriticalSection();
|
|
ErrorLog("AtpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAtpLosingData, IMsgAtpLosingData,
|
|
Insert0());
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
}
|
|
LeaveCriticalSection();
|
|
deferredPacket = (DeferredPacket)Malloc(sizeof(*deferredPacket) +
|
|
datagramLength);
|
|
if (deferredPacket is empty)
|
|
{
|
|
ErrorLog("AtpPacketIn", ISevError, __LINE__, port,
|
|
IErrAtpOutOfMemory, IMsgAtpOutOfMemory,
|
|
Insert0());
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Fill in the data strcuture, and place the packet at the end of the
|
|
queue. */
|
|
|
|
deferredPacket->errorCode = errorCode;
|
|
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();
|
|
FreeTempSpace();
|
|
|
|
return((long)True);
|
|
}
|
|
else
|
|
LeaveCriticalSection();
|
|
|
|
/* Well, from hear on in, we're in the thick of it, so defer incoming
|
|
packets. */
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
|
|
if (errorCode is ATsocketClosed)
|
|
{
|
|
/* If we're a result of a AtpCloseSocketOnNode, just ignore it, else
|
|
somehow else the the socket got closed and we should use this
|
|
oportunity to clean up our brains. If an AtpCloseSocketOnNode is
|
|
already in progress, we won't be able to find our ATP info! */
|
|
|
|
if (FindAtpInfoFor(destinationSocket) isnt empty)
|
|
AtpCloseSocketOnNode(destinationSocket);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
|
|
}
|
|
|
|
if (protocolType isnt DdpProtocolAtp)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeTempSpace();
|
|
return((long)True); /* Why are these guys talking to us??? */
|
|
}
|
|
|
|
if (datagramLength < AtpDataOffset)
|
|
{
|
|
ErrorLog("AtpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAtpPacketTooShort, IMsgAtpPacketTooShort,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Find the ATP descriptor for the destination. */
|
|
|
|
if ((atpInfo = FindAtpInfoFor(destinationSocket)) is empty)
|
|
{
|
|
ErrorLog("AtpPacketIn", ISevError, __LINE__, port,
|
|
IErrAtpWhyUs, IMsgAtpWhyUs,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Extract static fields from the ATP header. */
|
|
|
|
functionCode = (short unsigned)(datagram[AtpCommandControlOffset] &
|
|
AtpFunctionCodeMask);
|
|
trelTimerValue = (TRelTimerValue)(datagram[AtpCommandControlOffset] &
|
|
AtpTRelTimerValueMask);
|
|
if (trelTimerValue > LastValidTRelTimerValue)
|
|
{
|
|
trelTimerValue = ThirtySecondsTRelTimer;
|
|
ErrorLog("AtpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAtpFunnyTRelTimerValue, IMsgAtpFunnyTRelTimerValue,
|
|
Insert0());
|
|
}
|
|
exactlyOnce = ((datagram[AtpCommandControlOffset] & AtpExactlyOnceMask)
|
|
isnt 0);
|
|
endOfMessage = ((datagram[AtpCommandControlOffset] & AtpEndOfMessageMask)
|
|
isnt 0);
|
|
sendTransactionStatus = ((datagram[AtpCommandControlOffset] &
|
|
AtpSendTransactionStatusMask) isnt 0);
|
|
bitmap = sequenceNumber = (unsigned char)datagram[AtpBitmapOffset];
|
|
transactionId = (unsigned short)
|
|
(((unsigned char)datagram[AtpTransactionIdOffset] << 8)
|
|
+ (unsigned char)datagram[AtpTransactionIdOffset + 1]);
|
|
atpDataSize = (short)(datagramLength - AtpDataOffset);
|
|
|
|
if (atpDataSize > atpInfo->maximumSinglePacketDataSize)
|
|
{
|
|
ErrorLog("AtpPacketIn", ISevVerbose, __LINE__, port,
|
|
IErrAtpTooMuchData, IMsgAtpTooMuchData,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Okay, do the right thing based on the ATP function code. */
|
|
|
|
switch(functionCode)
|
|
{
|
|
case AtpRequestFunctionCode:
|
|
|
|
#if VerboseMessages
|
|
printf("AtpPacketIn [Request]: from %d:%d:%d to %d; tid = %u.\n",
|
|
source.networkNumber, source.nodeNumber, source.socketNumber,
|
|
destinationSocket, transactionId);
|
|
#endif
|
|
|
|
/* First, check to see if this is a request for a response that we
|
|
already have queued and are awaiting a release for. */
|
|
|
|
if (exactlyOnce)
|
|
for (response = atpInfo->sendResponseQueue;
|
|
response isnt empty;
|
|
response = response->next)
|
|
{
|
|
if (transactionId isnt response->transactionId or
|
|
not AppleTalkAddressesEqual(&source,
|
|
&response->destination) or
|
|
destinationSocket isnt response->sourceSocket)
|
|
continue;
|
|
|
|
/* We know about this request already... re-transmit the response
|
|
and restart the release timer. */
|
|
|
|
CancelTimer(response->timerId);
|
|
additionalData.id = response->id;
|
|
additionalData.socket = response->sourceSocket;
|
|
additionalData.pointer = (char *)response;
|
|
response->timerId = StartTimer(AtpReleaseTimerExpired,
|
|
trelTimerSeconds[response->
|
|
trelTimerValue],
|
|
sizeof(AdditionalData),
|
|
(char *)&additionalData);
|
|
response->bitmap = bitmap;
|
|
#if VerboseMessages
|
|
printf("AtpPacketIn [Request]: already know response.\n");
|
|
#endif
|
|
AtpTransmitResponse(destinationSocket, source, transactionId,
|
|
response->responseOpaqueBuffer,
|
|
response->responseBufferSize,
|
|
response->responseUserBytes,
|
|
bitmap,
|
|
response->explicitZero,
|
|
atpInfo->maximumSinglePacketDataSize);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Okay, do we have a queued request handler? */
|
|
|
|
if ((requestHandler = atpInfo->receiveRequestQueue) is empty)
|
|
#if VerboseMessages
|
|
printf("AtpPacketIn [Request]: ignored; no request handler.\n");
|
|
#else
|
|
;
|
|
#endif
|
|
else
|
|
{
|
|
/* Decode the response bitmap... */
|
|
|
|
if ((expectedResponseSize =
|
|
AtpBitmapToBufferSize(bitmap,
|
|
atpInfo->maximumSinglePacketDataSize)) < 0)
|
|
{
|
|
#if VerboseMessages
|
|
printf("AtpPacketIn [Request]: bad bitmap.\n");
|
|
#endif
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Okay, if the request is in exactly-once mode, build a send-
|
|
response structure for this beast. Set response data size
|
|
to indicate that we don't have a response yet. Start the
|
|
release timer. */
|
|
|
|
if (exactlyOnce)
|
|
{
|
|
if ((response = (AtpSendResponse)Calloc(sizeof(*response), 1))
|
|
is empty)
|
|
{
|
|
ErrorLog("AtpPacketIn", ISevError, __LINE__, port,
|
|
IErrAtpOutOfMemory, IMsgAtpOutOfMemory,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
}
|
|
else
|
|
{
|
|
response->sourceSocket = destinationSocket;
|
|
response->destination = source;
|
|
response->transactionId = transactionId;
|
|
response->trelTimerValue = trelTimerValue;
|
|
response->bitmap = bitmap;
|
|
response->explicitZero = (bitmap is 0);
|
|
response->responseBufferSize = AtpNoResponseKnownYet;
|
|
response->completionRoutine = empty;
|
|
response->next = atpInfo->sendResponseQueue;
|
|
atpInfo->sendResponseQueue = response;
|
|
response->id = additionalData.id = nextId++;
|
|
additionalData.socket = response->sourceSocket;
|
|
additionalData.pointer = (char *)response;
|
|
response->timerId = StartTimer(AtpReleaseTimerExpired,
|
|
trelTimerSeconds[trelTimerValue],
|
|
sizeof(AdditionalData),
|
|
(char *)&additionalData);
|
|
}
|
|
}
|
|
|
|
/* Unthread the request handler from the queue and prepare
|
|
to activate the handler. */
|
|
|
|
atpInfo->receiveRequestQueue = requestHandler->next;
|
|
|
|
/* Move the data into user space, if our called allocated space
|
|
for this... otherwise, just pas along pointers into the Ddp
|
|
datagram. */
|
|
|
|
if (requestHandler->userBytes is empty)
|
|
localUserBytes = &datagram[AtpUserBytesOffset];
|
|
else
|
|
{
|
|
MoveMem(requestHandler->userBytes, &datagram[AtpUserBytesOffset],
|
|
AtpUserBytesSize);
|
|
localUserBytes = requestHandler->userBytes;
|
|
}
|
|
|
|
if (requestHandler->opaqueBuffer is empty)
|
|
localBuffer = &datagram[AtpDataOffset];
|
|
else
|
|
{
|
|
if (atpDataSize > requestHandler->bufferSize)
|
|
{
|
|
bufferTooSmall = True;
|
|
if (atpDataSize isnt 0)
|
|
MoveToOpaque(requestHandler->opaqueBuffer, 0,
|
|
&datagram[AtpDataOffset],
|
|
requestHandler->bufferSize);
|
|
atpDataSize = requestHandler->bufferSize;
|
|
}
|
|
else if (atpDataSize isnt 0)
|
|
MoveToOpaque(requestHandler->opaqueBuffer, 0,
|
|
&datagram[AtpDataOffset], atpDataSize);
|
|
localBuffer = requestHandler->opaqueBuffer;
|
|
}
|
|
|
|
/* We're about to free the request handler structure, so pull
|
|
out the information that we need to call the completion
|
|
routine with. */
|
|
|
|
incomingRequest = requestHandler->completionRoutine;
|
|
userData = requestHandler->userData;
|
|
|
|
/* Free the request handler, and invoke the completion routine. */
|
|
|
|
Free(requestHandler);
|
|
if (bufferTooSmall)
|
|
errorCode = ATatpRequestBufferTooSmall;
|
|
else
|
|
errorCode = ATnoError;
|
|
#if VerboseMessages
|
|
printf("AtpPacketIn [Request]: calling completion routine.\n");
|
|
#endif
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*incomingRequest)(errorCode, userData, source, localBuffer,
|
|
atpDataSize, localUserBytes, exactlyOnce,
|
|
trelTimerValue, transactionId, bitmap);
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
}
|
|
break;
|
|
|
|
case AtpResponseFunctionCode:
|
|
|
|
#if VerboseMessages
|
|
printf("AtpPacketIn [Response]: from %d:%d:%d to %d; tid = %u.\n",
|
|
source.networkNumber, source.nodeNumber, source.socketNumber,
|
|
destinationSocket, transactionId);
|
|
#endif
|
|
|
|
/* Okay, do we have a pending request to match this response? */
|
|
|
|
for (previousRequest = empty,
|
|
request = atpInfo->sendRequestQueue;
|
|
request isnt empty;
|
|
previousRequest = request,
|
|
request = request->next)
|
|
if (transactionId is request->transactionId and
|
|
AppleTalkAddressesEqual(&source, &request->destination))
|
|
break;
|
|
if (request is empty)
|
|
{
|
|
ErrorLog("AtpPacketIn", ISevVerbose, __LINE__, port,
|
|
IErrAtpRespToDeadReq, IMsgAtpRespToDeadReq,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* If we're the first response and the user cares about "userBytes",
|
|
copy these for return. Save a local copy too, in case the user
|
|
didn't supply us a buffer to hold 'em. */
|
|
|
|
if (sequenceNumber is 0 and request->responseUserBytes isnt empty)
|
|
MoveMem(request->responseUserBytes, &datagram[AtpUserBytesOffset],
|
|
AtpUserBytesSize);
|
|
if (sequenceNumber is 0)
|
|
MoveMem(request->tempResponseUserBytes,
|
|
&datagram[AtpUserBytesOffset],
|
|
AtpUserBytesSize);
|
|
|
|
/* The bitmap could be zero now, if the user only wanted the userBytes
|
|
and not response buffer... */
|
|
|
|
if (request->bitmap isnt 0)
|
|
{
|
|
/* Do we want this response? Is the corresponding bit in our current
|
|
bitmap set? */
|
|
|
|
if (sequenceNumber > AtpMaximumResponsePackets-1)
|
|
{
|
|
ErrorLog("AtPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAtpOutOfSequence, IMsgAtpOutOfSequence,
|
|
Insert0());
|
|
break;
|
|
}
|
|
correctBit = 1;
|
|
for (index = 0; index < sequenceNumber; index += 1)
|
|
correctBit <<= 1;
|
|
if ((request->bitmap & correctBit) is 0)
|
|
break; /* We don't care... */
|
|
|
|
/* Okay, it's a response we want, clear the bit in the request
|
|
bitmap. */
|
|
|
|
request->bitmap &= (unsigned short)~correctBit;
|
|
|
|
/* Okay, we have to move the data into the user's buffer space, is
|
|
there room? */
|
|
|
|
startOffset = (short)(sequenceNumber *
|
|
atpInfo->maximumSinglePacketDataSize);
|
|
totalBufferSpaceNeeded = (short)(startOffset + atpDataSize);
|
|
if (request->responseBufferSize < totalBufferSpaceNeeded)
|
|
{
|
|
/* This should be a rare case; packet was within bitmap limits,
|
|
but still wouldn't fit into user space. The other way this
|
|
could occure is if the responder is sending less than full
|
|
responses -- we don't "synch" up the user buffer until all
|
|
packets hace been received.
|
|
|
|
We want to give up now, call the completion rotuine signaling
|
|
the error -- unthread and free the request control block --
|
|
cancel the retry timer. */
|
|
|
|
incomingResponse = request->completionRoutine;
|
|
userData = request->userData;
|
|
if (previousRequest is empty)
|
|
atpInfo->sendRequestQueue = request->next;
|
|
else
|
|
previousRequest->next = request->next;
|
|
CancelTimer(request->timerId);
|
|
Free(request);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (incomingResponse isnt empty)
|
|
(*incomingResponse)(ATatpResponseBufferTooSmall, userData,
|
|
source, empty, 0, empty, 0);
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
}
|
|
|
|
/* Okay, we have room to copy the data into user space. */
|
|
|
|
MoveToOpaque(request->responseOpaqueBuffer, startOffset,
|
|
&datagram[AtpDataOffset], atpDataSize);
|
|
request->packetsIn[sequenceNumber].received = True;
|
|
request->packetsIn[sequenceNumber].dataSize = atpDataSize;
|
|
|
|
/* If the "endOfMessage" bit is set, we need to reset all higher
|
|
order bits in the bitmap. */
|
|
|
|
if (endOfMessage)
|
|
for (bitToReset = (unsigned short)(sequenceNumber + 1);
|
|
bitToReset < AtpMaximumResponsePackets;
|
|
bitToReset += 1)
|
|
{
|
|
request->packetsIn[bitToReset].received = False;
|
|
correctBit = 1;
|
|
for (index = 0; index < bitToReset; index += 1)
|
|
correctBit <<= 1;
|
|
request->bitmap &= (unsigned short)~correctBit;
|
|
}
|
|
|
|
/* If the "send transaction status" bit is set, do it, and reset the
|
|
the retry timer. */
|
|
|
|
if (sendTransactionStatus)
|
|
{
|
|
CancelTimer(request->timerId);
|
|
additionalData.id = request->id;
|
|
additionalData.socket = request->sourceSocket;
|
|
additionalData.pointer = (char *)request;
|
|
request->timerId = StartTimer(AtpRequestTimerExpired,
|
|
request->retryInterval,
|
|
sizeof(AdditionalData),
|
|
(char *)&additionalData);
|
|
AtpTransmitRequest(request);
|
|
}
|
|
}
|
|
|
|
/* If the bitmap is non-zero, we are still awaiting more responses. */
|
|
|
|
if (request->bitmap isnt 0)
|
|
break;
|
|
|
|
/* Great! We're got the entire response. We must:
|
|
1. Cancel the retry timer.
|
|
2. Send a release (if in exactly once mode).
|
|
3. Synch of the user buffer, if needed.
|
|
4. Unthread and free the request control block.
|
|
5. Call the completion routine.
|
|
*/
|
|
|
|
CancelTimer(request->timerId);
|
|
if (request->exactlyOnce)
|
|
AtpTransmitRelease(request);
|
|
actualSize =
|
|
AtpSynchUpResponseBuffer(request,
|
|
atpInfo->maximumSinglePacketDataSize);
|
|
if (previousRequest is empty)
|
|
atpInfo->sendRequestQueue = request->next;
|
|
else
|
|
previousRequest->next = request->next;
|
|
|
|
/* Pull data out of the request control block, so we can free it
|
|
before the call. */
|
|
|
|
incomingResponse = request->completionRoutine;
|
|
userData = request->userData;
|
|
localBuffer = request->responseOpaqueBuffer;
|
|
localUserBytes = request->responseUserBytes;
|
|
if (localUserBytes is Empty)
|
|
{
|
|
MoveMem(tempUserBytes, request->tempResponseUserBytes,
|
|
AtpUserBytesSize);
|
|
localUserBytes = tempUserBytes;
|
|
}
|
|
|
|
/* Okay, free the control block and call the completion routine. */
|
|
|
|
Free(request);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (incomingResponse isnt empty)
|
|
(*incomingResponse)(ATnoError, userData, source, localBuffer,
|
|
actualSize, localUserBytes, transactionId);
|
|
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
|
|
case AtpReleaseFunctionCode:
|
|
|
|
#if VerboseMessages
|
|
printf("AtpPacketIn [Release]: from %d:%d:%d to %d; tid = %u.\n",
|
|
source.networkNumber, source.nodeNumber, source.socketNumber,
|
|
destinationSocket, transactionId);
|
|
#endif
|
|
|
|
/* Search our send-response queue for the current transaction; if not
|
|
found, ignore the release. */
|
|
|
|
for (previousResponse = empty,
|
|
response = atpInfo->sendResponseQueue;
|
|
response isnt empty;
|
|
previousResponse = response,
|
|
response = response->next)
|
|
if (transactionId is response->transactionId and
|
|
AppleTalkAddressesEqual(&source, &response->destination) and
|
|
destinationSocket is response->sourceSocket)
|
|
break;
|
|
if (response is empty)
|
|
{
|
|
ErrorLog("AtpPacketIn", ISevVerbose, __LINE__, port,
|
|
IErrAtpDeadRelease, IMsgAtpDeadRelease,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
/* Okay, unthread the response, cancel the release timer, and free
|
|
the structure. Call response completion routine, if needed. */
|
|
|
|
if (previousResponse is empty)
|
|
atpInfo->sendResponseQueue = response->next;
|
|
else
|
|
previousResponse->next = response->next;
|
|
CancelTimer(response->timerId);
|
|
incomingRelease = response->completionRoutine;
|
|
userData = response->userData;
|
|
Free(response);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (incomingRelease isnt empty)
|
|
(*incomingRelease)(ATnoError, userData, source, transactionId);
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
|
|
default:
|
|
|
|
#if VerboseMessages
|
|
printf("AtpPacketIn [Bad]: from %d:%d:%d to %d; tid = %u.\n",
|
|
source.networkNumber, source.nodeNumber, source.socketNumber,
|
|
destinationSocket, transactionId);
|
|
#endif
|
|
|
|
break;
|
|
|
|
} /* Switch on functionCode */
|
|
|
|
/* We're finished with this puppy... */
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
FreeTempSpace();
|
|
return((long)True);
|
|
|
|
#if Iam an OS2
|
|
#undef incomingRequest
|
|
#undef incomingResponse
|
|
#undef incomingRelease
|
|
#undef localBuffer
|
|
#undef localUserBytes
|
|
#endif
|
|
#undef FreeTempSpace
|
|
|
|
} /* AtpPacketIn */
|
|
|
|
ExternForVisibleFunction void
|
|
AtpTransmitResponse(long sourceSocket,
|
|
AppleTalkAddress destination,
|
|
short unsigned transactionId,
|
|
void far *responseOpaqueBuffer,
|
|
int responseBufferSize,
|
|
char far *responseUserBytes,
|
|
short unsigned bitmap,
|
|
Boolean explicitZero,
|
|
short maximumSinglePacketDataSize)
|
|
{
|
|
StaticForSmallStack BufferDescriptor datagram;
|
|
StaticForSmallStack int remainingBytes;
|
|
StaticForSmallStack short unsigned int sequenceNumber;
|
|
StaticForSmallStack short unsigned int currentBit;
|
|
StaticForSmallStack short bytesInPacket;
|
|
StaticForSmallStack short bytesSent;
|
|
StaticForSmallStack Boolean firstLoop, error;
|
|
|
|
bytesSent = 0;
|
|
sequenceNumber = 0;
|
|
currentBit = 1;
|
|
firstLoop = True;
|
|
error = False;
|
|
|
|
if (responseBufferSize is AtpNoResponseKnownYet)
|
|
{
|
|
#if VerboseMessages
|
|
printf("AtpTransmitResponse: client hasn't supplied data yet.\n");
|
|
#endif
|
|
return; /* 2nd request before client responded, ignore it. */
|
|
}
|
|
|
|
/* Send each response packet that is needed. */
|
|
|
|
remainingBytes = responseBufferSize;
|
|
while(remainingBytes > 0 or (firstLoop and remainingBytes is 0))
|
|
{
|
|
firstLoop = False;
|
|
if ((bitmap & currentBit) isnt 0 or
|
|
(sequenceNumber is 0 and explicitZero)) /* Do they want this chunk? */
|
|
{
|
|
bytesInPacket = (short)((remainingBytes > maximumSinglePacketDataSize) ?
|
|
maximumSinglePacketDataSize :
|
|
remainingBytes);
|
|
|
|
/* Depending on the nature of transmit completion, either reference
|
|
or copy the user response buffer. */
|
|
|
|
#if TransmitsCompleteSynchronously
|
|
if (bytesInPacket > 0)
|
|
{
|
|
Boolean freeOpaqueDataDescriptor;
|
|
void far *opaqueBuffer;
|
|
|
|
if ((opaqueBuffer =
|
|
SubsetOpaqueDataDescriptor(responseOpaqueBuffer, bytesSent,
|
|
bytesInPacket,
|
|
&freeOpaqueDataDescriptor))
|
|
is Empty)
|
|
error = True;
|
|
|
|
if (not error and
|
|
(datagram = DescribeBuffer(bytesInPacket, opaqueBuffer,
|
|
True)) is Empty)
|
|
{
|
|
if (freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(opaqueBuffer);
|
|
error = True;
|
|
}
|
|
|
|
if (not error)
|
|
datagram->freeOpaqueDataDescriptor = freeOpaqueDataDescriptor;
|
|
}
|
|
else
|
|
datagram = Empty;
|
|
if ((datagram = AllocateHeader(datagram, AtpDataOffset)) is Empty)
|
|
error = True;
|
|
#else
|
|
if ((datagram = NewBufferDescriptor(AtpDataOffset +
|
|
bytesInPacket)) is Empty)
|
|
error = True;
|
|
#endif
|
|
|
|
if (error)
|
|
{
|
|
ErrorLog("AtpTransmitResponse", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAtpOutOfMemory, IMsgAtpOutOfMemory,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* Fill in the ATP header. */
|
|
|
|
datagram->data[AtpCommandControlOffset] = AtpResponseFunctionCode;
|
|
datagram->data[AtpTransactionIdOffset] =
|
|
(char)(((transactionId >> 8) & 0xFF));
|
|
datagram->data[AtpTransactionIdOffset + 1] = (char)(transactionId & 0xFF);
|
|
datagram->data[AtpSequenceNumberOffset] = (char)sequenceNumber;
|
|
|
|
if (responseUserBytes isnt empty)
|
|
MoveMem(datagram->data + AtpUserBytesOffset, responseUserBytes,
|
|
AtpUserBytesSize);
|
|
else
|
|
FillMem(datagram->data + AtpUserBytesOffset, 0, AtpUserBytesSize);
|
|
|
|
/* UserBytes only in first packet! Except for PAP! */
|
|
|
|
if (sequenceNumber > 0 and
|
|
maximumSinglePacketDataSize isnt PapMaximumDataPacketSize)
|
|
FillMem(datagram->data + AtpUserBytesOffset, 0, AtpUserBytesSize);
|
|
|
|
#if not TransmitsCompleteSynchronously
|
|
if (bytesInPacket > 0)
|
|
MoveFromOpaque(datagram->data + AtpDataOffset,
|
|
responseOpaqueBuffer, bytesSent, bytesInPacket);
|
|
#endif
|
|
|
|
if ((remainingBytes -
|
|
maximumSinglePacketDataSize) <= 0) /* Last chunk? */
|
|
datagram->data[AtpCommandControlOffset] |= AtpEndOfMessageMask;
|
|
|
|
#if Debug
|
|
if (DropEveryNthResponse isnt 0 and
|
|
(RandomNumber() % DropEveryNthResponse) is 0)
|
|
{
|
|
FreeBufferChain(datagram);
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
DeliverDdp(sourceSocket, destination, DdpProtocolAtp, datagram,
|
|
AtpDataOffset + bytesInPacket, Empty, Empty, 0);
|
|
}
|
|
|
|
/* Bump positions for next chunk. */
|
|
|
|
sequenceNumber += 1;
|
|
currentBit <<= 1;
|
|
remainingBytes -= maximumSinglePacketDataSize;
|
|
bytesSent += maximumSinglePacketDataSize;
|
|
}
|
|
|
|
/* All set. */
|
|
|
|
return;
|
|
|
|
} /* AtpTransmitResponse */
|
|
|
|
ExternForVisibleFunction short
|
|
AtpBitmapToBufferSize(short unsigned bitmap,
|
|
short maximumSinglePacketDataSize)
|
|
{
|
|
short bitsOn = 0;
|
|
Boolean foundFirstZero = False;
|
|
short bitNumber;
|
|
|
|
bitmap &= 0xFF;
|
|
if (bitmap is 0)
|
|
return(0);
|
|
|
|
/* A seqeunce of low-order bits must be set, we don't allow holes! */
|
|
|
|
for (bitNumber = 1; bitNumber <= 8; bitNumber += 1)
|
|
{
|
|
bitsOn += (short)(bitmap & 1);
|
|
if ((bitmap & 1) is 0)
|
|
foundFirstZero = True;
|
|
else if (foundFirstZero)
|
|
{
|
|
ErrorLog("AtpBitmapToBufferSize", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAtpBadBitmap, IMsgAtpBadBitmap,
|
|
Insert0());
|
|
return(-1);
|
|
}
|
|
bitmap >>= 1;
|
|
}
|
|
|
|
/* Okay, we had a valid bitmap, return the required data size. */
|
|
|
|
return((short)(bitsOn * maximumSinglePacketDataSize));
|
|
|
|
} /* AtpBitmapToBufferSize */
|
|
|
|
ExternForVisibleFunction void far
|
|
AtpReleaseTimerExpired(long unsigned timerId,
|
|
int dataSize,
|
|
char far *incomingAdditionalData)
|
|
{
|
|
StaticForSmallStack AtpSendResponse response, previousResponse,
|
|
nextResponse;
|
|
StaticForSmallStack AtpInfo atpInfo;
|
|
StaticForSmallStack long id;
|
|
StaticForSmallStack long us;
|
|
StaticForSmallStack AdditionalData far *additionalData;
|
|
AtpIncomingReleaseHandler *incomingRelease;
|
|
long unsigned userData;
|
|
short unsigned transactionId;
|
|
AppleTalkAddress destination;
|
|
|
|
/* "Use" unneeded actual parameter. */
|
|
|
|
timerId;
|
|
|
|
additionalData = (AdditionalData far *)incomingAdditionalData;
|
|
|
|
/* Validate the data, it should be a pointer to a response control block. */
|
|
|
|
if (dataSize isnt sizeof(AdditionalData))
|
|
{
|
|
ErrorLog("AtpReleaseTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAtpBadDataSize, IMsgAtpBadDataSize,
|
|
Insert0());
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* We're going to muck with the ATP structures... defer packets. */
|
|
|
|
DeferAtpPackets();
|
|
|
|
response = (AtpSendResponse)(additionalData->pointer);
|
|
id = additionalData->id;
|
|
us = additionalData->socket;
|
|
|
|
/* We need to unthread the SendResponse structure from the OpenSocket. */
|
|
|
|
if ((atpInfo = FindAtpInfoFor(us)) is empty)
|
|
{
|
|
ErrorLog("AtpReleaseTimerExpired", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAtpSocketClosed, IMsgAtpSocketClosed,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
for (previousResponse = empty,
|
|
nextResponse = atpInfo->sendResponseQueue;
|
|
nextResponse isnt empty and
|
|
(id isnt nextResponse->id or
|
|
nextResponse isnt response);
|
|
previousResponse = nextResponse,
|
|
nextResponse = nextResponse->next)
|
|
/* Ring-around-the-rosey */ ;
|
|
if (nextResponse is empty)
|
|
{
|
|
ErrorLog("AtpReleaseTimerExpired", ISevVerbose, __LINE__, UnknownPort,
|
|
IErrAtpMissingResponse, IMsgAtpMissingResponse,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Pull enough data out of the response structure so we can call the
|
|
completion routine, if needed. */
|
|
|
|
incomingRelease = response->completionRoutine;
|
|
userData = response->userData;
|
|
destination = response->destination;
|
|
transactionId = response->transactionId;
|
|
|
|
if (previousResponse is empty)
|
|
atpInfo->sendResponseQueue = response->next;
|
|
else
|
|
previousResponse->next = response->next;
|
|
|
|
/* Okay, free the guy, and we're done! */
|
|
|
|
Free(response);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (incomingRelease isnt empty)
|
|
(*incomingRelease)(ATatpNoRelease, userData, destination, transactionId);
|
|
return;
|
|
|
|
} /* AtpReleaseTimerExpired */
|
|
|
|
ExternForVisibleFunction short unsigned
|
|
AtpBufferSizeToBitmap(int bufferSize,
|
|
short maximumSinglePacketDataSize)
|
|
{
|
|
short unsigned bitmap = 0;
|
|
|
|
while(bufferSize > 0)
|
|
{
|
|
bitmap <<= 1;
|
|
bitmap += 1;
|
|
bufferSize -= maximumSinglePacketDataSize;
|
|
}
|
|
|
|
return(bitmap);
|
|
|
|
} /* AtpBufferSizeToBitmap */
|
|
|
|
ExternForVisibleFunction void AtpTransmitRequest(AtpSendRequest request)
|
|
{
|
|
BufferDescriptor datagram;
|
|
Boolean error = False;
|
|
|
|
/* Depending on the nature of transmit completion, either reference
|
|
or copy the user request buffer. */
|
|
|
|
#if TransmitsCompleteSynchronously
|
|
if (request->requestBufferSize > 0)
|
|
{
|
|
if ((datagram = DescribeBuffer(request->requestBufferSize,
|
|
request->requestOpaqueBuffer,
|
|
True)) is Empty)
|
|
error = True;
|
|
}
|
|
else
|
|
datagram = Empty;
|
|
if ((datagram = AllocateHeader(datagram, AtpDataOffset)) is Empty)
|
|
error = True;
|
|
#else
|
|
if ((datagram = NewBufferDescriptor(AtpDataOffset +
|
|
request->requestBufferSize)) is Empty)
|
|
error = True;
|
|
#endif
|
|
|
|
if (error)
|
|
{
|
|
ErrorLog("AtpTransmitRequest", ISevWarning, __LINE__, UnknownPort,
|
|
IErrAtpOutOfMemory, IMsgAtpOutOfMemory,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* Build an ATP request datagram from the passed request structure. */
|
|
|
|
datagram->data[AtpCommandControlOffset] = AtpRequestFunctionCode;
|
|
datagram->data[AtpCommandControlOffset] |=
|
|
(char)(request->trelTimerValue & AtpTRelTimerValueMask);
|
|
if (request->exactlyOnce)
|
|
datagram->data[AtpCommandControlOffset] |= AtpExactlyOnceMask;
|
|
datagram->data[AtpBitmapOffset] = (char)(request->bitmap & 0xFF);
|
|
datagram->data[AtpTransactionIdOffset] =
|
|
(char)((request->transactionId >> 8) & 0xFF);
|
|
datagram->data[AtpTransactionIdOffset + 1] =
|
|
(char)(request->transactionId & 0xFF);
|
|
MoveMem(datagram->data + AtpUserBytesOffset, request->requestUserBytes,
|
|
AtpUserBytesSize);
|
|
|
|
#if not TransmitsCompleteSynchronously
|
|
if (request->requestBufferSize > 0)
|
|
MoveFromOpaque(datagram->data + AtpDataOffset,
|
|
request->requestOpaqueBuffer, 0,
|
|
request->requestBufferSize);
|
|
#endif
|
|
|
|
/* Okay, deliver the DDP packet (ignore error)! */
|
|
|
|
#if Debug
|
|
{
|
|
long temp;
|
|
|
|
if (DropEveryNthRequest isnt 0 and
|
|
((temp = RandomNumber()) % DropEveryNthRequest) is 0)
|
|
{
|
|
#if (Iam a Primos) and 0
|
|
printf("\nDropping request; rand = %d.\n", temp);
|
|
#endif
|
|
FreeBufferChain(datagram);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
DeliverDdp(request->sourceSocket, request->destination, DdpProtocolAtp,
|
|
datagram, AtpDataOffset + request->requestBufferSize,
|
|
Empty, Empty, 0);
|
|
return;
|
|
|
|
} /* AtpTransmitRequest */
|
|
|
|
ExternForVisibleFunction void far
|
|
AtpRequestTimerExpired(long unsigned timerId,
|
|
int dataSize,
|
|
char far *incomingAdditionalData)
|
|
{
|
|
StaticForSmallStack AtpSendRequest request, nextRequest, previousRequest;
|
|
StaticForSmallStack AtpInfo atpInfo;
|
|
StaticForSmallStack long us;
|
|
StaticForSmallStack long id;
|
|
StaticForSmallStack AdditionalData far *additionalData;
|
|
AtpIncomingResponseHandler *incomingResponse;
|
|
long unsigned userData;
|
|
AppleTalkAddress destination;
|
|
|
|
/* "Use" unneeded actual parameter. */
|
|
|
|
timerId;
|
|
|
|
additionalData = (AdditionalData far *)incomingAdditionalData;
|
|
|
|
/* Validate the data, it should be a pointer to a request control block. */
|
|
|
|
if (dataSize isnt sizeof(AdditionalData))
|
|
{
|
|
ErrorLog("AtpRequestTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrAtpBadDataSize, IMsgAtpBadDataSize,
|
|
Insert0());
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
DeferAtpPackets();
|
|
|
|
request = (AtpSendRequest)(additionalData->pointer);
|
|
id = additionalData->id;
|
|
us = additionalData->socket;
|
|
|
|
/* Is this request still pending? There is a vauage chance that it has
|
|
been freed out from under us. */
|
|
|
|
if ((atpInfo = FindAtpInfoFor(us)) is empty)
|
|
{
|
|
ErrorLog("AtpRequestTimerExpired", ISevVerbose, __LINE__, UnknownPort,
|
|
IErrAtpSocketClosed, IMsgAtpSocketClosed,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
for (previousRequest = empty,
|
|
nextRequest = atpInfo->sendRequestQueue;
|
|
nextRequest isnt empty and
|
|
(id isnt nextRequest->id or
|
|
nextRequest isnt request);
|
|
previousRequest = nextRequest,
|
|
nextRequest = nextRequest->next)
|
|
/* Boppity bopp */ ;
|
|
if (nextRequest is empty)
|
|
{
|
|
ErrorLog("AtpRequestTimerExpired", ISevVerbose, __LINE__, UnknownPort,
|
|
IErrAtpMissingRequest, IMsgAtpMissingRequest,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
/* Have we done all requested retries? */
|
|
|
|
if (request->retryCount is 1) /* We haven't decremented the count yet. */
|
|
{
|
|
/* We're finsihed with this request (due to timeout), unthread him from
|
|
the SendRequest list... */
|
|
|
|
if (previousRequest is empty)
|
|
atpInfo->sendRequestQueue = request->next;
|
|
else
|
|
previousRequest->next = request->next;
|
|
|
|
/* We have to call the completion routine with an error... pull the needed
|
|
information out of the control block so we can free it. */
|
|
|
|
incomingResponse = request->completionRoutine;
|
|
userData = request->userData;
|
|
destination = request->destination;
|
|
Free(request);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (incomingResponse isnt empty)
|
|
(*incomingResponse)(ATatpRequestTimedOut, userData, destination,
|
|
empty, 0, empty, 0);
|
|
return;
|
|
}
|
|
|
|
/* If at first you don't succeed, try, try again... */
|
|
|
|
if (request->retryCount isnt AtpInfiniteRetries)
|
|
request->retryCount -= 1;
|
|
request->timerId = StartTimer(AtpRequestTimerExpired,
|
|
request->retryInterval,
|
|
sizeof(AdditionalData),
|
|
(char *)additionalData);
|
|
AtpTransmitRequest(request);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* AtpRequestTimerExpired */
|
|
|
|
ExternForVisibleFunction void AtpTransmitRelease(AtpSendRequest request)
|
|
{
|
|
BufferDescriptor datagram;
|
|
|
|
/* Allocate a buffer chunk and build the release packet. */
|
|
|
|
if ((datagram = NewBufferDescriptor(AtpDataOffset)) is Empty)
|
|
{
|
|
ErrorLog("AtpTransmitRelease", ISevError, __LINE__, UnknownPort,
|
|
IErrAtpOutOfMemory, IMsgAtpOutOfMemory,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
datagram->data[AtpCommandControlOffset] = AtpReleaseFunctionCode;
|
|
datagram->data[AtpBitmapOffset] = 0;
|
|
datagram->data[AtpTransactionIdOffset] =
|
|
(char)((request->transactionId >> 8) & 0xFF);
|
|
datagram->data[AtpTransactionIdOffset + 1] =
|
|
(char)(request->transactionId & 0xFF);
|
|
FillMem(datagram->data + AtpUserBytesOffset, 0, AtpUserBytesSize);
|
|
|
|
#if Debug
|
|
{
|
|
long temp;
|
|
if (DropEveryNthRelease isnt 0 and
|
|
((temp = RandomNumber()) % DropEveryNthRelease) is 0)
|
|
{
|
|
#if (Iam a Primos) and 0
|
|
printf("\nDropping release; rand = %d.\n", temp);
|
|
#endif
|
|
FreeBufferChain(datagram);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
DeliverDdp(request->sourceSocket, request->destination, DdpProtocolAtp,
|
|
datagram, AtpDataOffset, Empty, Empty, 0);
|
|
return;
|
|
|
|
} /* AtpTransmitRelease */
|
|
|
|
ExternForVisibleFunction short
|
|
AtpSynchUpResponseBuffer(AtpSendRequest request,
|
|
short maximumSinglePacketDataSize)
|
|
{
|
|
/* If we're received shorter than expected responses, we need to "synch" up
|
|
the user's response buffer. Also, return the actual size of the
|
|
response.
|
|
|
|
Note that when responses are received, we will place them at their
|
|
sequenceNumber times maximumSinglePacketDataSize offsets within the user's
|
|
buffer; thus, short responses may leave holes. */
|
|
|
|
short index, packetOffset;
|
|
short actualSize = 0;
|
|
|
|
for (index = 0;
|
|
index < AtpMaximumResponsePackets and
|
|
request->packetsIn[index].received;
|
|
index += 1)
|
|
{
|
|
packetOffset = (short)(index * maximumSinglePacketDataSize);
|
|
if (actualSize isnt packetOffset)
|
|
MoveOpaqueToOpaque(request->responseOpaqueBuffer, actualSize,
|
|
request->responseOpaqueBuffer, packetOffset,
|
|
request->packetsIn[index].dataSize);
|
|
actualSize += request->packetsIn[index].dataSize;
|
|
}
|
|
|
|
return(actualSize);
|
|
|
|
} /* AtpSynchUpResponseBuffer */
|
|
|
|
ExternForVisibleFunction AtpInfo FindAtpInfoFor(long mySocket)
|
|
{
|
|
long index;
|
|
AtpInfo atpInfo;
|
|
|
|
CheckMod(index, mySocket, MaxAtpInfoHashBuckets, "FindAtpInfoFor");
|
|
for (atpInfo = atpInfoHashBuckets[index];
|
|
atpInfo isnt empty;
|
|
atpInfo = atpInfo->next)
|
|
if (atpInfo->mySocket is mySocket)
|
|
return(atpInfo);
|
|
return(empty);
|
|
|
|
} /* FindAtpInfoFor */
|