-- File: NameConversion.mesa, Last Edit:
-- MAS Apr 18, 1980 12:38 PM
-- HGM August 3, 1980 5:20 PM
-- Taft June 1, 1983 10:01 AM
-- Copyright Xerox Corporation 1979, 1980
DIRECTORY
Mopcodes: FROM "Mopcodes" USING [zEXCH],
StatsDefs: FROM "StatsDefs" USING [StatIncr],
CommUtilDefs: FROM "CommUtilDefs" USING [
AppendChar, AppendLongNumber, MsecToTicks, SetTimeout],
PupRouterDefs: FROM "PupRouterDefs" USING [
maxHop, PupRoutingTableEntry, GetPupRoutingTableEntry, IfMissing,
probesLeftToDo, probeResponse, routerLock],
DriverDefs: FROM "DriverDefs" USING [doStats, Glitch],
PupStream: FROM "PupStream", -- EXPORTs
PupDefs: FROM "PupDefs" USING [
GetFreePupBuffer, ReturnFreePupBuffer,
DataWordsPerPupBuffer, GetPupContentsBytes,
SetPupContentsWords, SetPupContentsBytes,
UniqueLocalPupAddress,
NameLookupErrorCode, PupAddress, PupBuffer, PupSocket,
PupRouterBroadcastThis,
PupSocketDestroy, PupSocketMake, SecondsToTocks],
PupTypes: FROM "PupTypes" USING [
fillInPupAddress, fillInSocketID, miscSrvSoc, PupSocketID];
NameConversion: MONITOR LOCKS PupRouterDefs.routerLock
IMPORTS CommUtilDefs, StatsDefs, DriverDefs, PupRouterDefs, PupDefs
EXPORTS PupStream, PupDefs =
BEGIN OPEN PupDefs;
PupNameTrouble: PUBLIC ERROR [e: STRING, code: NameLookupErrorCode] = CODE;
StringIsNIL: PUBLIC ERROR = CODE;
StringTooLong: PUBLIC ERROR = CODE;
GetPupAddress: PUBLIC PROCEDURE [a: POINTER TO PupAddress, s: STRING] =
BEGIN
IF ~ParsePupAddressConstant[a,s] THEN PupNameLookup[a,s];
END;
-- Lookup Text string as Name Via Gateways
PupNameLookup: PUBLIC PROCEDURE [a: POINTER TO PupAddress, s: STRING] =
BEGIN
TakeTheFirst: PROCEDURE [him: PupAddress] RETURNS [BOOLEAN] =
BEGIN
rte: PupRouterDefs.PupRoutingTableEntry ←
PupRouterDefs.GetPupRoutingTableEntry[net: him.net, ifMissing: return];
IF rte.network=NIL OR rte.hop>PupRouterDefs.maxHop THEN
RETURN[FALSE]; -- skip unreachable ones
a.net ← him.net;
a.host ← him.host;
IF him.socket#[0,0] THEN a.socket ← him.socket;
RETURN[TRUE];
END;
IF s.length=2 AND s[0]='M AND s[1]='E THEN
BEGIN -- special case hack
me: PupAddress ← UniqueLocalPupAddress[NIL];
a.net ← me.net;
a.host ← me.host;
RETURN;
END;
IF EnumeratePupAddresses[s,TakeTheFirst] THEN RETURN;
ERROR PupNameTrouble["No Route to that Host"L,noRoute];
END;
EnumeratePupAddresses: PUBLIC PROCEDURE [
s: STRING, filter: PROCEDURE [PupAddress] RETURNS [BOOLEAN] ]
RETURNS [hit: BOOLEAN] =
BEGIN
soc: PupSocket;
b: PupBuffer ← NIL;
soc ← PupSocketMake[
PupTypes.fillInSocketID,
PupTypes.fillInPupAddress,
SecondsToTocks[2]];
BEGIN ENABLE UNWIND => -- this is the error exit
BEGIN IF b#NIL THEN ReturnFreePupBuffer[b]; PupSocketDestroy[soc]; END;
THROUGH [0..10) DO -- try a few times
b ← GetFreePupBuffer[];
b.pupType ← nameLookup;
b.dest.socket ← PupTypes.miscSrvSoc;
b.source ← soc.getLocalAddress[];
MoveStringBodyToPupBuffer[b,s];
PupRouterBroadcastThis[b];
b ← NIL; -- In case we get Aborted and then UNWIND
b ← soc.get[]; -- 2 sec wait
IF b#NIL THEN
SELECT b.pupType FROM
nameIs =>
BEGIN
maxAnswers: CARDINAL = 35;
longHop: CARDINAL = PupRouterDefs.maxHop+1;
trialAddress: POINTER TO ARRAY OF PupAddress ← LOOPHOLE[@b.pupWords];
distance: ARRAY [0..maxAnswers) OF CARDINAL ← ALL[longHop];
i, j, howMany, try: CARDINAL;
atLeastOne: BOOLEAN ← FALSE;
howMany ← MIN[maxAnswers,GetPupContentsBytes[b]/(2*SIZE[PupAddress])];
FOR try IN [0..20] DO
-- Even tries just look in the routing table; odd tries initiate probes.
-- The purpose of this dance is to get all the information we can from the
-- existing routing table before initiating probes that might displace
-- useful entries already there. This can happen if the name we are looking
-- up maps to more networks than there are entries in the routing cache.
whatToDo: PupRouterDefs.IfMissing ←
IF try MOD 2 = 0 THEN return ELSE probeAndReturn;
WaitForRouter: ENTRY PROCEDURE =
BEGIN
ENABLE UNWIND => NULL;
WHILE PupRouterDefs.probesLeftToDo#0 DO
WAIT PupRouterDefs.probeResponse; ENDLOOP;
-- Wait some more for trailing packets in multi-packet response
WAIT halfSecond;
END;
allInCache: BOOLEAN ← TRUE;
FOR i IN [0..howMany) DO
IF distance[i]=longHop THEN
BEGIN
route: PupRouterDefs.PupRoutingTableEntry ←
PupRouterDefs.GetPupRoutingTableEntry[
net: trialAddress[i].net, ifMissing: whatToDo];
IF route.network#NIL AND route.hop<longHop
THEN { distance[i] ← route.hop; atLeastOne ← TRUE }
ELSE allInCache ← FALSE;
END;
ENDLOOP;
IF atLeastOne AND (allInCache OR try>=4) THEN EXIT;
IF whatToDo=probeAndReturn THEN WaitForRouter[];
ENDLOOP;
hit ← FALSE;
FOR j IN [0..longHop] UNTIL hit DO
FOR i IN [0..howMany) UNTIL hit DO
IF distance[i]#j THEN LOOP;
hit ← filter[trialAddress[i]];
ENDLOOP;
ENDLOOP;
ReturnFreePupBuffer[b];
PupSocketDestroy[soc];
RETURN;
END;
nameError =>
BEGIN
error: STRING = [50];
AppendPseudoString[error,DESCRIPTOR[@b.pupBytes,GetPupContentsBytes[b]]];
ERROR PupNameTrouble[error,errorFromServer];
END;
ENDCASE=>
BEGIN
IF DriverDefs.doStats THEN StatsDefs.StatIncr[statMouseTrap];
ReturnFreePupBuffer[b];
b ← NIL;
END;
ENDLOOP;
ERROR PupNameTrouble["No name lookup server responded"L,noResponse];
END; -- of ENABLE
END;
-- Parse a string of the form net#host#socket
-- Setup net and host appropiately, Don't change socket if not specified
-- RETURNs TRUE unless can't parse it.
ParsePupAddressConstant: PUBLIC PROCEDURE [a: POINTER TO PupAddress, s: STRING]
RETURNS [BOOLEAN] =
BEGIN
n, h, s1, s2: CARDINAL ← 0;
i: CARDINAL;
c: CHARACTER;
bar: BOOLEAN ← FALSE;
IF s=NIL OR s.length=0 THEN RETURN[FALSE];
FOR i←0,i+1 UNTIL i=s.length DO
SELECT (c←s[i]) FROM
'| =>
BEGIN
IF bar THEN RETURN[FALSE];
bar ← TRUE;
s1←s2; s2←0;
END;
'# =>
BEGIN
IF bar THEN RETURN[FALSE];
IF n#0 OR s1#0 THEN RETURN[FALSE];
n←h; h←s2; s1←s2←0;
END;
IN ['0..'9] =>
BEGIN
IF ~bar THEN s1←s1*8+s2/17777B -- 32 bit number
ELSE IF s2>17777B THEN RETURN[FALSE];
s2 ← s2*8+CARDINAL[c-'0];
END;
ENDCASE => RETURN [FALSE];
ENDLOOP;
IF n ~IN [0..377B] THEN RETURN [FALSE];
IF h ~IN [0..377B] THEN RETURN [FALSE];
a.net ← [n];
a.host ← [h];
IF s1#0 OR s2#0 THEN a.socket ← [s1,s2];
RETURN[TRUE];
END;
-- Inverse of PupNameLookup
PupAddressLookup: PUBLIC PROCEDURE [s: STRING, a: PupAddress] =
BEGIN
soc: PupSocket;
b: PupBuffer ← NIL;
soc ← PupSocketMake[
PupTypes.fillInSocketID,
PupTypes.fillInPupAddress,
SecondsToTocks[2]];
BEGIN ENABLE UNWIND => -- this is the error exit
BEGIN IF b#NIL THEN ReturnFreePupBuffer[b]; PupSocketDestroy[soc]; END;
THROUGH [0..10) DO -- try a few times
b ← GetFreePupBuffer[];
b.pupType ← addressLookup;
b.dest.socket ← PupTypes.miscSrvSoc;
b.source ← soc.getLocalAddress[];
b.address ← a;
SetPupContentsWords[b,SIZE[PupAddress]];
PupRouterBroadcastThis[b];
b ← NIL; -- In case we get Aborted and then UNWIND
b ← soc.get[]; -- 2 sec wait
IF b#NIL THEN
SELECT b.pupType FROM
addressIs =>
BEGIN
AppendPseudoString[s,DESCRIPTOR[@b.pupBytes,GetPupContentsBytes[b]]];
ReturnFreePupBuffer[b];
PupSocketDestroy[soc];
RETURN;
END;
nameError =>
BEGIN
error: STRING = [50];
AppendPseudoString[error,DESCRIPTOR[@b.pupBytes,GetPupContentsBytes[b]]];
ERROR PupNameTrouble[error,errorFromServer];
END;
ENDCASE=>
BEGIN
IF DriverDefs.doStats THEN StatsDefs.StatIncr[statMouseTrap];
ReturnFreePupBuffer[b];
b ← NIL;
END;
ENDLOOP;
ERROR PupNameTrouble["No name lookup server responded"L,noResponse];
END; -- of ENABLE
END;
AppendMyName: PUBLIC PROCEDURE [name: STRING] =
BEGIN
AppendHostName[name,UniqueLocalPupAddress[NIL]];
END;
AppendHostName: PUBLIC PROCEDURE [s: STRING, who: PupAddress] =
BEGIN
who.socket ← [0,0];
PupAddressLookup[s,who !
PupNameTrouble =>
BEGIN
AppendPupAddress[s,who];
CONTINUE;
END ];
END;
Flip: PROCEDURE [PupTypes.PupSocketID] RETURNS [LONG INTEGER] =
MACHINE CODE BEGIN Mopcodes.zEXCH END;
AppendPupAddress: PUBLIC PROCEDURE [s: STRING, a: PupAddress] =
BEGIN
AppendOctal[s,a.net];
CommUtilDefs.AppendChar[s,'#];
AppendOctal[s,a.host];
CommUtilDefs.AppendChar[s,'#];
CommUtilDefs.AppendLongNumber[s,Flip[a.socket],8];
END;
AppendOctal: PROCEDURE [s: STRING, n: CARDINAL] =
BEGIN
IF n>7 THEN AppendOctal[s,n/8];
CommUtilDefs.AppendChar[s,'0+(n MOD 8)];
END;
-- move the body of a mesa STRING into the body of a packet and set its length
MoveStringBodyToPupBuffer: PUBLIC PROCEDURE [b: PupBuffer, s: STRING] =
BEGIN
dataBytesPerPup: CARDINAL ← 2*DataWordsPerPupBuffer[];
i: CARDINAL;
IF s=NIL THEN DriverDefs.Glitch[StringIsNIL];
IF s.length>dataBytesPerPup THEN DriverDefs.Glitch[StringTooLong];
FOR i IN [0..s.length) DO b.pupChars[i] ← s[i]; ENDLOOP;
SetPupContentsBytes[b,s.length];
END;
-- append the body of a mesa STRING into the body of a packet and set its length
AppendStringBodyToPupBuffer: PUBLIC PROCEDURE [b: PupBuffer, s: STRING] =
BEGIN
i, length: CARDINAL;
dataBytesPerPup: CARDINAL ← 2*DataWordsPerPupBuffer[];
IF s=NIL THEN DriverDefs.Glitch[StringIsNIL];
length ← GetPupContentsBytes[b];
IF (s.length+length)>dataBytesPerPup THEN DriverDefs.Glitch[StringTooLong];
FOR i IN [0..s.length) DO
b.pupChars[length+i] ← s[i];
ENDLOOP;
SetPupContentsBytes[b,(s.length+length)];
END;
-- APPEND a pseudo string to a STRING
AppendPseudoString: PROCEDURE [e: STRING, p: LONG DESCRIPTOR FOR PACKED ARRAY OF CHARACTER] =
BEGIN
i: CARDINAL;
FOR i IN [0..MIN[e.maxlength-e.length,LENGTH[p]]) DO
CommUtilDefs.AppendChar[e,p[i]];
ENDLOOP;
END;
halfSecond: CONDITION;
CommUtilDefs.SetTimeout[@halfSecond, CommUtilDefs.MsecToTicks[500]];
END.