-- Registration Server: Implementation of protocols talking to a Registry -- [Juniper]<Grapevine>MS>Registration.mesa -- Randy Gobbel, 19-May-81 12:21:02 -- Jeremy Dion, September 1979 -- Andrew Birrell, 4-Jan-82 13:36:12 DIRECTORY AclDefs USING[ CanOperate ], BodyDefs USING [maxRNameLength, oldestTime, RName, RNameSize, Timestamp], HeapDefs USING [CopyReader, GetReaderOffset, HeapAbandonWrite, HeapEndRead, HeapReadData, HeapReadRName, ObjectOffset, ReaderHandle, SendComponent, SetReaderOffset, WriterHandle ], LogDefs, NameInfoDefs USING[ AuthenticateInfo, AuthenticateKey ], PolicyDefs USING[ CheckOperation, EndOperation ], Process USING[ Detach ], ProtocolDefs, PupDefs USING[ GetPupContentsBytes, PupAddress, PupBuffer, PupSocket, PupSocketMake, ReturnFreePupBuffer, 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, HeapDefs, LogDefs, NameInfoDefs, PolicyDefs, Process, ProtocolDefs, PupDefs, PupStream, RegistryDefs, RegServerDefs, String EXPORTS RegServerDefs --PROGRAM-- = BEGIN LogAction: PROCEDURE[ op: ProtocolDefs.RSOperation, from: PupDefs.PupAddress, name, entry: BodyDefs.RName, rc: ProtocolDefs.ReturnCode ] = BEGIN OPEN ProtocolDefs, String; log: STRING = [192]; 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]; 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.pupType = echoMe AND( mode = all OR ( mode = local AND ProtocolDefs.IsLocal[b.source] ) ) THEN PupDefs.ReturnPup[b, iAmEcho, PupDefs.GetPupContentsBytes[b] ] ELSE PupDefs.ReturnFreePupBuffer[b]; END; mode: {none, local, all} ← none; SetLocal: ENTRY PROC = INLINE { mode ← local }; SetAll: ENTRY PROC = INLINE { mode ← all }; 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.WriteLogEntry["Rejected RS-enquiry connection"L]; ERROR PupStream.RejectThisRequest["Server full"L] END; END; Process.Detach[FORK PollListener[PupDefs.PupSocketMake[ local: ProtocolDefs.RegServerPollingSocket, remote:, ticks: PupDefs.veryLongWait ] ] ]; [] ← PupStream.CreatePupByteStreamListener[ ProtocolDefs.RegServerEnquirySocket, Enquiries, PupStream.SecondsToTocks[60], EnquiryFilter ]; STOP; SetLocal[]; STOP; SetAll[]; END.