Chapter 22. The C and F (Class Macro) Configuration Commands

In the LHS of rules, it is sometimes advantageous to compare individual tokens to multiple strings when determining a match. The configuration class command provides this ability. The class command is similar to the macro definition command, except that instead of assigning a single value to a macro, it assigns many values to a class. Classes differ from macros in that they can be used only in the LHS of rules, whereas macros can be used in either the RHS or the LHS.

Two different configuration commands can be used to assign values to a class. The C configuration command is used to assign values from within the configuration file. The F configuration command is used in three ways: to assign values by reading them from a disk file, to assign values by looking up a key in a database, or to assign values by running a program and reading the output. These commands can be intermixed to create a single class, or used separately to create multiple classes.

Class Configuration Commands

The five forms for the class configuration command are the following:

CX listvalues from configuration file
CX $=Ycopy values from another class (V8.10 and later)
FX /filevalues from a disk file
FX |programvalues via another program
FX key@databasevalues from a database map (V8.12 and later)

The class configuration command starts with either the letter C or the letter F, which must begin a line. The C says values will be assigned as a part of the configuration command. The F says values will be assigned from an external file, program, or database map.

The C or F is immediately followed (with no intervening whitespace) by the name of the class (the X in the preceding commands). A class name is any single ASCII character or, beginning with V8.7 sendmail, a multicharacter name enclosed in curly braces:

CX listall versions
C{LongName} listbeginning with V8.7

See Multicharacter Names on page 790 for a full discussion of how to use multicharacter names.

Note that classes are separate from macros, so they can both use the same letter or name with no conflict.

The sendmail program reserves the lowercase letters for its own use as internally defined class names. All uppercase letters and all names that begin with uppercase letters are available for your use.

The C Class Command

The C form of the class command causes values to be assigned from within the configuration file. In general, the C class command looks like this:

CX listvalues from configuration file
C{XX} listvalues from configuration file

Here, list is a list of string elements (delimited by whitespace) that follows on the same line as the C command. Each word in list is added to the collection of values in the class $=X in the first case and to the class $={XX} in the second.[331]

Multiple declarations of the same named class can coexist in the configuration file. Each declaration after the first adds its string elements to those already in the collection. That is:

CX string1 string2
CX string3 string4

produces the same collection of class strings as does:

CX string1 string2 string3 string4

Both create a class containing four strings.

Whitespace separates one value from another. Whitespace is defined by the C-language isspace(3) routine and usually includes the space, tab, newline, carriage return, and form feed characters. Each line of text assigned to a class is broken up by sendmail into whitespace-delimited words when the C configuration command is parsed.

When a line is indented with a space or a tab, that line is joined by sendmail to the preceding line. Thus, the following three declarations also add four words to the class $=X:

CX string1
CX string2
CX string3
      string4
   ↑
   tab

Words that are added to a class cannot be removed after sendmail has read them. Instead, they must be edited out of whatever file or program produced them, and the sendmail daemon must be restarted.

The list of words in a class declaration can include macros. For example, the following assigns the same values to class $=X as did the earlier example:

D{LIST} string1 string2 string3 string4
CX ${LIST}

Macros used in class declarations are expanded when the configuration file is read. Deferred macros (those with the $& prefix) cannot be used in class declarations. But conditionals can:

CX ourhost$?{Domain}.${Domain}$.

Append one class to another

Beginning with V8.10 sendmail, it is possible to copy and add values from one class to another. The declaration to do this looks like the following:

C{To} $={From}

Here, the values stored in the $={From} class are added to the values stored in the $={To} class. If $={To} does not exist, it will create them.

This effect is caused by the fact that class macros are now expanded when placed on a C configuration line. To illustrate, consider the following mini configuration file, which we call x.cf:

V10
CA 1 2 3
CB 7 8 9
CX $=A 4 5 6 $=B

When this configuration file is read, first the class $=A is filled with three values: 1, 2, and 3. Then the class $=B is filled with three different values: 7, 8, and 9. Finally, the class $=X is filled first with the values from $=A (1, 2, and 3), then with its own values (4, 5, and 6), and lastly with the values from $=B (7, 8, and 9). The result can be seen by running sendmail on this mini configuration file in rule-testing mode:

% /usr/sbin/sendmail -bt -C x.cf
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
Enter <ruleset> <address>
> $=X
2
3
1
6
7
4
5
8
9
>

Ignore the fact that the values you put in are printed in a different order. This is an artifact of the way sendmail stores class values in its symbol table and actually improves the efficiency with which they are later looked up.

Class macros that you list as values for a C configuration line need not be previously declared or even hold any values. If they hold values, those values will be added to the target class. Valueless and undeclared classes will simply be ignored.

The F Class Command

The F form of the class configuration command allows values to be appended to a class from outside the configuration file. In general, the file command looks like one of the following:

FX filevalues from a disk file
FX |programvalues via another program (V8.7 and later)
FX key@dbmapvalues from a database map (V8.12 and later)

The F is immediately followed by the name of the class. This can be either a single-character name, as shown, or a multicharacter name. The name is followed by optional whitespace and then a filename, a program name, or a database-map lookup. If the name begins with the pipe character (|), it is taken to be the name of a program to run.[332] If the name includes an @ character, it is taken to be a key to look up, and the name of a database map. Otherwise, it is taken to be the name of a file to read.

If SCANF (SCANF on page 137) was defined when sendmail was compiled, each line that is read from a file or program (but not from a database map) is parsed by the C-language scanf(3) library routine. The formatting pattern given to scanf(3) is %s, which tells scanf(3) to read only the first whitespace-delimited word from each line of text.

When the configuration file is processed, the file is opened for reading, or the program is executed, or the database map is opened for lookups. If any cannot be opened (for reading, execution, or lookups), the following error is logged and sendmail ignores that configuration command:

fileclass: cannot open what: why

Here, the what is the exact text that was given in the configuration file, and why is the text of a system error.

A file, program, or database map can also fail to open because of defective permissions. See Permissions on page 164 to learn why permissions are important, and Recommended Permissions on page 167 for a list of recommended permissions.

