-- Copyright (C) 1984 by Xerox Corporation. All rights reserved.
-- PupNameServerHot.mesa, HGM, 3-Jun-86 12:49:27
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.