-- 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
<netNumber>.<hostNumber>.<socketNumber> or
<netNumber>#<hostNumber>#<socketNumber>
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, <defaultCHPID>, 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.