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.