In rule sets, it is often advantageous to compare individual
tokens to multiple strings in determining a match.
For example, consider the rules developed
in the last chapter, that is, the sender rewriting rules from
the hub delivery agent's S=Hubset
rule set:
SHubset # Rewrite the sender for the hub R$- $@ $1@${HUB} user -> user@hub R$-@$w $@ $1@${HUB} user@local -> user@hub
The second rule's LHS looks for any sender address
that is composed of a single username followed by an @
character and ending with the short name of the local machine ($w
).
Such an address is rewritten by the RHS to become that of
the central forwarding machine, defined by the {HUB}
macro.
Now suppose that the local machine is known by several names in
addition to the short name in $w
. All machines,
for example, have a short name (such as here)
and a fully qualified name (such as here.us.edu).
They also often refer to themselves as localhost.
In addition, some machines can play special roles at a site (such
as having a printer or containing a central directory of fonts) and
might have another name appropriate to that role.
To convert any sender address so that it becomes the central forwarder's name, no matter what the local host's name, you can use sendmail classes. In this chapter we will cover the class configuration command and its cousin, the file configuration command. Proper use of the class and file commands allows you to replace many rules with a single rule.
The class command declares a macro whose value is a list of strings. Rule sets may then compare the workspace to that list of strings. One such list could be a list of names by which the local machine is known.
A class is referenced in the LHS with the $=
prefix:
$=X
single-character name $={XXX}
multicharacter name (beginning with V8.7)
Here, X
is a single-character class name.
Beginning with V8.7 sendmail, multicharacter class name may be
used, as shown in the second line above.
Multicharacter class names must always be enclosed in
curly braces.
The workspace is tokenized as usual; then the appropriate
token is looked up to see whether it was defined as belonging
to the class referenced by $=
. If the token was
found, the workspace at that point is considered to be matched.
We'll cover this in more detail shortly.
The words that form the list of words in a class
are declared with the C
configuration command.
The form for the class configuration command is as follows:
CXlist
single-character name C{XXX}list
multicharacter name (beginning with V8.7)
The class configuration command starts with the letter
C
, which must begin a line. The C
is immediately followed
(with no intervening whitespace) by the name of that class.
A class name can be a single ASCII character
or, beginning with V8.7 sendmail,
multiple ASCII characters enclosed in curly braces.
A whitespace-separated list
of word elements follows
on the same line. Space between the name and the list
is optional.
For example, the following declaration places two possible
names for the local machine into the class named w
:
Cw printer1 fontserver
Multiple declarations of the same class macro may exist. Each appends its word elements to the preceding list. For example, the following produces the same result as the single line above:
Cw printer1 Cw fontserver
Both examples define a class named w
, and both assign to
that class the same list of two words.
Class names and macro names are completely independent. To illustrate, consider the following two configuration commands:
Dwprinter1 Cwprinter1
Both assign the value printer1
to the name w
.
The first assigns that value to a macro, the second to
a class. Internally, sendmail stores the value printer1
twice, first as type "macro" and second
as type "class." Although they share the same
value (printer1
), macros and classes are
completely independent of each other.
As an example of one common use of the class macro, we will consider the case of machines that are known by multiple names. Run sendmail again:
%./sendmail -Cclient.cf -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >
Now give it the special (new to V8.7 sendmail)
rule-testing command $=
(which
tells sendmail to print the values stored in a class) and follow
that with the letter (class name) w
:
>$=w
here.us.edu here [123.45.67.8] >
This illustrates that sendmail is aware of
three of your machine's possible names (the last is an IP address).
But recall from the previous chapter that the rule that we created
looked for the local machine using only the
short name in the macro w
, not the three names in class
w
:
R$-@$w $@ $1@${HUB} user@local -> user@hub
If we didn't have class macros, we would have to look for the local machine name using a series of rules like those below:
R$-@$w $@ $1@${HUB} user@local -> user@hub [email protected] $@ $1@${HUB} user@local -> user@hub R$-@[123.45.67.8] $@ $1@${HUB} user@local -> user@hub
Fortunately, the class configuration command provides an alternative. Use of a class list allows one rule to be written that does exactly the same thing as the three preceding rules:
R$-@$=w $@ $1@${HUB} user@local -> user@hub
Let's examine this new rule, then test it.
The list of words in a class are referenced in the LHS of
rules by prefixing the class name with the characters
$=
. For the class named w
the expression
in the LHS looks like this:
$=w
To understand how this expression is evaluated, we need to
look at how the words in the class are stored.
Each word that sendmail adds to the list (whether
internally or from a C
configuration command)
is stored by sendmail
in its symbol table. The symbol table holds all the words
that appear in the configuration file. It holds the values
of macro definitions and the symbolic names of delivery agents.
It also holds the words that form a class list.
Next, we need to review how the workspace is tokenized and compared to the LHS of a rule. Just before the LHS is compared to the workspace, the workspace is tokenized. The address of a local user in the workspace might look like, and be tokenized like, the following:
you@here becomes you @ here
When the $=w
expression appears in the LHS of a rule,
it is compared to the workspace at the point that corresponds to
the class's position in the LHS:
R$-@$=w $@$1@${HUB} user@local -> user@hub here
In performing its minimum match of the workspace to the LHS, the matches are the following:
$- match you @ match @ $=w does here match any token in class w?
When sendmail encounters a class-matching expression in
the LHS, it looks up the corresponding token from the workspace
in its symbol table. Here it looks up here
.
Because here
is listed in the symbol table
as belonging to the class w
, sendmail finds a match.
If there were no here
in the symbol table, the match
would fail. If there were one or
more here
entries in the symbol table, but none marked as belonging to
class w
, the match would also fail.
When a match in the LHS allows the RHS to rewrite the workspace, the
token matched by the $=
prefix can be referenced
with a $
digit
positional operator. This differs from the
$
macro prefix that you learned about earlier. Macros
are expanded when the configuration file is read (so $
digit
doesn't work), but classes are
given values only when a rule is matched (so $
digit
does work).
We don't use the positional operator in the RHS because we
don't care what word matched. Instead, we take the username from
the LHS with $1
, add an @
character and
the name of the forwarding hub machine (the ${HUB}
):
R$-@$=w $@$1@${HUB} user@local -> user@hub 1 2 $2 not needed
Modify the client.cf file by changing the $w
in
the second rule (of SHubset
) into a $=w
so that we
can begin to test the class:
R$-@$=
w $@ $1@${HUB} user@local -> user@hub insert an =
Now run sendmail in rule-testing mode once again:
%./sendmail -Cclient.cf -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >
First use the $=
rule-testing command to list the contents
of the class w
:
>$=w
here.us.edu here [123.45.67.8]
Next, to test the new rule, specify rule set Hubset
and
give sendmail an address with a user, an @
, and
one of the local addresses shown for your machine:
>Hubset [email protected]
rewrite: ruleset 199 input: you @ here . us . edu rewrite: ruleset 199 returns: you @ mail . us . edu
This result is just what we expected. After all, the value in ${HUB}
is mail.us.edu
. Now give sendmail another address,
only this time give it one not in the list:
>Hubset you@localhost
rewrite: ruleset 199 input: you @ localhost rewrite: ruleset 199 returns: you @ localhost
This time the address was not rewritten because localhost
is
not listed in the class w
. But, in general,
localhost is a legitimate name for the local machine, so
let's add it temporarily by employing a new rule-testing command
that you haven't seen before:
>.Cw localhost
>$=w
here.us.edu localhost here [123.45.67.8]
The .C
command (new with V8.7)
tells sendmail to temporarily add a word to a class. In this case
we added the word localhost
to the class w
. We then
ran the $=
rule-testing command to confirm that sendmail
had actually added the word to the class.
Test the address localhost again:
>Hubset you@localhost
rewrite: ruleset 199 input: you @ localhost rewrite: ruleset 199 returns: you @ mail . us . edu
This time, sendmail found localhost
in the list
and so rewrote the sender address.
As we mentioned earlier, a host may be known by many names.
Some of those names are found automatically by sendmail; others must be added by you. The name localhost
is one possibility, and there may be others (such as printer1).
You can add them to class w
in rule-testing mode, but
that is not a permanent solution (because they are forgotten as soon
you leave rule-testing mode). Instead, you need to add
them to your configuration file using the C
configuration
command.
Edit the client.cf file and add a new C
configuration
line to it:
V7 # Defined macros D{REMOTE}mailhost # The name of the mail hub D{HUB}mail.us.edu # Hub as known to the outside worldCw localhost printer1 # My other names.
new
Here we added localhost
because it was missing and printer1
to illustrate later points. Note that the name printer1
is probably
not appropriate for your site, but include it for now.
Run sendmail again in rule-testing mode:
%./sendmail -Cclient.cf -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >
Use the $=
rule-testing command again. Make sure the
C
line added the new names to the
class w
:
>$=w
here.us.edu localhost here [123.45.67.8] printer1
We can now test each name. Remember that the rule we are testing is this one:
R$-@$=w $@ $1@${HUB} user@local -> user@hub
We want to match any sender address that is a username,
an @
, and any of the names in the class w
.
We also want to rewrite each to appear as though the user is from
the hub.
>Hubset [email protected]
rewrite: ruleset 199 input: you @ here . us . edu rewrite: ruleset 199 returns: you @ mail . us . edu >Hubset you@localhost
rewrite: ruleset 199 input: you @ localhost rewrite: ruleset 199 returns: you @ mail . us . edu >Hubset you@here
rewrite: ruleset 199 input: you @ here rewrite: ruleset 199 returns: you @ mail . us . edu >Hubset you@[123.45.67.8]
rewrite: ruleset 199 input: you @ [ 123 . 45 . 67 . 8 ] rewrite: ruleset 199 returns: you @ mail . us . edu >Hubset you@printer1
rewrite: ruleset 199 input: you @ printer1 rewrite: ruleset 199 returns: you @ mail . us . edu >Hubset [email protected]
rewrite: ruleset 199 input: you @ printer1 . us . edu rewrite: ruleset 199 returns: you @ printer1 . us . edu
All the names in class w
match as they should. Notice
that here appears in the list as both a short and
a fully qualified name, but printer1 does not.
When we tried printer1 in a fully qualified form (in the last
three lines above), it failed.
To make printer1 succeed,
we could add its fully qualified form
to the class w
, but if we did that, we would have
to do the same for localhost and any other names
that we added. Instead, we will deal with the problem by adding
a rule to the client.cf file.
There are three approaches to writing a rule that matches any of the local hostnames with a domain added:
Use a $*
wildcard operator to match anything after the local hostname:
R$-@$=w$*
But this is unwise because it assumes that anything that follows the host is a domain, which might not always be true.
Use your domain name directly in the rule:
R$-@$=w.us.edu
Although this will work, it is better to use a macro in case you change your domain or use this file on a machine in another domain.
Use a macro that has your domain name as its value.
We will use the third approach because it is the cleanest. Begin by
running sendmail with the -d0.1
debugging switch
again:
Version 8.8.4 Compiled with: LOG MATCHGECOS MIME7TO8 MIME8TO7 NAMED_BIND NDBM NETINET NETUNIX NIS SCANF XDEBUG ============ SYSTEM IDENTITY (after readcf) ============ (short domain name) $w = here (canonical domain name) $j = here.us.edu (subdomain name) $m = us.edu (node name) $k = here ======================================================== ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >
Note that the line that defines the "subdomain name" (the one with the $m
)
is the local domain that we need for our new rule.
Add another rule to the Hubset
rule set. This new rule looks for
any local names (in $=w
) that are followed by the
local domain name defined for you by sendmail:
SHubset # Rewrite the sender for the hub R$- $@ $1@${HUB} user -> user@hub R$-@$=w $@ $1@${HUB} user@local -> user@hubR$-@$=w.$m $@ $1@${HUB} [email protected] -> user@hub
new
Now run sendmail once again and feed it the address that failed in the last test (but use your local domain):
%./sendmail -Cclient.cf -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >Hubset [email protected]
rewrite: ruleset 199 input: you @ printer1 . us . edu rewrite: ruleset 199 returns: you @ mail . us . edu
As expected, the new rule in the Hubset
rule set does its job.
It finds that
the printer1.us.edu
part of the address matches the $=w.$m
part
of the rule and rewrites the workspace to make the sender's address
appear as though it is from the hub.