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:
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];
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:
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}
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:
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];
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:
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.