Rules are like little if-then clauses, existing inside rule sets, that test a pattern against an address and change the address if the two match. The process of converting one form of an address into another is called rewriting. Most rewriting requires a sequence of many rules, because an individual rule is relatively limited in what it can do. This need for many rules, combined with the sendmail program's need for succinct expressions, can make sequences of rules dauntingly cryptic.
In this chapter we dissect the components of individual rules. In the previous chapter we showed how groups of rules are combined to perform necessary tasks.
Rules are declared in the configuration file with the R
command.
Like all configuration commands, the R
rule configuration command
must begin a line. The general form consists of an R
command
followed by three parts:
Rlhs rhs comment
tabs tabs
The lhs
stands for left-hand side and is most commonly expressed
as LHS. The rhs
stands for right-hand side and is expressed
as RHS.
The LHS and RHS are mandatory.
The third part (the comment
) is optional.
The three parts must be separated from each other by one
or more tab characters (space characters will not work).
Space characters between the R
and the LHS are optional.
If there is
a tab between the R
and the LHS, sendmail
silently uses the LHS as the RHS and the RHS
becomes the comment.
The tabs leading to the comment and the comment itself
are optional and may be omitted.
If the RHS is absent,
sendmail prints the following warning and ignores that
R
line:
invalid rewrite line "bad rule here" (tab expected)
This error is printed when the RHS is absent, even if there are tabs following the LHS. (This warning is usually the result of tabs being converted to spaces when text is copied from one window to another in a windowing system.)
Each noncomment part of a rule is expanded as the configuration file is read. [1] Thus any references to defined macros are replaced with the value that the macro has at that point in the configuration file. To illustrate, consider this mini-configuration file (called x.cf):
[1] Actually, the comment part is expanded too, but with no effect other than a tiny expenditure of time.
DAvalue1 R$A $A.new DAvalue2 R$A $A.new
In it, $A
will have the value value1
when the first R
line is expanded and value2
when the second is expanded.
Prove this to yourself by running sendmail in -bt
rule-testing mode on
that file:
%echo =S0 | /usr/lib/sendmail -bt -Cx.cf
> =S0 Rvalue1 value1 . new Rvalue2 value2 . new
Here, we use the =S
command (see Section 38.4.1, "Show Rules in a Rule Set with =S") to show each
rule after it has been read and expanded.
Another property of macros is that an undefined macro expands to an empty string. Consider this x.cf file:
DAvalue1 R$A $A.$B DAvalue2 R$A $A.$B
and this rule-testing run of sendmail:
%echo =S0 | /usr/lib/sendmail -bt -Cx.cf
> =S0 Rvalue1 value1 . Rvalue2 value2 .
Beginning with V8.7 sendmail, macros can be either single-character or multicharacter. Both forms are expanded when the configuration file is read:
D{OURDOMAIN}us.edu R${OURDOMAIN} localhost.${OURDOMAIN}
Multicharacter macros may be used in the LHS and in the RHS. When the configuration file is read, the previous example is expanded to look like this:
> =S0 Rus . edu localhost . us . edu
After each side (LHS and RHS) is expanded, each is then normalized just as though it were an address. A check is made for any tabs that may have been introduced during expansion. If any are found, everything from the first tab to the end of the string is discarded. Then RFC822-style comments are removed. An RFC822 comment is anything between and including an unquoted pair of parentheses:
[email protected] (Operator) R$A tabRHS [email protected] (Operator) tab
RHS expanded [email protected] tab
RHS RFC822 comment stripped
Finally, a check is made for
balanced quotation marks,
right parentheses balanced by left,
and right angle brackets balanced by left.
[2]
If any right-hand character appears without a corresponding
left-hand character, sendmail prints one of the following errors,
where configfile is the name of the configuration file that is being
read, num
shows the line number in that file, and expression
is the
part of the rule that was unbalanced:
[2] The
$>
operator isn't counted in checking balance.
configfile: linenum: expression...Unbalanced '"'
configfile: linenum: expression...Unbalanced '>'
configfile: linenum: expression...Unbalanced ')'
The first line shows that sendmail has balanced the unbalanced quotation mark by appending a second quotation mark. Each of the last two lines shows that the unbalanced character is removed. For example, the file:
Rx RHS" Ry RHS> Rz RHS)
produces these errors and rules:
%echo =S0 | /usr/lib/sendmail -bt -Cx.cf
x.cf: line 1: RHS"... Unbalanced '"' x.cf: line 2: RHS>... Unbalanced '>' x.cf: line 3: RHS)... Unbalanced ')' > =S0 Rx RHS "" Ry RHS Rz RHS
Note that prior to V8.7 sendmail, only an unbalanced right-hand character was checked: [3] Beginning with V8.7 sendmail, unbalanced left-hand characters are also detected, and sendmail attempts to balance them for you. Consider, the following file:
[3] That is, for example, there must not be a
>
before the<
character, and they must pair off.
Rx "RHS Ry <RHS Rz (RHS
Here, sendmail detects and fixes the unbalanced characters but does so with warnings:
%echo =S0 | /usr/lib/sendmail -bt -Cx.cf
x.cf: line 1: "RHS... Unbalanced '"' x.cf: line 2: <RHS... Unbalanced '<' x.cf: line 3: (RHS... Unbalanced '(' x.cf: line 3: R line: null RHS > =S0 Rx "RHS" Ry < RHS > Rz
Note that in the last example (Rz
), sendmail balanced the RHS
by adding a rightmost parenthesis. This caused the RHS to become
an RFC822 comment, which was then deleted, resulting in the null RHS error.
If you get one of these Unbalanced
errors, be sure to correct
the problem at once. If you leave the faulty rule in place, sendmail
will continue to run but will likely produce erroneous mail delivery
and other odd problems.
Backslash characters are used in addresses to protect certain special
characters from interpretation (see Section 35.3.2, "Escape Character in the Header Field").
For example, the address
blue;jay would ordinarily be interpreted as having three
parts (or tokens, which we'll discuss soon). To prevent sendmail
from treating this address as three parts and instead have it
viewed as a single item, the
special separating nature of the
;
can be escaped by prefixing it with
a backslash:
blue\;jay
V8 sendmail handles backslashes differently than other versions have in the past. Instead of stripping a backslash and setting a high bit (see below), it leaves backslashes in place:
blue\;jay becomes blue\;jay
This causes the backslash to mask the special meaning of characters, because sendmail always recognizes the backslash in that role.
The only time that V8 sendmail strips backslashes is during local delivery, and then only if they are not inside full quotation marks. Mail to \user is delivered to user on the local machine (bypassing further aliasing) with the backslash stripped. But for mail to \user@otherhost the backslash is preserved in both the envelope and the header.
Prior to V8 sendmail, for addresses and rules (which are normalized as addresses), the backslash was removed by sendmail and the high bit of the character following the backslash was set (turned on).
blue;jay high-bit turned on
This practice was abandoned because of the need to support international characters (such as ö, , and ). Most international character sets include many characters that have the high bit set. Escaping a character by setting its high bit is a practice that no longer works in our modern, international world.