For the file form only, if the file can optionally not exist, you can prefix its name with a -o switch:

FX -o fileOK for file to not exist

This tells sendmail to remain silent if the file does not exit. The -o switch is useful when a configuration file is shared by several machines, only some of which need the external class macro file. But be aware that there can be grave risk to not knowing when a critical file disappears.

The C and F forms of the configuration command can be intermixed for any given class name. For example, consider a file named /etc/mail/localnames with the following contents:

string3
string4

The following two configuration commands add the same four strings to the class X as did the C command alone in the previous section:

CX string1 string2
FX /etc/mail/localnames

This creates a class with four strings as elements. Whitespace delimits one string from the others in the C line declaration. The file /etc/local/names is then opened and read, and each of the two words in that file is added to the two words that are already in the class.

scanf(3) variations

The file form of the class configuration command allows different formatting patterns to be used with scanf(3).[333] But the program form does not allow any variation, and so its scanf(3) pattern is always %s, which tells scanf(3) to read only the first whitespace-delimited word from each line of text:

FX file patwith scanf(3) pattern
FX |programalways "%s"
FX key@dbmapcannot be used with scanf(3)

If the optional pat argument to the file form is missing, the pattern given to scanf(3) is %s. The optional pat argument is separated from the file argument by one or more spaces or tabs. It should not be quoted, and it consists of everything from its first character to the end of the line. Internally, scanf(3) is called with:

sscanf(result, pat, input)

Here, result is the string array element to be added to the class definition. The pat is the scanf(3) pattern, and input is the line of text read from the file.

After each line of text is read from the file and filtered with the scanf(3) pattern, it is further subdivided by sendmail into individual words. That subdividing uses whitespace (as defined by the C-language isspace(3) routine) to separate words. Each separate word is then appended as an individual element to the class array.

Consider the contents of the following file named /etc/mail/localhosts:

server1 server2 # my two nets
uuhost          # my uucp alias
#mailhost       # mail server alias (retired 06,23,91)

This file contains three hostname aliases to be added to a class—say, H. The following configuration command does just that:

