DIRECTORY BasicTime USING [earliestGMT, GMT, Now, Period, Update], CHACLOpsP127V1 USING [ArgumentError, AuthenticationError, CallError, PropertyError, UpdateError, WrongServer], CHEntriesP0V0 USING [addressList, members], CHOpsP2V3 USING [ArgumentError, AuthenticationError, Authenticator, CallError, ListDomainsServed, PropertyError, RetrieveItem, RetrieveMembers, UpdateError, WrongServer], CrRPC USING [BulkDataCheckAbortProc, BulkDataValueProc, BulkDataXferProc, CreateClientHandle, DestroyClientHandle, Error, GetRope, Handle, ReadBulkDataStream], Process USING [Detach, PauseMsec], Random USING [ChooseInt], RefTab USING [Create, EachPairAction, EqualProc, Fetch, HashProc, Insert, Pairs, Ref, Val], Rope USING [Cat, Equal, Index, IsEmpty, ROPE, Substr], SymTab USING [Create, EachPairAction, Fetch, Pairs, Ref, Store], XNS USING [Address, Host, unknownAddress, unknownSocket], XNSAuth USING [AuthenticationError, CallError, Conversation, GetCredentials, GetNextVerifier, GetNullIdentity, Identity, Initiate, SetRecipientHostNumber, Terminate], XNSCH USING [AddressesFromItem, BestAddressInList, Element, Error, ErrorCode, Item, Name, Pattern, Properties, PropertyID, Which], XNSCHConcreteTypes USING [ConversationObject], XNSCHPrivate USING [GetElement, RemoteProc], XNSCredentials USING [GetIdentity], XNSRouter USING [GetHops, unreachable], XNSServerLocation USING [EachAddressProc, LocateServers, StopBroadcast], XNSWKS USING [clearinghouse] ; XNSCHPrivateImpl: CEDAR MONITOR IMPORTS BasicTime, CHACLOpsP127V1, CHOpsP2V3, CrRPC, Process, Random, RefTab, Rope, SymTab, XNSAuth, XNSCH, XNSCHPrivate, XNSCredentials, XNSRouter, XNSServerLocation EXPORTS XNSCH, XNSCHPrivate ~ { OPEN CHEntries: CHEntriesP0V0, CHOps: CHOpsP2V3, CHACLOps: CHACLOpsP127V1; chPgmNum: CARD32 ~ 2; chVersionNum: CARD16 ~ 3; ROPE: TYPE ~ Rope.ROPE; Name: TYPE ~ XNSCH.Name; Pattern: TYPE ~ XNSCH.Pattern; Element: TYPE ~ XNSCH.Element; PropertyID: TYPE ~ XNSCH.PropertyID; Properties: TYPE ~ XNSCH.Properties; Item: TYPE ~ XNSCH.Item; WrongServerError: PUBLIC ERROR [hint: Name] ฌ CHOps.WrongServer; shiftInName: ROPE ~ "CHServers"; chServiceName: Name ~ [organization~shiftInName, domain~shiftInName, object~"Clearinghouse Service"]; NACacheEntry: TYPE ~ REF NACacheEntryObject; NACacheEntryObject: TYPE ~ RECORD [ next: NACacheEntry, name, distingName: Name, address: XNS.Address]; naCacheMaxSize: INT ฌ 8; naCacheSize: INT ฌ 0; naCache: NACacheEntry ฌ NIL; -- REF to tail of circular list of NACacheEntryObjects NACacheInsert: PUBLIC ENTRY PROC [name: Name, distingName: Name, address: XNS.Address] ~ { IF naCacheSize < naCacheMaxSize THEN { new: NACacheEntry ฌ NEW[NACacheEntryObject ฌ [address~XNS.unknownAddress]]; IF naCache = NIL THEN { new.next ฌ naCache ฌ new } ELSE { new.next ฌ naCache.next; naCache.next ฌ new }; naCacheSize ฌ naCacheSize.SUCC; }; naCache ฌ naCache.next; naCache.name ฌ name; naCache.distingName ฌ distingName; naCache.address ฌ address; }; SameName: PROC [a, b: Name] RETURNS [same: BOOL] ~ { same ฌ Rope.Equal[s1~a.object, s2~b.object, case~FALSE] AND Rope.Equal[s1~a.domain, s2~b.domain, case~FALSE] AND Rope.Equal[s1~a.organization, s2~b.organization, case~FALSE]; }; NACacheLookup: PUBLIC ENTRY PROC [name: Name] RETURNS [found: BOOL, distingName: Name, address: XNS.Address] ~ { p: NACacheEntry ฌ naCache; THROUGH [0..naCacheSize) DO IF SameName[p.name, name] OR SameName[p.distingName, name] THEN RETURN [TRUE, p.distingName, p.address]; p ฌ p.next; ENDLOOP; RETURN [FALSE, [NIL,NIL,NIL], XNS.unknownAddress]; }; NACacheRollback: ENTRY PROC ~ { p: NACacheEntry ฌ naCache; naCache ฌ NIL; naCacheSize ฌ 0; WHILE p # NIL DO nextp: NACacheEntry ~ p.next; p.next ฌ NIL; p ฌ nextp; ENDLOOP; }; minutesBetweenNACacheSweeps: INT ฌ 10; minutesUntilNACacheSweep: INT ฌ minutesBetweenNACacheSweeps; NACacheSweep: ENTRY PROC ~ { IF (minutesUntilNACacheSweep ฌ minutesUntilNACacheSweep.PRED) > 0 THEN RETURN; minutesUntilNACacheSweep ฌ minutesBetweenNACacheSweeps; SELECT naCacheSize FROM 0 => NULL; 1 => { naCache.next ฌ NIL; naCache ฌ NIL; naCacheSize ฌ 0 }; > 1 => { naCache.next ฌ naCache.next.next; naCacheSize ฌ naCacheSize.PRED }; ENDCASE => ERROR; }; Conversation: TYPE ~ REF ConversationObject; ConversationObject: PUBLIC TYPE ~ XNSCHConcreteTypes.ConversationObject; InitiateConversation: PUBLIC PROC [identity: XNSAuth.Identity, server: XNS.Address] RETURNS [c: Conversation] ~ { c ฌ NEW[ ConversationObject ฌ [] ]; IF identity = NIL THEN identity ฌ XNSCredentials.GetIdentity[]; IF identity # NIL THEN { c.conversation ฌ XNSAuth.Initiate[identity, chServiceName ! XNSAuth.AuthenticationError, XNSAuth.CallError => CONTINUE]; }; IF c.conversation = NIL THEN { identity ฌ XNSAuth.GetNullIdentity[]; c.conversation ฌ XNSAuth.Initiate[identity, chServiceName]; -- no ERROR }; IF server # XNS.unknownAddress THEN { c.handle ฌ CrRPC.CreateClientHandle[$CMUX, NEW[XNS.Address ฌ server]]; c.host ฌ server.host; }; }; TerminateConversation: PUBLIC PROC [c: Conversation] ~ { IF c.handle # NIL THEN CrRPC.DestroyClientHandle[c.handle]; XNSAuth.Terminate[c.conversation] }; GetAuthenticator: PUBLIC PROC [c: Conversation, host: XNS.Host] RETURNS [CHOps.Authenticator] ~ { XNSAuth.SetRecipientHostNumber[c.conversation, host]; RETURN [ [XNSAuth.GetCredentials[c.conversation], XNSAuth.GetNextVerifier[c.conversation]] ] }; IsGeneric: PUBLIC PROC [c: Conversation] RETURNS [BOOL] ~ { RETURN [c.handle = NIL] }; CachedServer: TYPE ~ REF CachedServerObject; CachedServerObject: TYPE ~ RECORD [ address: REF XNS.Address, -- readonly, REF to conform to CrRPC.RefAddress knowDomainsServed: BOOL ฌ FALSE, -- hint, no ML inactiveUntil: BasicTime.GMT ฌ BasicTime.earliestGMT -- ML ]; numHeaders: CARDINAL ~ 101; serversByAddress: RefTab.Ref ~ RefTab.Create[ mod~numHeaders, equal~EqualAddressesIgnoringSocket, hash~HashAddress]; HashAddress: RefTab.HashProc ~ { host: XNS.Host ฌ NARROW[key, REF XNS.Address].host; acc: CARDINAL ฌ ((((host.a*5+host.b)*5+host.c)*5+host.d)*5+host.e)*5+host.f; RETURN [acc] }; EqualAddressesIgnoringSocket: RefTab.EqualProc ~ { ra1: REF XNS.Address ฌ NARROW[key1]; ra2: REF XNS.Address ฌ NARROW[key2]; RETURN [(ra1.net = ra2.net) AND (ra1.host = ra2.host)] }; GetServerByAddress: PROC [ra: REF XNS.Address, makeActive: BOOL] RETURNS [s: CachedServer] ~ { ENABLE UNWIND => NULL; val: RefTab.Val; found: BOOL; [found, val] ฌ RefTab.Fetch[x~serversByAddress, key~ra]; IF NOT found THEN { val ฌ NEW[CachedServerObject ฌ [address~ra]]; [] ฌ RefTab.Insert[x~serversByAddress, key~ra, val~val] }; s ฌ NARROW[val]; IF makeActive THEN MarkServerUsable[s]; }; secondsBusy: INT ฌ 10; secondsDown: INT ฌ 600; secondsDead: INT ฌ 900; ServerIsUsableInternal: INTERNAL PROC [s: CachedServer] RETURNS [usable: BOOL] ~ { usable ฌ (BasicTime.Period[from: s.inactiveUntil, to: BasicTime.Now[]] >= 0) }; MarkServerUsable: ENTRY PROC [s: CachedServer] ~ { ENABLE UNWIND => NULL; s.inactiveUntil ฌ BasicTime.earliestGMT }; MarkServerBusy: ENTRY PROC [s: CachedServer] ~ { ENABLE UNWIND => NULL; s.inactiveUntil ฌ BasicTime.Update[BasicTime.Now[], secondsBusy] }; MarkServerDown: ENTRY PROC [s: CachedServer] ~ { ENABLE UNWIND => NULL; s.inactiveUntil ฌ BasicTime.Update[BasicTime.Now[], secondsDown]; s.knowDomainsServed ฌ FALSE }; MarkServerDead: ENTRY PROC [s: CachedServer] ~ { ENABLE UNWIND => NULL; s.inactiveUntil ฌ BasicTime.Update[BasicTime.Now[], secondsDead]; s.knowDomainsServed ฌ FALSE }; CachedServerListHead: TYPE ~ REF CachedServerList; CachedServerList: TYPE ~ REF CachedServerListElement; CachedServerListElement: TYPE ~ RECORD [ next: CachedServerList, -- ML server: CachedServer -- readonly ]; AddServerToList: ENTRY PROC [listHead: CachedServerListHead, server: CachedServer] RETURNS [new: BOOL] ~ { ENABLE UNWIND => NULL; new ฌ AddServerToListInternal[listHead, server] }; AddServerToListInternal: INTERNAL PROC [listHead: CachedServerListHead, server: CachedServer] RETURNS [new: BOOL] ~ { FOR p: CachedServerList ฌ listHeadญ, p.next WHILE p # NIL DO IF p.server = server THEN RETURN [FALSE]; ENDLOOP; listHeadญ ฌ NEW[CachedServerListElement ฌ [next~listHeadญ, server~server]]; RETURN [TRUE] }; DeleteServerFromListInternal: INTERNAL PROC [listHead: CachedServerListHead, server: CachedServer] ~ { p, prev: CachedServerList ฌ NIL; IF listHead = NIL THEN RETURN; p ฌ listHeadญ; WHILE (p # NIL) AND (p.server # server) DO prev ฌ p; p ฌ p.next ENDLOOP; IF p = NIL THEN RETURN; IF prev = NIL THEN listHeadญ ฌ p ELSE prev.next ฌ p; }; ServerFilterProc: TYPE ~ PROC [CachedServer] RETURNS [ok: BOOL]; GetBestServerFromList: ENTRY PROC [listHead: CachedServerListHead, filter: ServerFilterProc ฌ NIL] RETURNS [bestServer: CachedServer ฌ NIL] ~ { ENABLE UNWIND => NULL; bestHops: CARDINAL ฌ LAST[CARDINAL]; serversAtBestHops: CARDINAL ฌ 0; IF listHead = NIL THEN RETURN [NIL]; FOR p: CachedServerList ฌ listHeadญ, p.next WHILE p # NIL DO server: CachedServer ~ p.server; hops: CARDINAL; IF NOT ServerIsUsableInternal[server] THEN LOOP; hops ฌ XNSRouter.GetHops[server.address.net]; IF hops >= bestHops THEN LOOP; IF filter # NIL THEN IF NOT filter[server].ok THEN LOOP; IF hops=bestHops THEN { serversAtBestHops ฌ serversAtBestHops.SUCC; IF Random.ChooseInt[NIL, 1, serversAtBestHops] = 1 THEN { bestServer ฌ server; }; } ELSE { bestServer ฌ server; bestHops ฌ hops; serversAtBestHops ฌ 1; }; ENDLOOP; }; domainServers: SymTab.Ref ~ SymTab.Create[mod~numHeaders, case~FALSE]; GetServersForDomain: PROC [domain: ROPE] RETURNS [CachedServerListHead] ~ { RETURN [NARROW[SymTab.Fetch[domainServers, domain].val]] }; AddServerForDomain: ENTRY PROC [domain: ROPE, server: CachedServer] ~ { ENABLE UNWIND => NULL; listHead: CachedServerListHead; listHead ฌ NARROW[SymTab.Fetch[domainServers, domain].val]; IF listHead = NIL THEN { listHead ฌ NEW[CachedServerList ฌ NIL]; [] ฌ SymTab.Store[domainServers, domain, listHead] }; [] ฌ AddServerToListInternal[listHead, server] }; DeleteServerForDomain: ENTRY PROC [domain: ROPE, server: CachedServer] ~ { ENABLE UNWIND => NULL; listHead: CachedServerListHead ~ NARROW[SymTab.Fetch[domainServers, domain].val]; DeleteServerFromListInternal[listHead, server] }; ListDomainsIKnow: ENTRY PROC RETURNS [domains: LIST OF ROPE] ~ { EachPair: INTERNAL SymTab.EachPairAction -- [key, val] RETURNS[BOOL] -- ~ { domains ฌ CONS[key, domains]; RETURN[FALSE] }; [] ฌ SymTab.Pairs[domainServers, EachPair]; }; LearnDomainsServed: PROC [h: CrRPC.Handle, theServer: CachedServer] ~ { c: Conversation; ReadStreamOfDomainNames: CrRPC.BulkDataXferProc ~ { EachDomainNameInStream: CrRPC.BulkDataValueProc ~ { domPart, orgPart: ROPE; orgPart ฌ CrRPC.GetRope[s]; domPart ฌ CrRPC.GetRope[s]; AddServerForDomain[ DomainKeyFromComponents[organization~orgPart, domain~domPart], theServer]; AddServerForDomain[ OrganizationKeyFromComponents[orgPart], theServer]; }; RETURN [CrRPC.ReadBulkDataStream[h, s, checkAbort, EachDomainNameInStream]]; }; IF theServer.knowDomainsServed THEN RETURN; c ฌ InitiateConversation[NIL, XNS.unknownAddress]; CHOps.ListDomainsServed[h, ReadStreamOfDomainNames, GetAuthenticator[c, theServer.address.host]]; theServer.knowDomainsServed ฌ TRUE; TerminateConversation[c]; }; DomainKeyFromComponents: PROC [organization: ROPE, domain: ROPE] RETURNS [key: ROPE] ~ INLINE { key ฌ Rope.Cat[domain, ":", organization] }; OrganizationKeyFromComponents: PROC [organization: ROPE] RETURNS [key: ROPE] ~ INLINE { key ฌ Rope.Cat[organization, ":", shiftInName] }; OrganizationKeyFromDomainKey: PROC [domainKey: ROPE] RETURNS [organizationKey: ROPE] ~ { pos: INT ~ Rope.Index[s1~domainKey, s2~":"] + 1; organizationKey ฌ OrganizationKeyFromComponents[Rope.Substr[domainKey, pos]] }; nearbyServers: CachedServerListHead ~ NEW[CachedServerList ฌ NIL]; defaultMaxHops: CARDINAL ฌ 3; desperationContactMaxHops: CARDINAL ฌ 5; desperationBroadcastMaxHops: CARDINAL ฌ 4; desperationBroadcastTryLimit: CARDINAL ฌ 2; desperationBroadcastPauseLimit: CARDINAL ฌ 2; desperationBroadcastPauseMsec: CARD ฌ 8000; BroadcastForNearbyServers: PROC [ maxHops: CARDINAL, nWanted: CARDINAL, tryLimit: CARDINAL] ~ { nGot: CARDINAL ฌ 0; maxRetries: NAT ~ 3; EachAddress: XNSServerLocation.EachAddressProc -- [addr: XNS.Address] -- ~ { server: CachedServer; ra: REF XNS.Address ฌ NEW[XNS.Address ฌ [net~addr.net, host~addr.host, socket~XNS.unknownSocket]]; server ฌ GetServerByAddress[ra~ra, makeActive~TRUE]; IF AddServerToList[nearbyServers, server].new THEN nGot ฌ nGot.SUCC; IF nGot = nWanted THEN ERROR XNSServerLocation.StopBroadcast[]; }; FOR i: CARDINAL ฌ 0, i+1 DO XNSServerLocation.LocateServers[socket~XNSWKS.clearinghouse, remotePgm~chPgmNum, remotePgmVersion~chVersionNum, eachAddress~EachAddress, maxHops~maxHops, tryLimit~tryLimit]; IF (nGot > 0) OR (i >= desperationBroadcastPauseLimit) THEN EXIT; Process.PauseMsec[desperationBroadcastPauseMsec]; ENDLOOP; }; NoticeKnownNearbyServers: PROC[ maxHops: CARDINAL, nWanted: CARDINAL ฌ CARDINAL.LAST] ~ { nGot: CARDINAL ฌ 0; EachServer: RefTab.EachPairAction ~ { s: CachedServer ฌ NARROW[val]; IF XNSRouter.GetHops[s.address.net] > maxHops THEN RETURN [quit~FALSE]; IF AddServerToList[nearbyServers, s].new THEN nGot ฌ nGot.SUCC; RETURN [quit~(nGot=nWanted)] }; [] ฌ RefTab.Pairs[x~serversByAddress, action~EachServer]; }; GetBestKnownServer: PROC [maxHops: CARDINAL, filter: ServerFilterProc] RETURNS [bestServer: CachedServer ฌ NIL] ~ { EachServer: RefTab.EachPairAction ~ { s: CachedServer ฌ NARROW[val]; hops: CARDINAL; IF BasicTime.Period[from: BasicTime.Now[], to: s.inactiveUntil] > 0 THEN RETURN; hops ฌ XNSRouter.GetHops[s.address.net]; IF hops > maxHops THEN RETURN; IF (filter # NIL) AND (NOT filter[s]) THEN RETURN; maxHops ฌ hops; bestServer ฌ s }; [] ฌ RefTab.Pairs[x~serversByAddress, action~EachServer]; }; GetSomeServer: PROC [filter: ServerFilterProc ฌ NIL, okToBroadcast: BOOL] RETURNS [bestServer: CachedServer, didBroadcast: BOOL ฌ FALSE] ~ { bestServer ฌ GetBestServerFromList[nearbyServers, filter]; IF bestServer = NIL THEN { NoticeKnownNearbyServers[maxHops~defaultMaxHops]; bestServer ฌ GetBestServerFromList[nearbyServers, filter] }; IF bestServer = NIL THEN { bestServer ฌ GetBestKnownServer[desperationContactMaxHops, filter] }; IF (bestServer = NIL) AND okToBroadcast THEN { BroadcastForNearbyServers[maxHops~desperationBroadcastMaxHops, nWanted~1, tryLimit~desperationBroadcastTryLimit]; didBroadcast ฌ TRUE; bestServer ฌ GetBestServerFromList[nearbyServers, filter] }; }; maxHints: CARDINAL ~ 15; ProcessHint: PROC [h: CrRPC.Handle, theServer: CachedServer, hint: Name] RETURNS [servers: CachedServerListHead] ~ { c: Conversation; nRead: CARDINAL; hints: ARRAY [1..maxHints] OF ROPE ฌ ALL[NIL]; ReadStreamOfNames: CrRPC.BulkDataXferProc ~ { EachNameInStream: CrRPC.BulkDataValueProc ~ { serverName: Name ~ XNSCHPrivate.GetElement[s]; nRead ฌ nRead + 1; IF nRead <= maxHints THEN { hints[nRead] ฌ serverName.object; RETURN }; IF Random.ChooseInt[NIL, 1, nRead] > maxHints THEN RETURN; hints[Random.ChooseInt[NIL, 1, maxHints]] ฌ serverName.object; }; RETURN [CrRPC.ReadBulkDataStream[h, s, checkAbort, EachNameInStream]]; }; c ฌ InitiateConversation[NIL, XNS.unknownAddress]; nRead ฌ 0; [] ฌ CHOps.RetrieveMembers[h, hint, CHEntries.members, ReadStreamOfNames, GetAuthenticator[c, theServer.address.host] ! CHOps.ArgumentError, CHOps.PropertyError, CHOps.UpdateError => CONTINUE]; servers ฌ NEW[CachedServerList ฌ NIL]; FOR i: CARDINAL IN [1 .. MIN[nRead, maxHints]] DO { ENABLE CHOps.ArgumentError, CHOps.PropertyError, CHOps.UpdateError => CONTINUE; item: Item; addresses: LIST OF XNS.Address; [value~item] ฌ CHOps.RetrieveItem[h, [organization~shiftInName, domain~shiftInName, object~hints[i]], CHEntries.addressList, GetAuthenticator[c, theServer.address.host]]; addresses ฌ XNSCH.AddressesFromItem[item]; IF addresses # NIL THEN { addr: XNS.Address; ra: REF XNS.Address; server: CachedServer; addr ฌ XNSCH.BestAddressInList[addresses]; ra ฌ NEW[XNS.Address ฌ [net~addr.net, host~addr.host, socket~XNS.unknownSocket]]; server ฌ GetServerByAddress[ra~ra, makeActive~FALSE]; [] ฌ AddServerToList[servers, server] }; }; ENDLOOP; TerminateConversation[c]; }; maxTries: CARDINAL ~ 15; triesToUse: CARDINAL ฌ 7; -- Bounds Fault if this exceeds maxTries CallRemote: PUBLIC PROC [c: Conversation, proc: XNSCHPrivate.RemoteProc, domain: ROPE ฌ NIL, idempotent: BOOL ฌ TRUE] ~ { State: TYPE ~ { dom, hint, org, nearby, none }; state: State ฌ none; organization: ROPE ฌ NIL; theServer: CachedServer ฌ NIL; theHandle: CrRPC.Handle ฌ NIL; gotHint: BOOL; theHint: Name; hintServers: CachedServerListHead ฌ NIL; specific: BOOL ~ NOT IsGeneric[c]; inClientProc: BOOL ฌ FALSE; didBroadcast: BOOL ฌ FALSE; nAlreadyUsed: CARDINAL ฌ 0; alreadyUsedCache: ARRAY [0..maxTries) OF CachedServer; NoteServerAlreadyUsed: PROC [server: CachedServer] ~ { alreadyUsedCache[nAlreadyUsed] ฌ server; nAlreadyUsed ฌ nAlreadyUsed + 1; }; ServerMayBeUsed: PROC [server: CachedServer] RETURNS [BOOL] ~ { FOR i: CARDINAL IN [0..nAlreadyUsed) DO IF alreadyUsedCache[i] = server THEN RETURN [FALSE]; ENDLOOP; RETURN [TRUE] }; CleanUp: PROC ~ { IF theHandle # NIL THEN CrRPC.DestroyClientHandle[theHandle]; theHandle ฌ NIL }; GetServerCandidate: PROC ~ { state ฌ none; IF theHandle # NIL THEN { CrRPC.DestroyClientHandle[theHandle]; theHandle ฌ NIL }; theServer ฌ NIL; IF NOT Rope.IsEmpty[domain] THEN { state ฌ dom; theServer ฌ GetBestServerFromList[GetServersForDomain[domain], ServerMayBeUsed]; }; IF theServer = NIL THEN { state ฌ hint; theServer ฌ GetBestServerFromList[hintServers, ServerMayBeUsed]; }; IF (theServer = NIL) AND (NOT Rope.IsEmpty[domain]) THEN { state ฌ org; IF organization = NIL THEN organization ฌ OrganizationKeyFromDomainKey[domain]; theServer ฌ GetBestServerFromList[GetServersForDomain[organization], ServerMayBeUsed]; }; IF theServer = NIL THEN { state ฌ nearby; [theServer, didBroadcast] ฌ GetSomeServer[ServerMayBeUsed, (NOT didBroadcast)] }; IF theServer = NIL THEN { state ฌ none; RETURN }; theHandle ฌ CrRPC.CreateClientHandle[$CMUX, theServer.address]; }; FOR tryNo: CARDINAL IN [1..triesToUse] DO ENABLE { UNWIND => CleanUp[]; CHOps.AuthenticationError, CHACLOps.AuthenticationError => { ERROR XNSCH.Error[credentialsInvalid, first] }; CHOps.CallError, CHACLOps.CallError => { SELECT problem FROM accessRightsInsufficient => { ERROR XNSCH.Error[credentialsTooWeak, first] }; tooBusy => { IF specific THEN ERROR XNSCH.Error[serverTooBusy, first]; MarkServerBusy[theServer]; LOOP }; serverDown => { IF specific THEN ERROR XNSCH.Error[allDown, first]; EXIT }; ENDCASE => -- can't happen -- { IF specific THEN ERROR XNSCH.Error[unknown, first]; MarkServerDead[theServer]; LOOP }; }; CHOps.ArgumentError, CHACLOps.ArgumentError => { code: XNSCH.ErrorCode ~ (SELECT problem FROM illegalProperty => illegalPropertyID, illegalOrganizationName => illegalOrganizationName, illegalDomainName => illegalDomainName, illegalObjectName => illegalObjectName, noSuchOrganization => noSuchOrganization, noSuchDomain => noSuchDomain, noSuchObject => noSuchObject ENDCASE => unknown); ERROR XNSCH.Error[code, IF which = first THEN first ELSE second] }; CHOps.PropertyError, CHACLOps.PropertyError => { code: XNSCH.ErrorCode ~ (SELECT problem FROM missing => propertyIDNotFound, wrongType => wrongPropertyType ENDCASE => unknown); ERROR XNSCH.Error[code, first] }; CHOps.UpdateError, CHACLOps.UpdateError => { code: XNSCH.ErrorCode ~ (SELECT problem FROM noChange => noChange, outOfDate => outOfDate, objectOverflow => overflowOfName, databaseOverflow => overflowOfDataBase ENDCASE => unknown); ERROR XNSCH.Error[code, IF which = first THEN first ELSE second] }; CrRPC.Error => { SELECT errorReason FROM courierVersionMismatch => { IF specific THEN ERROR XNSCH.Error[serviceNotExported, first]; MarkServerDead[theServer]; LOOP }; rejectedNoSuchProgram, rejectedNoSuchVersion, rejectedNoSuchProcedure, rejectedUnspecified => { IF specific THEN ERROR XNSCH.Error[serviceNotExported, first]; MarkServerDown[theServer]; LOOP }; rejectedInvalidArgument => { IF specific THEN ERROR XNSCH.Error[protocolError, first]; MarkServerDown[theServer]; LOOP }; remoteError, argsError, resultsError, bulkDataError, protocolError, remoteClose, communicationFailure, cantConnectToRemote => -- can't retry, progress made -- { IF specific THEN ERROR XNSCH.Error[ (SELECT errorReason FROM communicationFailure => communicationFailure, cantConnectToRemote => communicationFailure, ENDCASE => protocolError), first]; MarkServerDown[theServer]; IF (NOT idempotent) AND (inClientProc) THEN ERROR XNSCH.Error[wasUpNowDown, first]; LOOP }; unknown, unknownClass, notImplemented, unknownOperation, notServerHandle, notClientHandle, addressInappropriateForClass -- can't happen -- => ERROR; ENDCASE => ERROR; }; CHOps.WrongServer => { IF specific THEN REJECT; MarkServerDead[theServer]; LOOP }; CHACLOps.WrongServer => { IF specific THEN ERROR CHOps.WrongServer[hint]; MarkServerDead[theServer]; LOOP }; }; IF specific THEN { inClientProc ฌ TRUE; proc[c.handle, c.host]; inClientProc ฌ FALSE; RETURN }; GetServerCandidate[]; -- sets [theServer, theHandle, state] IF state = none THEN EXIT; IF NOT theServer.knowDomainsServed THEN LearnDomainsServed[theHandle, theServer]; gotHint ฌ FALSE; inClientProc ฌ TRUE; proc[theHandle, theServer.address.host ! CHOps.WrongServer, CHACLOps.WrongServer => { theHint ฌ hint; gotHint ฌ TRUE; CONTINUE } ]; inClientProc ฌ FALSE; IF gotHint THEN { DeleteServerForDomain[domain, theServer]; -- correct the cache IF tryNo >= triesToUse THEN EXIT; hintServers ฌ ProcessHint[theHandle, theServer, theHint]; NoteServerAlreadyUsed[theServer]; LOOP }; NoteServerAlreadyUsed[theServer]; AddServerForDomain[domain, theServer]; CleanUp[]; RETURN; ENDLOOP; CleanUp[]; ERROR XNSCH.Error[allDown, first]; }; nNearbyServersToStart: CARDINAL ฌ 2; Daemon: PROC ~ { DO Process.PauseMsec[60000]; NACacheSweep[]; ENDLOOP; }; TRUSTED { Process.Detach[FORK Daemon[]] }; TRUSTED { Process.Detach[FORK BroadcastForNearbyServers[maxHops~defaultMaxHops, nWanted~nNearbyServersToStart, tryLimit~0]] }; }. ค XNSCHPrivateImpl.mesa Copyright ำ 1988, 1989, 1991, 1992 by Xerox Corporation. All rights reserved. Demers, August 3, 1988 9:44:08 am PDT Wes Irish, July 8, 1988 11:48:07 am PDT Willie-Sue, February 13, 1989 3:29:56 pm PST Tim Diebert: November 16, 1989 9:21:08 am PST Willie-s, February 14, 1992 10:54 am PST Name / Address Cache Cache of distinguished name / address pairs for recently looked-up names. Note the same name could end up in the cache more than once; this is benign. Periodically delete the oldest cache entry. Conversations This never raises an error, but if identity is invalid it will substitute NullIdentity, so the resulting conversation will be able to do simple queries but not updates. Get conversation ... Get Courier handle for server, if specified ... Server Objects Net and host are significant, socket is set to XNS.unknownSocket Lists of Servers IF hops >= XNSRouter.unreachable THEN LOOP; -- removed because XNSRouter.GetHops can lie about unreachability. Choose randomly among the servers at the best distance Cache of Servers by Domain For debugging. [h: Handle, s: IO.STREAM, checkAbort: BulkDataCheckAbortProc] RETURNS [abort: BOOL]; [s: IO.STREAM] RETURNS [abort: BOOL _ FALSE] The following is present because by convention every CH that serves a domain D:O also serves the "glue" domain O:CHServers, but won't include O:CHServers in the enumeration! I consider it BOGUS, but ... (ajd) e.g. "PARC:Xerox" -> "Xerox:CHServers" Cache of Nearby Servers 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. [key: Key, val: Val] RETURNS [quit: BOOL] [key: Key, val: Val] RETURNS [quit: BOOL] Hint Processing [h: Handle, s: IO.STREAM, checkAbort: BulkDataCheckAbortProc] RETURNS [abort: BOOL]; [s: IO.STREAM] RETURNS [abort: BOOL _ FALSE] Invoking a remote procedure Precondition: NOT ServerAlreadyUsed[server] Body of CallRemote ... If this server serves the right domain and can't answer, assume no other server can answer either. This is overly pessimistic. Doing better would be at least a little bit hard. useCourier, other, Mainline Code Rollback: Booting.RollbackProc ~ { TRUSTED { Process.Detach[FORK BroadcastForNearbyServers[maxHops~defaultMaxHops, nWanted~nNearbyServersToStart, tryLimit~0]] }; NACacheRollback[]; }; Booting.RegisterProcs[r: Rollback]; ส็–(cedarcode) style•NewlineDelimiter ™code™Kšœ ฯeœC™NK™%K™'K™,K™-K™(K™K˜—šฯk ˜ Kšœ žœ žœ˜8KšœžœZ˜nKšœžœ˜+Kšœ žœ›˜ชKšœžœ”˜ŸKšœžœ˜"Kšœžœ ˜K˜[Kšœžœžœ ˜6Kšœžœ4˜@Kšžœžœ0˜9Kšœžœ™˜ฆKšžœžœw˜‚Kšœžœ˜.Kšœ žœ˜,Kšœžœ˜#Kšœ žœ˜'Kšœžœ1˜HKšžœžœ˜Kšœ˜K˜—šฯnœžœž˜Kšžœ^žœ<˜ฆKšžœžœ˜K˜KšžœŸ œŸœ Ÿœ˜JK˜Kšœ žœ˜Kšœžœ˜K˜Kšžœžœžœ˜K˜Kšœžœžœ˜Kšœ žœžœ ˜Kšœ žœžœ ˜Kšœ žœžœ ˜$Kšœ žœžœ ˜$Kšœžœžœ˜K˜KšŸœžœžœ"˜@K˜Kšœ žœ˜ K˜eK˜head™K™IK™LK˜Kšœžœžœ˜,šœžœžœ˜#Kšœ˜Kšœ˜Kšœ žœ ˜K˜—Kšœžœ˜Kšœ žœ˜Kšœžœฯc6˜SK˜šŸ œž œžœ*žœ ˜Zšžœžœ˜&Kšœžœ4˜Kšžœ ž˜Kšžœ˜!Kšžœ2˜6—Kšœžœ˜K˜—K˜K˜K˜"K˜K˜K˜—šŸœžœžœžœ˜4Kš œ1žœžœ+žœžœ7žœ˜ฎKšœ˜K˜—š Ÿ œž œžœžœ žœžœ ˜pK˜šžœž˜šžœžœ˜:Kšžœžœžœ˜-—K˜ Kšžœ˜—Kš žœžœžœžœžœžœ˜2K˜K˜—šŸœžœžœ˜K˜Kšœ žœ˜K˜šžœžœž˜Kšœ˜Kšœ žœ˜ K˜ Kšžœ˜—Kšœ˜—K˜Kšœžœ˜&Kšœžœ˜Kšœ˜—Kšžœ@˜FKšœ˜—Kšœžœžœ˜2K˜ ˜uKšœAžœ˜K—Kšœ žœžœ˜&š žœžœžœžœž˜1šœ˜šžœ?˜EKšžœ˜ —K˜ Kšœ žœžœžœ ˜K˜ชKšœ žœ˜*šžœ žœžœ˜Kšœžœ ˜Kšœžœžœ ˜Kšœ˜Kšœžœ˜*Kšœžœžœ1žœ˜QKšœ.žœ˜5K˜(—K˜—Kšžœ˜—Kšœ˜K˜K˜——™Kšœ žœ˜Kšœ žœ (˜BK˜šŸ œžœžœ:žœžœžœžœ˜yKšœžœ$˜/K˜Kšœžœžœ˜Kšœžœ˜Kšœžœ˜Kšœ žœ˜K˜Kšœ$žœ˜(Kšœ žœžœ˜"Kšœžœžœ˜Kšœžœžœ˜K˜Kšœžœ˜Kšœžœžœ˜6K˜šŸœžœ˜6Kšœ+™+K˜(K˜ K˜—šŸœžœžœžœ˜?šžœžœžœž˜'Kšžœžœžœžœ˜4Kšžœ˜—Kšžœžœ˜—K˜šŸœžœ˜Kšžœ žœžœ&˜=Kšœ žœ˜K˜—šŸœžœ˜K˜ Kšžœ žœžœ5žœ˜RKšœ žœ˜šžœžœžœ˜"K˜ K˜PK˜—šžœ žœžœ˜K˜ K˜@Kšœ˜—š žœžœžœžœžœ˜:K˜ Kšžœžœžœ5˜OK˜VK˜—šžœ žœžœ˜K˜Kšœ<žœ˜Q—šžœ žœžœ˜K˜ Kšžœ˜ —K˜?K˜K˜—K™K˜šžœžœžœž˜)šžœ˜Kšžœ˜šœ<˜K˜Kšžœ˜—šœ_˜_Kšžœ žœžœžœ"˜>K˜Kšžœ˜—šœ˜Kšžœ žœžœžœ˜9K˜Kšžœ˜—šœ~  œ˜ šžœ žœžœžœ˜#šœžœ ž˜Kšœ-˜-Kšœ,˜,Kšžœ˜—Kšœ˜—K˜Kš žœžœ žœžœžœžœ˜SKšžœ˜—šœx œ˜Kšžœ˜—Kšžœžœ˜—K˜—šœ˜Kšžœ žœžœ˜K˜Kšžœ˜—šœ˜Kšžœ žœžœ˜/K˜Kšžœ˜—K˜K˜—šžœ žœ˜Kšœžœ˜Kšœ˜Kšœžœ˜Kšžœ˜ —K˜Kšœ %˜;Kšžœžœžœ˜šžœžœž˜'Kšœ)˜)—Kšœ žœ˜Kšœžœ˜šœ'˜'šœ.˜.K˜Kšœ žœ˜Kšžœ˜ ——Kšœžœ˜šžœ žœ˜Kšœ* ˜>Kšžœžœžœ˜!K˜9K˜!Kšžœ˜—K˜!Kšœ&˜&K˜ Kšžœ˜Kšžœ˜—K˜ Kšžœžœ˜"K˜——™ Kšœžœ˜$K˜šŸœžœ˜šž˜K˜K˜Kšžœ˜—K˜K˜—šŸœ™"Kšžœžœa™~K™K™—K˜K˜Kšžœžœ ˜*Kšžœžœa˜~Kšœ#™#—K˜—K˜—…—V yซ