-- File: NameResImpl.mesa - last edit:
-- JAV 26-Aug-87 11:43:20
-- AOF 1-Apr-87 12:39:11
-- Copyright (C) 1987 by Xerox Corporation. All rights reserved.
-- This software was produced by the University of Southern California (USC)
-- Information Sciences Institute (ISI).
-- USC-ISI does not assume any responsibility for the correctness,
-- performance, or use of this sotfware.
-- Distribution of this software is limited by agreement between USC-ISI
-- and the XEROX Corporation.
DIRECTORY
ArpaAddressTranslation USING [
AddressTableEntry, AddressTableLength, AppendINetAddress,
AppendINetAddressAndGrow, AppendProtocol, DebugProc, defaultTimeout,
HostTableEntry, HostTableLength, INetAddr, INetAddress,
MailboxTableEntry, mailboxTableLength, NamesPerEntry,
nullAddressDesc, nullHostDesc, nullINetAddress, nullMailboxDesc,
nullServerDesc, PortType, QClass, QType, QueryHeader, ResponseType,
ServerTableEntry, serverTableLength, ServerType],
ArpaBuffer USING [Body, Buffer, ReturnBuffer],
ArpaPort USING [
AssignPort, Create, Delete, GetPacket, GetSendBuffer, Handle,
PutPacket, SetIPLengths, SetUDPLength, SetWaitTime, Timeout, UDPHeaderBytes],
ArpaPortInternal USING [
AddrMatch, BuildMasks, GetArpaAddr, GetDomainNameServer],
ArpaRouter USING [InternetAddress, Port, unknownInternetAddress],
Environment USING [Byte],
Format USING [Number, StringProc],
Heap USING [systemZone],
Inline USING [BITSHIFT, DBITSHIFT, HighByte, LowByte],
Process USING [Detach],
String USING [
AppendChar, AppendDecimal, AppendString, AppendStringAndGrow,
CopyToNewString, Empty, Equivalent, EquivalentSubString, FreeString,
MakeString, Replace, SubStringDescriptor],
System USING [
AdjustGreenwichMeanTime, GetGreenwichMeanTime, GreenwichMeanTime,
SecondsSinceEpoch];
NameResImpl: MONITOR
IMPORTS ArpaAddressTranslation, ArpaBuffer, ArpaPort, ArpaPortInternal,
ArpaRouter, Format, Heap, Inline, Process, String, System
EXPORTS ArpaAddressTranslation =
BEGIN
OPEN ArpaAddressTranslation;
MaxQuerySize: CARDINAL = 512;
maxTries: CARDINAL = 4;
maxReferrals: CARDINAL = 4;
defaultNameServer: INetAddr ← nullINetAddress;
myINetAddress: ArpaRouter.InternetAddress ← LOOPHOLE[nullINetAddress];
--cache tables
hostTable: PUBLIC ARRAY [0..HostTableLength) OF HostTableEntry ← ALL[nullHostDesc];
serverTable: PUBLIC ARRAY [0..serverTableLength) OF ServerTableEntry ← ALL[nullServerDesc];
addressTable: PUBLIC ARRAY [0..AddressTableLength) OF AddressTableEntry ← ALL[nullAddressDesc];
mailboxTable: PUBLIC ARRAY [0..mailboxTableLength) OF MailboxTableEntry ← ALL[nullMailboxDesc];
NoAnswer: PUBLIC SIGNAL = CODE;
ErrorInReply: PUBLIC SIGNAL = CODE;
requestCount: CARDINAL ← 0; -- used for making up unique id
UDPBuffer: TYPE = LONG POINTER TO UDPBufferObject;
UDPBufferObject: TYPE = MACHINE DEPENDENT RECORD [
source(0:0..15): PortType ← null,
dest(1:0..15): PortType ← null,
length(2:0..15): [0..177777B] ← 0,
checksum(3:0..15): [0..177777B] ← 0,
data(4): PACKED ARRAY [0..0) OF Environment.Byte ← NULL
];
--****************************************************************************--
GetWord: PROCEDURE [data: LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte, index: LONG POINTER TO CARDINAL] RETURNS [i: CARDINAL] =
BEGIN
i ← Inline.BITSHIFT[data[index↑],8] + data[index↑+1];
index↑ ← index↑ + 2;
END; -- GetWord
GetLong: PROCEDURE [data: LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte, index: LONG POINTER TO CARDINAL] RETURNS [i: LONG UNSPECIFIED] =
BEGIN
i ← Inline.DBITSHIFT[data[index↑], 24] + Inline.DBITSHIFT[data[index↑+1],16] + Inline.DBITSHIFT[data[index↑+2],8] + data[index↑+3];
index↑ ← index↑ + 4;
END; -- GetLong
BuildRequest: PROCEDURE [domain: LONG STRING, type: QType, dest: INetAddr, destPort: PortType, b: ArpaBuffer.Buffer] =
BEGIN
-- UDP Data
bufferBody: ArpaBuffer.Body = b.arpa;
{
OPEN ip: bufferBody.ipHeader, udp: bufferBody.user;
hPtr: LONG POINTER TO QueryHeader ← LOOPHOLE[@udp.bytes];
index, countIndex, count: CARDINAL ← 0;
udp.destinationPort ← LOOPHOLE[destPort];
ip.destination ← LOOPHOLE[dest];
ip.protocol ← userDatagram;
ip.service ← LOOPHOLE[0];
ip.identification ← 0;
ip.lifetime ← 15B;
-- make up request
hPtr↑ ← [requestCount, query, query, FALSE, FALSE, FALSE, FALSE, 0, okay, 1, 0, 0, 0];
index ← 2 * SIZE[QueryHeader];
countIndex ← index; -- fill in count later
FOR i:CARDINAL IN [0..domain.length) DO
IF domain.text[i] = '. THEN {
udp.bytes[countIndex] ← count;
countIndex ← index ← index + 1;
count ← 0}
ELSE {
index ← index + 1;
udp.bytes[index] ← LOOPHOLE[domain.text[i]];
count ← count + 1};
ENDLOOP;
IF count # 0 THEN {
udp.bytes[countIndex] ← count;
index ← index + 1;
udp.bytes[index] ← 0;
index ← index + 1}
ELSE
IF udp.bytes[index-1] # 0 THEN index ← index + 1;
udp.bytes[index] ← Inline.HighByte[type];
udp.bytes[index+1] ← Inline.LowByte[type];
udp.bytes[index+2] ← Inline.HighByte[QClass.internet];
udp.bytes[index+3] ← Inline.LowByte[QClass.internet];
index ← index + 4;
ArpaPort.SetUDPLength[bufferBody, index]; --Sets the udp length.
ArpaPort.SetIPLengths[bufferBody, 0, index + ArpaPort.UDPHeaderBytes];
};
END; -- BuildRequest
RemoteRequest: PROCEDURE [domain: LONG STRING, type: QType, server: ServerType, udpH: ArpaPort.Handle, debugProc: DebugProc ← NIL] RETURNS [b: ArpaBuffer.Buffer ← NIL] =
BEGIN
IF debugProc # NIL THEN
BEGIN
tempString: LONG STRING ← [80];
String.AppendString[tempString, " Sending "L];
String.AppendString[tempString,
SELECT type FROM
address => "ADDRESS"L,
mb => "MAILBOX"L,
ns => "NAME SERVER"L,
ptr => "POINTER"L,
wks => "WELL KNOWN SERVICE"L,
ENDCASE => "UNKNOWN"L];
String.AppendString[tempString, " query to "L];
AppendINetAddress[tempString, server.host];
String.AppendString[tempString, ", port = "L];
String.AppendDecimal[tempString, LOOPHOLE[server.port]];
debugProc[tempString];
END;
-- send request
FOR i: CARDINAL IN [0..maxTries) WHILE b = NIL DO
sendBuf: ArpaBuffer.Buffer ← ArpaPort.GetSendBuffer[udpH];
BEGIN ENABLE UNWIND => IF sendBuf # NIL THEN ArpaBuffer.ReturnBuffer[sendBuf];
IF debugProc # NIL THEN debugProc["."L];
BuildRequest[domain, type, server.host, server.port, sendBuf];
ArpaPort.PutPacket[udpH, sendBuf];
sendBuf ← NIL;
b ← ArpaPort.GetPacket[udpH ! ArpaPort.Timeout => LOOP];
END;
ENDLOOP;
requestCount ← requestCount + 1;
IF debugProc # NIL THEN debugProc["\n"L];
IF b = NIL THEN SIGNAL NoAnswer;
END; --RemoteRequest
--****************************************************************************--
UnpackName: PROCEDURE [p: UDPBuffer, i: CARDINAL, s: LONG POINTER TO LONG STRING] RETURNS [newIndex: CARDINAL] =
BEGIN
UNTIL p.data[i] = 0 OR p.data[i] >= 300B OR i >= MaxQuerySize DO
FOR j: CARDINAL IN [i+1..i+p.data[i]] DO
s↑.text[s↑.length] ← LOOPHOLE[p.data[j]];
s↑.length ← s↑.length + 1;
ENDLOOP;
i ← i + p.data[i] + 1;
s↑.text[s↑.length] ← '. ;
s↑.length ← s↑.length + 1;
ENDLOOP;
IF i < MaxQuerySize THEN
IF p.data[i] >= 300B THEN
BEGIN
tempIndex: CARDINAL;
tempIndex ← GetWord[@p.data, @i];
tempIndex ← tempIndex - 140000B;
IF (s↑.maxlength - s↑.length) > p.data[tempIndex] THEN
tempIndex ← UnpackName[p, tempIndex, s];
END
ELSE
BEGIN
i ← i + 1; -- 1 byte for the 0 (end of name)
IF s↑.length =0 THEN {
s↑.text[s↑.length] ← '.;
s↑.length ← s↑.length + 1 };
END;
RETURN[i];
END; -- UnpackName
UpdateTime: PROCEDURE [timePtr: LONG POINTER TO System.GreenwichMeanTime, newTime: System.GreenwichMeanTime] =
BEGIN
IF timePtr↑ = 0 OR System.SecondsSinceEpoch[timePtr↑] < System.SecondsSinceEpoch[newTime] THEN
timePtr↑ ← newTime;
END; -- UpdateTime
AddStringEntry: PROCEDURE [arrayPtr: LONG POINTER TO ARRAY [0..NamesPerEntry) OF LONG STRING, countPtr: LONG POINTER TO CARDINAL, str: LONG STRING] =
BEGIN
IF countPtr↑ < NamesPerEntry THEN
BEGIN
FOR i: CARDINAL IN [0..countPtr↑) DO
IF String.Equivalent[str, arrayPtr[i]] THEN EXIT;
REPEAT FINISHED =>
BEGIN -- no matching entry
arrayPtr[countPtr↑] ← String.CopyToNewString[str, Heap.systemZone];
countPtr↑ ← countPtr↑ + 1;
END;
ENDLOOP;
END;
END; -- AddStringEntry
AddAddressEntry: PROCEDURE [arrayPtr: LONG POINTER TO ARRAY [0..NamesPerEntry) OF INetAddr, countPtr: LONG POINTER TO CARDINAL, addr: INetAddr] =
BEGIN
IF countPtr↑ < NamesPerEntry THEN
BEGIN
FOR i: CARDINAL IN [0..countPtr↑) DO
IF addr = arrayPtr[i] THEN EXIT;
REPEAT
FINISHED => BEGIN -- no matching entry
arrayPtr[countPtr↑] ← addr;
countPtr↑ ← countPtr↑ + 1;
END;
ENDLOOP;
END;
END; -- AddAddressEntry
ProcessReply: PROCEDURE [b: ArpaBuffer.Buffer, domain: LONG STRING, currentTime: System.GreenwichMeanTime, debugProc: DebugProc ← NIL] RETURNS [result: ResponseType] =
BEGIN
OPEN udp: b.arpa.user;
pUDP: UDPBuffer ← LOOPHOLE[@udp];
hPtr: LONG POINTER TO QueryHeader ← LOOPHOLE[@udp.data];
-- check rcode and counts in header
IF hPtr.qr = response AND hPtr.rcode = okay THEN
BEGIN ENABLE UNWIND => ArpaBuffer.ReturnBuffer[b];
domainAddress: INetAddr;
queryType: QType;
out: LONG STRING ← [120];
index: CARDINAL ← 2 * SIZE[QueryHeader];
-- check the query in the response
FOR i:CARDINAL IN [0..hPtr.queryCount) DO
index ← UnpackName[pUDP, index, @out];
IF NOT String.Equivalent[domain, out] THEN SIGNAL ErrorInReply;
queryType ← LOOPHOLE[GetWord[@pUDP.data, @index]];
IF queryType = ptr THEN
BEGIN
temp: INetAddr;
out.length ← out.length -9; --cut off .IN-ADDR.
temp ← INetAddress[out];
domainAddress.d ← temp.a;
domainAddress.c ← temp.b;
domainAddress.b ← temp.c;
domainAddress.a ← temp.d;
END;
index ← index + 2; -- 2 bytes for the class
ENDLOOP;
FOR i:CARDINAL IN [0..(hPtr.answerCount+hPtr.nsCount+hPtr.arCount)) DO
rdlen: CARDINAL ← 0;
type: QType;
ttl: System.GreenwichMeanTime ← [0];
time: LONG CARDINAL;
out.length ← 0;
index ← UnpackName[pUDP, index, @out];
type ← LOOPHOLE[GetWord[@pUDP.data, @index]];
index ← index + 2; -- 2 bytes for the class
time ← GetLong[@pUDP.data, @index];
ttl ← System.AdjustGreenwichMeanTime[currentTime, MIN[17777777777B, time]];
rdlen ← GetWord[@pUDP.data, @index];
SELECT type FROM
ns => -- reply to ns queryType, or referral
BEGIN
server: LONG POINTER TO ServerTableEntry ← InitServerEntry[out, currentTime];
out.length ← 0;
index ← UnpackName[pUDP, index, @out];
IF debugProc # NIL AND queryType # ns THEN
BEGIN
debugProc[" Received referral to "L];
debugProc[out];
debugProc["\n"];
END;
AddStringEntry[@server.host, @server.hostCount, out];
UpdateTime[@server.time, ttl];
END;
cName =>
BEGIN
fullName: LONG STRING ← [120];
index ← UnpackName[pUDP, index, @fullName];
SELECT queryType FROM
address, wks =>
BEGIN
host: LONG POINTER TO HostTableEntry ← InitHostEntry[@fullName, currentTime];
AddStringEntry[@host.name, @host.nameCount, out];
UpdateTime[@host.time, ttl];
END;
mb =>
BEGIN
mailbox: LONG POINTER TO MailboxTableEntry ← InitMailboxEntry[@fullName, currentTime];
AddStringEntry[@mailbox.name, @mailbox.nameCount, out];
UpdateTime[@mailbox.time, ttl];
END;
ENDCASE;
END;
mb =>
BEGIN
mailbox: LONG POINTER TO MailboxTableEntry ← InitMailboxEntry[@out, currentTime];
out.length ← 0;
index ← UnpackName[pUDP, index, @out];
String.Replace[@mailbox.host, out, Heap.systemZone];
UpdateTime[@mailbox.time, ttl];
END;
address =>
BEGIN
addr: LONG POINTER TO AddressTableEntry;
host: LONG POINTER TO HostTableEntry ← InitHostEntry[@out, currentTime];
address: INetAddr ← [pUDP.data[index], pUDP.data[index+1], pUDP.data[index+2], pUDP.data[index+3]];
index ← index + 4; -- 4 bytes for the internet address
AddAddressEntry[@host.addr, @host.addrCount, address];
UpdateTime[@host.time, ttl];
addr ← InitAddressEntry[address, currentTime];
FOR i: CARDINAL IN [0..host.nameCount) DO AddStringEntry[@addr.name, @addr.nameCount, host.name[i]]; ENDLOOP;
UpdateTime[@addr.time, ttl];
END;
ptr =>
IF String.Equivalent[domain, out] THEN
BEGIN
addr: LONG POINTER TO AddressTableEntry ← InitAddressEntry[domainAddress, currentTime];
out.length ← 0;
index ← UnpackName[pUDP, index, @out];
AddStringEntry[@addr.name, @addr.nameCount, out];
UpdateTime[@addr.time, ttl];
END;
wks => BEGIN
addr: LONG POINTER TO AddressTableEntry;
host: LONG POINTER TO HostTableEntry ← InitHostEntry[@out, currentTime];
address: INetAddr ← [pUDP.data[index], pUDP.data[index+1], pUDP.data[index+2], pUDP.data[index+3]];
AddAddressEntry[@host.addr, @host.addrCount, address];
UpdateTime[@host.time, ttl];
addr ← InitAddressEntry[address, currentTime];
FOR i: CARDINAL IN [0..host.nameCount) DO AddStringEntry[@addr.name, @addr.nameCount, host.name[i]]; ENDLOOP;
UpdateTime[@addr.time, ttl];
IF debugProc # NIL THEN
BEGIN
stringProc: Format.StringProc = {String.AppendString[out, s]};
out.length ← 0;
String.AppendString[out, " For: "L];
AppendINetAddress[out, address];
String.AppendString[out, " ("L];
AppendProtocol[out, LOOPHOLE[pUDP.data[index+4]]];
String.AppendString[out, "), WKS bitmap: "L];
debugProc[out];
FOR i: CARDINAL IN [index+5..index+rdlen) DO
IF ((i-index+5) MOD 10) = 0 THEN
BEGIN
debugProc["\n"];
debugProc[" "L]; -- 24 blanks
END;
out.length ← 0;
Format.Number[stringProc, pUDP.data[i], [8,TRUE,TRUE,3]];
String.AppendString[out,"B "L];
debugProc[out];
ENDLOOP;
debugProc["\n"];
END;
index ← index + rdlen;
END;
ENDCASE => index ← index + rdlen;
ENDLOOP;
END;
ArpaBuffer.ReturnBuffer[b];
RETURN[hPtr.rcode];
END; --ProcessReply
--****************************************************************************--
ClearHostEntry: PROCEDURE [host: LONG POINTER TO HostTableEntry] =
BEGIN
IF host # NIL THEN
BEGIN
FOR n: CARDINAL IN [0..host.nameCount) DO
IF host.name[n] # NIL THEN String.FreeString[Heap.systemZone, host.name[n]];
ENDLOOP;
host↑ ← nullHostDesc;
END;
END; -- ClearHostEntry
ClearMailboxEntry: PROCEDURE [desc: LONG POINTER TO MailboxTableEntry] =
BEGIN
IF desc # NIL THEN
BEGIN
FOR n: CARDINAL IN [0..desc.nameCount) DO
IF desc.name[n] # NIL THEN String.FreeString[Heap.systemZone, desc.name[n]];
ENDLOOP;
String.FreeString[Heap.systemZone, desc.host];
desc↑ ← nullMailboxDesc;
END;
END; -- ClearMailboxEntry
ClearServerEntry: PROCEDURE [server: LONG POINTER TO ServerTableEntry] =
BEGIN
IF server # NIL THEN
BEGIN
String.FreeString[Heap.systemZone, server.domain];
FOR i: CARDINAL IN [0..server.hostCount) DO
IF server.host[i] # NIL THEN String.FreeString[Heap.systemZone, server.host[i]];
ENDLOOP;
server↑ ← nullServerDesc;
END;
END; -- ClearServerEntry
ClearAddressEntry: PROCEDURE [address: LONG POINTER TO AddressTableEntry] =
BEGIN
IF address # NIL THEN
BEGIN
FOR i: CARDINAL IN [0..address.nameCount) DO
IF address.name[i] # NIL THEN String.FreeString[Heap.systemZone, address.name[i]];
ENDLOOP;
address↑ ← nullAddressDesc;
END;
END; -- ClearAddressEntry
--****************************************************************************--
FindHostEntry: PROCEDURE [domain: LONG POINTER TO LONG STRING, currentTime: System.GreenwichMeanTime] RETURNS [desc: LONG POINTER TO HostTableEntry ← NIL] =
BEGIN
FOR i: CARDINAL IN [0..HostTableLength) DO
FOR j: CARDINAL IN [0..hostTable[i].nameCount) DO
IF String.Equivalent[domain↑,hostTable[i].name[j]] THEN
IF hostTable[i].time # 0 AND (System.SecondsSinceEpoch[hostTable[i].time] >= System.SecondsSinceEpoch[currentTime]) THEN
RETURN[@hostTable[i]]
ELSE
EXIT;
ENDLOOP;
ENDLOOP;
END; -- FindHostEntry
FindMailboxEntry: PROCEDURE [domain: LONG POINTER TO LONG STRING, currentTime: System.GreenwichMeanTime] RETURNS [desc: LONG POINTER TO MailboxTableEntry ← NIL] =
BEGIN
FOR i: CARDINAL IN [0..mailboxTableLength) DO
FOR j: CARDINAL IN [0..mailboxTable[i].nameCount) DO
IF String.Equivalent[domain↑,mailboxTable[i].name[j]] THEN
IF mailboxTable[i].time # 0 AND (System.SecondsSinceEpoch[mailboxTable[i].time] >= System.SecondsSinceEpoch[currentTime]) THEN
RETURN[@mailboxTable[i]]
ELSE
EXIT;
ENDLOOP;
ENDLOOP;
END; -- FindMailboxEntry
FindAddress: PROCEDURE [address: INetAddr, currentTime: System.GreenwichMeanTime] RETURNS [desc: LONG POINTER TO AddressTableEntry ← NIL] =
BEGIN
FOR i: CARDINAL IN [0..AddressTableLength) DO
IF addressTable[i].addr = address AND addressTable[i].time # 0 AND System.SecondsSinceEpoch[addressTable[i].time] >= System.SecondsSinceEpoch[currentTime] THEN
RETURN[@addressTable[i]];
ENDLOOP;
END; -- FindAddress
MatchingServer: PROCEDURE [domain: LONG STRING, currentTime: System.GreenwichMeanTime] RETURNS [desc: LONG POINTER TO ServerTableEntry ← NIL] =
BEGIN
FOR i: CARDINAL IN [0..serverTableLength) DO
IF String.Equivalent[serverTable[i].domain, domain]
AND serverTable[i].time # 0
AND System.SecondsSinceEpoch[serverTable[i].time] >= System.SecondsSinceEpoch[currentTime] THEN
RETURN[@serverTable[i]];
ENDLOOP;
END; -- MatchingServer
BestServer: PROCEDURE [domain: LONG STRING, currentTime: System.GreenwichMeanTime] RETURNS [desc: LONG POINTER TO ServerTableEntry ← NIL] =
-- find the server from the cache (serverTable) for the longest domain name
-- that matches the domain in question (Domain)
BEGIN
bestIndex, strLength: CARDINAL ← 0;
FOR i: CARDINAL IN [0..serverTableLength) DO
IF serverTable[i].domain # NIL AND serverTable[i].domain.length > strLength AND NOT (serverTable[i].time = 0 OR System.SecondsSinceEpoch[serverTable[i].time] < System.SecondsSinceEpoch[currentTime]) THEN
IF (serverTable[i].domain.length < domain.length AND domain.text[domain.length-serverTable[i].domain.length-1] = '.) OR serverTable[i].domain.length = domain.length THEN
BEGIN
domainSubStr: String.SubStringDescriptor ← [serverTable[i].domain, 0, serverTable[i].domain.length];
tail: String.SubStringDescriptor ← [domain, (domain.length - serverTable[i].domain.length), serverTable[i].domain.length];
IF String.EquivalentSubString[@tail, @domainSubStr] THEN
BEGIN
bestIndex ← i;
strLength ← serverTable[i].domain.length;
END;
END;
ENDLOOP;
IF strLength # 0 THEN
desc ← @serverTable[bestIndex];
END; -- BestServer
--****************************************************************************--
InitMailboxEntry: PROCEDURE [domain: LONG POINTER TO LONG STRING, currentTime: System.GreenwichMeanTime] RETURNS [desc: LONG POINTER TO MailboxTableEntry ← NIL] =
BEGIN
IF (desc ← FindMailboxEntry[domain, currentTime]) # NIL THEN RETURN;
FOR j: CARDINAL IN [0..mailboxTableLength) DO
IF mailboxTable[j].time = 0 OR System.SecondsSinceEpoch[mailboxTable[j].time] < System.SecondsSinceEpoch[currentTime] THEN
BEGIN -- found an empty entry
desc ← @mailboxTable[j];
ClearMailboxEntry[desc];
EXIT;
END;
REPEAT
FINISHED =>
BEGIN -- clear out the whole mailboxTable
desc ← @mailboxTable[0];
FOR k: CARDINAL IN [0..mailboxTableLength) DO ClearMailboxEntry[@mailboxTable[k]]; ENDLOOP;
END;
ENDLOOP;
-- fill in name
desc.name[0] ← String.CopyToNewString[domain↑, Heap.systemZone];
desc.nameCount ← 1;
END; -- InitMailboxEntry
InitHostEntry: PROCEDURE [domain: LONG POINTER TO LONG STRING, currentTime: System.GreenwichMeanTime] RETURNS [desc: LONG POINTER TO HostTableEntry ← NIL] =
BEGIN
IF (desc ← FindHostEntry[domain, currentTime]) # NIL THEN RETURN;
FOR j: CARDINAL IN [0..HostTableLength) DO
IF hostTable[j].time = 0 OR System.SecondsSinceEpoch[hostTable[j].time] < System.SecondsSinceEpoch[currentTime] THEN
BEGIN -- found an empty entry
desc ← @hostTable[j];
ClearHostEntry[desc];
EXIT;
END;
REPEAT
FINISHED =>
BEGIN -- clear out the whole hostTable
desc ← @hostTable[0];
FOR k: CARDINAL IN [0..HostTableLength) DO ClearHostEntry[@hostTable[k]]; ENDLOOP;
END;
ENDLOOP;
-- fill in name
desc.name[0] ← String.CopyToNewString[domain↑, Heap.systemZone];
desc.nameCount ← 1;
END; --InitHostEntry
InitAddressEntry: PROCEDURE [address: INetAddr, currentTime: System.GreenwichMeanTime] RETURNS [desc: LONG POINTER TO AddressTableEntry ← NIL] =
BEGIN
-- find matching domain
IF (desc ← FindAddress[address, currentTime]) # NIL THEN RETURN[desc];
-- find unused entry
FOR i: CARDINAL IN [0..serverTableLength) DO
IF addressTable[i].time = 0 OR System.SecondsSinceEpoch[addressTable[i].time] < System.SecondsSinceEpoch[currentTime] THEN
BEGIN -- found an empty entry
ClearAddressEntry[@addressTable[i]];
addressTable[i].addr ← address;
RETURN[@addressTable[i]];
END;
REPEAT
FINISHED =>
BEGIN -- clear out the whole serverTable
FOR j: CARDINAL IN [0..AddressTableLength) DO ClearAddressEntry[@addressTable[j]]; ENDLOOP;
addressTable[0].addr ← address;
RETURN[@addressTable[0]];
END;
ENDLOOP;
END; --InitAddressEntry
InitServerEntry: PROCEDURE [domain: LONG STRING, currentTime: System.GreenwichMeanTime] RETURNS [desc: LONG POINTER TO ServerTableEntry ← NIL] =
BEGIN
-- find matching domain
IF (desc ← MatchingServer[domain, currentTime]) # NIL THEN RETURN[desc];
-- find unused entry
FOR i: CARDINAL IN [0..serverTableLength) DO
IF serverTable[i].time = 0 OR System.SecondsSinceEpoch[serverTable[i].time] < System.SecondsSinceEpoch[currentTime] THEN
BEGIN -- found an empty entry
ClearServerEntry[@serverTable[i]];
serverTable[i].domain ← String.CopyToNewString[domain, Heap.systemZone];
RETURN[@serverTable[i]];
END;
REPEAT
FINISHED =>
BEGIN -- clear out the whole serverTable
FOR j: CARDINAL IN [0..serverTableLength) DO ClearServerEntry[@serverTable[j]]; ENDLOOP;
serverTable[0].domain ← String.CopyToNewString[domain, Heap.systemZone];
RETURN[@serverTable[0]];
END;
ENDLOOP;
END; --InitServerEntry
--****************************************************************************--
Resolve: PROCEDURE [LookupEntry: PROCEDURE RETURNS [done: BOOLEAN], SendQuery: PROCEDURE [server: ServerType] RETURNS [done: BOOLEAN], domain: LONG STRING, server: ServerType ← [nullINetAddress, domainNS], debugProc: DebugProc ← NIL, currentTime: System.GreenwichMeanTime] =
BEGIN
done: BOOLEAN ← FALSE;
tries: CARDINAL ← 0;
TryServerFromCache: PROCEDURE =
BEGIN
serverHost: LONG POINTER TO HostTableEntry ← NIL;
previousServer, bestServer: LONG POINTER TO ServerTableEntry ← NIL;
WHILE NOT done DO
IF (bestServer ← BestServer[domain, currentTime]) = NIL OR (previousServer = bestServer) THEN
RETURN
ELSE
FOR i: CARDINAL IN [0..bestServer.hostCount) WHILE NOT done DO
IF (serverHost ← FindHostEntry[@bestServer.host[i], currentTime]) # NIL AND (tries ← tries + 1) <= maxReferrals THEN
BEGIN
IF debugProc # NIL THEN debugProc[" Cache:"L];
done ← SendQuery[[SelectLocalAddress[serverHost↑], domainNS] ! NoAnswer => CONTINUE];
END;
ENDLOOP;
previousServer ← bestServer;
ENDLOOP;
END; -- TryServerFromCache
TryServerFromParam: PROCEDURE =
BEGIN
IF server.host # nullINetAddress AND (tries ← tries + 1) <= maxReferrals THEN
BEGIN
IF debugProc # NIL THEN debugProc[" Param:"L];
done ← SendQuery[server];
END;
END; -- TryServerFromParam
TryServerFromInit: PROCEDURE =
BEGIN
IF defaultNameServer = nullINetAddress THEN defaultNameServer ← LOOPHOLE[ArpaPortInternal.GetDomainNameServer[]];
IF defaultNameServer # nullINetAddress AND (tries ← tries + 1) <= maxReferrals THEN
BEGIN
IF debugProc # NIL THEN debugProc[" Init:"L];
done ← SendQuery[[defaultNameServer, domainNS] ! NoAnswer => CONTINUE];
END;
END; -- TryServerFromInit
IF LookupEntry[] THEN RETURN; -- is information already in cache
TryServerFromParam[]; -- if server was specified, try it
IF done OR tries >= maxReferrals THEN RETURN;
IF domain # NIL THEN -- try any applicable servers from cache
BEGIN
TryServerFromCache[];
IF done OR tries >= maxReferrals THEN RETURN;
END;
TryServerFromInit[]; -- try default servers
IF done OR tries >= maxReferrals THEN RETURN;
IF domain # NIL THEN TryServerFromCache[]; -- in case default server made referral
END; --Resolve
--****************************************************************************--
SelectLocalAddress: PROCEDURE [host: HostTableEntry] RETURNS [address: INetAddr ← nullINetAddress] =
BEGIN
IF myINetAddress = LOOPHOLE[nullINetAddress] THEN
myINetAddress ← ArpaPortInternal.GetArpaAddr[];
IF host.addrCount # 0 THEN
BEGIN
FOR i: CARDINAL IN [0..host.addrCount) DO
mask: ArpaRouter.InternetAddress ← ArpaPortInternal.BuildMasks[myINetAddress].netMask;
IF ArpaPortInternal.AddrMatch[mask, myINetAddress, LOOPHOLE[host.addr[i]]] THEN
RETURN[host.addr[i]];
ENDLOOP;
RETURN[host.addr[0]];
END;
END; --SelectLocalAddress
INetAddressOrName: PUBLIC PROCEDURE [s: LONG STRING] RETURNS [address: INetAddr ← nullINetAddress] =
BEGIN
Later: PROCEDURE [domain: LONG STRING] ={
[] ← SelectLocalAddress[ResolveHostName[domain]]};
IF ~String.Empty[s] THEN
BEGIN
address ← INetAddress[s];
IF ArpaPortInternal.GetDomainNameServer[] # ArpaRouter.unknownInternetAddress THEN {
IF (address ← INetAddress[s]) = nullINetAddress THEN
address ← SelectLocalAddress[ResolveHostName[domain: s<<, time: 2>>]];
IF address = nullINetAddress THEN
Process.Detach[FORK Later[s]]};
END;
END; -- INetAddressOrName
ResolveHostName: PUBLIC PROCEDURE [domain: LONG STRING, WKSFlag: BOOLEAN ← FALSE, useCache: BOOLEAN ← TRUE, server: ServerType ← [nullINetAddress, domainNS], time: CARDINAL ← defaultTimeout, debugProc: DebugProc ← NIL] RETURNS [desc: HostTableEntry ← nullHostDesc] =
BEGIN
domainStr: LONG STRING ← String.CopyToNewString[domain, Heap.systemZone, 1];
currentTime: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
LookupHost: ENTRY PROCEDURE RETURNS [done: BOOLEAN ← FALSE] =
BEGIN
hostPtr: LONG POINTER TO HostTableEntry ← FindHostEntry[@domainStr, currentTime];
IF done ← (hostPtr # NIL) THEN
IF (done ← useCache) THEN
desc ← hostPtr↑
ELSE
ClearHostEntry[hostPtr]
END; -- LookupHost
SendHostQuery: PROCEDURE [server: ServerType] RETURNS [done: BOOLEAN ← FALSE] =
BEGIN
port: ArpaRouter.Port ← ArpaPort.AssignPort[];
udpHandle: ArpaPort.Handle ← ArpaPort.Create[port, 1, 1, normal];
b: ArpaBuffer.Buffer ← NIL;
UpdateTables: ENTRY PROCEDURE =
BEGIN
hostPtr: LONG POINTER TO HostTableEntry;
done ← (ProcessReply[b, domainStr, currentTime, debugProc] = nameError);
IF NOT done THEN
IF (done ← ((hostPtr ← FindHostEntry[@domainStr, currentTime]) # NIL)) THEN
desc ← hostPtr↑;
END; -- UpdateTables
BEGIN ENABLE UNWIND =>
{IF b # NIL THEN ArpaBuffer.ReturnBuffer[b];
ArpaPort.Delete[udpHandle]};
ArpaPort.SetWaitTime[udpHandle, time * (1000/maxTries)];
b ← RemoteRequest[domainStr, IF WKSFlag THEN wks ELSE address, server, udpHandle, debugProc];
UpdateTables[];
END;
ArpaPort.Delete[udpHandle];
END; -- SendHostQuery
-- main
IF domainStr.text[domainStr.length-1] # '. THEN String.AppendChar[domainStr, '.];
Resolve[LookupHost, SendHostQuery, domainStr, server, debugProc, currentTime];
String.FreeString[Heap.systemZone, domainStr];
END; -- ResolveHostName
--****************************************************************************--
ResolveMailboxName: PUBLIC PROCEDURE [domain: LONG STRING, useCache: BOOLEAN ← TRUE, server: ServerType ← [nullINetAddress, domainNS], time: CARDINAL ← defaultTimeout, debugProc: DebugProc ← NIL] RETURNS [desc: MailboxTableEntry ← nullMailboxDesc] =
BEGIN
domainStr: LONG STRING ← String.CopyToNewString[domain, Heap.systemZone, 1];
currentTime: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
LookupMailbox: ENTRY PROCEDURE RETURNS [done: BOOLEAN ← FALSE] =
BEGIN
mbPtr: LONG POINTER TO MailboxTableEntry ← FindMailboxEntry[@domainStr, currentTime];
IF done ← (mbPtr # NIL) THEN
IF (done ← useCache) THEN
desc ← mbPtr↑
ELSE
ClearMailboxEntry[mbPtr]
END; -- LookupMailbox
SendMailboxQuery: PROCEDURE [server: ServerType] RETURNS [done: BOOLEAN ← FALSE] =
BEGIN
port: ArpaRouter.Port ← ArpaPort.AssignPort[];
udpHandle: ArpaPort.Handle ← ArpaPort.Create[port, 1, 1, normal];
b: ArpaBuffer.Buffer ← NIL;
UpdateTables: ENTRY PROCEDURE =
BEGIN
mbPtr: LONG POINTER TO MailboxTableEntry;
done ← (ProcessReply[b, domainStr, currentTime, debugProc] = nameError);
IF NOT done THEN
IF (done ← ((mbPtr ← FindMailboxEntry[@domainStr, currentTime]) # NIL)) THEN
desc ← mbPtr↑;
END; -- UpdateTables
BEGIN ENABLE UNWIND =>
{IF b # NIL THEN ArpaBuffer.ReturnBuffer[b];
ArpaPort.Delete[udpHandle]};
ArpaPort.SetWaitTime[udpHandle, time * (1000/maxTries)];
b ← RemoteRequest[domainStr, mb, server, udpHandle, debugProc];
UpdateTables[];
END;
ArpaPort.Delete[udpHandle];
END; -- SendMailboxQuery
-- main
IF domainStr.text[domainStr.length-1] # '. THEN String.AppendChar[domainStr, '.];
Resolve[LookupMailbox, SendMailboxQuery, domainStr, server, debugProc, currentTime];
String.FreeString[Heap.systemZone, domainStr];
END; -- ResolveMailboxName
--****************************************************************************--
ClearServerCache: PUBLIC ENTRY PROCEDURE =
BEGIN
FOR i: CARDINAL IN [0..serverTableLength) DO
ClearServerEntry[@serverTable[i]];
ENDLOOP;
END; -- ClearServerCache
ResolveServerName: PUBLIC PROCEDURE [domain: LONG STRING, useCache: BOOLEAN ← TRUE, server: ServerType ← [nullINetAddress, domainNS], time: CARDINAL ← defaultTimeout, debugProc: DebugProc ← NIL] RETURNS [desc: ServerTableEntry ← nullServerDesc] =
BEGIN
domainStr: LONG STRING ← String.CopyToNewString[domain, Heap.systemZone, 1];
currentTime: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
LookupServer: ENTRY PROCEDURE RETURNS [done: BOOLEAN ← FALSE] =
BEGIN
serverPtr: LONG POINTER TO ServerTableEntry ← MatchingServer[domainStr, currentTime];
IF done ← (serverPtr # NIL) THEN
IF (done ← useCache) THEN
desc ← serverPtr↑
ELSE
ClearServerEntry[serverPtr]
END; -- LookupServer
SendServerQuery: PROCEDURE [server: ServerType] RETURNS [done: BOOLEAN ← FALSE] =
BEGIN
port: ArpaRouter.Port ← ArpaPort.AssignPort[];
udpHandle: ArpaPort.Handle ← ArpaPort.Create[port, 1, 1, normal];
b: ArpaBuffer.Buffer ← NIL;
UpdateTables: ENTRY PROCEDURE =
BEGIN
serverPtr: LONG POINTER TO ServerTableEntry;
done ← (ProcessReply[b, domainStr, currentTime, debugProc] = nameError);
IF NOT done THEN
IF (done ← ((serverPtr ← MatchingServer[domainStr, currentTime]) # NIL)) THEN
desc ← serverPtr↑;
END; -- UpdateTables
BEGIN ENABLE UNWIND =>
{IF b # NIL THEN ArpaBuffer.ReturnBuffer[b];
ArpaPort.Delete[udpHandle]};
ArpaPort.SetWaitTime[udpHandle, time * (1000/maxTries)];
b ← RemoteRequest[domainStr, ns, server, udpHandle, debugProc];
UpdateTables[];
END;
ArpaPort.Delete[udpHandle];
END; -- SendServerQuery
-- main
IF domainStr.text[domainStr.length-1] # '. THEN
String.AppendChar[domainStr, '.];
Resolve[LookupServer, SendServerQuery, domainStr, server, debugProc, currentTime];
String.FreeString[Heap.systemZone, domainStr];
END; --ResolveServerName
--****************************************************************************--
AppendNameOrINetAddress: PUBLIC PROCEDURE [to: LONG STRING, address: INetAddr] =
BEGIN
currentTime: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
addrPtr: LONG POINTER TO AddressTableEntry ← FindAddress[address, currentTime];
Later: PROCEDURE = {
[] ← ResolveHostAddress[host: address, time: 2*defaultTimeout]
};
IF addrPtr = NIL THEN
BEGIN
Process.Detach[FORK Later];
AppendINetAddress[to, address];
END
ELSE
String.AppendString[to: to, from: addrPtr↑.name[0]];
END; -- AppendNameOrINetAddress
AppendNameOrINetAddressAndGrow: PUBLIC PROCEDURE [to: LONG POINTER TO LONG STRING, address: INetAddr, z: UNCOUNTED ZONE] =
BEGIN
currentTime: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
addrPtr: LONG POINTER TO AddressTableEntry ← FindAddress[address, currentTime];
Later: PROCEDURE = {
[] ← ResolveHostAddress[host: address, time: 2*defaultTimeout]
};
IF addrPtr = NIL THEN
BEGIN
Process.Detach[FORK Later];
AppendINetAddressAndGrow[to, address, z];
END
ELSE
String.AppendStringAndGrow[to, addrPtr↑.name[0], z];
END; -- AppendNameOrINetAddress
ResolveHostAddress: PUBLIC PROCEDURE [host: INetAddr, useCache: BOOLEAN ← TRUE, server: ServerType ← [nullINetAddress, domainNS], time: CARDINAL ← defaultTimeout, debugProc: DebugProc ← NIL] RETURNS [desc: AddressTableEntry ← nullAddressDesc] =
BEGIN
currentTime: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
LookupAddress: ENTRY PROCEDURE RETURNS [done: BOOLEAN ← FALSE] =
BEGIN
addrPtr: LONG POINTER TO AddressTableEntry ← FindAddress[host, currentTime];
IF done ← (addrPtr # NIL) THEN
IF (done ← useCache) THEN
desc ← addrPtr↑
ELSE
ClearAddressEntry[addrPtr];
END; -- LookupAddress
SendInAddrQuery: PROCEDURE [server: ServerType] RETURNS [done: BOOLEAN ← FALSE] =
BEGIN
temp: LONG STRING ← [40];
port: ArpaRouter.Port ← ArpaPort.AssignPort[];
udpHandle: ArpaPort.Handle ← ArpaPort.Create[port, 1, 1, normal];
b: ArpaBuffer.Buffer ← NIL;
UpdateTables: ENTRY PROCEDURE =
BEGIN
addrPtr: LONG POINTER TO AddressTableEntry;
done ← (ProcessReply[b, temp, currentTime, debugProc] # okay);
IF NOT done THEN
IF (done ← ((addrPtr ← FindAddress[host, currentTime]) # NIL)) THEN
desc ← addrPtr↑;
END; -- UpdateTables
BEGIN ENABLE UNWIND =>
{IF b # NIL THEN ArpaBuffer.ReturnBuffer[b];
ArpaPort.Delete[udpHandle]};
-- build string to pass to RemoteRequest
String.AppendDecimal[temp, host.d];
String.AppendChar[temp, '.];
String.AppendDecimal[temp, host.c];
String.AppendChar[temp, '.];
String.AppendDecimal[temp, host.b];
String.AppendChar[temp, '.];
String.AppendDecimal[temp, host.a];
String.AppendString[temp, ".IN-ADDR.ARPA."L];
ArpaPort.SetWaitTime[udpHandle, time * (1000/maxTries)];
b ← RemoteRequest[temp, ptr, server, udpHandle, debugProc];
UpdateTables[];
END;
ArpaPort.Delete[udpHandle];
END; -- SendInAddrQuery
-- main
Resolve[LookupAddress, SendInAddrQuery, NIL, server, debugProc, currentTime];
END; -- ResolveHostAddress
--****************************************************************************--
MakePointerQuery: PUBLIC PROCEDURE [pointer: LONG STRING, server: ServerType ← [nullINetAddress, domainNS], time: CARDINAL ← defaultTimeout, debugProc: DebugProc ← NIL, z: UNCOUNTED ZONE] RETURNS [s: LONG STRING ← NIL] =
BEGIN
currentTime: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
strPointer: LONG STRING ← String.CopyToNewString[pointer, z, 1];
port: ArpaRouter.Port ← ArpaPort.AssignPort[];
udpHandle: ArpaPort.Handle ← ArpaPort.Create[port, 1, 1, normal];
b: ArpaBuffer.Buffer;
BEGIN ENABLE UNWIND => {
String.FreeString[z, strPointer];
IF b # NIL THEN ArpaBuffer.ReturnBuffer[b];
ArpaPort.Delete[udpHandle]};
IF strPointer.text[strPointer.length-1] # '. THEN
String.AppendChar[strPointer, '.];
ArpaPort.SetWaitTime[udpHandle, time * (1000/maxTries)];
b ← RemoteRequest[strPointer, ptr, server, udpHandle, debugProc];
BEGIN OPEN udp: b.arpa.user;
pUDP: UDPBuffer ← LOOPHOLE[@udp];
hPtr: LONG POINTER TO QueryHeader ← LOOPHOLE[@udp.data];
-- check rcode and counts in header
IF hPtr.qr = response AND hPtr.rcode = okay THEN
BEGIN ENABLE UNWIND => ArpaBuffer.ReturnBuffer[b];
temp: LONG STRING ← [120];
index: CARDINAL ← 2 * SIZE[QueryHeader];
-- check the query in the response
FOR i:CARDINAL IN [0..hPtr.queryCount) DO
index ← UnpackName[pUDP, index, @temp];
IF NOT String.Equivalent[strPointer, temp] THEN SIGNAL ErrorInReply;
index ← index + 4; -- 2 bytes for the type, 2 bytes for the class
ENDLOOP;
s ← String.MakeString[z, 40];
FOR i:CARDINAL IN [0..(hPtr.answerCount+hPtr.nsCount+hPtr.arCount)) DO
type: QType;
time: LONG CARDINAL;
temp.length ← 0;
index ← UnpackName[pUDP, index, @temp];
type ← LOOPHOLE[GetWord[@pUDP.data, @index]];
index ← index + 2; -- 2 bytes for the class
time ← GetLong[@pUDP.data, @index];
index ← index + 2; --2 bytes for the rdlength
SELECT type FROM
ns => -- referral
BEGIN
server: LONG POINTER TO ServerTableEntry ← InitServerEntry[temp, currentTime];
ttl: System.GreenwichMeanTime ← System.AdjustGreenwichMeanTime[currentTime, MIN[17777777777B, time]];
temp.length ← 0;
index ← UnpackName[pUDP, index, @temp];
IF debugProc # NIL THEN
BEGIN
debugProc[" Received referral to "L];
debugProc[temp];
debugProc["\n"];
END;
AddStringEntry[@server.host, @server.hostCount, temp];
UpdateTime[@server.time, ttl];
END;
ptr =>
BEGIN
temp.length ← 0;
index ← UnpackName[pUDP, index, @temp];
String.AppendStringAndGrow[@s, temp, z];
String.AppendStringAndGrow[@s, ", "L, z];
END
ENDCASE => SIGNAL ErrorInReply;
ENDLOOP;
IF s.length > 2 THEN s.length ← s.length -2;
END;
END; -- OPEN
END; -- ENABLE
ArpaBuffer.ReturnBuffer[b];
ArpaPort.Delete[udpHandle];
String.FreeString[z, strPointer];
END; -- MakePointerQuery
END.