ArpaNameImpl.mesa
Copyright © 1987 by Xerox Corporation. All rights reserved.
John Larson, November 1, 1987 7:01:11 pm PST
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;
Bootstrap configuration data
Setable from ArpaName interface
Arpanet root servers:
SRI-NIC.ARPA = [10.0.0.51] [26.0.0.73]
C.ISI.EDU = [10.0.0.52]
A.ISI.EDU = [26.3.0.103]
BRL-AOS.ARPA = [128.20.1.2], [192.5.25.82]
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]];
Local root servers:
parcvax.Xerox.COM = [13.2.16.8] [10.1.0.32]
palain.parc.Xerox.COM =[13.1.100.208]
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: ROPENIL] = {
[name, , ] ← AddressToName[myAddress];
IF name = NIL THEN RETURN[ArpaNameSupport.AddressToRope[myAddress]];
};
NameToAddress: PUBLIC PROC [name: Rope.ROPE, resolv: BOOLFALSE]
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: BOOLFALSE]
RETURNS [addrs: LIST OF Arpa.Address ← NIL, status: ReplyStatus ← down, source: Arpa.Address ← myAddress] = {
found: BOOL;
DoLookups: PROC[name: ROPE, resolv: BOOLFALSE]
RETURNS[found: BOOLFALSE, newName: ROPENIL, addrs: LIST OF Arpa.Address ← NIL, status: ReplyStatus ← down, source: Arpa.Address ← myAddress] = {
cName: ROPENIL;
[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: BOOLFALSE]
RETURNS [hosts: LIST OF ROPENIL, status: ReplyStatus ← down, source: Arpa.Address ← myAddress] = {
found: BOOL;
DoLookups: PROC[name: ROPE, resolv: BOOLFALSE]
RETURNS[found: BOOLFALSE, newName: ROPENIL, hosts: LIST OF ROPENIL, status: ReplyStatus ← down, source: Arpa.Address ← myAddress] = {
cName: ROPENIL;
[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];
Want to avoid bogus mx records being loaded.. see comments in DoQuery
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: BOOLFALSE]
RETURNS [name: Rope.ROPENIL, status: ReplyStatus ← down, source: Arpa.Address ← myAddress] = {
addrRope: ROPE ← ArpaNameSupport.AddressToRope[addr];
found: BOOL;
DoLookups: PROC[rope: ROPE, resolv: BOOLFALSE]
RETURNS[found: BOOLFALSE, name: ROPENIL, 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: BOOLFALSE]
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: BOOLFALSE]
RETURNS [name: Rope.ROPENIL, status: ReplyStatus ← down, source: Arpa.Address ← Arpa.nullAddress] = {
found: BOOL;
DoLookups: PROC[alias: ROPE, resolv: BOOLFALSE]
RETURNS[found: BOOLFALSE, name: ROPENIL, 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: BOOLFALSE]
RETURNS [servers: LIST OF Rope.ROPENIL, status: ReplyStatus ← down, source: Arpa.Address ← myAddress] = {
found: BOOL;
DoLookups: PROC[name: ROPE, resolv: BOOLFALSE]
RETURNS[found: BOOLFALSE, servers: LIST OF ROPENIL, 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: BOOLFALSE]
RETURNS [domainContact: ROPE, primaryServer: ROPE, status: ReplyStatus, source: Arpa.Address ← myAddress] = {
found: BOOL;
DoLookups: PROC[name: ROPE, resolv: BOOLFALSE]
RETURNS[found: BOOLFALSE, 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: BOOLFALSE,
name: ROPENIL,
ttl: ArpaNameQuery.Seconds,
addresses: LIST OF Arpa.Address ← NIL
];
NSRec: TYPE = RECORD[
loaded: BOOLFALSE,
name: ROPENIL,
ttl: ArpaNameQuery.Seconds,
servers: LIST OF ROPENIL
];
AliasRec: TYPE = RECORD[
loaded: BOOLFALSE,
name: ROPENIL,
ttl: ArpaNameQuery.Seconds,
canonicalName: ROPENIL
];
PtrRec: TYPE = RECORD[
loaded: BOOLFALSE,
address: ROPENIL,
ttl: ArpaNameQuery.Seconds,
names: LIST OF ROPENIL
];
SoaRec: TYPE = RECORD[
loaded: BOOLFALSE,
name: ROPENIL,
ttl: ArpaNameQuery.Seconds,
soa: ArpaNameQuery.SourceOfAuthRecord
];
MXRec: TYPE = RECORD[
loaded: BOOLFALSE,
name: ROPENIL,
ttl: ArpaNameQuery.Seconds,
mxList: LIST OF ArpaNameQuery.MailForwardRecord ← NIL
];
DoQuery: PROC[query: ROPE, type: ArpaNameQuery.QType, resolv: BOOLFALSE, 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;
};
Grrr.... lack of support for caching negative responses is one of the problems with the current domain system. We must cache implict MX records to avoid the extra MX query load on external servers, but interpreting ambiguous responses such as below is a problem. Mail clients should always do a Zone query before MX to avoid bogus mx entries being loaded here (which would cause mail accidently sent to a zone to be put in the sick queue rather than being properly rejected).
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: BOOLFALSE, 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];
Find working name server
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;
Try harder to find a working name server. Timeouts are based on an exponential backoff scheme as a function of the number of servers. Max total timeout ~ 30 secs.
1 server: 4, 8, 16 secs
2 servers: 2, 4, 8 secs
3 servers: 1, 2, 5 secs
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: BOOLFALSE, 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: INTINT.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: BOOLFALSE, acceptOld: BOOLTRUE]
RETURNS [found: BOOLFALSE, 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: BOOLFALSE, 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: BOOLFALSE, acceptOld: BOOLTRUE]
RETURNS [found: BOOLFALSE, 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: BOOLFALSE, 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: BOOLFALSE, acceptOld: BOOLTRUE]
RETURNS [found: BOOLFALSE, 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: BOOLFALSE, acceptOld: BOOLTRUE]
RETURNS [found: BOOLFALSE, name: ROPENIL, 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: BOOLFALSE, acceptOld: BOOLTRUE]
RETURNS [found: BOOLFALSE, hosts: LIST OF ROPENIL, 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 ROPENIL] ~ {
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: BOOLFALSE, acceptOld: BOOLTRUE]
RETURNS [found: BOOLFALSE, servers: LIST OF ROPENIL, 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: BOOLFALSE, acceptOld: BOOLTRUE]
RETURNS [found: BOOLFALSE, contact: ROPENIL, primaryServer: ROPENIL, 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: BOOLFALSE, acceptOld: BOOLTRUE]
RETURNS [found: BOOLFALSE, name: ROPENIL, 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.ROPENIL, tail: ROPENIL] = {
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.STREAMNIL;
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 ANYNIL, msg: ROPENIL]
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.