-- Copyright (C) 1981, 1982, 1983, 1984, 1985 by Xerox Corporation. All rights reserved.
-- Registration.mesa, Registration Server: Implementation of protocols talking to a Registry
-- HGM, 18-Sep-85 3:17:40
-- Randy Gobbel, 19-May-81 12:21:02
-- Jeremy Dion, September 1979
-- Andrew Birrell, 4-Jan-82 13:36:12
-- Mike Schroeder, 25-Jan-83 13:38:54
-- Hankins 13-Aug-84 8:14:53 Klamath update (PupDefs/Buffer)
DIRECTORY
AclDefs USING [CanOperate],
BodyDefs USING [maxRNameLength, oldestTime, RName, RNameSize, Timestamp],
Buffer USING [ReturnBuffer],
Heap USING [systemZone],
HeapDefs USING [
CopyReader, GetReaderOffset, HeapAbandonWrite, HeapEndRead, HeapReadData,
HeapReadRName, ObjectOffset, ReaderHandle, SendComponent, SetReaderOffset,
WriterHandle],
LogDefs USING [ShowRejection, WriteLogEntry],
NameInfoDefs USING [AuthenticateInfo, AuthenticateKey],
PolicyDefs USING [CheckOperation, EndOperation],
Process USING [Detach],
ProtocolDefs,
PupDefs USING [
GetPupContentsBytes, PupAddress, PupBuffer, PupSocket, PupSocketMake,
ReturnPup, veryLongWait],
PupStream USING [
CreatePupByteStreamListener, RejectThisRequest, SecondsToTocks],
RegistryDefs USING [
AddNameToSublist, CompareRNames, CompareTimestamps, EndSublist, Skip,
StartSublist],
RegServerDefs,
String USING [AppendChar, AppendNumber, AppendString, EquivalentStrings];
Registration: MONITOR
IMPORTS
AclDefs, BodyDefs, Buffer, Heap, HeapDefs, LogDefs, NameInfoDefs, PolicyDefs,
Process, ProtocolDefs, PupDefs, PupStream, RegistryDefs, RegServerDefs, String
EXPORTS RegServerDefs =
BEGIN
LogAction: PROCEDURE [
op: ProtocolDefs.RSOperation, from: PupDefs.PupAddress,
name, entry: BodyDefs.RName, rc: ProtocolDefs.ReturnCode] =
BEGIN OPEN ProtocolDefs, String;
log: LONG STRING ← Heap.systemZone.NEW[StringBody[300]];
AppendString[log, "RS op by "L];
AppendNumber[log, from.net, 8];
AppendChar[log, '#];
AppendNumber[log, from.host, 8];
AppendString[log, ", R-Name "];
AppendString[log, name];
AppendString[log, ": "L];
AppendString[
log,
SELECT op FROM
Expand => "Expand"L,
ReadMembers => "Read Members"L,
ReadOwners => "Read Owners"L,
ReadFriends => "Read Friends"L,
ReadEntry => "Read Entry"L,
CheckStamp => "Check Stamp"L,
ReadConnect => "Read Connect-site"L,
ReadRemark => "Read Remark"L,
Authenticate => "Authenticate"L,
CreateRegistry => "Create Registry"L,
DeleteRegistry => "Delete Registry"L,
CreateIndividual => "Create Individual"L,
DeleteIndividual => "Delete Individual"L,
CreateGroup => "Create Group"L,
DeleteGroup => "Delete Group"L,
ChangePassword => "Change Password"L,
ChangeConnect => "Change Connect-site to "L,
ChangeRemark => "Change Remark to "L,
AddMember => "Add Member "L,
AddMailBox => "Add Mailbox "L,
AddForward => "Add Forward "L,
AddOwner => "Add Owner "L,
AddFriend => "Add Friend "L,
DeleteMember => "Remove Member "L,
DeleteMailBox => "Remove Mailbox "L,
DeleteForward => "Remove Forward "L,
DeleteOwner => "Remove Owner "L,
DeleteFriend => "Remove Friend "L,
AddSelf => "Add Self"L,
DeleteSelf => "Remove Self"L,
AddListOfMembers => "Add List Of Members"L,
NewName => "New Name"L,
IdentifyCaller => "Identify Caller"L,
IsMemberDirect => "Is Member Direct"L,
IsOwnerDirect => "Is Owner Direct"L,
IsFriendDirect => "Is Friend Direct"L,
IsMemberClosure => "Is Member Closure"L,
IsOwnerClosure => "Is Owner Closure"L,
IsFriendClosure => "Is Friend Closure"L,
IsInList => "Is In List"L,
ENDCASE => "??Unknown op??"L];
IF op IN [ChangeConnect..DeleteFriend] OR op = NewName
OR op IN [IsMemberDirect..IsInList] THEN AppendString[log, entry];
IF rc.code # done THEN
AppendString[
log,
SELECT rc.code FROM
noChange => ": no change"L,
outOfDate => ": out of date"L,
NotAllowed => ": not allowed"L,
BadOperation => ": bad operation"L,
BadPassword => ": bad password"L,
BadProtocol => ": bad protocol"L,
BadRName => ": bad R-Name"L,
WrongServer => ": wrong server"L,
ENDCASE => ": *** bad return code ***"L];
LogDefs.WriteLogEntry[log];
Heap.systemZone.FREE[@log];
END;
SendDummyPwd: PROC [
reader: HeapDefs.ReaderHandle, str: ProtocolDefs.Handle,
callerKey: ProtocolDefs.Password] =
BEGIN
length: CARDINAL;
stamp: BodyDefs.Timestamp;
pwd: ProtocolDefs.Password;
[] ← HeapDefs.HeapReadData[reader, [@length, SIZE[CARDINAL]]];
IF length # SIZE[BodyDefs.Timestamp] + SIZE[ProtocolDefs.Password] THEN ERROR;
ProtocolDefs.SendCount[str, length];
[] ← HeapDefs.HeapReadData[reader, [@stamp, SIZE[BodyDefs.Timestamp]]];
ProtocolDefs.SendTimestamp[str, stamp];
[] ← HeapDefs.HeapReadData[reader, [@pwd, SIZE[ProtocolDefs.Password]]];
pwd ← ALL[0];
ProtocolDefs.SendPassword[str, callerKey, pwd];
END;
-- These should be in ProtocolDefs --
MembershipGrade: TYPE = RegServerDefs.MembershipGrade; --self,registry--
MembershipAcl: TYPE = RegServerDefs.MembershipAcl; --member,owner,friend--
MembershipLevel: TYPE = RegServerDefs.MembershipLevel; --direct,closure,upA--
ReceiveMembershipGrade: PROC [str: ProtocolDefs.Handle]
RETURNS [MembershipGrade] = INLINE
BEGIN
b: ProtocolDefs.Byte = ProtocolDefs.ReceiveByte[str];
IF b NOT IN
[LOOPHOLE[FIRST[MembershipGrade], ProtocolDefs.Byte]..LOOPHOLE[LAST[
MembershipGrade], ProtocolDefs.Byte]] THEN
ERROR ProtocolDefs.Failed[protocolError]
ELSE RETURN[LOOPHOLE[b]]
END;
ReceiveMembershipAcl: PROC [str: ProtocolDefs.Handle] RETURNS [MembershipAcl] =
INLINE
BEGIN
b: ProtocolDefs.Byte = ProtocolDefs.ReceiveByte[str];
IF b NOT IN
[LOOPHOLE[FIRST[MembershipAcl], ProtocolDefs.Byte]..LOOPHOLE[LAST[
MembershipAcl], ProtocolDefs.Byte]] THEN
ERROR ProtocolDefs.Failed[protocolError]
ELSE RETURN[LOOPHOLE[b]]
END;
ReceiveMembershipLevel: PROC [str: ProtocolDefs.Handle]
RETURNS [MembershipLevel] = INLINE
BEGIN
b: ProtocolDefs.Byte = ProtocolDefs.ReceiveByte[str];
IF b NOT IN
[LOOPHOLE[FIRST[MembershipLevel], ProtocolDefs.Byte]..LOOPHOLE[LAST[
MembershipLevel], ProtocolDefs.Byte]] THEN
ERROR ProtocolDefs.Failed[protocolError]
ELSE RETURN[LOOPHOLE[b]]
END;
ReadTimestamp: PROC [from: HeapDefs.ReaderHandle]
RETURNS [stamp: BodyDefs.Timestamp] = INLINE {
[] ← HeapDefs.HeapReadData[from, [@stamp, SIZE[BodyDefs.Timestamp]]]};
MailSiteSortFailed: ERROR = CODE;
SendMailboxes: PROC [reader: HeapDefs.ReaderHandle, str: ProtocolDefs.Handle] =
BEGIN
-- reader is positioned at start of mailbox site component
-- reader is left positioned at end of mailbox site component
candidate: BodyDefs.RName = [BodyDefs.maxRNameLength];
prevStamp: BodyDefs.Timestamp ← BodyDefs.oldestTime;
candStamp: BodyDefs.Timestamp ← BodyDefs.oldestTime;
stamps: HeapDefs.ReaderHandle = HeapDefs.CopyReader[reader];
nameStart: HeapDefs.ObjectOffset;
nameLength: CARDINAL;
stampStart: HeapDefs.ObjectOffset;
stampLength: CARDINAL;
sentLength: CARDINAL ← 0;
[] ← HeapDefs.HeapReadData[reader, [@nameLength, SIZE[CARDINAL]]];
ProtocolDefs.SendCount[str, nameLength];
nameStart ← HeapDefs.GetReaderOffset[reader];
RegistryDefs.Skip[stamps];
[] ← HeapDefs.HeapReadData[stamps, [@stampLength, SIZE[CARDINAL]]];
stampStart ← HeapDefs.GetReaderOffset[stamps];
DO
ENABLE UNWIND => HeapDefs.HeapEndRead[stamps];
namePos: CARDINAL ← nameLength;
-- skip until we have a candidate --
WHILE namePos > 0 DO
[] ← HeapDefs.HeapReadRName[reader, candidate];
namePos ← namePos - BodyDefs.RNameSize[candidate];
candStamp ← ReadTimestamp[stamps];
IF RegistryDefs.CompareTimestamps[prevStamp, candStamp] = less THEN EXIT;
REPEAT
FINISHED =>
IF sentLength = nameLength THEN EXIT -- from outer loop --
ELSE ERROR MailSiteSortFailed[]
ENDLOOP;
-- look for better candidates --
WHILE namePos > 0 DO
other: BodyDefs.RName = [BodyDefs.maxRNameLength];
otherStamp: BodyDefs.Timestamp = ReadTimestamp[stamps];
[] ← HeapDefs.HeapReadRName[reader, other];
namePos ← namePos - BodyDefs.RNameSize[other];
IF RegistryDefs.CompareTimestamps[prevStamp, otherStamp] = less
AND RegistryDefs.CompareTimestamps[otherStamp, candStamp] = less THEN
BEGIN
-- "other" is a better candidate --
candidate.length ← 0;
String.AppendString[candidate, other];
candStamp ← otherStamp;
END;
ENDLOOP;
-- now "candidate" is oldest name newer than prevStamp --
sentLength ← sentLength + BodyDefs.RNameSize[candidate];
IF sentLength > nameLength THEN ERROR MailSiteSortFailed[];
ProtocolDefs.SendRName[str, candidate];
IF sentLength = nameLength THEN EXIT;
prevStamp ← candStamp;
HeapDefs.SetReaderOffset[reader, nameStart];
HeapDefs.SetReaderOffset[stamps, stampStart];
ENDLOOP;
HeapDefs.HeapEndRead[stamps];
END;
Operate: PROCEDURE [
str: ProtocolDefs.Handle, from: PupDefs.PupAddress, type: {enquiry, update},
caller: BodyDefs.RName] =
BEGIN
wizard: BOOLEAN ← FALSE;
DO
BEGIN
callerKey: ProtocolDefs.Password ← [0, 0, 0, 0];
op: ProtocolDefs.RSOperation;
name: BodyDefs.RName = [BodyDefs.maxRNameLength];
entry: STRING = [
MAX[BodyDefs.maxRNameLength, ProtocolDefs.maxConnectLength]];
password: ProtocolDefs.Password;
stamp: BodyDefs.Timestamp;
rc: ProtocolDefs.ReturnCode;
membership: RegServerDefs.Membership;
reader: HeapDefs.ReaderHandle ← NIL;
nameCount: CARDINAL ← 0;
components: CARDINAL;
pwdPos: CARDINAL;
writer: HeapDefs.WriterHandle ← NIL;
membersOutOfOrder: BOOLEAN ← FALSE; -- failure in AddListOfMembers --
DO
op ← ProtocolDefs.ReceiveRSOperation[
str !
ProtocolDefs.Failed =>
IF why = noData AND ProtocolDefs.IsLocal[from] THEN RETRY];
IF op # NoOp THEN EXIT;
ENDLOOP;
ProtocolDefs.ReceiveRName[str, name];
entry.length ← 0;
SELECT op FROM
IN [Expand..CheckStamp] => stamp ← ProtocolDefs.ReceiveTimestamp[str];
IN [AddMember..DeleteFriend], NewName, IN [IsMemberDirect..IsInList] =>
ProtocolDefs.ReceiveRName[str, entry];
CreateIndividual, ChangePassword, Authenticate, IdentifyCaller =>
password ← ProtocolDefs.ReceivePassword[str, callerKey];
ChangeConnect => ProtocolDefs.ReceiveConnect[str, entry]; --PUN--
ChangeRemark => ProtocolDefs.ReceiveRemark[str, entry]; --PUN--
AddListOfMembers =>
BEGIN
Work: PROC [member: BodyDefs.RName] =
BEGIN
IF RegistryDefs.CompareRNames[entry, member] # less THEN
membersOutOfOrder ← TRUE;
entry.length ← 0;
String.AppendString[entry, member];
RegistryDefs.AddNameToSublist[writer, member];
nameCount ← nameCount + 1;
END;
writer ← RegistryDefs.StartSublist[];
ProtocolDefs.ReceiveRList[
str, Work ! UNWIND => HeapDefs.HeapAbandonWrite[writer]];
reader ← RegistryDefs.EndSublist[writer, nameCount];
writer ← NIL;
END;
ENDCASE;
IF NOT wizard
AND
(( -- not allowable as implicit AddSelf or RemoveSelf --
(op # AddMember AND op # DeleteMember) -- implicit AddSelf or RemoveSelf --
OR NOT String.EquivalentStrings[caller, entry]
OR AclDefs.CanOperate[
op: IF op = AddMember THEN AddSelf ELSE DeleteSelf, entry: name,
caller: caller] # yes)
AND AclDefs.CanOperate[op: op, entry: name, caller: caller] # yes)
THEN
BEGIN
IF reader # NIL THEN HeapDefs.HeapEndRead[reader];
reader ← NIL;
rc ← [NotAllowed, notFound];
END
ELSE
SELECT op FROM
Expand => [reader, rc] ← RegServerDefs.Expand[name, @stamp];
ReadMembers => [reader, rc] ← RegServerDefs.ReadMembers[name, @stamp];
ReadOwners => [reader, rc] ← RegServerDefs.ReadOwners[name, @stamp];
ReadFriends => [reader, rc] ← RegServerDefs.ReadFriends[name, @stamp];
ReadEntry =>
[reader, rc, components, pwdPos] ← RegServerDefs.Read[name];
CheckStamp => [rc] ← RegServerDefs.CheckRName[name, @stamp];
ReadConnect => [rc] ← RegServerDefs.ReadConnect[name, entry];
ReadRemark => [rc] ← RegServerDefs.ReadRemark[name, entry];
Authenticate, IdentifyCaller =>
BEGIN
actual: ProtocolDefs.Password;
[actual, rc] ← RegServerDefs.ReadPassword[name];
SELECT TRUE FROM
rc.code = done =>
IF actual # password OR actual = [0, 0, 0, 0] THEN
rc.code ← BadPassword;
rc.code = WrongServer AND op = IdentifyCaller =>
BEGIN
-- doing this for op=Authenticate would provoke a
-- deadlock, as we might be the first place tried by
-- our NameInfo package.
info: NameInfoDefs.AuthenticateInfo =
NameInfoDefs.AuthenticateKey[name, password];
SELECT info FROM
group => rc ← [BadRName, group];
individual => rc ← [done, individual];
notFound => rc ← [BadRName, notFound];
allDown => rc ← [AllDown, notFound];
badPwd => rc ← [BadPassword, individual];
ENDCASE => ERROR;
END;
ENDCASE => NULL;
IF op = IdentifyCaller AND rc.code = done THEN
BEGIN
caller.length ← 0;
String.AppendString[caller, name];
callerKey ← actual;
type ← update;
END;
END;
CreateIndividual =>
[rc] ← RegServerDefs.CreateIndividual[name, password];
DeleteIndividual => [rc] ← RegServerDefs.DeleteIndividual[name];
CreateGroup => [rc] ← RegServerDefs.CreateGroup[name, caller];
DeleteGroup => [rc] ← RegServerDefs.DeleteGroup[name];
ChangePassword => [rc] ← RegServerDefs.ChangePassword[name, password];
ChangeConnect => [rc] ← RegServerDefs.ChangeConnect[name, entry];
ChangeRemark => [rc] ← RegServerDefs.ChangeRemark[name, entry];
AddMember => [rc] ← RegServerDefs.AddMember[name, entry];
AddMailBox => [rc] ← RegServerDefs.AddMailbox[name, entry];
AddForward => [rc] ← RegServerDefs.AddForward[name, entry];
AddOwner => [rc] ← RegServerDefs.AddOwner[name, entry];
AddFriend => [rc] ← RegServerDefs.AddFriend[name, entry];
AddSelf => [rc] ← RegServerDefs.AddMember[name, caller];
DeleteMember => [rc] ← RegServerDefs.DeleteMember[name, entry];
DeleteMailBox => [rc] ← RegServerDefs.DeleteMailbox[name, entry];
DeleteForward => [rc] ← RegServerDefs.DeleteForward[name, entry];
DeleteOwner => [rc] ← RegServerDefs.DeleteOwner[name, entry];
DeleteFriend => [rc] ← RegServerDefs.DeleteFriend[name, entry];
DeleteSelf => [rc] ← RegServerDefs.DeleteMember[name, caller];
AddListOfMembers =>
BEGIN
IF membersOutOfOrder THEN
BEGIN HeapDefs.HeapEndRead[reader]; rc ← [BadProtocol, group] END
ELSE rc ← RegServerDefs.AddListOfMembers[name, reader];
reader ← NIL;
END;
NewName => rc ← RegServerDefs.NewName[old: entry, new: name];
IsMemberDirect =>
[membership, rc] ← RegServerDefs.IsMember[name, entry, direct];
IsOwnerDirect =>
[membership, rc] ← RegServerDefs.IsOwner[name, entry, direct];
IsFriendDirect =>
[membership, rc] ← RegServerDefs.IsFriend[name, entry, direct];
IsMemberClosure =>
[membership, rc] ← RegServerDefs.IsMember[name, entry, closure];
IsOwnerClosure =>
[membership, rc] ← RegServerDefs.IsOwner[name, entry, closure];
IsFriendClosure =>
[membership, rc] ← RegServerDefs.IsFriend[name, entry, closure];
IsInList =>
BEGIN
grade: MembershipGrade = ReceiveMembershipGrade[str];
acl: MembershipAcl = ReceiveMembershipAcl[str];
level: MembershipLevel = ReceiveMembershipLevel[str];
[membership, rc] ← RegServerDefs.IsInList[
name, entry, level, grade, acl];
END;
ENDCASE => rc ← [BadOperation, notFound];
ProtocolDefs.SendRC[str, rc];
IF rc.code = done THEN
SELECT op FROM
IN [Expand..ReadFriends] =>
BEGIN
ENABLE UNWIND => HeapDefs.HeapEndRead[reader];
ProtocolDefs.SendTimestamp[str, stamp];
IF rc.type = individual THEN SendMailboxes[reader, str]
ELSE HeapDefs.SendComponent[reader, str];
HeapDefs.HeapEndRead[reader];
END;
ReadEntry =>
BEGIN
ENABLE UNWIND => HeapDefs.HeapEndRead[reader];
ProtocolDefs.SendTimestamp[str, BodyDefs.oldestTime];
ProtocolDefs.SendCount[str, components];
IF rc.type = individual THEN
BEGIN
callerIsRServer: BOOLEAN = (type = update)
AND
(RegServerDefs.IsMember["*.gv"L, caller, direct].membership =
yes);
HeapDefs.SendComponent[reader, str]; --prefix--
IF callerIsRServer THEN HeapDefs.SendComponent[reader, str]
ELSE SendDummyPwd[reader, str, callerKey];
HeapDefs.SendComponent[reader, str]; --connect--
THROUGH [1..4] DO
HeapDefs.SendComponent[reader, str]; --forward--
ENDLOOP;
IF callerIsRServer THEN HeapDefs.SendComponent[reader, str]
ELSE SendMailboxes[reader, str];
THROUGH [2..4] DO
HeapDefs.SendComponent[reader, str]; --mailboxes--
ENDLOOP;
END
ELSE -- group, dead --
FOR component: CARDINAL IN [0..components) DO
HeapDefs.SendComponent[reader, str]; ENDLOOP;
HeapDefs.HeapEndRead[reader];
END;
CheckStamp => ProtocolDefs.SendTimestamp[str, stamp];
ReadConnect => ProtocolDefs.SendConnect[str, entry];
ReadRemark => ProtocolDefs.SendRemark[str, entry];
IN [IsMemberDirect..IsInList] =>
ProtocolDefs.SendBoolean[str, membership = yes];
ENDCASE;
IF op NOT IN [Expand..Authenticate] AND op NOT IN [IsMemberDirect..IsInList]
THEN LogAction[op, from, name, entry, rc];
END;
ProtocolDefs.SendNow[str];
ENDLOOP;
END;
Enquiries: PROCEDURE [str: ProtocolDefs.Handle, from: PupDefs.PupAddress] =
BEGIN
ENABLE ProtocolDefs.Failed => GOTO abort;
caller: BodyDefs.RName = [BodyDefs.maxRNameLength];
Operate[str: str, from: from, type: enquiry, caller: caller];
ERROR;
EXITS
abort =>
BEGIN
ProtocolDefs.DestroyStream[str];
IF NOT ProtocolDefs.IsLocal[from] THEN PolicyDefs.EndOperation[regExpand];
END;
END;
PollListener: PROCEDURE [socket: PupDefs.PupSocket] =
BEGIN
DO
b: PupDefs.PupBuffer = socket.get[];
IF b # NIL THEN ConsiderPoll[b];
ENDLOOP;
END;
ConsiderPoll: --ENTRY-- PROC [b: PupDefs.PupBuffer] = INLINE
BEGIN
IF b.pup.pupType = echoMe
AND (mode = all OR (mode = local AND ProtocolDefs.IsLocal[b.pup.source]))
THEN PupDefs.ReturnPup[b, iAmEcho, PupDefs.GetPupContentsBytes[b]]
ELSE Buffer.ReturnBuffer[b];
END;
mode: {none, local, all} ← none;
EnquiryFilter: ENTRY PROC [from: PupDefs.PupAddress] =
BEGIN
ENABLE UNWIND => NULL;
localClient: BOOLEAN = ProtocolDefs.IsLocal[from];
IF mode = none OR (mode = local AND NOT localClient) THEN
ERROR PupStream.RejectThisRequest["Server restarting"L]
ELSE
IF NOT localClient AND NOT PolicyDefs.CheckOperation[regExpand] THEN
BEGIN
LogDefs.ShowRejection["RS-enquiry", from]; -- No L
ERROR PupStream.RejectThisRequest["Server full"L]
END;
END;
RegistrationInit: PUBLIC PROCEDURE =
BEGIN
Process.Detach[
FORK PollListener[
PupDefs.PupSocketMake[
local: ProtocolDefs.RegServerPollingSocket, remote:,
ticks: PupDefs.veryLongWait]]];
[] ← PupStream.CreatePupByteStreamListener[
ProtocolDefs.RegServerEnquirySocket, Enquiries, PupStream.SecondsToTocks[
60], EnquiryFilter];
END;
RegistrationLocal: PUBLIC ENTRY PROCEDURE = {mode ← local};
RegistrationAll: PUBLIC ENTRY PROCEDURE = {mode ← all};
END.
13-Aug-84 8:13:19: rework to remove STOPs - blh