Chapter 23. The K (Database-Map) Configuration Command

Database maps can be used to look up information in databases, to perform transformations (such as dequoting), to perform computations, and to store values into macros. In their database role, they offer these advantages:

  • Information can be easily changed without having to restart sendmail because database information is external to the configuration file.

  • The sendmail program starts up faster because only the location of the information is stored at startup, not the information itself.

  • Rules are made more versatile because database information can be used in the RHS of rules. Class macros are still of use in the LHS.

To fully appreciate sendmail databases, consider the only alternative, the F configuration command. For example, mail that is sent via UUCP is a typical application that requires lists of information:

FU /etc/mail/uuhosts

Here, the external file /etc/mail/uuhosts contains a list of UUCP hosts connected to the local machine. If the list rarely changes, the F command is appropriate. On the other hand, if the list is volatile and changes often, the F command has drawbacks. The file /etc/mail/uuhosts is read only when the configuration file is processed. Any change to that file is ignored by a running sendmail (such as the daemon). To make the change effective, the daemon needs to be restarted.

In such volatile situations, storing UUCP information in a database is preferred. A change to a database is immediately available to the running daemon, eliminating the need to restart.

V8 sendmail is designed to rewrite addresses on the basis of information looked up in external databases or in its internal symbol table. It can use a wide variety of database forms, ranging from ndbm(3) files (dbm on page 903) to Hesiod network database maps (hesiod on page 909). The K configuration command (The K Configuration Command on page 882) is used to declare the name, location, and other parameters of databases or to modify use of its symbol table.

In their nondatabase role, database maps can also be used to perform a wide range of services that make the use of rules and rule sets easier and more versatile. For example, database maps can be used to:

  • Assign a value to a macro

  • Log information using the syslog facility

  • Perform mathematical computations and comparisons

  • Remove quotation marks from quoted strings

The $( and $) database-map operators (Use $( and $) in Rules on page 892) are used in the RHS of rules to access and utilize the information produced by all the database-map roles.

Enable at Compile Time

Vendors that provide V8 sendmail in precompiled form might or might not provide access to all the types of databases that V8 sendmail supports. If your online documentation lacks this information, you can run sendmail with the -d0.4 debugging switch to discover what it supports:

% /usr/sbin/sendmail -d0.4 -bt

Version 8.14.1
 Compiled with: MAP_REGEX LOG MIME7TO8 MIME8TO7 NAMED_BIND NETINET
                NETUNIX NIS NEWDB QUEUE SCANF SMTP TCPWRAPPERS USERDB
                XDEBUG
...

In this implementation of sendmail the following databases are available: regular-expression (the MAP_REGEX), Sun nis (the NIS), the bestmx database-map type (the NAMED_BIND), and the Sleepycat DB’s hash and btree types (the NEWDB). Many internal database maps needed by sendmail are also automatically included without being enabled. They are text, stab, implicit, user, host, program, sequence, null, syslog, arith, macro, and switch. Note that hesiod and nisplus database maps are not supported by this particular sendmail binary (neither HESIOD nor NISPLUS was printed in the preceding output).

If you download and compile sendmail yourself, you can include any supported databases. Support is declared in your m4 Build file. For example, the following includes support for the dns database-map type:

