-- Copyright (C) 1984 by Xerox Corporation. All rights reserved. -- PupNameServerHot.mesa, HGM, 22-Jan-84 7:34:51 DIRECTORY Format USING [HostNumber], Inline USING [LongCOPY], Mopcodes USING [zEXCH], Process USING [Detach, Yield], String USING [AppendLongNumber, AppendString, WordsForString], System USING [HostNumber], Stats USING [StatCounterIndex, StatIncr, StatGetCounter], Buffer USING [NSBuffer, ReturnBuffer], Socket USING [ ChannelHandle, GetPacketBytes, PutPacket, ReturnBuffer, SetPacketBytes, SetPacketWords], NameServerDefs USING [ nameStatsRequest, nameStatsReply, nameToCacheRequest, addressToCacheRequest, hereIsCacheEntry, CacheEntry, NameStatsEntry, nameVersion, statSend, statHits, statNone, ForceNameIntoCache, ForceAddressIntoCache, SearchCacheForName, SearchCacheForAddress], NetDirDefs USING [maxAddrsPerEntry], PupWireFormat USING [BcplLongNumber, MesaToBcplLongNumber], PupDefs USING [ AppendStringBodyToPupBuffer, GetPupContentsBytes, MoveStringBodyToPupBuffer, ParsePupAddressConstant, PupAddress, PupBuffer, PupRouterSendThis, ReturnPup, PupSocketID, SendPup, SetPupContentsWords, SwapPupSourceAndDest]; PupNameServerHot: MONITOR IMPORTS Format, Inline, Process, String, Stats, Socket, NameServerDefs, Buffer, PupWireFormat, PupDefs EXPORTS NameServerDefs = BEGIN OPEN Stats, NameServerDefs, PupDefs; busy: PUBLIC BOOLEAN ← FALSE; -- Before OISCPNameServer was added, this didn't need to be a monitor because only one process called into this module. busy is the only monitor data. statName, statAddress, statXlation: PUBLIC StatCounterIndex; statConst, statBusy: PUBLIC StatCounterIndex; PupNameServer: PUBLIC ENTRY PROCEDURE [b: PupBuffer] = BEGIN SELECT b.pup.pupType FROM nameLookup => BEGIN StatIncr[statName]; IF ~busy THEN BEGIN busy ← TRUE; Process.Detach[FORK PupNameLookup[b]]; Process.Yield[]; END ELSE BEGIN StatIncr[statBusy]; Buffer.ReturnBuffer[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]; Buffer.ReturnBuffer[b]; END; END; NameServerDefs.nameToCacheRequest => BEGIN IF ~busy THEN BEGIN busy ← TRUE; Process.Detach[FORK NameCacheLookup[b]]; Process.Yield[]; END ELSE BEGIN StatIncr[statBusy]; Buffer.ReturnBuffer[b]; END; END; NameServerDefs.addressToCacheRequest => BEGIN IF ~busy THEN BEGIN busy ← TRUE; Process.Detach[FORK AddressCacheLookup[b]]; Process.Yield[]; END ELSE BEGIN StatIncr[statBusy]; Buffer.ReturnBuffer[b]; END; END; NameServerDefs.nameStatsRequest => NameServerStats[b]; ENDCASE => BEGIN StatIncr[statMouseTrap]; Buffer.ReturnBuffer[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; 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]; SwapPupSourceAndDest[b]; -- Fixup source net number 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.pup.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]]; BEGIN found, notFound: BOOLEAN ← FALSE; CrossAddrs: PROCEDURE [ce: CacheEntry] = BEGIN IF LENGTH[ce.addrs] = 0 THEN BEGIN notFound ← TRUE; RETURN; END; FOR k: CARDINAL IN [0..LENGTH[ce.addrs]) DO CrossPort[ce.addrs[k]]; ENDLOOP; found ← TRUE; END; SELECT TRUE FROM ParsePupAddressConstant[@a, s] => GOTO Constant; SearchCacheForName[s, CrossAddrs] AND found => GOTO CacheHit; notFound => GOTO NotFound; ForceNameIntoCache[s, CrossAddrs] AND found => GOTO CacheHit; notFound => GOTO NotFound; ENDCASE => GOTO Ignore; EXITS Constant => BEGIN CrossPort[a]; StatIncr[statConst]; END; CacheHit => NULL; NotFound => BEGIN ReturnPupError[b, nameNotFound]; RETURN; END; Ignore => BEGIN -- disk locked out or something Buffer.ReturnBuffer[b]; busy ← FALSE; RETURN; END; END; ResetAddrLists[]; ENDLOOP; IF old = 0 THEN BEGIN ReturnPupError[b, empty]; RETURN; END; target ← LOOPHOLE[@b.pup.pupBody]; FOR i: CARDINAL IN [0..old) DO target[i] ← oldAddrs[i]; ENDLOOP; SendPup[b, nameIs, 2*old*SIZE[PupAddress]]; busy ← FALSE; END; NameCacheLookup: PROCEDURE [b: PupBuffer] = BEGIN CopyCacheEntry: PROCEDURE [ce: CacheEntry] = BEGIN MoveCacheEntryToPupBuffer[b, ce]; END; s: STRING = [50]; length: CARDINAL ← GetPupContentsBytes[b]; SwapPupSourceAndDest[b]; -- Fixup source net number IF length ~IN (0..200) THEN BEGIN ReturnPupError[b, illegalLength]; RETURN; END; FOR i: CARDINAL IN [0..length) DO c: CHARACTER ← b.pup.pupChars[i]; SELECT c FROM '#, '|, 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 s.length = s.maxlength THEN BEGIN ReturnPupError[b, nameTooLong]; RETURN; END; s[i] ← c; s.length ← i + 1; ENDLOOP; SELECT TRUE FROM SearchCacheForName[s, CopyCacheEntry] => NULL; ForceNameIntoCache[s, CopyCacheEntry] => NULL; ENDCASE => BEGIN Buffer.ReturnBuffer[b]; busy ← FALSE; RETURN; END; b.pup.pupType ← hereIsCacheEntry; PupRouterSendThis[b]; busy ← FALSE; END; MoveCacheEntryToPupBuffer: PROCEDURE [b: PupBuffer, ce: CacheEntry] = BEGIN p: LONG POINTER ← @b.pup.pupWords; n: CARDINAL ← 0; (p+n)↑ ← 0; -- File version number n ← n + SIZE[CARDINAL]; (p+n)↑ ← LENGTH[ce.names]; n ← n + SIZE[CARDINAL]; FOR i: CARDINAL IN [0..LENGTH[ce.names]) DO words: CARDINAL ← String.WordsForString[ce.names[i].length]; Inline.LongCOPY[to: (p+n), from: ce.names[i], nwords: words]; n ← n + words; ENDLOOP; (p+n)↑ ← LENGTH[ce.addrs]; n ← n + SIZE[CARDINAL]; FOR i: CARDINAL IN [0..LENGTH[ce.addrs]) DO Inline.LongCOPY[to: (p+n), from: @ce.addrs[i], nwords: SIZE[PupAddress]]; n ← n + SIZE[PupAddress]; ENDLOOP; PupDefs.SetPupContentsWords[b, n]; 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.pup.pupType ← nameError; MoveStringBodyToPupBuffer[b, s]; PupRouterSendThis[b]; busy ← FALSE; END; ReturnNSError: PROCEDURE [ cH: Socket.ChannelHandle, b: Buffer.NSBuffer, e: ErrorCode] = BEGIN s: STRING; text: LONG POINTER TO PACKED ARRAY [0..0) OF CHARACTER; text ← LOOPHOLE[@b.ns.nsWords[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.ns.nsWords[2] ← 3; -- translationError FOR i: CARDINAL IN [0..s.length) DO text[i] ← s[i]; ENDLOOP; Socket.SetPacketBytes[b, 2*3 + s.length]; Socket.PutPacket[cH, b]; busy ← FALSE; END; WhoAmI: PROCEDURE [cH: Socket.ChannelHandle, b: Buffer.NSBuffer] = BEGIN target: LONG POINTER TO ARRAY [0..0) OF PupAddress = LOOPHOLE[@b.ns.nsWords[3]]; s: STRING = [50]; who: LONG POINTER TO System.HostNumber = LOOPHOLE[@b.ns.nsWords[3]]; notFound: BOOLEAN ← FALSE; CopyAddrs: PROCEDURE [ce: CacheEntry] = BEGIN IF LENGTH[ce.addrs] = 0 THEN BEGIN notFound ← TRUE; RETURN; END; FOR i: CARDINAL IN [0..LENGTH[ce.addrs]) DO target[i] ← ce.addrs[i]; ENDLOOP; Socket.SetPacketWords[b, 3 + LENGTH[ce.addrs]*SIZE[PupAddress]]; END; IF Socket.GetPacketBytes[b] # 6*2 THEN BEGIN ReturnNSError[cH, b, illegalLength]; RETURN; END; AppendHostNumber[s, who↑]; SELECT TRUE FROM SearchCacheForName[s, CopyAddrs] => NULL; notFound => NULL; ForceNameIntoCache[s, CopyAddrs] => NULL; notFound => NULL; ENDCASE => BEGIN -- disk locked out or something Socket.ReturnBuffer[b]; busy ← FALSE; RETURN; END; IF notFound THEN BEGIN ReturnNSError[cH, b, nameNotFound]; busy ← FALSE; RETURN; END; b.ns.nsWords[2] ← 2; -- translationResponse Socket.PutPacket[cH, b]; busy ← FALSE; END; PupAddressLookup: PROCEDURE [b: PupBuffer] = BEGIN CopyCacheInfo: PROCEDURE [ce: CacheEntry] = BEGIN IF LENGTH[ce.names] = 0 THEN RETURN; hit ← TRUE; PupDefs.MoveStringBodyToPupBuffer[b, ce.names[0]]; END; AppendCacheInfo: PROCEDURE [ce: CacheEntry] = BEGIN IF LENGTH[ce.names] = 0 THEN RETURN; hit ← TRUE; AppendStringBodyToPupBuffer[b, ce.names[0]]; END; hit: BOOLEAN ← FALSE; host, socket: PupAddress ← b.pup.address; host.socket ← [0, 0]; socket.net ← [0]; socket.host ← [0]; SwapPupSourceAndDest[b]; -- Fixup source net number IF GetPupContentsBytes[b] # 2*SIZE[PupAddress] THEN BEGIN ReturnPupError[b, illegalLength]; RETURN; END; SELECT TRUE FROM SearchCacheForAddress[b.pup.address, CopyCacheInfo] => NULL; ForceAddressIntoCache[b.pup.address, CopyCacheInfo] => NULL; ENDCASE => GOTO DiskBusy; IF hit THEN GOTO SendThis; -- Raw address not found, try for something like Maxc+FTP SELECT TRUE FROM SearchCacheForAddress[host, CopyCacheInfo] => NULL; ForceAddressIntoCache[host, CopyCacheInfo] => NULL; ENDCASE => GOTO DiskBusy; IF ~hit THEN BEGIN ReturnPupError[b, nameNotFound]; busy ← FALSE; RETURN; END; AppendStringBodyToPupBuffer[b, "+"L]; hit ← FALSE; SELECT TRUE FROM SearchCacheForAddress[socket, AppendCacheInfo] => NULL; ForceAddressIntoCache[socket, AppendCacheInfo] => NULL; ENDCASE => GOTO DiskBusy; IF ~hit THEN BEGIN -- socket not in table, use number 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; GOTO SendThis; EXITS DiskBusy => BEGIN Buffer.ReturnBuffer[b]; busy ← FALSE; END; SendThis => BEGIN b.pup.pupType ← addressIs; PupRouterSendThis[b]; busy ← FALSE; END; END; AddressCacheLookup: PROCEDURE [b: PupBuffer] = BEGIN CopyCacheEntry: PROCEDURE [ce: CacheEntry] = BEGIN MoveCacheEntryToPupBuffer[b, ce]; END; SwapPupSourceAndDest[b]; -- Fixup source net number IF GetPupContentsBytes[b] # 2*SIZE[PupAddress] THEN BEGIN ReturnPupError[b, illegalLength]; RETURN; END; SELECT TRUE FROM SearchCacheForAddress[b.pup.address, CopyCacheEntry] => NULL; ForceAddressIntoCache[b.pup.address, CopyCacheEntry] => NULL; ENDCASE => BEGIN Buffer.ReturnBuffer[b]; busy ← FALSE; RETURN; END; b.pup.pupType ← hereIsCacheEntry; PupRouterSendThis[b]; busy ← FALSE; END; AppendHostNumber: PROCEDURE [string: LONG STRING, host: System.HostNumber] = BEGIN Append: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] = BEGIN String.AppendString[string, s]; END; Format.HostNumber[Append, host, octal]; END; NameServerStats: PUBLIC PROCEDURE [b: PupBuffer] = BEGIN Stat: PROCEDURE [stat: Stats.StatCounterIndex] RETURNS [PupWireFormat.BcplLongNumber] = BEGIN RETURN[PupWireFormat.MesaToBcplLongNumber[Stats.StatGetCounter[stat]]]; END; nse: LONG POINTER TO NameStatsEntry ← LOOPHOLE[@b.pup.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. NSNameServer: PUBLIC ENTRY PROCEDURE [ cH: Socket.ChannelHandle, b: Buffer.NSBuffer] = BEGIN StatIncr[statXlation]; IF ~busy THEN BEGIN busy ← TRUE; Process.Detach[FORK WhoAmI[cH, b]]; Process.Yield[]; END ELSE BEGIN StatIncr[statBusy]; Socket.ReturnBuffer[b]; END; END; END.