ArpaNameUDPImpl.mesa
John Larson, October 22, 1987 3:53:05 pm PDT
DIRECTORY
Arpa USING [Address, nullAddress],
ArpaNameUDP,
ArpaNameUDPBuf,
ArpaUDP USING [AllocBuffers, Create, Destroy, Error, FreeBuffers, Get, GetRemoteAddress, Handle, Kick, Milliseconds, ReceivedError, Put, SetUserBytes],
ArpaUDPBuf USING [Buffer, Port],
Basics USING [BytePair, HighByte, LongNumber, LowByte],
BasicTime USING [GetClockPulses, Pulses, PulsesToMicroseconds],
Rope USING [Concat, Fetch, Find, FromChar, Length, ROPE, Substr];
ArpaNameUDPImpl: CEDAR PROGRAM
IMPORTS ArpaUDP, Basics, BasicTime, Rope
EXPORTS ArpaNameUDP =
BEGIN OPEN ArpaNameUDP;
ROPE: TYPE = Rope.ROPE;
Error: PUBLIC ERROR [code: ATOM] = CODE;
Codes:
$destUnreachable : raised 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.ROPENIL,
type: QType ← a,
class: QClass ← in,
recurDesired: BOOLFALSE,
port: ArpaUDPBuf.Port ← domainPort,
timeout: ArpaUDP.Milliseconds ← 20000,
retry: CARDINAL ← 0,
acceptErrors: BOOLFALSE] RETURNS [reply: Reply←NIL] = {
{ENABLE {UNWIND => NULL; ArpaUDP.Error => Error[code]; ArpaUDP.ReceivedError => Error[code]};
packetStart: BasicTime.Pulses;
id: CARDINAL ← (sequenceNumber ← sequenceNumber + 1);
done: BOOLFALSE;
sendBuf: ArpaUDPBuf.Buffer;
replyBuf: ArpaUDPBuf.Buffer;
sendNameBuf: ArpaNameUDPBuf.Buffer;
replyNameBuf: ArpaNameUDPBuf.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 + ArpaNameUDPBuf.hdrBytes];
packetStart ← BasicTime.GetClockPulses[];
ArpaUDP.Put[sendBuf];
UNTIL done OR (replyBuf ← ArpaUDP.Get[handle]) = 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.udpLength ← 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: ArpaNameUDPBuf.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: ArpaNameUDPBuf.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;
EXITS
Quit => RETURN;
};
LoadAnswerRRs: PROC [udp: ArpaNameUDPBuf.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: ArpaNameUDPBuf.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: ArpaNameUDPBuf.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: ArpaNameUDPBuf.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;
EXITS
Quit => RETURN;
};
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
];
};
AppendQuery: PROC [
udp: ArpaNameUDPBuf.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: ArpaNameUDPBuf.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: ArpaNameUDPBuf.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: ArpaNameUDPBuf.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: ArpaNameUDPBuf.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: ArpaNameUDPBuf.Buffer, p: Pointer]
RETURNS [rope: ROPE] = {
ptr: CARDINAL ← p.ptr;
indirect: CARDINAL ← 300B;
rope ← NIL;
DO
bytes: CARDINAL;
IF ptr >= ArpaNameUDPBuf.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 >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData;
temp ← (bytes-indirect)*256 + udp.body.bytes[ptr];
temp ← temp - ArpaNameUDPBuf.hdrBytes;
ptr ← ptr + 1;
p.ptr ← temp;
rope ← Rope.Concat[rope, GetCompressedName[udp, p]];
EXIT; };
FOR i: CARDINAL IN [0..bytes) DO
IF ptr >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData;
rope ← Rope.Concat[rope, Rope.FromChar[LOOPHOLE[udp.body.bytes[ptr]]]];
ptr ← ptr + 1;
ENDLOOP;
IF ptr >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData;
IF udp.body.bytes[ptr] # 0 THEN rope ← Rope.Concat[rope, "."];
ENDLOOP;
p.ptr ← ptr; };
longTime: LONG CARDINAL = 10*365*86400;
GetTtl: PROC [
udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [INT] = {
card: LONG CARDINAL ← GetCard[udp, p];
IF card > longTime THEN card ← longTime;
RETURN[card]; };
GetCard: PROC [
udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [LONG CARDINAL] = {
ln: Basics.LongNumber;
ln.hi ← GetCardinal[udp, p];
ln.lo ← GetCardinal[udp, p];
RETURN[ln.lc]; };
GetCardinal: PROC [
udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [CARDINAL] = {
RETURN[GetTwoBytes[udp, p]]; };
GetTwoBytes: PROC [
udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [UNSPECIFIED] = {
ptr: CARDINAL ← p.ptr;
temp: Basics.BytePair;
IF ptr +1 >= ArpaNameUDPBuf.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: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [a: Arpa.Address] = {
ptr: CARDINAL ← p.ptr;
IF ptr + 3 >= ArpaNameUDPBuf.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: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [UNSPECIFIED] = {
ptr: CARDINAL ← p.ptr;
temp: Basics.BytePair;
IF ptr >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData;
temp.high ← 0;
temp.low ← udp.body.bytes[ptr];
p.ptr ← p.ptr + 1;
RETURN[LOOPHOLE[temp]]; };
GetChar: PROC [
udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [c: CHAR] = {
ptr: CARDINAL ← p.ptr;
IF ptr >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData;
c ← LOOPHOLE[udp.body.bytes[ptr]];
p.ptr ← p.ptr + 1;
RETURN[c]; };
GetRope: PROC [
udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [rope: Rope.ROPENIL] = {
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.