APPENDDEF(`confMAPDEF', `-DDNSMAP')

Here, APPENDDEF is used to append the compile-time switch to any previous definitions. The -DDNSMAP is the compile-time switch that, when given a positive, nonzero value, enables inclusion of that support.

Possible compile-time switches are shown in Table 23-1.

Table 23-1. m4 definitions for confMAPDEF

Switch

§

Database support included

-DDNSMAP

dns on page 905

dns lookups (V8.12 and later)

-DHESIOD

hesiod on page 909

hesiod(3) aliases, and userdb

-DLDAPMAP

ldap (was ldapx) on page 912

ldap(3)

-DMAP_NSD

nsd on page 929

IRIX nsd

-DMAP_REGEX

regex on page 932

Regular expression support

-DNDBM

dbm on page 903

ndbm(3) database files (dbm)

-DNAMED_BIND

bestmx on page 902

bestmx(3) DNS lookups

-DNETINFO

netinfo on page 926

NeXT netinfo(3) aliases only

-DNEWDB

btree on page 901

db(3) hash and btree databases, and userdb

-DNIS

nis on page 927

Sun NIS network database maps

-DNISPLUS

nisplus on page 928

Sun NIS+ network database maps

-DPH_MAP

ph on page 930

PH database maps

-DSOCKETMAP

socket on page 936

Socket database maps (V8.13 and later)

For example, the default Build m4 file for Ultrix (in devtools/OS/ULTRIX) might include this line:

define(`confMAPDEF', `-DNDBM=1 -DNIS=1')

which includes support for ndbm(3) and nis(3) database maps, whereas the m4 file for SunOS 5.5 might include the following:

define(`confMAPDEF', `-DNDBM=1 -DNIS=1 -DNISPLUS=1 -DMAP_REGEX=1')

which also includes support for the nisplus database map and regular expressions.

Beginning with V8.9, sendmail automatically determines whether NEWDB should be included by default. Only nonstandard locations of the db libraries will prevent this. So, in addition to the database support shown earlier, standard installations will also have db(3) support.

If you omit all database support with a declaration such as this in your m4 Build file:

define(`confMAPDEF', `')

and if your db libraries are in a nonstandard location, a sendmail binary will be created that will be unable to maintain its aliases in database format. Also, any attempt to rebuild the aliases database (with newaliases or with -bi) will fail with the following error message:

Cannot rebuild aliases: no database format defined
Cannot create database for alias file /etc/mail/aliases: No such device

Note that if you add new database-map types, you might also have to add to your m4 Build configuration file libraries with the confLIBS compile-time macro (confLIBDIRS on page 82) and #include-file directories with the confINCDIRS compile-time macro (confINCDIRS on page 78). For example:

APPENDDEF(`confINCDIRS', `-I/packages/include/db')
APPENDDEF(`confLIBDIRS', `-L/packages/lib')
APPENDDEF(`confMAPDEF', `-DNEWDB')

Here, support for db(3) is included where it otherwise would not have been because of its nonstandard location in /packages.

Create Files with makemap

The makemap program, supplied in source form with V8 sendmail, is fully described in The makemap Program on page 370. It is used to create database files and is run, in brief, from the command line like this:

% makemap type file < textfile

The type can be either dbm (which uses the ndbm(3) library routines), hash, or btree (both of which use the db(3) library routines). The file is the location and name (full path or relative name) for the database file to create. For dbm files, the .pag and .dir suffixes are added automatically. For db files, the .db suffix will be added automatically if it is not already included in the name.

The makemap program reads from its standard input. That input is line-oriented and contains the text from which the database files will be created. Lines that begin with a # are interpreted as comments and ignored. Lines that contain no characters (empty lines) are also ignored. Whitespace (spaces or tabs) separates the key on the left from the data on the right. An example of such an input file is the following:

lady     relaysite!lady
my.host  relaysite!lady
bug      bug.localuucp

The second line in this example shows that keys can be multitokened (my.host is three tokens). In reading from existing files, some conversion might be required to massage the input into a usable form. To make a database of the /etc/hosts file (for converting hostnames into IP addresses), for example, a command line such as the following might be required:[337]

% awk '/^[^#]/ {print $2, $1}' /etc/hosts | makemap ...

Here, awk(1) needs to eliminate comment lines (the /^[^#]/). Otherwise, it will wrongly move them to the second column, where makemap will not recognize them as comments.

The K Configuration Command

The K configuration command is used to associate a symbolic name with a database-map type. The symbolic name will later be used in the RHS of rules. The form of the K command looks like this:

Kname type args

The name is the symbolic name, the type is the kind of database map to use, and the args specifies its location and properties. We describe each in turn.

The name

The name portion of the K configuration command immediately follows the K. Whitespace between the K and the name is optional:

K name type argsoptional whitespace

The name must begin with a letter or digit and can contain only letters, digits, and the underscore character:

K local_hosts      ← good
K $andcents        ← bad

The case of the letters in name does not matter. All names are converted to lowercase before they are stored:

K LOCAL_Hosts
K local_hosts      ← the same

If you begin a name with a bad character, the following error will be printed and that K line will be ignored:

configfile: line num: readcf: config K line: no map name

If a bad character appears in the middle of a name, the part preceding the bad character will be taken as the name, and the part following the bad character will be taken as the type. For example, the name me@home will produce this error:

configfile: line num: readcf: map me: class home  not available

The type

Recall that the type[338] portion of the K configuration command follows the name:

Kname type args

Note that whitespace between the name and the type can be a joined indented line, which allows commenting and improves readability:

Kname             # Why this name
        type      # Why this type
        args      # and so on

The type declares which sort of database map to use. It must be one of the types listed in Table 23-2.

Table 23-2. Possible K command types

Type

§

Versions

Description

arith

arith on page 898

V8.10 and later

Perform arithmetic computations.

btree

btree on page 901

V8.1 and later

A db(3) form of database.

bestmx

bestmx on page 902

V8.7 and later

Look up the best MX record for a host.

dbm

dbm on page 903

V8.1 and later

Really ndbm supplied with most versions of Unix.

dequote

dequote on page 904

V8.6 and later

Remove quotation marks.

dns

dns on page 905

V8.12 and later

Look up information using DNS.

hash

hash on page 908

V8.1 and later

A db(3) form of database.

hesiod

hesiod on page 909

V8.7 and later

MIT network user authentication services.

host

host on page 910

V8.1 and later

Internal table to store and look up hostnames.

implicit

implicit on page 911

V8.1 and later

Search for an aliases database entry.

ldap

ldap (was ldapx) on page 912

V8.8 and later

The Lightweight Directory Access Protocol (LDAP).

ldapx

ldap (was ldapx) on page 912

V8.9 and earlier

Replaced by ldap.

macro

macro on page 925

V8.10 and later

Store a value into a macro via a rule.

netinfo

netinfo on page 926

V8.7 and later

NeXT, Darwin, and Mac OS X network information services.

nis

nis on page 927

V8.1 and later

Sun’s Network Information Services (NIS).

nisplus

nisplus on page 928

V8.7 and later

Sun’s newer version of NIS (NIS+).

nsd

nsd on page 929

V8.10 and later

IRIX nsd database maps.

null

null on page 929

V8.7 and later

Provide a never-found service.

ph

ph on page 930

V8.10 and later

CCSO Nameserver (ph) lookups.

program

program on page 931

V8.7 and later

Run an external program to look up the key.

regex

regex on page 932

V8.9 and later

Use regular expressions.

sequence

sequence on page 935

V8.7 and later

Search a series of database maps.

socket

socket on page 936

V8.13 and later

Connect to a socket for a database.

stab

stab on page 938

V8.10 and later

Internally load aliases into the symbol table.

switch

switch on page 938

V8.7 and later

Build sequences based on service switch.

syslog

syslog on page 939

V8.10 and later

Log information using syslog(3) via rule sets.

text

text on page 941

V8.7 and later

Look up in flat text files.

userdb

userdb on page 942

V8.7 and later

Look up in the User Database.

user

user on page 945

V8.7 and later

Look up local passwd information.

All of these database-map types are described in Alphabetized Database-Map Types on page 898 at the end of this chapter. If the type is not one of those listed, or if support for the type was not compiled in, the following error is printed and the K command is ignored:

configfile: line num: readcf: map name: class type  not available

The args

The args of the K configuration command follow the symbolic name and type:

Kname type args

The args specify (among other things) the location of the database file or the name of a network database map. The args is like a miniature command line, and its general form looks like this:

switches file_or_map

The switches are letters prefixed with a - character that modify the use of the database. (We’ll discuss them in the next section.) The file_or_map is the location of the database file or the name of a network database map. The file_or_map should exclude the .pag and .dir suffixes for dbm-type files and exclude the .db suffix for hash, or btree-type files.

A database map is opened for reading when the configuration file is processed. If the file cannot be opened (and the -o is omitted, -o on page 889), an appropriate error is printed. The file_or_map should be an absolute pathname of a file (such as /etc/mail/uuhosts) or a literal network database-map name (such as hosts.byname). An nis database-map specification can include a domain:

map@domain

Relative filenames (names that omit a leading /) are interpreted as relative to the queue directory and should never be used.

The database files must live in a safe directory (one whose every component is writable only by root or the user defined by the TrustedUser option, TrustedUser on page 1112). If the file itself is unsafe or its directory is unsafe, one of several errors will be printed or logged, depending on how you run sendmail. (See the description of the DontBlameSendmail option DontBlameSendmail on page 1009 for more information about this safety check.)

The K Command Switches

The switches must follow the type and precede the file_or_map:

Kname type switches file_or_map

If any switches follow file_or_map, they will be silently ignored.[339] All switches begin with a - character and are listed in Table 23-3. Note that some database-map types utilize only a small subset of all switches (e.g., dequote uses only -a, -D, -s, and -S, and sequence doesn’t use any).

Table 23-3. K command switches

Switch

§

Description

−1

The −1 ldap database-map switch on page 915

Consider successful only if exactly one key is matched (ldap only)

-A

-A on page 886

Append values for duplicate keys

-a

-a on page 887

Append tag on successful match

-B

DNS database-map -B switch on page 908

Append domain before the lookup (dns only) (V8.14 and later)

-b

The -b ldap database-map switch on page 915

Base from which to begin the search (ldap only)

-b

The -b regex database-map switch on page 933

Use basic, not extended, regular expression matching (regex only)

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer

-d

The -d ldap database-map switch on page 915

DN to bind to server as (ldap only)

-d

The -d regex database-map switch on page 934

The delimiting string (regex only)

-d

Timeout.resolver (V8.10 and later) on page 1108

The res_search( ) _res.retry interval (dns and host only)

-f

-f on page 887

Preserve case

-h

The -h ldap database-map switch on page 916

Hosts that serve this network database (ldap only)

-h

The -h ph database-map switch on page 931

Hosts that serve this network database (ph only)

-k

-k on page 888

Specify column for key

-k

The -k ldap database-map switch on page 917

The search query (ldap only)

-k

netinfo on page 926

The property that is searched (netinfo only)

-k

The -k ph database-map switch on page 931

Specify a list of fields to query (ph only)

-L

syslog on page 939

The logging level at which to log (syslog only)

-l

-l (lowercase L) on page 888

Time limit to timeout connection (ldap and ph only)

-M

The -M ldap database-map switch on page 917

The method to use for binding (ldap only)

-m

-m on page 888

Suppress replacement on match

-N

-N on page 889

Append a null byte to all keys

-n

The -n ldap database-map switch on page 917

Retrieve attribute names only, not values (ldap only)

-n

The -n regex database-map switch on page 934

NOT, that is, invert the test (regex only)

-O

-O on page 889

Never add a null byte

-o

-o on page 889

The database map is optional

-P

The -P ldap database-map switch on page 917

The secret password to use for binding (ldap only)

-p

The -p ldap database-map switch on page 917

Port to use when connecting to host (ldap only)

-q

-q on page 889

Don’t strip quotes from key

-R

The -R ldap database-map switch on page 917

Don’t auto-chase referrals (ldap only)

-R

dns on page 905

Record type to look up (dns only)

-r

The -r ldap database-map switch on page 918

Allow dereferencing of aliases (ldap only)

-r

Timeout.resolver (V8.10 and later) on page 1108

The res_search( ) _res.retries limit (dns and host only)

-S

-S on page 890

Space replacement character for database map

-s

The -s ldap database-map switch on page 918

Search scope of “base,” “one,” or “sub” (ldap only)

-s

The -s regex database-map switch on page 934

Substring to match and return (regex only)

-T

-T on page 890

Suffix to append on temporary failure

-t

-t on page 891

Ignore temporary errors

-V

The -V ldap database-map switch on page 918

Specify return attribute list separator (ldap only)

-v

-v on page 891

Specify the value’s column

-v

netinfo on page 926

The property to return (netinfo only)

-v

ph on page 930

Specify a list of fields to return (ph only, deprecated, and removed as of V8.13)

-v

The -v ldap database-map switch on page 919

Specify the list of attributes to return (ldap only)

-Z

DNS database-map -Z switch on page 908

Limit the number of items to return (dns as of V8.14)

-Z

The -Z ldap database-map switch on page 921

Limit the number of matches to return (ldap only)

-z

DNS database-map -z switch on page 908

Allow multiple returns and the delimiting character (dns as of V8.14)

-z

-z on page 891

Specify the column delimiter

If a switch other than those listed is specified, that switch either is silently ignored or an error reports, depending on the version of sendmail and the particular type.

In the sections that follow, we document the switches that are common to a number of database-map types. Those that are unique, or unique in meaning, to a particular database-map type are listed with the type.

-A

Append values for duplicate keys V8.7 and later

Ordinarily, when sendmail builds (rebuilds) an aliases database, it objects to duplicate keys on the left of the colon:

staff:  bill
staff:  leopold        ← this is an error

But sometimes—for example, in automating—such duplicates are necessary. In such instances, the -A switch can be used with the AliasFile (A) option (see AliasFile on page 970) to cause duplicates to be silently appended:

staff:  bill
staff:  leopold
... silently modified by sendmail to internally become
staff:  bill, leopold

Note that this process is further illustrated in Duplicate Entries and Automation on page 477.

The -A database switch is useful only with alias files because those are the only files that sendmail rebuilds on its own. Beginning with V8.10, this switch is also useful with the ph type (ph on page 930).

-a

Append tag on successful match V8.1 and later

When a key is looked up in a database (from inside the $( and $) operators of the RHS of rules) a successfully found key is replaced by its data. If the -a switch is given, the text following that switch, up to the first delimiting whitespace character, is appended to the replacement data. For example:

-a              appends  nothing
-a.             appends  .
-a,MAGICTOKEN   appends  ,MAGICTOKEN

The text to be appended is taken literally. Quotation marks and backslashed characters are included without interpretation, so whitespace cannot be included in that text. Because the rewritten RHS is normalized as an address, special address expressions (such as parentheses) should be avoided. The use of appended text is one of two methods used for recognizing a successful lookup in rules. We’ll discuss the other, $:, in Specify a Default with $: on page 893.

-D

Don’t use if DeliveryMode=defer V8.10 and later

The defer setting of the DeliveryMode option (DeliveryMode on page 1004) is intended for sites that do not have a continuous connection to the Internet (specifically, dial-on-demand sites). For such sites, mail should be placed in the queue without interacting with the Internet (which is likely unavailable). Then, when the Internet connection is made, a normal queue run will deliver the mail.

Some database maps (such as those that look up hosts and possibly those that log messages with syslog) should probably not be used when sendmail is running in defer mode. This -D database switch can also be used with a few database-map types. When it is, it advises those types to not operate when sendmail is in defer mode.

-f

Preserve case V8.1 and later

Ordinarily, sendmail will normalize a key to lowercase before looking it up in a database. If the keys in the database are case-sensitive (“TEX” is considered different from “tex,” for example), the -f database switch should be used to prevent this normalization. Note that if the -f switch is omitted (the default), the database must have been created with all lowercase keys (also the default).

Also note that when the -f switch is used with the regex database-map type, it causes the regular expression match to be made in a case-insensitive manner.

-k

Specify column for key or key name V8.7 and later

Beginning with V8.7, sendmail began to support a flat text-file form of database. The /etc/hosts file is an example of such a flat file, in that it is organized in a line-by-line manner:

123.45.67.89      here.our.domain

When such files are read as databases (with the text type, text on page 941) you need to specify which column contains the key and which contains the value.

For nisplus, netinfo, and ph database maps, the -k switch specifies the name (text) of the desired column.

When the -k switch specifies which column contains the key, its absence defaults to 0 for the text type (which is indexed beginning with 0) and defaults to the name of the first column for the nisplus type. See also -v (-v on page 891) for the returned value’s column, and -z (-z on page 891) for the column delimiter.

Finally, note that for ldap database maps, the -k switch has a different meaning, one that is particular to that type.

-l (lowercase L)

Set a timeout for the lookup V8.12 and later

When doing a lookup, the -l switch sets a time limit for how long to wait for a reply:

-l5

Note that the limit is not a general time expression (that is, 15m still evaluates to 15 seconds).

Also note that this -l switch is not available for all database-map types. As of this writing, it is available only with the ldap and ph database-map types.

-m

Suppress replacement on match V8.1 and later

Ordinarily, a successful lookup in a database map causes the key to be replaced by its value. When the intention is to merely verify that the key exists (not to replace it) the -m switch can be used to suppress replacement.

For example, the values that are returned from the hosts.byname NIS database map are not generally useful (they contain multiple hostnames). In looking up a key in this database map (with $( and $); see Use $( and $) in Rules on page 892), the -m switch prevents those multiple names from wrongly replacing the single hostname in the key. Note that the -a switch (-a on page 887) can still be used to append a suffix to a successful lookup. Also, the $:default (Specify a Default with $: on page 893) is still used if the lookup fails.

-N

Append a null byte to all keys V8.1 and later

If a database was created with makemap’s -N switch (-N on page 374) to include the terminating zero byte with each key, this -N switch should be specified with the corresponding K configuration command to force all lookups to also include a zero byte. Note that -N is not needed for the nis type and, if included, is ignored. See also -O in -O on page 889.

-O

Never add a null byte V8.2 and later

If neither -N nor -O is specified, sendmail uses an adaptive algorithm to decide whether to look for the terminating zero byte. The algorithm starts by accepting either possibility. If the first key looked up is found to end with a terminating zero byte, the algorithm will thereafter look only for keys with a terminating zero byte. If the first key that is looked up is found to not end with a terminating zero byte, the algorithm will thereafter look only for keys without a terminating zero byte.

If this -O switch is specified, sendmail never tries a zero byte, which can speed matches. Note that if both -N and -O are specified, sendmail will not produce an error message, and will never try to match at all, thus causing all lookups to appear to fail.

-o

The database map is optional V8.1 and later

Ordinarily, in the case of types that employ disk files, sendmail will complain if a specified file cannot be opened for reading. If the presence of a database file is optional (as it can be on certain machines), the -o switch should be used to tell sendmail that the database is optional. Note that if a database is optional and cannot be opened, all lookups will silently fail for rules that use that database.

Also note that for network-based types of database maps, this -o switch can be used to cause failed initializations to be ignored. If a database map is used during the processing of a message, and if a lookup fails in the absence of a -o switch, the message (or SMTP request) will be rejected with a temporary failure.

-q

Don’t strip quotes from key V8.7 and later

Ordinarily, sendmail strips all the nonescaped quotation marks (those not prefixed with a backslash) from a key before looking it up. For example, the following key:

"Bob "bigboy" Roberts \(esq\)"@bob.com

will have its nonescaped quotation marks removed and end up looking like this:

Bob "bigboy" Roberts (esq)@bob.com

Note that all escaped characters are de-escaped (have the backslash removed) during this process.

When quotation marks and escaped characters need to be preserved in a key before it is looked up, you can use the -q switch with the K configuration command. The -q switch suppresses dequoting and de-escaping.

-S

Space replacement character V8.8 and later

The dequote type (dequote on page 904) refuses to remove quotation marks if doing so will result in an illegal address. For example, internal space characters are illegal in addresses:

"a b"           becomes →      "a b"

The -S switch causes all the quoted space characters to be changed into a character that you specify just before the dequoting process:

Kdequote dequote -S+

Here, we specify that quoted strings will have quoted spaces converted into a plus sign before dequoting. Therefore, the preceding conversion becomes the following:

"a b"           becomes →      a+b

As you will see in the reference sections at the end of this chapter, this -S database switch can be used with a few other types as well.

-T

Suffix to append on temporary failure V8.10 and later

When a resource is temporarily unavailable, it would be handy if sendmail indicated that unavailability when the database lookup fails. Consider NIS, for example. It can time out when a server is down briefly, but a failed lookup of a user’s login name need not cause a permanent failure under such a circumstance. Instead, something should be returned to show that it is only a temporary failure.

The -T database switch was added with V8.10 sendmail to solve this problem. You use it to define a suffix to add to the key for the returned failure value when the problem is temporary. You might use it like this:

Kmailservers nis -T.Defer -o mailservers
...
R $* <@ $+ > $*                 $: $1<@$2>$3 <$(mailservers $2 $: Fail $)>
R $* <@ $+ > $* <$* . Defer>    $# error $@ 4.2.2 $: "450 defer"   ← handle failure
here
R $* <@ $+ > $* <Fail>          $# error $@ 5.7.1 $: "550 reject"  ← handle failure
here
R $* <@ $+ > $* <$+>            $# smtp $@ $4 $: $1 < @ $2 > $3    ← OK, so send it
...

Note that a permanent failure returns the failure alternative indicated by the $: operator (the Fail). But a temporary failure returns the suffix defined by the -T, appended to the original key (the $2) to form $2.Defer.

Note that this definition of temporary failure is different from that defined by the -D database switch. With -D, database lookups are not done at all if the DeliveryMode option (DeliveryMode on page 1004) is set to defer. Also note that this -T database switch affects only the return value. It does not affect the outcome of mail delivery. To affect the outcome on temporary failures, use the -t switch (-t on page 891).

-t

Ignore temporary errors V8.10 and later

Usually it is acceptable for a lookup to fail because of a temporary failure of a system resource. When reading from a network database map (such as the DNS name server) temporary failures (such as server down) generally cause email to be requeued for a later try. However, you might sometimes find it desirable for a database map’s temporary failures to be ignored. In such cases, you can enable this -t database switch. With it set, a temporary error will cause the mail to be delivered.

Note that failure of a key to be found in a database map is not a temporary error. Also note that this switch just determines the outcome of a message. It does not affect the nature of the returned value. To affect the return value on temporary failures, use the -T database switch (-T on page 890).

-v

Specify the value’s column V8.7 and later

The manner in which the key and its value are visually displayed in flat, sequential text files and certain network services, might not be directly suitable for use with database maps. A text-type file—for example, /etc/hosts—might display the key on the right and the value on the left:

123.45.67.89      here.our.domain

For such circumstances, the -v switch can be used with the K command to specify the column or item that will be returned as the value when a key is matched. For example:

Kaddr text -k1 -v0 /etc/hosts

For nisplus, netinfo, user, and other such database maps, the -v switch specifies the name (text) of the value’s column.

This -v switch specifies which column is the value to return. If it is omitted, it defaults to 0 for the text type (which is indexed beginning with 0) to the last named column for the nisplus type, and to the string "members" for the netinfo type. Note that the -v switch has a different meaning for the ph database-map type. See also -k (-k on page 888) for the value’s column and -z (-z on page 891) for the column delimiter.

-z

Specify the column delimiter V8.7 and later

Flat, sequential text files have columns of information delimited from each other with a variety of characters:

123.45.67.89      here.our.domain          ← /etc/hosts uses a whitespace
nobody:*:65534:65534::/:                   ← /etc/passwd uses a colon

The -z switch can be used to specify a delimiter whenever the default delimiter of whitespace is not appropriate. In the case of the /etc/passwd file, a database declaration might look like this:

Kuid text -z: -k2 -v0 /etc/passwd  # map to convert user-id to login name

The default is whitespace for the text type. It is a comma for the netinfo type.

For the ldap type, a -z switch specifies the character to use to separate values when building the resulting string when multiple attribute values are returned.

Use $( and $) in Rules

The information in database maps is accessed in the RHS of rules. This is the basic syntax:

$(name key $)

The key is looked up in the database map whose symbolic name (declared with the K configuration command, The K Configuration Command on page 882) is name. If the key is found, the entire expression, including the $( and $), is normally replaced with the value returned for that key.[340] Any suffix, as specified with the -a switch (-a on page 887) in the K configuration declaration for name, is appended to the data. If the key is not found, the entire expression is replaced with key. If the $) is omitted, all tokens up to but excluding the tab and comment, or end-of-line if there is no comment, are taken as the key. To illustrate one use for $( and $), see the following rule:

R$- . uucp      $: $(uucp $1.uucp $)

and the following K command:

Kuucp hash /etc/mail/uucp

This associates the symbolic name uucp with a hash-type file called /etc/mail/uucp. If the uucp database contained entries such as these:

lady.uucp    lady.localuucp
sonya.uucp   sonya.localuucp

a workspace of lady.uucp would match the LHS, so the RHS would look up $1.uucp (thus, lady.uucp) in the uucp.db database. Because lady.uucp is found, the entire $( to $) RHS expression is replaced with lady.localuucp from the database. Any UUCP hosts other than lady or sonya would not be found in the database, so the RHS expression would become the original workspace, unchanged.

Note that the entire RHS is prefixed with a $:. This prevents sendmail from retesting with the LHS after the RHS rewrite. If this prefix were omitted, endless looping could occur.

Also note that the -a switch of the K command can be used to simplify the writing of this rule. For example:

Kuucp hash -a.localuucp /etc/mail/uucp

The -a switch tells sendmail to append the text .localuucp to all successful lookups. Thus, the preceding database can be simplified to look like this:

lady.uucp    lady
sonya.uucp   sonya

But the preceding rule remains the same:

R$- . uucp      $: $(uucp $1.uucp $)

Beyond the simple macros and positional operators we have shown, the key part can use other operators and forms of macros. For example, delayed expansion macros can be useful:

R$&s       $: $( uucp $&s $)

Here, the sender’s host is looked up to see whether it is a UUCP host. The $& prefix (Use Value As Is with $& on page 793) prevents the s macro from being expanded as the configuration file is read. Instead, its value will change with each piece of mail that is processed.

Additional examples of database lookups are given with the individual type descriptions at the end of this chapter.

Specify a Default with $:

The $: operator can be used as an alternative to the -a switch (or in conjunction with it). The $: operator, when it stands between the $( and $), specifies a default to use instead of the key, should a lookup fail:

R$- . uucp      $: $(uucp $1 $: $1.uucp $)

Here, the $- part of the LHS is looked up in the uucp database. If it is found, the $( to the $) in the RHS expression is replaced by the data from that database. If it is not found, the $: causes the expression to be replaced with the $- LHS part and a .uucp suffix ($1.uucp).

This version of our rule further simplifies the contents of the database file. With this rule, the database file would contain information such as the following:

lady    lady
sonya   sonya

The -a is still used as before to append a .localuucp to each successful match:

Kuucp hash -a.localuucp /etc/mail/uucp

In the RHS expression, the $: must follow the key or it loses its special meaning:

$(name key $:  default  $)

If the $:default wrongly precedes the key, it is used as the key, lookups fail, and replacements are not as expected. If the $: is present but the default is missing, a failed lookup returns an empty workspace.

Specify Numbered Substitution with $@

For more complex substitutions, V8 sendmail offers use of the $@ operator in the RHS in conjunction with the $( and $) expressions in database maps.[341] There can be multiple $@-prefixed texts between the key and the $: (if present) or the $), where each of the texts may itself be multiple rule-set expressions:

$(name key $@ text1  $@ text2 and text3 $: default  $)

Each $@text expression is numbered by position (from left to right):

$(name key $@ text1  $@ text2  $: default  $)
               ↑          ↑
1                                2

In this numbering scheme the key is always number 0, even if no $@s are listed.

These numbers correspond to literal % digit expressions in the data portion of a database map. For example:

lady   %0!%1@%2

When a lookup of the key in the RHS of the rule is successful, the returned value is examined for %digit expressions. Each such expression is replaced by its corresponding $@text from the rule. In the case of the preceding database map, %0 would be replaced with lady (the key), %1 with text1, and %2 with text2.

To illustrate, consider the earlier database entry and the following rule:

R$- @ $-.uucp   $: $(uucp $2 $@ $1 $@ mailhost $: $1@$2.uucp $)

If the workspace contains the address joe@lady.uucp, the LHS matches. The RHS rewrites only once because it is prefixed with the $: operator. The expression between the $( and $) causes the second $- from the LHS (the $2, the key) to be looked up in the database whose symbolic name is uucp. Because $2 references lady from the workspace, lady is found and the data (%0!%1@%2) is used to rewrite. The %0 is replaced by lady (the key via $2). The text for the first $@ ($1 or joe) then replaces the %1. Then the second text for the second $@ (mailhost) replaces the %2. Thus, the address joe@lady.uucp is rewritten to become lady!joe@mailhost.

If a host other than lady appeared in the workspace, this RHS would use the $:default part. Thus, the address joe@foo.uucp would become (via the $:$1@$2.uucp) joe@foo.uucp. That is, any address that is not found in the database would remain unchanged.

If there are more $@text expressions in the RHS than there are numbers in the value, the excess $@text parts are ignored. If a %digit in the data references a nonexistent $@text, it is simply removed during the rewrite.

All $@text expressions must lie between the key and the $:default (if present). If any follow the $:, they become part of the default and cease to reference any %digit.

But be aware of a common mistake, which is to confuse a $number rule righthand-side expression with %digit results. For example, you might expect $1 assigned to %1 and $2 assigned to %2 with the following expression, but you would be wrong:

 $(lookup $&{client_addr} $@ $1 $2 $)

Here, $1 and $2 are both assigned to %1. Remember, each $@ corresponds to a single %digit, no matter how many expressions follow the $@. The correct way to code the preceding expression would look like this:

$(lookup $&{client_addr} $@ $1 $@ $2 $)

Here, the intended association is achieved, where $1 is correctly assigned to %1 and $2 is correctly assigned to %2.

$[ and $]: A Special Case

The special database-map type called host can be declared to modify name-server lookups with $[ and $]. The special symbolic name and type pair, host and host, is declared with the $( and $) operators like this:

Khost host -a.

The -a switch was discussed earlier in this chapter. Here, it is sufficient to note how it is used in resolving fully qualified domain names with the $[ and $] operators in the RHS of rules. Under V8 sendmail, $[ and $] are a special case of the following database lookup:

$(host lookuphost $)

A successful match will ordinarily append a dot to a successfully resolved hostname.

When a host type is declared with the K command, any suffix of the -a replaces the dot as the character or characters added.[342] For example:

$[ lookuphost $]     ← found, so rewritten as lookuphost.domain.

Khost host -a
$[ lookuphost $]     ← found, so rewritten as lookuphost.domain

Khost host -a.yes
$[ lookuphost $]     ← found, so rewritten as lookuphost.domain.yes

The first line shows the default action of the $[ and $] operators in the RHS of the rules. If lookuphost can be fully qualified, its fully qualified name becomes the rewritten value of the RHS and has a dot appended. The next two lines show the -a with no suffix (note that with no suffix the -a is optional). In this configuration file, the fully qualified name has nothing (not even a dot) appended. The last two lines show a configuration file with a .yes as the suffix. This time, the fully qualified name has a .yes appended instead of the dot.

Database Maps with mc Configuration

All available mc database maps are implemented as FEATUREs. Table 23-4 lists those that are available. The second column shows you where to find information about each.

Table 23-4. Database-map features

FEATURE()

§

Versions

Description

access_db

The access Database on page 277

V8.9 and later

A database for mail policy

authinfo

FEATURE(authinfo) on page 616

V8.12 and later

Use a separate database for authentication information

bestmx_is_local

FEATURE(bestmx_is_local) on page 617

V8.6 and later

Accept best MX record as local if in $=w

bitdomain

FEATURE(bitdomain) on page 617

Deprecated

Convert BITNET addresses into Internet addresses

dnsbl

FEATURE(dnsbl) on page 261

V8.10 and later

Reject based on various DNS blacklists

domaintable

FEATURE(domaintable) on page 621

V8.1 and later

Accept other domains as equivalent to the local domain

enhdnsbl

FEATURE(enhdnsbl) on page 263

V8.12 and later

Enhanced dnsbl lookups

genericstable

FEATURE(genericstable) on page 622

V8.8 and later

Transform sender addresses

ldap_routing

FEATURE(ldap_routing) on page 922

V8.10 and later

Reroute recipients based on LDAP lookups

mailertable

FEATURE(mailertable) on page 629

V8.1 and later

Select new delivery agents based on an external database

uucpdomain

FEATURE(uucpdomain) on page 644

Deprecated

Convert UUCP hosts via a database

virtusertable

FEATURE(virtusertable) on page 645

V8.8 and later

Support for virtual domains

Note that these FEATUREs do not necessarily need to be used with database files. To illustrate, consider FEATURE(domaintable) (FEATURE(domaintable) on page 621). It is included in your mc file like this:

FEATURE(`domaintable',`nis domaintable')

Here, we specify that the database map is to be the nis type. This causes the key to be looked up via NIS and any match to be returned the same way.

Set a Default Database-Map Type for Features

FEATUREs that employ on-disk database files all share the common default database-map type hash. But if you wish to change that default to another type, you can do so with the following mc configuration command:

define(`DATABASE_MAP_TYPE', `dbm')

Here, we declare the default to be dbm, thereby causing all such FEATUREs to use ndbm(3) database files. Note that if you declare a default, you must do so before declaring any database FEATUREs.

Many FEATUREs that take arguments require you to declare the database type. For example:

FEATURE(`authinfo', `dbm /etc/security/authinfo')

That is, this DATABASE_MAP_TYPE’s default is used only if no argument is given for the feature.

Pitfalls

  • The result of a subroutine call cannot be looked up directly in a database map. Consider this RHS of a rule:

    $(uucp $>96 $1 $)

    Here, the intention is to pass $1 to rule set 96 and then to look up the result in the uucp database map. Instead, the literal value 96 and the value in $1 are looked up together and fail first. Then $1 is passed to rule set 96, and the result of that subroutine call becomes the result of the RHS.

  • If you are running a Solaris 2.4 or earlier release of Sun’s operating system, your database files should not live on tmpfs-mounted filesystems. File locking was not implemented for tmpfs until Solaris 2.5.

  • Avoid assuming that all K command switches mean the same thing for all types. The ad hoc nature of database-type submissions by outsiders makes that assumption perilous.

  • Not all initialization errors or lookup errors are reported. For some of them you will see an indication of an error only if you use the -d38.2 debugging switch (-d38.2 on page 564).

  • The sendmail program automatically creates certain database maps as it needs them. This is done without the need to declare them with a K configuration command. For example, consider the following mc configuration line:

    define(`ALIAS_FILE', `/etc/mail/aliases')

    When sendmail encounters this AliasFile option (AliasFile on page 970) it automatically creates the aliases.files database map so that it can easily look up aliases. sendmail automatically creates the following database maps: aliases.files, aliases.nis, aliases.nisplus, aliases.netinfo, aliases.hesiod, passwd.files, passwd.nis, passwd.nisplus, passwd.hesiod, and users. You should avoid using these database maps in rule sets because they are essentially internal to sendmail and can change without notice.

Alphabetized Database-Map Types

Recall that the K configuration command (The K Configuration Command on page 882) is used like this:

Kname type args

The type determines the type of database map that will be used. For example, the type btree causes a db(3)-format database file to be used, whereas the type dequote causes an internal routine of sendmail’s to be called.

In this section, we present all the types in alphabetical order. They are summarized in Table 23-2 on page 883. Most interaction with these types can be watched by using the -d38.2 debugging switch (-d38.2 on page 564). Some specialty database maps use other debugging switches, which we indicate where appropriate.

arith

Perform arithmetic computations V8.10 and later

Beginning with V8.10, sendmail supports arithmetic computations in rule sets via a database-map type called arith. This form of database map is always present for your use, without the need for special compile-time macros. To illustrate one use for arith, consider this mini configuration file:

V10
Kmath arith
SCalculate
R $+ $+ $+      $@ $(math $2 $@ $1 $@ $3 $: EXCEPTION $)

The K configuration command declares that a database map named math will be of the database-map type arith. To use this database map we declare a rule set. We call that rule set Calculate so that rule-set testing will be mnemonically clear.

The rule is the crux of how this math database map is used:

R $+ $+ $+      $@ $(math $2 $@ $1 $@ $3 $: EXCEPTION $)
                          ↑     ↑     ↑
                      operator  lvalue   rvalue

The arith database-type database maps (such as math here) take three arguments. The first, in the position of the key that would otherwise be used for lookups, is the arithmetic operator. The legal operators, as of V8.12, are shown in Table 23-5.

Table 23-5. Operators for the arith database-map type

Operator

Description

[a]

+

Addition: add lvalue to rvalue

Subtraction: subtract rvalue from lvalue

*

Multiplication: multiply lvalue by rvalue

/

Division: divide lvalue by rvalue

l

Less-Than: if lvalue is less than rvalue return literal TRUE, otherwise literal FALSE

=

Equality: if lvalue is equal to rvalue return literal TRUE, otherwise literal FALSE

|

The bitwise OR operation (V8.12[a] and later)

&

The bitwise AND operation (V8.12[a] and later)

%

The modulo operator: lvalue modulo rvalue (V8.12 and later)

r

Provide a random value (V8.14 and later)

[a] a To enable these operators for V8.10 and V8.11, define _FFR_BITOPS when compiling sendmail.

If the arithmetic operator used is not one of those shown in the table (such as an illegal ! operator), the lookup (calculation) fails and the value following the $: operator is returned (the EXCEPTION). If the arithmetic operator is legal (is shown in the table), a calculation is performed and the result returned.

The two values used in the computation are passed following the first and second $@ operators. The lvalue follows the first $@ operator, and the rvalue follows the second. The arithmetic operation specified is performed on the two values and the result is returned.

Computations are always performed using integer calculations, and the values are always interpreted as integer values. A division by 0 always returns a failed lookup (the EXCEPTION). The less-than and equality arithmetic operators return the literal token TRUE or FALSE, indicating the truth of the comparison.

To demonstrate this arith database-map type, you can run sendmail on the mini configuration file listed earlier. If that file were called demo.cf you might test it like this:

% /usr/sbin/sendmail -Cdemo.cf -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
Enter <ruleset> <address>
> Calculate 1 + 1
Calculate          input: 1 + 1
Calculate        returns: 2
> Calculate 5 / 0
Calculate          input: 5 / 0
Calculate        returns: EXCEPTION
> Calculate 5 / 2
Calculate          input: 5 / 2
Calculate        returns: 2
> Calculate −1 * 4
Calculate          input: −1 * 4
Calculate        returns: −4
> Calculate 2 = 2
Calculate          input: 2 = 2
Calculate        returns: TRUE
> Calculate 0xff / 2
Calculate          input: 0xff / 2
Calculate        returns: 0

The last three lines show that only decimal integer values can be used. Also note that negative values work properly.

One example of a real use for this type of database map might be a test to see whether the ETRN command should be run if the machine’s load average is too high:

D{OurMaxLoad}20
Scheck_etrn
R $*          $: $(math l $@ $&{load_avg} $@ ${OurMaxLoad} $) $1
R FALSE       $#error $@ 4.7.1 $: "450 The load average is currently too high."

The check_etrn rule set is called by V8.10 and later sendmail each time the remote site sends an ETRN command, and before any reply is sent to the remote site.

The $& prevents the {load_avg} macro (${load_avg} on page 832) from being interpreted too early (when the configuration file was read). Consequently, its current value is compared to the value in the ${OurMaxLoad} macro. If the truncated integer value of the load average is higher than our limit, the request is denied. Note that if ${OurMaxLoad} is undefined, the rule will return a failed lookup, but not the literal token FALSE. Thus, by undefining ${OurMaxLoad} you disable this test.

To fetch a random value using the new r operator (available as of V8.14) you need to provide lower and upper bounds for the random number as the first and second arguments following the operator:

 V10
Kmath arith
SRandomize
R $+ $+      $@ $(math r $@ $1 $@ $2 $)

Here, if the first argument ($1) is numerically less than the second argument ($2), the ASCII representation of a pseudorandom value will be returned that is greater than or equal to the first and less than or equal to the second. If the two values are equal, that equal value is returned. If the first is greater than the second, a literal r is returned to indicate an error. If either, or both, values are non-numeric, the value 0 is returned.

Only a few database switches are useful with the arith database-map type. They are listed in Table 23-6.

Table 23-6. The arith database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-S

-S on page 890

Space replacement character

-s

 

Synonym for -S

Although these switches are allowed, it will take some inventiveness to devise a use for them with this arith database-map type. If you specify a switch that is not listed in the table, it will be silently ignored.

btree

The db(3) form of database V8.1 and later

The term btree stands for “balanced tree.” It is a grow-only form of database. Lookups and insertions are fast, but deletions do not shrink the size of the database file.[343] A good description of this form of database can be found in The Art of Computer Programming, Vol. 3: Sorting and Searching, by D.E. Knuth. The btree type is available only if sendmail was compiled with NEWDB defined and the Berkeley or Sleepycat db library linked (The Sleepycat DB Library on page 104). In most cases, the hash type (hash on page 908) will perform slightly better.

Quite a few database switches are available with this database-map type. They are listed in Table 23-7.

Table 23-7. The btree database-map type K command switches

Switch

§

Description

-A

-A on page 886

Append values for duplicate keys.

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-f

-f on page 887

Don’t fold keys to lowercase.

-m

-m on page 888

Suppress replacement on match.

-N

-N on page 889

Append a null byte to all keys.

-O

-O on page 889

Never add a null byte.

-o

-o on page 889

The database map is optional.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

One use for this btree type might be to look up users for whom permission to send offsite email is denied. The data source file might look like the following, and might live in the file /etc/mail/badusers.db (after makemap was run to create it):

bob    bob
ted    ted
alice  alice

A simple configuration file to test this database can then be created like this:

V10
Kbaduser btree -a.BAD -t /etc/mail/badusers
R $+ < @ $+ > $*                    $: $1< @ $2 > $3 < $(baduser $1 $) >
R $+ < @ $+ > $* < $* . BAD >       $#error $@ 5.1.3 $: "Offsite mailing denied"

Here, the database is declared with the K configuration command. The -a database switch causes .BAD to be appended to any key that is found in the database. The -t switch causes temporary errors to be ignored. A match causes the workspace to carry the extra information that is matched by <$*.BAD >, and which results in an error being reported back to the sender.

The -d38.20 command-line switch (-d38.20 on page 568) can be used to observe this type’s lookups in more detail.

bestmx

Look up the best MX record for a host V8.7 and later

The bestmx database-map type looks up a hostname as the key and returns the current, single best MX record as the value. Because bestmx is a type, not a database map, you need to declare it with a K configuration command before you can use it:

Kbestmx bestmx

One use for this database-map type might be to see whether a particular host has any usable MX records:[344]

Kbestmx bestmx
...
R $*< @ $+ > $*                $: $1<@$2>$3 < $(bestmx $2 $: NO $) >
R $*< @ $+ > $* < NO >         $#smtp  $@ $2 $: $1 < @ $2 > $3
R $*< @ $+ > $* < $* >         $: $1<@ $[ $2 $] > $3

In the first rule, we look up the host part of an address (which has already been focused by the canonify rule set 3) with the bestmx database map. The result of the lookup is surrounded with angle brackets and appended to the original address. The second rule looks for the NO caused by an unsuccessful lookup (the $:). The original address is then sent with the smtp delivery agent. If the hostname inside the appended angle braces is not NO, the host part of the original address is canonicalized with the $[ and $] operators.

bestmx is a special internal type that can utilize only a few of the K command switches, as listed in Table 23-8.

Table 23-8. The bestmx database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-m

-m on page 888

Suppress replacement on match.

-q

-q on page 889

Don’t strip quotes from key.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

-z

-z on page 891

Specify the column delimiter.

The -z switch (special for this bestmx database-map type) allows multiple MX records to be returned, and specifies a column delimiter used to separate one record from another. As long as the column delimiter is not a character that appears in any domain name, it will be used to separate all the MX records returned by the MX lookup. These records will be returned in the new workspace. For example, if the -z switch specified a comma, and if abc.com were looked up, the following might be returned:

mail11 . disney . com . , mail . disney . com .

If the -z switch wrongly specifies a character that can exist in a domain name (such as a dot), the following error will be reported and only one MX record will be returned:

bestmx_map_lookup: MX host mail11.disney.com. includes map delimiter character 0x2E

If too many MX records are returned, the list can be truncated to avoid an overly long workspace. When the list is truncated, some MX records can be lost. This can become a serious problem when this -z switch is used with this database-map type and when FEATURE(relay_based_on_MX) is also declared (FEATURE(relay_based_on_MX) on page 271).

This type can be watched with the -d8 debugging switch (-d8.1 on page 548).

dbm

Really ndbm supplied with most versions of Unix V8.1 and later

The dbm database-map type, which is really the ndbm form of database, is the traditional form of Unix database file. Data is stored in one file, keys in another. The data must fit in blocks of fixed sizes, so there is usually a limit on the maximum size (1 kilobyte or so) on any given stored piece of data. The dbm database-map type is available only if sendmail was compiled with NDBM declared (NDBM on page 125).

Many database switches are available with this dbm database-map type. All are listed in Table 23-9.

Table 23-9. The dbm database-map type K command switches

Switch

§

Description

-A

-A on page 886

Append values for duplicate keys.

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-f

-f on page 887

Don’t fold keys to lowercase.

-m

-m on page 888

Suppress replacement on match.

-N

-N on page 889

Append a null byte to all keys.

-O

-O on page 889

Never add a null byte.

-o

-o on page 889

The database file is optional.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

This is the database-map type used with aliases files, if the hash type is unavailable. This type is also needed on machines that employ NIS because the underlying files for those services are stored in dbm format. Note that because of the implicit limit on the size of a piece of data, you should consider using one of the db(3) hash or btree types instead.

dequote

Remove quotation marks V8.6 and later

V8 sendmail can remove quotation marks from around tokens by using the special dequote database-map type. Because dequote is a type, not a database map, you need to declare it with a K configuration command before you can use it:

Kunquote dequote

This declares a database map named unquote of the type dequote. Once a database-map name has been declared, the dequote type can be used in the RHS of rules to remove quotation marks. It is used with $( and $) just like all database-map lookups:

$(unquote tokens $)

Here, arbitrary tokens are looked up in the database map named unquote. That database map is special because it is of the type dequote. Instead of being looked up in an external database file, tokens will just have any surrounding quotation marks removed:

"A.B.C"           becomes   A.B.C
"A"."B"."C"       becomes   A.B.C
"A B"             becomes   "A B"
"A,B"             becomes   "A,B"
"A>B"             becomes   "A>B"

The first example shows that surrounding quotation marks are removed. The second shows that multiple quoted tokens are all dequoted. The last three show that sendmail refuses to dequote any tokens that will form an illegal or ambiguous address when dequoted.

As an aid to understanding this dequoting process, run the following two-line configuration file in rule-testing mode:

V10
Kdequote dequote

You can then use the -bt /map command to try various dequoting possibilities:

> /map dequote "A.B.C"
map_lookup: dequote ("A.B.C") returns A.B.C (0)
> /map dequote "A"."B"."C"
map_lookup: dequote ("A"."B"."C") returns A.B.C (0)
> /map dequote "A B"
map_lookup: dequote ("A B") no match (0)

A few database switches are available to modify the behavior of this dequote database-map type. They are listed in Table 23-10.

Table 23-10. The dequote database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-S

-S on page 890

Space replacement character.

-s

 

Synonym for -S.

Note that beginning with V8.7, specifying the -s switch (and beginning with V8.10, specifying the -S switch) causes the space character to be replaced with another character before dequoting (-S on page 890):

Kdequote dequote -s+           ← V8.7 through V8.9
Kdequote dequote -S+           ← V8.10 and later

When using the mc configuration technique, dequote switches are declared like this:

define(`confDEQUOTE_OPTS', `-S+')

In either case, the last example would have the space converted to a plus sign before the conversion, thus resulting in a legal address. The "A B" example (which failed before) will become the following:

> /map dequote "A B"
map_lookup: dequote ("A B") returns A+B (0)

Also note that beginning with V8.8, specifying the -a switch causes a suffix of your choice to be appended to a successful match:

define(`confDEQUOTE_OPTS', `-a.yes')

In that case, the "A.B.C" example would become the following:

> /map dequote "A.B.C"
map_lookup: dequote ("A.B.C") returns A.B.C.yes (0)

In addition to removing quotes, the dequote type also tokenizes everything that is returned. It does this because quotes are ordinarily used to mask the separation characters that delimit tokens.

No debugging switch is available to watch the actions of the dequote type.

dns

Look up addresses using DNS V8.12 and later

The dns type is an internal database map available to perform DNS lookups. It is declared like this:

Kdnslookup dns -Rlookup-type

The -R switch—which specifies the DNS query to perform—must always be included. Table 23-11 shows the DNS queries that are supported.

Table 23-11. The dns database-map type -R switch query values

-R Value

Means

A

Return IPv4 address records for the host (RFC1035).

AAAA

Return IPv6 address records for the host (RFC1886).

AFSDB

Return an AFS server resource record (RFC1183).

CNAME

Return the canonical name for the host (RFC1035).

MX

Return a best MX record for the host (RFC1035).

NS

Return a name server record (RFC1035).

PTR

Return the hostname that corresponds to an IP record (RFC1035).

SRV

Return the port to use for a service (RFC2782).

TXT

Return general (human-readable) information (RFC1035).

If an -R value other than those in Table 23-11 is specified, the following two errors are printed and logged. If the -R switch is omitted, only the second error is printed and logged:

configfile: line num: dns map lookup: wrong type bad -R value
configfile: line num: dns map lookup: missing -R type

To make this dns database-map type more useful, the switches shown in Table 23-12 are also available for your use.

Table 23-12. The dns database-map type K command switches

Switch

§

Description

-A

-A on page 886

Append values for duplicate keys.

-a

-a on page 887

Append tag on successful match.

-B

DNS database-map -B switch on page 908

Specify domain to append to all queries (V8.14 and later).

-d

Timeout.resolver (V8.10 and later) on page 1108

The res_search() _res.retry interval (V8.12 and later).

-f

-f on page 887

Don’t fold keys to lowercase.

-m

-m on page 888

Suppress replacement on match.

-N

-N on page 889

Append a null byte to all keys.

-O

-O on page 889

Never add a null byte.

-o

-o on page 889

This database map is optional.

-q

-q on page 889

Don’t strip quotes from key.

-R

Previous paragraphs

Record type to look up.

-r

Timeout.resolver (V8.10 and later) on page 1108

The res_search() _res.retries limit (V8.12 and later).

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

-Z

DNS database-map -Z switch on page 908

The maximum number of returned entries to form a lookup result (V8.14 and later).

-z

DNS database-map -z switch on page 908

The delimiter to use to delimit multiple returned entries (V8.14 and later).

One possible use for this dns database map might be to do a reverse lookup of a connecting host’s address and to defer the message if that address does not resolve.[345] Consider the following mc configuration, for example:

LOCAL_CONFIG
Krlookup dns -RPTR -a.FOUND -d5s -r2

LOCAL_RULESETS
Local_check_relay
R $*             $: $&{client_addr}
R IPv6: $*       $# OK
R $+.$+.$+.$+    $: $(rlookup $4.$3.$2.$1.in-addr.arpa. $)
R $* . FOUND     $# OK
R $*             $#error $@ 4.1.8 $: "450 cannot resolve " $&{client_addr}

Here, under the LOCAL_CONFIG, we declare a dns-type database called rlookup. The -RPTR specifies that we will be looking up PTR (address) records. The -a.FOUND instructs sendmail to append a literal .FOUND to the value returned by a successful lookup. Finally, the -d5s and -r2 switches prevent the lookup from hanging for too long an interval.

The actual rules are under the LOCAL_RULESETS section of your mc configuration file. We place the rules under the Local_check_relay rule set (Local_check_relay and check_relay on page 252), which is used to screen incoming network connections and accept or reject them based on the hostname, domain, or IP address. The first rule matches everything and simply copies the value of the ${client_addr} macro into the workspace. That macro contains the connecting host’s IP address.

The second rule checks to see whether the IP address is an IPv6 address (the IPv6: prefix) and if so, accepts the address (the $#OK). If the address is a normal dotted-quad, IPv4-style address (such as 123.45.67.8), the third rule finds it in the workspace. An IPv4 address is looked up in the RHS of the third rule using the rlookup database. The key point here is that an address has to look like a hostname, so we reverse it and add a literal .in-addr.arpa. suffix to it. For example:

123.45.67.8     would look up as →    8.67.45.123.in-addr.arpa.

The fourth rule detects the result of the lookup. If the workspace ends in a literal .FOUND, the lookup was successful and the rule set returns a $#OK, which means that the message is acceptable.

The last rule handles any lookup failure (including temporary failures). The envelope sender is rejected with a temporary error, thus causing the sending site to retain the message in its queue. If the IP address can be looked up in the future, no harm is done. Otherwise, the message will eventually bounce.

The value returned by the dns-type database map is always a single item. If a host has multiple MX, A, or AAAA records, a successful lookup will return only one such record. In the case of MX records, the lowest-cost record may not be returned.[346]

This dns-type database map can be used only if sendmail was built with the NAMED_BIND and DNSMAP compile-time macros defined (which they are by default).

This dns-type database map is used primarily by FEATURE(dnsbl) (FEATURE(dnsbl) on page 261) and FEATURE(enhdnsbl) (FEATURE(enhdnsbl) on page 263). Both of these features use the -RA and -T<TMP> switches. FEATURE(enhdnsbl) also uses the -r5 and -a. switches. Beginning with V8.13, these switches can be overridden for FEATURE(dnsbl) using the DNSBL_MAP_OPT mc configuration macro (FEATURE(dnsbl) on page 261). For FEATURE(enhdnsbl), the timeout for -r can be changed using the EDNSBL_TO mc configuration macro.

DNS database-map -B switch

As of V8.14, the -B database-map switch may be used to add a domain specification that will automatically be appended to each lookup. For example:

LOCAL_CONFIG
Ktxtlookup dns -RTXT -a.FOUND -Bexample.com

Here, if an unqualified host, such as hostA, is looked up, it has the domain example.com appended to it to form hostA.example.com and the resulting hostname will be looked up. If you use the -B switch to look up a fully qualified name (such as www.example.com), the domain is also appended (to form www.example.com.example.com) and the lookup will fail or possibly return an unexpected value. Thus, we recommend that you use only -B to look up unqualified hostname.

DNS database-map -Z switch

As of V8.14, the -Z database-map switch may be used to limit the number of entries returned on a successful lookup. For example:

LOCAL_CONFIG
Klookup dns -RA -z, -Z2

Here, the lookup database map will query for an A record. Normally, if a host has several A records, this lookup would return only one, but with the addition of the V8.14 -z switch, sendmail will return all the entries it finds. Note that when using the -Z dns database-map switch, if you specify a limit of 2, the successful result will be limited to just two addresses even if there are more.

DNS database-map -z switch

As of V8.14, the -z database-map switch may be used to specify the delimiter that separates one returned value from the next. For example:

LOCAL_CONFIG
Klookup dns -RA -z,

Here, the lookup database-map will query for an A record. Normally, if a host has several A records, this lookup would return only one. But with the addition of the V8.14 -z switch, sendmail will return all the entries it finds, each separated by the character specified, in this instance the comma:

hostA . example . com ,  hostB . example . com

Note that if a query will likely return too many entries, you may use the -Z dns database-map switch detailed in DNS database-map -Z switch on page 908 to limit the number returned.

hash

A db(3) form of database V8.1 and later

The hash database map type uses a hashing algorithm for storing data. This approach to a database is described in A New Hash Package for UNIX, by Margo Seltzer (Usenix Proceedings, Winter 1991). The hash type is available only if sendmail was compiled with NEWDB defined and the Berkeley or Sleepycat db(3) library linked.

The hash type is the default that is used with most of the features offered by the mc configuration technique (see Table 23-4 on page 896). For example, consider the following:

Kuudomain hash -o /etc/mail/uudomain

Here, a database map named uudomain is declared to be of type hash. The -o says that the database file /etc/mail/uudomain is optional.

Quite a few other database-map switches are available with this type. The complete list is shown in Table 23-13.

Table 23-13. The hash database-map type K command switches

Switch

§

Description

-A

-A on page 886

Append values for duplicate keys.

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-f

-f on page 887

Don’t fold keys to lowercase.

-m

-m on page 888

Suppress replacement on match.

-N

-N on page 889

Append a null byte to all keys.

-O

-O on page 889

Never add a null byte.

-o

-o on page 889

This database map is optional.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

The -d38.20 command-line switch (-d38.20 on page 568) can be used to observe this type’s lookups in more detail. See also the btree type (btree on page 901).

hesiod

MIT network user authentication services V8.7 and later

The hesiod type of database map uses the Hesiod system, a network information system developed as Project Athena. Support of hesiod database maps is available only if you declare HESIOD when compiling sendmail. (See HESIOD on page 115 for a fuller description of the Hesiod system.)

A hesiod database map is declared like this:

Kname hesiod HesiodNameType

The HesiodNameType must be one that is known at your site, such as passwd or service. An unknown HesiodNameType will yield this error when sendmail begins to run:

cannot initialize Hesiod map (hesiod error number)

One example of a lookup might look like this:

Kuid2name hesiod uid
R$-      $: $(uid2name $1 $)

Here, we declare the network database map uid2name using the Hesiod type uid, which converts user-id numbers into login names. If the conversion was successful, we use the login name returned; otherwise, we use the original workspace.

Quite a few database-map switches are available with this type. They are all listed in Table 23-14.

Table 23-14. The hesiod database-map type K command switches

Switch

§

Description

-A

-A on page 886

Append values for duplicate keys.

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-f

-f on page 887

Don’t fold keys to lowercase.

-m

-m on page 888

Suppress replacement on match.

-N

-N on page 889

Append a null byte to all keys.

-O

-O on page 889

Never add a null byte.

-o

-o on page 889

The network database map is optional.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

The -d38.20 command-line switch (-d38.20 on page 568) can be used to observe this type’s lookups in more detail.

host

Internal table to store lookup hostnames V8.1 and later

The host database-map type is a special internal database used by sendmail to help resolve hostnames. It is fully described under the $[ and $] operators in $[ and $]: A Special Case on page 895.

Only a few database-map switches are available with the host type, and they are listed in Table 23-15.

Table 23-15. The host database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-d

Timeout.resolver (V8.10 and later)

The res_search() _res.retry interval (V8.12 and later).

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-m

-m on page 888

Suppress replacement on match.

-r

Timeout.resolver (V8.10 and later) on page 1108

The res_search() _res.retries limit (V8.12 and later)

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

The -D database switch should probably always be used with this type on sites that have dial-on-demand connections to the Internet. It prevents host lookups when the DeliveryMode option (DeliveryMode on page 1004) is set to defer.

implicit

Search for an aliases database entry V8.1 and later

The implicit database-map type refers specifically to aliases(5) files only. It causes sendmail to first try to open a db(3) hash-style alias file. If that fails or if NEWDB support was not compiled in, it tries to open an ndbm(3)-style database. If that fails, sendmail reads the aliases(5) source file into its internal symbol table.

When sendmail rebuilds its aliases database (as with newaliases) it looks for the special string literal /yp/ anywhere in the path specified for the aliases source file. If that string literal is found, sendmail uses this implicit type to create both a db(3) hash-style alias file, and an ndbm(3)-style database. It creates both to support NIS compatibility.

Although you can declare and use this type in a configuration file, there is no reason to do so. It is of use only to the internals of sendmail. If implicit fails to open an aliases file, probably because of a faulty AliasFile option (AliasFile on page 970), sendmail will issue the following error if it is running in verbose mode:

WARNING: cannot open alias database bad filename

If the source aliases file exists but no database form exists, sendmail will read that source file into its internal symbol table using the stab type (stab on page 938).

You can experiment with this implicit database-map type using a mini configuration file such as this:

V10
Kxlate implicit -a.Yes -o /etc/mail/aliases
Stest
R$*     $: $(xlate $1 $)

Here, we declare a database map named xlate to be of type implicit. We use it to look up aliases in the file /etc/mail/aliases (which can optionally not exist because of the -o switch). We don’t care whether that file is a db file, a dbm file, or a text file. The implicit type will find the right type and use it. A successful match will append a .Yes suffix to the returned value.

The -d38.20 command-line switch (-d38.20 on page 568) can be used to observe this type’s lookups in db files and dbm files.

ldap (was ldapx)

The Lightweight Directory Access Protocol V8.8 and later

LDAP stands for Lightweight Directory Access Protocol and provides access to a service based on X.500. Additional information about LDAP is available from:

http://www.ldapman.org/

The ldap database-map type is used to look up items in that directory service. (Prior to V8.10, this was called ldapx to reflect its experimental condition at the time. That prior name still works but is deprecated.) The ldap database-map type is declared like this:

Kname ldap  switches

Lookups via LDAP are defined entirely by the switches specified. To illustrate, consider the following X.500 entry:

cn=Full Name, o=Organization, c=US
sn=Name
uid=yourname
mail=yourname@mailhub.your.domain
objectclass=person
objectclass=deptperson

To look up a login name in this database and have the official email address for that user returned, you might use a declaration such as this:

Kgetname ldap -k"uid=%s" -v"mail" -hldap_host -b"o=Organization, c=US"

Here we use only three switches:

  • The -k switch is in the form of an ldap_search(3) filter. Here, the key will replace the %s and then the whole expression will be searched using the new key.

  • The -b switch is necessary if you wish to specify the base from which to search.

  • The -h switch is required to specify the host to contact to perform the lookup.

The -k, -h, and -v switches are mandatory.

You can omit selected switches from the K configuration command by defining them with the LDAPDefaultSpec option (LDAPDefaultSpec on page 1039). In general, this option is used to define the -b and -h switch settings. You can, however, use it to define any number of defaults that you wish.

The following rule can be used with the preceding declaration to look up the preferred mail address for a user:

R $* <@ $=w . > $*       $: $(getname $1 $: $1<@$2>$3 $)

Here, we presume that this rule was preceded by a call to the canonify rule set 3 to focus on the host part of the address. If the lookup succeeds, the new (unfocused) address is returned from the mail= line in the database. Otherwise, the original address is returned.

This ldap type has more database switches available for it than most other types. They are all listed in Table 23-16.

Table 23-16. The ldap database-map type K command switches

Switch

§

Description

−1

The −1 ldap database-map switch on page 915

Consider successful only if one key is matched.

-A

-A on page 886

Append values for duplicate keys.

-a

-a on page 887

Append tag on successful match.

-b

The -b ldap database-map switch on page 915

Base from which to begin the search.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-d

The -d ldap database-map switch on page 915

DN to bind to server as.

-f

-f on page 887

Don’t fold keys to lowercase.

-H

The -H ldap database-map switch on page 915

Specify an LDAP URI (V8.13 and later).

-h

The -h ldap database-map switch on page 916

Hosts that serve this network database (required).

-K

The -K ldap database-map switch (V8.14 and later) on page 916

Use %1 though %9 in the query.

-k

The -k ldap database-map switch on page 917

The search query (required).

-l

-l (lowercase L) on page 888

Set a timeout for the lookup.

-M

The -M ldap database-map switch on page 917

The method to use for binding.

-m

-m on page 888

Suppress replacement on match.

-n

The -n ldap database-map switch on page 917

Retrieve attribute names only, not values.

-o

-o on page 889

The database map is optional.

-P

The -P ldap database-map switch on page 917

The secret password to use for binding.

-p

The -p ldap database-map switch on page 917

Port to use when connecting to host.

-q

-q on page 889

Don’t strip quotes from key.

-R

The -R ldap database-map switch on page 917

Don’t autochase referrals.

-r

The -r ldap database-map switch on page 918

Allow dereferencing of aliases.

-S

-S on page 890

Space replacement character.

-s

The -s ldap database-map switch on page 918

Search scope of “base,” “one,” or “sub”.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

-V

The -V ldap database-map switch on page 918

Specify a separator (V8.12 and later).

-v

The -v ldap database-map switch on page 919

Specify the list of attributes to return (required).

-w

The -w ldap database-map switch on page 921

Specify the LDAP API/protocol version (V8.13 and later).

-z

The -z ldap database-map switch on page 921

Specify return value delimiter.

-Z

The -Z ldap database-map switch on page 921

Limit the number of matches to return.

Although some of these switches are also used by other database-map types, many of them are unique to this ldap database-map type. In addition to setting switches with the K command, you can also preset selected switches with the LDAPDefaultSpec option (LDAPDefaultSpec on page 1039).

Each successful lookup can cause a line such as the following to be logged via syslog(3) when the LogLevel option (LogLevel on page 1040) is greater than 9:

qid: ldap  key  =>  value

Note that the ldap type can be used only if the LDAPMAP compile-time macro was defined when sendmail was compiled (LDAPMAP on page 119). Also note that the USING_NETSCAPE_LDAP compile-time macro (USING_NETSCAPE_LDAP on page 150) will need to be defined if your ldap libraries are from Netscape or are derived from Netscape’s libraries.

LDAP default schema for aliases includes recursion

As of V8.13, the default schema for alias lookups using LDAP has been changed to include LDAP recursion support. Recall that you declare alias lookups with LDAP like this:

define(`ALIAS_FILE', `ldap:')

This causes aliases to be looked up using LDAP and the following default schema:

ldap -k (&(objectClass=sendmailMTAAliasObject)
        (sendmailMTAAliasGrouping=aliases)
        (|(sendmailMTACluster=${sendmailMTACluster})
        (sendmailMTAHost=$j))
        (sendmailMTAKey=%0))
     -v sendmailMTAAliasValue,
        sendmailMTAAliasSearch:FILTER:sendmailMTAAliasObject,
        sendmailMTAAliasURL:URL:sendmailMTAAliasObject

Note that sendmail macros (like $j) are not expanded when the default schema is first defined. Rather, they are expanded each time an LDAP lookup is performed.

In the event you wish to use your own schema rather than the default, you may do so by appending it to ldap: when defining ALIAS_FILE:

define(`ALIAS_FILE', `ldap:-k (&objectClass=mg)(mail=%0) -v mmember')

Here, we replaced the long, recursive default schema shown earlier with a much shorter and nonrecursive schema of our own design.

See cf/README in the sendmail source distribution for an additional discussion of the default schema and how to use it.

LDAP default schema for classes includes recursion

As of V8.13, the default schema for class macro assignments using LDAP has been changed to include LDAP recursion support. For example, recall (in Class via ldap map lookups on page 862) that you declare classes with LDAP like this:

RELAY_DOMAIN_FILE(`@LDAP')

This causes the class $=R to be filled with values that match a sendmailMTAClassName with the value R. More generally, for any class X, the following default schema will be used:

F{X}@ldap:-k (&(objectClass=sendmailMTAClass)
        (sendmailMTAClassName=X)
        (|(sendmailMTACluster=${sendmailMTACluster})
        (sendmailMTAHost=$j)))
     -v sendmailMTAClassValue,
        sendmailMTAClassSearch:FILTER:sendmailMTAclass,
        sendmailMTAClassURL:URL:sendmailMTAClass

Note that sendmail macros (like $j) are not expanded when the default schema is first defined. Rather, they are expanded each time an LDAP lookup is performed.

See cf/README in the sendmail source distribution for an additional discussion of this default schema and how to use it.

The −1 ldap database-map switch

The −1 switch prevents LDAP from returning multiple values when only one is sought. This can be used to save the server from extra work. If −1 is specified, sendmail tells the LDAP server to examine only enough records to determine whether there is a single match. That is, if there is more than one match, and if this −1 switch is specified, the lookup will return that no matches were found.

Note the difference between the -Z and −1 switches. A -Z1 (The -Z ldap database-map switch on page 921) will return only the first match, while ignoring the rest of the matches. A −1 returns failure if there is more than one match.

The -b ldap database-map switch

The -b switch is used to specify the base tree from which to search. In general, if specified, it should ensure that sendmail will always get a unique result:

-b"o=Organization, c=US"

If the base contains a space character, the entire expression should be quoted. Here, the search will include only records under the tree shown. Essentially, this -b tree specification is prepended to each -k query value just before it is looked up.

The -d ldap database-map switch

The -d switch specifies the distinguished name (DN) to use when binding to the server.[347] In general, such names contain spaces and other special characters, and therefore should be quoted. For example:

-d"cn=Directory Manager, o=igloo CA, l=Melbourne, st=Victoria, c=AU"

There is no default, and this switch is optional. (See also the -P [The -P ldap database-map switch on page 917] and -M [The -M ldap database-map switch on page 917] switches.)

The -H ldap database-map switch

Modern versions of LDAP allow you to use Universal Resource Identifiers (URIs) in place of host and port combinations when specifying an LDAP server. Beginning with V8.13 sendmail, you may specify an LDAP URI using the new -H database-map switch. For example, prior to V8.13 sendmail, you might have used an mc configuration statement like this:

define(`confLDAP_DEFAULT_SPEC', `-h ldap.example.gov -p 8389')

Here, -h specifies the LDAP server host and -p specifies the nonstandard port 8389. Beginning with V8.13, you can simplify this declaration by using the -H database-map switch:

define(`confLDAP_DEFAULT_SPEC', `-H ldap://ldap.example.gov:8389')

One advantage of -H is that it allows you to fetch the URI from a secure server by using ldaps:// instead of ldap://, as for example:

define(`confLDAP_DEFAULT_SPEC', `-H ldaps://ldap.example.gov -b dc=example,dc=gov')

Here, the -b LDAP database-map switch (The -b ldap database-map switch on page 915) specifies the base from which to begin the search for the URI. If, rather than reading from a TCP/IP socket, your LDAP server uses a Unix-domain socket, you may use ldapi:// instead of ldap://, to access that Unix-domain socket:[348]

define(`confLDAP_DEFAULT_SPEC', `-H ldapi:///path/to/file -b dc=example,dc=gov')

Note that when you build sendmail with LDAP support, the sendmail code will look to see whether you have a working ldap_init() function in your LDAP library. If you do (and all modern versions of LDAP do), you will be allowed to use the new -H database-map switch. If not, you will see the following warning when you attempt to use it:

Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map name

If you believe sendmail interpreted your LDAP setup wrongly, you may define USE_LDAP_INIT when building to correct the error.

The -h ldap database-map switch

The -h is mandatory. It specifies the host, or hosts, to which to connect for the LDAP lookup. If you wish to specify a sequence of hosts, you can do so by listing them, each separated from the others by space characters:

-h"hostA hostB"

Here, because a space is the separator, this expression must be quoted. The lookup will cause sendmail to connect to hostA first. If it connects, and if a successful match is found, the lookup terminates and that value is returned. If the lookup fails, no further hosts are connected. If the connection cannot be established, a connection to the next host in the sequence (hostB) is tried, and if successful, the lookup is made on that host. This continues until all connections to all hosts have failed, or until a connection can be made.

In the event that you need to specify a port for a host different from that specified by the LDAP_PORT macro in the LDAP source, you can do so by using the -p switch (The -p ldap database-map switch on page 917) or by adding a port specification to one or more hosts. You add a port specification to a host by appending a colon, and then the port number:

-h"hostA hostB:463"

Here, hostA is contacted on the default port, and hostB is contacted on port 463.

In general, the hosts specified should be fully qualified hostnames:

-h ldaphost                 ← not this
-h ldaphost.your.domain     ← this is preferred

The -K ldap database-map switch (V8.14 and later)

The -K switch is optional. When used, it allows the arguments of -k to include the positional arguments %1 through %9. For example:

-K -k gid=%2

See your LDAP documentation to learn about the special meaning of certain characters (such as % and *) in lookup keys, and how to correctly formulate key lookup expressions.

The -k ldap database-map switch

The -k switch is mandatory. It is used to specify the key to look up. The lookup key is in the form of an ldap_search(3), which can be simple:

-k uid=%s

or omplex (note that we split the line to fit the page):

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

See your LDAP documentation to learn about the special meaning of some characters (such as % and *) in lookup keys, and how to correctly formulate key lookup expressions.

The -M ldap database-map switch

The -M switch specifies the method to use for binding. It can be one of three case-insensitive, literal expressions that specify the method: none, simple, or krbv4. Or it can be any of these with an LDAP_AUTH_ prefix. If it is any other expression or word, the following error is printed and logged when sendmail starts:

Method for binding must be [none|simple|krbv4] (not bad word) in map name

The default method is none, which means anonymous access to LDAP. This switch is optional. See also the -P switch (The -P ldap database-map switch on page 917) for simple and krbr4, and the -d switch (The -d ldap database-map switch on page 915) for simple only.

The -n ldap database-map switch

The -n switch is used to limit the returned information to attributes only. An attribute is the information to the left of the = in an X.500 entry:

cn=Full Name,  o=Organization,   c=US
sn=Name
uid=yourname
mailfrom=youraddress

One use for this switch might be to look up an address to see whether it is associated with a mailfrom or mailto attribute. If the address is not found, neither attribute will be returned. Otherwise, the attribute that defines the address will be returned.

The -P ldap database-map switch

The -P switch specifies the secret password to use when authenticating the distinguished name set by the -d switch (The -d ldap database-map switch on page 915). For the simple method (see -M; The -M ldap database-map switch on page 917), this is the pathname of the file containing the secret key. For the krbv4 method, this is the name of the Kerberos ticket file.

The -p ldap database-map switch

The -p switch specifies the port to which to connect on the LDAP server. The default port is defined by LDAP_PORT in the LDAP source. See also the -h switch (The -h ldap database-map switch on page 916) to see how a port number can be associated with individual hosts.

The -R ldap database-map switch

The -R switch is used to enable V2 and later LDAP to follow referrals. If the contacted server does not have the information sought, it can return a referral to one or more other servers that might have the information. With this -R switch specified, sendmail will follow referrals until either the information sought is found, or no more referrals are given. The -R switch can be used only if sendmail was compiled with the LDAP_REFERRALS compile-time macro defined.

The -r ldap database-map switch

The -r switch specifies how LDAP aliases are dereferenced. In LDAP, you can set a leaf entry (such as ou=hardware) to point to another object in the same name space (such as ou=engineering). This is called an alias entry. When you perform a lookup using an alias, the alias is dereferenced so that what is returned is the value of the object pointed to by the alias. For example, if your company once had two departments:

ou=hardware
ou=software

and those departments were merged into a single one called ou=engineering, you could achieve backward compatibility by turning the two, old leaf entries into aliases pointing to the new one. When sendmail encounters an LDAP alias it has four choices that are reflected by four possible, case-insensitive settings for the -r switch: never means to not follow aliases and instead to return a failed lookup; always means to follow aliases (the default); search means to perform a lookup first and to follow an alias only if the lookup succeeded; and find means to follow an alias only if all attributes match (that is, for example, the alias "cn=x, ou=y" won’t be followed if only ou= is looked up). These four keywords can also be prefixed with a literal LDAP_DEREF_ expression. If another word or expression is used, the following error is printed and logged:

Deref must be [never|always|search|find] (not badword) in map name

The -s ldap database-map switch

The -s switch is used to specify the scope of the search to perform on the LDAP server. There are three allowable scopes and, thus, three settings for the -s switch. They are: base (the default), which retrieves information only about the base distinguished name specified (with the -b switch, The -b ldap database-map switch on page 915); one, which retrieves information about entries one level below the base distinguished name, where the base entry is not included in this scope; and sub, which retrieves information about entries at all levels below the base distinguished name, where the base entry is included in this scope. The scope can also be any of these three keywords with an LDAP_SCOPE_ prefix. If it is any other word or expression, the following error is printed and logged when sendmail starts:

Scope must be [base|one|sub] (not badword) in map name

The -V ldap database-map switch

The -V switch (new with V8.12) allows you to specify a separator such that a lookup can return both an attribute and a value separated by that separator. Without this switch, only values are returned. With this switch, attributes/value pairs are returned. For example:

-V=

might cause a successful return to appear like this:

user=bob

The -v ldap database-map switch

The -v switch specifies a list of attributes to return. The attributes must be separated from each other by commas. If the attributes contain spaces, the entire expression must be quoted:

-voc,ou
-v"oc, ou"

If more than one attribute is requested (and if the -z switch [The -z ldap database-map switch on page 921] is omitted), only the first attribute value returned from the LDAP server is given to the rule set. If the -z switch is used to specify a delimiter, all the attributes returned from the LDAP server are returned to the rule that did the lookup, each value separated from the others by that delimiter. For the earlier -v example, and a -z:, the following values might be returned to the rule that did the lookup:

foo:org:bar

When you query multiple attributes from the LDAP server, it can be beneficial to also specify a -V switch (The -V ldap database-map switch on page 918), which causes the attribute name to be returned along with each value. For a -V= (along with the earlier -z:), for example, the following attribute names and returned values might be returned to the rule that did the lookup:

oc=foo:ou=org:oc=bar

Note that if you list too many attributes with -v (usually more than 64), the following error will print and log:

Too many return attributes in name (max 64)

Prior to V8.13, LDAP lookups could only return the actual data sought, rather than information that would automatically result in another lookup, but beginning with V8.13, lookups are allowed to be recursive. LDAP recursion allows a query to return either a new query, a Distinguished Name (DN) or an LDAP URL. When any of these are returned, they result in another lookup.

LDAP recursion is requested with this -v ldap database-map switch, which specifies the list of attributes to return, like this:

-v attribute:type:objectclass|objectclass|...

Here, the type can be one of four literal values: NORMAL, DN, FILTER, or URL.

The NORMAL type says that the attribute will be added to the result of the lookup if the record found is a member of the objectclass specified. NORMAL is the default type if type is omitted.

The Distinguished Name (DN) type expects that any matches of the attribute have a fully qualified distinguished name. If so, the sendmail program will perform a second lookup of the attribute using the returned DN record.

The FILTER type requires that any matches of the attribute have the value of an LDAP search filter. If so, the sendmail program will perform the same lookup again but will replace the original search filter with the new filter returned.

The URL type expects that the lookup will return a URL. If so, the sendmail program will perform a lookup using the returned URL and will then use the resulting attributes returned.

The objectclass list, in the -v expression, is optional and, if present, contains the object-class values for which the attribute applies. If there is more than one object-class value, each must be separated from the next by a vertical bar character (|). If object-class values are listed, the attribute will be used only if the LDAP record returned by a lookup is a member of any of the object-class values listed.

Note that recursion is liberal. That is, no error results if recursion ultimately fails to lead to an LDAP record. The lookup will simply fail in the same manner as it would if the record did not exist.

To illustrate, consider the following mc configuration file lines.

define(`confLDAP_DEFAULT_SPEC', `-H ldaps://ldap.example.com -b dc=example,dc=gov')

LOCAL_CONFIG
Kgetname ldap
-k (&(objectClass=sendmailMTAAliasObject)(sendmailMTAKey=%0))
-v sendmailMTAAliasValue,
   mail:NORMAL:inetOrgPerson,
   uniqueMember:DN:groupOfUniqueNames,
   sendmailMTAAliasSearch:FILTER:sendmailMTAAliasObject,
   sendmailMTAAliasURL:URL:sendmailMTAAliasObject

First, we use -H when defining confLDAP_DEFAULT_SPEC. The use of ldaps://, instead of ldap://, allows us to fetch the LDAP URI from the secure server, ldap.example.com.

Second, under the LOCAL_CONFIG part of our mc configuration file, we define a database map using the K configuration command. We give the database map the name getname and the type ldap. The -k LDAP database-map switch specifies the LDAP search query to use.

Note how LDAP recursion is used here. There are five statements following -v, each on its own line for clarity, and each separated from the next by a comma.

The first statement following the -v is a lone attribute named sendmailMTAAliasValue. Because it lacks a colon-keyword type, it is presumed to be type NORMAL. Here, any value in the sendmailMTAAliasValue attribute will be added to any result string regardless of any object classes (because the attribute has no object-classes).

The second statement contains an attribute named mail, defined to be the type NORMAL, with a single object class called inetOrgPerson. The value in the attribute mail will be added to the result string only if the LDAP record that is looked up is a member of the inetOrgPerson object class. The type NORMAL is not recursive. Only a single lookup is performed and only a single result is added to the string.

The third statement contains an attribute named uniqueMember, defined to be the type DN, with a single object class called groupOfUniqueNames. The type DN makes the action associated with the attribute uniqueMember recursive. When uniqueMember is looked up, the return value may contain zero or more DN records that belong to the object class groupOfUniqueNames. Each of those returned DN records will again be searched to find any of the attributes listed in the -v line.

The fourth statement contains an attribute named sendmailMTAAliasSearch, defined to be the type FILTER, with an object class of sendmailMTAAliasObject. The type FILTER makes the attribute sendmailMTAAliasSearch recursive. A lookup is made using the initial filter (the -k line) to find any new filters that are in the object class sendmailMTAAliasObject. For any that are found, a second lookup is performed using each new filter, to return any records that contain any of the attributes listed in the -v line.

The fifth statement contains an attribute named sendmailMTAAliasURL, defined to be the type URL, with an object class called sendmailMTAAliasObject. The type URL makes the attribute sendmailMTAAliasURL recursive. A lookup is made using the default URL to find any new URLs that are in the object-class sendmailMTAAliasObject. For any that are found, a second lookup is performed using each new URL to return records that contain the attributes requested in the original URL.

The -w ldap database-map switch

If your LDAP library returns one API version, but your LDAP server uses a different one, you force sendmail to use the version on the server by supplying this new -w switch with your ldap database-type declaration. For example, to look up a login name in an LDAP database and have the official email address for that user returned, you might use a declaration like this:

Kgetname ldap -k"uid=%s" -v"mail" -hhost -b"o=Organization, c=US" -w3

Note that the trailing argument to this K configuration line is the new -w switch, which specifies the use of LDAP API version 3 with the server running on host.

If your system’s <ldap.h> include file defines a maximum API version, and you exceed that maximum with -w, the following error will print:

LDAP version specified exceeds max of max in map name

If your system’s <ldap.h> include file defines a minimum API version, and you specify too low a minimum with -w, the following error will print:

LDAP version specified is lower than min in map name

Either error will cause the API version specified with -w to be ignored. For example, on Solaris 9, with Sun-supplied LDAP, the minimum and maximum are both set to 3.

The -z ldap database-map switch

By default, if a single query matches multiple values, only the first value will be returned unless the -z database-map switch is specified. The -z switch specifies a separator (delimiter) character that will separate one return value from the next when multiple values are returned:

-zchar

Here, char is a single, printable character. If you wish to specify a newline, tab, or backslash, you can do so using backslash-escaped notation (\n for newline, \t for tab, and \ for a backslash). In general, the character selected should not be one that you expect to be part of a returned value. No internal check is made to ensure that the character chosen by you makes sense for your values.

The -Z ldap database-map switch

The -Z switch is used to limit the number of entries returned on a single query. The default is unlimited.

Note the difference between the -Z and −1 switches. A -Z1 will return only the first match, while ignoring the rest of the matches. A −1 (The −1 ldap database-map switch on page 915) returns failure if there is more than one match.

FEATURE(ldap_routing)

When using ldap, it can be desirable to reroute an address to another host or a different email address. To accomplish this, V8.10 sendmail introduced FEATURE(ldap_routing). In its simplest form, it is declared in your mc configuration file like this:

FEATURE(`ldap_routing')

This declaration causes two ldap-type database maps to be defined:

Kldapmh ldap −1 -v mailHost
       -k (&(objectClass=inetLocalMailRecipient)(mailLocalAddress=%0))
Kldapmra ldap −1 -v mailRoutingAddress
       -k (&(objectClass=inetLocalMailRecipient)(mailLocalAddress=%0))

Here, the ldapmh stands for LDAP mail host, and the ldapmra stands for LDAP mail routing address.

Note that the LDAP server’s hostname (set with -h) and the base of the lookup (set with -b) were both omitted. FEATURE(ldap_routing) presumes you will set those values with the confLDAP_DEFAULT_SPEC option (LDAPDefaultSpec on page 1039) in your mc configuration file. If you don’t, FEATURE(ldap_routing) will fail.

For an example of how these database maps work, consider the following partial listing of an LDAP record:

mailLocalAddress: alice@your.domain
mailHost: another.domain
mailRoutingAddress: alice@another.domain

Assume that a rule set first checks to see whether the recipients domain is in the class $={LDAPRoute} (LDAPROUTE_DOMAIN and LDAPROUTE_DOMAIN_FILE on page 924). If it isn’t, it skips these lookups. Otherwise, the first database map, the ldapmh, looks up the attribute mailLocalAddress, and if the value following that item matches, it looks for the attribute mailHost. If that attribute is found, it returns that field’s value. The second database map, the ldapmra, also looks up the attribute mailLocalAddress, and if the value following that item matches, it looks for the attribute mailRoutingAddress. If that is found, it returns that field’s value.

The preceding two K configuration commands can be replaced with ones of your own design by adding extra arguments to FEATURE(ldap_routing):

                                                       replaces the declaration
following Kldapmh
                             ↓
 FEATURE(`ldap_routing', `newldapmh', ` newldapmra')
                                         ↑
                                                       replaces the declaration
following Kldapmra

For example, the following declaration:

FEATURE(`ldap_routing', `ldap −1 -T<TMPF> -v relayHub
        -k (&(objectClass=inetLocalMailRecipient)(mailLocalAddress=%0))')

would result in this new ldapmh K configuration line:

Kldapmh ldap −1 -v relayHub
       -k (&(objectClass=inetLocalMailRecipient)(mailLocalAddress=%0))

For backward compatibility, FEATURE(ldap_routing) will not bounce addresses that fail to be found with a lookup. Instead, they will be delivered as is. If you want to bounce those failed lookups, you can add a third argument to the preceding declaration:

FEATURE(`ldap_routing', `newldapmh', ` newldapmra',  `bounce')

If the third argument is present and is neither an empty string nor the string passthru, failed lookups will bounce. To make your meaning clear, we recommend you restrict your choices to the two literal words bounce or passthru. Table 23-17 shows the relationship between the two database maps and the lack or presence of a bounce. Beginning with V8.13, two additional arguments are allowed (discussed shortly).

Table 23-17. FEATURE(ldap_routing) lookup relationships

Value of mailHost

Value of mailRoutingAddress

Result

Is a local host

Exists

Deliver to mailRoutingAddress address.

Is a local host

Does not exist

Deliver to original address.

Is a remote host

Exists

mailRoutingAddress is relayed via mailHost.

Is a remote host

Does not exist

Original address is relayed via mailHost.

Does not exist

Exists

Deliver to mailRoutingAddress address.

Does not exist

Does not exist

If bounce defined, bounce as “User Unknown.” Otherwise, deliver to original address.

Beginning with V8.13, a new literal word, sendertoo, may be used in place of either bounce or passthru. When you specify sendertoo, you cause the envelope sender to also be rejected if that address is not found in LDAP. Thus, sendertoo acts as if bounce was also specified (that is, both not-found recipients and senders will be rejected).

If you wish to define how +detail addresses (Plussed Detail Addressing on page 476) are handled, you can do so by adding a fourth argument to FEATURE(ldap_routing). That fourth argument must be either a literal strip or a literal preserve:

FEATURE(`ldap_routing', `newldapmh', ` newldapmra',  `bounce', `strip')

If an address contains a +detail (such as george+nospam), strip causes the address to first be looked up with the +detail attached, and if no match is found, strip removes the +detail and looks up the address again. A preserve is the same as strip except that if a mail routing address match is found (with ldapmra), the +detail is copied from the original address and appended to the new address. If neither preserve nor strip is specified, the address is looked up only with the +detail attached.

For FEATURE(ldap_routing) to work, you need to set your LDAP entries with an objectClass of inetLocalMailRecipient. If present, there must be only one mailHost attribute, and it must contain a fully qualified hostname as its value. If present, there must be only one mailRoutingAddress attribute, and it must contain a legal RFC2822 address as its value. For example:

dn: uid=alice, o=your.domain, c=US
uid: alice
objectClass: inetLocalMailRecipient
mailLocalAddress: alice@your.domain
mailRoutingAddress: alice@another.domain

This entry would cause mail destined for alice@your.domain to be delivered to the new address alice@another.domain.

The flow of FEATURE(ldap_routing) through the parse rule set 0 looks like this:

  1. Basic canonicalization (list syntax, delete local host, etc.)

  2. LOCAL_RULE_0 (LOCAL_RULE_0 mc macro on page 596)

  3. FEATURE(ldap_routing)

  4. FEATURE(virtusertable) (FEATURE(virtusertable) on page 645)

  5. Addresses of the form user@$=w passed to local delivery agent (The parse Rule Set 0 on page 696)

  6. FEATURE(mailertable) (FEATURE(mailertable) on page 629)

  7. UUCP, BITNET_RELAY ($B on page 808), etc.

  8. LOCAL_NET_CONFIG (LOCAL_NET_CONFIG mc macro on page 598)

  9. SMART_HOST (SMART_HOST mc macro on page 597)

  10. SMTP, local, etc. delivery agents

Beginning with V8.13, two more arguments are now available for your use:

FEATURE(`ldap_routing', `newldapmh', ` newldapmra',  `bounce', `detail', `
nodomain',  `tempfail')

The new argument, nodomain (in the fifth position following ldap_routing), is an argument with no literal word required—for example, any one of nodomain, no, or UncleBob will work. Without this new argument (if there are fewer than five arguments or if this argument is present but empty), a failed lookup of an address (user@host.domain) would cause the @host.doman part of the address to also be looked up in LDAP. But the presence of an argument in the nodomain position prevents that secondary lookup.

The new sixth argument, tempfail, can be one of two possible literal expressions: tempfail or queue. These tell sendmail what to do if sendmail cannot connect to the LDAP server, and what to do if the LDAP lookup fails because of a temporary LDAP failure. If this sixth argument is missing (if there are fewer than six arguments or if this argument is present and empty) or if it contains the literal queue, the message will be queued for a later attempt. If the sixth argument contains the literal tempfail, the message will be temporarily rejected with a 4yz reply code. We recommend you make your intent clear by specifying a literal queue rather than omitting the sixth argument and relying on the default.

LDAPROUTE_DOMAIN and LDAPROUTE_DOMAIN_FILE

FEATURE(ldap_routing) (explained earlier) only looks up addresses with domains that are listed with the $={LDAPRoute} class. The mc configuration technique provides two macros that facilitate the process of adding domains to the $={LDAPRoute} class:

LDAPROUTE_DOMAIN(`list of domains')
LDAPROUTE_DOMAIN_FILE(`file')

The first form directly adds the list of domains to the $={LDAPRoute} class by creating a C configuration file command. The second indirectly adds domains to the $={LDAPRoute} class by reading them from a file. It does this by creating a F configuration file command.

LDAPROUTE_EQUIVALENT and LDAPROUTE_EQUIVALENT_FILE

In addition to looking up hosts in the $={LDAPRoute} class (explained earlier), FEATURE(ldap_routing) will also look up hosts in the $={LDAPRouteEquiv} class. The difference is that hosts in this $={LDAPRouteEquiv} class are converted into the MASQUERADE_AS (MASQUERADE_AS mc Macro on page 600) host’s name just before the lookup.

The mc configuration technique provides two macros that facilitate the process of adding hosts to the $={LDAPRouteEquiv} class:

LDAPROUTE_EQUIVALENT(`list of domains')
LDAPROUTE_EQUIVALENT_FILE(`file')

The first form directly adds the list of domains to the class by creating a C configuration file command. The second indirectly adds domains to it by reading them from a file. It does this by creating an F configuration file command.

macro

Store a value into a macro via a rule V8.10 and later

Not only is it possible to use defined macros in rule sets, but as of V8.10 sendmail, it is also possible to place new values into defined macros from inside rule sets. This marvel is accomplished using the macro-type database map. It is an internal type, always available regardless of how sendmail was compiled.

The macro type can be used in three ways. For example:

Kstore_it_in macro
R$*     $: $(store_it_in {MyMacro} $@ $1 $)   ← store new value into macro
R$*     $: $(store_it_in {MyMacro} $@ $)      ← clear macro to empty string
R$*     $: $(store_it_in {MyMacro} $)         ← undefine the macro

The first line declares store_it_in to be the name of a macro database-map type that is used in the rules that follow. Those three rules show three different ways to affect the value stored in the macro {MyMacro}. Note that the macro name must not be prefixed with a $. If it is, its value will be used as the name, instead of its actual name. We cover the use of $& later.

The first rule shows that the value to be stored into the macro is passed as the first $@ argument, the $1. If this value is that of an undefined macro, the stored result is an empty string. Otherwise, the value is stored as is into the {MyMacro} macro. If the value contains macro-like expressions (such as $x), their values are used. If {MyMacro} was previously undefined, it becomes defined.

The second rule shows what happens when the value to be stored is missing (or undefined). A missing value has the effect of clearing the value stored in the {MyMacro} macro to that of an empty string. If {MyMacro} was previously undefined, it becomes defined.

The third rule shows what happens when the argument part (the $@ part) is omitted. The effect is to undefine the {MyMacro} macro.

Regardless of how you update the value in the macro, an empty string is returned. This can cause the original workspace to be lost. If you need to preserve the original workspace (or part of it), consider a variation such as the following:

R$*     $: $(store_it_in {MyMacro} $@ $1 $) $1
                                            ↑
                                    return original workspace

This macro type can also be used as an indirect way to store values into different sendmail macros. To illustrate, consider the following mini configuration file and its use of $&:

V10
Kput macro
D{Target}{LocalTarget}
Stest
R $*    $: $(put $&{Target} $@ $1 $)

Here, the intention is to store the value (the $@ $1) into the macro whose name is stored in {Target}. The D line initializes that name as {LocalTarget}. To witness this indirect method in action run this mini configuration file in rule-testing mode:

% /usr/sbin/sendmail -Cdemo.cf -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
Enter <ruleset> <address>
> test foo in local target
test               input: foo in local target
test             returns:
> ${LocalTarget}
foo in local target
> .D{Target}{NewTarget}
> test foo in new target
test               input: foo in new target
test             returns:
> ${NewTarget}
foo in new target

This sort of indirection can be useful in rules that might, for example, cause one relay host to be selected under high load and another under low load. Another use might be to reject certain outside mail during business hours, but accept it after business hours.

No database-map switches are useful with this type.

netinfo

NeXT, Darwin, and Mac OS X NetInfo V8.7 and later

NetInfo is NeXT’s implementation of a network-based information service. It has also been adopted by the Darwin and Mac OS X operating systems. The netinfo type expects a database-map declaration to be of the following form:

Kname netinfo  database-map

The database-map name defaults to /aliases.

The netinfo type uses only a handful of database switches, as shown in Table 23-18.

Table 23-18. The netinfo database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-k

-k on page 888

Specify column for key or key name.

-m

-m on page 888

Suppress replacement on match.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

-v

-v on page 891

Specify the value’s column.

-z

-z on page 891

Specify the column delimiter.

The -v property returns defaults to members. The -z column delimiter defaults to a comma.

Support of netinfo database maps is available only if you declare NETINFO when compiling sendmail (NETINFO on page 127).

nis

Sun’s Network Information Services (NIS) V8.1 and later

Sun Microsystems offers a network information service called NIS. It provides the ability to look up various kinds of information in network databases. The nis type allows you to access that network information by way of rules in rule sets. You declare an nis database-map type like this:

Kname nis nismap

Here, name is the identifier that you will later use in rule sets. The nismap is any NIS database map that defaults to mail.aliases. Lookups will occur in the default NIS domain. If you wish to specify some other domain, you can append an @ character and the domain name to the nismap:

Kname nis nismap @ domain

To illustrate, consider the need to look up the name of the central mail server for your department. If such a database map were called mailservers, you could use the following configuration file line to look up your domain in that database map:

Kmailservers nis -o mailservers
...
R $* <@ $+ > $*          $: $1<@$2>$3 <$(mailservers $2 $)>
R $* <@ $+ > $* <$+>     $#smtp $@ $4 $: $1 < @ $2 > $3
...

Here, we look up the host part of an address ($2) in the mailservers NIS database map. The -o makes the existence of the database map optional. If the host part is found, it is rewritten to be the name of the mail server for that host. In the last rule, we forward the original address to that server.

Without the -o, the nonexistence of a database map will cause this error to be logged:

Cannot bind to map name in domain  domain:  reason here

If NIS is not running or if sendmail cannot bind to the domain specified for the default domain, the following error is logged:

421 4.3.5 NIS map name specified, but NIS not running

Only a few database switches are available with this nis database-map type. They can be found in Table 23-19.

Table 23-19. The nis database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-f

-f on page 887

Don’t fold keys to lowercase.

-m

-m on page 888

Suppress replacement on match.

-N

-N on page 889

Append a null byte to all keys.

-O

-O on page 889

Never add a null byte.

-o

-o on page 889

This database map is optional.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to return on temporary failure.

-t

-t on page 891

Ignore temporary errors.

The nis database-map type is available only if sendmail is compiled with NIS defined (NIS on page 128).

nisplus

Sun’s newer version of NIS V8.7 and later

Sun Microsystems’ NIS+ is a complete redo of its earlier NIS system. The nisplus type allows you to look up information using NIS+. The form of that type declaration looks like this:

Kname nisplus  nismap.domain

Here, the nismap is an NIS+ database-map name, such as mail_aliases.[349] If the domain or .domain is missing, the nisplus default domain is used. If the entire nismap.domain is missing, the default becomes mail_aliases.org_dir. The domain org_dir contains all the systemwide administration tables.

Any lookup failures that can be retried will automatically be retried up to five times, with a sleep(3) of 2 seconds between each try. If the map.domain doesn’t exist in the local NIS+ system, no error is reported.

Only a modest number of database switches are available for this type. They are listed in Table 23-20.

Table 23-20. The nisplus database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-f

-f on page 887

Don’t fold keys to lowercase.

-k

-k on page 888

Specify column for key or key name.

-m

-m on page 888

Suppress replacement on match.

-o

-o on page 889

This database map is optional.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

-v

-v on page 891

Specify the value’s column.

You can use the -k switch to specify a key column to look up. Under nisplus, columns are named, so the -k must be followed by a valid name. You can also use the -v switch to specify the value’s column, and a name. If the -v is omitted, the last column becomes the default.

nsd

IRIX nsd database maps V8.10 and later

The nsd database-map type implements an interface to the Unified Name Service supplied under IRIX 6.5 and later. That service is a translation layer between any program and a wide range of services (ranging from NIS to LDAP). You declare an nsd database-map type like this:

Kname nsd  switches  nsdmap

The name is the symbolic name you will later use in the RHS of rule sets. The nsdmap is a full path into the nsd(8) daemons’ name space (such as /ns/engr.sgi.com/passwd.byname).

This nsd database-map type supports only a few switches. They are listed in Table 23-21.

Table 23-21. The nsd database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-f

-f on page 887

Don’t fold keys to lowercase.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

This nsd type was contributed by Bob Mende of SGI Inc.

null

Provide a never-found service V8.7 and later

The null database-map type is an internal service that always returns a failed lookup.

Normally, the null type is used only internally. It can, however, be useful when used to replace another database-map type so that it can force failure without causing an error. Consider, for example, a tiny configuration file that does not need the use of the aliases facilities. One way to declare aliases would be like this:

O AliasFile=null:

This tells sendmail to use the null type for looking up aliases. Therefore, no aliases will ever be found.

None of the K command switches can be used with the null database-map type. If you try to use any, they will be silently ignored. No debugging switch is available to watch this null database-map type.

ph

CCSO Nameserver (ph) lookups V8.10 and later

Prior to V8.10 sendmail, redirecting email with a ph server required running the phquery program. Beginning with V8.10 sendmail, a database-map type called ph has been added that allows sendmail to perform direct ph queries. You declare it like this:

Kname ph  switches

The complete list of switches for this database-map type is shown in Table 23-22.

Table 23-22. The ph database-map type K command switches

Switch

§

Description

[a]

-A

-A on page 886

Append values for duplicate keys.

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-f

-f on page 887

Don’t fold keys to lowercase.

-h

The -h ph database-map switch on page 931

Hosts that serve this network database map.

-k

The -k ph database-map switch on page 931

Specify a list of fields to query.

-l

-l (lowercase L) on page 888

Set a timeout for the lookup.[a]

-m

-m on page 888

Suppress replacement on match.

-N

-N on page 889

Append a null byte to all keys.

-O

-O on page 889

Never add a null byte.

-o

-o on page 889

This database map is optional.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

-v

 

Deprecated, and as of V8.13 removed, use -k instead.

[a] a As of V8.10, _FFR_PHMAP_TIMEOUT must be defined when compiling sendmail to enable this -l switch. As of V8.11, that definition is no longer necessary.

This ph database map was contributed by Mark Roth of the University of Illinois at Urbana-Champaign. For additional information see:

http://www.feep.net/sendmail/phmap/

The -h ph database-map switch

The -h switch is used to specify the host to which to connect for the lookup. In general, the host specified should be a fully qualified hostname:

-h phserver.your.domain

In the event you wish to employ multiple ph servers, you can list them, one separated from the next by a space character:

-h "phserver.your.domain phserver2.your.domain"

Because the host list contains space characters, it must be quoted.

Note that this -h switch is mandatory. If it is omitted, the following error is printed and logged:

ph_map_parseargs: -h flag is required

The -k ph database-map switch

The -k switch[350] specifies a quoted, space-delimited list of fields to query.[351] Fields are queried in the order listed, and the first query that returns a single match is the one whose returned value is used. If the -k switch is omitted, the list of fields to query is obtained by looking up the mailmatches field in the ph server’s siteinfo list.

program

Run an external program to look up the key V8.7 and later

The program type allows you to perform lookups via arbitrary external programs. The form for the declaration of this database-map type looks like this:

Kname program  /path  arg1 arg2 ...

The /path must be the full pathname to the program. Relative paths will not work, and attempts to use them will log the following error and cause the lookup to fail:

NOQUEUE: SYSERR(user): relative name: cannot exec: No such file or directory

The program is run as the user and group specified by the DefaultUser option (DefaultUser on page 1000) unless the RunAsUser option (RunAsUser on page 1083) is declared, in which case it will run as the user and group declared by that latter option.

The arguments to the program always have the key to be looked up added as a final argument:

Kname program  /path  arg1 arg2 ...key added here

This is the only way the key can be passed to the program. The key will specifically not be piped to the program’s standard input.

The value (result of the lookup) is read from the program’s standard output. Only the first MAXLINE-1 characters are read (where MAXLINE is defined in conf.h, currently as 2048). The read result is processed like an address and placed into the workspace (unless the -m switch is used with the K command).

To illustrate, consider the need to look up a user’s preferred address in an external relational database:

Kilook program /usr/sbin/ingres_lookup -d users.database

This program has been custom-written to accept the key as its final argument. To prevent spurious errors, it exits with a zero value regardless of whether the key is found. Any system errors cause it to exit with a value selected from those defined in <sysexits.h > (those recognized by sendmail). Error messages are printed to the standard error output, and the found value (if there was one) is printed to the standard output.

In general, it is better to use one of the database formats known to sendmail than to attempt to look up keys via external programs. The process of fork(2)ing and exec(2)ing the program can become expensive if it is done often, slowing down the handling of mail.

This type of database map employs only a small set of switches. They are listed in Table 23-23.

Table 23-23. The program database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-m

-m on page 888

Suppress replacement on match.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

regex

Use regular expressions V8.9 and later

The regex type allows you to parse tokens in the workspace using POSIX regular expressions. For information on how to use regular expressions, see the online manuals ed(1) and regexp(1). A regex database-map type is declared like this:

Kname regex expression

The name is the symbolic name you will use to reference this database map from inside the RHS of rule sets. The expression is the literal text that composes your regular expression. Here is a simple example:

Knumberedname regex   ^[0-9]+<@(aol|msn).com.?>

The intention here is for this regular expression to match any address that has an all-numeric user part (the part before the <@) and a domain part that is either aol.com or (the | character) msn.com. To make rules that use this type easier to write, you can add a -a switch to the declaration:

Knumberedname regex -a.FOUND ^[0-9]+<@(aol|msn).com.?>

Here the -a database switch causes .FOUND to be appended to any successful match.

Note that because of the way we have declared this database map, nothing but the suffix will be returned on a successful match. To get the original key returned you need to also use the -m database switch (-m on page 888).

This regex type can use a number of switches to good advantage. The complete list is shown in Table 23-24.

Table 23-24. The regex database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-b

The -b regex database-map switch on page 933

Use basic, not extended, regular expression matching.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-d

The -d regex database-map switch on page 934

The delimiting string.

-f

-f on page 887

Don’t fold keys to lowercase, and cause the regular expression to match in a case-insensitive manner.

-m

-m on page 888

Suppress replacement on match.

-n

The -n regex database-map switch on page 934

NOT—that is, invert the test.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-s

The -s regex database-map switch on page 934

Substring to match and return.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

Note that some additional explanation for a few of these switches is provided in the sections that follow. Also, for an actual example of the regex type, see the file cf/cf/knecht.mc, which demonstrates a way to deal with one type of spam email.

The -b regex database-map switch

The -b switch limits the regular expression to a more limited but faster form. If you are using only simple regular expressions, as in the nature of those defined by ed(1), you can use this -b switch to slightly speed up the process:

Kmatch regex -b -aLOCAL @localhost

Here, the search is for a workspace that contains the substring @localhost. Because this is a very simple regular expression, the -b switch is appropriate. If you use the -b on a complex match (such as the one in the previous section’s -n example), you might see an error such as this:

configfile: line num: field (2) out of range, only 1 substring in pattern

The -d regex database-map switch

There might be times when you would prefer some other character, operator, or token to replace the $| that is returned when using the -s switch. If so, you can specify a different one with the -d database switch. Consider:

Kmatch regex -s2,3 -d+|+ -a.FOUND (\<a\>|\<b\>)@(\<bob\>|\<ted\>).
(\<com\>|\<org\>)

Here, we specify that the three characters +|+ will replace the single operator $| in the returned value:

> test a@bob.com
test               input: a @ bob . com
test             returns: bob+|+com . FOUND

Note that here the bob+|+com is a single token.

You can opt to have the original key returned. This is done by specifying the -m database switch:

Kmatch regex -s2,3 -m -d+|+ -a.FOUND (\<a\>|\<b\>)@(\<bob\>|\<ted\>)
.(\<com\>|\<org\>)

Note that the -m switch overrides the presence of the -s and -d switches:

> test a@bob.com
test               input: a @ bob . com
test             returns: a @ bob . com . FOUND

The -n regex database-map switch

The -n switch inverts the entire sense of the regular expression lookup. It returns a successful match only if the regular expression does not match. Consider:

Kmatch regex -m -n -a.FOUND (\<a\>|\<b\>)@(\<bob\>|\<ted\>).(\<com\>|\<org\>)

If you view the effect of this switch in rule-testing mode, you will see that the result is inverted:

> test a@bob.com
test               input: a @ bob . com
test             returns: a @ bob . com
> test x@y.net
test               input: x @ y . net
test             returns: x @ y . net . FOUND

The -s regex database-map switch

The -s database-map switch is used with the regex type to specify a substring to match and return. To illustrate, consider the following mini configuration file:

V10
Kmatch regex -s (\<bob\>|\<ted\>)
Stest
R $*       $@ $(match $1 $)

The regular expression looks to match either the name bob or the name ted, but no other names. The -s says to return the substring actually matched in the expression along with the key, the two separated from each other by a $| operator. Now, observe this mini configuration file in rule-testing mode:

% /usr/sbin/sendmail -bt -Cdemo.cf
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
Enter <ruleset> <address>
> test bob
test               input: bob
test             returns: bob $| bob
> test alice
test               input: alice
test             returns: alice

By adding a -a switch, which appends text to the matched key:

Kmatch regex -s -a.FOUND (bob|ted)

we see that the matched key with -s is second:

> test bob
test               input: bob
test             returns: bob $| bob . FOUND

When multiple substrings can be matched, the -s database switch can be used to specify which substring match to return. Consider:

Kmatch regex -s2 -a.FOUND (\<a\>|\<b\>)@(\<bob\>|\<ted\>)

There are two substring searches here, first the (\<a\>|\<b\>) choice and then the (\<bob\>|\<ted\>) choice. Because the -s has a 2 as its argument, the second matched substring will be returned, not the first:

> test a@bob
test               input: a @ bob
test             returns: bob . FOUND

In more complex expressions, it might be desirable to return multiple substrings. To do that just list them following the -s with each separated from the next by a comma:

Kmatch regex -s2,3 -a.FOUND (\<a\>|\<b\>)@(\<bob\>|\<ted\>).(\<com\>|\<org\>)

When multiple substrings are listed in this way, they are separated by the $| operator when they are returned:

> test a@bob.com
test               input: a @ bob . com
test             returns: bob $| com . FOUND

sequence

Search a series of database maps V8.7 and later

The sequence type allows you to declare a single name that will be used to search a series of databases. It is declared like this:

Kname sequence  map1  map2 ...

Here, a key (in a later rule set) will be looked up first in the database map named map1, and if not found there, it will be looked up in the database map named map2. The type of each of the listed database maps should logically relate but need not be the same. Consider, for example, a rule’s RHS, where a lookup will match if the workspace contains either a user’s login name or the name of a host, with the hostname taking precedence:

Khosts host  -a<+> /etc/hosts
Kpasswd user -a<-> /etc/passwd
Kboth sequence hosts passwd

R$-     $: $(both $1 $)

Here, we say that the database map named both is of type sequence. Any single token in the LHS will be looked up first in the database map named hosts, and if it is found there the hostname will be returned with a <+> appended. If it is not found in the hosts database map, it will be next looked up in the passwd database map. If it is found there, the original workspace will be returned with a <-> appended. If the workspace is not found in either database map, the lookup fails and the workspace remains unchanged.

If any database map in the series of database maps declared with the K command does not exist, as for example:

Kboth sequence hosts passwd badname

the following error is logged and printed, and that database map is ignored:

Sequence map both: unknown member map  badname

If the number of database maps that are sequenced exceeds the maximum allowed (MAXMAPSTACK in conf.h, currently 12), the following error is printed and the overflow of database maps is ignored:

Sequence map name: too many member maps ( max  max)

None of the K command switches can be used with the sequence type. If you try to use any, they will be wrongly interpreted as database-map names.

socket

Perform lookups over a socket V8.13 and later

Beginning with V8.13 sendmail, a new database-map type called socket is available for your use.[352] You declare a socket database-map type like this:

Kname socket type:port@host

Here, name is the identifier that you will later use in rule sets. The type:port@host is declared in the same fashion as a Milter is declared using the X configuration command (The X Configuration Command on page 1173). For example:

Ktrustedip socket inet:8020@db5.example.gov

Here, lookups can be made in rule sets using the database map named trustedip. The sendmail program will make an IPv4 connection (the inet) to port 8020 on the host db5.example.gov. Once the connection has been made, lookups are performed using a simple dialog that looks like this:

sendmail sends:    database_map_name key
sendmail receives:   status datum

Note that neither the request sent nor the reply received may end in a carriage-return/linefeed pair, a carriage return, or a line feed. Also note that the two parts of each dialog are separated by a single space character.

Both the request and the reply begin and end with characters that denote their length and termination. The length is an ASCII representation of the number of characters sent or received, stated as a prefix and a colon. Both the request and the reply are terminated by a comma. But note that the length prefix must not include the comma in its length computation. For example:

sendmail sends:    17:trustedip 1.2.3.4,
sendmail receives:  14:OK VERYTRUSTED,

The sendmail program sends the database-map name declared earlier using a K configuration command. In our example, that would be the database map named trustedip. That name is followed by a single space and then the key to look up in the database. Again, the entire request is prefixed with the length and a colon and terminated with a comma (and excludes any terminating newline or carriage-return characters).

The connected-to host replies with one of the keywords shown in Table 23-25. Each must be completely uppercase. Each keyword is followed by a single space, then information appropriate to the keyword (the keyword OK, for example, would be followed by the sought datum). The entire reply is prefixed with a length and a colon and terminated with a comma.

Table 23-25. The socket database-map reply keywords

Keyword

Description

OK

The key was found in the database, and the datum is the value sought.

NOTFOUND

The key was not found in the database, and the datum is empty.

TEMP

A temporary failure occurred while performing the lookup. The datum may contain an explanatory message.

TIMEOUT

The lookup timed out. The datum may contain an explanatory message.

PERM

A permanent failure occurred while performing the lookup. The datum may contain an explanatory message.

To illustrate, consider the need to look up the name of the central mail server for your department. If such a database map were called mailservers, you could use the following configuration file line to look up your domain in that database map:

Kmailservers socket -o inet:8020@db4.example.gov
...
R $* <@ $+ > $*          $: $1<@$2>$3 <$(mailservers $2 $)>
R $* <@ $+ > $* <$+>     $#smtp $@ $4 $: $1 < @ $2 > $3
...

Here, we look up the host part of an address ($2) in the mailservers database on the host db4.example.gov. The -o makes the existence of the database map optional. If the host part is found, it is rewritten to be the name of the mail server for that host. Finally, in the last rule, we forward the original address to that server.

Note that only a few database switches (shown in Table 23-26) are available with this socket database-map type.

Table 23-26. The socket database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-f

-f on page 887

Don’t fold keys to lowercase, and cause the regular expression to match in a case-insensitive manner.

-m

-m on page 888

Suppress replacement on match.

-N

-N on page 889

Append a null byte to all keys.

-O

-O on page 889

Never add a null byte.

-o

-o on page 889

This database map is optional.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character

-T

-T on page 890

Suffix to append on temporary failure

-t

-t on page 891

Ignore temporary errors.

Note that the socket database-map type is available only if sendmail is compiled with the SOCKETMAP compile-time macro (SOCKETMAP on page 145) defined when you build sendmail (which is normally not done by default).

For examples of how to use this new socket database-map type, see the files contrib/socketmapServer.pl and contrib/socketmapClient.pl.

stab

Internally load aliases into the symbol table V8.10 and later

The stab database-map type is used internally by sendmail to load the raw aliases(5) file into its internal symbol table.[353] This is a fallback position that is taken if no database form of aliasing is found.

The stab type should never be used in configuration files.

switch

Build sequences based on service switch V8.7 and later

The switch database-map type is used internally by sendmail to create sequence types of database maps based on external service-switch files. The lines inside a service-switch file look like this:

service     methodA methodB

as, for example:

aliases   files nis

This line tells sendmail to search for its aliases in files first, and then using NIS.

To illustrate the switch type, consider the need to look up aliases inside rule sets in the same way that sendmail looks up its own aliases. To do this, you would declare a switch database map. For example:

Kali switch aliases

This causes sendmail to search for the service named aliases in the service-switch file. In this example it finds such a line, so for each how that follows the aliases in that line, sendmail creates a new database map with the name ali followed by a dot and the how:[354]

aliases   files    becomes →    ali.files
aliases   nis      becomes →    ali.nis

These named database maps are then sequenced for you. Recall that sequence database maps are declared like this:

Kname sequence  map1  map2,...

The name given to the sequence is ali. In our example, the following sequence is automatically created for you from your original switch declaration:

Kali sequence ali.files ali.nis

In rule sets, when you look up aliases with the ali database map:

R...        $( ali $1 $)
                ↑
          the sequence named ali

you will use the sequence named ali that was automatically built for you from a combination of your original switch definition and your service-switch file’s aliases line. That is, you declare a switch, but you use a sequence.

syslog

Log information using syslog(3) via rule sets V8.10 and later

The syslog database-map type allows you to log messages directly from inside rule sets. If you are unfamiliar with syslog, see Log with syslog on page 513 for a general discussion of syslog-style logging.

The syslog type is declared like this:

Kname syslog switches

The name is the database-map name you will use in rule sets. The switches are selected from those shown in Table 23-27.

Table 23-27. The syslog database-map type K command switches

Switch

§

Description

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-L

The -L syslog database-map switch on page 940

The logging level at which to log.

-S

-S on page 890

Space replacement character.

In rule sets, the syslog type is used, for example, like this:

R $*    $: $(name what to log $)

The information in the position of the key is logged as is via the syslog facility. An empty workspace is returned as a result of logging. That is, for the syslog type, the $( and $) expressions evaluate to an empty string.

Any use of defined macros in the message should use the $& prefix so that the current value is logged. For example, the following might be used to log the load average:

Kdolog syslog
R $*    $: $(dolog The cutoff was caused by a load average of $&{load_average}. $)

If you need to have a sendmail macro or positional macro literally logged as is, just prefix it with an extra $ character. For example, the following shows the macro and logs its value:

R $*    $: $(dolog Failure detected with $$1=$1 $)

Don’t use quotation marks to surround macro references. Quotation marks cause the macro’s internal binary value to print, instead of its defined value. For example, the following will log $1=^U1:

R $*    $: $(dolog $$1="$1" $)         ← wrong

If macros are not included inside quotation marks, you can use quotation marks for clarity. They will be stripped from the output:

R $*    $: $(dolog "Aborting the use of ETRN because of high load" $)

In general, this syslog type of database map will be used in conjunction with other database maps that can make decisions about behavior, such as arith (arith on page 898). You should avoid the temptation to overlog because rule sets can be parsed every time mail is sent or received, and if you place a logging rule in the wrong place, you risk flooding your site’s syslog facility with extraneous messages.

The -L syslog database-map switch

Normally, the logging priority (syslog(3) on page 514) defaults to LOG_INFO. If this priority is inappropriate, you can change it with this -L switch. Just specify the new priority following the -L. The following, for example, sets the logging priority to LOG_CRIT:

Kname syslog -LLOG_CRIT

Note that omitting the leading four characters after the -L switch (the LOG_) but leaving the rest (the CRIT) will also work:

Kname syslog -LCRIT

If an unknown or unsupported priority is specified, the following error will be logged and printed:

syslog_map_parseargs: Unknown priority LOG_MAIL

Here, the syslog facility LOG_MAIL was wrongly used in place of a priority.

text

Look up in flat text files V8.7 and later

The text database-map type allows you to look up keys in flat text files. This technique is vastly less efficient than looking up keys in real databases, but it can serve as a way to test rules before implementing them in database form.

For the text database map, columns for the key and value are measured as an index. That is, the first column is number 0. To illustrate, consider the following mini configuration file that can be used to check spelling:

Kspell text /usr/dict/words
Spell
R$-      $: $(spell $1 $: not in dictionary $)

The /usr/dict/words file contains only a single column of words. This rule shows that the key is (by default) the first column (index 0). And the value is (by default) also the first column (index 0).

For more sophisticated applications you can specify the key’s column (with the -k switch), the value’s column (with the -v switch), and the column delimiter (with the -z switch). To illustrate, consider the need to look up a user-id in the /etc/passwd file and to return the login name of the user to whom it belongs:

Kgetuid text -k2 -v0 -z: /etc/passwd
R$-      $: $(getuid $1 $)

The lines of a password file look like this:

ftp:*:1092:255:File Transfer Protocol Program:/u/ftp:/bin/sh

The third column (where the columns are separated by colons) is the uid field. The first is the login name. Note that the -k and -v switches show these fields as indexes, where the first is 0 and the third is 2.

Note that if a file cannot be opened because it is unsafe (DontBlameSendmail on page 1009), the following warning will be logged and printed:

text map "name": unsafe map file filename

This message will not be printed if the -o switch is specified with the database-map declaration.

Only a handful of database switches are available with this text type. The are listed in Table 23-28.

Table 23-28. The text database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-f

-f on page 887

Perform a case-insensitive search.

-k

-k on page 888

Specify column for key or key name.

-m

-m on page 888

Suppress replacement on match.

-o

-o on page 889

This database map is optional.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

-v

-v on page 891

Specify the value’s column.

-z

-z on page 891

Specify the column delimiter.

The -d38.20 debugging command-line switch (-d38.20 on page 568) is available to watch this text type.

userdb

Look up in the User Database V8.7 and later

The User Database is a special database file that you create for use by sendmail. It causes sender and recipient addresses to be rewritten under control of an external database. Ordinarily, any local address is first looked up in the aliases database. If it is not found there, that user’s ~/.forward is next examined. If the User Database is enabled, the address is looked up in that database after aliasing and before forwarding, but only if the selected delivery agent has the F=@ flag set (F=@ on page 766).

In the sections that follow, we describe the use of this database in detail, but first we will note a few important points.

Although we illustrate here that a lookup can be done using a database file, a remote lookup can also be done via a User Database server, or via a network service. Those forms of lookup are described in UserDatabaseSpec on page 1116.

You can also look up addresses in the User Database with rule sets using this userdb database-map type. To do so, you declare it like this:

Kname userdb  switches  field

Here, the name is the name you will use in later rule sets. The field is either a literal maildrop or mailname (see Create the User Database on page 944). The possible switches are shown in Table 23-29.

Table 23-29. The userdb database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-f

-f on page 887

Perform a case-insensitive search.

-m

-m on page 888

Suppress replacement on match.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

One use for this database-map type might be to intercept each RCPT To: address, and log whether it will be transformed by the User Database:

Kudb userdb -f -a.FOUND maildrop
Klog syslog

SLocal_check_rcpt
R $*                  $: $>canonify $1
R $+ < @ $* > $*      $: $1<@$2>$3 $| $(udb $1 $)
R $* $| $* . FOUND    $: $(log $1 transformed by userdb into $2 $) $1

Here, we declare a userdb database-map type called udb. The -f says to look up addresses in a case-insensitive manner. The -a says to append a literal .FOUND to any match. Finally, the maildrop says to look up a recipient address with a :maildrop suffix attached.

We also declare a syslog database-map type (syslog on page 939) named log, which we will use to syslog the result.

The rule set in Local_check_rcpt (Local_check_rcpt and check_rcpt on page 257) contains three rules, and they are called just after each RCPT To: command. In the first rule, we make sure the address is focused. In the second rule, we first arrange to return the original address in the workspace (the $1<@$2>$3 in the RHS) and a $| separator. Then we perform the lookup and add that result to the workspace.

The third rule looks for a workspace that ends in a literal .FOUND and, if it finds such a workspace, logs the result. For a focused address such as gw<@wash.dc.gov>, the result might be:

gw<@wash.dc.gov> transformed by userdb into george@retired.wash.dc.gov

Enable the User Database

The User Database is automatically enabled when you compile sendmail if you include support for NEWDB or HESIOD (USERDB on page 150). To see whether a precompiled version of sendmail includes User Database support, run it with the -d0.1 switch:

% /usr/sbin/sendmail -d0.1 -bt < /dev/null
Version 8.12
 Compiled with: LOG MIME8TO7 NETINET NETUNIX NEWDB SCANF USERDB XDEBUG
                                                          ↑
                                                        note

If USERDB is listed, User Database support is included.

Next, you must declare the location of the database file with the UserDatabaseSpec option (UserDatabaseSpec on page 1116):

OU/etc/mail/userdb                           ← in your cf file (V8)
O UserDatabaseSpec=/etc/mail/userdb          ← in your cf file (V8.7 and later)
define(`confUSERDB_SPEC', /etc/mail/userdb)  ← in your mc file

Here, the location of the database file is set to be /etc/mail/userdb. You can also enable a default location for the database file that will take effect should the UserDatabaseSpec option be missing by defining that location with UDB_DEFAULT_SPEC when compiling (UDB_DEFAULT_SPEC on page 149).

Create the User Database

The User Database is a btree-type (btree on page 901) database file created from a source text file using the makemap program:

% makemap btree /etc/mail/userdb.db < /etc/mail/userdbthis type is mandatory for the User Database

Here, /etc/mail/userdb is the source-text file that is input, and /etc/mail/userdb.db is the database we are creating (the one defined by the UserDatabaseSpec option in the previous section).[355]

The source text file is composed of key and value pairs, one pair per line:

key    valuewhitespace

The key is a user’s login name, a colon, and one of two possible keywords: maildrop or mailname. The keyword determines the nature of the value:

maildrop

For maildrop, the value is the official delivery address for this user. If there are multiple official addresses, they can be listed as a single compound value, with separating commas. For example:

root:maildrop         sysadmin@here.us.edu,bill@there.us.edu

Or they can be listed on individual lines:

root:maildrop         sysadmin@here.us.edu
root:maildrop         bill@there.us.edu

This latter form requires you to use the -d command-line switch with the makemap(1) program (-d on page 372) when creating the database, but it has the advantage of being a simpler source file to manage.

mailname

The mailname keyword causes a “reverse alias” transformation. That is, it causes the login name in the key to be changed into the address in the value for outgoing mail. For example:

bob:mailname          Bob.Roberts@Here.US.EDU

This causes mail sent by bob to go out addressed as though it is from Bob.Roberts@Here.US.EDU.[356] This transformation occurs in the header and envelope. But note that the sender envelope is not rewritten by UDB unless the F=i flag (F=i on page 772) is present in the delivery agent that is selected for the sender. Also note that the recipient headers are not rewritten by UDB unless the F=j flag (F=j on page 773) is set for the delivery agent that was selected for the recipient.

Naturally, the maildrop and mailname keywords should occur in pairs. Each outgoing address that is created with mailname should have a corresponding maildrop entry so that return mail can be delivered. In the previous example, a reasonable pair might look like this:

bob:mailname          Bob.Roberts@Here.US.EDU
Bob.Roberts:maildrop  bob

Here, outgoing mail from the user named bob will be addressed as though it is from Bob.Roberts@Here.US.EDU. Incoming mail (whether it is original or in reply to the outgoing mail) will be addressed as though it is to the name Bob.Roberts, which will be transformed into and delivered to the local user bob.

A :default outgoing hostname

The mailname keyword allows the host part of outgoing addresses to mask the real hostname of the originating machine. This property can, for example, be used to convert the hostname into a firewall name:

bob:mailname          bob@Firewall.US.EDU

Here, the canonical name of bob’s machine is Here.US.EDU. The mailname keyword causes outgoing mail from bob to appear as though it is from the firewall machine (Firewall.US.EDU) instead.

Ordinarily, this transformation is not automatic. Each username that is to appear to be from the firewall machine will need an entry such as that in the User Database (see earlier example). To automate this process, you can use the special username :default in a mailname declaration:

:default:mailname     Firewall.US.EDU

If a maildrop entry is found for a particular name, but no corresponding mailname record is found, the outgoing address is ordinarily unchanged. If, however, a default hostname has been defined with :default, that hostname replaces the local hostname for all addresses that lack their own mailname entry:

:default:mailname     Firewall.US.EDU
bob:maildrop          bob@here.us.edu

In this example, the user bob has a maildrop entry but lacks a mailname entry. Outgoing mail from this user will have the :default hostname used instead of the local hostname. The user sally, on the other hand, has neither a maildrop entry nor a mailname entry and so will not have her outgoing address rewritten.

user

Look up local passwd information V8.7 and later

The user type is used to look up passwd(5) information using the method defined by the MailboxDatabase option (MailboxDatabase on page 1042). A password entry typically looks like this:

ftp:*:1092:255:File Transfer Protocol Program:/u/ftp:/bin/sh

Here, there are seven fields, each separated from the others by colon characters. The key is always compared to the first field. The value returned is (by default) the first field unless you specify another field with a -v switch:

Kname user -vfield

Here, field can be either a number 1 through 7, or one of the names name, passwd, uid, gid, gecos, dir, or shell, which correspond to the numbers. For example, to look up usernames and get the full name (GECOS) field returned, you could use something such as this:

Kgetgecos user -vgecos
...
R$-        $: $( getgecos $1 $)

Note that this returns the full GECOS field in its rawest form. It is not cleaned up to provide a reliable full name, as is the $x macro ($x on page 851).

The user database-map type can be used in conjunction with the Local_check_rcpt rule set (Local_check_rcpt and check_rcpt on page 257). In the following, for example, we check to see whether a recipient is a local user and, if so, reject the user if that user’s home directory is /home/retired/tars:

Kislocal user -vdir

SLocal_check_rcpt
R$*                        $: $>canonify $1        focus on host
R$* <@ $+ > $*             $: $1                   discard host
R$+                        $: $1 $(islocal $1 $)
R$- /home/retired/tars     $#error $@ 5.1.3 $: 553 Sorry, $1 is retired, no forwarding

Here, we focus on the host part with the canonify rule set 3, and then discard all but the user part in the second rule. The third rule performs the lookup. If the user is not found, that username is returned unchanged. If, on the other hand, the user is found, that user’s name and home directory are placed into the workspace. The last rule rejects any SMTP RCPT command that contains a local-user part whose home directory is /home/retired/tars.

Only a few database switches are useful with this user type. All are listed in Table 23-30.

Table 23-30. The user database-map type K command switches

Switch

§

Description

-a

-a on page 887

Append tag on successful match.

-D

-D on page 887

Don’t use this database map if DeliveryMode=defer.

-m

-m on page 888

Suppress replacement on match.

-q

-q on page 889

Don’t strip quotes from key.

-S

-S on page 890

Space replacement character.

-T

-T on page 890

Suffix to append on temporary failure.

-t

-t on page 891

Ignore temporary errors.

-v

-v on page 891

Specify the column to return.

This user database-map type can be watched with the -d38.20 debugging command-line switch (-d38.20 on page 568).



[337] * This simplified example won’t work if the /etc/hosts file has multiple hostnames on the righthand side. For more complicated situations such as this, a shell script might be required.

[338] * The sendmail source calls this class, but we chose type to make it clear that this is different from class macros.

[339] * This is true as of V8.12. Future versions might change the semantics of the K line such that switches can follow.

[340] * Note that the -m switch (-m on page 888) prevents the found value from replacing the $( and $) enclosed expression.

[341] * Note that this substitution technique does not work for most internal database-map types. For example, it does not work with arith or dequote, but it does work with regex.

[342] * This happens only for V2 and higher configuration files. Below that level, the dot is not appended unless it is specifically added by the -a of the K command.

[343] * However, space is reclaimed in the file for future use.

[344] * We are clutching at straws here for an example. Note that sendmail already does all this, including looking up more than just the first MX record.

[345] * We are clutching at straws here for an example. Note that sendmail already does all this for you and puts the result in the ${client_resolve} macro (${client_resolve} on page 814).

[346] * If you need to find the lowest-cost (or other preference) MX record, or multiple MX records, use the bestmx database map instead (bestmx on page 902).

[347] * Under LDAP, binding to the server is sort of like “logging in” to a Unix machine.

[348] * Note, however, that if you wish to use Unix domain sockets, your underlying LDAP library must support Unix-domain sockets.

[349] * Note that under NIS+, names cannot contain a dot, whereas under NIS they can—for example, mail_aliases for NIS+ but mail.aliases for NIS.

[350] * This used to be the -v switch, but -v has been deprecated in this role.

[351] Note that the spacedname field name is no longer understood by ph.

[352] * The sendmail program needs to be built with SOCKETMAP defined (SOCKETMAP on page 145) in order to use this new database-map type. NETUNIX is required to use Unix-domain sockets but is generally defined by default.

[353] * As such, it is somewhat misnamed. One might reasonably expect a type named stab to provide access to the symbol table, but alas, this is not so.

[354] * Your switch database-map declaration references the new database maps named ali.files and ali.nis. These must be declared before the switch database map is declared. Note that switch database-map declarations always reference other database-map names!

[355] * The .db is added automatically if it is missing. We include it here for clarity.

[356] Using full names in outgoing mail is probably not a good idea. Unlike login names, full names are not guaranteed to be unique. If current users expect to be able to receive mail under full names, future users with the same full name might be out of luck. Always weigh convenience against maintainable uniqueness when designing your mail setup.