initialStrongVerifier: Auth.StrongVerifier ~ [BasicTime.ToNSTime[BasicTime.earliestGMT], 0];
Key and Password Administration
ChangeMyPasswords:
PUBLIC PROC [myIdentity: Identity, newPassword:
ROPE,
changeStrong:
BOOL ¬
TRUE, changeSimple:
BOOL ¬
TRUE] ~ {
IF changeStrong
THEN {
newKey: Key ¬ StrongKeyFromPassword[newPassword];
ChangeMyStrongKey[myIdentity, newKey] };
IF changeSimple
THEN {
newKey: HashedPassword ¬ SimpleKeyFromPassword[newPassword];
ChangeMySimpleKey[myIdentity, newKey] };
};
CreateStrongKey:
PUBLIC
PROC [myIdentity: Identity, name: Name, newKey: Key] ~ {
c: Conversation;
DoIt:
PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.CreateStrongKey[h, GetCredentials[c], GetNextVerifier[c], name, EncryptKey[GetConversationKey[c], newKey]] };
myIdentity ¬ NonNilIdentity[myIdentity];
c ¬ Initiate[myIdentity, authSvcName, oneMinuteOfSeconds];
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
ChangeMyStrongKey:
PUBLIC
PROC [myIdentity: Identity, newKey: Key] ~ {
c: Conversation;
DoIt:
PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.ChangeStrongKey[h, GetCredentials[c], GetNextVerifier[c], EncryptKey[GetConversationKey[c], newKey]] };
myIdentity ¬ NonNilIdentity[myIdentity];
c ¬ Initiate[myIdentity, authSvcName, oneMinuteOfSeconds];
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
DeleteStrongKey:
PUBLIC
PROC [myIdentity: Identity, name: Name] ~ {
c: Conversation;
DoIt:
PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.DeleteStrongKey[h, GetCredentials[c], GetNextVerifier[c], name] };
myIdentity ¬ NonNilIdentity[myIdentity];
c ¬ Initiate[myIdentity, authSvcName, oneMinuteOfSeconds];
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
CreateSimpleKey:
PUBLIC
PROC [myIdentity: Identity, name: Name, newKey: HashedPassword] ~ {
c: Conversation;
DoIt:
PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.CreateSimpleKey[h, GetCredentials[c], GetNextVerifier[c], name, newKey] };
myIdentity ¬ NonNilIdentity[myIdentity];
c ¬ Initiate[myIdentity, authSvcName, oneMinuteOfSeconds];
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
ChangeMySimpleKey:
PUBLIC PROC [myIdentity: Identity, newKey: HashedPassword] ~ {
c: Conversation;
DoIt:
PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.ChangeSimpleKey[h, GetCredentials[c], GetNextVerifier[c], newKey] };
myIdentity ¬ NonNilIdentity[myIdentity];
c ¬ Initiate[myIdentity, authSvcName, oneMinuteOfSeconds];
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
DeleteSimpleKey:
PUBLIC PROC [myIdentity: Identity, name: Name] ~ {
c: Conversation;
DoIt:
PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.DeleteSimpleKey[h, GetCredentials[c], GetNextVerifier[c], name] };
myIdentity ¬ NonNilIdentity[myIdentity];
c ¬ Initiate[myIdentity, authSvcName, oneMinuteOfSeconds];
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
Private Utilities
EqualNames:
PROC [a: Name, b: Name]
RETURNS [
BOOL] ~ {
RETURN[Rope.Equal[a.organization, b.organization, FALSE]
AND Rope.Equal[a.domain, b.domain, FALSE]
AND Rope.Equal[a.object, b.object, FALSE]] };
EqualSeqWords:
PROC [a, b: SeqWords]
RETURNS [
BOOL] ~ {
IF a.length # b.length THEN RETURN [FALSE];
FOR i:
CARDINAL
IN [0..a.length)
DO
IF a.body[i] # b.body[i] THEN RETURN [FALSE];
ENDLOOP;
RETURN [TRUE] };
IncrementStrongVerifier:
PROC [v: Auth.StrongVerifier]
RETURNS [result: Auth.StrongVerifier] ~ {
result.timeStamp ¬ v.timeStamp;
IF (result.ticks ¬ v.ticks + 1) = 0
THEN result.timeStamp ¬ result.timeStamp + 1;
};
StrongVerifierGT:
PROC [new, old: Auth.StrongVerifier]
RETURNS [gt:
BOOL] ~ {
deltaT: INT ~ BasicTime.Period[from~BasicTime.FromNSTime[old.timeStamp], to~BasicTime.FromNSTime[new.timeStamp]];
SELECT deltaT
FROM
> 0 => RETURN [TRUE];
< 0 => RETURN [FALSE];
0 => RETURN [new.ticks > old.ticks];
ENDCASE => RETURN [new.ticks > old.ticks];
};
StrongVerifierExpired:
PROC [v: Auth.StrongVerifier]
RETURNS [expired:
BOOL] ~ {
deltaT: INT ~ BasicTime.Period[from~BasicTime.FromNSTime[v.timeStamp], to~Now[]];
RETURN [ deltaT > strongVerifierTimeout ] };
FetchRopeFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, r:
ROPE] ~ {
i, count, seqLen: CARDINAL;
w: Basics.Word16;
buf: REF TEXT;
seqLen ¬ s.length;
IF where >= seqLen THEN ERROR MarshallError;
count ¬ s.body[where]; where ¬ where + 1;
buf ¬ RefText.ObtainScratch[count];
i ¬ 0;
WHILE i < count
DO
IF where >= seqLen THEN ERROR MarshallError;
w.card ¬ s.body[where]; where ¬ where + 1;
buf ¬ RefText.InlineAppendChar[buf, VAL[w.hi]]; i ¬ i + 1;
IF i < count THEN { buf ¬ RefText.InlineAppendChar[buf, VAL[w.lo]]; i ¬ i + 1 };
ENDLOOP;
newWhere ¬ where; r ¬ Rope.FromRefText[buf];
RefText.ReleaseScratch[buf] };
StoreRopeInSeqWords:
PROC [r:
ROPE, s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL] ~ {
i, length, seqLen: CARDINAL;
w: Basics.Word16;
length ¬ Rope.Length[r];
seqLen ¬ s.length;
IF where >= seqLen THEN ERROR MarshallError;
s.body[where] ¬ length; where ¬ where + 1;
i ¬ 0;
WHILE i < length
DO
w.hi ¬ ORD[Rope.Fetch[r,i]]; i ¬ i + 1;
w.lo ¬ (IF i < length THEN ORD[Rope.Fetch[r,i]] ELSE 0); i ¬ i + 1;
IF where >= seqLen THEN ERROR MarshallError;
s.body[where] ¬ w.card; where ¬ where + 1; -- USED TO BE LOOPHOLE[w] ????
ENDLOOP;
RETURN [where] };
FetchSeqWordsFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, result: SeqWords] ~ {
n, seqLen: CARDINAL;
seqLen ¬ s.length;
IF where >= seqLen THEN ERROR MarshallError;
n ¬ s.body[where]; where ¬ where + 1;
IF (n+where) > seqLen THEN ERROR MarshallError;
result ¬ NEW[SeqWordsObject[n]];
FOR i:
CARDINAL
IN [0..n)
DO
result.body[i] ¬ s.body[where]; where ¬ where + 1;
ENDLOOP;
newWhere ¬ where;
};
StoreSeqWordsInSeqWords:
PROC [s: SeqWords, arg: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL] ~ {
n: CARDINAL ~ arg.length;
seqLen: CARDINAL ~ s.length;
IF (where+n+1) > seqLen THEN ERROR MarshallError;
s.body[where] ¬ n; where ¬ where + 1;
FOR i:
CARDINAL
IN [0..n)
DO
s.body[where] ¬ arg.body[i]; where ¬ where + 1;
ENDLOOP;
newWhere ¬ where;
};
FetchBlockFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, block: Block] ~ {
IF (newWhere ¬ where+4) > s.length THEN ERROR MarshallError;
block[0] ¬ s.body[where];
block[1] ¬ s.body[where+1];
block[2] ¬ s.body[where+2];
block[3] ¬ s.body[where+3];
};
StoreBlockInSeqWords:
PROC [s: SeqWords, block: Block, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL] ~ {
IF (newWhere ¬ where+4) > s.length THEN ERROR MarshallError;
s.body[where] ¬ block[0];
s.body[where+1] ¬ block[1];
s.body[where+2] ¬ block[2];
s.body[where+3] ¬ block[3];
};
FetchCredentialsFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, c: Credentials] ~ {
type: CARDINAL;
IF where >= s.length THEN ERROR MarshallError;
type ¬ s.body[where]; where ¬ where + 1;
SELECT type
FROM
ORD[CredentialsType.strong] => c.type ¬ strong;
ORD[CredentialsType.simple] => c.type ¬ simple;
ENDCASE => ERROR MarshallError;
[newWhere~newWhere, result~c.value] ¬ FetchSeqWordsFromSeqWords[s, where];
};
FetchCard32FromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, result:
CARD32] ~ {
n: Basics.LongNumber;
IF(newWhere ¬ where+2) > s.length THEN ERROR MarshallError;
n.hi ¬ s.body[where];
n.lo ¬ s.body[where+1];
result ¬ n.lc };
FetchCredentialsPackageFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, cp: Auth.CredentialsPackage] ~ {
[where, cp.credentials] ¬ FetchCredentialsFromSeqWords[s, where];
IF cp.credentials.type # strong THEN ERROR AuthenticationError[inappropriateCredentials];
[where, cp.nonce] ¬ FetchCard32FromSeqWords[s, where];
[where, cp.recipient] ¬ FetchNameFromSeqWords[s, where];
[where, cp.conversationKey] ¬ FetchBlockFromSeqWords[s, where];
newWhere ¬ where };
FetchStrongCredentialsFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, sc: Auth.StrongCredentials] ~ {
[where, sc.conversationKey] ¬ FetchBlockFromSeqWords[s, where];
[where, sc.expirationTime] ¬ FetchCard32FromSeqWords[s, where];
[where, sc.initiator] ¬ FetchNameFromSeqWords[s, where];
newWhere ¬ where };
StoreSubropeInBlock:
PROC [r:
ROPE, index, len:
INT,
to:
LONG
POINTER
TO Block, mapCase:
BOOL ¬
TRUE] ~ {
Store len chars in block using 16-bit encoding.
c: CHAR;
blockIndex: INT ¬ 0;
WHILE blockIndex < len
DO
c ¬ Rope.Fetch[r, index];
IF mapCase
THEN
SELECT c
FROM
IN ['A .. 'Z] => c ¬ 'a + (c - 'A);
ENDCASE;
TRUSTED { to[blockIndex] ¬ ORD[c] };
index ¬ index + 1;
blockIndex ¬ blockIndex + 1;
ENDLOOP;
WHILE blockIndex < 4
DO
TRUSTED { to[blockIndex] ¬ 0 };
blockIndex ¬ blockIndex + 1;
ENDLOOP;
};
FetchNameFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, name: Name] ~ {
[where, name.organization] ¬ FetchRopeFromSeqWords[s, where];
[where, name.domain] ¬ FetchRopeFromSeqWords[s, where];
[where, name.object] ¬ FetchRopeFromSeqWords[s, where];
newWhere ¬ where };
StoreNameInSeqWords:
PROC [name: Name, s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL] ~ {
where ¬ StoreRopeInSeqWords[name.organization, s, where];
where ¬ StoreRopeInSeqWords[name.domain, s, where];
where ¬ StoreRopeInSeqWords[name.object, s, where];
RETURN [where] };
MarshalledNameHwords:
PROC [name: Name]
RETURNS [hWords:
CARDINAL] ~ {
RETURN [CrRPC.MarshalledRopeHWords[name.organization] + CrRPC.MarshalledRopeHWords[name.domain] + CrRPC.MarshalledRopeHWords[name.object]] };
SeqWordsFromName:
PROC [name: Name]
RETURNS [SeqWords] ~ {
s: SeqWords ¬ NEW[SeqWordsObject[MarshalledNameHwords[name]]];
[] ¬ StoreNameInSeqWords[name, s, 0];
RETURN [s] };
CredentialsFromName:
PROC [name: Name]
RETURNS [credentials: Credentials] ~ {
RETURN[ [type~simple, value~SeqWordsFromName[name]] ] };
VerifierFromHashedPassword:
PROC [hashedPassword: HashedPassword]
RETURNS [verifier: Verifier] ~ {
verifier ¬ NEW[SeqWordsObject[1]];
verifier.body[0] ¬ hashedPassword };
EncryptKey:
PROC [key: Key, data: Key]
RETURNS [result: Key] ~
TRUSTED {
DESFace.EncryptBlock[LOOPHOLE[key], LOOPHOLE[LONG[@data]], LOOPHOLE[LONG[@result]]] };
EncryptSeqWords:
PROC [key: Key, seqWords: SeqWords]
RETURNS [result: SeqWords] ~ {
nBlocks: CARDINAL ¬ (seqWords.length*BITS[CARD16] + BITS[Block] - 1) / BITS[Block];
roundedLength: CARDINAL ¬ nBlocks * (BITS[Block]/BITS[CARD16]);
result ¬ NEW[SeqWordsObject[roundedLength]];
IF roundedLength # seqWords.length
THEN {
FOR i:
CARDINAL
IN [0..seqWords.length)
DO result.body[i] ¬ seqWords.body[i] ENDLOOP;
FOR i:
CARDINAL
IN [seqWords.length..roundedLength)
DO result.body[i] ¬ 0 ENDLOOP;
seqWords ¬ result };
TRUSTED {
blocksFrom: DESFace.Blocks ~ LOOPHOLE[
LOOPHOLE[seqWords, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
blocksTo: DESFace.Blocks ~ LOOPHOLE[
LOOPHOLE[result, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
DESFace.CBCCheckEncrypt[LOOPHOLE[key], nBlocks, blocksFrom, blocksTo, ALL[0]];
};
};
DecryptSeqWords:
PROC [key: Key, seqWords: SeqWords]
RETURNS [result: SeqWords] ~ {
???? Word size dependent. ????
nBlocks: CARDINAL ~ (seqWords.length*BITS[CARD16]) / BITS[Block];
roundedLength: CARDINAL ¬ nBlocks * (BITS[Block]/BITS[CARD16]);
IF seqWords.length # roundedLength THEN ERROR MarshallError;
result ¬ NEW[SeqWordsObject[roundedLength]];
TRUSTED {
blocksFrom: DESFace.Blocks ~ LOOPHOLE[
LOOPHOLE[seqWords, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
blocksTo: DESFace.Blocks ~ LOOPHOLE[
LOOPHOLE[result, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
DESFace.CBCCheckDecrypt[LOOPHOLE[key], nBlocks, blocksFrom, blocksTo, ALL[0]];
};
};
PackBytes: PROC[hi, lo: BYTE] RETURNS [CARD16] ~ INLINE { RETURN [256*hi + lo] };
EncryptedVerifierFromStrongVerifier:
PROC [conversationKey: Key,
hostNumber: HostNumber, strongVerifier: Auth.StrongVerifier]
RETURNS [verifier: Verifier] ~ {
verifierInHWords: CARDINAL ~ BYTES[Auth.StrongVerifier]/BYTES[Basics.HWORD];
seqWords: SeqWords ~ NEW[SeqWordsObject[verifierInHWords]];
temp: Basics.LongNumber;
temp.lc ¬ strongVerifier.timeStamp;
seqWords.body[0] ¬ Basics.BITXOR[temp.hi, PackBytes[hostNumber.a, hostNumber.b]];
seqWords.body[1] ¬ Basics.BITXOR[temp.lo, PackBytes[hostNumber.c, hostNumber.d]];
temp.lc ¬ strongVerifier.ticks;
seqWords.body[2] ¬ Basics.BITXOR[temp.hi, PackBytes[hostNumber.e, hostNumber.f]];
seqWords.body[3] ¬ temp.lo;
verifier ¬ EncryptSeqWords[key~LOOPHOLE[conversationKey], seqWords~seqWords];
};
StrongVerifierFromEncryptedVerifier:
PROC [conversationKey: Key,
hostNumber: HostNumber, verifier: Verifier]
RETURNS [strongVerifier: Auth.StrongVerifier] ~ {
temp: Basics.LongNumber;
IF verifier.length # (BITS[Auth.StrongVerifier]/BITS[CARD16]) THEN ERROR MarshallError;
verifier ¬ DecryptSeqWords[key~LOOPHOLE[conversationKey], seqWords~verifier];
temp.hi ¬ Basics.BITXOR[verifier.body[0], PackBytes[hostNumber.a, hostNumber.b]];
temp.lo ¬ Basics.BITXOR[verifier.body[1], PackBytes[hostNumber.c, hostNumber.d]];
strongVerifier.timeStamp ¬ temp.lc;
temp.hi ¬ Basics.BITXOR[verifier.body[2], PackBytes[hostNumber.e, hostNumber.f]];
temp.lo ¬ verifier.body[3];
strongVerifier.ticks ¬ temp.lc;
};
Locating Authentication Servers and Performing Remote Calls
ServerInfo: TYPE ~ REF ServerInfoObject;
ServerInfoObject:
TYPE ~
RECORD [
next: ServerInfo,
refAddress: REF XNS.Address, -- REF conforms to CrRPC.RefAddress
hops: XNSRouter.Hops
];
serverInfoLock: Lock ¬ NEW[LockObject ¬ []];
serverBroadcastLock: Lock ¬ NEW[LockObject ¬ []];
serverInfo: ServerInfo ¬ NIL;
serverInfoTimestamp: GMT;
defaultServerHops: XNSRouter.Hops ~ 3;
maxServerHops: XNSRouter.Hops ~ 6;
secondsBetweenBroadcasts: INT ~ 600;
broadcastPauseLimit: CARDINAL ¬ 3;
broadcastPauseMsec:
CARDINAL ¬ 11000;
These are necessary for machines with only 3Mb Ethernets, because it can take so long for the routing and translation caches to fill. They should go away after improvements are made in the underlying transport.
InvalidateServerCache:
ENTRY
PROC [lock: Lock ¬ serverInfoLock] ~ { serverInfo ¬
NIL };
DeleteServer:
ENTRY
PROC [lock: Lock ¬ serverInfoLock, it: ServerInfo] ~ {
ENABLE UNWIND => NULL;
IF it = serverInfo THEN { serverInfo ¬ it.next; RETURN };
FOR p: ServerInfo ¬ serverInfo, p.next
WHILE p #
NIL
DO
IF p.next = it THEN { p.next ¬ it.next; RETURN };
ENDLOOP;
};
AddServerAddress:
ENTRY
PROC [lock: Lock ¬ serverInfoLock, addr:
XNS.Address, hops: XNSRouter.Hops] ~ {
ENABLE UNWIND => NULL;
p, prev, new: ServerInfo;
Delete old entry for this address (hops may have changed)
prev ¬ NIL; p ¬ serverInfo;
WHILE (p # NIL) AND (p.refAddress # addr) DO { prev ¬ p; p ¬ p.next } ENDLOOP;
IF p =
NIL
THEN {
new ¬
NEW[ServerInfoObject ¬ [
next~NIL,
refAddress~NEW[XNS.Address ¬ addr],
hops~hops]]
}
ELSE {
IF prev # NIL THEN prev.next ¬ p.next ELSE serverInfo ¬ p.next;
new ¬ p; new.hops ¬ hops;
};
Add new entry in correct position (sorted by hops)
prev ¬ NIL; p ¬ serverInfo;
WHILE (p # NIL) AND (p.hops < hops) DO { prev ¬ p; p ¬ p.next } ENDLOOP;
new.next ¬ p;
IF prev # NIL THEN prev.next ¬ new ELSE serverInfo ¬ new;
};
BroadcastForServers:
ENTRY
PROC [lock: Lock ¬ serverBroadcastLock,
maxHops:
CARDINAL, nWanted:
CARDINAL ¬ 0, tryLimit:
CARDINAL ¬ 0] ~ {
ENABLE UNWIND => NULL;
nGot: CARDINAL ¬ 0;
EachAddress: XNSServerLocation.EachAddressProc
-- [addr: XNS.Address] -- ~ {
hops: XNSRouter.Hops;
addr.socket ¬ XNS.unknownSocket;
hops ¬ XNSRouter.GetHops[addr.net];
AddServerAddress[addr~addr, hops~hops];
IF (nGot ¬ nGot.SUCC) = nWanted THEN ERROR XNSServerLocation.StopBroadcast[] };
XNSServerLocation.LocateServers[eachAddress~EachAddress, socket~XNSWKS.authenticationInfo, remotePgm~authPgmNum, remotePgmVersion~authVersionNum, maxHops~maxHops, tryLimit~tryLimit];
serverInfoTimestamp ¬ Now[];
};
numBroadcastPauses: INT ¬ 0; -- DEBUG
GetBestServer:
PROC
RETURNS [p: ServerInfo] ~ {
nBroadcasts: CARDINAL ¬ 0;
DO
currentHops: XNSRouter.Hops;
p ¬ serverInfo; -- ATOMIC
SELECT
TRUE
FROM
(p =
NIL) => {
BroadcastForServers[maxHops~maxServerHops, nWanted~1];
nBroadcasts ¬ nBroadcasts.SUCC;
IF ((p ¬ serverInfo) =
NIL)
AND (nBroadcasts <= broadcastPauseLimit)
THEN {
Process.PauseMsec[broadcastPauseMsec];
numBroadcastPauses ¬ numBroadcastPauses.SUCC; -- DEBUG
LOOP };
TRUSTED { Process.Detach[ FORK BroadcastForServers[maxHops~defaultServerHops, tryLimit~1] ] };
RETURN };
((currentHops ¬ XNSRouter.GetHops[p.refAddress.net]) # p.hops) => {
DeleteServer[it~p];
AddServerAddress[addr~p.refAddress, hops~currentHops];
LOOP };
((p.hops > 0)
AND (BasicTime.Period[from~serverInfoTimestamp, to~Now[]] > secondsBetweenBroadcasts)) =>
TRUSTED { Process.Detach[ FORK BroadcastForServers[maxHops~MIN[(p.hops-1), defaultServerHops], tryLimit~1] ];
RETURN };
ENDLOOP;
};
CallRemote:
PROC [proc:
PROC [CrRPC.Handle, HostNumber]] ~ {
h: CrRPC.Handle ¬ NIL;
p: ServerInfo;
maxTries: CARDINAL ~ 10;
CleanUp: PROC ~ { IF h # NIL THEN { CrRPC.DestroyClientHandle[h]; h ¬ NIL }};
FOR try:
CARDINAL
IN [1..maxTries]
DO
BEGIN
ENABLE {
UNWIND => CleanUp[];
CrRPC.Error => { CleanUp[]; DeleteServer[it~p]; LOOP };
CallError =>
SELECT problem
FROM
tooBusy, databaseFull => { CleanUp[]; DeleteServer[it~p]; LOOP };
ENDCASE => REJECT;
};
IF (p ¬ GetBestServer[]) = NIL THEN EXIT;
h ¬ CrRPC.CreateClientHandle[class~$CMUX, remote~p.refAddress];
proc[h, p.refAddress.host]; CleanUp[]; RETURN;
END;
ENDLOOP;
CleanUp[]; CantContactAuthServer[];
};
}.