IPNameImpl:
CEDAR
MONITOR
IMPORTS
Ascii, BasicTime, IO, Rope, RopeList, IPConfig, IPNameCache, IPNameSupport, IPNameUdp, IPRouter, UDP
EXPORTS IPName =
BEGIN
ROPE: TYPE = Rope.ROPE;
STREAM:
TYPE =
IO.
STREAM;
sequenceNumber: CARDINAL ← 0;
-- Default Time To Lives (end in 777 so it's obvious where these come from)
ttlDefault: INT = 3777;
mxTTL: INT = 77777;
bogusTTL: INT = 77777;
downTTL: INT = 1777; -- Less than half hour (less than time for sick host retry)
AliasEntry: TYPE = IPNameCache.AliasEntry;
NameEntry: TYPE = IPNameCache.NameEntry;
MXEntry: TYPE = IPNameCache.MXEntry;
BogusNameEntry: TYPE = IPNameCache.BogusNameEntry;
DownNameEntry: TYPE = IPNameCache.DownNameEntry;
AddressEntry: TYPE = IPNameCache.AddressEntry;
BogusAddressEntry: TYPE = IPNameCache.BogusAddressEntry;
DownAddressEntry: TYPE = IPNameCache.DownAddressEntry;
ZoneEntry:
TYPE = IPNameCache.ZoneEntry;
rootServerAddresses: LIST OF IPDefs.Address ← IPConfig.rootServerAddresses;
rootServers: LIST OF Rope.ROPE ← IPConfig.rootServers;
LoadCacheFromName:
PUBLIC
PROC [name: Rope.
ROPE, takeOld, needAddress:
BOOL ←
FALSE]
RETURNS [state: IPName.NameState] = {
takeOld TRUE will avoid cache refresh
needAddress TRUE causes extended lookup, all addresses loaded
entry, initEntry: NameEntry;
alias, bogus, down, mx, aliasmx, gotAddress: BOOL ← FALSE;
IF Rope.Length[name] > 0 AND Rope.Fetch[name, 0] = '[ THEN RETURN[nameOk];
IF Rope.Find[name, "."] = -1 THEN name ← Rope.Concat[name, ".ARPA"];
[initEntry, bogus, down, mx, alias] ← FetchNameEntry[name, takeOld];
IF bogus THEN RETURN[bogus];
IF ~needAddress
THEN {
SELECT
TRUE
FROM
alias => RETURN[aliasOk];
mx => RETURN[mxOk];
initEntry #
NIL => {
initEntry.nameCacheLoad ← initEntry.nameCacheLoad.SUCC;
RETURN[nameOk]};
ENDCASE;
}
ELSE {
SELECT
TRUE
FROM
alias
=> {
aliasEntry: AliasEntry ← IPNameCache.AliasLookup[name];
[entry, bogus, down, aliasmx, alias] ← FetchNameEntry[aliasEntry.name, takeOld];
IF bogus THEN RETURN[bogus];
IF down THEN RETURN[down];
IF entry #
NIL
THEN {
entry.nameCacheLoad ← entry.nameCacheLoad.SUCC;
gotAddress ← TRUE};
IF aliasmx
THEN {
mxEntry: MXEntry ← IPNameCache.MXLookup[aliasEntry.name];
FOR list:
LIST
OF IPNameCache.MXHostInfo ← mxEntry.mxHosts, list.rest
UNTIL list =
NIL
DO
mxName: Rope.ROPE ← list.first.host;
[entry, bogus, down, aliasmx, alias] ← FetchNameEntry[mxName, takeOld];
IF entry #
NIL
THEN {
entry.nameCacheLoad ← entry.nameCacheLoad.SUCC;
gotAddress ← TRUE };
ENDLOOP;
};
IF gotAddress THEN RETURN[aliasOk];
};
mx
=> {
mxEntry: MXEntry ← IPNameCache.MXLookup[name];
FOR list:
LIST
OF IPNameCache.MXHostInfo ← mxEntry.mxHosts, list.rest
UNTIL list =
NIL
DO
mxName: Rope.ROPE ← list.first.host;
[entry, bogus, down, mx, alias] ← FetchNameEntry[mxName, takeOld];
IF entry #
NIL
THEN {
entry.nameCacheLoad ← entry.nameCacheLoad.SUCC;
gotAddress ← TRUE };
ENDLOOP;
IF gotAddress THEN RETURN[mxOk];
};
initEntry #
NIL => {
initEntry.nameCacheLoad ← initEntry.nameCacheLoad.SUCC;
RETURN[nameOk]};
ENDCASE;
};
RETURN[down];
};
Return a sorted list of addresses for the name or NIL if the name is not found. Case is ignored. name is allowed to be an Internet address constant of the form "[a.b.c.d]".
NameToAddress:
PUBLIC
PROC [name: Rope.
ROPE, mail:
BOOL ←
FALSE]
RETURNS [addresses: LIST OF IPDefs.Address← NIL] = {
entry, mxEntry: NameEntry ← NIL;
bogus: BOOL ← FALSE;
alias, down, mx, aliasmx: BOOL;
length: INT ← Rope.Length[name];
IF length > 2
AND Rope.Fetch[name, 0] = '[
THEN {
-- It's an address already
address: IPDefs.Address;
IF Tailed[name, ".ARPA"] THEN name ← StripTail[name, ".ARPA"];
address ← ParseAddress[IO.RIS[name] !AddressError => {bogus ← TRUE; CONTINUE};];
IF bogus THEN RETURN[NIL] ELSE RETURN [LIST[address]]; };
IF Rope.Find[name, "."] = -1
THEN name ← Rope.Concat[name, ".ARPA"];
[entry, bogus, down, mx, alias] ← FetchNameEntry[name, FALSE];
IF bogus THEN RETURN[NIL];
IF entry #
NIL
AND ~mail
THEN {
addresses ← AppendAddressList[addresses, entry.addresses];
entry.nameToAddress ← entry.nameToAddress.SUCC;
RETURN[addresses];
};
IF alias
THEN {
aliasEntry: AliasEntry ← IPNameCache.AliasLookup[name];
[entry, , , aliasmx, ] ← FetchNameEntry[aliasEntry.name, FALSE];
IF entry #
NIL
AND ~mail
THEN {
addresses ← AppendAddressList[addresses, entry.addresses];
entry.nameToAddress ← entry.nameToAddress.SUCC;
RETURN[addresses];
};
IF aliasmx
AND mail
THEN {
mxHosts: LIST OF Rope.ROPE ← NameToMXHostList[aliasEntry.name];
FOR list:
LIST
OF Rope.
ROPE ← mxHosts, list.rest
UNTIL list =
NIL
DO
mxName: Rope.ROPE ← list.first;
[mxEntry, , , , ] ← FetchNameEntry[mxName, FALSE];
IF mxEntry #
NIL
THEN {
addresses ← AppendAddressList[addresses, mxEntry.addresses];
mxEntry.nameToAddress ← mxEntry.nameToAddress.SUCC;
};
ENDLOOP;
};
IF mx
AND mail
THEN {
mxHosts: LIST OF Rope.ROPE ← NameToMXHostList[name];
FOR list:
LIST
OF Rope.
ROPE ← mxHosts, list.rest
UNTIL list =
NIL
DO
mxName: Rope.ROPE ← list.first;
[mxEntry, , , , ] ← FetchNameEntry[mxName, FALSE];
IF mxEntry #
NIL
THEN {
addresses ← AppendAddressList[addresses, mxEntry.addresses];
mxEntry.nameToAddress ← mxEntry.nameToAddress.SUCC;
};
ENDLOOP;
};
IF entry #
NIL
THEN {
addresses ← AppendAddressList[addresses, entry.addresses];
entry.nameToAddress ← entry.nameToAddress.SUCC;
};
RETURN[addresses]; };
AppendAddressList:
PROC [oldList:
LIST
OF IPDefs.Address, addList:
LIST
OF IPDefs.Address]
RETURNS[newList:
LIST
OF IPDefs.Address] = {
orderedAddList: LIST OF IPDefs.Address ← IPRouter.SortAddresses[addList]; -- best address first
newList ← oldList;
FOR list:
LIST
OF IPDefs.Address ← orderedAddList, list.rest
UNTIL list =
NIL
DO
address: IPDefs.Address ← list.first;
newList ← IPNameSupport.MakeAddressInList[newList, address];
ENDLOOP;
};
Return sorted list of mail forwarding hosts if MX entry exists. NIL otherwise.
NameToMXHostList:
PUBLIC
PROC [name: Rope.
ROPE]
RETURNS [hosts: LIST OF Rope.ROPE] = {
entry: MXEntry;
entry ← IPNameCache.MXLookup[name];
IF entry = NIL THEN RETURN[NIL];
RETURN[SortMXHosts[entry]]; };
This code should really check for self (Xerox.com) in the mx list and do the right thing ... (see rfc974) For now, I'm assuming only one mail path into Xerox.
SortMXHosts:
PROC [entry: MXEntry]
RETURNS [sorted: LIST OF Rope.ROPE ← NIL] = {
bestPreference: INT ← INT.LAST; -- least is best
prevBestPreference: INT ← INT.LAST;
bestHost: ROPE ← NIL;
IF entry = NIL THEN RETURN[NIL];
FOR list:
LIST
OF IPNameCache.MXHostInfo ← entry.mxHosts, list.rest
UNTIL list =
NIL
DO
IF list.first.preference < bestPreference
THEN bestPreference ← list.first.preference;
ENDLOOP;
FOR list:
LIST
OF IPNameCache.MXHostInfo ← entry.mxHosts, list.rest
UNTIL list =
NIL
DO
IF list.first.preference = bestPreference
THEN {sorted ← IPNameSupport.MakeRopeInList[sorted, list.first.host]};
ENDLOOP;
DO
prevBestPreference ← bestPreference;
bestPreference ←
INT.
LAST;
FOR list:
LIST
OF IPNameCache.MXHostInfo ← entry.mxHosts, list.rest
UNTIL list =
NIL
DO
IF list.first.preference < bestPreference
AND list.first.preference
> prevBestPreference
THEN {bestPreference ← list.first.preference};
ENDLOOP;
IF bestPreference = INT.LAST THEN EXIT;
FOR list:
LIST
OF IPNameCache.MXHostInfo ← entry.mxHosts, list.rest
UNTIL list =
NIL
DO
IF list.first.preference = bestPreference
THEN {sorted ← IPNameSupport.MakeRopeInList[sorted, list.first.host]};
ENDLOOP;
ENDLOOP;
RETURN[sorted]; };
Returns name of server responsible for data. Returns NIL if not in cache.
Source:
PUBLIC
PROC [name: Rope.
ROPE, type: IPName.ExternalSourceCache]
RETURNS [server: Rope.ROPE ← NIL] = {
SELECT type
FROM
nameCache => {
entry: NameEntry ← IPNameCache.NameLookup[name];
IF entry = NIL THEN RETURN[NIL];
server ← AddressToName[entry.source]};
aliasCache=> {
entry: AliasEntry ← IPNameCache.AliasLookup[name];
IF entry = NIL THEN RETURN[NIL];
server ← AddressToName[entry.source]};
mxCache => {
entry: MXEntry ← IPNameCache.MXLookup[name];
IF entry = NIL THEN RETURN[NIL];
server ← AddressToName[entry.source]};
bogusNameCache => {
entry: BogusNameEntry ← IPNameCache.BogusNameLookup[name];
IF entry = NIL THEN RETURN[NIL];
server ← AddressToName[entry.source]};
addressCache => {
entry: AddressEntry ← IPNameCache.AddressLookup[NameToAddress[name].first];
IF entry = NIL THEN RETURN[NIL];
server ← AddressToName[entry.source]};
bogusAddressCache => {
entry: BogusAddressEntry ← IPNameCache.BogusAddressLookup[NameToAddress[name].first];
IF entry = NIL THEN RETURN[NIL];
server ← AddressToName[entry.source]};
zoneCache => {
entry: ZoneEntry ← IPNameCache.ZoneLookup[name];
IF entry = NIL THEN RETURN[NIL];
server ← AddressToName[entry.source]};
ENDCASE;
};
AddressError: ERROR = CODE;
ParseAddress:
PROC [s:
IO.
STREAM]
RETURNS [a: IPDefs.Address] = {
Get an internet address (optionally preceded by spaces and tabs and surrounded by [ ]) from the input stream. Raises AddressError for illegal syntax.
ENABLE IO.EndOfStream => GOTO EndOfStream;
c: CHAR;
WHILE (c ← IO.GetChar[s])=' OR c='\t DO ENDLOOP;
IF c = '[ THEN c ← IO.GetChar[s];
FOR i:
CARDINAL
IN [0..3]
DO
b: CARDINAL ← 0;
IF ~Ascii.Digit[c] THEN ERROR AddressError;
DO
SELECT c
FROM
IN ['0..'9] => b ← b*10 + (c-'0);
'. => IF i<3 THEN {c ← IO.GetChar[s]; EXIT} ELSE ERROR AddressError;
'] => IF i=3 THEN EXIT ELSE ERROR AddressError;
ENDCASE => IF i=3 THEN {IO.Backup[s, c]; EXIT} ELSE ERROR AddressError;
c ← IO.GetChar[s];
ENDLOOP;
IF b > IPDefs.Byte.LAST THEN ERROR AddressError;
a[i] ← b;
ENDLOOP;
EXITS EndOfStream => ERROR AddressError; };
LoadCacheFromAddress:
PUBLIC
PROC [address: IPDefs.Address, takeOld:
BOOL]
RETURNS [state: IPName.AddressState] = {
entry: AddressEntry;
bogus, down: BOOL;
IF address = IPDefs.nullAddress THEN RETURN[bogus];
[entry, bogus, down] ← FetchAddressEntry[address, takeOld];
IF bogus THEN RETURN[bogus];
IF down THEN RETURN[down];
IF entry = NIL THEN RETURN[down];
entry.addressCacheLoad ← entry.addressCacheLoad.SUCC;
RETURN[addressOk]; };
NormalizeName:
PUBLIC
PROC [name:
ROPE]
RETURNS [
ROPE] = {
entry: NameEntry;
bogus, down, mx, alias: BOOL;
FOR list:
LIST
OF Rope.
ROPE ← IPConfig.specialDomains, list.rest
UNTIL list =
NIL
DO
domain: Rope.ROPE ← list.first;
IF DotTailed[name, domain] THEN RETURN[NIL];
ENDLOOP;
IF Rope.Find[name, "."] = -1
THEN name ← Rope.Concat[name, ".ARPA"];
name ← FixupTail[name, ".AG"];
name ← FixupTail[name, ".ArpaGateway"];
name ← FixupTail[name, ".NotArpa"];
name ← FixupTail[name, ".ARPA.ARPA"]; -- Hardy 10.0 always adds .ArpaGateway
IF Rope.Length[name] > 2
AND Rope.Fetch[name, 0] = '[
THEN {
IF Tailed[name, ".ARPA"] THEN name ← StripTail[name, ".ARPA"];
RETURN [name]; };
[entry, bogus, down, mx, alias] ← FetchNameEntry[name, TRUE];
IF down THEN RETURN[name];
IF bogus THEN RETURN[NIL];
IF alias THEN RETURN[IPNameCache.AliasLookup[name].name];
IF entry #
NIL
THEN {
entry.normalizeName ← entry.normalizeName.SUCC;
RETURN[entry.name]; };
IF mx THEN RETURN[name];
RETURN[NIL];
};
AddressToName:
PUBLIC
PROC [address: IPDefs.Address]
RETURNS [name:
ROPE] = {
entry: AddressEntry;
bogus, down: BOOL;
IF address = IPDefs.nullAddress THEN RETURN[NIL];
[entry, bogus, down] ← FetchAddressEntry[address, TRUE];
IF entry = NIL THEN RETURN[AddressToRope[address]];
entry.addressToName ← entry.addressToName.SUCC;
name ← entry.names.first;
FOR list:
LIST
OF
ROPE ← entry.names.rest, list.rest
UNTIL list =
NIL
DO
name ← PickBestName[name, list.first];
ENDLOOP;
RETURN; };
MyAddressToName:
PROC [address: IPDefs.Address]
RETURNS [name:
ROPE] = {
entry: AddressEntry;
IF address = IPDefs.nullAddress THEN RETURN[NIL];
entry ← IPNameCache.AddressLookup[address];
IF entry #
NIL
THEN {
entry.addressToName ← entry.addressToName.SUCC;
name ← entry.names.first;
FOR list:
LIST
OF
ROPE ← entry.names.rest, list.rest
UNTIL list =
NIL
DO
name ← PickBestName[name, list.first];
ENDLOOP; };
RETURN[AddressToRope[address]]; };
AddressToRope:
PUBLIC
PROC [a: IPDefs.Address]
RETURNS [result:
ROPE] = {
result ←
IO.PutFR["[%g.%g.%g.%g]",
[cardinal[a[0]]], [cardinal[a[1]]], [cardinal[a[2]]], [cardinal[a[3]]]]; };
FetchNameEntry:
PROC [name:
ROPE, takeOld:
BOOL ←
FALSE]
takeOld Boolean TRUE forces expedited query
RETURNS [entry: NameEntry ← NIL, bogus, down, mx, alias: BOOL← FALSE] = {
DoQuery:
PROC [name:
ROPE]
RETURNS[down, bogus:
BOOL ←
FALSE] = {
reply: BOOL ← FALSE;
-- Do zone and mx queries if necessary to guarantee entries are in cache by the time n𡤊 query happens
[down, bogus] ← LoadZones[name];
IF down OR bogus THEN RETURN;
reply ← Query["MX Query", name, mx];
IF IPNameCache.BogusNameLookup[name] # NIL THEN RETURN[FALSE, TRUE];
IF ~reply
THEN {
[] ← IPNameCache.UpdateDownName[name, downTTL];
down ← TRUE;
RETURN;
};
reply ← Query["Name Query", name, name];
IF ~reply
THEN {
[] ← IPNameCache.UpdateDownName[name, downTTL];
down ← TRUE;
RETURN;
};
};
[entry, bogus, down, alias, mx] ← LookInNameCache[name, takeOld];
IF mx OR alias OR bogus OR down THEN RETURN;
IF entry #
NIL
THEN {
IF entry.authoritative
OR takeOld
THEN RETURN; };
[down, bogus] ← DoQuery[name];
IF down OR bogus THEN RETURN;
[entry, bogus, down, alias, mx] ← LookInNameCache[name, takeOld];
IF mx OR alias OR bogus OR down THEN RETURN;
IF entry #
NIL
THEN {
IF entry.authoritative
OR takeOld
THEN RETURN; };
IF takeOld
THEN {
[] ← IPNameCache.UpdateDownName[name, downTTL];
down ← TRUE;
RETURN;
};
CheckNewServers[name];
[down, bogus] ← DoQuery[name];
IF down OR bogus THEN RETURN;
[entry, bogus, down, alias, mx] ← LookInNameCache[name, takeOld];
IF mx OR alias OR bogus OR down THEN RETURN;
IF entry # NIL THEN {IF entry.authoritative OR takeOld THEN RETURN; };
[] ← IPNameCache.UpdateDownName[name, downTTL];
down ← TRUE;
};
loopStop:
INT ← 10;
-- shouldn't be recursion to this extent
LoadZones:
PROC [name:
ROPE, queryAddress: IPDefs.Address ← IPDefs.nullAddress]
RETURNS[down, bogus:
BOOL←
FALSE]= {
reply: BOOL ← FALSE;
loopCtr: INT ← 0;
childZone: ROPE ← NIL;
lastChildZone: ROPE ← NIL;
zone: ZoneEntry ← BestZone[name];
IF zone =
NIL
THEN {
topZone: ROPE ← TopLevelZone[name];
reply ← Query["Zone Query", topZone, newZone, queryAddress];
{bogusEntry: BogusNameEntry ← IPNameCache.BogusNameLookup[topZone];
IF bogusEntry #
NIL
THEN {
InsertBogusName[bogusEntry.source, bogusTTL, bogusEntry.authoritative, name];
RETURN[FALSE, TRUE]};
};
IF ~reply
OR BestZone[name] =
NIL
THEN {
[] ← IPNameCache.UpdateDownName[name, downTTL];
down ← TRUE;
RETURN};
};
DO
zone ← BestZone[name];
reply ← FALSE;
IF ~zone.authoritative
THEN {
reply ← Query["Zone Query", zone.zone, newZone, queryAddress];
IF ~reply
THEN {
[] ← IPNameCache.UpdateDownName[name, downTTL];
down ← TRUE;
RETURN};
};
IF Tailed[name, "IN-ADDR.ARPA"] THEN childZone ← name
ELSE {
IF Rope.Equal[zone.zone, name, FALSE] THEN EXIT;
IF Rope.Equal[lastChildZone, name, FALSE] THEN EXIT;
childZone ← ChildZone[name, zone.zone];
IF childZone = NIL THEN EXIT;
IF Rope.Equal[childZone, zone.zone, FALSE] THEN EXIT;
IF Rope.Equal[childZone, lastChildZone, FALSE] THEN EXIT;
IF Tailed[childZone, "ARPA"] THEN EXIT;
};
reply ← Query["Zone Query", childZone, newZone, queryAddress];
{bogusEntry: BogusNameEntry ← IPNameCache.BogusNameLookup[childZone];
IF bogusEntry #
NIL
THEN {
InsertBogusName[bogusEntry.source, bogusTTL, bogusEntry.authoritative, name];
RETURN[FALSE, TRUE]};
};
IF ~reply
THEN {
[] ← IPNameCache.UpdateDownName[name, downTTL];
down ← TRUE;
RETURN};
lastChildZone ← childZone;
IF SameZone[BestZone[name], zone] THEN EXIT;
IF loopCtr = loopStop THEN EXIT; -- beware of bogus reponses causing infinite loop
loopCtr ← loopCtr +1;
ENDLOOP;
};
SameZone:
PROC[zone1, zone2: ZoneEntry]
RETURNS [same:
BOOL ←
TRUE] = {
IF zone1 = NIL AND zone2 = NIL THEN RETURN[TRUE];
IF zone1 = NIL OR zone2 = NIL THEN RETURN[FALSE];
IF zone1.source # zone2.source THEN RETURN[FALSE];
IF zone1.authoritative # zone2.authoritative THEN RETURN[FALSE];
IF ~Rope.Equal[zone1.zone, zone2.zone, FALSE] THEN RETURN[FALSE];
IF ~RopeList.EqualLists[zone1.servers, zone2.servers, FALSE] THEN RETURN[FALSE];
RETURN[TRUE];
};
LookInNameCache:
PROC [name:
ROPE, takeOld:
BOOL]
RETURNS [entry: NameEntry← NIL, bogus: BOOL← FALSE, down: BOOL← FALSE, alias: BOOL← FALSE, mx: BOOL← FALSE] = {
[NIL, FALSE, FALSE, FALSE, FALSE] => Don't know
bogusEntry: BogusNameEntry;
aliasEntry: AliasEntry;
downEntry: DownNameEntry;
mxEntry: MXEntry;
entry ← IPNameCache.NameLookup[name];
IF entry #
NIL
THEN {
IF ~takeOld THEN entry ← MaybeRejuvinateName[entry];
};
mxEntry ← IPNameCache.MXLookup[name];
IF mxEntry #
NIL
THEN {
IF ~takeOld THEN mxEntry ← MaybeRejuvinateMX[mxEntry];
IF mxEntry = NIL THEN mx ← FALSE -- Can't rejuvinate an MX record
ELSE {
mx ← TRUE;
mxEntry.nameToMX ← mxEntry.nameToMX.SUCC};
};
aliasEntry ← IPNameCache.AliasLookup[name];
IF aliasEntry #
NIL
THEN {
IF ~takeOld THEN aliasEntry ← MaybeRejuvinateAlias[aliasEntry];
IF aliasEntry = NIL THEN alias ← FALSE -- Can't rejuvinate an Alias
ELSE {
alias ← TRUE;
aliasEntry.aliasToName ← aliasEntry.aliasToName.SUCC};
};
bogusEntry ← IPNameCache.BogusNameLookup[name];
IF bogusEntry #
NIL
THEN {
IF ~takeOld THEN bogusEntry ← MaybeRejuvinateBogusName[bogusEntry];
IF bogusEntry = NIL THEN bogus ← FALSE -- Can't rejuvinate a Bogus name
ELSE {
bogus ← TRUE;
bogusEntry.nameToBogus ← bogusEntry.nameToBogus.SUCC};
};
downEntry ← IPNameCache.DownNameLookup[name];
IF downEntry #
NIL
THEN {
IF ~takeOld THEN downEntry ← MaybeRejuvinateDownName[downEntry];
IF downEntry = NIL THEN down ← FALSE -- time to check name
ELSE {
down ← TRUE;
downEntry.nameToDown ← downEntry.nameToDown.SUCC};
};
};
AddressToQueryRope:
PROC [address: IPDefs.Address]
RETURNS[rope:
ROPE] = {
rope ← IO.PutFR["%G.%G.%G.%G.IN-ADDR.ARPA", [integer[address[3]]], [integer[address[2]]], [integer[address[1]]], [integer[address[0]]]];
};
FetchAddressEntry:
PROC [address: IPDefs.Address, takeOld:
BOOL]
RETURNS [entry: AddressEntry←NIL, bogus: BOOL ← FALSE, down: BOOL ← FALSE] = {
[NIL, FALSE, FALSE] => Don't know
servers, newServers: LIST OF IPDefs.Address ← NIL;
reply: BOOL ← FALSE;
addressRope: ROPE ← AddressToQueryRope[address];
entry ← IPNameCache.AddressLookup[address];
IF entry =
NIL
THEN {
bogusEntry: BogusAddressEntry ← IPNameCache.BogusAddressLookup[address];
IF bogusEntry #
NIL
THEN {
IF ~takeOld
THEN {
bogusEntry ← MaybeRejuvinateBogusAddress[bogusEntry];
IF bogusEntry = NIL THEN RETURN[NIL, FALSE, FALSE]; }; }; -- Can't rejuvinate a Bogus Address
IF bogusEntry #
NIL
THEN {
bogusEntry.addressToBogus ← bogusEntry.addressToBogus.SUCC;
RETURN[NIL, TRUE, FALSE]; }; }; -- We are sure this address is bogus
IF entry #
NIL
THEN {
IF ~takeOld
THEN {
entry ← MaybeRejuvinateAddress[entry];
IF entry = NIL THEN RETURN[NIL, FALSE, FALSE]; }; }; -- Can't rejuvinate an Address
IF entry # NIL THEN RETURN; -- We are sure this address is valid
IF entry =
NIL
THEN {
downEntry: DownAddressEntry ← IPNameCache.DownAddressLookup[address];
IF downEntry # NIL THEN downEntry ← MaybeRejuvinateDownAddress[downEntry];
IF downEntry = NIL THEN down ← FALSE; -- time to check name
IF downEntry #
NIL
THEN {
downEntry.addressToDown ← downEntry.addressToDown.SUCC;
RETURN[NIL, FALSE, TRUE]; }; };
[down, bogus] ← LoadZones[addressRope, address];
IF bogus THEN RETURN;
IF down
THEN {
[] ← IPNameCache.UpdateDownAddress[address, downTTL];
RETURN; };
reply ← Query["Address Query", addressRope, address, address];
IF ~reply
THEN {
[] ← IPNameCache.UpdateDownAddress[address, downTTL];
down ← TRUE;
RETURN;
};
entry ← IPNameCache.AddressLookup[address];
-- We are sure this address is valid
FixupTail:
PROC [old, tail:
ROPE]
RETURNS [new:
ROPE] = {
new ← old;
IF Tailed[old, tail]
THEN {
new ← StripTail[old, tail];
new ← Rope.Concat[new, ".ARPA"]; }; };
DotTailed:
PROC [body, tail:
ROPE]
RETURNS [match:
BOOL] = {
IF ~Tailed[body, tail] THEN RETURN[FALSE];
IF Rope.Fetch[body, Rope.Length[body]-Rope.Length[tail]-1] # '. THEN RETURN[FALSE];
RETURN[TRUE]; };
Tailed:
PROC [body, tail:
ROPE]
RETURNS [match:
BOOL] = {
bodyLength: INT = Rope.Length[body];
tailLength: INT = Rope.Length[tail];
back: ROPE;
IF bodyLength <= tailLength THEN RETURN[FALSE];
back ← Rope.Substr[body, bodyLength-tailLength, tailLength];
IF ~Rope.Equal[back, tail, FALSE] THEN RETURN[FALSE];
RETURN[TRUE]; };
StripTail:
PROC [body, tail:
ROPE]
RETURNS [new:
ROPE] = {
bodyLength: INT = body.Length[];
tailLength: INT = tail.Length[];
RETURN[Rope.Substr[body, 0, bodyLength - tailLength]]};
PickBestName:
PUBLIC
PROC [a, b:
ROPE]
RETURNS [
ROPE] = {
mumble.COM is better than mumble.ARPA
mumble.ARPA is better than mumble-GW.ARPA
mumble.ARPA is better than mumble-GATEWAY.ARPA (MARYLAND)
mumble.ARPA is better than mumble.jumble.ARPA (F.ISI.ARPA)
IF a = NIL THEN RETURN[b];
IF b = NIL THEN RETURN[a];
IF ~Tailed[a, ".ARPA"] THEN RETURN[a];
IF ~Tailed[b, ".ARPA"] THEN RETURN[b];
IF Gateway[a] THEN RETURN[b];
IF Gateway[b] THEN RETURN[a];
IF Dots[a] < Dots[b] THEN RETURN[a];
RETURN[b]; };
Gateway:
PROC [name:
ROPE]
RETURNS [match:
BOOL] = {
IF Tailed[name, "-GW.ARPA"] THEN RETURN[TRUE];
IF Tailed[name, "-GATEWAY.ARPA"] THEN RETURN[TRUE];
RETURN[FALSE]; };
Dots:
PROC [name:
ROPE]
RETURNS [dots:
INT ← 0] = {
FOR i:
INT
IN [0..Rope.Length[name])
DO
IF Rope.Fetch[name, i] = '. THEN dots ← dots+1;
ENDLOOP; };
Class: TYPE = IPNameUdp.Class;
DomainHeader: TYPE = IPNameUdp.DomainHeader;
Type:
TYPE = IPNameUdp.Type;
FindServers:
PROCEDURE [name:
ROPE]
RETURNS [answers:
LIST
OF IPDefs.Address ←
NIL] = {
bestZone: ZoneEntry ← BestZone[name];
nameEntry: NameEntry ← IPNameCache.NameLookup[name];
serverList: LIST OF ROPE ← NIL;
Brew up the list of addresses to try:
First pass: use best address from each server
Second pass: try back doors
Don't use down server addresses
IF nameEntry # NIL THEN
IF nameEntry.authoritative = TRUE THEN
answers ← IPNameSupport.MakeAddressInList[answers, nameEntry.source];
IF bestZone = NIL THEN serverList ← rootServers
ELSE serverList ← bestZone.servers;
FOR list:
LIST
OF
ROPE ← serverList, list.rest
UNTIL list =
NIL
DO
serverName: ROPE ← list.first;
server: NameEntry ← IPNameCache.NameLookup[serverName]; -- Beware of recursion
best: IPDefs.Address;
IF server = NIL THEN LOOP;
IF server.addresses = NIL THEN LOOP;
best ← IPRouter.BestAddress[server.addresses];
IF IPNameCache.DownServerLookup[best] # NIL THEN LOOP;
answers ← IPNameSupport.MakeAddressInList[answers, best];
ENDLOOP;
FOR list:
LIST
OF
ROPE ← serverList, list.rest
UNTIL list =
NIL
DO
serverName: ROPE ← list.first;
server: NameEntry ← IPNameCache.NameLookup[serverName]; -- Beware of recursion
IF server = NIL THEN LOOP;
FOR list:
LIST
OF IPDefs.Address ← server.addresses, list.rest
UNTIL list =
NIL
DO
IF IPNameCache.DownServerLookup[list.first] # NIL THEN LOOP;
answers ← IPNameSupport.MakeAddressInList[answers, list.first];
ENDLOOP;
ENDLOOP;
IF answers # NIL THEN RETURN[answers];
IF bestZone # NIL THEN RETURN[NIL]; -- All domain servers down
Root server addresses not loaded yet (startup)
FOR list:
LIST
OF IPDefs.Address ← rootServerAddresses, list.rest
UNTIL list =
NIL
DO
IF IPNameCache.DownServerLookup[list.first] # NIL THEN LOOP;
answers ← IPNameSupport.MakeAddressInList[answers, list.first];
ENDLOOP;
RETURN[answers]; };
BestZone:
PROCEDURE [name:
ROPE]
RETURNS[bestZone: ZoneEntry←
NIL] = {
zones: LIST OF ZoneEntry = IPNameCache.GetZones[];
bestZoneName: ROPE ← NIL;
bestZoneLength: INT ← 0;
FOR list:
LIST
OF ZoneEntry ← zones, list.rest
UNTIL list =
NIL
DO
zone: ZoneEntry ← list.first;
zoneName: ROPE ← zone.zone;
zoneLength: INT ← Rope.Length[zoneName];
IF zoneLength < bestZoneLength THEN LOOP;
IF Rope.Equal[name, zoneName,
FALSE]
OR DotTailed[name, zoneName]
THEN {
bestZone ← zone;
bestZoneLength ← zoneLength; };
ENDLOOP;
CheckNewServers:
PROCEDURE [name:
ROPE] = {
zone: ZoneEntry ← IPNameCache.GetZone[name];
IF zone =
NIL
THEN
RETURN;
FOR list:
LIST
OF
ROPE ← zone.servers, list.rest
UNTIL list =
NIL
DO
serverName: ROPE ← list.first;
entry: NameEntry ← IPNameCache.NameLookup[serverName];
IF entry # NIL AND entry.authoritative THEN LOOP;
[] ← Query["New Server", serverName, newServer];
ENDLOOP;
IF zone.authoritative THEN RETURN;
[] ← Query["New Zone", zone.zone, newZone];
};
rejuvinationThreshold: INT = -60;
MaybeRejuvinateName:
PROCEDURE [entry: NameEntry]
RETURNS [NameEntry] = {
now: BasicTime.GMT ← BasicTime.Now[];
ttl: INT ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN { IPNameCache.AddToCacheHits[entry.source, name]; RETURN[entry]; };
{bestZone: ZoneEntry ← BestZone[entry.name];
IF bestZone # NIL THEN [] ← MaybeRejuvinateZone[bestZone];};
[] ← Query["Name Rejuvenation", entry.name, nameRejuvination];
ttl ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold
THEN {
entry.rejuvinations ← entry.rejuvinations.SUCC;
RETURN[entry]; };
entry.expires ← BasicTime.Update[now, ttlDefault]; -- Don't thrash
RETURN[NIL]; };
MaybeRejuvinateAlias:
PROCEDURE [entry: AliasEntry]
RETURNS [AliasEntry] = {
now: BasicTime.GMT ← BasicTime.Now[];
ttl: INT ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN { IPNameCache.AddToCacheHits[entry.source, alias]; RETURN[entry]; };
[] ← Query["Alias Rejuvenation", entry.alias, aliasRejuvination];
ttl ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN { entry.rejuvinations ← entry.rejuvinations.SUCC; RETURN[entry]; };
entry.expires ← BasicTime.Update[now, ttlDefault]; -- Don't thrash
RETURN[NIL]; };
MaybeRejuvinateMX:
PROCEDURE [entry: MXEntry]
RETURNS [MXEntry] = {
now: BasicTime.GMT ← BasicTime.Now[];
ttl: INT ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN { IPNameCache.AddToCacheHits[entry.source, mx]; RETURN[entry]; };
[] ← Query["MX Rejuvenation", entry.name, mxRejuvination];
ttl ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN { entry.rejuvinations ← entry.rejuvinations.SUCC; RETURN[entry]; };
entry.expires ← BasicTime.Update[now, ttlDefault]; -- Don't thrash
RETURN[NIL]; };
MaybeRejuvinateBogusName:
PROCEDURE [entry: BogusNameEntry]
RETURNS [BogusNameEntry] = {
now: BasicTime.GMT ← BasicTime.Now[];
ttl: INT ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN { IPNameCache.AddToCacheHits[entry.source, bogusName]; RETURN[entry]; };
[] ← Query["Bogus Name Rejuvenation", entry.bogus, bogusNameRejuvination];
ttl ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN { entry.rejuvinations ← entry.rejuvinations.SUCC; RETURN[entry]; };
entry.expires ← BasicTime.Update[now, ttlDefault]; -- Don't thrash
RETURN[NIL]; };
MaybeRejuvinateDownName:
PROCEDURE [entry: DownNameEntry]
RETURNS [DownNameEntry] = {
now: BasicTime.GMT ← BasicTime.Now[];
ttl: INT ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN RETURN[entry];
RETURN[NIL]; }; -- Time to check name again
MaybeRejuvinateAddress:
PROCEDURE [entry: AddressEntry]
RETURNS [AddressEntry] = {
now: BasicTime.GMT ← BasicTime.Now[];
ttl: INT ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN { IPNameCache.AddToCacheHits[entry.source, address]; RETURN[entry]; };
[] ← Query["Address Rejuvenation", AddressToQueryRope[entry.address], addressRejuvination, entry.address];
ttl ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN { entry.rejuvinations ← entry.rejuvinations.SUCC; RETURN[entry]; };
entry.expires ← BasicTime.Update[now, ttlDefault]; -- Don't thrash
RETURN[NIL]; };
MaybeRejuvinateBogusAddress:
PROCEDURE [entry: BogusAddressEntry]
RETURNS [BogusAddressEntry] = {
now: BasicTime.GMT ← BasicTime.Now[];
ttl: INT ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN { IPNameCache.AddToCacheHits[entry.source, bogusAddress]; RETURN[entry]; };
[] ← Query["Bogus Address Rejuvenation", AddressToQueryRope[entry.bogus], bogusAddressRejuvination, entry.bogus];
ttl ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN { entry.rejuvinations ← entry.rejuvinations.SUCC; RETURN[entry]; };
entry.expires ← BasicTime.Update[now, ttlDefault]; -- Don't thrash
RETURN[NIL]; };
MaybeRejuvinateDownAddress:
PROCEDURE [entry: DownAddressEntry]
RETURNS [DownAddressEntry] = {
now: BasicTime.GMT ← BasicTime.Now[];
ttl: INT ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN RETURN[entry];
RETURN[NIL]; };
MaybeRejuvinateZone:
PROCEDURE [entry: ZoneEntry]
RETURNS [ZoneEntry] = {
now: BasicTime.GMT ← BasicTime.Now[];
ttl: INT ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold AND entry.authoritative THEN {IPNameCache.AddToCacheHits[entry.source, zone]; RETURN[entry]; };
[] ← Query["Zone Rejuvenation", entry.zone, zoneRejuvination];
ttl ← BasicTime.Period[from: now, to: entry.expires];
IF ttl > rejuvinationThreshold THEN { entry.rejuvinations ← entry.rejuvinations.SUCC; RETURN[entry]; };
entry.expires ← BasicTime.Update[now, ttlDefault]; -- Don't thrash
RETURN[NIL]; };
nQueries: CARDINAL ← 3; -- Number of query tries with timeouts below
baseTimeout: CARDINAL ← 5; -- 5, 10, 15 seconds
Query:
PROCEDURE [
what: ROPE, name: ROPE, type: IPNameCache.ProbeType, queryAddress: IPDefs.Address ← IPDefs.nullAddress] RETURNS[done: BOOL ← FALSE] = TRUSTED {
packetStart, packetStop: BasicTime.Pulses;
milliseconds: LONG CARDINAL;
id: CARDINAL ← (sequenceNumber ← sequenceNumber.SUCC);
servers: LIST OF IPDefs.Address ← FindServers[name];
FOR list:
LIST
OF IPDefs.Address ← servers, list.rest
UNTIL done
OR list =
NIL
DO
server: IPDefs.Address ← list.first;
handle: UDP.Handle;
handle ← UDP.Create[him: server, remote: UDP.domain, local: UDP.default];
IPNameCache.AddToProbes[server, type];
BEGIN
ENABLE ServerIsDown => {
IPNameSupport.LogRope[IO.PutFR["%T ** ServerDown during %G for %G via %G.\n",
[time[BasicTime.Now[]]], [rope[what]], [rope[name]], [rope[MyAddressToName[server]]]]];
CONTINUE; };
FOR i:
CARDINAL
IN [1..nQueries]
UNTIL done
DO
dg: IPDefs.Datagram ← NEW [IPDefs.DatagramRec];
udp: LONG POINTER TO UDP.BodyRec ← LOOPHOLE[@dg.data];
domain: LONG POINTER TO DomainHeader ← LOOPHOLE[@udp.data];
qType: IPNameUdp.Type ← a;
domain^ ← [id: id];
udp.length ← UDP.minLength + DomainHeader.SIZE*2;
IF type = newZone OR type = zoneRejuvination THEN qType ← ns;
IF type = mx OR type = mxRejuvination THEN qType ← mx;
IF type = address THEN qType ← star;
IPNameUdp.AppendQuery[udp, domain, name, qType, in];
UDP.Send[handle, dg, udp.length];
packetStart ← BasicTime.GetClockPulses[];
UNTIL done
OR (dg ←
UDP.Receive[handle, i*1000*baseTimeout]) =
NIL
DO
packetStop ← BasicTime.GetClockPulses[];
milliseconds ← BasicTime.PulsesToMicroseconds[packetStop-packetStart]/1000;
udp ← LOOPHOLE[@dg.data];
domain ← LOOPHOLE[@udp.data];
IF dg.inHdr.source # server
THEN
IPNameSupport.LogRope[IO.PutFR["Strange source address: expected %G, found %G = %G: \n",
[rope[AddressToRope[server]]],
[rope[AddressToRope[dg.inHdr.source]]],
[rope[MyAddressToName[dg.inHdr.source]]]]];
done ← ProcessReply[udp, domain, dg.inHdr.source, queryAddress];
IPNameCache.AddToCounter[server, milliseconds];
IPNameSupport.LogRope[IO.PutFR["%G for %G took %G ms via %G.\n",
[rope[what]], [rope[name]], [integer[milliseconds]], [rope[MyAddressToName[server]]]]];
dg ← NIL;
ENDLOOP;
IF ~done THEN IPNameCache.AddToLost[server];
ENDLOOP;
IF ~done
THEN {IPNameSupport.LogRope[
IO.PutFR["%T ** No response to %G for %G via %G.\n",
[time[BasicTime.Now[]]], [rope[what]], [rope[name]], [rope[MyAddressToName[server]]]]];
[] ← IPNameCache.UpdateDownServer[server, downTTL]; };
END;
UDP.Destroy[handle];
ENDLOOP;
IF ~done
THEN {
IPNameSupport.LogRope[IO.PutFR["%T ** No response to %G for %G from any server.\n",
[time[BasicTime.Now[]]], [rope[what]], [rope[name]]]];
};
};
ServerIsDown: SIGNAL = CODE;
LikelyValidReply:
PROC [name:
ROPE, source: IPDefs.Address, bogus:
BOOL ←
TRUE]
RETURNS[ok:
BOOL] = {
zone: ZoneEntry;
servers: LIST OF ROPE ← NIL;
addresses: LIST OF IPDefs.Address ← NIL;
IF bogus AND IPNameCache.NameLookup[name] # NIL THEN RETURN[FALSE];
IF bogus AND IPNameCache.MXLookup[name] # NIL THEN RETURN[FALSE];
IF bogus AND IPNameCache.AliasLookup[name] # NIL THEN RETURN[FALSE];
zone ← IPNameCache.ZoneLookup[name];
IF zone = NIL THEN zone ← IPNameCache.ZoneLookup[BestZoneFromName[name]];
IF zone = NIL THEN RETURN[TRUE];
addresses ← ServerListToAddressList[zone.servers];
IF ~IPNameSupport.AddressInList[addresses, source] THEN RETURN[FALSE];
RETURN[TRUE];
};
ServerListToAddressList:
PROC [servers:
LIST
OF
ROPE]
RETURNS[answers:
LIST
OF IPDefs.Address ←
NIL] = {
FOR list:
LIST
OF
ROPE ← servers, list.rest
UNTIL list =
NIL
DO
serverName: ROPE ← list.first;
serverEntry: NameEntry ← IPNameCache.NameLookup[serverName];
IF serverEntry = NIL THEN LOOP;
FOR addresses
: LIST
OF IPDefs.Address ← serverEntry.addresses, addresses.rest
UNTIL addresses =
NIL
DO
answers ← IPNameSupport.MakeAddressInList[answers, addresses.first];
ENDLOOP;
ENDLOOP;
};
BestZoneFromName:
PROC [name:
ROPE]
RETURNS[
ROPE] = {
startPos: INT ← 0;
nameLength: INT;
IF name = NIL THEN RETURN[NIL];
nameLength ← name.Length[];
FOR i:
INT
IN [0..nameLength)
DO
IF Rope.Fetch[name, i] = '. THEN {startPos ← i+1; EXIT;};
ENDLOOP;
RETURN[Rope.Substr[name, startPos, nameLength-startPos]];
};
TopLevelZone:
PROC [name:
ROPE]
RETURNS[
ROPE] = {
startPos: INT ← 0;
nameLength: INT;
IF name = NIL THEN RETURN[NIL];
nameLength ← name.Length[];
FOR i:
INT
DECREASING IN [0..nameLength)
DO
IF Rope.Fetch[name, i] = '. THEN {startPos ← i+1; EXIT;};
ENDLOOP;
RETURN[Rope.Substr[name, startPos, nameLength]];
};
ChildZone:
PROC [name:
ROPE, parentZone:
ROPE← NIL]
RETURNS[
ROPE] = {
startPos: INT ← 0;
nameLength: INT;
parentLength: INT;
IF name = NIL THEN RETURN[NIL];
IF parentZone = NIL THEN RETURN[TopLevelZone[name]];
nameLength ← name.Length[];
parentLength ← parentZone.Length[];
FOR i:
INT
DECREASING
IN [0..nameLength-parentLength-1)
DO
IF Rope.Fetch[name, i] = '. THEN {startPos ← i+1; EXIT;};
ENDLOOP;
RETURN[Rope.Substr[name, startPos, nameLength]];
};
SelfMX:
PROC [mxEntry: MXEntry, name:
ROPE]
RETURNS[yes:
BOOL←
FALSE] = {
IF mxEntry = NIL OR name = NIL THEN RETURN[FALSE];
FOR list:
LIST
OF IPNameCache.MXHostInfo ← mxEntry.mxHosts, list.rest
UNTIL list =
NIL
DO
IF Rope.Equal[list.first.host, name, FALSE] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
ProcessReply:
PROC [
-- Smashes udp.length
udp: LONG POINTER TO UDP.BodyRec,
domain: LONG POINTER TO DomainHeader,
source: IPDefs.Address, queryAddress: IPDefs.Address← IPDefs.nullAddress] RETURNS[done: BOOLEAN←TRUE] = TRUSTED {
ENABLE {UNWIND => NULL; IPNameUdp.EndOfData => GOTO Quit};
qname: ROPE;
qtype: Type;
qclass: Class;
udp.length ← UDP.minLength + DomainHeader.SIZE*2;
IF domain.qr # response THEN RETURN[FALSE];
IF domain.tc THEN RETURN[FALSE];
SELECT domain.rcode
FROM
ok => NULL;
format => RETURN[TRUE];
nameNotFound => {
name: ROPE ← IPNameUdp.GetName[udp];
Servers sometimes hand out bogus nameNotFounds, so must be careful;
IF LikelyValidReply[name, source]
AND domain.aa
THEN
InsertBogusName[source, bogusTTL, domain.aa, name]
ELSE RETURN[FALSE];
RETURN[TRUE]; };
serverFailed, notImplemented, refused => SIGNAL ServerIsDown;
ENDCASE => ERROR;
FOR i:
INT
IN [0..domain.qdCount)
DO
qname ← IPNameUdp.GetName[udp];
qtype ← IPNameUdp.GetTwoBytes[udp];
qclass ← IPNameUdp.GetTwoBytes[udp];
ENDLOOP;
IF domain.anCount = 0
THEN {
Name exists, but no address => mx, or zone, or server is confused !
IF qtype = mx
AND IPNameCache.ZoneLookup[qname] =
NIL
AND LikelyValidReply[qname, source,
FALSE]
THEN {
SELECT
TRUE
FROM
domain.nsCount # 0 => RETURN[FALSE]; -- server is confused ! Try another server.
domain.nsCount = 0 => MergeMXInfo[source, mxTTL, domain.aa, qname, qname, 0];
ENDCASE;
IF qtype = a AND IPNameCache.ZoneLookup[qname] # NIL AND LikelyValidReply[qname, source, TRUE] AND domain.aa THEN {InsertBogusName[source, bogusTTL, domain.aa, qname];
RETURN[TRUE];};
{mxEntry: MXEntry ← IPNameCache.MXLookup[qname];
IF qtype = a AND SelfMX[mxEntry, qname] AND LikelyValidReply[qname, source, FALSE] THEN RETURN[FALSE]; -- server is confused! Try another server.
};
};
FOR i:
INT
IN [0..domain.anCount)
DO
name: ROPE ← IPNameUdp.GetName[udp];
type: Type ← IPNameUdp.GetTwoBytes[udp];
class: Class ← IPNameUdp.GetTwoBytes[udp];
ttl: INT ← IPNameUdp.GetTtl[udp];
rDataLength: CARDINAL ← IPNameUdp.GetTwoBytes[udp];
SELECT
TRUE
FROM
type = a
AND class = in => {
address: IPDefs.Address ← IPNameUdp.GetIPAddress[udp];
MergeNameInfo[source, ttl, domain.aa, name, address]; };
type = cName
AND class = in => {
alias: ROPE ← name;
real: ROPE ← IPNameUdp.GetName[udp];
MergeAliasInfo[source, ttl, domain.aa, real, alias]; };
type = mx
AND class = in => {
preference: INT ← IPNameUdp.GetCardinal[udp];
host: ROPE ← IPNameUdp.GetName[udp];
MergeMXInfo[source, ttl, domain.aa, name, host, preference]};
type = ns
AND class = in => {
zone: ROPE ← name;
server: ROPE ← IPNameUdp.GetName[udp];
IF Rope.Length[zone] # 0
THEN
Root of world is wired into to code - avoid bogus info
MergeServerInfo[source, ttl, domain.aa, zone, server]; };
type = ptr
AND class = in => {
nameForAddress: ROPE ← IPNameUdp.GetName[udp];
MergeAddressInfo[source, ttl, domain.aa, queryAddress, nameForAddress]; };
ENDCASE => udp.length ← udp.length + rDataLength;
ENDLOOP;
FOR i:
INT
IN [0..domain.nsCount)
DO
name: ROPE ← IPNameUdp.GetName[udp];
type: Type ← IPNameUdp.GetTwoBytes[udp];
class: Class ← IPNameUdp.GetTwoBytes[udp];
ttl: INT ← IPNameUdp.GetTtl[udp];
rDataLength: CARDINAL ← IPNameUdp.GetTwoBytes[udp];
SELECT
TRUE
FROM
type = ns
AND class = in => {
zone: ROPE ← name;
server: ROPE ← IPNameUdp.GetName[udp];
IF Rope.Length[zone] # 0
THEN
Root of world is wired into to code - avoid bogus info
MergeServerInfo[source, ttl, FALSE, zone, server]; };
ENDCASE => udp.length ← udp.length + rDataLength;
ENDLOOP;
FOR i:
INT
IN [0..domain.arCount)
DO
name: ROPE ← IPNameUdp.GetName[udp];
type: Type ← IPNameUdp.GetTwoBytes[udp];
class: Class ← IPNameUdp.GetTwoBytes[udp];
ttl: INT ← IPNameUdp.GetTtl[udp];
rDataLength: CARDINAL ← IPNameUdp.GetTwoBytes[udp];
SELECT
TRUE
FROM
type = a
AND class = in => {
address: IPDefs.Address ← IPNameUdp.GetIPAddress[udp];
MergeNameInfo[source, ttl, FALSE, name, address]; };
type = cName
AND class = in => {
alias: ROPE ← name;
real: ROPE ← IPNameUdp.GetName[udp];
MergeAliasInfo[source, ttl, FALSE, real, alias]; };
type = mx
AND class = in => {
preference: INT ← IPNameUdp.GetCardinal[udp];
host: ROPE ← IPNameUdp.GetName[udp];
MergeMXInfo[source, ttl, FALSE, name, host, preference]};
type = ns
AND class = in => {
zone: ROPE ← name;
server: ROPE ← IPNameUdp.GetName[udp];
IF Rope.Length[zone] # 0
THEN
Root of world is wired into to code - avoid bogus info
MergeServerInfo[source, ttl, FALSE, zone, server]; };
ENDCASE => udp.length ← udp.length + rDataLength;
ENDLOOP;
EXITS
Quit => RETURN[FALSE];
};
MergeNameInfo:
PROC [
source: IPDefs.Address, ttl: INT, authoritative: BOOL, name: ROPE, address: IPDefs.Address] = {
entry: NameEntry ← IPNameCache.UpdateName[name, source, ttl, authoritative];
IF entry = NIL THEN RETURN;
IPNameCache.AddAddressToName[entry, address];
IPNameSupport.NameSummaryLine[entry, TRUE]; };
MergeAliasInfo:
PROC [
source: IPDefs.Address, ttl: INT, authoritative: BOOL, name, alias: ROPE] = {
entry: AliasEntry ← IPNameCache.UpdateAlias[alias, source, ttl, authoritative];
IF entry = NIL THEN RETURN;
IPNameCache.AddNameToAlias[entry, name];
IPNameSupport.AliasSummaryLine[entry, TRUE]; };
MergeAddressInfo:
PROC [
source: IPDefs.Address, ttl: INT, authoritative: BOOL, address: IPDefs.Address, name: ROPE] = {
entry: AddressEntry ← IPNameCache.UpdateAddress[address, source, ttl, authoritative];
IF entry = NIL THEN RETURN;
IPNameCache.AddNameToAddress[entry, name];
IPNameSupport.AddressSummaryLine[entry, TRUE]; };
MergeServerInfo:
PROC [
source: IPDefs.Address, ttl: INT, authoritative: BOOL, zone, server: ROPE] = {
now: BasicTime.GMT ← BasicTime.Now[];
entry: ZoneEntry ← IPNameCache.UpdateZone[zone, source, ttl, authoritative];
IF entry = NIL THEN RETURN;
IPNameCache.AddServer[entry, server];
IPNameSupport.ZoneSummaryLine[entry, TRUE]; };
MergeMXInfo:
PROC [
source: IPDefs.Address, ttl: INT, authoritative: BOOL, name, host: ROPE, preference: INT] = {
now: BasicTime.GMT ← BasicTime.Now[];
entry: MXEntry ← IPNameCache.UpdateMX[name, source, ttl, authoritative];
IF entry = NIL THEN RETURN;
IPNameCache.AddHostToMX[entry, [host, preference]];
IPNameSupport.MXSummaryLine[entry, TRUE]; };
ProcessBogusAddress:
PROC [bogus: IPDefs.Address] = {
InsertBogusAddress[IPDefs.nullAddress, bogusTTL, FALSE, bogus]; };
InsertBogusAddress:
PROC [
source: IPDefs.Address, ttl: INT, authoritative: BOOL, bogus: IPDefs.Address] = {
[] ← IPNameCache.UpdateBogusAddress[bogus, source, ttl, authoritative];
NULL; };
ProcessBogusName:
PROC [bogus:
ROPE] = {
InsertBogusName[IPDefs.nullAddress, ttlDefault, FALSE, bogus]; };
InsertBogusName:
PROC [
source: IPDefs.Address, ttl: INT, authoritative: BOOL, bogus: ROPE] = {
[] ← IPNameCache.UpdateBogusName[bogus, source, ttl, authoritative];
NULL; };
END.