<> <> <> <> <> <> DIRECTORY BasicTime USING [earliestGMT, Now, Period], Convert USING [RopeFromInt], GVBasics USING [Connect, MakeKey, maxRNameLength, Password, RName], GVNames USING [ConnectInfo, GetConnect, Outcome, SetConnect], PrincOpsUtils USING [LongCopy], Pup USING [Address, allHosts, nullAddress, nullSocket], PupName USING [Error, NameLookup], Rope USING [Cat, ROPE, Fetch, Find, Flatten, Length, Text], RPC USING [AuthenticateFailure, CallFailed, CallFailure, EncryptionKey, ExportFailed, ExportFailure, ImportFailed, ImportFailure, InterfaceName, matchAllVersions, Principal, VersionRange], RPCInternal USING [ExportInstance, ExportTable, ImportInstance], RPCPkt USING [DispatcherDetails, DispatcherID, ExportHandle, Header, Machine, noDispatcher], RPCLupine USING [Call, Dispatcher, GetStubPkt, pktOverhead, StartCall, StubPkt], RPCLupineExtras USING []; RPCBinding: MONITOR IMPORTS BasicTime, Convert, GVBasics, GVNames, PrincOpsUtils, PupName, Rope, RPC, RPCLupine EXPORTS RPC, RPCInternal, RPCLupine, RPCLupineExtras = { <> AuthenticateFailed: PUBLIC SAFE ERROR[why: RPC.AuthenticateFailure] = CODE; CallFailed: PUBLIC SAFE SIGNAL[why: RPC.CallFailure] = CODE; ExportFailed: PUBLIC SAFE ERROR[why: RPC.ExportFailure] = CODE; ImportFailed: PUBLIC SAFE ERROR[why: RPC.ImportFailure] = CODE; LongEquivalent: PROC [a: LONG STRING, b: Rope.ROPE] RETURNS [BOOL] = { IF a.length # Rope.Length[b] THEN RETURN[FALSE]; FOR i: CARDINAL IN [0..a.length) DO ac: CHAR = a[i]; bc: CHAR = Rope.Fetch[b, i]; IF ac # bc THEN { au: CHAR = IF ac IN ['a..'z] THEN 'A + (ac-'a) ELSE ac; bu: CHAR = IF bc IN ['a..'z] THEN 'A + (bc-'a) ELSE bc; IF au # bu THEN RETURN[FALSE]; }; ENDLOOP; RETURN[TRUE]; }; CopyRope: PROC [from: Rope.ROPE, to: LONG POINTER] = { flat: Rope.Text = Rope.Flatten[from]; text: LONG POINTER TO TEXT = LOOPHOLE[flat]; PrincOpsUtils.LongCopy[from: LOOPHOLE[text], to: to, nwords: SIZE[StringBody[text.length]]]; }; InstanceInfo: TYPE = { ok, badName, allDown, noAddress }; LocateInstance: PROC [instance: Rope.ROPE] RETURNS [info: InstanceInfo, isRName: BOOL, host: RPCPkt.Machine] = { connect: Rope.ROPE _ NIL; IF instance.Length[] > GVBasics.maxRNameLength THEN RETURN[badName,,]; isRName _ Rope.Find[instance, "."] >= 0; IF isRName THEN { info: GVNames.ConnectInfo; [info, connect] _ GVNames.GetConnect[instance]; SELECT info FROM individual => NULL; notFound, group => RETURN[badName,isRName,]; allDown => RETURN[allDown,isRName,]; ENDCASE => ERROR; } ELSE connect _ instance; IF connect.Length[] = 0 THEN info _ IF isRName THEN noAddress ELSE badName ELSE { addr: Pup.Address; info _ ok; addr _ PupName.NameLookup[connect, Pup.nullSocket ! PupName.Error => IF code = errorFromServer THEN { info _ IF isRName THEN noAddress ELSE badName; CONTINUE; } ELSE { info _ allDown; CONTINUE; } ]; host _ [net: addr.net, host: addr.host]; }; }; <<******** Binding primitives ********>> ExportHandle: PUBLIC TYPE = RPCPkt.ExportHandle; exportTable: PUBLIC REF RPCInternal.ExportTable _ NEW[RPCInternal.ExportTable[20] _ [used:0, entries:] ]; <> <> <> <> <> <> <> MakeKey: PUBLIC SAFE PROC [text: Rope.ROPE] RETURNS [ RPC.EncryptionKey ] = CHECKED { RETURN[GVBasics.MakeKey[text]] }; ExportInterface: PUBLIC PROC [user: RPC.Principal, password: RPC.EncryptionKey, interface: RPC.InterfaceName, dispatcher: RPCLupine.Dispatcher, stubProtocol: RPC.VersionRange, localOnly: BOOL _ FALSE] RETURNS [ instance: ExportHandle ] = { IF interface.type = NIL THEN ERROR RPC.ExportFailed[badType]; IF interface.version # RPC.matchAllVersions AND interface.version.first > interface.version.last THEN ERROR RPC.ExportFailed[badVersion]; instance _ AddToExportTable[interface, dispatcher, stubProtocol]; IF ~localOnly AND interface.instance # NIL THEN { ENABLE UNWIND => [] _ UnexportInterface[instance]; prevHost: RPCPkt.Machine; prevInfo: InstanceInfo; isRName: BOOL; [prevInfo, isRName, prevHost] _ LocateInstance[interface.instance]; SELECT prevInfo FROM ok => IF prevHost = myHost THEN RETURN; badName => ERROR RPC.ExportFailed[badInstance]; allDown => ERROR RPC.ExportFailed[communications]; noAddress => NULL; ENDCASE => ERROR; IF isRName THEN { outcome: GVNames.Outcome; IF Rope.Length[user] > GVBasics.maxRNameLength THEN ERROR RPC.ExportFailed[badCredentials]; outcome _ GVNames.SetConnect[user, LOOPHOLE[password], interface.instance, Rope.Cat[ Convert.RopeFromInt[myHost.net, 8, FALSE], "#", Convert.RopeFromInt[myHost.host, 8, FALSE], "#"] ]; SELECT outcome FROM individual, noChange => NULL; group, notFound => ERROR RPC.ExportFailed[badInstance]; badPwd, notAllowed => ERROR RPC.ExportFailed[badCredentials]; allDown => ERROR RPC.ExportFailed[communications]; ENDCASE => ERROR; } ELSE ERROR RPC.ExportFailed[badInstance]; }; }; lastExportID: RPCPkt.DispatcherID _ BasicTime.Period[from: BasicTime.earliestGMT, to: BasicTime.Now[]]; <> AddToExportTable: PUBLIC ENTRY PROC [interface: RPC.InterfaceName, dispatcher: RPCLupine.Dispatcher, stubProtocol: RPC.VersionRange] RETURNS [ instance: ExportHandle ] = { FOR instance IN [0..exportTable.used) DO IF exportTable[instance].id = RPCPkt.noDispatcher THEN EXIT; REPEAT FINISHED => IF exportTable.used = exportTable.length THEN RETURN WITH ERROR RPC.ExportFailed[tooMany] ELSE { instance _ exportTable.used; exportTable.used _ exportTable.used+1 } ENDLOOP; exportTable[instance] _ [lastExportID _ lastExportID+1, dispatcher, interface, stubProtocol]; }; UnexportInterface: PUBLIC ENTRY PROC [ instance: ExportHandle ] RETURNS [ExportHandle] = { IF exportTable[instance].id # RPCPkt.noDispatcher AND instance # binderHint THEN exportTable[instance] _ [RPCPkt.noDispatcher,NIL,[NIL,NIL,],]; RETURN[instance]; }; <
> binderID: RPCPkt.DispatcherID = SUCC[RPCPkt.noDispatcher]; binderHint: ExportHandle = 0; binderProc: CARDINAL = 0; BinderResult: TYPE = MACHINE DEPENDENT RECORD[ stubProtocol: RPC.VersionRange, version: RPC.VersionRange, dispatcher: RPCPkt.DispatcherDetails]; BinderArgs: TYPE = MACHINE DEPENDENT RECORD[ request(0): CARDINAL, type(1): CARDINAL, -- offset in pkt, 0 => NIL -- instance(2): CARDINAL -- offset in pkt, 0 => NIL -- -- followed by the StringBody values for type, instance --]; -- Server-stub for binding calls -- Binder: RPCLupine.Dispatcher = { PktString: PROC [n: CARDINAL] RETURNS [ LONG STRING ] = { RETURN[ IF n+SIZE[StringBody[0]] NOT IN [SIZE[BinderArgs]+SIZE[StringBody[0]]..callLength] THEN NIL ELSE LOOPHOLE[@pkt[n]] ] }; args: LONG POINTER TO BinderArgs = LOOPHOLE[@pkt.data]; result: LONG POINTER TO BinderResult = LOOPHOLE[@pkt.data]; header: LONG POINTER TO RPCPkt.Header = LOOPHOLE[@pkt.header]; SELECT args.request FROM binderProc => result^ _ LocalBind[PktString[args.type], PktString[args.instance]]; ENDCASE => NULL -- ??--; IF result.dispatcher.dispatcherID = RPCPkt.noDispatcher AND header.destHost.host = Pup.allHosts THEN ERROR RPC.CallFailed[unbound]; -- Don't complain about failing requests that were broadcast RETURN[SIZE[BinderResult]]; }; -- Server-implementation for binding calls -- LocalBind: ENTRY PROC [type, instance: LONG STRING] RETURNS [BinderResult] = { FOR i: CARDINAL IN [1..exportTable.used) DO IF exportTable[i].id # RPCPkt.noDispatcher AND (type = NIL OR LongEquivalent[type, exportTable[i].name.type]) AND (instance = NIL OR exportTable[i].name.instance = NIL OR LongEquivalent[instance, exportTable[i].name.instance]) THEN RETURN[[exportTable[i].stubProtocol, exportTable[i].name.version, [1, exportTable[i].id, i]]]; ENDLOOP; RETURN[[,,[,RPCPkt.noDispatcher,]]] }; RemoteBind: SAFE PROC [host: RPCPkt.Machine, type, instance: Rope.ROPE] RETURNS [BinderResult] = TRUSTED { <> binderInterface: REF ImportInstance = NEW[ImportInstance _ [host,[,binderID,binderHint]]]; argSize: CARDINAL = SIZE[BinderArgs]; pktSize: CARDINAL = MAX[argSize+2*SIZE[StringBody[GVBasics.maxRNameLength]], SIZE[BinderResult] ]; pktData: ARRAY [0..RPCLupine.pktOverhead+pktSize) OF WORD; pkt: RPCLupine.StubPkt = RPCLupine.GetStubPkt[@pktData]; args: POINTER TO BinderArgs = LOOPHOLE[@pkt.data]; resultLength: CARDINAL; IF Rope.Length[type] > GVBasics.maxRNameLength OR Rope.Length[instance] > GVBasics.maxRNameLength THEN ERROR RPC.ImportFailed[unbound]; RPCLupine.StartCall[pkt, binderInterface]; { used: CARDINAL; args^ _ [binderProc, 0, 0]; used _ SIZE[BinderArgs]; IF type # NIL THEN { args.type _ used; CopyRope[from: type, to: @pkt[used]]; used _ used + SIZE[StringBody[type.Length[]]]; }; IF instance # NIL THEN { args.instance _ used; CopyRope[from: instance, to: @pkt[used]]; used _ used + SIZE[StringBody[instance.Length[]]]; }; IF used > pktSize THEN ERROR; [resultLength,] _ RPCLupine.Call[pkt, used, pktSize]; }; RETURN[LOOPHOLE[@pkt.data, POINTER TO BinderResult]^]; }; ImportInstance: PUBLIC TYPE = RPCInternal.ImportInstance; debugRemoteBind: BOOL _ FALSE; LocateImportInstance: PROC [instance: Rope.ROPE] RETURNS [host: RPCPkt.Machine] = { info: InstanceInfo; isRName: BOOL; [info, isRName, host] _ LocateInstance[instance]; SELECT info FROM ok => NULL; badName => ERROR RPC.ImportFailed[badInstance]; allDown => ERROR RPC.ImportFailed[communications]; noAddress => ERROR RPC.ImportFailed[unbound]; ENDCASE => ERROR; }; ImportInterface: PUBLIC PROC [interface: RPC.InterfaceName, stubProtocol: RPC.VersionRange, localOnly: BOOL _ FALSE] RETURNS [ handle: REF ImportInstance ] = { host: RPCPkt.Machine; dispatcher: RPCPkt.DispatcherDetails; IF interface.type = NIL THEN ERROR RPC.ExportFailed[badType]; IF debugRemoteBind AND NOT localOnly THEN { host _ LocateImportInstance[interface.instance]; dispatcher _ TryBinding[host, interface, stubProtocol]; } ELSE { host _ LocateImportInstance[interface.instance]; IF localOnly AND host # myHost THEN ERROR RPC.ImportFailed[unbound] ELSE dispatcher _ TryBinding[host, interface, stubProtocol]; }; RETURN[NEW[ImportInstance _ [host, dispatcher]]] }; TryBinding: PROC [host: RPCPkt.Machine, impName: RPC.InterfaceName, stubProtocol: RPC.VersionRange] RETURNS [RPCPkt.DispatcherDetails] = CHECKED { expDetails: BinderResult; expDetails _ RemoteBind[host, impName.type, impName.instance ! RPC.CallFailed => IF why = busy THEN RETRY ELSE ERROR RPC.ImportFailed[communications] ]; IF expDetails.dispatcher.dispatcherID = RPCPkt.noDispatcher THEN ERROR RPC.ImportFailed[unbound]; IF stubProtocol # RPC.matchAllVersions AND expDetails.stubProtocol # RPC.matchAllVersions AND ( stubProtocol.first > expDetails.stubProtocol.last OR stubProtocol.last < expDetails.stubProtocol.first ) THEN ERROR RPC.ImportFailed[stubProtocol]; IF impName.version # RPC.matchAllVersions AND impName.version.first > impName.version.last THEN ERROR RPC.ImportFailed[badVersion]; IF impName.version # RPC.matchAllVersions AND expDetails.version # RPC.matchAllVersions AND ( impName.version.first > expDetails.version.last OR impName.version.last < expDetails.version.first ) THEN ERROR RPC.ImportFailed[wrongVersion]; RETURN[expDetails.dispatcher]; }; ImportInterfaceWithHost: PUBLIC PROC [ -- Exported to RPCLupineExtras, for now interface: RPC.InterfaceName, stubProtocol: RPC.VersionRange, hostHint: Pup.Address _ Pup.nullAddress, localOnly: BOOLEAN _ FALSE] RETURNS [handle: REF ImportInstance] = { machneHint: RPCPkt.Machine _ [hostHint.net, hostHint.host]; dispatcher: RPCPkt.DispatcherDetails; IF hostHint = Pup.nullAddress THEN GOTO DefaultToStandard; IF localOnly AND machneHint # myHost THEN ERROR RPC.ImportFailed[unbound]; dispatcher _ TryBinding[machneHint, interface, stubProtocol! RPC.ImportFailed => IF why = $unbound THEN GOTO DefaultToStandard]; RETURN[NEW[ImportInstance _ [machneHint, dispatcher]]]; EXITS DefaultToStandard => RETURN[ImportInterface[interface, stubProtocol, localOnly]]; }; UnimportInterface: PUBLIC PROC [handle: REF ImportInstance] RETURNS [REF ImportInstance] = { RETURN[NIL]; }; myHost: RPCPkt.Machine; -- ******** Initialization ******** -- StartRPCBinding: PUBLIC ENTRY PROC [me: RPCPkt.Machine] = { myHost _ me; IF exportTable.used = 0 THEN { exportTable[binderHint] _ [binderID, Binder, ["Binder",NIL,[0,0]], [0,0]]; exportTable.used _ 1; }; }; RollbackRPCBinding: PUBLIC ENTRY PROC = { lastExportID _ BasicTime.Period[from: BasicTime.earliestGMT, to: BasicTime.Now[]]; }; }. <> <> <<>> <<>> <<>>