-- File: PupNameServer.mesa, Last Edit: HGM March 17, 1981 1:06 AM DIRECTORY InlineDefs USING [BcplLongNumber, MesaToBcplLongNumber], Mopcodes USING [zEXCH], Process USING [Detach, Yield], String USING [AppendChar, AppendLongNumber], AddressTranslation USING [AppendNetworkAddress], StatsDefs USING [StatCounterIndex, StatIncr, StatGetCounter], BufferDefs USING [OisBuffer], OISCP USING [ReturnFreeOisBuffer, GetOisPacketTextLength, SetOisPacketTextLength], Socket USING [ChannelAborted, ChannelHandle, PutPacket], SpecialSystem USING [HostNumber, NetworkAddress], System USING [NetworkAddress], NameServerDefs USING [ nameStatsRequest, nameStatsReply, whoAmI, CacheEntry, NameStatsEntry, nameVersion, statSend, statHits, statNone, ForceNameIntoCache, SearchCacheForName, ForceAddressIntoCache, SearchCacheForAddress], NetDirDefs USING [maxAddrsPerEntry], PupDefs USING [ GetPupContentsBytes, ReturnPup, ParsePupAddressConstant, PupAddress, PupSocketID, PupBuffer, PupRouterSendThis, ReturnFreePupBuffer, SwapPupSourceAndDest, MoveStringBodyToPupBuffer, AppendStringBodyToPupBuffer]; PupNameServerHot: MONITOR IMPORTS InlineDefs, Process, String, AddressTranslation, StatsDefs, OISCP, Socket, NameServerDefs, PupDefs EXPORTS System, NameServerDefs = BEGIN OPEN StatsDefs, NameServerDefs, PupDefs; busy: PUBLIC BOOLEAN _ FALSE; -- Before OISCPNameServer was added, this didn't need to ba a monitor because only one process called into this module. busy is the only monitor data. statName, statAddress, statWhoAmI, statXlation: PUBLIC StatCounterIndex; statConst, statBusy: PUBLIC StatCounterIndex; PupNameServer: PUBLIC ENTRY PROCEDURE [b: PupBuffer] = BEGIN SELECT b.pupType FROM nameLookup => BEGIN StatIncr[statName]; IF ~busy THEN BEGIN busy _ TRUE; Process.Detach[FORK PupNameLookup[b]]; Process.Yield[]; END ELSE BEGIN StatIncr[statBusy]; ReturnFreePupBuffer[b]; END; END; addressLookup => BEGIN StatIncr[statAddress]; IF ~busy THEN BEGIN busy _ TRUE; Process.Detach[FORK PupAddressLookup[b]]; Process.Yield[]; END ELSE BEGIN StatIncr[statBusy]; ReturnFreePupBuffer[b]; END; END; NameServerDefs.whoAmI => BEGIN StatIncr[statWhoAmI]; IF ~busy THEN BEGIN busy _ TRUE; Process.Detach[FORK PupWhoAmI[b]]; Process.Yield[]; END ELSE BEGIN StatIncr[statBusy]; ReturnFreePupBuffer[b]; END; END; NameServerDefs.nameStatsRequest => NameServerStats[b]; ENDCASE => BEGIN StatIncr[statMouseTrap]; ReturnFreePupBuffer[b]; END; END; PupNameLookup: PROCEDURE [b: PupBuffer] = BEGIN -- This kludgy looking structure is just an easy way of moving what would otherwise be global data into our local frame. oldAddrs, newAddrs: ARRAY [0..NetDirDefs.maxAddrsPerEntry) OF PupAddress; old, new: CARDINAL; InitAddrLists: PROCEDURE = BEGIN -- initialize to a single empty item oldAddrs[0] _ [[0], [0], [0, 0]]; old _ 1; new _ 0; END; CrossPort: PROCEDURE [a: PupAddress] = BEGIN FOR i: CARDINAL IN [0..old) DO b: PupAddress _ oldAddrs[i]; IF ~(a.net = b.net OR a.net = 0 OR b.net = 0) THEN LOOP; IF ~(a.host = b.host OR a.host = 0 OR b.host = 0) THEN LOOP; IF ~(a.socket = b.socket OR a.socket = [0, 0] OR b.socket = [0, 0]) THEN LOOP; -- it got past the filter, add it to the list IF new = NetDirDefs.maxAddrsPerEntry THEN ERROR; IF b.net = 0 THEN b.net _ a.net; IF b.host = 0 THEN b.host _ a.host; IF b.socket = [0, 0] THEN b.socket _ a.socket; newAddrs[new] _ b; new _ new + 1; ENDLOOP; END; ResetAddrLists: PROCEDURE = BEGIN -- flush old, move new to old FOR i: CARDINAL IN [0..new) DO oldAddrs[i] _ newAddrs[i]; ENDLOOP; old _ new; new _ 0; END; ce: CacheEntry; target: LONG POINTER TO ARRAY [0..0) OF PupAddress; length: CARDINAL; i, j: CARDINAL; c: CHARACTER; s: STRING = [50]; a: PupAddress; InitAddrLists[]; length _ GetPupContentsBytes[b]; IF length ~IN (0..200) THEN BEGIN ReturnPupError[b, illegalLength]; RETURN; END; i _ 0; UNTIL i = length DO s.length _ j _ 0; UNTIL i = length DO -- collect a string until we come to a + or the end c _ b.pupChars[i]; i _ i + 1; SELECT c FROM ' => LOOP; -- skip blanks (how did they get this far?) '+ => EXIT; '#, '|, IN ['0..'9] => NULL; -- as in 5#30#123|456 '-, '/, IN ['A..'Z], IN ['a..'z] => NULL; -- MAXC, Maxc, maxc ENDCASE => BEGIN ReturnPupError[b, illegalCharacter]; RETURN; END; IF j = s.maxlength THEN BEGIN ReturnPupError[b, nameTooLong]; RETURN; END; s[j] _ c; j _ j + 1; ENDLOOP; IF j = 0 THEN BEGIN ReturnPupError[b, empty]; RETURN; END; s.length _ j; a _ [[0], [0], [0, 0]]; SELECT TRUE FROM ParsePupAddressConstant[@a, s] => BEGIN CrossPort[a]; StatIncr[statConst]; END; ((ce _ SearchCacheForName[s]) # NIL OR (ce _ ForceNameIntoCache[s]) # NIL) => BEGIN k: CARDINAL; IF LENGTH[ce.addrs] = 0 THEN BEGIN ReturnPupError[b, nameNotFound]; RETURN; END; FOR k IN [0..LENGTH[ce.addrs]) DO CrossPort[ce.addrs[k]]; ENDLOOP; END; ENDCASE => BEGIN -- disk locked out or something ReturnFreePupBuffer[b]; busy _ FALSE; RETURN; END; ResetAddrLists[]; ENDLOOP; IF old = 0 THEN BEGIN ReturnPupError[b, empty]; RETURN; END; target _ LOOPHOLE[@b.pupBody]; FOR i IN [0..old) DO target[i] _ oldAddrs[i]; ENDLOOP; ReturnPup[b, nameIs, 2*old*SIZE[PupAddress]]; busy _ FALSE; END; ErrorCode: TYPE = { illegalLength, illegalCharacter, nameTooLong, empty, nameNotFound, noName}; ReturnPupError: PROCEDURE [b: PupBuffer, e: ErrorCode] = BEGIN s: STRING; SELECT e FROM illegalLength => s _ "Illegal Length"L; illegalCharacter => s _ "Illegal Character"L; nameTooLong => s _ "Name too long"L; nameNotFound => s _ "Name not found"L; noName => s _ "Host not in directory"L; empty => s _ "Inconsistent expression"L; ENDCASE => ERROR; b.pupType _ nameError; MoveStringBodyToPupBuffer[b, s]; SwapPupSourceAndDest[b]; PupRouterSendThis[b]; busy _ FALSE; END; ReturnOiscpError: PROCEDURE [cH: Socket.ChannelHandle, b: BufferDefs.OisBuffer, e: ErrorCode] = BEGIN s: STRING; text: LONG POINTER TO PACKED ARRAY [0..0) OF CHARACTER; text _ LOOPHOLE[@b.ois.oisWords[3]]; SELECT e FROM illegalLength => s _ "Illegal Length"L; illegalCharacter => s _ "Illegal Character"L; nameTooLong => s _ "Name too long"L; nameNotFound => s _ "Name not found"L; noName => s _ "Host not in directory"L; empty => s _ "Inconsistent expression"L; ENDCASE => ERROR; b.ois.oisWords[2] _ 3; -- translationError FOR i: CARDINAL IN [0..s.length) DO text[i] _ s[i]; ENDLOOP; OISCP.SetOisPacketTextLength[b, 2*3+s.length]; b.ois.destination _ b.ois.source; Socket.PutPacket[cH, b ! Socket.ChannelAborted => CONTINUE]; busy _ FALSE; END; PupWhoAmI: PROCEDURE [b: PupBuffer] = BEGIN target: LONG POINTER TO ARRAY [0..0) OF PupAddress; s: STRING = [50]; who: LONG POINTER TO SpecialSystem.HostNumber = LOOPHOLE[@b.pupWords[0]]; ce: CacheEntry; IF GetPupContentsBytes[b] # 6 THEN BEGIN ReturnPupError[b, illegalLength]; RETURN; END; AppendHostNumber[s,who^]; SELECT TRUE FROM (ce _ SearchCacheForName[s]) # NIL => NULL; (ce _ ForceNameIntoCache[s]) # NIL => NULL; ENDCASE => BEGIN -- disk locked out or something ReturnFreePupBuffer[b]; busy _ FALSE; RETURN; END; IF LENGTH[ce.addrs] = 0 THEN BEGIN ReturnPupError[b, nameNotFound]; RETURN; END; target _ LOOPHOLE[@b.pupBody]; FOR i: CARDINAL IN [0..LENGTH[ce.addrs]) DO target[i] _ ce.addrs[i]; ENDLOOP; ReturnPup[b, nameIs, 2*LENGTH[ce.addrs]*SIZE[PupAddress]]; busy _ FALSE; END; OISCPWhoAmI: PROCEDURE [cH: Socket.ChannelHandle, b: BufferDefs.OisBuffer] = BEGIN target: LONG POINTER TO ARRAY [0..0) OF PupAddress; s: STRING = [50]; who: LONG POINTER TO SpecialSystem.HostNumber = LOOPHOLE[@b.ois.oisWords[3]]; ce: CacheEntry; IF OISCP.GetOisPacketTextLength[b] # 6*2 THEN BEGIN ReturnOiscpError[cH, b, illegalLength]; RETURN; END; AppendHostNumber[s,who^]; SELECT TRUE FROM (ce _ SearchCacheForName[s]) # NIL => NULL; (ce _ ForceNameIntoCache[s]) # NIL => NULL; ENDCASE => BEGIN -- disk locked out or something OISCP.ReturnFreeOisBuffer[b]; busy _ FALSE; RETURN; END; IF LENGTH[ce.addrs] = 0 THEN BEGIN ReturnOiscpError[cH, b, nameNotFound]; RETURN; END; b.ois.oisWords[2] _ 2; -- translationResponse target _ LOOPHOLE[@b.ois.oisWords[3]]; FOR i: CARDINAL IN [0..LENGTH[ce.addrs]) DO target[i] _ ce.addrs[i]; ENDLOOP; OISCP.SetOisPacketTextLength[b, 2*(3+3)]; b.ois.destination _ b.ois.source; Socket.PutPacket[cH, b ! Socket.ChannelAborted => CONTINUE]; busy _ FALSE; END; NetworkAddress: PUBLIC TYPE = SpecialSystem.NetworkAddress; AppendHostNumber: PROCEDURE [s: STRING, h: SpecialSystem.HostNumber] = BEGIN temp: STRING = [50]; na: SpecialSystem.NetworkAddress = [[0,0],h,[0]]; first: CARDINAL; AddressTranslation.AppendNetworkAddress[temp,na]; FOR i: CARDINAL IN [0..temp.length) DO first _ i+1; IF temp[i] = '# THEN EXIT; ENDLOOP; FOR i: CARDINAL IN [first..temp.length) DO IF temp[i] = '# THEN EXIT; String.AppendChar[s,temp[i]]; ENDLOOP; END; PupAddressLookup: PROCEDURE [b: PupBuffer] = BEGIN ce: CacheEntry; host, socket: PupAddress _ b.address; host.socket _ [0, 0]; socket.net _ [0]; socket.host _ [0]; IF GetPupContentsBytes[b] # 2*SIZE[PupAddress] THEN BEGIN ReturnPupError[b, illegalLength]; RETURN; END; IF (ce _ FindAddress[b.address]) = NIL THEN GOTO DiskBusy; IF LENGTH[ce.names] # 0 THEN BEGIN -- Check for ambigious name PupDefs.MoveStringBodyToPupBuffer[b, ce.names[0]]; GOTO SendThis; END; IF (ce _ FindAddress[host]) = NIL THEN GOTO DiskBusy; IF LENGTH[ce.names] = 0 THEN BEGIN ReturnPupError[b, nameNotFound]; RETURN; END; PupDefs.MoveStringBodyToPupBuffer[b, ce.names[0]]; -- Check for ambigious name IF socket.socket # [0, 0] THEN BEGIN IF (ce _ FindAddress[socket]) = NIL THEN GOTO DiskBusy; AppendStringBodyToPupBuffer[b, "+"L]; IF LENGTH[ce.names] = 0 THEN BEGIN Flip: PROCEDURE [PupDefs.PupSocketID] RETURNS [LONG INTEGER] = MACHINE CODE BEGIN Mopcodes.zEXCH END; s: STRING = [20]; String.AppendLongNumber[s, Flip[socket.socket], 8]; AppendStringBodyToPupBuffer[b, s]; END ELSE AppendStringBodyToPupBuffer[b, ce.names[0]]; END; GOTO SendThis; EXITS DiskBusy => BEGIN ReturnFreePupBuffer[b]; busy _ FALSE; END; SendThis => BEGIN b.pupType _ addressIs; SwapPupSourceAndDest[b]; PupRouterSendThis[b]; busy _ FALSE; END; END; FindAddress: PROCEDURE [a: PupAddress] RETURNS [ce: CacheEntry] = BEGIN ce _ SearchCacheForAddress[a]; IF ce # NIL THEN RETURN; ce _ ForceAddressIntoCache[a]; END; NameServerStats: PUBLIC PROCEDURE [b: PupBuffer] = BEGIN Stat: PROCEDURE [stat: StatsDefs.StatCounterIndex] RETURNS [InlineDefs.BcplLongNumber] = BEGIN RETURN[InlineDefs.MesaToBcplLongNumber[StatsDefs.StatGetCounter[stat]]]; END; nse: LONG POINTER TO NameStatsEntry _ LOOPHOLE[@b.pupWords]; nse^ _ [version: nameVersion, nameRequests: Stat[statName], directoriesSend: Stat[statSend], cacheHits: Stat[statHits], cacheMisses: Stat[statNone]]; ReturnPup[b, nameStatsReply, 2*SIZE[NameStatsEntry]]; END; -- This lives here so that it can access busy. OISCPNameServer: PUBLIC ENTRY PROCEDURE [cH: Socket.ChannelHandle, b: BufferDefs.OisBuffer] = BEGIN StatIncr[statXlation]; IF ~busy THEN BEGIN busy _ TRUE; Process.Detach[FORK OISCPWhoAmI[cH, b]]; Process.Yield[]; END ELSE BEGIN StatIncr[statBusy]; OISCP.ReturnFreeOisBuffer[b]; END; END; END.