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.
3614 lines
115 KiB
3614 lines
115 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pap.c
|
|
|
|
Abstract:
|
|
|
|
|
|
Author:
|
|
|
|
Garth Conboy initial coding
|
|
Nikhil Kamkolkar recoding/mpsafe
|
|
|
|
Revision History:
|
|
|
|
GC - (01/20/92): Workaround for bug in the new Apple LaserWriter IIg
|
|
(it sends truncated open request denied replys, it
|
|
doesn't bother to add the one byte for a zero-sized
|
|
status string).
|
|
GC - (03/24/92): Changed calls to AtpEnqueueRequestHandler() to operate
|
|
in the new mode that allows Atp to simply pass up pointers
|
|
into incoming Ddp buffers rather than copying the data
|
|
into supplied buffers.
|
|
GC - (09/02/92): PapCreateServiceListenerOnNode now can optionally take
|
|
and existing Atp socket on which to create the listener,
|
|
and may optionally not do server name registration.
|
|
GC - (09/02/92): PapRead now takes a buffer size and returns an error if
|
|
it is smaller than the receive quantum size.
|
|
GC - (10/13/92): OpenJobOnNode can now take a target address as well as
|
|
an Nbp name.
|
|
GC - (11/14/92): Added PapSetCookieForJob() and PapGetCookieForJob().
|
|
GC - (11/14/92): Added "existingAtpSocket" argument to PapOpenJobOnNode().
|
|
|
|
--*/
|
|
|
|
#include "atalk.h"
|
|
|
|
#define VerboseMessages 0
|
|
#define DebugCode 0
|
|
|
|
ExternForVisibleFunction ServiceListenerInfo
|
|
FindServiceListenerInfoFor(long serviceListenerRefNum);
|
|
|
|
ExternForVisibleFunction NbpCompletionHandler NbpRegisterComplete;
|
|
|
|
ExternForVisibleFunction ActiveJobInfo FindActiveJobInfoFor(long jobRefNum);
|
|
|
|
ExternForVisibleFunction AtpIncomingRequestHandler IncomingServiceListenerPacket;
|
|
|
|
ExternForVisibleFunction long GetNextJobRefNum(void);
|
|
|
|
ExternForVisibleFunction TimerHandler ConnectionTimerExpired;
|
|
|
|
ExternForVisibleFunction AtpIncomingRequestHandler IncomingRequestPacket;
|
|
|
|
ExternForVisibleFunction AtpIncomingResponseHandler IncomingStatus;
|
|
|
|
ExternForVisibleFunction AtpIncomingResponseHandler IncomingOpenReply;
|
|
|
|
ExternForVisibleFunction NbpCompletionHandler NbpLookupComplete;
|
|
|
|
ExternForVisibleFunction AtpIncomingResponseHandler IncomingReadComplete;
|
|
|
|
ExternForVisibleFunction AtpIncomingReleaseHandler PapIncomingRelease,
|
|
SlsIncomingRelease;
|
|
|
|
ExternForVisibleFunction void PostSendDataResponse(ActiveJobInfo activeJobInfo);
|
|
|
|
//
|
|
// The service listener needs to send out various internally created Atp
|
|
// responses (open replies, status replies, etc.). The data packet that
|
|
// is sent must be dynamically allocated, and freed when the Release comes
|
|
// in; to make matters worse, Atp wants an "opaque" buffer rather than a
|
|
// "char *." So, the following node holds the required fields, it is passed
|
|
// "as userData" to these AtpPostResponses and, at the release, we free it
|
|
// and the "opaque descriptor," if needed.
|
|
//
|
|
|
|
typedef struct { char papData[PapMaximumDataPacketSize];
|
|
void far *opaquePapData; // Opaque descriptor for above.
|
|
Boolean freeOpaquePapData; // Do we need to free descriptor?
|
|
} *SlsResponseCompletionCookie;
|
|
|
|
ExternForVisibleFunction SlsResponseCompletionCookie
|
|
NewSlsResponseCompletionCookie(void);
|
|
|
|
ExternForVisibleFunction void
|
|
FreeSlsResponseCompletionCookie(SlsResponseCompletionCookie cookie);
|
|
|
|
|
|
AppleTalkErrorCode far PapCreateServiceListenerOnNode(
|
|
int port, // Port on which the socket should live.
|
|
//
|
|
long existingAtpSocket, // "-1" if we should open our own Atp socket
|
|
// for the listener; if ">= 0" this is
|
|
// an already open Atp socket on which to
|
|
// create the listener.
|
|
//
|
|
//
|
|
int desiredSocket, // Desired static socket or zero for
|
|
// dynamic; ignored if above isnt -1.
|
|
//
|
|
//
|
|
char far *object, // NBP object of the server; if Empty, don't
|
|
// bother doing a name registration; in this
|
|
// case this routine completes syncrhonously
|
|
// (the completionRoutine will NOT be
|
|
// called).
|
|
//
|
|
char far *type, // NBP type of the server.
|
|
//
|
|
char far *zone, // NBP zone of the server, beter be Empty
|
|
// or "*".
|
|
//
|
|
//
|
|
short serverQuantum, // How many full PAP packets (512 bytes)
|
|
// can the server handle.
|
|
//
|
|
//
|
|
long far *returnSocket, // Full AppleTalkAddress of the ATP socket
|
|
// we'll open (the socket that the service
|
|
// listener will be open on).
|
|
//
|
|
long far *serviceListenerRefNum,
|
|
//
|
|
// The service listener ref num of the
|
|
// created service listener.
|
|
//
|
|
PapNbpRegisterComplete *completionRoutine,
|
|
//
|
|
// "Who ya gonna call?", "PapBusters!";
|
|
// not called if "object" is Empty.
|
|
//
|
|
//
|
|
long unsigned userData, // User data to be passed on to the
|
|
// completion routine.
|
|
//
|
|
StartJobHandler *startJobRoutine,
|
|
// incoming connection event handler.
|
|
long unsigned startJobUserData) // User data for above.
|
|
{
|
|
StaticForSmallStack Boolean wrapped, okay;
|
|
StaticForSmallStack ServiceListenerInfo serviceListenerInfo;
|
|
StaticForSmallStack AppleTalkErrorCode errorCode;
|
|
StaticForSmallStack PapCompletionInfo completionInfo;
|
|
long socket;
|
|
|
|
//
|
|
// Create a new service listener. Register the specified NBP name on the
|
|
// new socket. When the NBP register complete call the specified completion
|
|
// routine with the following arguments:
|
|
//
|
|
// errorCode - AppleTalkErrorCode; from NBP.
|
|
// userData - long unsigned; as passed to this routine.
|
|
// serviceListenerRefNum - long; the service listener who's register
|
|
// is now complete.
|
|
//
|
|
// This routine will NOT be called if the "object" argument is Empty,
|
|
// indicating that we shouldn't do a name registation.
|
|
//
|
|
//
|
|
|
|
// Argument error checking.
|
|
|
|
if (serverQuantum < 1 or serverQuantum > PapMaximumFlowQuantum)
|
|
return(ATpapBadQuantum);
|
|
|
|
wrapped = False;
|
|
okay = False;
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
// Try to open the ATP socket for the service listener.
|
|
|
|
if (existingAtpSocket >= 0)
|
|
socket = existingAtpSocket;
|
|
else {
|
|
if ((errorCode = AtpOpenSocketOnNode(&socket, port, empty, desiredSocket,
|
|
empty, 0)) isnt ATnoError) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((AppleTalkErrorCode)socket);
|
|
}
|
|
}
|
|
if (returnSocket isnt empty)
|
|
*returnSocket = socket;
|
|
|
|
// Well, so far, so good, come up with a service listener ref num.
|
|
|
|
while(not okay) {
|
|
okay = True;
|
|
if ((lastServiceListenerRefNum += 1) < 0) {
|
|
lastServiceListenerRefNum = 0;
|
|
if (wrapped) {
|
|
ErrorLog("PapCreateServiceListenerOnNode", ISevError, __LINE__,
|
|
port, IErrPapNoMoreSLRefNums, IMsgPapNoMoreSLRefNums,
|
|
Insert0());
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(socket);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATinternalError);
|
|
}
|
|
wrapped = True;
|
|
}
|
|
for (serviceListenerInfo = serviceListenerInfoHead;
|
|
okay and serviceListenerInfo isnt empty;
|
|
serviceListenerInfo = serviceListenerInfo->next)
|
|
if (serviceListenerInfo->serviceListenerRefNum is
|
|
lastServiceListenerRefNum)
|
|
okay = False;
|
|
}
|
|
*serviceListenerRefNum = lastServiceListenerRefNum;
|
|
|
|
//
|
|
// Okay, things are really looking good now, build up a new service listener
|
|
// structure.
|
|
//
|
|
|
|
serviceListenerInfo =
|
|
(ServiceListenerInfo)Calloc(sizeof(*serviceListenerInfo), 1);
|
|
if (object is Empty) {
|
|
//
|
|
// We don't need to do the register, so pick some dummy non-Empty value
|
|
// so that the following test won't trigger.
|
|
//
|
|
|
|
completionInfo = (PapCompletionInfo)&completionInfo;
|
|
}
|
|
else {
|
|
// Else, get a completionInfo structure.
|
|
|
|
completionInfo = (PapCompletionInfo)Calloc(sizeof(*completionInfo), 1);
|
|
}
|
|
|
|
if (serviceListenerInfo is empty or completionInfo is empty) {
|
|
ErrorLog("PapCreateServiceListenerOnNode", ISevError, __LINE__,
|
|
port, IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
if (serviceListenerInfo isnt empty)
|
|
Free(serviceListenerInfo);
|
|
if (existingAtpSocket < 0)
|
|
AtpCloseSocketOnNode(socket);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
serviceListenerInfo->serviceListenerRefNum = lastServiceListenerRefNum;
|
|
serviceListenerInfo->port = port;
|
|
serviceListenerInfo->socket = socket;
|
|
serviceListenerInfo->closeSocket = (existingAtpSocket < 0);
|
|
serviceListenerInfo->serverState = PapBlockedState;
|
|
serviceListenerInfo->serverFlowQuantum = serverQuantum;
|
|
serviceListenerInfo->next = serviceListenerInfoHead;
|
|
serviceListenerInfoHead = serviceListenerInfo;
|
|
|
|
// Set indication handler info - they could all be empty
|
|
serviceListenerInfo->startJobRoutine = startJobRoutine;
|
|
serviceListenerInfo->startJobUserData = startJobUserData;
|
|
|
|
// if the startJob indication handler is not empty, unblock
|
|
if (startJobRoutine isnt empty) {
|
|
serviceListenerInfo->serverState = PapUnblockedState;
|
|
}
|
|
|
|
//
|
|
// If we're not doing a register, we need to post an outstanding ATP
|
|
// GetRequest in order to handle incoming OpenConn's and SendStatus's.
|
|
//
|
|
|
|
if (object is Empty) {
|
|
errorCode = AtpEnqueueRequestHandler(empty, serviceListenerInfo->socket,
|
|
empty, 0, empty,
|
|
IncomingServiceListenerPacket,
|
|
(long unsigned)
|
|
lastServiceListenerRefNum);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (errorCode isnt ATnoError)
|
|
PapDeleteServiceListener(*serviceListenerRefNum);
|
|
else
|
|
//
|
|
errorCode = ATnoError; // We don't care about the request
|
|
// handler ID.
|
|
//
|
|
return(errorCode);
|
|
}
|
|
|
|
// Otherwise, start the NBP register of the server's name.
|
|
|
|
completionInfo->serviceListenerRefNum = lastServiceListenerRefNum;
|
|
completionInfo->initialRegister = True;
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
errorCode = NbpAction(ForRegister, socket, object, type,
|
|
zone, GetNextNbpIdForNode(socket), 0, 0,
|
|
NbpRegisterComplete,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError) {
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
PapDeleteServiceListener(*serviceListenerRefNum);
|
|
Free(completionInfo);
|
|
}
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} // PapCreateServiceListener
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapDeleteServiceListener(
|
|
long serviceListenerRefNum) // The service listener to delete.
|
|
{
|
|
// This routine can indirectly recurse through "AtpCloseSocketOnNode".
|
|
|
|
StaticForSmallStack ActiveJobInfo activeJobInfo, nextActiveJobInfo;
|
|
StaticForSmallStack ServiceListenerInfo previousServiceListenerInfo;
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
GetNextJobInfo getNextJobInfo, nextGetNextJobInfo;
|
|
|
|
AppleTalkErrorCode errorCode = ATnoError;
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
// Find our service listener.
|
|
|
|
for (previousServiceListenerInfo = empty,
|
|
serviceListenerInfo = serviceListenerInfoHead;
|
|
serviceListenerInfo isnt empty;
|
|
previousServiceListenerInfo = serviceListenerInfo,
|
|
serviceListenerInfo = serviceListenerInfo->next)
|
|
if (serviceListenerInfo->serviceListenerRefNum is serviceListenerRefNum)
|
|
break;
|
|
if (serviceListenerInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
|
|
//
|
|
// Grab the list of GetNextJobs, so the we can teminate them later without
|
|
// timers deferred.
|
|
//
|
|
|
|
getNextJobInfo = serviceListenerInfo->getNextJobList;
|
|
|
|
// Remove the service listener from the master linked list.
|
|
|
|
if (previousServiceListenerInfo is empty)
|
|
serviceListenerInfoHead = serviceListenerInfo->next;
|
|
else
|
|
previousServiceListenerInfo->next = serviceListenerInfo->next;
|
|
|
|
// Close the ATP socket that the service listener is open on.
|
|
|
|
if (serviceListenerInfo->closeSocket)
|
|
errorCode = AtpCloseSocketOnNode(serviceListenerInfo->socket);
|
|
|
|
// Close all active jobs to this server.
|
|
|
|
for (activeJobInfo = serviceListenerInfo->activeJobList;
|
|
activeJobInfo isnt empty;
|
|
activeJobInfo = nextActiveJobInfo) {
|
|
nextActiveJobInfo = activeJobInfo->nextForMyServiceListener;
|
|
PapCloseJob(activeJobInfo->jobRefNum, False, False);
|
|
}
|
|
Free(serviceListenerInfo);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
// Terminate all GetNextJobs that are currently pending.
|
|
|
|
for ( ;
|
|
getNextJobInfo isnt empty;
|
|
getNextJobInfo = nextGetNextJobInfo) {
|
|
nextGetNextJobInfo = getNextJobInfo->next;
|
|
if (getNextJobInfo->startJobRoutine isnt empty)
|
|
(*getNextJobInfo->startJobRoutine)(ATpapServiceListenerDeleted,
|
|
getNextJobInfo->startJobUserData,
|
|
getNextJobInfo->jobRefNum,
|
|
0, 0);
|
|
Free(getNextJobInfo);
|
|
}
|
|
|
|
return(errorCode);
|
|
|
|
} // PapDeleteServiceListener
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapSetConnectionEventHandler(
|
|
long serviceListenerRefNum, // listener on which to set the handlers
|
|
StartJobHandler *startJobRoutine,
|
|
// incoming connection event handler.
|
|
long unsigned startJobUserData) // User data for above.
|
|
{
|
|
AppleTalkErrorCode errorCode = ATnoError;
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
|
|
// Find our service listener.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo isnt empty) {
|
|
serviceListenerInfo->startJobRoutine = startJobRoutine;
|
|
serviceListenerInfo->startJobUserData = startJobUserData;
|
|
|
|
// if the startJob indication handler is not empty, unblock
|
|
if (startJobRoutine isnt empty) {
|
|
serviceListenerInfo->serverState = PapUnblockedState;
|
|
}
|
|
|
|
} else {
|
|
errorCode = ATpapNoSuchServiceListener;
|
|
}
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
} // PapSetConnectionEventHandler
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapOpenJobOnNode(
|
|
//
|
|
int port, // Port on which the ATP socket should
|
|
// live.
|
|
//
|
|
//
|
|
long existingAtpSocket, // "-1" if we should open our own Atp socket
|
|
// for the new job; if ">= 0" this is
|
|
// an already open Atp socket on which to
|
|
// open job.
|
|
//
|
|
//
|
|
int desiredSocket, // Desired static socket or zero for
|
|
// dynamic; ignored if above isnt "-1".
|
|
//
|
|
//
|
|
long far *jobRefNum, // The jobRefNum that will be used when this
|
|
// job is really started.
|
|
//
|
|
AppleTalkAddress *serviceListenerAddress,
|
|
//
|
|
// Address of remote service listener, if
|
|
// Empty look it up using the below Nbp
|
|
// information.
|
|
//
|
|
char far *object, // The NBP object of the service listener.
|
|
char far *type, // The NBP type of the service listener.
|
|
char far *zone, // The NBP zone of the service listener.
|
|
//
|
|
short workstationQuantum, // The number of full PAP buffers (512 bytes
|
|
// each) that we can handle coming in our
|
|
// direction; 8 is the "right" value.
|
|
//
|
|
//
|
|
void far *opaqueStatusBuffer, // "Buffer" to be filled in with the server's
|
|
// status (at least 255 bytes).
|
|
//
|
|
SendPossibleHandler *sendPossibleRoutine,
|
|
// send ok routine
|
|
long unsigned sendPossibleUserData,
|
|
// user data for above
|
|
CloseJobHandler *closeJobRoutine,
|
|
//
|
|
// If the open goes okay, when do we call
|
|
// when the connection is eventually
|
|
// closed.
|
|
//
|
|
long unsigned closeJobUserData, // User data for close routine.
|
|
PapOpenComplete *completionRoutine,
|
|
// Who to call on completeion.
|
|
long unsigned userData) // Passed on to the above.
|
|
{
|
|
PapCompletionInfo completionInfo;
|
|
OpenJobInfo openJobInfo;
|
|
AppleTalkAddress address;
|
|
AppleTalkErrorCode errorCode;
|
|
long socket;
|
|
StaticForSmallStack char userBytes[AtpUserBytesSize];
|
|
long unsigned now;
|
|
|
|
// A little argument verification.
|
|
|
|
if (workstationQuantum < 1 or workstationQuantum > PapMaximumFlowQuantum)
|
|
return(ATpapBadQuantum);
|
|
if (port is DEFAULT_PORT)
|
|
if ((port = FindDefaultPort()) < 0)
|
|
return(ATappleTalkShutDown);
|
|
|
|
//
|
|
// We will need a socket to do an NBP lookup from; use the NIS on one of
|
|
// our current port's node to perform the lookup. There is a vauge chance
|
|
// that our port doesn't have any nodes yet...
|
|
//
|
|
|
|
if (GET_PORTDESCRIPTOR(port)->ActiveNodes is empty)
|
|
if ((errorCode = GetNodeOnPort(port, True, True, False, empty))
|
|
isnt ATnoError)
|
|
return(errorCode);
|
|
|
|
// Pick (and assign) the next job ref num.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((*jobRefNum = GetNextJobRefNum()) < 0) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATinternalError);
|
|
}
|
|
|
|
//
|
|
// Well, it looks as we can get started; get the needed memory. We'll
|
|
// need to house an NbpLookupReply in our completion info... Nbp and Atp
|
|
// want to fill an "opaque" buffer, so create one of these.
|
|
//
|
|
|
|
completionInfo =
|
|
(PapCompletionInfo)Calloc(sizeof(*completionInfo) +
|
|
PapMaximumDataPacketSize, 1);
|
|
if (completionInfo isnt Empty)
|
|
completionInfo->opaqueResponse =
|
|
MakeOpaqueDataDescriptor(completionInfo->responseBuffer,
|
|
PapMaximumDataPacketSize,
|
|
&completionInfo->freeOpaqueResponse);
|
|
openJobInfo = (OpenJobInfo)Malloc(sizeof(*openJobInfo));
|
|
if (completionInfo is Empty or
|
|
completionInfo->opaqueResponse is Empty or
|
|
openJobInfo is Empty) {
|
|
if (completionInfo isnt Empty)
|
|
{
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
}
|
|
if (openJobInfo isnt Empty)
|
|
Free(openJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
|
|
// Build (and link) the OpenJob node.
|
|
|
|
openJobInfo->jobRefNum = *jobRefNum;
|
|
openJobInfo->port = port;
|
|
openJobInfo->existingAtpSocket = existingAtpSocket;
|
|
openJobInfo->desiredSocket = desiredSocket;
|
|
openJobInfo->workstationQuantum = workstationQuantum;
|
|
openJobInfo->closeJobRoutine = closeJobRoutine;
|
|
openJobInfo->closeJobUserData = closeJobUserData;
|
|
openJobInfo->startTime = CurrentRelativeTime();
|
|
openJobInfo->next = openJobInfoHead;
|
|
openJobInfoHead = openJobInfo;
|
|
|
|
// Build the completion into structure.
|
|
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
completionInfo->jobRefNum = *jobRefNum;
|
|
completionInfo->usersOpaqueStatusBuffer = opaqueStatusBuffer;
|
|
|
|
// If we know the SLS address, just proceed to doing a Pap open.
|
|
|
|
if (serviceListenerAddress isnt Empty) {
|
|
// We need to open our ATP responding socket.
|
|
|
|
if (existingAtpSocket < 0)
|
|
errorCode = AtpOpenSocketOnNode(&socket, openJobInfo->port, empty,
|
|
openJobInfo->desiredSocket, empty, 0);
|
|
else {
|
|
errorCode = ATnoError;
|
|
socket = existingAtpSocket;
|
|
}
|
|
|
|
if (errorCode isnt ATnoError or MapSocketToAddress(socket, &address)) {
|
|
// Couldn't open our ATP socket. Sigh.
|
|
|
|
openJobInfoHead = openJobInfo->next;
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
Free(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
// Save this beast.
|
|
|
|
completionInfo->socket = socket;
|
|
completionInfo->mustCloseAtpSocket = (existingAtpSocket < 0);
|
|
|
|
// Set the ATP data packet size correctly for PAP.
|
|
|
|
if (AtpMaximumSinglePacketDataSize(completionInfo->socket,
|
|
PapMaximumDataPacketSize) isnt
|
|
ATnoError)
|
|
ErrorLog("PapOpenJobOnNode", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadSetSize, IMsgPapBadSetSize,
|
|
Insert0());
|
|
|
|
// Assign a new connection ID.
|
|
|
|
openJobInfo->connectionId = (unsigned char)(lastConnectionId += 1);
|
|
|
|
// Save away the address of our target service listener.
|
|
|
|
openJobInfo->serviceListenerAddress = *serviceListenerAddress;
|
|
|
|
//
|
|
// Okay, prepare to post the OpenConn request; build up both userBytes and
|
|
// data buffer!
|
|
//
|
|
|
|
userBytes[PapConnectionIdOffset] = openJobInfo->connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapOpenConnectionCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
|
|
//
|
|
// The following is somewhat of a hack... we really want to build our own
|
|
// data buffer: a "char *," not whatever our surrounding environment thinks
|
|
// is an "opaque" buffer. So, build our array, and then make a system
|
|
// dependent "opaque data descriptor" for it, noting whether we need to
|
|
// free this when we free the openJobInfo.
|
|
//
|
|
|
|
now = CurrentRelativeTime();
|
|
openJobInfo->buffer[PapRespondingSocketOffset] = address.socketNumber;
|
|
openJobInfo->buffer[PapFlowQuantumOffset] =
|
|
(char)openJobInfo->workstationQuantum;
|
|
openJobInfo->buffer[PapWaitTimeOffset] =
|
|
(char)((now - openJobInfo->startTime) >> 8);
|
|
openJobInfo->buffer[PapWaitTimeOffset + 1] =
|
|
(char)((now - openJobInfo->startTime) & 0xFF);
|
|
errorCode = ATnoError;
|
|
if ((openJobInfo->opaqueDataDescriptor =
|
|
MakeOpaqueDataDescriptor(openJobInfo->buffer, PapStatusOffset,
|
|
&openJobInfo->freeOpaqueDataDescriptor))
|
|
is Empty)
|
|
errorCode = AToutOfMemory;
|
|
|
|
// PostIt! (a trademark of 3M)
|
|
|
|
if (errorCode is ATnoError)
|
|
errorCode = AtpPostRequest(completionInfo->socket,
|
|
*serviceListenerAddress,
|
|
AtpGetNextTransactionId(completionInfo->socket),
|
|
openJobInfo->opaqueDataDescriptor,
|
|
PapStatusOffset, userBytes,
|
|
True, completionInfo->opaqueResponse,
|
|
PapMaximumDataPacketSize,
|
|
completionInfo->responseUserBytes,
|
|
PapOpenConnRequestRetryCount,
|
|
PapOpenConnAtpRetrySeconds,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingOpenReply,
|
|
(long unsigned)completionInfo);
|
|
|
|
if (errorCode < 0) // Couldn't send request. {
|
|
if (completionInfo->mustCloseAtpSocket)
|
|
AtpCloseSocketOnNode(completionInfo->socket);
|
|
openJobInfoHead = openJobInfo->next;
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
Free(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
}
|
|
|
|
//
|
|
// Okay, we need to lookup the guy up first, use the NIS socket, and
|
|
// post the lookup.
|
|
//
|
|
|
|
errorCode = ATnoError;
|
|
if ((socket = MapNisOnPortToSocket(port)) < 0 or
|
|
(errorCode = NbpAction(ForLookup, socket, object, type,
|
|
zone, GetNextNbpIdForNode(socket), 0, 0,
|
|
NbpLookupComplete, (long unsigned)completionInfo,
|
|
completionInfo->opaqueResponse,
|
|
MaximumNbpTupleLength, 1)) isnt ATnoError) {
|
|
if (errorCode = ATnoError)
|
|
errorCode = ATinternalError;
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
openJobInfoHead = openJobInfo->next;
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
Free(openJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
else {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
}
|
|
|
|
} // PapOpenJob
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapCloseJob(
|
|
long jobRefNum, // The job to close.
|
|
//
|
|
Boolean remoteClose, // All external callers should pass "False";
|
|
// inverts server/workstation context of the
|
|
// code passed to the close completeion
|
|
// routine.
|
|
//
|
|
Boolean closedByConnectionTimer)
|
|
//
|
|
// All external callers should pass "False";
|
|
// causes close code to be due to the
|
|
// connection timer expiring.
|
|
//
|
|
{
|
|
ActiveJobInfo activeJobInfo, previousActiveJobInfo, nextActiveJobInfo;
|
|
AppleTalkErrorCode closeCode;
|
|
long index;
|
|
char userBytes[AtpUserBytesSize];
|
|
|
|
// Find the activeJob node.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
CheckMod(index, jobRefNum, NumberOfActiveJobHashBuckets,
|
|
"PapCloseJob");
|
|
|
|
for (previousActiveJobInfo = empty,
|
|
activeJobInfo = activeJobHashBuckets[index];
|
|
activeJobInfo isnt empty;
|
|
previousActiveJobInfo = activeJobInfo,
|
|
activeJobInfo = activeJobInfo->next)
|
|
if (activeJobInfo->jobRefNum is jobRefNum)
|
|
break;
|
|
if (activeJobInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchJob);
|
|
}
|
|
|
|
// Remove this beast from the active job hash table.
|
|
|
|
if (previousActiveJobInfo is empty)
|
|
activeJobHashBuckets[index] = activeJobInfo->next;
|
|
else
|
|
previousActiveJobInfo->next = activeJobInfo->next;
|
|
|
|
//
|
|
// If we're a server job, we also need to remove this guy from the active
|
|
// job list hanging off the service listener.
|
|
//
|
|
|
|
if (activeJobInfo->serverJob) {
|
|
for (previousActiveJobInfo = empty,
|
|
nextActiveJobInfo = activeJobInfo->ourServiceListener->
|
|
activeJobList;
|
|
nextActiveJobInfo isnt empty;
|
|
previousActiveJobInfo = nextActiveJobInfo,
|
|
nextActiveJobInfo = nextActiveJobInfo->nextForMyServiceListener)
|
|
if (nextActiveJobInfo->jobRefNum is jobRefNum)
|
|
break;
|
|
if (nextActiveJobInfo is empty)
|
|
ErrorLog("PapCloseJob", ISevError, __LINE__, UnknownPort,
|
|
IErrPapJobNotOnSLList, IMsgPapJobNotOnSLList,
|
|
Insert0());
|
|
else
|
|
if (previousActiveJobInfo is empty)
|
|
activeJobInfo->ourServiceListener->activeJobList =
|
|
nextActiveJobInfo->nextForMyServiceListener;
|
|
else
|
|
previousActiveJobInfo->nextForMyServiceListener =
|
|
nextActiveJobInfo->nextForMyServiceListener;
|
|
}
|
|
|
|
// Cancel the connection maintenance timer.
|
|
|
|
if (not closedByConnectionTimer)
|
|
CancelTimer(activeJobInfo->connectionTimerId);
|
|
|
|
//
|
|
// Send a close-connection to the other side. Don't send out a Close packet
|
|
// if this close is taking place on behalf of an incoming close from the
|
|
// other side!
|
|
//
|
|
|
|
if (not remoteClose) {
|
|
userBytes[PapConnectionIdOffset] = (char)activeJobInfo->connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapCloseConnectionCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
if (AtpPostRequest(activeJobInfo->ourSocket,
|
|
activeJobInfo->theirAddress,
|
|
AtpGetNextTransactionId(activeJobInfo->ourSocket),
|
|
empty, 0, userBytes, False,
|
|
empty, 0, empty,
|
|
0, 0, ThirtySecondsTRelTimer,
|
|
empty, (long unsigned)0) isnt ATnoError)
|
|
ErrorLog("PapCloseJob", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadCloseConnSend, IMsgPapBadCloseConnSend,
|
|
Insert0());
|
|
}
|
|
|
|
// Compute close type before we enable timers...
|
|
|
|
if (closedByConnectionTimer)
|
|
closeCode = ATpapClosedByConnectionTimer;
|
|
else if ((not remoteClose and activeJobInfo->serverJob) or
|
|
(remoteClose and not activeJobInfo->serverJob))
|
|
closeCode = ATpapClosedByServer;
|
|
else
|
|
closeCode = ATpapClosedByWorkstation;
|
|
|
|
//
|
|
// We can get away with this now because we've already removed the active
|
|
// job from all of the lists... so he won't be munged with by anybody else.
|
|
//
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
//
|
|
// If a PapWrite has been posted, but no "sendData" has been recieved yet,
|
|
// we should simulate that completion...
|
|
//
|
|
|
|
if (activeJobInfo->writeDataWaiting and
|
|
not activeJobInfo->incomingSendDataPending)
|
|
(*activeJobInfo->writeCompletionRoutine)(ATatpTransactionAborted,
|
|
activeJobInfo->writeUserData,
|
|
activeJobInfo->jobRefNum);
|
|
|
|
// Close the ATP socket.
|
|
|
|
if (activeJobInfo->closeOurSocket)
|
|
if (AtpCloseSocketOnNode(activeJobInfo->ourSocket) isnt ATnoError)
|
|
ErrorLog("PapCloseJob", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadSocketClose, IMsgPapBadSocketClose,
|
|
Insert0());
|
|
|
|
// Call the close complete routine, if any.
|
|
|
|
if (activeJobInfo->closeJobRoutine isnt empty)
|
|
(*activeJobInfo->closeJobRoutine)(closeCode,
|
|
activeJobInfo->closeJobUserData,
|
|
jobRefNum);
|
|
|
|
// Okay, free the active job info.
|
|
|
|
Free(activeJobInfo);
|
|
return(ATnoError);
|
|
|
|
} // PapCloseJob
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapGetRemoteAddressForJob(
|
|
long jobRefNum,
|
|
AppleTalkAddress *remoteAddress)
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
AppleTalkErrorCode errorCode = ATnoError;
|
|
|
|
// Find the activeJob node.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) isnt Empty) {
|
|
// Do the deed.
|
|
remoteAddress->networkNumber = activeJobInfo->theirAddress.networkNumber;
|
|
remoteAddress->nodeNumber = activeJobInfo->theirAddress.nodeNumber;
|
|
remoteAddress->socketNumber = activeJobInfo->theirAddress.socketNumber;
|
|
} else
|
|
errorCode = ATpapNoSuchJob;
|
|
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapSetCookieForJob(
|
|
long jobRefNum, // The job on which to set the cookie.
|
|
long unsigned cookie) // The cookie to set.
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
|
|
// Find the activeJob node.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) is Empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchJob);
|
|
}
|
|
|
|
// Do the deed.
|
|
|
|
activeJobInfo->usersCookie = cookie;
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} // PapSetCookieForJob
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapGetCookieForJob(
|
|
long jobRefNum, // The job from which to get the cookie.
|
|
long unsigned far *cookie) // The place to stick the gotten cookie.
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
|
|
// Find the activeJob node.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) is Empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchJob);
|
|
}
|
|
|
|
// Do the deed.
|
|
|
|
*cookie = activeJobInfo->usersCookie;
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} // PapGetCookieForJob
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapGetNextJob(
|
|
//
|
|
long serviceListenerRefNum, // The service listener ref num of the
|
|
// service listener we're looking for
|
|
// work on.
|
|
//
|
|
//
|
|
long far *jobRefNum, // The job ref num that will be assigned to
|
|
// this job when it gets started.
|
|
//
|
|
StartJobHandler *startJobRoutine,
|
|
// Routine to call when we get going.
|
|
long unsigned startJobUserData, // User data for above.
|
|
CloseJobHandler *closeJobRoutine,
|
|
// Routine to call when the job completes.
|
|
long unsigned closeJobUserData) // User data for above.
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
GetNextJobInfo getNextJobInfo;
|
|
|
|
// Find our service listener.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
|
|
// Build up a getNextJobInfo.
|
|
|
|
getNextJobInfo = (GetNextJobInfo)Calloc(sizeof(*getNextJobInfo), 1);
|
|
if (getNextJobInfo is empty) {
|
|
ErrorLog("PapGetNextJob", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
if ((getNextJobInfo->jobRefNum = GetNextJobRefNum()) < 0) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATinternalError);
|
|
}
|
|
getNextJobInfo->startJobRoutine = startJobRoutine;
|
|
getNextJobInfo->startJobUserData = startJobUserData;
|
|
getNextJobInfo->closeJobRoutine = closeJobRoutine;
|
|
getNextJobInfo->closeJobUserData = closeJobUserData;
|
|
|
|
// Link it into the service listener's list.
|
|
|
|
getNextJobInfo->next = serviceListenerInfo->getNextJobList;
|
|
serviceListenerInfo->getNextJobList = getNextJobInfo;
|
|
|
|
// All set.
|
|
|
|
*jobRefNum = getNextJobInfo->jobRefNum;
|
|
serviceListenerInfo->serverState = PapUnblockedState;
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} // PapGetNextJob
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapCancelGetNextJob(
|
|
//
|
|
long serviceListenerRefNum, // The service listener ref num of the
|
|
// service listener we're looking to
|
|
// cancel a GetNextJob on.
|
|
//
|
|
//
|
|
long jobRefNum) // The job ref num of the GetNextJob to
|
|
// cancel.
|
|
//
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
GetNextJobInfo getNextJobInfo, previousGetNextJobInfo = Empty;
|
|
|
|
// Find our service listener.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
|
|
//
|
|
// Okay, walk the list of GetNextJob handlers and find the one we're
|
|
// looking to cancel.
|
|
//
|
|
|
|
for (getNextJobInfo = serviceListenerInfo->getNextJobList;
|
|
getNextJobInfo isnt Empty;
|
|
previousGetNextJobInfo = getNextJobInfo,
|
|
getNextJobInfo = getNextJobInfo->next)
|
|
if (getNextJobInfo->jobRefNum is jobRefNum)
|
|
break;
|
|
if (getNextJobInfo is Empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchGetNextJob);
|
|
}
|
|
|
|
// Remove this guy from the list.
|
|
|
|
if (previousGetNextJobInfo is Empty)
|
|
serviceListenerInfo->getNextJobList = getNextJobInfo->next;
|
|
else
|
|
previousGetNextJobInfo->next = getNextJobInfo->next;
|
|
if (serviceListenerInfo->getNextJobList is Empty)
|
|
serviceListenerInfo->serverState = PapBlockedState;
|
|
Free(getNextJobInfo);
|
|
|
|
// All set.
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} // PapCancelGetNextJob
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapAcceptJob(
|
|
long jobRefNum, // The job to accept.
|
|
SendPossibleHandler *sendPossibleRoutine,
|
|
// send ok routine
|
|
long unsigned sendPossibleUserData,
|
|
// user data for above
|
|
CloseJobHandler *closeJobRoutine,
|
|
// Routine to call when the job completes.
|
|
long unsigned closeJobUserData) // User data for above.
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
AppleTalkErrorCode errorCode = ATnoError;
|
|
|
|
// Find the activeJob node.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) isnt Empty) {
|
|
// Set the handlers for the job
|
|
activeJobInfo->closeJobRoutine = closeJobRoutine;
|
|
activeJobInfo->closeJobUserData = closeJobUserData;
|
|
activeJobInfo->sendPossibleRoutine = sendPossibleRoutine;
|
|
activeJobInfo->sendPossibleUserData = sendPossibleUserData;
|
|
} else
|
|
errorCode = ATpapNoSuchJob;
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} // PapAcceptJob
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapRegisterName(
|
|
//
|
|
long serviceListenerRefNum, // The service listener ref num of the
|
|
// service listener that the new name should
|
|
// be registered on.
|
|
//
|
|
char far *object, // NBP object of the server.
|
|
char far *type, // NBP type of the server.
|
|
//
|
|
char far *zone, // NBP zone of the server, beter be empty
|
|
// or "*".
|
|
//
|
|
PapNbpRegisterComplete *completionRoutine,
|
|
// Who to call on NBP completion.
|
|
long unsigned userData) // Passed on to the completion routine.
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
PapCompletionInfo completionInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
// Find our service listener.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
|
|
// Build the completion info that we'll need for NBP.
|
|
|
|
completionInfo = (PapCompletionInfo)Calloc(sizeof(*completionInfo), 1);
|
|
if (completionInfo is empty) {
|
|
ErrorLog("PapRegisterName", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
completionInfo->serviceListenerRefNum = serviceListenerRefNum;
|
|
completionInfo->initialRegister = False;
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
|
|
// Start the NBP action and return.
|
|
|
|
errorCode = NbpAction(ForRegister, serviceListenerInfo->socket,
|
|
object, type, zone,
|
|
GetNextNbpIdForNode(serviceListenerInfo->socket), 0, 0,
|
|
NbpRegisterComplete,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError) {
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
}
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} // PapRegisterName
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapRemoveName(
|
|
//
|
|
long serviceListenerRefNum, // The service listener ref num of the
|
|
// service listener that the name should
|
|
// be removed from.
|
|
//
|
|
char far *object, // NBP object of the server.
|
|
char far *type, // NBP type of the server.
|
|
//
|
|
char far *zone) // NBP zone of the server, beter be empty
|
|
// or "*".
|
|
//
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
// Find our service listener.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
|
|
// Do the NBP remove.
|
|
|
|
errorCode = NbpRemove(serviceListenerInfo->socket, object,
|
|
type, zone);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
|
|
} // PapRemoveName
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapHereIsStatus(
|
|
long serviceListenerRefNum, // The server to set statrus for.
|
|
void far *opaqueStatusBuffer, // Actual status "buffer."
|
|
int statusSize) // Size of status string.
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
|
|
if (statusSize < 0 or statusSize > PapMaximumStatusSize)
|
|
return(ATpapBadStatus);
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchServiceListener);
|
|
}
|
|
|
|
// Replace the previous status buffer.
|
|
|
|
if (serviceListenerInfo->statusBuffer isnt empty) {
|
|
Free(serviceListenerInfo->statusBuffer);
|
|
serviceListenerInfo->statusBuffer = empty;
|
|
}
|
|
if (statusSize is 0) {
|
|
serviceListenerInfo->statusSize = 0;
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
}
|
|
if ((serviceListenerInfo->statusBuffer =
|
|
(char *)Malloc(statusSize)) is empty) {
|
|
serviceListenerInfo->statusSize = 0;
|
|
ErrorLog("PapHereIsStatus", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
|
|
// Copy in the new status.
|
|
|
|
MoveFromOpaque(serviceListenerInfo->statusBuffer, opaqueStatusBuffer, 0,
|
|
statusSize);
|
|
serviceListenerInfo->statusSize = (short)statusSize;
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} // PapHereIsStatus
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapGetStatus(
|
|
//
|
|
long jobRefNum, // Ref num of a workstation job so we can
|
|
// query its server side; if <0 not used.
|
|
//
|
|
AppleTalkAddress far *serverAddress,
|
|
//
|
|
// if jobRefNum <0, maybe this is the address
|
|
// of the server.
|
|
//
|
|
//
|
|
char far *object, // if jobRefNum <0 and serverAddress is Empty;
|
|
// NBP object of server.
|
|
//
|
|
//
|
|
char far *type, // if jobRefNum <0 and serverAddress is Empty;
|
|
// NBP type of server.
|
|
//
|
|
//
|
|
char far *zone, // if jobRefNum <0 and serverAddress is Empty;
|
|
// NBP zone of server.
|
|
//
|
|
//
|
|
void far *opaqueStatusBuffer, // Buffer to fill with status (at least 255
|
|
// bytes).
|
|
//
|
|
PapGetStatusComplete *completionRoutine,
|
|
// Who to call when the call completes.
|
|
//
|
|
long unsigned userData) // User data to pass on to the completion
|
|
// routine.
|
|
//
|
|
{
|
|
StaticForSmallStack ActiveJobInfo activeJobInfo;
|
|
StaticForSmallStack PapCompletionInfo completionInfo;
|
|
StaticForSmallStack char userBytes[AtpUserBytesSize];
|
|
StaticForSmallStack AppleTalkAddress destinationAddress;
|
|
AppleTalkErrorCode errorCode;
|
|
int port;
|
|
long socket = -1;
|
|
|
|
//
|
|
// Get a server's status. Call a completion routine with the following
|
|
// arguments when status arrives:
|
|
//
|
|
// errorCode - AppleTalkErrorCode; completion code.
|
|
// userData - long unsigned; as passed to us.
|
|
// opaqueStatusBuffer
|
|
// - void *; as passed to us.
|
|
// statusSize - int; actual length of status buffer.
|
|
//
|
|
//
|
|
|
|
if ((port = FindDefaultPort()) < 0)
|
|
return(ATappleTalkShutDown);
|
|
|
|
//
|
|
// We may need to post an NBP lookup for the SL... make sure we have a
|
|
// node.
|
|
//
|
|
|
|
if (GET_PORTDESCRIPTOR(port)->ActiveNodes is empty)
|
|
if ((errorCode = GetNodeOnPort(port, True, True, False, empty))
|
|
isnt ATnoError)
|
|
return(errorCode);
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if (jobRefNum >= 0) {
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) is empty)
|
|
{
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchJob);
|
|
}
|
|
if (activeJobInfo->serverJob) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNotWorkstationJob);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Well, it looks like we'll need to make either an NBP or ATP call, so
|
|
// build up the data block that we'll need for completion handling.
|
|
// The max size we can get back is a staus buffer, allocate that too,
|
|
// and build and "opaque" descriptor for it, so we can pass it down
|
|
// to Atp.
|
|
//
|
|
|
|
completionInfo = (PapCompletionInfo)Calloc(sizeof(*completionInfo) +
|
|
PapMaximumDataPacketSize, 1);
|
|
if (completionInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
if ((completionInfo->opaqueResponse =
|
|
MakeOpaqueDataDescriptor(completionInfo->responseBuffer,
|
|
PapMaximumDataPacketSize,
|
|
&completionInfo->freeOpaqueResponse)) is Empty) {
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
completionInfo->lookupForStatus = True;
|
|
completionInfo->usersOpaqueStatusBuffer = opaqueStatusBuffer;
|
|
|
|
//
|
|
// If we know the address of the SL, just send the ATP request. If we
|
|
// have a jobRefNum, this is easy, we have an Atp socket to post the
|
|
// request from. If we just have the server's address, it is a little
|
|
// hard, we don't have any particularly good socket (let alone an ATP
|
|
// socket) to post the GetRequest for status on. For lack of any better
|
|
// idea, open an Atp socket on the default port.
|
|
//
|
|
|
|
if (jobRefNum >= 0) {
|
|
socket = activeJobInfo->ourSocket;
|
|
destinationAddress = activeJobInfo->serviceListenerAddress;
|
|
}
|
|
else if (serverAddress isnt Empty) {
|
|
destinationAddress = *serverAddress;
|
|
if ((errorCode = AtpOpenSocketOnNode(&socket, -1, empty, 0, empty, 0))
|
|
isnt ATnoError) {
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
completionInfo->socket = socket;
|
|
completionInfo->mustCloseAtpSocket = True; // !!!
|
|
}
|
|
|
|
if (socket >= 0) {
|
|
userBytes[PapConnectionIdOffset] = 0;
|
|
userBytes[PapCommandTypeOffset] = PapSendStatusCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
errorCode = AtpPostRequest(socket,
|
|
destinationAddress,
|
|
AtpGetNextTransactionId(socket),
|
|
empty, 0, userBytes,
|
|
True, completionInfo->opaqueResponse,
|
|
PapMaximumDataPacketSize,
|
|
completionInfo->responseUserBytes,
|
|
PapGetStatusRequestRetryCount,
|
|
PapGetStatusAtpRetrySeconds,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingStatus,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError) {
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
}
|
|
|
|
//
|
|
// We don't know the SL's address, so post an NBP lookup to find it!
|
|
// We don't really have a particularly good address to post the lookup
|
|
// from, so use the NIS of a node on our default port.
|
|
//
|
|
|
|
errorCode = ATinternalError;
|
|
if ((socket = MapNisOnPortToSocket(port)) < 0 or
|
|
(errorCode = NbpAction(ForLookup, socket, object, type,
|
|
zone, GetNextNbpIdForNode(socket), 0, 0,
|
|
NbpLookupComplete, (long unsigned)completionInfo,
|
|
completionInfo->opaqueResponse,
|
|
MaximumNbpTupleLength, 1)) isnt ATnoError) {
|
|
if (socket < 0)
|
|
ErrorLog("PapGetStatus", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadSocketMake, IMsgPapBadSocketMake,
|
|
Insert0());
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
// All set.
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} // PapGetStatus
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapRead(
|
|
long jobRefNum, // The job we're reading from.
|
|
void far *opaqueBuffer, // The "buffer" to fill.
|
|
//
|
|
long bufferSize, // How big a buffer... better be able to
|
|
// handle a full flow quantum.
|
|
//
|
|
PapReadComplete *completionRoutine,
|
|
//
|
|
// Routine to call when the read
|
|
// completes.
|
|
//
|
|
long unsigned userData) // Data passed to the above.
|
|
{
|
|
StaticForSmallStack ActiveJobInfo activeJobInfo;
|
|
StaticForSmallStack char userBytes[AtpUserBytesSize];
|
|
StaticForSmallStack PapCompletionInfo completionInfo;
|
|
AppleTalkErrorCode errorCode;
|
|
|
|
//
|
|
// Post a PAP read (send data). Call a supplied completion routine when
|
|
// the read completes:
|
|
//
|
|
// errorCode - AppleTalkErrorCode; completion code.
|
|
// userData - long unsigned; as passed to this routine.
|
|
// jobRefNum - long; as passed to this routine.
|
|
// opaqueBuffer - void *; buffer full of data; as passed to this routine.
|
|
// bufferLength - int; actual number of recieved bytes.
|
|
// eofFlag - Boolean; did the other side signal EOF?
|
|
//
|
|
//
|
|
|
|
// Do some error checking.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
activeJobInfo = FindActiveJobInfoFor(jobRefNum);
|
|
if (activeJobInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchJob);
|
|
}
|
|
if (activeJobInfo->outgoingSendDataPending) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapReadAlreadyPending);
|
|
}
|
|
|
|
f (IamNot a WindowsNT)
|
|
if (bufferSize < activeJobInfo->receiveFlowQuantum * PapMaximumDataPacketSize)
|
|
lse
|
|
if (bufferSize < 0)
|
|
ndif {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapReadBufferTooSmall);
|
|
}
|
|
|
|
//
|
|
// Okay, build the completion info that we'll need to handle the completion
|
|
// of this transaction.
|
|
//
|
|
|
|
completionInfo = (PapCompletionInfo)Calloc(sizeof(*completionInfo), 1);
|
|
if (completionInfo is empty) {
|
|
ErrorLog("PapRead", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(AToutOfMemory);
|
|
}
|
|
completionInfo->completionRoutine = (void *)completionRoutine;
|
|
completionInfo->userData = userData;
|
|
completionInfo->jobRefNum = jobRefNum;
|
|
|
|
// Build up userBytes to post the "sendData" to the other side.
|
|
|
|
userBytes[PapConnectionIdOffset] = activeJobInfo->connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapSendDataCommand;
|
|
if (activeJobInfo->lastOutgoingSequenceNumber is (short unsigned)0xFFFF)
|
|
activeJobInfo->lastOutgoingSequenceNumber = 0;
|
|
activeJobInfo->lastOutgoingSequenceNumber += 1;
|
|
userBytes[PapSequenceNumberOffset] =
|
|
(char)(activeJobInfo->lastOutgoingSequenceNumber >> 8);
|
|
userBytes[PapSequenceNumberOffset + 1] =
|
|
(char)(activeJobInfo->lastOutgoingSequenceNumber & 0xFF);
|
|
|
|
// Post the SendData request.
|
|
|
|
errorCode = AtpPostRequest(activeJobInfo->ourSocket,
|
|
activeJobInfo->theirAddress,
|
|
AtpGetNextTransactionId(activeJobInfo->ourSocket),
|
|
empty, 0, userBytes, True, opaqueBuffer,
|
|
activeJobInfo->receiveFlowQuantum *
|
|
PapMaximumDataPacketSize,
|
|
activeJobInfo->outgoingSendDataUserBytes,
|
|
AtpInfiniteRetries, PapSendDataRequestRetrySeconds,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingReadComplete,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError) {
|
|
ErrorLog("PapRead", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadSendDataSend, IMsgPapBadSendDataSend,
|
|
Insert0());
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(errorCode);
|
|
}
|
|
|
|
// We've got one going now!
|
|
|
|
activeJobInfo->outgoingSendDataPending = True;
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} // PapRead
|
|
|
|
|
|
|
|
|
|
AppleTalkErrorCode far PapWrite(
|
|
long jobRefNum, // The job we're writting from.
|
|
void far *opaqueBuffer, // The "buffer" to write from.
|
|
long bufferSize, // Number of bytes to write.
|
|
Boolean eofFlag, // End of xfer flag.
|
|
PapWriteComplete *completionRoutine,
|
|
//
|
|
// Routine to call when the write
|
|
// completes.
|
|
//
|
|
long unsigned userData) // Data passed to the above.
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
|
|
//
|
|
// Post a PAP write (handle a send data). Call a supplied completion
|
|
// routine when the read completes:
|
|
//
|
|
// errorCode - AppleTalkErrorCode; completion code.
|
|
// userData - long unsigned; as passed to this routine.
|
|
// jobRefNum - long; as passed to this routine.
|
|
//
|
|
//
|
|
|
|
// Do some error checking.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
activeJobInfo = FindActiveJobInfoFor(jobRefNum);
|
|
if (activeJobInfo is empty) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapNoSuchJob);
|
|
}
|
|
if (activeJobInfo->writeDataWaiting) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapWriteAlreadyPending);
|
|
}
|
|
|
|
if (bufferSize > activeJobInfo->sendFlowQuantum * PapMaximumDataPacketSize) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATpapWriteTooBig);
|
|
}
|
|
|
|
// Place our write request onto the active job structure.
|
|
|
|
activeJobInfo->writeDataWaiting = True;
|
|
activeJobInfo->writeOpaqueBuffer = opaqueBuffer;
|
|
activeJobInfo->writeBufferSize = bufferSize;
|
|
activeJobInfo->writeEofFlag = eofFlag;
|
|
activeJobInfo->writeCompletionRoutine = completionRoutine;
|
|
activeJobInfo->writeUserData = userData;
|
|
|
|
//
|
|
// We're all set if we don't have a pending "sendData" from the other
|
|
// side.
|
|
//
|
|
|
|
if (not activeJobInfo->incomingSendDataPending) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
}
|
|
|
|
//
|
|
// Otherwise, the other side is already waiting for a response, so send it
|
|
// off...
|
|
//
|
|
|
|
PostSendDataResponse(activeJobInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return(ATnoError);
|
|
|
|
} // PapWrite
|
|
|
|
|
|
|
|
|
|
Boolean far PapSendCreditAvailable(
|
|
long jobRefNum) // The job we're checking.
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
Boolean sendCreditAvailable;
|
|
|
|
// Do some error checking.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
activeJobInfo = FindActiveJobInfoFor(jobRefNum);
|
|
sendCreditAvailable = ((activeJobInfo isnt empty) and
|
|
(activeJobInfo->writeDataWaiting is False) and
|
|
(activeJobInfo->incomingSendDataPending is True));
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
|
|
return(sendCreditAvailable);
|
|
|
|
} // PapSendCreditAvailable
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingServiceListenerPacket(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
AppleTalkAddress source,
|
|
void far *buffer,
|
|
// really a "char *"
|
|
int bufferSize,
|
|
char far *incomingUserBytes,
|
|
Boolean exactlyOnce,
|
|
TRelTimerValue trelTimerValue,
|
|
short unsigned transactionId,
|
|
short unsigned bitmap)
|
|
{
|
|
StaticForSmallStack ServiceListenerInfo serviceListenerInfo;
|
|
StaticForSmallStack short commandType, connectionId;
|
|
StaticForSmallStack char userBytes[AtpUserBytesSize];
|
|
StaticForSmallStack Boolean error;
|
|
StaticForSmallStack long serverSocket, workstationSocket;
|
|
StaticForSmallStack ActiveJobInfo activeJobInfo;
|
|
StaticForSmallStack GetNextJobInfo getNextJobInfo;
|
|
StaticForSmallStack SlsResponseCompletionCookie cookie;
|
|
long index;
|
|
long serviceListenerRefNum = (long)userData;
|
|
StartJobHandler *startJobRoutine;
|
|
short waitTime, workstationQuantum;
|
|
long jobRefNum;
|
|
long unsigned startJobUserData;
|
|
Boolean needToUndefer = True;
|
|
AppleTalkAddress serverAddress;
|
|
|
|
Boolean indication = True;
|
|
|
|
// Touch unused formal...
|
|
|
|
bitmap, trelTimerValue;
|
|
|
|
error = False;
|
|
|
|
if (errorCode is ATatpTransactionAborted or errorCode is ATsocketClosed)
|
|
return; // Service listener closed...
|
|
|
|
// Is service listener still with us?
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty) {
|
|
ErrorLog("IncomingServiceListenerPacket", ISevWarning, __LINE__,
|
|
UnknownPort, IErrPapSLGonePoof, IMsgPapSLGonePoof,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// For other AppleTalk errors we could try to re-enqueue the request handler,
|
|
// but more than likely the error would happen again (and quickly) thus
|
|
// hanging the driver...
|
|
//
|
|
|
|
if (errorCode isnt ATnoError and errorCode isnt ATatpResponseBufferTooSmall) {
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapLostSLPacket, IMsgPapLostSLPacket,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
// Pull the needed fields out of the UserBytes.
|
|
|
|
connectionId = (unsigned char)incomingUserBytes[PapConnectionIdOffset];
|
|
commandType = (unsigned char)incomingUserBytes[PapCommandTypeOffset];
|
|
switch(commandType) {
|
|
case PapOpenConnectionCommand:
|
|
|
|
//
|
|
// First get the buffer to send the response with, if we can't
|
|
// get that, run away!
|
|
//
|
|
|
|
if ((cookie = NewSlsResponseCompletionCookie()) is Empty)
|
|
break;
|
|
|
|
// Start trying to set up a new connection.
|
|
//
|
|
// If PapUnblockedState - either there is a GetNextJob posted, or
|
|
// an incoming event handler is set on the listener
|
|
//
|
|
|
|
if (bufferSize < PapStatusOffset or
|
|
serviceListenerInfo->serverState isnt PapUnblockedState)
|
|
error = True;
|
|
|
|
getNextJobInfo = serviceListenerInfo->getNextJobList;
|
|
if (getNextJobInfo isnt Empty) {
|
|
indication = False;
|
|
}
|
|
|
|
if (not error) {
|
|
// Try to open our responding socket.
|
|
|
|
if (AtpOpenSocketOnNode(&serverSocket, serviceListenerInfo->port,
|
|
empty, 0, empty, 0) isnt ATnoError)
|
|
error = True;
|
|
else if (MapSocketToAddress(serverSocket, &serverAddress)
|
|
isnt ATnoError) {
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadSrvrAddrMap, IMsgPapBadSrvrAddrMap,
|
|
Insert0());
|
|
error = True;
|
|
}
|
|
|
|
}
|
|
|
|
if (not error) {
|
|
// Allocate a new active job node.
|
|
|
|
activeJobInfo = (ActiveJobInfo)Calloc(sizeof(*activeJobInfo), 1);
|
|
if (activeJobInfo is empty) {
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
AtpCloseSocketOnNode(serverSocket);
|
|
error = True;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Okay, we should have all of our resources now... return an error
|
|
// if we couldn't get any of them.
|
|
//
|
|
|
|
if (error) {
|
|
userBytes[PapConnectionIdOffset] = (char)connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapOpenConnectionReplyCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
cookie->papData[PapRespondingSocketOffset] = 0;
|
|
cookie->papData[PapFlowQuantumOffset] =
|
|
(char)serviceListenerInfo->serverFlowQuantum;
|
|
cookie->papData[PapResultOffset] = (char)(PapPrinterBusy >> 8);
|
|
cookie->papData[PapResultOffset + 1] = (char)(PapPrinterBusy & 0xFF);
|
|
cookie->papData[PapStatusOffset] =
|
|
(char)serviceListenerInfo->statusSize;
|
|
if (serviceListenerInfo->statusSize isnt 0)
|
|
MoveMem(cookie->papData + PapStatusOffset + 1,
|
|
serviceListenerInfo->statusBuffer,
|
|
serviceListenerInfo->statusSize);
|
|
errorCode = AtpPostResponse(serviceListenerInfo->socket,
|
|
source, transactionId,
|
|
cookie->opaquePapData,
|
|
PapStatusOffset + 1 +
|
|
serviceListenerInfo->statusSize,
|
|
userBytes, exactlyOnce,
|
|
SlsIncomingRelease,
|
|
(long unsigned)cookie);
|
|
if (errorCode isnt ATnoError) {
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadPrinterBusySend, IMsgPapBadPrinterBusySend,
|
|
Insert0());
|
|
FreeSlsResponseCompletionCookie(cookie);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Okay, start filling in our new active job structure.
|
|
|
|
workstationSocket =
|
|
(unsigned char)((char *)buffer)[PapRespondingSocketOffset];
|
|
|
|
// if we are indicating this, the jobRefNum needs to be generated now
|
|
if (indication) {
|
|
activeJobInfo->jobRefNum = jobRefNum = GetNextJobRefNum();
|
|
if (jobRefNum < 0) {
|
|
// Free the activeJobInfo structure / release socket and flee
|
|
AtpCloseSocketOnNode(serverSocket);
|
|
Free(activeJobInfo);
|
|
break;
|
|
}
|
|
|
|
|
|
// Grab the start job routine information.
|
|
//
|
|
// BUGBUG: sync issues - what if these have been set to null by now?
|
|
// deal during mp-changes
|
|
//
|
|
|
|
startJobRoutine = serviceListenerInfo->startJobRoutine;
|
|
startJobUserData = serviceListenerInfo->startJobUserData;
|
|
|
|
} else {
|
|
|
|
activeJobInfo->jobRefNum = jobRefNum = getNextJobInfo->jobRefNum;
|
|
activeJobInfo->closeJobRoutine = getNextJobInfo->closeJobRoutine;
|
|
activeJobInfo->closeJobUserData = getNextJobInfo->closeJobUserData;
|
|
|
|
//
|
|
// Grab the start job routine information, and free the GetNextJob
|
|
// node.
|
|
//
|
|
|
|
startJobRoutine = getNextJobInfo->startJobRoutine;
|
|
startJobUserData = getNextJobInfo->startJobUserData;
|
|
serviceListenerInfo->getNextJobList = getNextJobInfo->next;
|
|
Free(getNextJobInfo);
|
|
if (serviceListenerInfo->getNextJobList is empty)
|
|
serviceListenerInfo->serverState = PapBlockedState;
|
|
}
|
|
|
|
activeJobInfo->serverJob = True;
|
|
activeJobInfo->ourPort = serviceListenerInfo->port;
|
|
activeJobInfo->ourSocket = serverSocket;
|
|
activeJobInfo->closeOurSocket = True;
|
|
activeJobInfo->theirAddress = source;
|
|
activeJobInfo->theirAddress.socketNumber = (char)workstationSocket;
|
|
if (MapSocketToAddress(serviceListenerInfo->socket,
|
|
&activeJobInfo->serviceListenerAddress)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadSLSAddress, IMsgPapBadSLSAddress,
|
|
Insert0());
|
|
activeJobInfo->connectionId = (unsigned char)connectionId;
|
|
activeJobInfo->receiveFlowQuantum =
|
|
serviceListenerInfo->serverFlowQuantum;
|
|
workstationQuantum =
|
|
(unsigned char)((char *)buffer)[PapFlowQuantumOffset];
|
|
if (workstationQuantum > PapMaximumFlowQuantum)
|
|
workstationQuantum = PapMaximumFlowQuantum;
|
|
activeJobInfo->sendFlowQuantum = workstationQuantum;
|
|
|
|
// Thead the new active job into the two lookup structures.
|
|
|
|
CheckMod(index, jobRefNum, NumberOfActiveJobHashBuckets,
|
|
"IncomingServiceListenerPacket");
|
|
activeJobInfo->next = activeJobHashBuckets[index];
|
|
activeJobHashBuckets[index] = activeJobInfo;
|
|
|
|
activeJobInfo->nextForMyServiceListener =
|
|
serviceListenerInfo->activeJobList;
|
|
serviceListenerInfo->activeJobList = activeJobInfo;
|
|
activeJobInfo->ourServiceListener = serviceListenerInfo;
|
|
|
|
// Set the ATP data packet size correctly for PAP.
|
|
|
|
if (AtpMaximumSinglePacketDataSize(activeJobInfo->ourSocket,
|
|
PapMaximumDataPacketSize)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadSetSize, IMsgPapBadSetSize,
|
|
Insert0());
|
|
|
|
|
|
//
|
|
// Get the last of the usefull information out of the OpenConn
|
|
// buffer; the wait time.
|
|
//
|
|
|
|
waitTime = (short)(((char *)buffer)[PapWaitTimeOffset] << 8);
|
|
waitTime += (unsigned char)((char *)buffer)[PapWaitTimeOffset + 1];
|
|
|
|
// Build up and send the open reply.
|
|
|
|
userBytes[PapConnectionIdOffset] = (char)connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapOpenConnectionReplyCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
cookie->papData[PapRespondingSocketOffset] = serverAddress.socketNumber;
|
|
cookie->papData[PapFlowQuantumOffset] =
|
|
(char)activeJobInfo->receiveFlowQuantum;
|
|
cookie->papData[PapResultOffset] = (char)(PapNoError >> 8);
|
|
cookie->papData[PapResultOffset + 1] = (char)(PapNoError & 0xFF);
|
|
cookie->papData[PapStatusOffset] =
|
|
(char)serviceListenerInfo->statusSize;
|
|
if (serviceListenerInfo->statusSize isnt 0)
|
|
MoveMem(cookie->papData + PapStatusOffset + 1,
|
|
serviceListenerInfo->statusBuffer,
|
|
serviceListenerInfo->statusSize);
|
|
errorCode = AtpPostResponse(serviceListenerInfo->socket,
|
|
source, transactionId,
|
|
cookie->opaquePapData,
|
|
PapStatusOffset + 1 +
|
|
serviceListenerInfo->statusSize,
|
|
userBytes, exactlyOnce,
|
|
SlsIncomingRelease,
|
|
(long unsigned)cookie);
|
|
if (errorCode isnt ATnoError) {
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadOkaySend, IMsgPapBadOkaySend,
|
|
Insert0());
|
|
FreeSlsResponseCompletionCookie(cookie);
|
|
}
|
|
|
|
// Build up userBytes to start tickling the other end.
|
|
|
|
userBytes[PapConnectionIdOffset] = (char)connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapTickleCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
if (AtpPostRequest(activeJobInfo->ourSocket,
|
|
activeJobInfo->theirAddress,
|
|
AtpGetNextTransactionId(activeJobInfo->ourSocket),
|
|
empty, 0, userBytes, False, empty, 0, empty,
|
|
AtpInfiniteRetries, PapTickleSeconds,
|
|
ThirtySecondsTRelTimer, empty,
|
|
(long unsigned)0) isnt ATnoError)
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadTickleStart, IMsgPapBadTickleStart,
|
|
Insert0());
|
|
|
|
//
|
|
// Post a few get request handlers on this connection (for incoming
|
|
// tickle's, close's, or sendData's).
|
|
//
|
|
|
|
for (index = 0; index < PapPendingReadsPerJob; index += 1) {
|
|
if (AtpEnqueueRequestHandler(empty, activeJobInfo->ourSocket,
|
|
empty, 0, empty,
|
|
IncomingRequestPacket,
|
|
(long unsigned)activeJobInfo->jobRefNum)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadRequestHandler, IMsgPapBadRequestHandler,
|
|
Insert0());
|
|
}
|
|
|
|
// Start the connection timer.
|
|
|
|
activeJobInfo->lastContactTime = CurrentRelativeTime();
|
|
activeJobInfo->connectionTimerId =
|
|
StartTimer(ConnectionTimerExpired, PapConnectionSeconds,
|
|
sizeof(long), (char *)&activeJobInfo->jobRefNum);
|
|
|
|
// Call our completion routine.
|
|
|
|
needToUndefer = False;
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (startJobRoutine isnt empty)
|
|
(*startJobRoutine)(errorCode, startJobUserData, jobRefNum,
|
|
workstationQuantum, waitTime);
|
|
|
|
break;
|
|
|
|
case PapSendStatusCommand:
|
|
|
|
// Get the buffering for a Atp response... run away if we fail.
|
|
|
|
if ((cookie = NewSlsResponseCompletionCookie()) is Empty)
|
|
break;
|
|
|
|
// Reply with our current status.
|
|
|
|
userBytes[PapConnectionIdOffset] = 0;
|
|
userBytes[PapCommandTypeOffset] = PapStatusReplyCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
cookie->papData[PapStatusOffset] =
|
|
(char)serviceListenerInfo->statusSize;
|
|
if (serviceListenerInfo->statusSize isnt 0)
|
|
MoveMem(cookie->papData + PapStatusOffset + 1,
|
|
serviceListenerInfo->statusBuffer,
|
|
serviceListenerInfo->statusSize);
|
|
errorCode = AtpPostResponse(serviceListenerInfo->socket,
|
|
source, transactionId,
|
|
cookie->opaquePapData,
|
|
PapStatusOffset + 1 +
|
|
serviceListenerInfo->statusSize,
|
|
userBytes, exactlyOnce,
|
|
SlsIncomingRelease,
|
|
(long unsigned)cookie);
|
|
if (errorCode isnt ATnoError) {
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__,
|
|
UnknownPort, IErrPapBadSendStatSend, IMsgPapBadSendStatSend,
|
|
Insert0());
|
|
FreeSlsResponseCompletionCookie(cookie);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ErrorLog("IncomingServiceListenerPacket", ISevWarning, __LINE__,
|
|
UnknownPort, IErrPapBadCommand, IMsgPapBadCommand,
|
|
Insert0());
|
|
}
|
|
|
|
// Re-enqueue the GetRequest handler.
|
|
|
|
if (AtpEnqueueRequestHandler(empty, serviceListenerInfo->socket,
|
|
empty, 0, empty,
|
|
IncomingServiceListenerPacket,
|
|
(long unsigned)serviceListenerRefNum)
|
|
isnt ATnoError)
|
|
ErrorLog("IncomingServiceListenerPacket", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadReEnqueue, IMsgPapBadReEnqueue,
|
|
Insert0());
|
|
if (needToUndefer) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
return;
|
|
|
|
} // IncomingServiceListenerPacket
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction ServiceListenerInfo
|
|
FindServiceListenerInfoFor(long serviceListenerRefNum)
|
|
{
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
|
|
for (serviceListenerInfo = serviceListenerInfoHead;
|
|
serviceListenerInfo isnt empty;
|
|
serviceListenerInfo = serviceListenerInfo->next)
|
|
if (serviceListenerInfo->serviceListenerRefNum is
|
|
serviceListenerRefNum)
|
|
return(serviceListenerInfo);
|
|
return(empty);
|
|
|
|
} // FindServiceListenerInfoFor
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void far
|
|
NbpRegisterComplete(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
int reason,
|
|
long onWhosBehalf,
|
|
int nbpId,
|
|
...)
|
|
{
|
|
PapCompletionInfo completionInfo = (PapCompletionInfo)incomingUserData;
|
|
ServiceListenerInfo serviceListenerInfo;
|
|
Boolean initialRegister;
|
|
PapNbpRegisterComplete *completionRoutine;
|
|
long serviceListenerRefNum;
|
|
long unsigned userData;
|
|
|
|
// Touch unused formals...
|
|
|
|
reason, onWhosBehalf, nbpId;
|
|
|
|
// Pull the information out of the completion info block then free it.
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
serviceListenerRefNum = completionInfo->serviceListenerRefNum;
|
|
initialRegister = completionInfo->initialRegister;
|
|
completionRoutine =
|
|
(PapNbpRegisterComplete *)completionInfo->completionRoutine;
|
|
userData = completionInfo->userData;
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
|
|
//
|
|
// If this was the initial register (due to a call to
|
|
// PapCreateServiceListener) and the register failed, we should remove the
|
|
// service listener.
|
|
//
|
|
|
|
if (initialRegister and errorCode isnt ATnoError)
|
|
PapDeleteServiceListener(serviceListenerRefNum);
|
|
|
|
//
|
|
// If this is the first register (i.e. the service listener is just being
|
|
// created) we need to post an outstanding ATP GetRequest in order to handle
|
|
// incoming OpenConn's and SendStatus's.
|
|
//
|
|
|
|
if (initialRegister and errorCode is ATnoError) {
|
|
serviceListenerInfo = FindServiceListenerInfoFor(serviceListenerRefNum);
|
|
if (serviceListenerInfo is empty) {
|
|
ErrorLog("NbpRegisterComplete", ISevError, __LINE__, UnknownPort,
|
|
IErrPapSLGonePoof, IMsgPapSLGonePoof,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATinternalError, userData, serviceListenerRefNum);
|
|
return;
|
|
}
|
|
errorCode = AtpEnqueueRequestHandler(empty, serviceListenerInfo->socket,
|
|
empty, 0, empty,
|
|
IncomingServiceListenerPacket,
|
|
(long unsigned)serviceListenerRefNum);
|
|
if (errorCode isnt ATnoError)
|
|
PapDeleteServiceListener(serviceListenerRefNum);
|
|
}
|
|
|
|
// Okay, call the user's completion routine.
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
if (completionRoutine isnt empty)
|
|
(*completionRoutine)(errorCode, userData, serviceListenerRefNum);
|
|
return;
|
|
|
|
} // NbpRegisterComplete
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction ActiveJobInfo FindActiveJobInfoFor(long jobRefNum)
|
|
{
|
|
long index;
|
|
ActiveJobInfo activeJobInfo;
|
|
|
|
CheckMod(index, jobRefNum, NumberOfActiveJobHashBuckets,
|
|
"FindActiveJobInfoFor");
|
|
|
|
for (activeJobInfo = activeJobHashBuckets[index];
|
|
activeJobInfo isnt empty;
|
|
activeJobInfo = activeJobInfo->next)
|
|
if (activeJobInfo->jobRefNum is jobRefNum)
|
|
break;
|
|
|
|
return(activeJobInfo);
|
|
|
|
} // FindActiveJobInfoFor
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction long GetNextJobRefNum(void)
|
|
{
|
|
StaticForSmallStack ActiveJobInfo activeJobInfo;
|
|
StaticForSmallStack GetNextJobInfo getNextJobInfo;
|
|
StaticForSmallStack OpenJobInfo openJobInfo;
|
|
StaticForSmallStack ServiceListenerInfo serviceListenerInfo;
|
|
StaticForSmallStack int index;
|
|
Boolean wrapped = False;
|
|
Boolean okay = False;
|
|
|
|
//
|
|
// This routine is only a bitch because we solve the problem with such
|
|
// tremendous overkill. Active job ref nums are kept on one of three lists:
|
|
// the get next job list, and the active job list, and the pending open job
|
|
// list. We walk all of these to see if our cantidate is truely available.
|
|
// The likelyhood of us ever wrapping in 32 bits is so small that this is
|
|
// a thorough waste of time!
|
|
//
|
|
|
|
while(not okay) {
|
|
okay = True;
|
|
if ((lastJobRefNum += 1) < 0) {
|
|
lastJobRefNum = 0;
|
|
if (wrapped) {
|
|
ErrorLog("GetNextJobRefNum", ISevError, __LINE__, UnknownPort,
|
|
IErrPapNoMoreJobRefNums, IMsgPapNoMoreJobRefNums,
|
|
Insert0());
|
|
return(-1);
|
|
}
|
|
wrapped = True;
|
|
}
|
|
|
|
// First, walk the active job hash table...
|
|
|
|
for (index = 0;
|
|
okay and index < NumberOfActiveJobHashBuckets;
|
|
index += 1)
|
|
for (activeJobInfo = activeJobHashBuckets[index];
|
|
okay and activeJobInfo isnt empty;
|
|
activeJobInfo = activeJobInfo->next)
|
|
if (activeJobInfo->jobRefNum is lastJobRefNum)
|
|
okay = False;
|
|
|
|
// Now walk all of the GetNextJobLists.
|
|
|
|
for (serviceListenerInfo = serviceListenerInfoHead;
|
|
okay and serviceListenerInfo isnt empty;
|
|
serviceListenerInfo = serviceListenerInfo->next)
|
|
for (getNextJobInfo = serviceListenerInfo->getNextJobList;
|
|
okay and getNextJobInfo isnt empty;
|
|
getNextJobInfo = getNextJobInfo->next)
|
|
if (getNextJobInfo->jobRefNum is lastJobRefNum)
|
|
okay = False;
|
|
|
|
// Lastly, the pending open job list.
|
|
|
|
for (openJobInfo = openJobInfoHead;
|
|
okay and openJobInfo isnt empty;
|
|
openJobInfo = openJobInfo->next)
|
|
if (openJobInfo->jobRefNum is lastJobRefNum)
|
|
okay = False;
|
|
}
|
|
|
|
//
|
|
// We found one. What do you want to bet that it was the first one we
|
|
// tried?
|
|
//
|
|
|
|
return(lastJobRefNum);
|
|
|
|
} // GetNextJobRefNum
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void far
|
|
ConnectionTimerExpired(long unsigned timerId,
|
|
int additionalDataSize,
|
|
char far *additionalData)
|
|
{
|
|
long jobRefNum;
|
|
ActiveJobInfo activeJobInfo;
|
|
|
|
// Touch unused formal...
|
|
|
|
timerId;
|
|
|
|
// Find our active job structure.
|
|
|
|
if (additionalDataSize isnt sizeof(long)) {
|
|
ErrorLog("ConnectionTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadData, IMsgPapBadData,
|
|
Insert0());
|
|
return;
|
|
}
|
|
DeferAtpPackets();
|
|
jobRefNum = *(long *)additionalData;
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) is empty) {
|
|
ErrorLog("ConnectionTimerExpired", ISevError, __LINE__, UnknownPort,
|
|
IErrPapJobNotActive, IMsgPapJobNotActive,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If we've heard from this fellow recently, just restart the timer and
|
|
// return.
|
|
//
|
|
|
|
if ((CurrentRelativeTime() - activeJobInfo->lastContactTime) <=
|
|
PapConnectionSeconds) {
|
|
activeJobInfo->connectionTimerId =
|
|
StartTimer(ConnectionTimerExpired, PapConnectionSeconds,
|
|
sizeof(long), (char *)&jobRefNum);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
// All is not well... put this guy out of his misery!
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
PapCloseJob(jobRefNum, False, True);
|
|
return;
|
|
|
|
} // ConnectionTimerExpired
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingRequestPacket(AppleTalkErrorCode errorCode,
|
|
long unsigned userData,
|
|
AppleTalkAddress source,
|
|
void far *buffer, // Really "char *"
|
|
int bufferSize,
|
|
char far *userBytes,
|
|
Boolean exactlyOnce,
|
|
TRelTimerValue trelTimerValue,
|
|
short unsigned transactionId,
|
|
short unsigned bitmap)
|
|
{
|
|
ActiveJobInfo activeJobInfo;
|
|
unsigned char commandType;
|
|
unsigned char connectionId;
|
|
short unsigned sequenceNumber, expectedSequenceNumber;
|
|
long jobRefNum = (long)userData;
|
|
Boolean enqueueNewRequestHandler = True;
|
|
Boolean mustUndefer = True;
|
|
|
|
// Touch unused formals...
|
|
|
|
source, buffer, bufferSize, bitmap, trelTimerValue;
|
|
|
|
// Check for errors.
|
|
|
|
if (errorCode is ATatpTransactionAborted or errorCode is ATsocketClosed)
|
|
return; // Job closed...
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
if ((activeJobInfo = FindActiveJobInfoFor(jobRefNum)) is empty) {
|
|
ErrorLog("IncomingRequestPacket", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapJobGonePoof, IMsgPapJobGonePoof,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Make sure connection ID's match, if not, just re-enqueue the request
|
|
// handler and we're done.
|
|
//
|
|
|
|
commandType = (unsigned char)userBytes[PapCommandTypeOffset];
|
|
connectionId = (unsigned char)userBytes[PapConnectionIdOffset];
|
|
if (connectionId isnt activeJobInfo->connectionId) {
|
|
ErrorLog("IncomingRequestPacket", ISevVerbose, __LINE__, UnknownPort,
|
|
IErrPapIdMismatch, IMsgPapIdMismatch,
|
|
Insert0());
|
|
AtpCancelResponse(activeJobInfo->ourSocket,
|
|
activeJobInfo->theirAddress,
|
|
transactionId, False);
|
|
if (AtpEnqueueRequestHandler(empty, activeJobInfo->ourSocket,
|
|
empty, 0, empty,
|
|
IncomingRequestPacket,
|
|
(long unsigned)jobRefNum) isnt ATnoError)
|
|
ErrorLog("IncomingRequestPacket", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadRequestHandler, IMsgPapBadRequestHandler,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
|
|
// Well, regardless of what we've got, at least it's contact.
|
|
|
|
activeJobInfo->lastContactTime = CurrentRelativeTime();
|
|
|
|
// Do the right thing with the contained command.
|
|
|
|
switch(commandType) {
|
|
case PapTickleCommand:
|
|
|
|
// Not much to do here, we've already noted the contact.
|
|
|
|
break;
|
|
|
|
case PapSendDataCommand:
|
|
|
|
//
|
|
// Check Sequence number, note that zero means "unsequenced" and we
|
|
// should accept the beast.
|
|
//
|
|
|
|
sequenceNumber =
|
|
(short unsigned)(userBytes[PapSequenceNumberOffset] << 8);
|
|
sequenceNumber +=
|
|
(unsigned char)(userBytes[PapSequenceNumberOffset + 1] & 0xFF);
|
|
|
|
if (sequenceNumber is 0 and
|
|
activeJobInfo->incomingSendDataPending) {
|
|
//
|
|
// What we have here is failure to communicate... no, no, just
|
|
// kidding. What we really have here is an incoming unsequenced
|
|
// sendData request before we've finished with the previous one
|
|
// (gotten a release for it). We don't clear the
|
|
// "incomingSendDataPending" flag until a release comes in (either
|
|
// real or time-out). Also, we can't play Steve Wolfe's "implied
|
|
// release" game that is legal with sequenced requests. So,
|
|
// we don't let the other side get a jump on us, just cancel the
|
|
// response, so we'll get another delivery of the sendData later
|
|
// (maybe we'll be ready then).
|
|
//
|
|
|
|
AtpCancelResponse(activeJobInfo->ourSocket,
|
|
source, transactionId, False);
|
|
break;
|
|
}
|
|
else if (sequenceNumber is 0) {
|
|
//
|
|
// Okay, an incoming unsequenced sendData that we're really ready
|
|
// to handle... accept him.
|
|
//
|
|
}
|
|
else {
|
|
// Verify sequence number.
|
|
|
|
expectedSequenceNumber = activeJobInfo->lastIncomingSequenceNumber;
|
|
if (expectedSequenceNumber is (short unsigned)0xFFFF)
|
|
expectedSequenceNumber = 0;
|
|
expectedSequenceNumber += 1;
|
|
if (sequenceNumber isnt expectedSequenceNumber) {
|
|
ErrorLog("IncomingRequestPacket", ISevVerbose, __LINE__,
|
|
UnknownPort, IErrPapSendDataSeqError, IMsgPapSendDataSeqError,
|
|
Insert0());
|
|
AtpCancelResponse(activeJobInfo->ourSocket,
|
|
source, transactionId, False);
|
|
break;
|
|
}
|
|
|
|
if (activeJobInfo->incomingSendDataPending) {
|
|
//
|
|
// Steve Wolfe [Apple] taught me a neat trick here. Whats up
|
|
// is we've gotten a new sendData before we think we're finished
|
|
// with the previous (i.e. have gotten its release or time out).
|
|
// However, this new guy has arrived AND it has the next
|
|
// expected sequence number which means, given PAPs ono-at-a-
|
|
// time nature, that the previous transaction's release was
|
|
// dropped on the floor (due, most likey, to a network error).
|
|
// So, we can assume this and "finish" the previous transaction
|
|
// and then move ahead with this one. This gets rid of the
|
|
// 30 second pauses when a release is dropped!
|
|
//
|
|
|
|
AtpCancelResponse(activeJobInfo->ourSocket,
|
|
activeJobInfo->incomingSendDataSource,
|
|
activeJobInfo->incomingSendDataTransactionId,
|
|
False);
|
|
activeJobInfo->incomingSendDataPending = False;
|
|
activeJobInfo->writeDataWaiting = False;
|
|
}
|
|
|
|
activeJobInfo->lastIncomingSequenceNumber = expectedSequenceNumber;
|
|
}
|
|
|
|
// Copy the needed information into the active job node.
|
|
|
|
activeJobInfo->incomingSendDataPending = True;
|
|
activeJobInfo->incomingSendDataTransactionId = transactionId;
|
|
activeJobInfo->incomingSendDataExactlyOnce = exactlyOnce;
|
|
|
|
//
|
|
// The Macintosh may not send the "SendData" from its "responding
|
|
// socket" (the one we're tickling and we've noted as "theirAddress"),
|
|
// we need to address the response to the socket that the request
|
|
// originated on, so, save it away.
|
|
//
|
|
|
|
activeJobInfo->incomingSendDataSource = source;
|
|
|
|
// If we're waiting to write data, do it!
|
|
|
|
if (activeJobInfo->writeDataWaiting) {
|
|
PostSendDataResponse(activeJobInfo);
|
|
} else {
|
|
|
|
// call the senddatapossible handler
|
|
if (activeJobInfo->sendPossibleRoutine isnt empty) {
|
|
(*activeJobInfo->sendPossibleRoutine)(
|
|
activeJobInfo->jobRefNum,
|
|
activeJobInfo->sendPossibleUserData,
|
|
activeJobInfo->sendFlowQuantum);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case PapCloseConnectionCommand:
|
|
|
|
//
|
|
// Post the close connection reply. Who really cares whether the
|
|
// other side really gets it???
|
|
//
|
|
|
|
// PapCloseJob will call the closeJobRoutine (indication) if set
|
|
|
|
userBytes[PapCommandTypeOffset] = PapCloseConnectionReplyCommand;
|
|
AtpPostResponse(activeJobInfo->ourSocket,
|
|
source,
|
|
transactionId, empty, 0,
|
|
userBytes, exactlyOnce, empty,
|
|
(long unsigned)0);
|
|
|
|
// Close the specified connection (job).
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
PapCloseJob(jobRefNum, True, False);
|
|
mustUndefer = False;
|
|
enqueueNewRequestHandler = False;
|
|
break;
|
|
|
|
default:
|
|
ErrorLog("IncomingRequestPacket", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapFunnyCommand, IMsgPapFunnyCommand,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
// Repost the request handler.
|
|
|
|
if (enqueueNewRequestHandler)
|
|
if (AtpEnqueueRequestHandler(empty, activeJobInfo->ourSocket,
|
|
empty, 0, empty,
|
|
IncomingRequestPacket,
|
|
(long unsigned)jobRefNum) isnt ATnoError)
|
|
ErrorLog("IncomingRequestPacket", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadRequestHandler, IMsgPapBadRequestHandler,
|
|
Insert0());
|
|
if (mustUndefer) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
}
|
|
return;
|
|
|
|
} // IncomingRequestPacket
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void far
|
|
NbpLookupComplete(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
int reason,
|
|
long onWhosBehalf,
|
|
int nbpId,
|
|
...)
|
|
{
|
|
StaticForSmallStack AppleTalkAddress serviceListenerAddress;
|
|
StaticForSmallStack OpenJobInfo openJobInfo, previousOpenJobInfo;
|
|
StaticForSmallStack char userBytes[AtpUserBytesSize];
|
|
StaticForSmallStack char far *tupleOpaqueBuffer;
|
|
StaticForSmallStack int tupleCount;
|
|
StaticForSmallStack va_list ap;
|
|
StaticForSmallStack long unsigned now;
|
|
PapCompletionInfo completionInfo = (PapCompletionInfo)incomingUserData;
|
|
Boolean getStatus = completionInfo->lookupForStatus;
|
|
long unsigned userData = completionInfo->userData;
|
|
long jobRefNum = completionInfo->jobRefNum;
|
|
PapGetStatusComplete *getStatusCompletionRoutine =
|
|
(PapGetStatusComplete *)completionInfo->completionRoutine;
|
|
PapOpenComplete *openCompletionRoutine =
|
|
(PapOpenComplete *)completionInfo->completionRoutine;
|
|
long socket;
|
|
AppleTalkAddress socketAddress;
|
|
|
|
// Touch unused formal...
|
|
|
|
reason, onWhosBehalf;
|
|
|
|
now = CurrentRelativeTime();
|
|
|
|
// If we got an error... give up.
|
|
|
|
if (errorCode isnt ATnoError) {
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
if (getStatus)
|
|
(*getStatusCompletionRoutine)(errorCode, userData, empty, 0);
|
|
else
|
|
(*openCompletionRoutine)(errorCode, userData, jobRefNum, 0, empty, 0);
|
|
return;
|
|
}
|
|
|
|
// Okay, extract the additional arguments...
|
|
|
|
va_start(ap, nbpId);
|
|
tupleOpaqueBuffer = va_arg(ap, void far *);
|
|
tupleCount = va_arg(ap, int);
|
|
va_end(ap);
|
|
if (tupleCount isnt 1) {
|
|
if (tupleCount is 0)
|
|
errorCode = ATpapServiceListenerNotFound;
|
|
else
|
|
errorCode = ATpapNonUniqueLookup;
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
if (getStatus)
|
|
(*getStatusCompletionRoutine)(errorCode, userData, empty, 0);
|
|
else
|
|
(*openCompletionRoutine)(errorCode, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
return;
|
|
}
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
//
|
|
// Decode the NBP tuple... that will be the address of the service
|
|
// listener (server). The tupleBuffer is really the response buffer in
|
|
// our completionInfo -- use this rather than the "opaque" guy that we
|
|
// just got as extra arguments... might as well take the direct aproach.
|
|
//
|
|
|
|
serviceListenerAddress.networkNumber =
|
|
(unsigned short)(completionInfo->responseBuffer[0] << 8);
|
|
serviceListenerAddress.networkNumber +=
|
|
(unsigned char)(completionInfo->responseBuffer[1]);
|
|
serviceListenerAddress.nodeNumber =
|
|
(unsigned char)(completionInfo->responseBuffer[2]);
|
|
serviceListenerAddress.socketNumber =
|
|
(unsigned char)(completionInfo->responseBuffer[3]);
|
|
|
|
//
|
|
// Okay, do the right thing for our two cases... GetStatus or
|
|
// OpenConnection.
|
|
//
|
|
|
|
if (getStatus) {
|
|
//
|
|
// This is a little hard, we don't have any particularly good socket
|
|
// (let alone an ATP socket) to post the GetRequest for status on. For
|
|
// lack of any better idea, open one on the default port.
|
|
//
|
|
|
|
if ((errorCode = AtpOpenSocketOnNode(&socket, -1, empty, 0, empty, 0))
|
|
isnt ATnoError) {
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*getStatusCompletionRoutine)(errorCode, userData, empty, 0);
|
|
return;
|
|
}
|
|
completionInfo->socket = socket;
|
|
completionInfo->mustCloseAtpSocket = True; // !!!
|
|
|
|
// Okay, finally post the damn request!
|
|
|
|
userBytes[PapConnectionIdOffset] = 0;
|
|
userBytes[PapCommandTypeOffset] = PapSendStatusCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
errorCode = AtpPostRequest(completionInfo->socket,
|
|
serviceListenerAddress,
|
|
AtpGetNextTransactionId(completionInfo->
|
|
socket),
|
|
empty, 0, userBytes,
|
|
True, completionInfo->opaqueResponse,
|
|
PapMaximumDataPacketSize,
|
|
completionInfo->responseUserBytes,
|
|
PapGetStatusRequestRetryCount,
|
|
PapGetStatusAtpRetrySeconds,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingStatus,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError) {
|
|
if (AtpCloseSocketOnNode(socket) isnt ATnoError)
|
|
ErrorLog("NbpLookupComplete", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadTempSocketClose, IMsgPapBadTempSocketClose,
|
|
Insert0());
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*getStatusCompletionRoutine)(errorCode, userData, empty, 0);
|
|
}
|
|
else {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Okay, it looks like the NBP lookup completed on behalf of an OpenJob.
|
|
// Continue processing. Just for fun, and in-case we encouter and error
|
|
// try to find the openJobInfo...
|
|
//
|
|
|
|
for (previousOpenJobInfo = empty,
|
|
openJobInfo = openJobInfoHead;
|
|
openJobInfo isnt empty;
|
|
previousOpenJobInfo = openJobInfo,
|
|
openJobInfo = openJobInfo->next)
|
|
if (openJobInfo->jobRefNum is jobRefNum)
|
|
break;
|
|
if (openJobInfo is empty) {
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
ErrorLog("NbpLookupComplete", ISevError, __LINE__, UnknownPort,
|
|
IErrPapLostNode, IMsgPapLostNode,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*openCompletionRoutine)(ATinternalError, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
return;
|
|
}
|
|
|
|
// We need to open our ATP responding socket.
|
|
|
|
if (openJobInfo->existingAtpSocket < 0)
|
|
errorCode = AtpOpenSocketOnNode(&socket, openJobInfo->port, empty,
|
|
openJobInfo->desiredSocket, empty, 0);
|
|
else {
|
|
errorCode = ATnoError;
|
|
socket = openJobInfo->existingAtpSocket;
|
|
}
|
|
|
|
if (errorCode isnt ATnoError or
|
|
MapSocketToAddress(socket, &socketAddress) isnt ATnoError) {
|
|
// Couldn't open our ATP socket. Sigh.
|
|
|
|
if (previousOpenJobInfo is empty)
|
|
openJobInfoHead = openJobInfo->next;
|
|
else
|
|
previousOpenJobInfo->next = openJobInfo->next;
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
Free(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
ErrorLog("NbpLookupComplete", ISevVerbose, __LINE__, UnknownPort,
|
|
IErrPapCouldNotOpen, IMsgPapCouldNotOpen,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*openCompletionRoutine)(errorCode, userData, jobRefNum, 0, empty, 0);
|
|
return;
|
|
}
|
|
|
|
// Save this beast.
|
|
|
|
completionInfo->socket = socket;
|
|
completionInfo->mustCloseAtpSocket = (openJobInfo->existingAtpSocket < 0);
|
|
|
|
// Set the ATP data packet size correctly for PAP.
|
|
|
|
if (AtpMaximumSinglePacketDataSize(completionInfo->socket,
|
|
PapMaximumDataPacketSize) isnt ATnoError)
|
|
ErrorLog("NbpLookupComplete", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadSetSize, IMsgPapBadSetSize,
|
|
Insert0());
|
|
|
|
// Assign a new connection ID.
|
|
|
|
openJobInfo->connectionId = (unsigned char)(lastConnectionId += 1);
|
|
|
|
// Save away the address of our target service listener.
|
|
|
|
openJobInfo->serviceListenerAddress = serviceListenerAddress;
|
|
|
|
//
|
|
// Okay, prepare to post the OpenConn request; build up both userBytes and
|
|
// data buffer!
|
|
//
|
|
|
|
userBytes[PapConnectionIdOffset] = openJobInfo->connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapOpenConnectionCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
|
|
//
|
|
// The following is somewhat of a hack... we really want to build our own
|
|
// data buffer: a "char *," not whatever our surrounding environment thinks
|
|
// is an "opaque" buffer. So, build our array, and then make a system
|
|
// dependent "opaque data descriptor" for it, noting whether we need to free
|
|
// this when we free the openJobInfo.
|
|
//
|
|
|
|
openJobInfo->buffer[PapRespondingSocketOffset] = socketAddress.socketNumber;
|
|
openJobInfo->buffer[PapFlowQuantumOffset] =
|
|
(char)openJobInfo->workstationQuantum;
|
|
openJobInfo->buffer[PapWaitTimeOffset] =
|
|
(char)((now - openJobInfo->startTime) >> 8);
|
|
openJobInfo->buffer[PapWaitTimeOffset + 1] =
|
|
(char)((now - openJobInfo->startTime) & 0xFF);
|
|
errorCode = ATnoError;
|
|
if ((openJobInfo->opaqueDataDescriptor =
|
|
MakeOpaqueDataDescriptor(openJobInfo->buffer, PapStatusOffset,
|
|
&openJobInfo->freeOpaqueDataDescriptor))
|
|
is Empty)
|
|
errorCode = AToutOfMemory;
|
|
|
|
// PostIt! (a trademark of 3M)
|
|
|
|
if (errorCode is ATnoError)
|
|
errorCode = AtpPostRequest(completionInfo->socket,
|
|
serviceListenerAddress,
|
|
AtpGetNextTransactionId(completionInfo->socket),
|
|
openJobInfo->opaqueDataDescriptor,
|
|
PapStatusOffset, userBytes,
|
|
True, completionInfo->opaqueResponse,
|
|
PapMaximumDataPacketSize,
|
|
completionInfo->responseUserBytes,
|
|
PapOpenConnRequestRetryCount,
|
|
PapOpenConnAtpRetrySeconds,
|
|
ThirtySecondsTRelTimer,
|
|
IncomingOpenReply,
|
|
(long unsigned)completionInfo);
|
|
|
|
if (errorCode < 0) // Couldn't send request. {
|
|
if (completionInfo->mustCloseAtpSocket)
|
|
AtpCloseSocketOnNode(completionInfo->socket);
|
|
if (previousOpenJobInfo is empty)
|
|
openJobInfoHead = openJobInfo->next;
|
|
else
|
|
previousOpenJobInfo->next = openJobInfo->next;
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
Free(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
ErrorLog("NbpLookupComplete", ISevVerbose, __LINE__, UnknownPort,
|
|
IErrPapBadOpenConnReqSend, IMsgPapBadOpenConnReqSend,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*openCompletionRoutine)(errorCode, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
return;
|
|
}
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} // NbpLookupComplete
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingStatus(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
void far *opaqueResponseBuffer,
|
|
int responseBufferSize,
|
|
char far *responseUserBytes,
|
|
short unsigned transactionId)
|
|
{
|
|
PapCompletionInfo completionInfo = (PapCompletionInfo)incomingUserData;
|
|
PapGetStatusComplete *completionRoutine =
|
|
(PapGetStatusComplete *)completionInfo->completionRoutine;
|
|
long unsigned userData = completionInfo->userData;
|
|
Boolean mustCloseAtpSocket = completionInfo->mustCloseAtpSocket;
|
|
long socket = completionInfo->socket ;
|
|
void far *usersOpaqueStatusBuffer = completionInfo->usersOpaqueStatusBuffer;
|
|
int statusSize;
|
|
|
|
// Touch unused formals...
|
|
|
|
source, transactionId;
|
|
|
|
// We only use our stack... no need to defer anything...
|
|
|
|
// If we used a temporary ATP socket, close it now.
|
|
|
|
if (mustCloseAtpSocket)
|
|
if (AtpCloseSocketOnNode(socket) isnt ATnoError)
|
|
ErrorLog("IncomingStatus", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadTempSocketClose, IMsgPapBadTempSocketClose,
|
|
Insert0());
|
|
|
|
// Check for errors.
|
|
|
|
if (errorCode isnt ATnoError) {
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
(*completionRoutine)(errorCode, userData, empty, 0);
|
|
return;
|
|
}
|
|
if (responseUserBytes[PapCommandTypeOffset] isnt PapStatusReplyCommand or
|
|
responseBufferSize < PapStatusOffset + 1) {
|
|
ErrorLog("IncomingStatus", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapBadStatusResp, IMsgPapBadStatusResp,
|
|
Insert0());
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
(*completionRoutine)(ATinternalError, userData, empty, 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Okay, copy the status buffer into the user's buffer. We know for a
|
|
// fact that our incoming opaqueResponseBuffer really describes the
|
|
// responseBuffer in our completionInfo, so use the easy route!
|
|
//
|
|
|
|
statusSize = (unsigned char)completionInfo->responseBuffer[PapStatusOffset];
|
|
MoveToOpaque(usersOpaqueStatusBuffer, 0, completionInfo->responseBuffer +
|
|
PapStatusOffset + 1, statusSize);
|
|
|
|
// All looks good, call the completion routine.
|
|
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
(*completionRoutine)(ATnoError, userData, usersOpaqueStatusBuffer,
|
|
statusSize);
|
|
return;
|
|
|
|
} // IncomingStatus
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingOpenReply(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
void far *opaqueResponseBuffer,
|
|
int responseBufferSize,
|
|
char far *responseUserBytes,
|
|
short unsigned transactionId)
|
|
{
|
|
StaticForSmallStack unsigned char respondingSocket;
|
|
StaticForSmallStack Boolean error;
|
|
StaticForSmallStack char userBytes[AtpUserBytesSize];
|
|
StaticForSmallStack OpenJobInfo openJobInfo, previousOpenJobInfo;
|
|
StaticForSmallStack ActiveJobInfo activeJobInfo;
|
|
StaticForSmallStack unsigned char connectionId;
|
|
StaticForSmallStack short unsigned result;
|
|
long index;
|
|
PapCompletionInfo completionInfo = (PapCompletionInfo)incomingUserData;
|
|
PapOpenComplete *completionRoutine =
|
|
(PapOpenComplete *)completionInfo->completionRoutine;
|
|
long unsigned userData = completionInfo->userData;
|
|
long jobRefNum = completionInfo->jobRefNum;
|
|
char *usersOpaqueStatusBuffer = completionInfo->usersOpaqueStatusBuffer;
|
|
int statusBufferLength;
|
|
short serverFlowQuantum;
|
|
|
|
// Touch unused formals...
|
|
|
|
source, transactionId;
|
|
|
|
error = False;
|
|
|
|
// Are we bing called indirtectly from CloseJob?
|
|
|
|
if (errorCode is ATsocketClosed) {
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
(*completionRoutine)(errorCode, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
return;
|
|
}
|
|
|
|
// Okay, find our OpenJobInfo...
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
for (previousOpenJobInfo = empty,
|
|
openJobInfo = openJobInfoHead;
|
|
openJobInfo isnt empty;
|
|
previousOpenJobInfo = openJobInfo,
|
|
openJobInfo = openJobInfo->next)
|
|
if (openJobInfo->jobRefNum is jobRefNum)
|
|
break;
|
|
if (openJobInfo is empty) {
|
|
if (completionInfo->mustCloseAtpSocket)
|
|
AtpCloseSocketOnNode(completionInfo->socket);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATinternalError, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
ErrorLog("IncomingOpenReply", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOpenJobMissing, IMsgPapOpenJobMissing,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check the incoming ATP error code. Pap-ize the error code and call the
|
|
// user's completion routine.
|
|
//
|
|
|
|
if (errorCode isnt ATnoError) {
|
|
if (errorCode is ATatpTransactionAborted)
|
|
errorCode = ATpapOpenAborted; // Won't really happen.
|
|
else if (errorCode is ATatpRequestTimedOut)
|
|
errorCode = ATpapOpenTimedOut;
|
|
if (completionInfo->mustCloseAtpSocket)
|
|
AtpCloseSocketOnNode(completionInfo->socket);
|
|
if (previousOpenJobInfo is empty)
|
|
openJobInfoHead = openJobInfo->next;
|
|
else
|
|
previousOpenJobInfo->next = openJobInfo->next;
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
Free(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// NOTE BENE: In the following code you will note that we refernce
|
|
// "completionInfo->responseBuffer" when one would think we
|
|
// shoud be referencing "opaqueResponse." Not to worry!
|
|
// We know for a fact that the latter describes the former,
|
|
// so we take the easy way out!
|
|
//
|
|
|
|
//
|
|
// Well, lets see what kind of response we got; take a look at both the
|
|
// response user-bytes and the response buffer. Note that we now allow
|
|
// the silly LaserWriter IIg to leave the status string off altogether,
|
|
// rather than the [correct] zero-sized string.
|
|
//
|
|
|
|
errorCode = ATinternalError; // Default reason for giving up.
|
|
if (responseBufferSize < PapStatusOffset) {
|
|
ErrorLog("IncomingOpenReply", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapTooShort, IMsgPapTooShort,
|
|
Insert0());
|
|
error = True;
|
|
}
|
|
if (not error) {
|
|
if (responseBufferSize is PapStatusOffset)
|
|
statusBufferLength = 0; // Missing, from LaserWriter IIg.
|
|
else
|
|
statusBufferLength =
|
|
(unsigned char)completionInfo->responseBuffer[PapStatusOffset];
|
|
if (statusBufferLength isnt 0 and
|
|
statusBufferLength + 1 + PapStatusOffset > responseBufferSize) {
|
|
ErrorLog("IncomingOpenReply", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapTooLong, IMsgPapTooLong,
|
|
Insert0());
|
|
error = True;
|
|
}
|
|
else {
|
|
connectionId = (unsigned char)responseUserBytes[PapConnectionIdOffset];
|
|
if (connectionId isnt openJobInfo->connectionId) {
|
|
ErrorLog("IncomingOpenReply", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapIdMismatch, IMsgPapIdMismatch,
|
|
Insert0());
|
|
error = True;
|
|
}
|
|
if (responseUserBytes[PapCommandTypeOffset] isnt
|
|
PapOpenConnectionReplyCommand) {
|
|
ErrorLog("IncomingOpenReply", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapNotOpenReply, IMsgPapNotOpenReply,
|
|
Insert0());
|
|
error = True;
|
|
}
|
|
respondingSocket = (unsigned char)completionInfo->
|
|
responseBuffer[PapRespondingSocketOffset];
|
|
serverFlowQuantum = (unsigned char)completionInfo->
|
|
responseBuffer[PapFlowQuantumOffset];
|
|
if (serverFlowQuantum > PapMaximumFlowQuantum)
|
|
serverFlowQuantum = PapMaximumFlowQuantum;
|
|
|
|
result = (short unsigned)((unsigned char)completionInfo->
|
|
responseBuffer[PapResultOffset] << 8);
|
|
result += (unsigned char)completionInfo->
|
|
responseBuffer[PapResultOffset + 1];
|
|
if (result isnt PapNoError) {
|
|
errorCode = ATpapServerBusy;
|
|
error = True;
|
|
}
|
|
|
|
// Move the returned status information to the user's buffer.
|
|
|
|
if (usersOpaqueStatusBuffer isnt empty)
|
|
MoveToOpaque(usersOpaqueStatusBuffer, 0,
|
|
completionInfo->responseBuffer + PapStatusOffset + 1,
|
|
statusBufferLength);
|
|
}
|
|
}
|
|
|
|
// If we're okay so far, try to allocate the active-job node.
|
|
|
|
if (not error) {
|
|
activeJobInfo = (ActiveJobInfo)Calloc(sizeof(*activeJobInfo), 1);
|
|
if (activeJobInfo is empty) {
|
|
ErrorLog("IncomingOpenReply", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
errorCode = AToutOfMemory;
|
|
error = True;
|
|
}
|
|
}
|
|
|
|
// Well, if things aren't going to fly, lets be real men and run away...
|
|
|
|
if (error) {
|
|
if (completionInfo->mustCloseAtpSocket)
|
|
AtpCloseSocketOnNode(completionInfo->socket);
|
|
if (previousOpenJobInfo is empty)
|
|
openJobInfoHead = openJobInfo->next;
|
|
else
|
|
previousOpenJobInfo->next = openJobInfo->next;
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
Free(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, jobRefNum, 0,
|
|
empty, 0);
|
|
return;
|
|
}
|
|
|
|
// Looks pretty good so far, start filling in our new ActiveJob structure.
|
|
|
|
activeJobInfo->jobRefNum = jobRefNum;
|
|
activeJobInfo->ourSocket = completionInfo->socket;
|
|
activeJobInfo->closeOurSocket = completionInfo->mustCloseAtpSocket;
|
|
activeJobInfo->ourPort = openJobInfo->port;
|
|
activeJobInfo->theirAddress = openJobInfo->serviceListenerAddress;
|
|
activeJobInfo->theirAddress.socketNumber = respondingSocket;
|
|
activeJobInfo->serviceListenerAddress = openJobInfo->serviceListenerAddress;
|
|
activeJobInfo->connectionId = connectionId;
|
|
activeJobInfo->receiveFlowQuantum = openJobInfo->workstationQuantum;
|
|
activeJobInfo->sendFlowQuantum = serverFlowQuantum;
|
|
activeJobInfo->closeJobRoutine = openJobInfo->closeJobRoutine;
|
|
activeJobInfo->closeJobUserData = openJobInfo->closeJobUserData;
|
|
|
|
// Thread this new beast into the lookup structure.
|
|
|
|
CheckMod(index, jobRefNum, NumberOfActiveJobHashBuckets, "IncomingOpenReply");
|
|
activeJobInfo->next = activeJobHashBuckets[index];
|
|
activeJobHashBuckets[index] = activeJobInfo;
|
|
|
|
// Start tickling (coochi coo) the other side.
|
|
|
|
userBytes[PapConnectionIdOffset] = (char)connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapTickleCommand;
|
|
userBytes[PapSequenceNumberOffset] = 0;
|
|
userBytes[PapSequenceNumberOffset + 1] = 0;
|
|
if (AtpPostRequest(activeJobInfo->ourSocket,
|
|
activeJobInfo->theirAddress,
|
|
AtpGetNextTransactionId(activeJobInfo->ourSocket),
|
|
empty, 0, userBytes, False, empty, 0, empty,
|
|
AtpInfiniteRetries, PapTickleSeconds,
|
|
ThirtySecondsTRelTimer, empty,
|
|
(long unsigned)0) isnt ATnoError)
|
|
ErrorLog("IncomingOpenReply", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadTickleStart, IMsgPapBadTickleStart,
|
|
Insert0());
|
|
|
|
//
|
|
// Post a few get request handlers on this connection (for incoming
|
|
// tickle's, close's, or sendData's).
|
|
//
|
|
|
|
for (index = 0; index < PapPendingReadsPerJob; index += 1) {
|
|
if (AtpEnqueueRequestHandler(empty, activeJobInfo->ourSocket,
|
|
empty, 0, empty,
|
|
IncomingRequestPacket,
|
|
(long unsigned)jobRefNum) isnt ATnoError)
|
|
ErrorLog("IncomingOpenReply", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadRequestHandler, IMsgPapBadRequestHandler,
|
|
Insert0());
|
|
}
|
|
|
|
// Start the connection timer...
|
|
|
|
activeJobInfo->lastContactTime = CurrentRelativeTime();
|
|
activeJobInfo->connectionTimerId =
|
|
StartTimer(ConnectionTimerExpired, PapConnectionSeconds,
|
|
sizeof(long), (char *)&jobRefNum);
|
|
|
|
// We're radio-active now!
|
|
|
|
if (previousOpenJobInfo is empty)
|
|
openJobInfoHead = openJobInfo->next;
|
|
else
|
|
previousOpenJobInfo->next = openJobInfo->next;
|
|
if (openJobInfo->freeOpaqueDataDescriptor)
|
|
FreeOpaqueDataDescriptor(openJobInfo->opaqueDataDescriptor);
|
|
Free(openJobInfo);
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnoError, userData, jobRefNum, serverFlowQuantum,
|
|
usersOpaqueStatusBuffer, statusBufferLength);
|
|
return;
|
|
|
|
} // IncomingOpenReply
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void far
|
|
IncomingReadComplete(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
void far *opaqueResponseBuffer,
|
|
int responseBufferSize,
|
|
char far *responseUserBytes,
|
|
short unsigned transactionId)
|
|
{
|
|
PapCompletionInfo completionInfo = (PapCompletionInfo)incomingUserData;
|
|
long jobRefNum = completionInfo->jobRefNum;
|
|
PapReadComplete *completionRoutine =
|
|
(PapReadComplete *)completionInfo->completionRoutine;
|
|
long unsigned userData = completionInfo->userData;
|
|
ActiveJobInfo activeJobInfo;
|
|
Boolean eofFlag;
|
|
|
|
// Touch unused formals...
|
|
|
|
source, transactionId;
|
|
|
|
//
|
|
// We've already pulled all of the good stuff out of the completion node,
|
|
// so lets free it now.
|
|
//
|
|
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
if (errorCode is ATsocketClosed) {
|
|
(*completionRoutine)(errorCode, userData, jobRefNum, empty, 0, False);
|
|
return;
|
|
}
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
//
|
|
// Before we look at the incoming error code, see if we can mark in the active
|
|
// job structure that the other sides sendData is complete.
|
|
//
|
|
|
|
activeJobInfo = FindActiveJobInfoFor(jobRefNum);
|
|
if (activeJobInfo is empty) {
|
|
if (errorCode isnt ATatpTransactionAborted)
|
|
ErrorLog("IncomingReadComplete", ISevError, __LINE__, UnknownPort,
|
|
IErrPapActiveJobMissing, IMsgPapActiveJobMissing,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, jobRefNum, empty, 0, False);
|
|
return;
|
|
}
|
|
activeJobInfo->outgoingSendDataPending = False;
|
|
if (errorCode isnt ATnoError) {
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, jobRefNum, empty, 0, False);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Sometimes the Mac forgets to Tickle when its just responding to SendData's
|
|
// from a PAP server. So, when a read completes, tag that we have heard
|
|
// something from the other side...
|
|
//
|
|
|
|
activeJobInfo->lastContactTime = CurrentRelativeTime();
|
|
|
|
// A little error checking, just for fun.
|
|
|
|
if ((unsigned char)responseUserBytes[PapConnectionIdOffset] isnt
|
|
activeJobInfo->connectionId or
|
|
responseUserBytes[PapCommandTypeOffset] isnt PapDataCommand) {
|
|
ErrorLog("IncomingReadComplete", ISevWarning, __LINE__, UnknownPort,
|
|
IErrPapBadUserBytes, IMsgPapBadUserBytes,
|
|
Insert0());
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATinternalError, userData, jobRefNum, empty,
|
|
0, False);
|
|
return;
|
|
}
|
|
|
|
// Looks fine to us...
|
|
|
|
eofFlag = (responseUserBytes[PapEofFlagOffset] isnt 0);
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(ATnoError, userData, jobRefNum, opaqueResponseBuffer,
|
|
responseBufferSize, eofFlag);
|
|
return;
|
|
|
|
} // IncomingReadComplete
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void PostSendDataResponse(ActiveJobInfo activeJobInfo)
|
|
{
|
|
AppleTalkErrorCode errorCode;
|
|
char userBytes[AtpUserBytesSize];
|
|
PapCompletionInfo completionInfo;
|
|
|
|
// We should only be called from cluey sources...
|
|
|
|
if (not activeJobInfo->incomingSendDataPending or
|
|
not activeJobInfo->writeDataWaiting) {
|
|
ErrorLog("PostSendDataResponse", ISevError, __LINE__, UnknownPort,
|
|
IErrPapWhyAreWeHere, IMsgPapWhyAreWeHere,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Our function in life is to post the actual ATP response and set up
|
|
// a handler for the release. Build up completion info and user bytes.
|
|
//
|
|
|
|
completionInfo = (PapCompletionInfo)Calloc(sizeof(*completionInfo), 1);
|
|
if (completionInfo is empty) {
|
|
ErrorLog("PostSendDataResponse", ISevError, __LINE__, UnknownPort,
|
|
IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
AtpCancelResponse(activeJobInfo->ourSocket,
|
|
activeJobInfo->theirAddress,
|
|
activeJobInfo->incomingSendDataTransactionId, False);
|
|
activeJobInfo->incomingSendDataPending = False;
|
|
activeJobInfo->writeDataWaiting = False;
|
|
(*activeJobInfo->writeCompletionRoutine)(AToutOfMemory,
|
|
activeJobInfo->writeUserData,
|
|
activeJobInfo->jobRefNum);
|
|
return;
|
|
}
|
|
completionInfo->completionRoutine =
|
|
(void *)activeJobInfo->writeCompletionRoutine;
|
|
completionInfo->userData = activeJobInfo->writeUserData;
|
|
completionInfo->jobRefNum = activeJobInfo->jobRefNum;
|
|
userBytes[PapConnectionIdOffset] = (char)activeJobInfo->connectionId;
|
|
userBytes[PapCommandTypeOffset] = PapDataCommand;
|
|
userBytes[PapEofFlagOffset] = (char)(activeJobInfo->writeEofFlag);
|
|
userBytes[PapEofFlagOffset + 1] = 0;
|
|
|
|
// Post the response.
|
|
|
|
errorCode = AtpPostResponse(activeJobInfo->ourSocket,
|
|
activeJobInfo->incomingSendDataSource,
|
|
activeJobInfo->incomingSendDataTransactionId,
|
|
activeJobInfo->writeOpaqueBuffer,
|
|
activeJobInfo->writeBufferSize, userBytes,
|
|
activeJobInfo->incomingSendDataExactlyOnce,
|
|
PapIncomingRelease,
|
|
(long unsigned)completionInfo);
|
|
if (errorCode isnt ATnoError) {
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
ErrorLog("PostSendDataResponse", ISevError, __LINE__, UnknownPort,
|
|
IErrPapBadResponseSend, IMsgPapBadResponseSend,
|
|
Insert0());
|
|
activeJobInfo->incomingSendDataPending = False;
|
|
activeJobInfo->writeDataWaiting = False;
|
|
(*activeJobInfo->writeCompletionRoutine)(errorCode,
|
|
activeJobInfo->writeUserData,
|
|
activeJobInfo->jobRefNum);
|
|
return;
|
|
}
|
|
|
|
// All set.
|
|
|
|
return;
|
|
|
|
} // PostSendDataResponse
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void far
|
|
PapIncomingRelease(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
short unsigned transactionId)
|
|
{
|
|
PapCompletionInfo completionInfo = (PapCompletionInfo)incomingUserData;
|
|
PapWriteComplete *completionRoutine =
|
|
(PapWriteComplete *)completionInfo->completionRoutine;
|
|
long unsigned userData = completionInfo->userData;
|
|
long jobRefNum = completionInfo->jobRefNum;
|
|
ActiveJobInfo activeJobInfo;
|
|
|
|
// Touch unused formals...
|
|
|
|
source, transactionId;
|
|
|
|
//
|
|
// Okay, call the completion... we don't really care about the reason.
|
|
// Mark the write as complete first.
|
|
//
|
|
|
|
if (completionInfo->freeOpaqueResponse)
|
|
FreeOpaqueDataDescriptor(completionInfo->opaqueResponse);
|
|
Free(completionInfo);
|
|
if (errorCode is ATsocketClosed) {
|
|
(*completionRoutine)(errorCode, userData, jobRefNum);
|
|
return;
|
|
}
|
|
|
|
DeferTimerChecking();
|
|
DeferAtpPackets();
|
|
|
|
activeJobInfo = FindActiveJobInfoFor(jobRefNum);
|
|
if (activeJobInfo is empty) {
|
|
if (errorCode isnt ATatpTransactionAborted)
|
|
ErrorLog("PapIncomingRelease", ISevError, __LINE__, UnknownPort,
|
|
IErrPapLostActiveNode, IMsgPapLostActiveNode,
|
|
Insert0());
|
|
}
|
|
else {
|
|
activeJobInfo->incomingSendDataPending = False;
|
|
activeJobInfo->writeDataWaiting = False;
|
|
}
|
|
|
|
HandleAtpPackets();
|
|
HandleDeferredTimerChecks();
|
|
(*completionRoutine)(errorCode, userData, jobRefNum);
|
|
return;
|
|
|
|
} // PapIncomingRelease
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction SlsResponseCompletionCookie NewSlsResponseCompletionCookie(void)
|
|
{
|
|
SlsResponseCompletionCookie cookie;
|
|
|
|
//
|
|
// Allocate buffering and make "opaque descriptor" for posting an Sls
|
|
// Atp reponse.
|
|
//
|
|
|
|
if ((cookie = Malloc(sizeof(*cookie))) is Empty or
|
|
(cookie->opaquePapData =
|
|
MakeOpaqueDataDescriptor(cookie->papData,
|
|
PapMaximumDataPacketSize,
|
|
&cookie->freeOpaquePapData)) is Empty) {
|
|
if (cookie isnt Empty)
|
|
Free(cookie);
|
|
ErrorLog("NewSlsResponseCompletionCookie", ISevError, __LINE__,
|
|
UnknownPort, IErrPapOutOfMemory, IMsgPapOutOfMemory,
|
|
Insert0());
|
|
return(Empty);
|
|
}
|
|
|
|
return(cookie);
|
|
|
|
} // NewSlsResponseCompletionCookie
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void
|
|
FreeSlsResponseCompletionCookie(SlsResponseCompletionCookie cookie)
|
|
{
|
|
// Free above buffering.
|
|
|
|
if (cookie->freeOpaquePapData)
|
|
FreeOpaqueDataDescriptor(cookie->opaquePapData);
|
|
Free(cookie);
|
|
return;
|
|
|
|
} // FreeSlsResponseCompletionCookie
|
|
|
|
|
|
|
|
|
|
ExternForVisibleFunction void far
|
|
SlsIncomingRelease(AppleTalkErrorCode errorCode,
|
|
long unsigned incomingUserData,
|
|
AppleTalkAddress source,
|
|
short unsigned transactionId)
|
|
{
|
|
SlsResponseCompletionCookie cookie =
|
|
(SlsResponseCompletionCookie)incomingUserData;
|
|
|
|
// An Sls AtpPostResponse completed... free the buffering.
|
|
|
|
FreeSlsResponseCompletionCookie(cookie);
|
|
return;
|
|
|
|
} // SlsIncomingRelease
|