FH /etc/mail/localhosts %[^#]

The pattern %[^#] causes scanf(3) to read all characters in each line up to, but not including, the first # character. The first line includes two whitespace-delimited words that are appended to the class H. The second line contains one word, and the third contains none.

Class via Database-Map Lookups

Beginning with V8.12, you can declare class values by specifying and using database maps. Database maps are described in Chapter 23 on page 878. In its simplest form, such a declaration looks like this:

FXkey@ type:detail
F{Name}key@ type:detail

Each such declaration begins with the F configuration command, which is immediately followed (with no intervening space) by the name of the class that will be filled with values. The first line shows the single-character name form (the X) and the second line shows the multicharacter name form (the {Name}).

The name of the class is immediately followed by the key to look up in the database map. Note that you must be very careful to specify a key that actually exists. If the key is not found in the database map, sendmail silently ignores the error.

The key is immediately followed by a literal @ character, which in turn is immediately followed by the type of the database map. A db-type database map, for example, could have a type of either hash or btree. An ldap-type database map, for example, would have a type of ldap. (We discuss ldap in detail in the next section.) A complete list of types can be found in the leftmost column of Table 23-2 on page 883.

The type is immediately followed by a colon and then by the detail. The nature of the detail varies depending on what you want this command to do. To illustrate, consider the following addition to an mc configuration file:

LOCAL_CONFIG
FwCWhosts@hash:/etc/mail/access

Here, under the LOCAL_CONFIG part of the mc file, we place an F configuration command. The class that will be filled with values is the $=w class ($=w on page 876), a special one that contains all the names by which the local host can be known.

It will be filled with values by looking up the key CWhosts in the hash-type database that is contained in the file /etc/mail/access.

The key is optional, and it is not an error to omit it. This property can be useful for ldap-type maps, but is generally not useful for other database maps. For most database-map types, a missing key will simply match nothing and result in no values filling the class.

The type is mandatory. If it is missing (for example, if hash were omitted from the preceding declaration), the following error would be printed and logged:

fileclass: cannot open 'CWhosts@:/etc/mail/access': No such file or directory

If the type is misstated as one that does not exist (for example, if foo replaced hash), the following would be printed and logged:

fileclass: F{w}: class foo not available

If there is a problem with the detail (for example, if access were misspelled as acess), the following error would be printed and logged:

hash map "w": missing map file /etc/mail/acess.db: No such file or directory

If the key contains an @ character (as, for example, ), the part to the left of the first @ is taken as the key (gw) and the rest of the line through the : is taken as the type (wash.dc.gov@hash), yielding the following error:

F{w}: class wash.dc.gov@hash not available

There is no possible way to put an @ character into a key.

One use for filling a class with a database-map lookup might involve looking up the name for root on the local machine:

LOCAL_CONFIG
F{RootName}0@text:-k2 -v0 -z: /etc/passwd

Here, we need to know the name of root because it is not the same on all machines (some might call it toor, and others rot). The name found will be placed into the class $={RootName}. The text-type database map is used because it can look up keys in a plain file. The /etc/passwd file might look, in part, like this:

0th
 ↓
boss:Kmz4md67r66n2:0:1:Operator:/:/bin/csh daemon:*:1:1::/:
                   ↑
                  2nd

We wish to look up the first entry in that file that has a user-id of zero. Note that text type database maps are arranged in columns that are numbered, starting with column zero. In this case, the second column holds the user-id and the “zeroth” column holds the name we seek.

The F configuration command looks up the key 0 in a text type database map found in the file /etc/passwd. The database-map switches that prefix the file name tell sendmail to do the following: look up the key in the second column (the -k2); return the value from the zeroth column (the -v0); and use a colon as the column separator (the -z:). The text type database map and its switches are described in text on page 941.

Class by replacing files with database lookups in mc macros

Several mc macros are used to fill class macros with values. They are listed in Table 22-1, along with the class macros they fill. Note that the classes shown should not be used directly because there is no guarantee that they will continue to be available in the future. To be safe, always use the mc macro instead. To reinforce this precaution in the descriptions that follow, we use the mc name for the class (as the EXPOSED_USER class) instead of the class macro name (as the $=E class).

Table 22-1. mc macros used to fill class macros

mc macro

§

Class macro

CANONIFY_DOMAIN_FILE

FEATURE(nocanonify) on page 634

$={Canonify}

confCT_FILE

FEATURE(use_ct_file) on page 643

$=t

EXPOSED_USER_FILE

EXPOSED_USER mc Macro on page 599

$=E

GENERICS_DOMAIN_FILE

FEATURE(generics_entire_domain) on page 622

$=G

LDAPROUTE_DOMAIN_FILE

LDAPROUTE_DOMAIN and LDAPROUTE_DOMAIN_FILE on page 924

$={LDAPRoute}

LDAPROUTE_EQUIVALENT_FILE

LDAPROUTE_DOMAIN and LDAPROUTE_DOMAIN_FILE on page 924

$={LDAPRouteEquiv}

LOCAL_DOMAIN

$=w on page 876

$=w

LOCAL_USER_FILE

LOCAL_USER mc Macro on page 605

$=L

MASQUERADE_DOMAIN_FILE

MASQUERADE_DOMAIN mc Macro on page 600

$=M

MASQUERADE_EXCEPTION_FILE

MASQUERADE_EXCEPTION_FILE mc Macro on page 602

$=N

RELAY_DOMAIN_FILE

The RELAY_DOMAIN_FILE mc macro on page 269

$=R

VIRTUSER_DOMAIN_FILE

FEATURE(virtuser_entire_domain) on page 645

$={VirtHost}

It is possible to fill these class macros from database maps using these mc macros. Instead of the filename, just place the database lookup expression between the trailing parentheses of the mc macro. For example, consider this way of filling the RELAY_DOMAIN class with values from the access database, assuming the following entry exists in your access database:

DomainList:      our.domain their.domain another.domain

Recall that the RELAY_DOMAIN class (The RELAY_DOMAIN mc macro on page 269) determines which domains you want to relay for. The idea here is that you want to fill it with the values our.domain, their.domain, and another.domain. You could perform that lookup with an mc configuration line such as this:

RELAY_DOMAIN_FILE(`DomainList:@hash:/etc/mail/access')

Here, DomainList: (colon included) is the key looked up in the hash-type database-map located in the database file /etc/mail/access. The presence of the literal @ tells sendmail this is a database-map lookup, and not the name of a file to read.

To use an example from the previous section, consider adding a user-id name to the EXPOSED_USER class (EXPOSED_USER mc Macro on page 599) like this:

EXPOSED_USER_FILE(`0@text:-k2 -v0 -z: /etc/passwd')

This lookup would result in the addition of the name boss (from the previous section) to the EXPOSED_USER class.

Class via ldap map lookups

Adding values to class macros with ldap-type map databases is very easy. In its simplest form, just use a literal @LDAP as the type and nothing else:

RELAY_DOMAIN_FILE(`@LDAP')
FR@LDAP

The first form uses the mc macro RELAY_DOMAIN_FILE to add values to the RELAY_DOMAIN class (The RELAY_DOMAIN mc macro on page 269). The second line adds to the same class, but uses the F configuration command. For both lines, the database used for the lookup is the ldap-type database because of the literal @LDAP in both. That literal expression causes the following default ldap schema to be used:

-k (&(objectClass=sendmailMTAClass)(sendmailMTAClassName=R)
   (|(sendmailMTACluster=${sendmailMTACluster})(sendmailMTAHost=$j)))
   -v sendmailMTAClassValue

When using the F configuration command form, you must specify the class to be filled. For example:

F{OurStuff}@LDAP

Whichever class you specify (the {OurStuff} here) will become the class listed with the sendmailMTAClassName= in the default schema:

-k (&(objectClass=sendmailMTAClass)(sendmailMTAClassName={OurStuff}
)
   (|(sendmailMTACluster=${sendmailMTACluster})(sendmailMTAHost=$j)))
   -v sendmailMTAClassValue

Naturally you can bypass the default ldap definition altogether by placing your own into the declaration. Consider the following two lines, which do just that:

VIRTUSER_DOMAIN_FILE(`@ldap:-k (&(objectClass=virtHosts)(host=*)) -v host')
F{VirtHosts}@ldap:-k (&(objectClass=virtHosts)(host=*)) -v host

Note that by replacing the literal @LDAP with a type declaration of @ldap, you eliminate the automatic generation of a default definition.

One possible pitfall is the temptation to define an identical class macro’s values in both your domain record and individual host records. If you do, the lookup will be additive, adding record values from both the domain and the host records.

Access Classes in Rules

Class macros are useful only in the LHS of rules. The sendmail program offers two ways to use them:

$=X

The $= prefix causes sendmail to seek a match between the workspace and one of the words in a class list.[334]

$˜X

The prefix causes sendmail to accept only a single token in the workspace that does not match any of the words in a class list.

Matching Any in a Class: $=

The list of words that form a class are searched by prefixing the class name with the characters $=:

R$=X    $@<$1>

In this rule, the expression $=X causes sendmail to search a class for the word that is in the current workspace. If sendmail finds that the word has been defined, and if it finds that the word is associated with the class $=X, only then is a match made.

The matching word is made available for use in the RHS rewriting. Because the value of $=X is not known ahead of time, the matched word can be referenced in the RHS with the $digit positional operator.

Consider the following example. Two classes have been declared elsewhere in the configuration file. The first, $=w, contains all the possible names for the local host:

Cw localhost mailhost server1 server2

The second, $=D, contains the domain names of the two different networks on which this host sits:

CD internal.domain external.domain

If the object of a rule is to match any variation on the local hostname at either of the domains and to rewrite the result as the official hostname at the appropriate domain, the following rule can be used:

R $=w . $=D    $@ $w . $2     make any variations "official"

If the workspace contains the tokenized address server1.external.domain, sendmail first checks to see whether the word server1 has been defined as part of the class w. If it has, the dots in the rule and workspace match each other, and then sendmail looks up external.domain.

If both the host part and the domain part are found to be members of their respective classes, the RHS of the rule is called to rewrite the workspace. The $2 in the workspace corresponds to the $=D in the LHS. The $=D matches the external.domain from the workspace, so that text is used to rewrite the new workspace.

Note that prior to V8, when sendmail looked up the workspace to check for a match to a class, it looked up only a single token. V8 sendmail allows multitoken class matching.

Matching Any Token Not in a Class: $~

The prefix is used to match any single token in the workspace that is not in a class. It is used fewer than a dozen times in a typical production configuration file, but when the need for its properties arises, it can be very useful.

To illustrate, consider a network with three PC machines on it. The PC machines cannot receive mail, whereas all the other machines on the network can. If the list of PC hostnames is defined in the class {PChosts}:

C{PChosts} pc1 pc2 pc3

a rule can be designed that will match any but a PC hostname:

R $* < @ $˜{PChosts} >     $@ $1 < @ $2 >       filter out the PC hosts

Here the LHS looks for an address of the form:

"user" "<" "@" "not-a-PC" "">

This matches only if the @ token is not followed by one of the PC hosts listed in class $={PChosts}. If the part of the workspace that is tested against the list provided by is found in that list, the match fails.

Note that the $digit positional operator in the RHS (the $2 in the preceding example) references the part that matches $˜{PChosts}. That is, $2 references the token in the workspace that is not in the class {PChosts}. If the workspace contains ben<@philly>, the $2 references the philly.

Also note that multitoken expressions in the workspace will not match. That is, for multitoken expressions in the workspace, is not the opposite of $=. To illustrate, consider this mini configuration file:

V10
CX hostA.com
Stest
R $˜X        $@ no $1 is not in X
R $=X        $@ yes $1 is in X
R $*         $@ neither

Now feed a multitokened address through these rules in rule-testing mode:

% /usr/sbin/sendmail -Cx.cf -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
Enter <ruleset> <address>
> test hostC.com
test               input: hostC . com
test             returns: neither

Here, the rule set returned neither because a multitoken expression in the workspace should never be used with . That is, looks for a workspace that is not a member of the class and, indeed, hostC.com is not. But because hostC.com is multi-tokened, acts as though it is a member of the class, and so does not call the RHS of the rule:

R $˜X     ← a multitokened workspace will never call the RHS

If you consider multitokens and as illegal to use together, this failure, although convoluted, makes sense.

Another way to think of this failure is by comparing the operator to the $- operator. Neither will match more than a single token in the workspace. If the does not match a single token, the LHS does not match, and the RHS is not called.

There are two ways to circumvent this problem. One alternative is to make the always look up only a single token:

R $~X $*        $@ no $1 is not in X

Here, the $* will match the .com. Then $~X will correctly look up only the single token hostC, and correctly not find it.

A second alternative is to invert the logic of the test, and use the $= prefix only when multiple tokens are in the workspace:

R $=X        $@ yes $1 is in X
R $*         $@ no $1 is not in X

Here, we first check to see whether the multitokened workspace is in the class $=X, and return yes if it is. Otherwise, we know it is not in the class.

Back Up and Retry

Multitoken matching operators, such as $+, always try to match the least that they can (Backup and Retry on page 660). Such a simple-minded approach could lead to problems in matching (or not matching) classes in the LHS. However, the ability of sendmail to back up and retry alleviates this problem. For example, consider the following five tokens in the workspace:

"A" "." "B" "." "C"

and consider the following LHS rule:

R $+ . $=X $*

Because the $+ tries to match the minimum, it first matches only the A in the workspace. The $=X then tries to match the B. and then B.C to the class $=X. If this match fails, sendmail backs up to the $+ and tries again.

The next time through, the $+ matches A. in the workspace, but that fails to match the dot in the rule, so it backs up again and matches A.B. The $=X tries to match the C in the workspace. If C is not in the class $=X, the entire LHS fails.

The ability of the sendmail program to back up and retry LHS matches eliminates much of the ambiguity from rule design. The multitoken matching operators try to match the minimum but match more if necessary for the whole LHS to match.

Class Name Hashing Algorithm

When comparing a token in the workspace to a list of words in a class array, sendmail tries to be as efficient as possible. Instead of comparing the token to each word in the list, one by one, it simply looks up the token in its internal string pool. If the token is in the pool and if the pool listing is marked as belonging to the class being sought, a match is found.

The comparison of tokens to entries in the string pool is case-insensitive. Each token is converted to lowercase before the comparison, and all strings in the string pool are stored in lowercase.

Because strings are stored in the pool as text with a type, the same string value can be used for different types with no conflict. For example, the symbolic name of a delivery agent and a word as a class macro’s value can be identical, yet they will still be separate entries in the string pool.

The sendmail program uses a simple hashing algorithm to ensure that the token is compared to the fewest possible strings in the string pool. In normal circumstances, that algorithm performs its job well. At sites with unusually large classes (perhaps a few thousand hosts in a class of host aliases), it might be necessary to tune the hashing algorithm. The code is in the file stab.c with the sendmail source. The number of hash buckets is set by the constant STABSIZE.

As an alternative to very full classes, sendmail offers database maps (Enable at Compile Time on page 879). No information is currently available contrasting the efficiency of the various approaches.

Classes with mc Configuration

In configuring with the mc technique, many classes are defined for your convenience. You need to be aware of these, not only to take advantage of them, but also to avoid reusing their names by mistake. Table 22-2 lists all the macros that the mc technique uses as of version 8.12. Most are described in other sections, but a few are described here. See a description of LOCAL_CONFIG (LOCAL_CONFIG mc macro on page 595) for the general method used for adding members and new class names using the mc configuration technique.

Table 22-2. Class macros used with the mc configuration technique

Class

§

Description

$={Accept}

Enabling the access Database Generally on page 277

With FEATURE(access_db), the possible acceptance strings from the access database (V8.10 and later)

$=B

FEATURE(bestmx_is_local) on page 617

With FEATURE(bestmx_is_local), the domains to look up in bestmx in place of $=w

$={Canonify}

FEATURE(nocanonify) on page 634

With CANONIFY_DOMAIN or CANONIFY_DOMAIN_FILE, do canonify these domains (V8.10 and later)

$=E

EXPOSED_USER mc Macro on page 599

With EXPOSED_USER or EXPOSED_USER_FILE, the list of exposed users

$=G

GENERICS_DOMAIN mc macro on page 624

With GENERICS_DOMAIN or GENERICS_DOMAIN_FILE, list of domains to look up in generics table

$=L

LOCAL_USER mc Macro on page 605

With LOCAL_USER or LOCAL_USER_FILE, the list of local users

$={LDAPRoute}

LDAPROUTE_DOMAIN and LDAPROUTE_DOMAIN_FILE on page 924

With LDAPROUTE_DOMAIN or LDAPROUTE_DOMAIN_FILE, route only LDAP hosts in this class

$={LDAPRouteEquiv}

LDAPROUTE_EQUIVALENT and LDAPROUTE_EQUIVALENT_FILE on page 924

With LDAPROUTE_EQUIVALENT or LDAPROUTE_EQUIVALENT_FILE, the host to treat as equivalent to $M for LDAP routing lookups (V8.12 and later)

$=M

MASQUERADE_DOMAIN mc Macro on page 600

With MASQUERADE_DOMAIN or MASQUERADE_DOMAIN_FILE, the list of hosts to masquerade

$=N

MASQUERADE_EXCEPTION mc Macro on page 601

With MASQUERADE_EXCEPTION or MASQUERADE_EXCEPTION_FILE, the hosts excepted from masquerading

$=O

Follows table

The list of nonusername characters that can cause forwarding (<, >, %, and possibly !)

$=P

Follows table

The list of pseudo top-level domains (e.g., .uucp and .fax)

$={ResOk}

$={ResOk} on page 874

Mark a successful DNS lookup.

$=R

The RELAY_DOMAIN mc macro on page 269

With RELAY_DOMAIN or RELAY_DOMAIN_FILE, the list of domains and hosts for which to relay

$={SpamTag}

Accept and Reject per Recipient on page 284

With FEATURE(delay_checks), holds the strings SPAMFRIEND and SPAMHATER (V8.10 and later)

$={src}

Follows table

List of rule sets to call for searching the access database map (prior to V8.13 called this)

$={Src}

Follows table

List of rule sets to call for searching the access database map (V8.13 and later called this)

$={tls}

$={tls} and $={Tls} on page 875

Possible values for TLS policy in the access database map (prior to V8.13 called this)

$={Tls}

$={tls} and $={Tls} on page 875

Possible values for TLS policy in the access database map (V8.13 and later called this)

$={TrustAuthMech}

SASL and Your mc File on page 191

With TRUST_AUTH_MECH, the mechanisms used to allow relaying (V8.10 and later)

$=U

UUCP Support on page 606

With MAILER(uucp), the locally connected UUCP hosts

$=V

UUCP Support on page 606

With MAILER(uucp), the hosts connected to UUCP relay $V

$={VirtHost}

FEATURE(virtuser_entire_domain) on page 645

With VIRTUSER_DOMAIN or VIRTUSER_DOMAIN_FILE, the list of additional domains to look up in virtuser beyond $=w (V8.10 and later)

$=W

UUCP Support on page 606

With MAILER(uucp), the hosts connected to UUCP relay $W

$=X

UUCP Support on page 606

With MAILER(uucp), the hosts connected to UUCP relay $X

$=Y

UUCP Support on page 606

With MAILER(uucp), the locally connected smart UUCP hosts

$=Z

UUCP Support on page 606

With MAILER(uucp), the locally connected domainized UUCP hosts

The class $=O is used by the m4 technique to hold a list of characters that cannot be used in local usernames. This list is used to detect certain kinds of routing addresses that might otherwise be difficult to detect. This list initially contains:

@ %

but can also contain an ! if UUCP support is included.

The class $=P holds a list of pseudodomains that will not be looked up using DNS. Unless you use a FEATURE(), this class will contain a dot only. Various FEATURE()s will add appropriate pseudodomains to it, such as .UUCP and .REDIRECT.

The class $={src} (prior to V8.13) or $=Src (V8.13 and later) holds a list of rule set names that can be called to look up items in the access database. It is a clever trick that you might wish to copy for use in your own rule sets. To see how this trick is performed, look for that expression in your configuration file.

Internal Class Macros

Prior to V8 sendmail, only the class $=w was used internally, and only a small handful of classes were used in the configuration file. Recently, more and more classes have been added to that list. Table 22-3 lists all the class macros defined internally by sendmail as of V8.14.

Table 22-3. All the class macros defined internally by sendmail

Class

§

Description

$=b

$=b on page 870

MIME types for no NL-to-CRLF translation

$={checkMIMEFieldHeaders}

$={checkMIMEFieldHeaders} on page 870

MIME headers for maximum parameter length checking

$={checkMIMEHeaders}

$={checkMIMEHeaders} on page 871

MIME headers for maximum legal length checking

$={checkMIMETextHeaders}

$={checkMIMETextHeaders} on page 871

MIME headers for maximum arbitrary length checking

$=e

$=e on page 872

Encode this Content-Transfer-Encoding:

$=k

$=k on page 872

The local UUCP name

$=m

$=m on page 872

List of local domains

$=n

$=n on page 873

Don’t encode these Content-Types

$={persistentMacros}

$={persistentMacros} on page 873

Macros preserved in the qf file

$=q

$=q on page 874

Always quoted-printable encode Content-Type:

$=s

$=s on page 875

Presume an RFC2822 7-bit body

$=t

$=t on page 875

List of trusted users

$=w

$=w on page 876

List of our other names

Note that these classes really are used internally by sendmail, so don’t try to redefine their use in the configuration file. Such an attempt will be doomed to failure.

Pitfalls

  • Although a class macro name can be any ASCII character[335] (any character in the range 0x0 to 0x7f), avoid using any of the nonletter characters. At the very least, they create confusing reading, and at worst they can cause sendmail to completely misinterpret your intentions.

  • Although values can traditionally be made to contain whitespace by quoting them, class macros will misinterpret those quotes. For example, "vax ds1" wrongly parses into two class entries: "vax and ds1", with the quotes a part of each.

  • Duplicate values are silently ignored. Therefore, typos in a list of values can cause an accidentally duplicated entry to be silently excluded.

  • Avoid creating a new class macro name without first checking to see whether it has already been used. That is, don’t create a list of UUCP hosts within class $=U without first checking both for preexisting CU and FU definitions and for rule-set uses of $=U and $˜U. It is perfectly legal for the $=U and $˜U expressions to exist in rule sets without a corresponding CU or FU definition. However, such empty references will still cause sendmail to search the string pool.

  • Under V8 sendmail, you can watch your class macro definitions being formed by using the -d37.8 debugging switch (-d37.8 on page 564). Under other versions of sendmail, you can only approximate this information by using the -d36.9 debugging switch.

  • The file form’s scanf(3) pattern can produce unexpected results. Remember that the pattern is applied to a line, not to a stream.

  • No error checking is performed during reads for the F form of the class configuration command. An I/O error reading from a file silently causes the rest of that file’s contents to be ignored. An unreported error from a program (one that silently returns 0 on both success and failure) is also silently ignored by sendmail.

Alphabetized Class Macros

We document most of the class macros employed by sendmail in chapters appropriate to the use of each. Here we collect, and document, those few class macros that have no other natural home.

$=b

MIME types for no NL-to-CRLF translation V8.8 and later

Ordinarily, MIME mail is translated into SMTP format before it is encoded with Base64. Specifically, the newline character that ends each line is converted into the SMTP carriage-return/linefeed form before being encoded. This adds time to the process, and extra size to the result, and for some forms of MIME mail this translation makes little sense. Video, for example, is not text-oriented, and so should not be treated like text (even though it will be encoded as text for transmission).

Beginning with V8.8, sendmail will skip converting newlines under certain conditions. Before deciding to convert, sendmail extracts the type and subtype from the Content-Type: header (Content-Type: on page 1154):

Content-Type: type/subtype; ...

If the type is in the class $=b, newline conversion will be skipped. If a concatenation of type, a slash (/), and subtype are in class $=b, newline conversion will also be skipped.

Note that this class is not automatically available. To use it in this way, you need to define USE_B_CLASS when you compile sendmail.

If you define USE_B_CLASS, sendmail will automatically assign to class $=b the values application/octet-stream, image, audio, and video.

$={checkMIMEFieldHeaders}

MIME headers for maximum parameter length checking V8.10 and later

Beginning with V8.10 sendmail, the MaxMimeHeaderLength option (MaxMimeHeaderLength on page 1047) can be used to define the maximum length for the parameters that some MIME headers take. A parameter is separated from the main header name and value by a semicolon:

name: value  ;  parameter  ; parameter  ...

Before checking that parameter’s length, sendmail looks to see whether the header name is in the class $={checkMIMEFieldHeaders}. If it isn’t, sendmail skips the parameter length check.

When V8.10 sendmail starts up, it predefines the $={checkMIMEFieldHeaders} class to contain two MIME headers: the Content-Disposition: header (Content-Disposition: on page 1153); and the Content-Type: header (Content-Type: on page 1154). You can add more headers with the C or F configuration file command.

If any of these parameters are found to be too long, they are truncated to the limit imposed by the MaxMimeHeaderLength option (MaxMimeHeaderLength on page 1047).

$={checkMIMEHeaders}

MIME headers for maximum legal length checking V8.10 and later

Beginning with V8.10 sendmail, the MaxMimeHeaderLength option (MaxMimeHeaderLength on page 1047) can be used to define the maximum length for selected MIME headers. Before making that check, sendmail looks to see whether a particular header is in the class $={checkMIMEHeaders}. If it isn’t, sendmail skips this length check.

When V8.10 sendmail starts up, it predefines the $={checkMIMEHeaders} class to contain five MIME headers: the Content-Disposition: header (Content-Disposition: on page 1153); the Content-Id: header (Content-Id: on page 1153); the Content-Transfer-Encoding: header (Content-Transfer-Encoding: on page 1154); the Content-Type: header (Content-Type: on page 1154); and the MIME-Version: header (MIME-Version: on page 1160). You can add more headers with the C or F configuration file command.

If any of these headers are found to be too long, they are truncated to the length specified by the MaxMimeHeaderLength option (MaxMimeHeaderLength on page 1047). Note that this truncation is done carefully so as to maintain the appearance of an RFC2822-legal header.

$={checkMIMETextHeaders}

MIME headers for maximum arbitrary length checking V8.10 and later

Beginning with V8.10 sendmail, the MaxMimeHeaderLength option (MaxMimeHeaderLength on page 1047) can be used to define the maximum length for selected MIME headers that present text descriptions. Before making that check, sendmail looks to see whether a particular header is in the class $={checkMIMETextHeaders}. If it isn’t, sendmail skips this length check.

When V8.10 sendmail starts up, it predefines the $={checkMIMETextHeaders} class to contain the single MIME header Content-Description: header (Content-Description: on page 1153). You can add more headers with the C or F configuration file command.

If this header’s value is found to be too long, it is truncated to the length specified by the MaxMimeHeaderLength option. Note that this is a blatant truncation, and no effort is made to keep the header legal because it contains only random text.

Note also that you should use $={checkMIMEHeaders} ($={checkMIMEHeaders} on page 871) for RFC-format-specific headers.

$=e

Encode this Content-Transfer-Encoding: V8.7 and later

The F=7 delivery agent flag (F=7 on page 764) determines whether MIME-encoded data should be converted from 8 to 7 bits. If the message is in 8-bit format and if it is going to a MIME-capable destination that requires 7-bit data, the message body will be converted to 7 bits by using either quoted-printable or Base64 (EightBitMode on page 1025).

Not all datatypes should be converted to 7 bits, however. The types that might possibly be converted are listed with the Content-Transfer-Encoding: header (Content-Transfer-Encoding: on page 1154). One type that should not be converted, for example, is the quoted-printable type because it is already converted. Types that can be converted are 7bit, 8bit, and binary.

Beginning with V8.7 sendmail, the class $=e is used to determine whether a type will be encoded. Only those values listed in this class will be encoded. When sendmail first starts, it initializes the list of values in class $=e to be:

7bit 8bit binary

You can add types to this class, but you can never remove them.

Note that a type in class $=e can still be prevented from being encoded on the basis of the considerations imposed by class $=n. Also note that the actual encoding can be restricted to quoted-printable by use of the class $=q.

$=k

The local UUCP name V8.6.5 and later

When sendmail first begins to run, it figures out what your local UUCP node name is and assigns the result to the $k macro ($k on page 831). At the same time, it assigns the same name to this class $=k.

$=m

List of local domains V8.7 and later

When sendmail first begins to run, it figures out what your DNS domain is and assigns the result to the $m macro ($m on page 833). The sendmail program then processes the configuration file. This gives you the opportunity to redefine $m. After that, sendmail assigns the final value in $m to the class $=m.

Unfortunately, prior to V8.10 sendmail, the class macro $=m was not used by sendmail, or by any of the configuration files produced by the m4 technique. Beginning with V8.10, $=m is used as part of screening to allow relaying. Note that $=m should not be used to have mail accepted as local under a variety of domains. Instead, use FEATURE(domaintable) (FEATURE(domaintable) on page 621).

$=n

Don’t encode these Content-Types V8.7 and later

Although some MIME content types can be converted to 7 bits, not all types should be. Content types are defined by the Content-Type: header (Content-Type: on page 1154). For example, the type multipart/ should not be converted, whereas its component boundary-separated parts probably should be. Conversion is done by encoding with either quoted-printable or Base64 (EightBitMode on page 1025).

Beginning with V8.7 sendmail, types that should not be encoded are those defined as members of the class $=n. When sendmail first starts to run, it defines the following list of values for class $=n:

multipart/signed

As of V8.10, no other useful values exist for this class.

Note that a type in class $=n can still be prevented from being encoded based on the considerations imposed by class $=e. Also note that the actual encoding can be restricted to quoted-printable by use of the class $=q.

$={persistentMacros}

Macros preserved in the qf file V8.10 and later

When a message is first accepted, sendmail usually queues it first,[336] then tries to deliver it. The qf file contains all the envelope information about a message, including information specific to the sendmail delivery process, and several macros whose values are important to preserve between queue runs. This {persistentMacros} class holds the names of those important macros.

When V8.10 sendmail and later starts to run, it adds to the {persistentMacros} class a list of five macro names:

  • The $r macro ($r on page 842) holds the protocol used to receive a message when it was first accepted.

  • The $s macro ($s on page 844) holds the hostname of the sender’s machine.

  • The $_ macro ($_ on page 801) holds the validated hostname and address, RFC1413-validation (if available), and IP source route information associated with the incoming SMTP connection.

  • The ${if_addr} macro (${if_addr} on page 827) holds the IP address of the interface on which the message was received.

  • The ${daemon_flags} macro (${daemon_flags} on page 818) holds the flags specified by the DaemonPortOptions option (DaemonPortOptions on page 993).

To add macro names to this class, omit the leading dollar symbol. For example, you might add the macro ${MyMacro} like this:

LOCAL_CONFIG
C{persistentMacros} {MyMacro}

However, you are strongly advised not to add any macros to this class. Should you feel the need to do so, take enough time to fully examine how that macro is used in rule sets, and how it can be used internally by sendmail. Then cautiously test and observe to be certain nothing broke when you added it.

$=q

Always quoted-printable encode Content-Type: V8.8 and later

The EightBitMode (8) option (EightBitMode on page 1025) determines when and how 8-bit data will be encoded into a 7-bit format. Ordinarily, the decision to use quoted-printable as opposed to Base64 is made by examining the input stream and choosing quoted-printable if less than 1/8 of the first 4 kilobytes of data has the high bit set. Otherwise, encoding is with Base64.

Beginning with V8.8, sendmail offers the class $=q as the means to force the selection of quoted-printable. Just before scanning the input data, sendmail extracts the type and subtype from the Content-Type: header (Content-Type: on page 1154):

Content-Type: type/subtype; ...

If the type is in the class $=q, the body will definitely be encoded with quoted-printable if encoding occurs. Also, if a concatenation of type, a slash (/), and subtype is in class $=q, the body will definitely be encoded with quoted-printable.

When sendmail first begins to run, class $=q is empty. A reasonable value in most countries might be text/plain (although probably not in countries that use 16-bit characters, such as China). Other values for this class might be text or text/html.

$={ResOk}

Mark a successful DNS lookup V8.12 and later

FEATURE(accept_unresolvable_domains) (FEATURE(accept_unresolvable_domains) on page 614) allows all mail to be received, even when the domain part of the envelope-sender address cannot be looked up. This feature is implemented in rules, in part, by using the $={ResOk} class macro to hold a value that indicates that an unresolved, envelope-sender address is acceptable.

The $={ResOk} class macro is strictly intended for use by this feature and should not be used for anything else, or be modified in any way.

$=R

Hosts for whom to relay V8.9 and later

The class $=R holds as its list of values the host and domain names that sendmail should allow mail to be relayed to. This $=R class should not be used directly because it could change without notice in future versions of sendmail. See RELAY_DOMAIN (The RELAY_DOMAIN mc macro on page 269) and RELAY_DOMAIN_FILE (The RELAY_DOMAIN_FILE mc macro on page 269).

$={tls} and $={Tls}

Possible values for TLS policy in access map V8.12 and later

The tls_server rule set is called at the start of any connection in which the local sendmail would normally issue the STARTTLS SMTP command. The tls_client rule set is called at the start of any inbound connection in which the STARTTLS SMTP command was offered. Both rule sets look up information in the access database. (See The access database with tls_server and tls_client on page 214 for a full description of this process.)

The tls_server rule set prefixes its lookups with a literal TLS_Srv: expression, and the tls_client rule set prefixes its lookups with a literal TLS_Clt: expression. Among the possible returned values from the lookup can be two special keywords:

TLS_Srv:hostA.domain            VERIFY
TLS_Clt:hostB.domain            ENCR:bits

These two special keywords (VERIFY and ENCR) are not defined inside sendmail. Instead, they are defined as values given to the class $={tls} (prior to V8.13) or $={Tls} (V8.13 and later).

This class macro is properly defined in your default configuration file and should never need adjustment.

$=s

Presume an RFC2822 7-bit body V8.7 and later

An email message as defined by RFC822 cannot contain 8-bit data. Consequently, when the MIME Content-Type: header declares a message subtype that is rfc822, we immediately know that it will contain nothing that needs 8- to 7-bit encoding:

Content-Type: message/rfc822

As other message subtypes evolve, this assumption can safely be made about them too. So, to make sendmail more adaptable, the $=s class was added beginning with V8.7. This class contains a list of subtypes that should be treated the same as rfc822. When sendmail first begins to run, it initializes that list to contain:

rfc822

Other subtypes that can legitimately appear here might be partial or delivery-status.

Note that this provides only an initial hint to sendmail. The rfc822 subtype can itself contain MIME information that might require 8- to 7-bit encoding.

$=t

List trusted users V8.7 and later

Trusted users are those who can run sendmail with the -f command-line switch to specify who sent the message, without generating a warning. Prior to V8.6 sendmail, such users had to be listed with the T command (Declare trusted users (ignored V8.1 through V8.6) on page 174). That command was ignored in V8.1 through V8.6, and with those versions anyone could use the -f switch. Beginning with V8.7 sendmail, the T command was reintroduced, but it now causes the list of trusted users to be added to the class $=t. Now, any user who uses the -f switch and who is not listed in class $=t will cause the following error message (X-Authentication-Warning: on page 1167) to be included in the outgoing mail message (if the PrivacyOptions option, PrivacyOptions on page 1065, has authwarnings set):

X-Authentication-Warning: user set sender to other using -f

See FEATURE(use_ct_file) (FEATURE(use_ct_file) on page 643) for an easy way to add users to this class using the m4 technique.

$=w

List of our other names All versions

Before the sendmail program reads its configuration file, it calls gethostbyname(3) or getipnodebyname(3) to find all the known aliases for the local machine. The argument given to gethostbyname(3) or getipnodebyname(3) is the value of the $w macro that was derived from a call to gethostname(3) ($w on page 850).

Depending on the version of sendmail you are running, the aliases that are found will be either those from your /etc/hosts file or those found as additional A or AAAA records in a DNS lookup. Then, depending on the DontProbeInterfaces option (DontProbeInterfaces on page 1023), sendmail will round out that picture by examining (probing) each network interface and extracting from it the associated IP address or hostname.

To see the aliases that sendmail found, or to see what it missed and should have found, use the -d0.4 debugging switch (-d0.4 on page 542). Any aliases that are found are printed as:

aka: alias

Depending on your version of sendmail, each alias is either a hostname (such as rog.stan.edu) or an IPv4 address (such as [123.45.67.8]), or an IPv6 address (such as [IPv6:2002:c0a8:51d2::23f4]).

Prior to V8.13, sendmail would also add leading name components to the list of host names in $=w (for example, for the hostname a.b.c.d, it would add a and a.b). Also prior to V8.13, each such name found (if not duplicated) would be reverse-looked-up to find its IP number and that IP number would be added to the list. Beginning with V8.13, these two steps are skipped. If you are running pre-V8.13 sendmail and you desire those hostname variations to be added to the list of hostnames, you will henceforth have to add them to class $=w yourself.

Many sendmail.cf files use the $=w class macro to define all the ways users might reference the local machine. This list must contain all names for the local machine as given in the /etc/hosts file, all names for the local host as listed in DNS (including CNAME and MX records), and the names associated with your network interfaces. For example:

# All our routing identities
Cw server1 server2
# All our local aliases
Cw localhost mailhost tops-link print-router loghost
# DNS records
Cw serv-link
# We are a bitnet registered node
Cw bitserver

The correct way to add these domains to $=w in your mc file is with LOCAL_DOMAIN, like this:

LOCAL_DOMAIN(`server1 server2')
LOCAL_DOMAIN(`localhost mailhost tops-link print-router loghost')
LOCAL_DOMAIN(`serv-link')
LOCAL_DOMAIN(`bitserver')

Another correct way to add hostnames to class $=w is with FEATURE(use_ct_file) (FEATURE(use_cw_file) on page 643).

In addition to hostnames, you can also add addresses to the $=w class. To do so, just surround each address with square braces:

LOCAL_DOMAIN(`[123.45.67.8]')                  ← IPv4 address
LOCAL_DOMAIN(`[IPv6:2002:c0a8:51d2::23f4]')    ← IPv6 address

Note in the second example that you must prefix any IPv6 addresses with a literal IPv6: expression. That prefix signals to sendmail that it is dealing with an IPv6 address.



[331] * Note that when a class name is a single character, it can be referenced with or without enclosing curly braces, with no change in meaning. That is, CX and C{X} are equivalent.

[332] * This was removed from V8.1 sendmail because it presented a security risk. It was restored to V8.7 and later because sendmail now checks permissions more carefully and exec(2) is the program itself, instead of using the old, buggy popen(3) approach of yore.

[333] * The version of sendmail that you are using must have been compiled with SCANF defined (SCANF on page 137) for scanf(3) to be usable from within the configuration file.

[334] * With V8 and later, words in a class can be multitokened.

[335] * Other than the { character.

[336] * If the SuperSafe option (SuperSafe on page 1096) is false, or interactive with the DeliveryMode option (DeliveryMode on page 1004) also set to interactive, and if the DataFileBufferSize (DataFileBufferSize on page 998) and XscriptFileBufferSize (XscriptFileBufferSize on page 1117) options are large enough, it is possible that no mail will ever hit the disk.