DIRECTORY
Arpa USING [Address, nullAddress],
ArpaNameBuf USING [domainPort, hdrBytes, maxBodyBytes, Buffer, Port],
ArpaNameQuery,
ArpaUDP USING [AllocBuffers, Create, Destroy, Error, FreeBuffers, Get, GetRemoteAddress, Handle, Kick, Milliseconds, ReceivedError, Put, SetUserBytes],
ArpaUDPBuf USING [Buffer],
Basics USING [BytePair, HighByte, LongNumber, LowByte],
BasicTime USING [GetClockPulses, Pulses, PulsesToMicroseconds],
Rope
USING [Concat, Fetch, Find, FromChar, Length,
ROPE, Substr];
ArpaNameQueryImpl:
CEDAR
PROGRAM
IMPORTS ArpaUDP, Basics, BasicTime, Rope
EXPORTS ArpaNameQuery =
BEGIN OPEN ArpaNameQuery;
Error:
PUBLIC ERROR [code:
ATOM] =
CODE;
Codes are those passed up by ArpaUDP.
EndOfData: SIGNAL = CODE;
Raised when no more udp data
sequenceNumber: CARDINAL ← 0;
Pointer: TYPE = REF PointerRec;
PointerRec:
TYPE =
RECORD[
ptr: CARDINAL ← 0
];
Query:
PUBLIC
PROC [
server: Arpa.Address ← Arpa.nullAddress,
query: Rope.ROPE ← NIL,
type: QType ← a,
class: QClass ← in,
recurDesired: BOOL ← FALSE,
protocol: Protocol ← udpOnly,
port: ArpaNameBuf.Port ← ArpaNameBuf.domainPort,
timeout: ArpaUDP.Milliseconds ← 20000,
retry: CARDINAL ← 0,
acceptErrors: BOOL ← FALSE] RETURNS [reply: Reply←NIL] = {
SELECT protocol
FROM
udpOnly, retryWithTCP => reply ← UDPQuery[server, query, type, class, recurDesired, port, timeout, retry, acceptErrors];
tcpOnly => reply ← TCPQuery[server, query, type, class, recurDesired, port, timeout, retry, acceptErrors];
ENDCASE;
IF reply = NIL THEN RETURN;
IF reply.hdr.truncated
AND protocol = retryWithTCP
THEN
reply ← TCPQuery[server, query, type, class, recurDesired, port, timeout, retry, acceptErrors];
};
TCPQuery:
PROC [
server: Arpa.Address ← Arpa.nullAddress,
query: Rope.ROPE ← NIL,
type: QType ← a,
class: QClass ← in,
recurDesired: BOOL ← FALSE,
port: ArpaNameBuf.Port ← ArpaNameBuf.domainPort,
timeout: ArpaUDP.Milliseconds ← 20000,
retry: CARDINAL ← 0,
acceptErrors: BOOL ← FALSE] RETURNS [reply: Reply←NIL] = {};
UDPQuery:
PROC [
server: Arpa.Address ← Arpa.nullAddress,
query: Rope.ROPE ← NIL,
type: QType ← a,
class: QClass ← in,
recurDesired: BOOL ← FALSE,
port: ArpaNameBuf.Port ← ArpaNameBuf.domainPort,
timeout: ArpaUDP.Milliseconds ← 20000,
retry: CARDINAL ← 0,
acceptErrors: BOOL ← FALSE]
RETURNS [reply: Reply←NIL] =
{
{ENABLE {UNWIND => NULL};
packetStart: BasicTime.Pulses;
id: CARDINAL ← (sequenceNumber ← sequenceNumber + 1);
done: BOOL ← FALSE;
sendBuf: ArpaUDPBuf.Buffer;
replyBuf: ArpaUDPBuf.Buffer;
sendNameBuf: ArpaNameBuf.Buffer;
replyNameBuf: ArpaNameBuf.Buffer;
length: CARDINAL ← 0;
handle: ArpaUDP.Handle ← ArpaUDP.Create[remoteAddress: server, remotePort: port, getTimeout: timeout, acceptErrors: acceptErrors];
IF handle = NIL THEN RETURN[];
sendBuf ← ArpaUDP.AllocBuffers[handle];
TRUSTED { sendNameBuf ← LOOPHOLE[sendBuf]; };
sendNameBuf.hdr3.id ← id;
sendNameBuf.hdr3.qr ← query;
sendNameBuf.hdr3.opcode ← query;
sendNameBuf.hdr3.authoritative ← FALSE;
sendNameBuf.hdr3.truncated ← FALSE;
sendNameBuf.hdr3.recurDesired ← recurDesired;
sendNameBuf.hdr3.recurAvail ← FALSE;
sendNameBuf.hdr3.rcode ← ok;
sendNameBuf.hdr3.qdCount ← 0;
sendNameBuf.hdr3.anCount ← 0;
sendNameBuf.hdr3.nsCount ← 0;
sendNameBuf.hdr3.arCount ← 0;
FOR i:
CARDINAL
IN [0..retry]
UNTIL done
DO
length ← AppendQuery[sendNameBuf, query, type, class];
ArpaUDP.SetUserBytes[sendBuf, length + ArpaNameBuf.hdrBytes];
packetStart ← BasicTime.GetClockPulses[];
ArpaUDP.Put[sendBuf ! ArpaUDP.Error => IF acceptErrors THEN Error[code] ELSE EXIT; ArpaUDP.ReceivedError => IF acceptErrors THEN Error[code] ELSE EXIT];
UNTIL done
OR (replyBuf ← ArpaUDP.Get[handle ! ArpaUDP.Error =>
IF acceptErrors
THEN Error[code]
ELSE
EXIT; ArpaUDP.ReceivedError =>
IF acceptErrors
THEN Error[code]
ELSE
EXIT]) =
NIL
DO
packetStop: BasicTime.Pulses ← BasicTime.GetClockPulses[];
milliSeconds: LONG CARDINAL ← (BasicTime.PulsesToMicroseconds[packetStop-packetStart])/1000;
remoteAddress: Arpa.Address ← ArpaUDP.GetRemoteAddress[handle].address;
TRUSTED { replyNameBuf ← LOOPHOLE[replyBuf]; };
reply ← NEW[ReplyRecord];
reply.nRetries ← i;
reply.responseTime ← milliSeconds;
reply.source ← remoteAddress;
reply.domainPacketLength ← LOOPHOLE[replyNameBuf.hdr2.length];
reply.hdr ← replyNameBuf.hdr3;
ProcessDomainPacket[replyNameBuf, reply];
done ← TRUE;
ENDLOOP;
ENDLOOP;
IF replyBuf # NIL THEN ArpaUDP.FreeBuffers[replyBuf];
sendNameBuf ← NIL; replyNameBuf ← NIL; sendBuf ← NIL; replyBuf ← NIL;
ArpaUDP.Kick[handle];
ArpaUDP.Destroy[handle];
}};
ProcessDomainPacket:
PROC [udp: ArpaNameBuf.Buffer, reply: Reply] = {
p: Pointer ← NEW[PointerRec];
p.ptr ← 0;
LoadQueries[udp, reply, p];
LoadAnswerRRs[udp, reply, p];
LoadAuthorityRRs[udp, reply, p];
LoadAdditionalRRs[udp, reply, p];
};
LoadQueries:
PROC [udp: ArpaNameBuf.Buffer, reply: Reply, p: Pointer] = {
ENABLE {UNWIND => NULL; EndOfData => GOTO Quit};
reply.questions ← NEW[QSequenceBody[reply.hdr.qdCount]];
FOR i:
INT
IN[0..reply.hdr.qdCount)
DO
q: QuestionRecord ← NEW[QBody];
q.name ← GetCompressedName[udp, p];
q.type ← VAL[Basics.LowByte[GetTwoBytes[udp, p]]];
q.class ← VAL[Basics.LowByte[GetTwoBytes[udp, p]]];
reply.questions[i] ← q;
reply.qdCount ← reply.qdCount +1;
ENDLOOP;
};
LoadAnswerRRs:
PROC [udp: ArpaNameBuf.Buffer, reply: Reply, p: Pointer] = {
reply.answers ← NEW[RRSequenceBody[reply.hdr.anCount]];
reply.anCount ← LoadRRs[reply.hdr.anCount, reply.answers, udp, p];
};
LoadAuthorityRRs:
PROC [udp: ArpaNameBuf.Buffer, reply: Reply, p: Pointer] = {
reply.authority ← NEW[RRSequenceBody[reply.hdr.nsCount]];
reply.nsCount ← LoadRRs[reply.hdr.nsCount, reply.authority, udp, p];
};
LoadAdditionalRRs:
PROC [udp: ArpaNameBuf.Buffer, reply: Reply, p: Pointer] = {
reply.additional ← NEW[RRSequenceBody[reply.hdr.arCount]];
reply.arCount ← LoadRRs[reply.hdr.arCount, reply.additional, udp, p];
};
LoadRRs:
PROC [inCount:
CARDINAL, rrs: RRSequence, udp: ArpaNameBuf.Buffer, p: Pointer]
RETURNS[outCount:
CARDINAL𡤀] = {
ENABLE {UNWIND => NULL; EndOfData => GOTO Quit};
FOR i:
INT
IN[0..inCount)
DO
name: ROPE ← GetCompressedName[udp, p];
type: RRType ← VAL[Basics.LowByte[GetTwoBytes[udp, p]]];
thisRR: ResourceRecord ← NewRR[type];
thisRR.type ← type;
thisRR.name ← name;
thisRR.class ← VAL[Basics.LowByte[GetTwoBytes[udp, p]]];
thisRR.ttl ← GetTtl[udp, p];
thisRR.dataLength ← GetTwoBytes[udp, p];
WITH thisRR
SELECT
FROM
rr: ARR => rr.address ← GetIPAddress[udp, p];
rr: NsRR => rr.serverRope ← GetCompressedName[udp, p];
rr: CNameRR => rr.cNameRope ← GetCompressedName[udp, p];
rr: PtrRR => rr.ptrRope ← GetCompressedName[udp, p];
rr: MbRR => rr.mbRope ← GetCompressedName[udp, p];
rr: MgRR => rr.mgRope ← GetCompressedName[udp, p];
rr: MrRR => rr.mrRope ← GetCompressedName[udp, p];
rr: MdRR => rr.mdRope ← GetCompressedName[udp, p];
rr: MfRR => rr.mfRope ← GetCompressedName[udp, p];
rr: NullRR => rr.nullRope ← GetCompressedName[udp, p];
rr: SoaRR => {
rr.soaRec.primaryServer ← GetCompressedName[udp, p];
rr.soaRec.domainContact ← GetCompressedName[udp, p];
rr.soaRec.serial ← GetCard[udp, p];
rr.soaRec.refresh ← GetCard[udp, p];
rr.soaRec.retry ← GetCard[udp, p];
rr.soaRec.expire ← GetCard[udp, p];
rr.soaRec.minTtl ← GetCard[udp, p]};
rr: WksRR => {
tempPortArray: ARRAY[0..100) OF CARDINAL;
nPorts: CARDINAL ← 0;
port: INT ← 0;
rr.wksRec.address ← GetIPAddress[udp, p];
rr.wksRec.protocol ← GetOneByte[udp, p];
FOR j:
INT
IN [5..rr.dataLength)
DO
-- parse port bit array
byte: INT [0..255] ← GetOneByte[udp, p];
FOR k:
INT
IN [0..8)
DO
IF byte >= 80H
THEN {
byte ← byte - 80H;
tempPortArray[nPorts] ← port;
nPorts ← nPorts +1};
byte ← byte * 2;
port ← port + 1;
ENDLOOP;
ENDLOOP;
rr.wksRec.nPorts ← nPorts;
rr.wksRec.ports ← NEW[PortSequenceBody[nPorts]];
FOR j:
INT
IN [0..nPorts)
DO
rr.wksRec.ports[j] ← tempPortArray[j];
ENDLOOP;
};
rr: HinfoRR => {
rr.hinfoRec.cpu ← GetRope[udp, p];
rr.hinfoRec.os ← GetRope[udp, p]};
rr: MinfoRR => {
rr.minfoRec.rmailbx ← GetCompressedName[udp, p];
rr.minfoRec.emailbx ← GetCompressedName[udp, p]};
rr: MxRR => {
rr.mxRec.preference ← GetCardinal[udp, p];
rr.mxRec.host ← GetCompressedName[udp, p]};
ENDCASE => p.ptr ← p.ptr + thisRR.dataLength; -- unknown type
rrs[i] ← thisRR;
outCount ← outCount +1;
ENDLOOP;
};
NewRR:
PROC [type: RRType]
RETURNS[ResourceRecord] = {
RETURN[
SELECT type
FROM
a => NEW[a RRBody],
ns => NEW[ns RRBody],
cName => NEW[cName RRBody],
ptr => NEW[ptr RRBody],
mb => NEW[mb RRBody],
mg => NEW[mg RRBody],
mr => NEW[mr RRBody],
md => NEW[md RRBody],
mf => NEW[mf RRBody],
null => NEW[null RRBody],
soa => NEW[soa RRBody],
wks => NEW[wks RRBody],
hinfo => NEW[hinfo RRBody],
minfo => NEW[minfo RRBody],
mx => NEW[mx RRBody],
ENDCASE => NIL
];
};
Yuk... Much of this code is from the old Arpanet mail gateway and is rather crufty, but it seems to work and I don't have time to clean it up right now....
AppendQuery:
PROC [
udp: ArpaNameBuf.Buffer, query: ROPE, type: QType, class: QClass] RETURNS[length: CARDINAL𡤀] = {
p: Pointer ← NEW[PointerRec];
p.ptr ← 0;
AppendName[udp, query, p];
AppendTwoBytes[udp, LOOPHOLE[type], p];
AppendTwoBytes[udp, LOOPHOLE[class], p];
udp.hdr3.qdCount ← udp.hdr3.qdCount + 1;
length ← p.ptr;
};
AppendName:
PROC [
udp: ArpaNameBuf.Buffer, name: ROPE, p: Pointer] = {
DO
dot: INT ← Rope.Find[name, "."];
IF dot = -1 THEN EXIT;
IF dot = 0 THEN EXIT; -- Bounds fault
AppendFragment[udp, Rope.Substr[name, 0, dot], p];
name ← Rope.Substr[name, dot+1]
ENDLOOP;
IF Rope.Length[name] # 0 THEN AppendFragment[udp, name, p];
AppendFragment[udp, NIL, p]; };
AppendFragment:
PROC [
udp: ArpaNameBuf.Buffer, rope: ROPE, p: Pointer] = {
ptr: CARDINAL ← p.ptr;
chars: CARDINAL ← Rope.Length[rope];
udp.body.bytes[ptr] ← chars;
FOR i:
CARDINAL
IN [0..chars)
DO
udp.body.bytes[ptr+i+1] ← LOOPHOLE[Rope.Fetch[rope, i]];
ENDLOOP;
p.ptr ← p.ptr + chars + 1; };
AppendTwoBytes:
PROC [
udp: ArpaNameBuf.Buffer, data: UNSPECIFIED, p: Pointer] = {
ptr: CARDINAL ← p.ptr;
udp.body.bytes[ptr] ← Basics.HighByte[data];
udp.body.bytes[ptr+1] ← Basics.LowByte[data];
p.ptr ← p.ptr + 2; };
AppendIPAddress:
PROC [
udp: ArpaNameBuf.Buffer, address: Arpa.Address, p: Pointer] = {
ptr: CARDINAL ← p.ptr;
udp.body.bytes[ptr+0] ← address.a;
udp.body.bytes[ptr+1] ← address.b;
udp.body.bytes[ptr+2] ← address.c;
udp.body.bytes[ptr+3] ← address.d;
p.ptr ← p.ptr + 4; };
GetCompressedName:
PROC [udp: ArpaNameBuf.Buffer, p: Pointer]
RETURNS [rope: ROPE] = {
ptr: CARDINAL ← p.ptr;
indirect: CARDINAL ← 300B;
rope ← NIL;
DO
bytes: CARDINAL;
IF ptr >= ArpaNameBuf.maxBodyBytes THEN SIGNAL EndOfData;
bytes ← udp.body.bytes[ptr];
ptr ← ptr + 1;
IF bytes = 0 THEN EXIT;
IF bytes >= indirect
THEN {
-- Indirect link, keep length we have
temp: CARDINAL;
IF ptr >= ArpaNameBuf.maxBodyBytes THEN SIGNAL EndOfData;
temp ← (bytes-indirect)*256 + udp.body.bytes[ptr];
temp ← temp - ArpaNameBuf.hdrBytes;
ptr ← ptr + 1;
p.ptr ← temp;
rope ← Rope.Concat[rope, GetCompressedName[udp, p]];
EXIT; };
FOR i:
CARDINAL
IN [0..bytes)
DO
IF ptr >= ArpaNameBuf.maxBodyBytes THEN SIGNAL EndOfData;
rope ← Rope.Concat[rope, Rope.FromChar[LOOPHOLE[udp.body.bytes[ptr]]]];
ptr ← ptr + 1;
ENDLOOP;
IF ptr >= ArpaNameBuf.maxBodyBytes THEN SIGNAL EndOfData;
IF udp.body.bytes[ptr] # 0 THEN rope ← Rope.Concat[rope, "."];
ENDLOOP;
p.ptr ← ptr; };
GetTtl:
PROC [
udp: ArpaNameBuf.Buffer, p: Pointer] RETURNS [Seconds] = {
RETURN[GetCard[udp, p]]; };
GetCard:
PROC [
udp: ArpaNameBuf.Buffer, p: Pointer] RETURNS [CARD32] = {
ln: Basics.LongNumber;
ln.hi ← GetCardinal[udp, p];
ln.lo ← GetCardinal[udp, p];
RETURN[ln.lc]; };
GetCardinal:
PROC [
udp: ArpaNameBuf.Buffer, p: Pointer] RETURNS [CARDINAL] = {
RETURN[GetTwoBytes[udp, p]]; };
GetTwoBytes:
PROC [
udp: ArpaNameBuf.Buffer, p: Pointer] RETURNS [UNSPECIFIED] = {
ptr: CARDINAL ← p.ptr;
temp: Basics.BytePair;
IF ptr +1 >= ArpaNameBuf.maxBodyBytes THEN SIGNAL EndOfData;
temp.high ← udp.body.bytes[ptr];
temp.low ← udp.body.bytes[ptr+1];
p.ptr ← p.ptr + 2;
RETURN[LOOPHOLE[temp]]; };
GetIPAddress:
PROC [
udp: ArpaNameBuf.Buffer, p: Pointer] RETURNS [a: Arpa.Address] = {
ptr: CARDINAL ← p.ptr;
IF ptr + 3 >= ArpaNameBuf.maxBodyBytes THEN SIGNAL EndOfData;
a.a ← udp.body.bytes[ptr+0];
a.b ← udp.body.bytes[ptr+1];
a.c ← udp.body.bytes[ptr+2];
a.d ← udp.body.bytes[ptr+3];
p.ptr ← p.ptr + 4; };
GetOneByte:
PROC [
udp: ArpaNameBuf.Buffer, p: Pointer] RETURNS [UNSPECIFIED] = {
ptr: CARDINAL ← p.ptr;
temp: Basics.BytePair;
IF ptr >= ArpaNameBuf.maxBodyBytes THEN SIGNAL EndOfData;
temp.high ← 0;
temp.low ← udp.body.bytes[ptr];
p.ptr ← p.ptr + 1;
RETURN[LOOPHOLE[temp]]; };
GetChar:
PROC [
udp: ArpaNameBuf.Buffer, p: Pointer] RETURNS [c: CHAR] = {
ptr: CARDINAL ← p.ptr;
IF ptr >= ArpaNameBuf.maxBodyBytes THEN SIGNAL EndOfData;
c ← LOOPHOLE[udp.body.bytes[ptr]];
p.ptr ← p.ptr + 1;
RETURN[c]; };
GetRope:
PROC [
udp: ArpaNameBuf.Buffer, p: Pointer] RETURNS [rope: Rope.ROPE←NIL] = {
chars: CARDINAL ← GetOneByte[udp, p];
rope ← NIL;
FOR i:
INT
IN [0..chars)
DO
rope ← Rope.Concat[rope, Rope.FromChar[GetChar[udp, p]]];
ENDLOOP;
RETURN[rope]; };
END.