-- File: AddressTranslationImpl.mesa - last edit: -- AOF 26-Apr-85 13:30:18 -- BGY 29-Jul-85 14:03:58 -- BJD 9-Oct-84 17:28:00 -- HGM 7-Sep-85 14:26:12 -- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved. DIRECTORY AddressCache USING [AddNSName, GetSize, LookupNSName], AddressTranslation USING [ErrorRecord, Reason], Auth USING [CallProblem, IdentityHandle], AuthSpecial USING [MakeNullCHConversation], CH USING [ConversationHandle, FreeConversationHandle, LookupDistinguishedName, MakeConversationHandle, Name, ReturnCode], CHCommonLookups USING [LookupAddress, LookupNameProperty], CHPIDs USING [associatedWorkstation], Courier USING [LocalSystemElement], ExtendedString USING [StringToNumber], Format USING [DecimalFormat, Number, StringProc], Heap USING [systemZone], NSName USING [Error, FreeName, MakeName, maxFullNameLength, Name, NameFieldsFromString, NameTooSmall], NSString USING [String, StringFromMesaString], Profile USING [GetDefaultOrganization, GetID, Qualify], Runtime USING [IsBound], String USING [AppendChar, AppendString, AppendSubString, EqualStrings, SubString, SubStringDescriptor], System USING [broadcastHostNumber, HostNumber, NetworkAddress, NetworkNumber, nullHostNumber, nullNetworkNumber, nullSocketNumber, SocketNumber]; AddressTranslationImpl: PROGRAM IMPORTS AddressCache, AuthSpecial, CH, CHCommonLookups, Courier, ExtendedString, Format, Heap, NSName, NSString, Profile, Runtime, String EXPORTS AddressTranslation = BEGIN --Local constants, types and variables NetworkAddress: TYPE = System.NetworkAddress; bound: BOOLEAN = Runtime.IsBound[LOOPHOLE[AddressCache.GetSize]]; Error: PUBLIC ERROR [errorRecord: AddressTranslation.ErrorRecord] = CODE; << This module implements the address translation interface. It translates strings to internal records. A fully specified address string is of the form .. or ## hostNumber must always be specified. It can have one of several forms: 1) A sequence of characters in ['0 .. '7] optionally followed by a 'B. 2) A sequence of characters in ['0..'9] optionally followed by 'D. The string may also have '- characters imbedded impying product format. 3) A sequence of characters in ['0..'9] or in ['A..'F] optionally followed by 'H. Hex specifications may have a leading zero to reduce ambiguity. 4) A Clearinghouse distinguished name containing at least one character not in the set {in['0..'9], in['A..'F]}. The Clearinghouse name may include blanks, the characters '-, '*, '@, ':, or numerals as well as upper and lower case letters. The string is broken into its local, domain, and organization (org) components by looking for either ': or '@ characters. Both separators are not allowed in the same string. Clients are advised to only use ': as separator characters. If the string does not contain a domain and/or org field, values for those fields will be supplied from a Tajo interface, see the documentation for exact details. The resulting Clearinghouse name is looked up in the CH. The name must denote (or be an alias for) one of a specific set of CH objects for the lookup to succeed. Refer to the release documentation for the currently supported set of allowed CH objects. 5) The special string "ME" is equivalent to specifing the Processor ID of the host that AddressTranslation is called on. 6) The special string "*" is equivalent to specifing the broadcast Processor ID. Both netNumber and socketNumber can be defaulted. The translation routine will translate any string that is well formed and unambiguous. Examples: 74B#25200000016#2 Lassen:OSBU North 25200000016 60.Lassen ME * 74.*. #Lassen:Bayhill:Xerox#2B #25200000016#2 Ambiguous (because pair could mean net#host or host#socket): 74#2 >> BigNumber: TYPE = MACHINE DEPENDENT RECORD [ SELECT OVERLAID * FROM null => [a, b, c, d: WORD], net => [an, bn: WORD, net: System.NetworkNumber], host => [ah: WORD, host: System.HostNumber], socket => [as, bs, cs: WORD, socket: System.SocketNumber], ENDCASE]; nullBigNumber: BigNumber = [null[0, 0, 0, 0]]; --PROCS PrintCHReturnCode: PUBLIC PROC[ rc: CH.ReturnCode, proc: Format.StringProc, data: LONG POINTER ¬ NIL] = BEGIN SELECT rc.code FROM done => proc ["[done, "L, data]; notAllowed => proc ["[notAllowed, "L, data]; rejectedTooBusy => proc ["[rejectedTooBusy, "L, data]; allDown => proc ["[allDown, "L, data]; LOOPHOLE [4] => proc ["[operationRejectedUseCourier, "L, data]; badProtocol => proc ["[badProtocol, "L, data]; illegalPropertyID => proc ["[illegalPropertyID, "L, data]; illegalOrgName => proc ["[illegalOrgName, "L, data]; illegalDomainName => proc ["[illegalDomainName, "L, data]; illegalLocalName => proc ["[illegalLocalName, "L, data]; noSuchOrg => proc ["[noSuchOrg, "L, data]; noSuchDomain => proc ["[noSuchDomain, "L, data]; noSuchLocal => proc ["[noSuchLocal, "L, data]; propertyIDNotFound => proc ["[propertyIDNotFound, "L, data]; wrongPropertyType => proc ["[wrongPropertyType, "L, data]; noChange => proc ["[noChange, "L, data]; outOfDate => proc ["[outOfDate, "L, data]; overflowOfName => proc ["[overflowOfName, "L, data]; overflowOfDataBase => proc ["[overflowOfDataBase, "L, data]; LOOPHOLE [50] => proc ["[wrongServer, "L, data]; LOOPHOLE [60] => proc ["[identifierRejected, "L, data]; LOOPHOLE [61] => proc ["[verifierInvalid, "L, data]; LOOPHOLE [62] => proc ["[verifierExpired, "L, data]; LOOPHOLE [63] => proc ["[verifierReused, "L, data]; LOOPHOLE [64] => proc ["[credentialsExpired, "L, data]; credentialsTooWeak => proc ["[credentialsTooWeak, "L, data]; wasUpNowDown => proc ["[wasUpNowDown, "L, data]; ENDCASE => {proc ["[???("L, data]; Format.Number [proc, rc.code, Format.DecimalFormat]; proc ["), "L, data]; }; SELECT rc.which FROM first => proc ["first]"L, data]; second => proc ["second]"L, data]; ENDCASE => {proc ["???("L, data]; Format.Number [proc, rc.which, Format.DecimalFormat, data]; proc [")]"L, data]; }; END; -- of PrintCHReturnCode PrintError: PUBLIC PROCEDURE [ error: AddressTranslation.ErrorRecord, proc: Format.StringProc, clientData: LONG POINTER ¬ NIL] = { WITH e: error SELECT FROM scanError => { proc["Scan error at position "L, clientData]; Format.Number [proc, e.position, [], clientData]; proc["."L, clientData]; }; badSyntax => { proc["Bad syntax in "L, clientData]; proc[SELECT e.field FROM net => "net"L, host => "host"L, socket => "socket"L, ENDCASE => "string"L, clientData]; proc["."L, clientData]; }; chLookupProblem => { proc["Clearinghouse lookup problem: "L]; PrintCHReturnCode[e.rc, proc, clientData]; proc["."L, clientData]; }; otherCHProblem => { proc["AddressTranslation problem: "L, clientData]; proc[SELECT e.reason FROM noUsefulProperties => "no useful properties"L, ambiguousSeparators => "ambiguous separators"L, tooManySeparators => "too many separators"L, authentication => "authentication"L, invalidName => "invalid name"L, invalidPassword => "invalid password"L, couldntDetermineAddress => "couldn't determine address"L, spare1 => "spare1"L, spare2 => "spare2"L, spare3 => "spare3"L, ENDCASE => "unknown"L, clientData]; proc["."L, clientData]; }; ENDCASE; }; StringToNetworkNumber: PUBLIC PROC[s: LONG STRING] RETURNS [nN: System.NetworkNumber] = BEGIN net: String.SubStringDescriptor; type: FieldType ¬ default; nN ¬ System.nullNetworkNumber; --set default IF s = NIL THEN GOTO nope; net ¬ [s, 0, s.length]; type ¬ GetType[@net]; SELECT type FROM default => nN ¬ DefaultNet[]; broadcast => nN ¬ LOOPHOLE[LAST[LONG CARDINAL]]; octal, decimal, hex => Convert[@nN, SIZE[System.NetworkNumber], @net, type]; ENDCASE => GOTO nope; EXITS nope => NULL; END; --StringToNetworkNumber StringToHostNumber: PUBLIC PROC[s: LONG STRING] RETURNS [hN: System.HostNumber] = { host: String.SubStringDescriptor; type: FieldType ¬ default; hN ¬ System.nullHostNumber; --set default IF s = NIL THEN GOTO nope; host ¬ [s, 0, s.length]; type ¬ GetType[@host]; SELECT type FROM default => hN ¬ DefaultHost[]; broadcast => hN ¬ System.broadcastHostNumber; octal, decimal, hex => Convert[@hN, SIZE[System.HostNumber], @host, type]; ENDCASE => GOTO nope; EXITS nope => NULL; }; --StringToHostNumber defaultHost: System.HostNumber ¬ System.nullHostNumber; defaultNet: System.NetworkNumber ¬ System.nullNetworkNumber; DefaultHost: PROC RETURNS [dH: System.HostNumber] = { IF defaultHost = System.nullHostNumber THEN defaultHost ¬ Courier.LocalSystemElement[].host; RETURN[defaultHost]; }; DefaultNet: PROC RETURNS [dN: System.NetworkNumber] = { IF defaultNet = System.nullNetworkNumber THEN defaultNet ¬ Courier.LocalSystemElement[].net; RETURN[defaultNet]; }; << If the user doesn't specify a socket then addr.socket is defaulted to System.nullSocketNumber If the string requires CH lookup, the first property searched for will be defaultCHPID. If defaultCHPID is set to the default "all", or the lookup using the default did not succeed, then all allowable properties will be checked. The property that the lookup succeeded on is returned as usedCHPID (unknown value if chUsed is False). If the string requires CH lookup, then distingName will be passed to the CH interfaces used, allowing the user to determine the exact distinguished name stored in the CH. See the release documentation for further information. >> FieldType: TYPE = {default, broadcast, octal, decimal, hex, clearinghouse}; GetType: PROC[ss: String.SubString] RETURNS [type: FieldType ¬ default] = { end: CARDINAL ¬ ss.offset+ss.length; FOR i: CARDINAL IN [ss.offset..end) DO SELECT ss.base[i] FROM IN['0..'7] => type ¬ MAX[type, octal]; '8, '9 => type ¬ MAX[type, decimal]; 'A, 'C, 'E, 'F => type ¬ MAX[type, hex]; 'B, 'b => SELECT TRUE FROM (type > octal) => LOOP; (i = end - 1) => type ¬ octal; ENDCASE => type ¬ hex; 'D, 'd => SELECT TRUE FROM (type > decimal) => LOOP; (i = end - 1) => type ¬ decimal; ENDCASE => type ¬ MAX[type, hex]; 'H, 'h => SELECT TRUE FROM (type > hex) => LOOP; (i = end - 1) => type ¬ hex; ENDCASE => {type ¬ clearinghouse; EXIT}; '- => type ¬ SELECT type FROM default, hex => clearinghouse, octal => decimal, ENDCASE => type; '* => type ¬ MAX[type, broadcast]; ENDCASE => {type ¬ clearinghouse; EXIT}; ENDLOOP; IF (type = broadcast AND ss.length # 1) OR (type = hex AND ~(ss.base[ss.offset] IN ['0..'9])) THEN type ¬ clearinghouse; }; StringToNetworkAddress: PUBLIC PROC[ s: LONG STRING, id: Auth.IdentityHandle ¬ NIL, distingName: NSName.Name ¬ NIL] RETURNS [addr: NetworkAddress, chUsed: BOOLEAN ¬ FALSE] = { netPart, hostPart, socketPart: String.SubStringDescriptor; netType, hostType, socketType: FieldType ¬ default; i, firstDot, secondDot, second, third: CARDINAL ¬ 0; netPart ¬ hostPart ¬ socketPart ¬ [s, 0, 0]; IF s = NIL THEN Error[[scanError[0]]]; BEGIN FOR i IN [0..s.length) DO -- find net part IF s[i] = '. OR s[i] = '# THEN {firstDot ¬ i; EXIT}; REPEAT FINISHED => GOTO onlyHost; ENDLOOP; netPart ¬ [s, 0, firstDot]; netType ¬ GetType[@netPart]; second ¬ firstDot + 1; FOR i DECREASING IN (firstDot..s.length) DO -- find socket part IF s[i] = '. OR s[i] = '# THEN {secondDot ¬ i; EXIT}; REPEAT FINISHED => { -- only two parts socketPart ¬ [s, second, s.length - second]; socketType ¬ GetType[@socketPart]; SELECT netType FROM clearinghouse => { IF socketType = clearinghouse THEN GOTO onlyHost; hostPart ¬ netPart; hostType ¬ netType; netType ¬ default; GOTO done}; ENDCASE => { IF socketType # clearinghouse THEN Error[[badSyntax[host]]]; hostPart ¬ socketPart; hostType ¬ socketType; socketType ¬ default; GOTO done}}; ENDLOOP; third ¬ secondDot + 1; socketPart ¬ [s, third, s.length - third]; socketType ¬ GetType[@socketPart]; hostPart ¬ [s, second, secondDot - second]; hostType ¬ GetType[@hostPart]; IF netType = clearinghouse AND socketType = clearinghouse AND hostType = clearinghouse THEN GOTO onlyHost; IF netType = clearinghouse THEN Error[[badSyntax[net]]]; IF socketType = clearinghouse THEN Error[[badSyntax[socket]]]; EXITS onlyHost => { netType ¬ socketType ¬ default; hostPart ¬ [s, 0, s.length]; hostType ¬ GetType[@hostPart]}; done => NULL; END; [addr, chUsed] ¬ GetAddress[ @netPart, @hostPart, @socketPart, netType, hostType, socketType, id, distingName]; }; --StringToNetworkAddress GetAddress: PROC[ net, host, socket: String.SubString, netType, hostType, socketType: FieldType, id: Auth.IdentityHandle ¬ NIL, distingName: NSName.Name ¬ NIL] RETURNS[na: NetworkAddress, chUsed: BOOLEAN ¬ FALSE] = { overWrite: BOOLEAN ¬ hostType # clearinghouse; SELECT hostType FROM default => na.host ¬ DefaultHost[]; broadcast => na.host ¬ System.broadcastHostNumber; octal, decimal, hex => Convert[ @na.host, SIZE[System.HostNumber], host, hostType]; ENDCASE => { s: LONG STRING ¬ Heap.systemZone.NEW[StringBody[host.length]]; String.AppendSubString[s, host]; [na, chUsed] ¬ NameToNetworkAddress[s, id, distingName! UNWIND => Heap.systemZone.FREE[@s]]; Heap.systemZone.FREE[@s]; }; SELECT netType FROM default => IF overWrite THEN na.net ¬ DefaultNet[]; broadcast => na.net ¬ LOOPHOLE[LAST[LONG CARDINAL]]; octal, decimal, hex => Convert[ @na.net, SIZE[System.NetworkNumber], net, netType]; ENDCASE => ERROR Error[[badSyntax[net]]]; SELECT socketType FROM default => IF overWrite THEN na.socket ¬ System.nullSocketNumber; broadcast => na.socket ¬ LOOPHOLE[LAST[CARDINAL]]; octal, decimal, hex => Convert[ @na.socket, SIZE[System.SocketNumber], socket, socketType]; ENDCASE => ERROR Error[[badSyntax[socket]]]; }; Convert: PROC [ f: LONG POINTER, size: CARDINAL, ss: String.SubString, type: FieldType] = { s: LONG STRING ¬ [40]; long: LONG STRING ¬ NIL; base: CARDINAL ¬ SELECT type FROM octal => 8, decimal => 10, ENDCASE => 16; IF ss.length > 40 THEN long ¬ s ¬ Heap.systemZone.NEW[StringBody[ss.length]]; BEGIN ENABLE UNWIND => IF long # NIL THEN Heap.systemZone.FREE[@long]; s.length ¬ 0; FOR i: CARDINAL IN [ss.offset..ss.offset + ss.length) DO IF ss.base[i] # '- THEN String.AppendChar[s, ss.base[i]]; ENDLOOP; SELECT s[s.length - 1] FROM IN ['0..'9] => NULL; IN ['A..'F] => IF base # 16 THEN s.length ¬ s.length - 1; ENDCASE => s.length ¬ s.length - 1; ExtendedString.StringToNumber[f, size, base, s]; IF long # NIL THEN Heap.systemZone.FREE[@long]; END; }; NameToNetworkAddress: PROC[ s: LONG STRING, identity: Auth.IdentityHandle ¬ NIL, distingName: NSName.Name] RETURNS [na: NetworkAddress, chUsed: BOOLEAN ¬ FALSE] = BEGIN qualified: LONG STRING = [NSName.maxFullNameLength]; target: NSName.Name ¬ NIL; z: UNCOUNTED ZONE = Heap.systemZone; conversation: CH.ConversationHandle ¬ [NIL, NIL]; cacheSize: CARDINAL ¬ 0; hit: BOOLEAN ¬ FALSE; --special case - see if the name is "ME" IF String.EqualStrings[s, "ME"L] THEN { na ¬ Courier.LocalSystemElement[]; na.socket ¬ System.nullSocketNumber; RETURN}; BEGIN ENABLE UNWIND => BEGIN IF target # NIL THEN {NSName.FreeName[z, target]; target ¬ NIL}; IF conversation.conversation # NIL THEN CH.FreeConversationHandle [@conversation, z]; END; << Try to get address of s from the CH, using first the default property type, , then any CHPID that we know about >> ok: BOOLEAN ¬ TRUE; done: CH.ReturnCode ¬ [done, first]; reason: AddressTranslation.Reason ¬ couldntDetermineAddress; -- default auth: Auth.CallProblem; chUsed ¬ TRUE; -- set the return variables Profile.Qualify[s, qualified, clearinghouse]; BEGIN -- Profile.Qualify doesn't do anything if you feed it A:B separators: CARDINAL ¬ 0; GetDefaultOrganization: PROCEDURE [s: LONG STRING] = BEGIN String.AppendChar[qualified, ':]; String.AppendString[qualified, s]; END; FOR i: CARDINAL IN [0..qualified.length) DO SELECT qualified[i] FROM ': => {IF (separators ¬ separators + 1) = 2 THEN EXIT}; ENDCASE; REPEAT FINISHED => Profile.GetDefaultOrganization[GetDefaultOrganization]; ENDLOOP; END; target ¬ NSName.MakeName[z]; NSName.NameFieldsFromString[ z: z, destination: target, s: NSString.StringFromMesaString[qualified] ! NSName.Error => {reason ¬ tooManySeparators; CONTINUE}; NSName.NameTooSmall => {reason ¬ tooManySeparators; CONTINUE}]; IF reason # couldntDetermineAddress THEN -- NSName.Error was signaled ERROR Error[[otherCHProblem[reason]]]; -- UNWIND will free allocated storage IF bound THEN cacheSize ¬ AddressCache.GetSize[]; IF cacheSize > 0 THEN { [na, hit] ¬ AddressCache.LookupNSName[target]; IF hit THEN {chUsed ¬ FALSE; RETURN}; }; BEGIN -- All this crap just to read the CH. Argh!!!!! GetIdentity: PROCEDURE [id: Auth.IdentityHandle] = {[conversation, ok, auth] ¬ CH.MakeConversationHandle [id, z]}; IF identity = NIL THEN Profile.GetID[strong, GetIdentity] ELSE [conversation, ok, auth] ¬ CH.MakeConversationHandle [identity, z]; IF ~ok THEN SELECT auth FROM strongKeyDoesNotExist, badKey => { -- Try back door if not logged in. (May not work) -- Humm. strongKeyDoesNotExist works if password is NIL (Dicentra case) -- but Tajo uses an empty string, and that seems to generate badKey conversation ¬ AuthSpecial.MakeNullCHConversation[z]; ok ¬ TRUE; }; ENDCASE; IF ~ok THEN ERROR Error[[otherCHProblem[SELECT auth FROM badKey => invalidPassword, strongKeyDoesNotExist, simpleKeyDoesNotExist => invalidName, ENDCASE => authentication]]]; END; IF distingName # NIL THEN done ¬ CH.LookupDistinguishedName [conversation, target, distingName]; IF done.code = done THEN [done, na] ¬ CHCommonLookups.LookupAddress[conversation, target]; IF done.code # done AND done.code # allDown AND done.code # noSuchLocal THEN BEGIN workstationName: NSName.Name ¬ NIL; -- try to lookup the associated workstation [done, workstationName, ] ¬ CHCommonLookups.LookupNameProperty[ conversation, target, CHPIDs.associatedWorkstation, z]; IF done.code = done THEN [done, na, ] ¬ CHCommonLookups.LookupAddress[ conversation, workstationName]; IF workstationName # NIL THEN NSName.FreeName[z, workstationName]; workstationName ¬ NIL; END; -- free up storage IF cacheSize > 0 AND ~hit AND done.code = done THEN AddressCache.AddNSName[target, na]; IF target # NIL THEN {NSName.FreeName[z, target]; target ¬ NIL}; IF conversation.conversation # NIL THEN CH.FreeConversationHandle[ @conversation, z]; IF done.code = propertyIDNotFound THEN RETURN WITH ERROR Error[[otherCHProblem[couldntDetermineAddress]]]; IF done.code # done THEN RETURN WITH ERROR Error[[chLookupProblem[done]]]; END; END; --NameToNetworkAddress END.