-- 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.