/* asp.h, /appletalk/ins, Garth Conboy, 04/01/89 */ /* Copyright (c) 1989 by Pacer Software Inc., La Jolla, CA */ /* GC - Initial coding. GC - (12/09/89): AppleTalk phase II comes to town. GC - (03/24/92): Added a refNum to GetSessionHandler such that we can cancel outstanding AspGetSessions. GC - (03/27/92): Removed buffering for GetRequests... Atp now just passes up pointers into the Ddp buffers. Data structure changes to support AspGetAnyRequest(). GC - (07/08/92): We now store/retrieve a user-suppiled magic cookie on a per-session basis. *** Make the PVCS source control system happy: $Header$ $Log$ *** ASP specific declarations. */ /* ASP command type bytes: */ #define AspCloseSessionCommand 1 #define AspCommandCommand 2 #define AspGetStatusCommand 3 #define AspOpenSessionCommand 4 #define AspTickleCommand 5 #define AspWriteCommand 6 #define AspWriteDataCommand 7 #define AspAttentionCommand 8 /* ASP version: */ #define AspVersionBytes "\001\000" /* Offsets into the ATP user bytes for finding various fields: */ #define AspCommandTypeOffset 0 #define AspCommandResultOffset 0 #define AspSssNumberOffset 0 #define AspSessionIdOffset 1 #define AspWssNumberOffset 1 #define AspVersionNumberOffset 2 #define AspErrorCodeOffset 2 #define AspAttentionWordOffset 2 #define AspSequenceNumberOffset 2 #define AspCommandResultSize 4 /* ASP timer values: */ #define AtpIntervalSecondsForAsp 2 #define AspTickleSeconds 30 #define AspSessionMaintenanceSeconds 120 #define AtpRetriesForAsp 10 /* For open, status, close; infinite for others. */ #define AspWriteDataSize 2 /* WriteData command has two bytes of data with it. */ /* Session status size: */ #define AspMaximumStatusSize AtpMaximumTotalResponseSize /* How many ATP request handlers should be posted on each session listener socket (SLS) for handling Open, GetStatus and Tickle events. */ #define OutstandingSlsHandlers 3 /* Routine type that we call when we get an ASP OpenSessionCommand. */ typedef void far AspIncomingSessionOpenHandler(APPLETALK_ERROR errorCode, long unsigned userData, long socket, long sessionRefNum); /* Routine type to call when an OpenSession completes. */ typedef void far AspIncomingOpenReplyHandler(APPLETALK_ERROR errorCode, long unsigned userData, long sessionRefNum); /* Routine type that we call when an attention comes in. */ typedef void far AspIncomingAttentionHandler(APPLETALK_ERROR errorCode, long unsigned userData, long sessionRefNum, short unsigned attentionData); /* Type of routine to call when a AspGetStatus completes: */ typedef void far AspIncomingStatusHandler(APPLETALK_ERROR errorCode, long unsigned userData, void far *opaqueBuffer, int bufferSize); /* Routine type to call when a Command or Write request comes in (as a response to an AspGetReqeust call). */ typedef void far AspIncomingCommandHandler(APPLETALK_ERROR errorCode, long unsigned userData, long sessionRefNum, long unsigned usersCookie, void far *opaqueBuffer, int bufferSize, short requestType, long getRequestRefNum); /* Routine type to call when a reply completes. */ typedef void far AspReplyCompleteHandler(APPLETALK_ERROR errorCode, long unsigned userData, long sessionRefNum, long getRequestRefNum); /* Routine type to call when a write-continue completes. */ typedef void far AspIncomingWriteDataHandler(APPLETALK_ERROR errorCode, long unsigned userData, long sessionRefNum, long getRequestRefNum, void far *opaqueBuffer, int bufferSize); /* Routine type to call when a write or a command completes. */ typedef void far AspWriteOrCommCompleteHandler(APPLETALK_ERROR errorCode, long unsigned userData, long sessionRefNum, char far *resultCode, void far *opaqueBuffer, int bufferSize, int acceptedBytes); /* The node that ATP needs to point to (as userData) in order for us to be able to handle reply-completes and write-continue-completes [also, command-replies and write-replies]. */ typedef struct { void far *completionRoutine; long unsigned userData; long sessionRefNum; long getRequestRefNum; short unsigned sequenceNumber; } far *CompletionInfo; /* The basic theory here is as follows. [A "basic theory" is just like a "design", but shorter.] We maintain two main databases for support of ASP. First, a list of SessionListeners; each call to the routine "AspCreateSessionListener" will create a new one of these. Clients will issue their AspOpenSession requests to the ATP socket associated with the given session listener (the SLS). Second, a list (actually a hash table by SessionReferenceNumber) of active ASP sessions; these are created in one of two ways: a server session is created when the completion routine passed to "AspGetSession" is activated, a workstation session is created by a call to AspOpenSession. All server sessions are logically associated with the SLS that their open request went to. If the SLS is deleted (via AspDeleteSessionListener) all of its server sessions are also closed. A server session will actually operate over a different socket (the SSS), but tickles will still go to the SLS. Workstation sessions are outgoing and thus not associated with an SLS; they too operate over an ATP socket (the WSS). Multiple server and workstation ASP sessions may be multiplexed over a single SSS or WSS. The caller to "AspGetSession" or "AspOpenSession" may specify that a "private session" is desired, in which case multiplexing will not be performed. A "true" ASP address is a full ATP socket plus an 8-bit session ID used within ASP. Thus, to really represent a fully qualified ASP session 36 bits are needed [not a nice number to work with]. So, to keep things neat for our callers we represent each active session with a 32 bit session reference number. We keep a hash table so that we can map an incoming ASP packet (it's destination socket ("our" side of the connection) and the contained session ID) to it's correct sessionRefNum. We then hash this guy to come up with the correct session structure (sessionInfo node). Note that when ASP tickle transactions come in to the server-side of an ASP connection that they are addressed to the "wrong" socket (they go to the SLS rather than the SSS). Thus, when a tickle comes in we need to walk the list of all sessions on all SSSs owned by the SLS to find the session ID that was contianed in the tickle packet. Of course, session IDs need to be unique for all connections "spawned" by an SLS, regardless of socket number (SSS)! All of this said, the nodes of these two main lists (the session listener list and the session list) are defined below. The members of the session listener list will be linked to any server-members of the session list that were created by the given session listener (SLS). Got this? There will be a quiz later, neatness will count. */ /* The following list is linked off the session listener list to hold all of the pending AspGetSession requests... */ typedef struct openSes { struct openSes far *next; long refNum; Boolean privateSocket; AspIncomingSessionOpenHandler *sessionOpenHandler; long unsigned userData; } far *GetSessionHandler; /* The following nodes are linked off the SessionListenerInfo nodes to keep track of which ATP sockets are really in use for ASP sessions. */ typedef struct socInfo { struct socInfo far *next; /* Next link. */ long socket; /* The ATP/DDP socket that we describe. */ short activeSessions; /* How many ASP sessions are operating over this socket? */ Boolean privateSocket; /* Don't overload this socket flag; user wants only one ASP session on this socket. */ } far *SocketInfo; /* On non-private ATP sockets, what is the maximum number of ASP sessions we'll run over each? */ #define MaximumAspSessionsPerSocket 16 /* Each workstation session has a list of writes/commands outstanding. These are the nodes: */ typedef struct comInfo { struct comInfo far *next; /* Link; we may have a number of these guys queued. */ short unsigned sequenceNumber; /* ASP sequence number for the write or command. */ Boolean writeCommand; /* We're a Write command. */ Boolean writeReplyPosted; /* We've received a writeContinue, and have posted the reply. */ char far *resultCode; /* Where to we store that ASP code. */ void far *writeOpaqueBuffer; /* Data the workstation would like to write to the server. */ int writeBufferSize; /* Size of a above. */ int acceptedBytes; /* How much data is the server willing to accept? */ } far *WriteOrCommandInfo; /* Each server session will have a list of handlers for incoming requests [both to be processed and "currently" being processed]. There is also a list of these nodes hanging off the SessionListenerInfo used to process GetAnyRequest. These are the nodes: */ typedef struct reqInfo { struct reqInfo far *next; /* Link; we can have a number of these guys queued off either the SessionInfo or SessionListenerInfo. */ struct reqInfo far *nextForMySessionListener; /* Link; in per-Sls hash list. */ struct sesInfo far *mySessionInfo; /* If on a SessionInfo list, which one? */ struct slsInfo far *mySessionListener; /* Who begat us? */ long getRequestRefNum; /* This request's number as given to our caller. */ Boolean inUse; /* Is our buffer currently filled with a request, awaiting a reply to come down from our caller? That is, if true, we cannot use this buffer for an incoming request. */ short requestType; /* Command or Write. */ short unsigned sequenceNumber; /* ASP sequence number as generated by the workstation. */ AppleTalkAddress source; /* The true source of the request, it does not have to be the Wss. */ Boolean exactlyOnce; /* ATP mode of the request. */ short unsigned transactionId; /* ATP transaction ID of the incoming request. */ char far *opaqueBuffer; /* "Buffer" for the incoming request data. */ int bufferSize; /* Allocated size of "buffer". */ AspIncomingCommandHandler *completionRoutine; /* User routine to call when the request comes in. */ long unsigned userData; /* User data to be passed on to the completion routine. */ Boolean writeContinueInProgress; /* Are we doing a write continue now? */ char writeContinueData[AspWriteDataSize]; /* When we issue the Atp request, this is it's "data" -- the expected response size. */ void far *opaqueWriteContinueData; /* Opaque descriptor for above. */ Boolean freeOpaqueWriteContinueData; /* Do we free the above? */ short unsigned writeContinueTransactionId; /* ATP transaction ID of the ATP request. */ } far *GetRequestInfo; /* The session list members: */ typedef struct sesInfo { struct sesInfo far *next; /* Overflow of hash by session reference number. */ struct slsInfo far *mySessionListener; /* Who begat us, if server session? */ long sessionRefNum; /* My session reference number. */ long unsigned usersCookie; /* A 32-bit "thing" that can be stored and retrieved on a per- session basis. */ struct sesInfo far *nextForMySls; /* Link for server sessions created by a given SLS. */ unsigned char sessionId; /* The 8-bit ASP ID of this session. */ Boolean serverSession; /* Am I a server session? */ short unsigned tickleTransactionId; /* For "server" sessions only, the ATP TID of the tickle. */ Boolean waitingForOpenReply; /* Workstation session, but not really alive yet! */ int ourPort; /* What port does our address exist on? */ long ourSocket; /* Address of "this" side of the connection. */ Boolean closeOurSocket; /* For workstation sessions only; do we close the socket when the session closes? For multi- plexed server sessions, we always close the socket when the usage drops to zero, because we've always opened the socket originally. */ AppleTalkAddress theirAddress; /* Address of "the other" side of the connection. */ AppleTalkAddress slsAddress; /* The SLS associated with with this session. */ long atpRequestHandlerId; /* The ID of the AtpRequestHandler currently posted for this session, so that we can cancel it on an ASP session close. */ char sessionUserBytes[AtpUserBytesSize]; /* UserBytes for responses to Atp requests posted by this session. */ short unsigned nextSequenceNumber; /* ASP request number [for outgoing "workstation" transactions]. */ short unsigned nextExpectedSequenceNumber; /* Sequence number expected on next incoming write or command from the workstation. */ GetRequestInfo getRequestInfoList; /* List of handlers for incoming requests to this server session. */ WriteOrCommandInfo writeOrCommandInfoList; /* List of outgoing request from a workstation. */ AspIncomingAttentionHandler *incomingAttentionHandler; /* For workstation sessions... who do we call with an attention. */ long unsigned userDataForAttention; /* User data to pass on to the attention handler. */ long unsigned lastContactTime; /* When was the last time we heard from the other end? */ } far *SessionInfo; /* Lists GetRequestInfos can exist in two places: for specific sessions they will live on the SessionInfo, for "any session to this server" they will live on the SessionListenerInfo. We want to make the getRequestRefNum unique over all "get requests" to an SLS, so we need to keep track of these guys on a per server basis. To do this, we keep a hash table (by getRequestRefNum) in each SessionListenerInfo. The following is this hash table. */ #define NumGetRequestInfoHashBuckets 11 typedef GetRequestInfo GetRequestInfoHashBuckets[NumGetRequestInfoHashBuckets]; /* If sessions are being handled using GetAnyRequests, we may have a case where a session closes (either locally or remotely) and we don't have any current GetAnyRequests pending and we have no specified GetRequests pending. In this case we don't have anybody to tell about the session close, so we keep a list of sessions that have closed that we have "deferred notification" for -- in this list is non-Empty when a GetAnyRequest is posted, we complete it imediately with a SessionClosed error code telling what refNum closed. */ typedef struct dcn {struct dcn far *next; APPLETALK_ERROR closeCode; long sessionRefNum; long unsigned usersCookie; } far *DeferredCloseNotify; /* The session listener list members: */ typedef struct slsInfo { struct slsInfo far *next; /* Next on list. */ long sessionListenerRefNum; /* Our number. */ long ourSocket; /* What address are we listening on? */ Boolean closeSocket; /* True if we opened the service listener socket. */ int port; /* What port does our address exist on? */ SessionInfo sessionList; /* List of sessions created by this session listener. */ SocketInfo socketList; /* Info on the sockets that our various sessions are using. */ unsigned char lastSessionId; /* The last ASP session ID that we used on this SLS. */ char far *serviceStatus; /* The status to return to a Get Status request; we're allocated this buffer and copied any user status to it. */ void far *opaqueServiceStatus; /* An "opaque" descriptor for the status block. */ Boolean freeOpaqueServiceStatus; /* Do we have to free the above? */ short serviceStatusSize; /* Size of status block. */ GetSessionHandler getSessionHandlers; /* Who do we call when an OpenSession comes in? */ long lastGetRequestRefNum; /* Reference number for incoming Commands or Writes to a any session to this server. */ GetRequestInfo getRequestInfoList; /* List of handlers for incoming requests to ANY session on this server. */ GetRequestInfoHashBuckets getRequestInfoHashBuckets; /* See comment above. */ Boolean getAnyRequestsSeen; /* For the life of this listener, have there been GetAnyRequests? Used to tune session close reporting. */ DeferredCloseNotify deferredCloseNotifyList; /* See commend above the declaration of DeferredCloseNoitfy. */ } far *SessionListenerInfo; /* Nodes used to map an ASP packet's address (destination or "our" side) to it's sessionRefNum: */ typedef struct sesMap { struct sesMap far *next; long socket; unsigned char sessionId; long sessionRefNum; } far *SessionRefNumMap; /* Head of the session listener list: */ #ifndef InitializeData extern #endif SessionListenerInfo sessionListenerInfoHead; /* Hash table (by sessionRefNum) for the session table: */ #define NumberOfAspSessionHashBuckets 23 #ifndef InitializeData extern #endif SessionInfo sessionInfoHashBuckets[NumberOfAspSessionHashBuckets]; /* Hash table (by "((socket & 0xFFFF) << 8) + sessionId") for finding a given session's sessionRefNum. */ #define NumberOfSessionRefNumBuckets 23 #ifndef InitializeData extern #endif SessionRefNumMap sessionRefNumMapHashBuckets[NumberOfSessionRefNumBuckets]; /* What is the next value that we should use for a session listener reference number? */ #ifndef InitializeData extern #endif long lastSessionListenerRefNum; /* What is the next value that we should use for a session reference number? */ #ifndef InitializeData extern #endif long lastSessionRefNum;