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