<> <> <> DIRECTORY Arpa, ArpaExtras USING [MyAddress], ArpaName, ArpaNameCache, ArpaNameQuery USING [ARR, CNameRR, MailForwardRecord, MxRR, NsRR, PtrRR, Query, QType, Reply, Seconds, SoaRR, SourceOfAuthRecord], ArpaNameLogSupport, ArpaNameSupport, ArpaRouter USING [GetHops, Hops, unreachable], ArpaUDP USING [Milliseconds], Commander USING [CommandProc, Register], IO USING [STREAM, Flush, PutRope], Process USING [Detach], Rope USING [ROPE, Cat, Find, IsEmpty, Length, Substr], RopeList USING [Append], ViewerEvents USING [EventProc, RegisterEventProc], ViewerIO USING [CreateViewerStreams, GetViewerFromStream, Viewer]; ArpaNameImpl: CEDAR MONITOR IMPORTS ArpaExtras, ArpaNameCache, ArpaNameLogSupport, ArpaNameSupport, ArpaNameQuery, ArpaRouter, Commander, IO, Process, Rope, RopeList, ViewerEvents, ViewerIO EXPORTS ArpaName = BEGIN OPEN ArpaName; ROPE: TYPE = Rope.ROPE; <> <> <> <> <> <> <> <<>> arpanetRootServers: LIST OF Arpa.Address _ LIST[[10,0,0,51], [26,0,0,73],[10,0,0,52],[26,3,0,103],[128,20,1,2],[192,5,25,82]]; <> <> <> <<>> localRootServers: LIST OF Arpa.Address _ LIST[[13,2,16,8], [13,1,100,208]]; localDefaultDomain: ROPE _ "parc.Xerox.COM"; myAddress: Arpa.Address = ArpaExtras.MyAddress[]; downEntryTTL: ArpaNameQuery.Seconds _ 1777; -- ~ 0.5 hour, s/b < sick mail host retry time downServerTTL: ArpaNameQuery.Seconds _ 1777; -- ~ 0.5 hour bogusTTL: ArpaNameQuery.Seconds _ 86400; -- 1 day mxDefaultTTL: ArpaNameQuery.Seconds _ 86400; -- 1 day upperLimitTTL: ArpaNameQuery.Seconds _ 1222222; -- approx. 2 weeks, 2's for noticeability lowerLimitTTL: ArpaNameQuery.Seconds _ 1222; -- approx. 20 mins GetLocalRootServers: PUBLIC PROC RETURNS[addrs: LIST OF Arpa.Address] = {RETURN[localRootServers]}; SetLocalRootServers: PUBLIC PROC[addrs: LIST OF Arpa.Address] = { IF addrs # NIL THEN localRootServers _ addrs}; GetArpanetRootServers: PUBLIC PROC RETURNS[addrs: LIST OF Arpa.Address] = {RETURN[arpanetRootServers]}; SetArpanetRootServers: PUBLIC PROC[addrs: LIST OF Arpa.Address] = { IF addrs # NIL THEN arpanetRootServers _ addrs}; GetDefaultDomain: PUBLIC PROC RETURNS[domain: ROPE] = {RETURN[localDefaultDomain]}; SetDefaultDomain: PUBLIC PROC[domain: ROPE] = { IF ~Rope.IsEmpty[domain] THEN localDefaultDomain _ domain}; MyName: PUBLIC PROC RETURNS[name: ROPE_NIL] = { [name, , ] _ AddressToName[myAddress]; IF name = NIL THEN RETURN[ArpaNameSupport.AddressToRope[myAddress]]; }; NameToAddress: PUBLIC PROC [name: Rope.ROPE, resolv: BOOL _ FALSE] RETURNS [addr: Arpa.Address _ Arpa.nullAddress, status: ReplyStatus _ down, source: Arpa.Address _ myAddress] = { addrs: LIST OF Arpa.Address _ NIL; IF Rope.IsEmpty[name] THEN RETURN[Arpa.nullAddress, bogus, myAddress]; [addrs, status, source] _ NameToAddressList[name, resolv]; IF addrs = NIL THEN RETURN[Arpa.nullAddress, status, source]; RETURN[addrs.first, status, source]; }; NameToAddressList: PUBLIC PROC [name: Rope.ROPE, resolv: BOOL _ FALSE] RETURNS [addrs: LIST OF Arpa.Address _ NIL, status: ReplyStatus _ down, source: Arpa.Address _ myAddress] = { found: BOOL; DoLookups: PROC[name: ROPE, resolv: BOOL _ FALSE] RETURNS[found: BOOL _ FALSE, newName: ROPE _ NIL, addrs: LIST OF Arpa.Address _ NIL, status: ReplyStatus _ down, source: Arpa.Address _ myAddress] = { cName: ROPE _ NIL; [found, addrs, source] _ NameToAddressLookup[name, resolv]; IF found AND addrs # NIL THEN {status _ ok; RETURN}; [found, cName, source] _ AliasToNameLookup[name, resolv]; IF found AND ~Rope.IsEmpty[cName] THEN newName _ cName ELSE newName _ name; [found, addrs, source] _ NameToAddressLookup[newName, resolv]; IF found AND addrs # NIL THEN {status _ ok; RETURN}; [found, source] _ NameToBogusLookup[newName, resolv]; IF found THEN {status _ bogus; RETURN}; [found, source] _ NameToDownLookup[newName]; IF found THEN {status _ down; RETURN}; }; IF Rope.IsEmpty[name] THEN RETURN[NIL, bogus, myAddress]; IF ArpaNameSupport.IsAddressRope[name] THEN { -- It's an address already address: Arpa.Address _ ArpaNameSupport.RopeToAddress[name]; IF address # Arpa.nullAddress THEN RETURN[LIST[ArpaNameSupport.RopeToAddress[name]], ok, myAddress] ELSE RETURN[NIL, bogus, myAddress]; }; name _ DefaultDomain[name]; [found, name, addrs, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[addrs, status, source]; DoQuery[name, a, resolv]; [found, name, addrs, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[addrs, status, source]; [found,, source] _ NameToZoneLookup[name, resolv]; IF found THEN RETURN[NIL, other, source]; [found,, source] _ NameToMXLookup[name, resolv]; IF found THEN RETURN[NIL, other, source]; RETURN[NIL, other, myAddress]; }; NameToMailHostList: PUBLIC PROC [name: ROPE, resolv: BOOL _ FALSE] RETURNS [hosts: LIST OF ROPE _ NIL, status: ReplyStatus _ down, source: Arpa.Address _ myAddress] = { found: BOOL; DoLookups: PROC[name: ROPE, resolv: BOOL _ FALSE] RETURNS[found: BOOL _ FALSE, newName: ROPE _ NIL, hosts: LIST OF ROPE _ NIL, status: ReplyStatus _ down, source: Arpa.Address _ myAddress] = { cName: ROPE _ NIL; [found, hosts, source] _ NameToMXLookup[name, resolv]; IF found AND hosts # NIL THEN {status _ ok; RETURN}; [found, cName, source] _ AliasToNameLookup[name, resolv]; IF found AND ~Rope.IsEmpty[cName] THEN name _ newName ELSE newName _ name; [found, hosts, source] _ NameToMXLookup[name, resolv]; IF found AND hosts # NIL THEN {status _ ok; RETURN}; [found, source] _ NameToBogusLookup[name, resolv]; IF found THEN {status _ bogus; RETURN}; [found, source] _ NameToDownLookup[name]; IF found THEN {status _ down; RETURN}; }; IF Rope.IsEmpty[name] THEN RETURN[NIL, bogus, myAddress]; name _ DefaultDomain[name]; [found, name, hosts, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[hosts, status, source]; <> IF ~ArpaNameCache.FetchZone[name].found THEN DoQuery[name, ns, resolv]; [found, name, hosts, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[hosts, status, source]; -- in case bogus or down, avoid extra query DoQuery[name, mx, resolv]; [found, name, hosts, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[hosts, status, source]; [found,, source] _ NameToZoneLookup[name, resolv]; IF found THEN RETURN[NIL, other, source]; [found,, source] _ NameToAddressLookup[name, resolv]; IF found THEN RETURN[NIL, other, source]; RETURN[NIL, other, myAddress]; }; AddressToName: PUBLIC PROC [addr: Arpa.Address, resolv: BOOL _ FALSE] RETURNS [name: Rope.ROPE_NIL, status: ReplyStatus _ down, source: Arpa.Address _ myAddress] = { addrRope: ROPE _ ArpaNameSupport.AddressToRope[addr]; found: BOOL; DoLookups: PROC[rope: ROPE, resolv: BOOL _ FALSE] RETURNS[found: BOOL _ FALSE, name: ROPE _ NIL, status: ReplyStatus _ down, source: Arpa.Address _ myAddress] = { [found, name, source] _ AddressToNameLookup[rope, resolv]; IF found AND name # NIL THEN {status _ ok; RETURN}; [found, source] _ AddressToBogusLookup[rope, resolv]; IF found THEN {status _ bogus; RETURN}; [found, source] _ AddressToDownLookup[rope]; IF found THEN {status _ down; RETURN}; }; [found, name, status, source] _ DoLookups[addrRope, resolv]; IF found THEN { IF name = NIL AND status # bogus THEN name _ addrRope; RETURN[name, status, source]; }; DoQuery[addrRope, ptr, resolv]; [found, name, status, source] _ DoLookups[addrRope, resolv]; IF found THEN { IF name = NIL AND status # bogus THEN name _ addrRope; RETURN[name, status, source]; }; RETURN[name, other, myAddress]; }; AddressRopeToName: PUBLIC PROC [addr: ROPE, resolv: BOOL _ FALSE] RETURNS [name: ROPE, status: ReplyStatus _ down, source: Arpa.Address_ myAddress] = { address: Arpa.Address; IF Rope.IsEmpty[addr] THEN RETURN[NIL, bogus, source]; address _ ArpaNameSupport.RopeToAddress[addr]; IF address = Arpa.nullAddress THEN RETURN[NIL, bogus, source]; [name, status, source] _ AddressToName[address, resolv]; IF status = down OR status = other THEN name _ addr; }; AliasToName: PUBLIC PROC [alias: Rope.ROPE, resolv: BOOL _ FALSE] RETURNS [name: Rope.ROPE_ NIL, status: ReplyStatus _ down, source: Arpa.Address _ Arpa.nullAddress] = { found: BOOL; DoLookups: PROC[alias: ROPE, resolv: BOOL _ FALSE] RETURNS[found: BOOL _ FALSE, name: ROPE _ NIL, status: ReplyStatus _ down, source: Arpa.Address _ myAddress] = { [found, name, source] _ AliasToNameLookup[alias, resolv]; IF found AND name # NIL THEN RETURN[found, name, ok, source]; [found, source] _ NameToBogusLookup[alias, resolv]; IF found THEN RETURN[found, NIL, bogus, source]; [found, source] _ NameToDownLookup[alias]; IF found THEN RETURN[found, NIL, down, source]; }; IF Rope.IsEmpty[alias] THEN RETURN[NIL, bogus, myAddress]; alias _ DefaultDomain[alias]; [found, name, status, source] _ DoLookups[alias, resolv]; IF found THEN RETURN[name, status, source]; DoQuery[alias, cName, resolv]; [found, name, status, source] _ DoLookups[alias, resolv]; IF found THEN RETURN[name, status, source]; [found,, source] _ NameToZoneLookup[alias, resolv]; IF found THEN RETURN[alias, other, source]; [found,, source] _ NameToAddressLookup[alias, resolv]; IF found THEN RETURN[alias, other, source]; [found,, source] _ NameToMXLookup[alias, resolv]; IF found THEN RETURN[alias, other, source]; RETURN[alias, other, myAddress]; }; DomainServers: PUBLIC PROC [name: Rope.ROPE, resolv: BOOL _ FALSE] RETURNS [servers: LIST OF Rope.ROPE_ NIL, status: ReplyStatus _ down, source: Arpa.Address _ myAddress] = { found: BOOL; DoLookups: PROC[name: ROPE, resolv: BOOL _ FALSE] RETURNS[found: BOOL _ FALSE, servers: LIST OF ROPE _ NIL, status: ReplyStatus _ down, source: Arpa.Address _ myAddress] = { [found, servers, source] _ NameToZoneLookup[name, resolv]; IF found AND servers # NIL THEN {status _ ok; RETURN}; [found, source] _ NameToBogusLookup[name, resolv]; IF found THEN {status _ bogus; RETURN}; [found, source] _ NameToDownLookup[name]; IF found THEN {status _ down; RETURN}; }; IF Rope.IsEmpty[name] THEN RETURN[NIL, bogus, myAddress]; IF NoDots[name] THEN { status _ AliasToName[name, resolv].status; IF status = ok OR status = other THEN name _ GetDefaultDomain[]; }; [found, servers, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[servers, status, source]; DoQuery[name, ns, resolv]; [found, servers, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[servers, status, source]; name _ StripHead[name].tail; IF Rope.IsEmpty[name] THEN RETURN[NIL, bogus, myAddress]; [found, servers, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[servers, status, source]; DoQuery[name, ns, resolv]; [found, servers, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[servers, status, source]; RETURN[NIL, other, myAddress]; }; DomainInfo: PUBLIC PROC [name: ROPE, resolv: BOOL _ FALSE] RETURNS [domainContact: ROPE, primaryServer: ROPE, status: ReplyStatus, source: Arpa.Address _ myAddress] = { found: BOOL; DoLookups: PROC[name: ROPE, resolv: BOOL _ FALSE] RETURNS[found: BOOL _ FALSE, domainContact: ROPE, primaryServer: ROPE, status: ReplyStatus _ down, source: Arpa.Address _ myAddress] = { [found, domainContact, primaryServer, source] _ NameToZoneInfoLookup[name, resolv]; IF found AND domainContact # NIL THEN {status _ ok; RETURN}; [found, source] _ NameToBogusLookup[name, resolv]; IF found THEN {status _ bogus; RETURN}; [found, source] _ NameToDownLookup[name]; IF found THEN {status _ down; RETURN}; }; IF Rope.IsEmpty[name] THEN RETURN[NIL, NIL, bogus, myAddress]; IF NoDots[name] THEN { status _ AliasToName[name, resolv].status; IF status = ok OR status = other THEN name _ GetDefaultDomain[]; }; [found, domainContact, primaryServer, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[domainContact, primaryServer, status, source]; DoQuery[name, soa, resolv]; [found, domainContact, primaryServer, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[domainContact, primaryServer, status, source]; name _ StripHead[name].tail; IF Rope.IsEmpty[name] THEN RETURN[NIL, NIL, bogus, myAddress]; [found, domainContact, primaryServer, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[domainContact, primaryServer, status, source]; DoQuery[name, soa, resolv]; [found, domainContact, primaryServer, status, source] _ DoLookups[name, resolv]; IF found THEN RETURN[domainContact, primaryServer, status, source]; RETURN[NIL, NIL, other, myAddress]; }; ARec: TYPE = RECORD[ loaded: BOOL _ FALSE, name: ROPE _ NIL, ttl: ArpaNameQuery.Seconds, addresses: LIST OF Arpa.Address _ NIL ]; NSRec: TYPE = RECORD[ loaded: BOOL _ FALSE, name: ROPE _ NIL, ttl: ArpaNameQuery.Seconds, servers: LIST OF ROPE _ NIL ]; AliasRec: TYPE = RECORD[ loaded: BOOL _ FALSE, name: ROPE _ NIL, ttl: ArpaNameQuery.Seconds, canonicalName: ROPE _ NIL ]; PtrRec: TYPE = RECORD[ loaded: BOOL _ FALSE, address: ROPE _ NIL, ttl: ArpaNameQuery.Seconds, names: LIST OF ROPE _ NIL ]; SoaRec: TYPE = RECORD[ loaded: BOOL _ FALSE, name: ROPE _ NIL, ttl: ArpaNameQuery.Seconds, soa: ArpaNameQuery.SourceOfAuthRecord ]; MXRec: TYPE = RECORD[ loaded: BOOL _ FALSE, name: ROPE _ NIL, ttl: ArpaNameQuery.Seconds, mxList: LIST OF ArpaNameQuery.MailForwardRecord _ NIL ]; DoQuery: PROC[query: ROPE, type: ArpaNameQuery.QType, resolv: BOOL _ FALSE, serverHint: Arpa.Address _ Arpa.nullAddress] = { reply: ArpaNameQuery.Reply; queryRope: ROPE; aRec: ARec; nsRec: NSRec; mxRec: MXRec; aliasRec: AliasRec; soaRec: SoaRec; ptrRec: PtrRec; source: Arpa.Address _ myAddress; IF Rope.IsEmpty[query] THEN RETURN; IF type = ptr THEN { queryRope _ ArpaNameSupport.AddressRopeToQueryRope[query]; IF Rope.IsEmpty[queryRope] THEN RETURN} ELSE queryRope _ query; reply _ SendQuery[queryRope, type, resolv, serverHint]; IF reply = NIL THEN { SELECT type FROM a, ns, cName, soa, mx => { entry: ArpaNameCache.DownNameEntry _ ArpaNameCache.UpdateDownName[query, myAddress, downEntryTTL, TRUE]; ArpaNameLogSupport.DownNameSummaryLine[entry, TRUE,,Log]; }; ptr => { entry: ArpaNameCache.DownAddressEntry _ ArpaNameCache.UpdateDownAddressRope[query, myAddress, downEntryTTL, TRUE]; ArpaNameLogSupport.DownAddressSummaryLine[entry, TRUE,,Log]; }; ENDCASE; RETURN; }; source _ reply.source; SELECT reply.hdr.rcode FROM ok => NULL; nameError => { IF reply.hdr.authoritative THEN { SELECT type FROM a, ns, cName, soa, mx => { entry: ArpaNameCache.BogusNameEntry _ ArpaNameCache.UpdateBogusName[query, source, bogusTTL, TRUE]; ArpaNameLogSupport.BogusNameSummaryLine[entry, TRUE,,Log]; }; ptr => { entry: ArpaNameCache.BogusAddressEntry _ ArpaNameCache.UpdateBogusAddressRope[query, source, bogusTTL, TRUE]; ArpaNameLogSupport.BogusAddressSummaryLine[entry, TRUE,,Log]; }; ENDCASE; RETURN}; }; ENDCASE => { SELECT type FROM a, ns, cName, soa, mx => { entry: ArpaNameCache.DownNameEntry _ ArpaNameCache.UpdateDownName[query, source, downEntryTTL, TRUE]; ArpaNameLogSupport.DownNameSummaryLine[entry, TRUE,,Log]; }; ptr => { entry: ArpaNameCache.DownAddressEntry _ ArpaNameCache.UpdateDownAddressRope[query, source, downEntryTTL, TRUE]; ArpaNameLogSupport.DownAddressSummaryLine[entry, TRUE,,Log]; }; ENDCASE; RETURN; }; <> <<>> IF reply.anCount = 0 AND reply.nsCount = 0 AND type = mx AND reply.hdr.authoritative AND ~ArpaNameCache.FetchZone[query].found THEN { mxRec.loaded _ TRUE; mxRec.name _ query; mxRec.ttl _ mxDefaultTTL; mxRec.mxList _ AppendUniqueMXRec[[preference: 0, host: query], mxRec.mxList]; }; FOR i: INT IN [0..reply.anCount) DO WITH reply.answers[i] SELECT FROM rr: ArpaNameQuery.ARR => IF rr.class = in THEN { aRec.loaded _ TRUE; aRec.name _ rr.name; aRec.ttl _ LimitTTL[rr.ttl]; aRec.addresses _ ArpaNameSupport.AppendUniqueAddress[rr.address, aRec.addresses]; }; rr: ArpaNameQuery.PtrRR => IF rr.class = in THEN { ptrRec.loaded _ TRUE; ptrRec.address _ query; ptrRec.ttl _ LimitTTL[rr.ttl]; ptrRec.names _ RopeList.Append[ptrRec.names, LIST[rr.ptrRope]]; }; rr: ArpaNameQuery.CNameRR => IF rr.class = in THEN { aliasRec.loaded _ TRUE; aliasRec.name _ rr.name; aliasRec.ttl _ LimitTTL[rr.ttl]; aliasRec.canonicalName _ rr.cNameRope; }; rr: ArpaNameQuery.NsRR => IF rr.class = in THEN { nsRec.loaded _ TRUE; nsRec.name _ rr.name; nsRec.ttl _ LimitTTL[rr.ttl]; nsRec.servers _ RopeList.Append[nsRec.servers, LIST[rr.serverRope]]; }; rr: ArpaNameQuery.SoaRR => IF rr.class = in THEN { soaRec.loaded _ TRUE; soaRec.name _ rr.name; soaRec.ttl _LimitTTL[rr.ttl]; soaRec.soa _ rr.soaRec; }; rr: ArpaNameQuery.MxRR => IF rr.class = in THEN { mxRec.loaded _ TRUE; mxRec.name _ rr.name; mxRec.ttl _ LimitTTL[rr.ttl]; mxRec.mxList _ AppendUniqueMXRec[rr.mxRec, mxRec.mxList]; }; ENDCASE; ENDLOOP; IF aRec.loaded THEN { entry: ArpaNameCache.NameEntry; aRec.addresses _ SortAddresses[aRec.addresses]; entry _ ArpaNameCache.UpdateName[aRec.name, source, aRec.ttl, reply.hdr.authoritative, aRec.addresses]; ArpaNameLogSupport.NameSummaryLine[entry, TRUE,,Log]; }; IF nsRec.loaded THEN { entry: ArpaNameCache.ZoneEntry _ ArpaNameCache.UpdateZone[nsRec.name, source, nsRec.ttl, reply.hdr.authoritative, nsRec.servers]; ArpaNameLogSupport.ZoneSummaryLine[entry, TRUE,,Log]; }; IF mxRec.loaded THEN { entry: ArpaNameCache.MXEntry; mxRec.mxList _ SortMXList[mxRec.mxList]; entry _ ArpaNameCache.UpdateMX[mxRec.name, source, mxRec.ttl, reply.hdr.authoritative, mxRec.mxList]; ArpaNameLogSupport.MXSummaryLine[entry, TRUE,,Log]; }; IF aliasRec.loaded THEN { entry: ArpaNameCache.AliasEntry _ ArpaNameCache.UpdateAlias[aliasRec.name, source, aliasRec.ttl, reply.hdr.authoritative, aliasRec.canonicalName]; ArpaNameLogSupport.AliasSummaryLine[entry, TRUE,,Log]; }; IF soaRec.loaded THEN { entry: ArpaNameCache.SoaEntry _ ArpaNameCache.UpdateSoa[soaRec.name, source, soaRec.ttl, reply.hdr.authoritative, NEW[ArpaNameQuery.SourceOfAuthRecord _ soaRec.soa]]; ArpaNameLogSupport.SoaSummaryLine[entry, TRUE,,Log]; }; IF ptrRec.loaded THEN { entry: ArpaNameCache.AddressEntry _ ArpaNameCache.UpdateAddressRope[ptrRec.address, source, ptrRec.ttl, reply.hdr.authoritative, ptrRec.names]; ArpaNameLogSupport.AddressSummaryLine[entry, TRUE,,Log]; }; }; LimitTTL: PROC[in: ArpaNameQuery.Seconds] RETURNS[out: ArpaNameQuery.Seconds] = { out _ in; IF in > upperLimitTTL THEN out _ upperLimitTTL; IF in < lowerLimitTTL THEN out _ lowerLimitTTL; }; SendQuery: PROC[rope: ROPE, type: ArpaNameQuery.QType, resolv: BOOL _ FALSE, serverHint: Arpa.Address _ Arpa.nullAddress] RETURNS[reply: ArpaNameQuery.Reply] = { servers: LIST OF Arpa.Address; nServers: CARD; baseTimeout: ARRAY [1..3] OF ArpaUDP.Milliseconds = [4000, 8000, 16000]; downServer: ArpaNameCache.DownServerEntry _ NIL; IF Rope.IsEmpty[rope] THEN RETURN[NIL]; [servers, nServers] _ FindServers[rope, resolv, serverHint]; IF nServers = 0 OR servers = NIL THEN RETURN[NIL]; <> <<>> FOR list: LIST OF Arpa.Address _ servers, list.rest UNTIL list = NIL DO reply _ ArpaNameQuery.Query[ server: list.first, query: rope, type: type, class: in, recurDesired: ~resolv, timeout: 3000, -- 3 seconds retry: 0]; IF reply # NIL THEN { IF reply.hdr.rcode = nameError OR reply.hdr.rcode = ok THEN RETURN}; ENDLOOP; <> FOR list: LIST OF Arpa.Address _ servers, list.rest UNTIL list = NIL DO FOR retry: INT IN [1..3] DO reply _ ArpaNameQuery.Query[ server: list.first, query: rope, type: type, class: in, recurDesired: ~resolv, timeout: baseTimeout[retry]/nServers, retry: 0]; IF reply # NIL THEN { IF reply.hdr.rcode = nameError OR reply.hdr.rcode = ok THEN RETURN}; ENDLOOP; downServer _ ArpaNameCache.UpdateDownServer[list.first, myAddress, downServerTTL, TRUE]; ArpaNameLogSupport.DownServerSummaryLine[downServer, TRUE,, Log]; ENDLOOP; }; FindServers: PROC [rope: ROPE, resolv: BOOL _ FALSE, serverHint: Arpa.Address _ Arpa.nullAddress] RETURNS[answers: LIST OF Arpa.Address_ NIL, nServers: CARD _ 0] = { servers: LIST OF Arpa.Address _ IF resolv THEN arpanetRootServers ELSE localRootServers; IF serverHint # Arpa.nullAddress THEN answers _ ArpaNameSupport.AppendUniqueAddress[serverHint, answers]; FOR list: LIST OF Arpa.Address _ servers, list.rest UNTIL list = NIL DO server: Arpa.Address _ list.first; IF ~ArpaNameCache.FetchDownServer[server, FALSE].found THEN answers _ ArpaNameSupport.AppendUniqueAddress[server, answers]; ENDLOOP; nServers _ ArpaNameSupport.CountAddresses[answers]; RETURN[answers, nServers]; }; SortAddresses: PUBLIC PROC [list: LIST OF Arpa.Address] RETURNS[result: LIST OF Arpa.Address] = { rest: LIST OF Arpa.Address; bestAddress: Arpa.Address _ BestAddress[list]; IF list = NIL THEN RETURN[NIL]; result _ LIST[bestAddress]; rest _ RemoveAddress[list, bestAddress]; UNTIL rest = NIL DO bestAddress _ BestAddress[rest]; result _ ArpaNameSupport.AppendUniqueAddress[bestAddress, result]; rest _ RemoveAddress[rest, bestAddress]; ENDLOOP; }; RemoveAddress: PROC [list: LIST OF Arpa.Address, target: Arpa.Address] RETURNS[result: LIST OF Arpa.Address] = { z: LIST OF Arpa.Address _ NIL; result _ NIL; UNTIL list = NIL DO IF list.first # target THEN {IF result = NIL THEN {result _ CONS[list.first, NIL]; z _ result} ELSE {z.rest _ CONS[list.first, z.rest]; z _ z.rest}}; list _ list.rest; ENDLOOP; }; BestAddress: PUBLIC PROC [addresses: LIST OF Arpa.Address] RETURNS [bestAddress: Arpa.Address] = { bestHops: ArpaRouter.Hops _ ArpaRouter.unreachable; bestAddress _ Arpa.nullAddress; FOR list: LIST OF Arpa.Address _ addresses, list.rest UNTIL list = NIL DO hops: ArpaRouter.Hops _ ArpaRouter.GetHops[list.first]; IF hops < bestHops THEN {bestHops _ hops; bestAddress _ list.first}; ENDLOOP; IF bestAddress = Arpa.nullAddress THEN bestAddress _ addresses.first; RETURN[bestAddress]; }; SortMXList: PROC[list: LIST OF ArpaNameQuery.MailForwardRecord] RETURNS[result: LIST OF ArpaNameQuery.MailForwardRecord] = { rest: LIST OF ArpaNameQuery.MailForwardRecord; bestMXRec: ArpaNameQuery.MailForwardRecord _ BestMXRec[list]; IF list = NIL THEN RETURN[NIL]; result _ LIST[bestMXRec]; rest _ RemoveMXRec[list, bestMXRec]; UNTIL rest = NIL DO bestMXRec _ BestMXRec[rest]; result _ AppendUniqueMXRec[bestMXRec, result]; rest _ RemoveMXRec[rest, bestMXRec]; ENDLOOP; RETURN[result]; }; BestMXRec: PUBLIC PROC [in: LIST OF ArpaNameQuery.MailForwardRecord] RETURNS [bestMXRec: ArpaNameQuery.MailForwardRecord] = { bestPreference: INT _ INT.LAST; FOR list: LIST OF ArpaNameQuery.MailForwardRecord _ in, list.rest UNTIL list = NIL DO IF list.first.preference < bestPreference THEN {bestPreference _ list.first.preference; bestMXRec _ list.first}; ENDLOOP; RETURN[bestMXRec]; }; RemoveMXRec: PROC [list: LIST OF ArpaNameQuery.MailForwardRecord, target: ArpaNameQuery.MailForwardRecord] RETURNS[result: LIST OF ArpaNameQuery.MailForwardRecord] = { z: LIST OF ArpaNameQuery.MailForwardRecord _ NIL; result _ NIL; UNTIL list = NIL DO IF list.first # target THEN {IF result = NIL THEN {result _ CONS[list.first, NIL]; z _ result} ELSE {z.rest _ CONS[list.first, z.rest]; z _ z.rest}}; list _ list.rest; ENDLOOP; }; AppendUniqueMXRec: PUBLIC PROC [mxRec: ArpaNameQuery.MailForwardRecord, list: LIST OF ArpaNameQuery.MailForwardRecord _ NIL] RETURNS [new: LIST OF ArpaNameQuery.MailForwardRecord _ NIL] = { IF list = NIL THEN RETURN[LIST[mxRec]]; new _ list; DO IF list.first = mxRec THEN RETURN; IF list.rest = NIL THEN { list.rest _ LIST[mxRec]; RETURN; }; list _ list.rest; ENDLOOP; }; NameToBogusLookup: PROC [name: ROPE, resolv: BOOL _ FALSE, acceptOld: BOOL _ TRUE] RETURNS [found: BOOL _ FALSE, source: Arpa.Address _ Arpa.nullAddress] = { expired: BOOL; entry: ArpaNameCache.BogusNameEntry _ NIL; [found, expired, entry] _ ArpaNameCache.FetchBogusName[name, acceptOld]; IF entry # NIL THEN source _ entry.source; IF expired AND ~ArpaNameCache.FetchDownName[name, FALSE].found THEN TRUSTED {Process.Detach[FORK DoQuery[name, a, resolv, source]]}; -- attempt refresh RETURN[found, source]; }; NameToDownLookup: PROC [name: ROPE] RETURNS [found: BOOL _ FALSE, source: Arpa.Address _ Arpa.nullAddress] = { expired: BOOL; entry: ArpaNameCache.DownNameEntry; [found, expired, entry] _ ArpaNameCache.FetchDownName[name, FALSE]; IF entry # NIL THEN source _ entry.source; RETURN[found, source]; }; AddressToBogusLookup: PROC [rope: ROPE, resolv: BOOL _ FALSE, acceptOld: BOOL _ TRUE] RETURNS [found: BOOL _ FALSE, source: Arpa.Address _ Arpa.nullAddress] = { expired: BOOL; entry: ArpaNameCache.BogusAddressEntry _ NIL; [found, expired, entry] _ ArpaNameCache.FetchBogusAddressRope[rope, acceptOld]; IF entry # NIL THEN source _ entry.source; IF expired AND ~ArpaNameCache.FetchDownAddressRope[rope, FALSE].found THEN TRUSTED {Process.Detach[FORK DoQuery[rope, ptr, resolv, source]]};-- attempt refresh RETURN[found, source]; }; AddressToDownLookup: PROC [rope: ROPE] RETURNS [found: BOOL _ FALSE, source: Arpa.Address _ myAddress] = { expired: BOOL; entry: ArpaNameCache.DownAddressEntry; [found, expired, entry] _ ArpaNameCache.FetchDownAddressRope[rope, FALSE]; IF entry # NIL THEN source _ entry.source; RETURN[found, source]; }; NameToAddressLookup: PROC [name: ROPE, resolv: BOOL _ FALSE, acceptOld: BOOL _ TRUE] RETURNS [found: BOOL _ FALSE, addrs: LIST OF Arpa.Address _ NIL, source: Arpa.Address _ Arpa.nullAddress] = { expired: BOOL; entry: ArpaNameCache.NameEntry; [found, expired, entry] _ ArpaNameCache.FetchName[name, acceptOld]; IF entry # NIL THEN source _ entry.source; IF expired AND ~ArpaNameCache.FetchDownName[name, FALSE].found THEN TRUSTED {Process.Detach[FORK DoQuery[name, a, resolv, source]]};-- attempt refresh IF found THEN { addrs _ entry.addresses; IF addrs # NIL THEN RETURN[found, addrs, source]}; RETURN[FALSE, NIL, source]; }; AddressToNameLookup: PROC [rope: ROPE, resolv: BOOL _ FALSE, acceptOld: BOOL _ TRUE] RETURNS [found: BOOL _ FALSE, name: ROPE _ NIL, source: Arpa.Address _ Arpa.nullAddress] = { expired: BOOL; entry: ArpaNameCache.AddressEntry; [found, expired, entry] _ ArpaNameCache.FetchAddressRope[rope, acceptOld]; IF entry # NIL THEN source _ entry.source; IF expired AND ~ArpaNameCache.FetchDownAddressRope[rope, FALSE].found THEN TRUSTED {Process.Detach[FORK DoQuery[rope, ptr, resolv, source]]};-- attempt refresh IF found THEN { name _ entry.names.first; IF name # NIL THEN RETURN[found, name, source]}; RETURN[FALSE, NIL, source]; }; NameToMXLookup: PROC [name: ROPE, resolv: BOOL _ FALSE, acceptOld: BOOL _ TRUE] RETURNS [found: BOOL _ FALSE, hosts: LIST OF ROPE _ NIL, source: Arpa.Address _ Arpa.nullAddress] = { mxList: LIST OF ArpaNameQuery.MailForwardRecord _ NIL; entry: ArpaNameCache.MXEntry; expired: BOOL; [found, expired, entry] _ ArpaNameCache.FetchMX[name, acceptOld]; IF entry # NIL THEN source _ entry.source; IF expired AND ~ArpaNameCache.FetchDownName[name, FALSE].found THEN TRUSTED {Process.Detach[FORK DoQuery[name, mx, resolv, source]]};-- attempt refresh IF found THEN { mxList _ entry.mxList; hosts _ HostsFromMXList[mxList]; IF hosts # NIL THEN RETURN[found, hosts, source]; }; RETURN[FALSE, NIL, source]; }; HostsFromMXList: PROC [mxList: LIST OF ArpaNameQuery.MailForwardRecord] RETURNS [hosts: LIST OF ROPE _ NIL] ~ { IF mxList = NIL THEN RETURN; FOR list: LIST OF ArpaNameQuery.MailForwardRecord _ mxList, list.rest UNTIL list = NIL DO hosts _ RopeList.Append[hosts, LIST[list.first.host]]; ENDLOOP; }; NameToZoneLookup: PROC [name: ROPE, resolv: BOOL _ FALSE, acceptOld: BOOL _ TRUE] RETURNS [found: BOOL _ FALSE, servers: LIST OF ROPE _ NIL, source: Arpa.Address _ Arpa.nullAddress] = { entry: ArpaNameCache.ZoneEntry; expired: BOOL; [found, expired, entry] _ ArpaNameCache.FetchZone[name, acceptOld]; IF entry # NIL THEN source _ entry.source; IF expired AND ~ArpaNameCache.FetchDownName[name, FALSE].found THEN TRUSTED {Process.Detach[FORK DoQuery[name, ns, resolv, source]]}; -- attempt refresh IF found THEN { servers _ entry.servers; IF name # NIL THEN RETURN[found, servers, source]}; RETURN[FALSE, NIL, source]; }; NameToZoneInfoLookup: PROC [name: ROPE, resolv: BOOL _ FALSE, acceptOld: BOOL _ TRUE] RETURNS [found: BOOL _ FALSE, contact: ROPE _ NIL, primaryServer: ROPE _ NIL, source: Arpa.Address _ Arpa.nullAddress] = { entry: ArpaNameCache.SoaEntry; expired: BOOL; [found, expired, entry] _ ArpaNameCache.FetchSoa[name, acceptOld]; IF entry # NIL THEN source _ entry.source; IF expired AND ~ArpaNameCache.FetchDownName[name, FALSE].found THEN TRUSTED {Process.Detach[FORK DoQuery[name, soa, resolv, source]]};-- attempt refresh IF found THEN { contact _ entry.soaRef.domainContact; primaryServer _ entry.soaRef.primaryServer; IF ~Rope.IsEmpty[contact] THEN { IF NoAt[contact] THEN { head, tail: ROPE; [head, tail] _ StripHead[contact]; contact _ Rope.Cat[head, "@", tail]; }; RETURN[found, contact, primaryServer, source] }; }; RETURN[FALSE, NIL, NIL, source]; }; AliasToNameLookup: PROC [alias: ROPE, resolv: BOOL _ FALSE, acceptOld: BOOL _ TRUE] RETURNS [found: BOOL _ FALSE, name: ROPE _ NIL, source: Arpa.Address _ Arpa.nullAddress] = { expired: BOOL; entry: ArpaNameCache.AliasEntry; [found, expired, entry] _ ArpaNameCache.FetchAlias[alias, acceptOld]; IF entry # NIL THEN source _ entry.source; IF expired AND ~ArpaNameCache.FetchDownName[name, FALSE].found THEN TRUSTED {Process.Detach[FORK DoQuery[name, cName, resolv, source]]};-- attempt refresh IF found THEN { name _ entry.canonicalName; IF name # NIL THEN RETURN[found, name, source]}; RETURN[FALSE, NIL, source]; }; StripHead: PROC[inRope: ROPE] RETURNS [head: Rope.ROPE_NIL, tail: ROPE_NIL] = { dot, length: INT _ 0; dot _ Rope.Find[inRope, ".",, FALSE]; IF dot <=0 THEN RETURN[NIL]; length _ Rope.Length[inRope]; tail _ Rope.Substr[inRope, dot+1, length-dot-1]; head _ Rope.Substr[inRope, 0, dot]; }; NoDots: PROC[name: ROPE] RETURNS[BOOL] = {RETURN[Rope.Find[name, ".",, FALSE] = -1]}; NoAt: PROC[name: ROPE] RETURNS[BOOL] = {RETURN[Rope.Find[name, "@",, FALSE] = -1]}; DefaultDomain: PROC[name: ROPE] RETURNS[new: ROPE] = { IF NoDots[name] THEN new _ Rope.Cat[name, ".", GetDefaultDomain[]] ELSE new _ name;}; Log: PROC [rope: ROPE] ~ { IF log # NIL THEN { IO.PutRope[log, rope]; IO.Flush[log]}; }; log: IO.STREAM _ NIL; DestroyProc: ViewerEvents.EventProc = { <<[viewer: ViewerClasses.Viewer, event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS [abort: BOOL _ FALSE]>> log _ NIL; }; StartWatcher: Commander.CommandProc = { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> out: IO.STREAM; viewer: ViewerIO.Viewer; IF log #NIL THEN RETURN[msg: "ArpaNameWatcher already started."]; out _ ViewerIO.CreateViewerStreams [ name: "ArpaNameWatcher.log", backingFile: "ArpaNameWatcher.log", editedStream: FALSE].out; viewer _ ViewerIO.GetViewerFromStream[out]; [] _ ViewerEvents.RegisterEventProc[proc: DestroyProc, event: destroy, filter: viewer]; log _ out; }; Commander.Register["ArpaNameWatcher", StartWatcher, "Start up a typescript to watch ArpaName operations."]; END.