Types
ROPE: TYPE ~ Rope.ROPE;
Address: TYPE ~ Arpa.Address;
nullAddress: Address ~ Arpa.nullAddress;
Port: TYPE ~ ArpaUDP.Port;
nullPort: Port ~ ArpaUDP.nullPort;
UnsafeBlock: TYPE ~ Basics.UnsafeBlock;
ObjectClass: TYPE ~ SunRPCPrivate.ObjectClass;
Handle: TYPE ~ REF Object;
Object: PUBLIC TYPE ~ SunRPCPrivate.Object;
Server: TYPE ~ REF ServerObject;
ServerObject: PUBLIC TYPE ~ SunRPCPrivate.ServerObject;
Conversation: TYPE ~ SunRPCAuth.Conversation;
AuthFlavor:
TYPE ~ SunRPCAuth.Flavor;
nullFlavor: AuthFlavor ~ SunRPCAuth.nullFlavor;
AuthValue: TYPE ~ REF TEXT;
Error: PUBLIC ERROR [code: ATOM] ~ CODE;
Parameters
maxPull: CARDINAL ~ 50;
dataBufBytes: CARDINAL ~ 2*maxPull;
maxAuthBytes: CARDINAL ~ SunRPCAuth.maxValueBytes;
maxRefTextLength: CARDINAL ← 8*1024;
maxBLimit:
CARDINAL ~ ArpaUDPBuf.maxBytes - (ArpaUDPBuf.maxBytes
MOD 8);
Must be a multiple of 8 for fragmentation/reassembly to work.
Must be a multiple of 4 for alignment to work.
PutAlign works only because ArpaUDPBuf.hdrBytes is a multiple of 4, sigh.
maxBuffersPerDatagram: CARDINAL ← 6;
maxProcessesPerServer: CARDINAL ~ 6;
maxRetries: CARDINAL ← 10;
minTimeout: CARD ← 50;
maxTimeout: CARD ← 30*1000;
rwCacheSize: CARDINAL ~ 8;
useSendToSelf: BOOL ← TRUE; -- DEBUG
defaultReplyTTL: CARDINAL ← 5; -- seconds
Client Handles
Create:
PUBLIC
PROC [remoteAddress: Address, remotePort: Port]
RETURNS [h: Handle]
~ {
h ← NEW[Object];
h.class ← network;
h.dataBuf ← RefText.ObtainScratch[dataBufBytes];
h.udpHandle ← ArpaUDP.Create[sendBuffers~maxBuffersPerDatagram, recvBuffers~maxBuffersPerDatagram, acceptErrors~TRUE, acceptLongDatagrams~TRUE];
h ← SetRemote[h, remoteAddress, remotePort];
SafeStorage.EnableFinalization[h];
};
GetRemote:
PUBLIC
PROC [h: Handle]
RETURNS [remoteAddress: Address, remotePort: Port]
~ {
remoteAddress ← h.address;
remotePort ← h.port;
};
SetRemote:
PUBLIC
PROC [h: Handle, remoteAddress: Address, remotePort: Port]
RETURNS [newH: Handle]
~ {
h.address ← remoteAddress;
h.port ← remotePort;
h.addressIsMe ← ArpaExtras.IsMyAddress[remoteAddress];
newH ← h;
};
Destroy:
PUBLIC
PROC [h: Handle] ~ {
IF h.class # network THEN ERROR Error[$notNetworkHandle];
FreeTheBuffers[h];
IF h.dataBuf # NIL THEN { RefText.ReleaseScratch[h.dataBuf]; h.dataBuf ← NIL };
IF h.udpHandle # NIL THEN { ArpaUDP.Destroy[h.udpHandle]; h.udpHandle ← NIL };
};
FreeTheBuffers:
PROC [h: Handle] ~
INLINE {
IF h.bHead # NIL THEN { ArpaUDP.FreeBuffers[h.bHead]; h.bHead ← h.bTail ← NIL };
};
Finalization
ofq: SafeStorage.FinalizationQueue;
InitObjectFinalizer:
PROC ~ {
ofq ← SafeStorage.NewFQ[];
SafeStorage.EstablishFinalization[type: CODE[Object], npr: 0, fq: ofq];
TRUSTED { Process.Detach[FORK ObjectFinalizer[]] };
};
ObjectFinalizer:
PROC = {
Process.SetPriority[Process.priorityForeground];
DO
Destroy[NARROW[SafeStorage.FQNext[ofq]]];
ENDLOOP;
};
Clients
StartCall:
PUBLIC
PROC [h: Handle, c: Conversation, pgm, version, proc:
CARD] ~ {
cFlavor, vFlavor: AuthFlavor;
credentials, verifier: AuthValue;
FreeTheBuffers[h];
h.index ← h.limit ← 0;
h.dataBuf.length ← h.dataBuf.maxLength;
h.xid ← h.xid + 1;
h.authData ← c;
IF h.class # network THEN ERROR Error[$notNetworkHandle];
Fill in the call message header ...
SunRPC.PutCard32[h, h.xid]; -- xid
SunRPC.PutCard32[h, ORD[RPCT.MsgType.call]]; -- msgType
SunRPC.PutCard32[h, RPCT.rpcVersion]; -- rpcvers
SunRPC.PutCard32[h, pgm]; -- prog
SunRPC.PutCard32[h, version]; -- vers
SunRPC.PutCard32[h, proc]; -- proc
[cFlavor, credentials, vFlavor, verifier] ← SunRPCAuth.GetCredentialsAndNextVerifier[c];
SunRPC.PutCard32[h, cFlavor];
SunRPC.PutCard32[h, credentials.length];
IF credentials.length > 0 THEN PutBlock[h, credentials, 0, credentials.length];
SunRPC.PutCard32[h, vFlavor];
SunRPC.PutCard32[h, verifier.length];
IF verifier.length > 0 THEN PutBlock[h, verifier, 0, credentials.length];
};
SendCallAndReceiveReply:
PUBLIC
PROC [h: Handle, timeoutMsec:
CARD, retries:
CARD]
RETURNS [remoteAddress: Address, remotePort: Port] ~ {
sB: ArpaUDP.Buffer;
IF h.class # network THEN ERROR Error[$notNetworkHandle];
OutOfLinePush[h]; -- make sure all the data is in the send buffer
IF (sB ← h.bHead) = NIL THEN ERROR Error[$outOfData];
ArpaIP.SetUserBytes[IPBuffer[h.bTail], h.bLimit];
h.bytesInSendBuffers ← h.bytesInSendBuffers + h.bLimit;
h.bHead ← h.bTail ← NIL;
sB.hdr2.length ← Basics.HFromCard16[h.bytesInSendBuffers];
[remoteAddress, remotePort] ← SendAndReceive[h, timeoutMsec, retries, sB];
};
ReceiveAnotherReply:
PUBLIC
PROC [h: Handle, timeoutMsec:
CARD]
RETURNS [remoteAddress: Address, remotePort: Port] ~ {
IF h.class # network THEN ERROR Error[$notNetworkHandle];
[remoteAddress, remotePort] ← SendAndReceive[h, timeoutMsec, 0, NIL];
};
SendAndReceive:
PROC [h: Handle, timeoutMsec:
CARD, retries:
CARD, sB: ArpaUDP.Buffer]
RETURNS [remoteAddress: Address, remotePort: Port] ~ {
errorCode: ATOM ← NIL;
replyVerifier: AuthValue;
timeoutMsec ← MAX[timeoutMsec, minTimeout];
retries ← MIN[retries, maxRetries];
IF retries > 0
THEN timeoutMsec ←
MIN[timeoutMsec, maxTimeout];
IF sB #
NIL
THEN {
IF h.addressIsMe
AND useSendToSelf
THEN { ArpaUDP.SendToSelf[sB, h.address, h.port]; sB ← NIL }
ELSE ArpaUDP.Send[sB, h.address, h.port
! ArpaUDP.Error => { errorCode ← code; CONTINUE }];
};
IF errorCode =
NIL
THEN
DO
errorCode ← NIL;
FreeTheBuffers[h];
ArpaUDP.SetGetTimeout[h.udpHandle, timeoutMsec];
h.bHead ← ArpaUDP.Get[h.udpHandle
! ArpaUDP.ReceivedError => { errorCode ← code; CONTINUE }];
SELECT errorCode
FROM
NIL => {
IF h.bHead #
NIL
THEN {
Insert the received datagram into h ...
h.bIndex ← ArpaUDPBuf.hdrBytes;
h.bLimit ← ArpaIP.GetUserBytes[IPBuffer[h.bHead]].bodyBytes;
h.index ← h.limit ← 0;
Is it from the guy I'm interested in?
[remoteAddress, remotePort] ← ArpaUDP.GetSource[h.bHead];
IF remoteAddress # h.address
THEN
IF
NOT ArpaExtras.IsBroadcast[h.address]
THEN LOOP;
Is it a reply message for this call?
{
ENABLE Error =>
LOOP;
returnedXid, returnedMsgType: CARD32;
returnedXid ← SunRPC.GetCard32[h];
returnedMsgType ← SunRPC.GetCard32[h];
IF (returnedXid # h.xid) OR (returnedMsgType # ORD[RPCT.MsgType.reply]) THEN LOOP;
};
At this point, committed to accepting the reply message. Parse it, switching on replyStat ...
{
ENABLE Error => { errorCode ← $protocolError;
EXIT };
replyStat, acceptStat, rejectStat, authStat: CARD32;
replyFlavor: AuthFlavor;
SELECT (replyStat ← SunRPC.GetCard32[h])
FROM
ORD[
RPCT.ReplyStat.msgAccepted] => {
[replyFlavor, replyVerifier] ← GetAuth[h];
SELECT SunRPCAuth.CheckReplyVerifier[
NARROW[h.authData], replyFlavor, replyVerifier]
FROM
ok => NULL;
badVerifier => { errorCode ← $badReplyVerifier; EXIT };
wrongVerifier => { errorCode ← $wrongReplyVerifier; EXIT };
ENDCASE => ERROR;
acceptStat ← SunRPC.GetCard32[h];
errorCode ←
SELECT acceptStat
FROM
ORD[RPCT.AcceptStat.success] => NIL, -- winner!
ORD[RPCT.AcceptStat.progUnavail] => $wrongProgram,
ORD[RPCT.AcceptStat.progMismatch] => $wrongProgramVersion,
ORD[RPCT.AcceptStat.procUnavail] => $wrongProc,
ENDCASE => $protocolError;
EXIT;
};
ORD[
RPCT.ReplyStat.msgDenied] => {
SELECT (rejectStat ← SunRPC.GetCard32[h])
FROM
ORD[
RPCT.RejectStat.rpcMismatch] => {
errorCode ← $wrongRPCVersion;
EXIT;
};
ORD[
RPCT.RejectStat.authError] => {
authStat ← SunRPC.GetCard32[h];
errorCode ←
SELECT authStat
FROM
ORD[RPCT.AuthStat.authBadcred] => $badCredentials,
ORD[RPCT.AuthStat.authRejectedcred] => $wrongCredentials,
ORD[RPCT.AuthStat.authBadverf] => $badVerifier,
ORD[RPCT.AuthStat.authRejectedverf] => $wrongVerifier,
ORD[RPCT.AuthStat.authTooweak] => $weakCredentials,
ENDCASE => $protocolError;
EXIT;
};
ENDCASE => { errorCode ← $protocolError; EXIT };
};
ENDCASE => { errorCode ← $protocolError; EXIT };
};
};
At this point the ArpaUDP.Get[...] timed out without receiving a datagram.
IF (retries > 0)
AND (sB #
NIL)
THEN {
retries ← retries - 1;
IF h.addressIsMe
AND useSendToSelf
THEN { ArpaUDP.SendToSelf[sB, h.address, h.port]; sB ← NIL }
ELSE ArpaUDP.Send[sB, h.address, h.port
! ArpaUDP.Error => { errorCode ← code; EXIT }];
LOOP;
};
errorCode ← $timeout;
EXIT;
};
$timeoutTimeToLive, $timeoutReassembly, $sourceQuench => {
LOOP;
};
$netUnreachable, $hostUnreachable, $portUnreachable => {
errorCode ← $unreachable;
EXIT;
};
ENDCASE => {
errorCode ← $protocolError;
EXIT;
};
ENDLOOP;
IF replyVerifier #
NIL
THEN { RefText.ReleaseScratch[replyVerifier]; replyVerifier ← NIL };
IF sB #
NIL
THEN { ArpaUDP.FreeBuffers[sB] };
IF errorCode #
NIL
THEN { FreeTheBuffers[h]; ERROR Error[errorCode] };
};
ReleaseReply:
PUBLIC
PROC [h: Handle] ~ {
IF h.class # network THEN ERROR Error[$notNetworkHandle];
h.authData ← NIL; -- help finalization
FreeTheBuffers[h];
};
Server Registration
CreateServer:
PUBLIC
PROC [pgm, version:
CARD, serverProc: SunRPC.ServerProc, port: Port, concurrency:
CARDINAL, clientData:
REF]
RETURNS [s: Server]
~ {
buffers: CARDINAL;
concurrency ← MAX[concurrency, 1];
concurrency ← MIN[concurrency, maxProcessesPerServer];
buffers ← maxBuffersPerDatagram * concurrency;
s ← NEW[ServerObject[concurrency+1]];
s.pgm ← pgm;
s.version ← version;
s.udpHandle ← ArpaUDP.Create[localPort ~ port, sendBuffers~buffers, recvBuffers~buffers, getTimeout~ArpaUDP.waitForever];
s.clientData ← clientData;
s.serverProc ← serverProc;
FOR i:
CARDINAL
IN [0..concurrency+1)
DO
h: Handle ← NEW[Object];
h.class ← network;
h.dataBuf ← RefText.ObtainScratch[dataBufBytes];
h.udpHandle ← s.udpHandle;
s.handles[i] ← h;
ENDLOOP;
SafeStorage.EnableFinalization[s];
TRUSTED { Process.Detach[ FORK Serve[s] ] };
TRUSTED { Process.Detach[ FORK AgeResults[s] ] };
};
GetServerPort:
PUBLIC
PROC [s: Server]
RETURNS [port: ArpaUDP.Port] ~ {
RETURN[ ArpaUDP.GetLocalPort[s.udpHandle] ];
};
DestroyServer:
PUBLIC
ENTRY
PROC [s: Server] ~ {
s.dead ← TRUE;
ArpaUDP.Kick[s.udpHandle];
Drop the server, let finalization finish it off.
};
Finalization
Statistics
droppedServers: CARD ← 0;
finishedServers: CARD ← 0;
sfq: SafeStorage.FinalizationQueue;
InitServerFinalizer:
PROC ~ {
sfq ← SafeStorage.NewFQ[];
SafeStorage.EstablishFinalization[type: CODE[SunRPCPrivate.ServerObject], npr: 0, fq: sfq];
TRUSTED { Process.Detach[FORK ServerFinalizer[]] };
};
ServerFinalizer:
PROC = {
Process.SetPriority[Process.priorityForeground];
DO
s: Server ← NARROW[SafeStorage.FQNext[sfq]];
IF
NOT s.dead
THEN {
-- Can't happen unless the daemons have failed for some reason ...
droppedServers ← droppedServers.SUCC;
SafeStorage.EnableFinalization[s];
DestroyServer[s];
}
ELSE {
-- Normal end of life
finishedServers ← finishedServers.SUCC;
FOR i:
CARDINAL
IN [0 .. s.concurrencyPlusOne)
DO
h: Handle ~ s.handles[i];
IF h #
NIL
THEN {
FreeTheBuffers[h];
IF h.dataBuf #
NIL
THEN { RefText.ReleaseScratch[h.dataBuf]; h.dataBuf ← NIL };
h.udpHandle ← NIL;
};
ENDLOOP;
IF s.udpHandle #
NIL
THEN { ArpaUDP.Destroy[s.udpHandle]; s.udpHandle ← NIL };
};
ENDLOOP;
};
Servers
GetFreeHandle:
ENTRY
PROC [s: Server]
RETURNS [handle: Handle] ~ {
DO
i: CARDINAL ← s.freeHandleIndex;
bestTTL: CARDINAL ← CARDINAL.LAST;
bestIndex: CARDINAL ← s.concurrencyPlusOne;
THROUGH [0 .. s.concurrencyPlusOne)
DO
h: Handle;
IF (i ← i + 1) >= s.concurrencyPlusOne THEN i ← 0;
h ← s.handles[i];
IF (
NOT h.busy)
AND (h.ttl < bestTTL)
THEN { bestTTL ← h.ttl; bestIndex ← i };
ENDLOOP;
IF bestIndex < s.concurrencyPlusOne
THEN {
handle ← s.handles[bestIndex];
handle.busy ← TRUE;
RETURN;
};
IF s.dead THEN RETURN;
WAIT s.freeHandle;
ENDLOOP;
};
GetThisFreeHandle:
ENTRY
PROC [s: Server, h: Handle]
RETURNS [gotIt:
BOOL] ~ {
IF h.busy THEN RETURN [FALSE];
RETURN [h.busy ← TRUE];
};
NotifyFreeHandle:
ENTRY
PROC [s: Server, h: Handle] ~ {
h.busy ← FALSE;
BROADCAST s.freeHandle;
};
AgeResults:
PROC [s: Server] ~ {
DO
buffersToFree: ArpaUDP.Buffers;
IF (buffersToFree ← AgeResultsInner[s]) #
NIL
-- NIL only if s.dead --
THEN ArpaUDP.FreeBuffers[buffersToFree];
IF s.dead THEN EXIT;
Process.PauseMsec[1000];
ENDLOOP;
};
AgeResultsInner:
ENTRY
PROC [s: Server]
RETURNS [buffersToFree: ArpaUDP.Buffers] ~ {
DO
tail: ArpaUDP.Buffer ← NIL;
FOR i:
CARDINAL
IN [0 .. s.concurrencyPlusOne)
DO
h: Handle ~ s.handles[i];
IF h.busy THEN LOOP;
IF h.ttl > 0 THEN h.ttl ← h.ttl - 1;
IF (h.ttl = 0)
AND (h.bHead #
NIL)
THEN {
IF buffersToFree =
NIL
THEN buffersToFree ← h.bHead
ELSE tail.ovh.next ← h.bHead;
tail ← h.bTail;
h.bHead ← h.bTail ← NIL;
};
ENDLOOP;
IF (buffersToFree # NIL) OR s.dead THEN EXIT;
WAIT s.freeHandle;
ENDLOOP;
};
Serve:
PROC [s: Server] ~ {
h: Handle ← GetFreeHandle[s];
WHILE
NOT s.dead
DO
Attach the incoming datagram to the free handle ...
h.bHead ← ArpaUDP.Get[s.udpHandle
-- ! ArpaUDP.ReceivedError => CONTINUE can't happen -- ];
IF h.bHead = NIL THEN LOOP;
[h.address, h.port] ← ArpaUDP.GetSource[h.bHead];
h.addressIsMe ← ArpaExtras.IsMyAddress[h.address];
h.bIndex ← ArpaUDPBuf.hdrBytes;
h.bLimit ← ArpaIP.GetUserBytes[IPBuffer[h.bHead]].bodyBytes;
h.index ← h.limit ← 0;
{
ENABLE Error =>
CONTINUE;
msgType: CARD32;
h.xid ← SunRPC.GetCard32[h];
msgType ← SunRPC.GetCard32[h];
SELECT
TRUE
FROM
(msgType #
ORD[
RPCT.MsgType.call]) => {
NULL;
};
ENDCASE => {
isDuplicate: BOOL ← FALSE;
finger: Handle;
FOR i:
CARDINAL
IN [0 .. s.concurrencyPlusOne)
DO
finger ← s.handles[i];
IF (finger # h)
AND (finger.xid = h.xid)
AND (finger.port = h.port)
AND (finger.address = h.address)
AND (finger.ttl > 0)
THEN { isDuplicate ← TRUE; EXIT };
ENDLOOP;
IF isDuplicate
THEN {
IF GetThisFreeHandle[s, finger]
THEN {
SELECT
TRUE
FROM
(finger.bHead #
NIL) => {
TRUSTED { Process.Detach[FORK SendDuplicateReply[s, finger]] };
};
ENDCASE => {
TRUSTED { Process.Detach[FORK CallServerProcAndSendReply[s, h]] };
h ← finger;
};
};
}
ELSE
-- not a duplicate -- {
finger ← GetFreeHandle[s]; -- may block
TRUSTED { Process.Detach[FORK CallServerProcAndSendReply[s, h]] };
h ← finger;
};
};
};
IF h # NIL -- h=NIL only if s.dead -- THEN FreeTheBuffers[h];
ENDLOOP;
};
SendDuplicateReply:
PROC [s: Server, h: Handle] ~ {
ArpaUDP.Send[h.bHead, h.address, h.port];
h.ttl ← MAX[h.ttl, defaultReplyTTL];
NotifyFreeHandle[s, h];
};
StartAcceptReply:
PUBLIC
PROC [h: Handle, acceptStat:
CARD32]
~ {
Fill in the reply message header ... through acceptStat
FreeTheBuffers[h];
h.index ← h.limit ← 0;
SunRPC.PutCard32[h, h.xid]; -- xid
SunRPC.PutCard32[h, ORD[RPCT.MsgType.reply]]; -- msgType
SunRPC.PutCard32[h, ORD[RPCT.ReplyStat.msgAccepted]]; -- replyStat
PutAuth[h, h.authFlavor, NARROW[h.authData]]; -- replyVerifier
SunRPC.PutCard32[h, acceptStat]; -- acceptStat
};
StartRejectReply:
PUBLIC
PROC [h: Handle, rejectStat:
CARD32] ~ {
Fill in the reply message header ... through rejectStat
FreeTheBuffers[h];
h.index ← h.limit ← 0;
SunRPC.PutCard32[h, h.xid]; -- xid
SunRPC.PutCard32[h, ORD[RPCT.MsgType.reply]]; -- msgType
SunRPC.PutCard32[h, ORD[RPCT.ReplyStat.msgDenied]]; -- replyStat
SunRPC.PutCard32[h, rejectStat]; -- rejectStat
};
StartReply:
PUBLIC
PROC [h: Handle] ~ {
IF h.class # network THEN ERROR Error[$notNetworkHandle];
StartAcceptReply[h, ORD[RPCT.AcceptStat.success]];
};
CallServerProcAndSendReply:
PROC [s: Server, h: Handle] ~ {
errorCode: ATOM;
credentials, verifier: AuthValue;
sendReply: BOOL ← TRUE;
h.ttl ← defaultReplyTTL;
BEGIN
{
ENABLE Error => { sendReply ←
FALSE;
GOTO Out };
rpcvers, prog, vers, proc: CARD32;
cFlavor, vFlavor: AuthFlavor;
authResult: SunRPCAuth.AuthenticateResult;
conversation: Conversation;
Check RPC version (else we can't parse the message)
rpcvers ← SunRPC.GetCard32[h];
IF (rpcvers #
RPCT.rpcVersion)
THEN { errorCode ← $wrongRPCVersion; GOTO Reply };
Get <prog, vers, proc>. There's nothing we can do with them yet, until we've examined the credentials and verifier, but that's the way they defined the protocol.
prog ← SunRPC.GetCard32[h];
vers ← SunRPC.GetCard32[h];
proc ← SunRPC.GetCard32[h];
Get credentials ...
[cFlavor, credentials] ← GetAuth[h
! Error => { errorCode ← $badCredentials; GOTO Reply }];
Get verifier ...
[vFlavor, verifier] ← GetAuth[h
! Error => { errorCode ← $badVerifier; GOTO Reply }];
Authenticate ...
[authResult, h.authFlavor, h.authData, conversation] ← SunRPCAuth.Authenticate[cFlavor, credentials, vFlavor, verifier];
IF authResult # ok
THEN {
errorCode ←
SELECT authResult
FROM
badCredentials => $badCredentials,
wrongCredentials => $wrongCredentials,
badVerifier => $badVerifier,
wrongVerifier => $wrongVerifier,
ENDCASE => ERROR;
GOTO Reply;
};
Check program, version ...
IF prog # s.pgm
THEN { errorCode ← $wrongProgram; GOTO Reply };
IF vers # s.version
THEN { errorCode ← $wrongProgramVersion; GOTO Reply };
Call the server proc!
[sendReply, h.ttl] ← s.serverProc[h, conversation, proc, s.clientData
! Error => { errorCode ← code; CONTINUE }];
};
{
ENABLE Error =>
ERROR;
SELECT errorCode
FROM
NIL => NULL;
$wrongRPCVersion => {
StartRejectReply[h, ORD[RPCT.RejectStat.rpcMismatch]];
SunRPC.PutCard32[h, RPCT.rpcVersion];
SunRPC.PutCard32[h, RPCT.rpcVersion];
};
$badCredentials => {
StartRejectReply[h, ORD[RPCT.RejectStat.authError]];
SunRPC.PutCard32[h, ORD[RPCT.AuthStat.authBadcred]];
};
$wrongCredentials => {
StartRejectReply[h, ORD[RPCT.RejectStat.authError]];
SunRPC.PutCard32[h, ORD[RPCT.AuthStat.authRejectedcred]];
};
$badVerifier => {
StartRejectReply[h, ORD[RPCT.RejectStat.authError]];
SunRPC.PutCard32[h, ORD[RPCT.AuthStat.authBadverf]];
};
$wrongVerifier => {
StartRejectReply[h, ORD[RPCT.RejectStat.authError]];
SunRPC.PutCard32[h, ORD[RPCT.AuthStat.authRejectedverf]];
};
$weakCredentials => {
StartRejectReply[h, ORD[RPCT.RejectStat.authError]];
SunRPC.PutCard32[h, ORD[RPCT.AuthStat.authTooweak]];
};
$wrongProgram => {
StartAcceptReply[h, ORD[RPCT.AcceptStat.progUnavail]];
};
$wrongProgramVersion => {
StartAcceptReply[h, ORD[RPCT.AcceptStat.progMismatch]];
SunRPC.PutCard32[h, s.version];
SunRPC.PutCard32[h, s.version];
};
$wrongProc => {
StartAcceptReply[h, ORD[RPCT.AcceptStat.procUnavail]];
};
$abortWithoutReturn => {
sendReply ← FALSE;
};
ENDCASE => {
StartAcceptReply[h, ORD[RPCT.AcceptStat.garbageArgs]];
};
};
END;
IF sendReply
THEN
-- send the reply -- {
OutOfLinePush[h];
IF h.bHead #
NIL
THEN {
ArpaIP.SetUserBytes[IPBuffer[h.bTail], h.bLimit];
h.bytesInSendBuffers ← h.bytesInSendBuffers + h.bLimit;
h.bHead.hdr2.length ← Basics.HFromCard16[h.bytesInSendBuffers];
IF h.addressIsMe
AND useSendToSelf
THEN {
ArpaUDP.SendToSelf[h.bHead, h.address, h.port];
h.bHead ← h.bTail ← NIL;
}
ELSE {
ArpaUDP.Send[h.bHead, h.address, h.port ! ArpaUDP.Error => CONTINUE];
};
};
}
ELSE {
FreeTheBuffers[h];
};
h.authData ← NIL; -- help finalization
NotifyFreeHandle[s, h];
IF credentials # NIL THEN { RefText.ReleaseScratch[credentials]; credentials ← NIL };
IF verifier # NIL THEN { RefText.ReleaseScratch[verifier]; verifier ← NIL };
};
Serializing / Deserializing
Pull:
PROC [h: Handle, bytes:
CARDINAL] ~
INLINE {
IF h.index + bytes > h.limit THEN OutOfLinePull[h, bytes];
};
IncGet:
PROC [h: Handle, bytes:
CARDINAL] ~
INLINE {
h.index ← h.index + bytes;
};
OutOfLinePull:
PROC [h: Handle, bytesWanted:
CARDINAL] ~ {
Ensure at least bytesWanted contiguous bytes appear in h.dataBuf if that's possible.
Preserve (h.index MOD 4). This is important for GetAlign[].
For a network handle, the max legal Pull request is less than half the length of the data buffer, guaranteeing that the call to UnsafeGetBlockInner will specify nonoverlapping fields. This is a bit subtle.
dB: REF TEXT;
bytesInDataBuf, newIndex: CARDINAL;
IF h.class = local THEN ERROR Error[$outOfData];
dB ← h.dataBuf;
newIndex ← (h.index MOD 4);
bytesInDataBuf ← h.limit - h.index;
IF (bytesInDataBuf + newIndex) <= h.index
THEN {
TRUSTED { bytesInDataBuf ← UnsafeGetBlockInner[h, [base~TextPtrFromRefText[dB], startIndex~newIndex, count~dB.maxLength-newIndex]] };
h.index ← newIndex;
dB.length ← h.limit ← (newIndex + bytesInDataBuf);
};
IF bytesInDataBuf < bytesWanted THEN ERROR Error[$outOfData];
};
BytesRemaining:
PUBLIC
PROC [h: Handle]
RETURNS [bytes:
CARDINAL] ~ {
b: ArpaUDP.Buffer;
bytes ← h.limit ← h.index;
IF h.class = network
THEN {
IF (b ← h.bHead) #
NIL
THEN {
bytes ← bytes + (h.bLimit - h.bIndex);
b ← Next[b];
};
WHILE b #
NIL
DO
bytes ← bytes + ArpaIP.GetUserBytes[IPBuffer[b]].bodyBytes;
b ← Next[b];
ENDLOOP;
};
};
GetByte:
PUBLIC
PROC [h: Handle]
RETURNS [byte:
BYTE] ~ {
dB: REF TEXT ~ h.dataBuf;
Pull[h, BYTES[BYTE]];
byte ← ORD[dB[h.index]];
IncGet[h, BYTES[BYTE]];
};
GetH:
PUBLIC
PROC [h: Handle]
RETURNS [hword: Basics.
HWORD] ~ {
Pull[h, BYTES[Basics.HWORD]];
TRUSTED { hword ← (HPtrFromRefTextAndIndex[h.dataBuf, h.index])^ };
IncGet[h, BYTES[Basics.HWORD]];
};
GetF:
PUBLIC
PROC [h: Handle]
RETURNS [fword: Basics.
FWORD] ~ {
Pull[h, BYTES[Basics.FWORD]];
TRUSTED { fword ← (FPtrFromRefTextAndIndex[h.dataBuf, h.index])^ };
IncGet[h, BYTES[Basics.FWORD]];
};
UnsafeGetBlock:
PUBLIC
UNSAFE
PROC [h: Handle, block: UnsafeBlock]
~ UNCHECKED {
IF UnsafeGetBlockInner[h, block] # block.count THEN ERROR Error[$outOfData];
};
UnsafeGetBlockInner:
UNSAFE
PROC [h: Handle, block: UnsafeBlock]
RETURNS[bytesMoved:
CARDINAL]
~ {
Preserves (h.index MOD 4).
bytesLeft: CARDINAL ← block.count;
toOffset: CARDINAL ← block.startIndex;
count: CARDINAL;
IF bytesLeft = 0 THEN RETURN[0];
IF (count ←
MIN[bytesLeft, h.limit - h.index]) > 0
THEN {
TRUSTED { [] ← PrincOpsUtils.ByteBlt[to~[block.base, toOffset, toOffset+count], from~[TextPtrFromRefText[h.dataBuf], h.index, h.index+count]] };
toOffset ← toOffset + count;
h.index ← h.index + count;
bytesLeft ← bytesLeft - count;
};
WHILE bytesLeft > 0
DO
b: ArpaUDP.Buffer ← h.bHead;
IF b = NIL THEN EXIT;
IF (count ←
MIN[bytesLeft, h.bLimit - h.bIndex]) > 0
THEN {
TRUSTED { [] ← PrincOpsUtils.ByteBlt[to~[block.base, toOffset, toOffset+count], from~[@b.hdr2, h.bIndex, h.bIndex+count]] };
toOffset ← toOffset + count;
h.bIndex ← h.bIndex + count;
bytesLeft ← bytesLeft - count;
IF h.index # h.limit THEN ERROR; -- DEBUG
h.index ← h.limit ← ((h.index + count) MOD 4);
};
IF h.bIndex >= h.bLimit
THEN {
h.bHead ← Next[b];
b.ovh.next ← NIL;
ArpaUDP.FreeBuffers[b];
h.bIndex ← 0;
IF h.bHead # NIL THEN h.bLimit ← ArpaIP.GetUserBytes[IPBuffer[h.bHead]].bodyBytes;
};
ENDLOOP;
RETURN[block.count - bytesLeft];
};
GetBlock:
PUBLIC
PROC [h: Handle, block:
REF
TEXT, startIndex, count:
CARDINAL] ~ {
actualCount: CARDINAL;
IF startIndex > block.length THEN ERROR;
count ← MIN[count, block.maxLength-startIndex];
TRUSTED { actualCount ← UnsafeGetBlockInner[h, [base~TextPtrFromRefText[block], startIndex~startIndex, count~count]] };
block.length ← startIndex + actualCount;
IF actualCount # count THEN ERROR Error[$outOfData];
};
GetAlign:
PUBLIC
PROC [h: Handle] ~ {
n: CARDINAL;
IF (n ← (h.index MOD 4)) = 0 THEN RETURN;
n ← 4 - n;
Pull[h, n];
IncGet[h, n];
};
GetRefText:
PUBLIC
PROC [h: Handle]
RETURNS [refText:
REF
TEXT] ~ {
length: CARD ← SunRPC.GetCard32[h];
IF length > maxRefTextLength THEN ERROR Error[$outOfBufferSpace];
refText ← RefText.New[length];
GetBlock[h, refText, 0, length];
GetAlign[h];
};
GetEphemeralRefText:
PUBLIC
PROC [h: Handle, oldBuffer: REF TEXT]
RETURNS [newBuffer:
REF
TEXT] ~ {
length: CARD ← SunRPC.GetCard32[h];
IF length > maxRefTextLength THEN ERROR Error[$outOfBufferSpace];
SELECT
TRUE
FROM
(oldBuffer =
NIL) => {
newBuffer ← RefText.ObtainScratch[length];
};
(oldBuffer.maxLength < length) => {
RefText.ReleaseScratch[oldBuffer];
newBuffer ← RefText.ObtainScratch[length];
};
ENDCASE => {
newBuffer ← oldBuffer;
};
IF (newBuffer.length ← length) > 0
THEN {
GetBlock[h, newBuffer, 0, length];
GetAlign[h];
};
};
GetRope:
PUBLIC
PROC [h: Handle]
RETURNS [
ROPE] ~ {
temp: REF TEXT ← GetEphemeralRefText[h, NIL];
result: ROPE ← Rope.FromRefText[temp];
RefText.ReleaseScratch[temp];
RETURN [result];
};
GetAuth:
PROC [h: Handle]
RETURNS [flavor: AuthFlavor, value: AuthValue] ~ {
BEWARE: the returned value is ephemeral (allocated with ObtainScratch rather than New).
flavor ← [SunRPC.GetCard32[h]];
value ← GetEphemeralRefText[h, NIL];
};
AllocFirstSendBuffer:
PROC [h: Handle]
RETURNS [b: ArpaUDP.Buffer] ~ {
h.bIndex ← h.bLimit ← ArpaUDPBuf.hdrBytes;
b ← h.bHead ← h.bTail ← ArpaUDP.AllocBuffers[h.udpHandle, 1];
h.sendBuffersInUse ← 1;
h.bytesInSendBuffers ← 0;
};
AllocNextSendBuffer:
PROC [h: Handle]
RETURNS [b: ArpaUDP.Buffer] ~ {
IF (h.bLimit # maxBLimit) THEN ERROR; -- DEBUG
ArpaIP.SetUserBytes[IPBuffer[h.bTail], h.bLimit];
IF h.sendBuffersInUse >= maxBuffersPerDatagram
THEN ERROR Error[$outOfBufferSpace];
h.bytesInSendBuffers ← h.bytesInSendBuffers + h.bLimit;
h.bIndex ← h.bLimit ← 0;
b ← ArpaUDP.AllocBuffers[h.udpHandle, 1];
h.bTail.ovh.next ← b;
h.bTail ← b;
h.sendBuffersInUse ← h.sendBuffersInUse + 1;
};
Push:
PROC [h: Handle, bytes:
CARDINAL] ~
INLINE {
IF h.limit + bytes > h.dataBuf.maxLength THEN OutOfLinePush[h];
};
IncPut:
PROC [h: Handle, bytes:
CARDINAL] ~
INLINE {
h.limit ← h.limit + bytes;
};
OutOfLinePush:
PROC [h: Handle] ~ {
Move everything from the h.dataBuf into the buffer chain.
Preserve (h.limit MOD 4).
bytesLeft: CARDINAL;
IF h.class = local THEN ERROR Error[$outOfBufferSpace];
IF (bytesLeft ← h.limit - h.index) > 0
THEN {
b: ArpaUDP.Buffer;
IF (b ← h.bTail) = NIL THEN b ← AllocFirstSendBuffer[h];
DO
count: CARDINAL;
IF (count ←
MIN[bytesLeft, maxBLimit - h.bLimit]) > 0
THEN {
TRUSTED { [] ← PrincOpsUtils.ByteBlt[to~[@b.hdr2, h.bLimit, h.bLimit+count], from~[TextPtrFromRefText[h.dataBuf], h.index, h.index + count]] };
h.bLimit ← h.bLimit + count;
h.index ← h.index + count;
bytesLeft ← bytesLeft - count;
};
IF bytesLeft = 0 THEN EXIT;
IF h.bLimit # maxBLimit THEN ERROR; -- DEBUG
b ← AllocNextSendBuffer[h];
ENDLOOP;
};
h.index ← h.limit ← (h.limit MOD 4);
};
PutByte:
PUBLIC
PROC [h: Handle
, byte:
BYTE] ~ {
dB: REF TEXT ~ h.dataBuf;
Push[h, BYTES[BYTE]];
dB[h.limit] ← VAL[byte];
IncPut[h, BYTES[BYTE]];
};
PutH:
PUBLIC
PROC [h: Handle, hword: Basics.
HWORD] ~ {
Push[h, BYTES[Basics.HWORD]];
TRUSTED { HPtrFromRefTextAndIndex[h.dataBuf, h.limit]^ ← hword };
IncPut[h, BYTES[Basics.HWORD]];
};
PutF:
PUBLIC
PROC [h: Handle, fword: Basics.
FWORD] ~ {
Push[h, BYTES[Basics.FWORD]];
TRUSTED { FPtrFromRefTextAndIndex[h.dataBuf, h.limit]^ ← fword };
IncPut[h, BYTES[Basics.FWORD]];
};
UnsafePutBlock:
PUBLIC
UNSAFE
PROC [h: Handle, block: UnsafeBlock]
~ {
bytesLeft: CARDINAL ← block.count;
fromOffset: CARDINAL ← block.startIndex;
b: ArpaUDP.Buffer;
IF bytesLeft = 0 THEN RETURN;
IF h.class = local
THEN {
IF (h.limit + bytesLeft) <= h.dataBuf.maxLength
THEN {
TRUSTED { [] ← PrincOpsUtils.ByteBlt[to~[TextPtrFromRefText[h.dataBuf], h.limit, h.limit+bytesLeft], from~[block.base, fromOffset, fromOffset+bytesLeft]] };
h.limit ← h.limit + bytesLeft;
RETURN;
}
ELSE {
ERROR Error[$outOfBufferSpace];
};
OutOfLinePush[h];
IF (b ← h.bTail) = NIL THEN b ← AllocFirstSendBuffer[h];
DO
count: CARDINAL;
IF (count ←
MIN[bytesLeft, maxBLimit - h.bLimit]) > 0
THEN {
TRUSTED { [] ← PrincOpsUtils.ByteBlt[to~[@b.hdr2, h.bLimit, h.bLimit+count], from~[block.base, fromOffset, fromOffset+count]] };
fromOffset ← fromOffset + count;
h.bLimit ← h.bLimit + count;
bytesLeft ← bytesLeft - count;
};
IF bytesLeft = 0 THEN EXIT;
IF h.bLimit # maxBLimit THEN ERROR; -- DEBUG
b ← AllocNextSendBuffer[h];
ENDLOOP;
h.index ← h.limit ← (h.bLimit MOD 4);
};
PutBlock:
PUBLIC
PROC [h: Handle, block:
REF
READONLY
TEXT, startIndex:
CARDINAL ← 0, count:
CARDINAL]
~ TRUSTED {
IF startIndex > block.length THEN ERROR;
count ← MIN[count, block.length-startIndex];
UnsafePutBlock[h, [base~TextPtrFromRefText[block], startIndex~startIndex, count~count]];
};
PutAlign:
PUBLIC
PROC [h: Handle, padValue:
BYTE] ~ {
WHILE (h.limit
MOD 4) # 0
DO
PutByte[h, padValue];
ENDLOOP;
};
PutRefText:
PUBLIC
PROC [h: Handle, refText:
REF
READONLY
TEXT] ~ {
SunRPC.PutCard32[h, refText.length];
PutBlock[h, refText, 0, refText.length];
PutAlign[h, 0];
};
PutRope:
PUBLIC
PROC [h: Handle, rope:
ROPE] ~ {
IF rope =
NIL
THEN {
SunRPC.PutCard32[h, 0];
}
ELSE
TRUSTED {
PutRefText[h, RefText.TrustTextRopeAsText[Rope.InlineFlatten[rope]]];
};
};
PutAuth:
PROC [h: Handle, flavor: AuthFlavor, value: AuthValue] ~ {
SunRPC.PutCard32[h, flavor];
IF value.length > maxAuthBytes THEN ERROR;
SunRPC.PutCard32[h, value.length];
IF value.length > 0 THEN PutBlock[h, value, 0, value.length];
PutAlign[h, 0];
};
}...