sendmail provides powerful tools for configuration testing and debugging. These test tools are invoked on the sendmail command line using some of the many sendmail command-line arguments. Appendix E lists all of the command-line arguments; Table 10.6 summarizes those that relate to testing and debugging.
|-t||Send to everyone listed in To:, Cc:, and Bcc:.|
|-bt||Run in test mode.|
|-bv||Verify addresses; don't collect or deliver mail.|
|-bp||Print the mail queue.|
|-d||Set debugging level.|
|-e||Defines how errors are returned.|
|-v||Run in verbose mode.|
Some command-line arguments are used to verify address processing and to gain confidence in the new configuration. Once you think your configuration will work, choose friends at various sites and send them mail. Use the -C argument to read the test configuration file and the -v argument to display the details of the mail delivery. -v displays the complete SMTP exchange between the two hosts.
By observing if your mailer properly connects to the remote mailer and formats the addresses correctly, you'll get a good idea of how the configuration is working. The following example is a test from peanut using the test.cf configuration file we just created:
/usr/lib/sendmail -Ctest.cf -t -vTo: email@example.com From: craig Subject: Sendmail Test Ignore this test. ^D firstname.lastname@example.org... Connecting to ora.com. via smtp... 220-ruby.ora.com Sendmail 8.6.13/8.6.11 ready at Sat, 16 Nov 1996 220 ESMTP spoken here >>> EHLO peanut.nuts.com 250-ruby.ora.com Hello email@example.com [172.16.12.2], pleased to meet you 250-EXPN 250-SIZE 250 HELP >>> MAIL From:<firstname.lastname@example.org> SIZE=64 250 <email@example.com>... Sender ok >>> RCPT To:<firstname.lastname@example.org> 250 <email@example.com>... Recipient ok >>> DATA 354 Enter mail, end with "." on a line by itself >>> . 250 SAA27399 Message accepted for delivery firstname.lastname@example.org... Sent (SAA27399 Message accepted for delivery) Closing connection to ora.com. >>> QUIT 221 ruby.ora.com closing connection
We entered everything before the CTRL-D (^D). Everything after the ^D was displayed by sendmail. Figure 10.5 highlights some of the important information in this display, and notes the sendmail macros that relate to the highlighted material.
This test successfully transfers mail to a remote Internet site. The sendmail output shows that peanut sent the mail to ora.com via the smtp mail delivery program. The sendmail greeting shows that the remote host handling this SMTP connection is ruby.ora.com. Therefore, ruby must be the mail server for the ora.com domain; i.e., the MX record for ora.com points to ruby.ora.com.
Everything worked just fine! We could quit right now and use this configuration. But like most computer people, we cannot stop ourselves from tinkering in order to make things "better."
The From: address, email@example.com, is clearly a valid address but it is not quite what we want. What we want is to have people address us as firstname.lastname@domain - not as firstname.lastname@example.org, which is exactly the configuration we created earlier in this chapter with a few lines of m4 code. We will create the same configuration here to provide an example of how to use the various troubleshooting tools that come with sendmail. However, if you really want to make major sendmail configuration changes, you should use m4 to build your configuration.
Most changes to sendmail.cf are small and are made near the beginning of the file in the Local Information section. Looking closely at that section provides the clues we need to solve part of our configuration problem.
Without knowing what "masquerading" means, the comments for class E, class M, and macro M lead us to guess that the value set for macro M will be used to rewrite the hostname.  In particular, the comment "names that should be exposed as from this host, even if we masquerade" led me to believe that masquerading hides the hostname. Based on this guess, we set a value for macro M as follows:
 In the m4 source file we configured masquerading with the MASQUERADE_AS(nuts.com) command.
# who I masquerade as (null for no masquerading) (see also $=M) DMnuts.com
Are we sure that setting a value for the M macro will hide the hostname? No, but changing the value in test.cf and running another test will do no harm. Running the test program with the test configuration has no affect on the running sendmail daemon that was started by the sendmail -bd -q1h command in the boot script. Only an instantiation of sendmail with the -Ctest.cf argument will use the test.cf test configuration.
In the initial test, the From: address went into sendmail as craig, and it came out as email@example.com. Obviously it has been rewritten. This time we test whether the change we made to the macro M in the configuration files modifies the rewrite process by directly testing the rewrite rulesets. First, we need to find out what rules were used to rewrite this address. To get more information, we run sendmail with the -bt option.
When sendmail is invoked with the -bt option, it prompts for input using the greater than symbol (>). At the prompt, enter one of the test commands shown in Table 10.7
|=S||Display the rules in |
|=M||Display the mailer definitions.|
|-d||Set the debug flag to |
|$||Display the value of macro |
|$=||Display the contents of class |
|/mx ||Display the MX records for |
|/parse ||Return the mailer/host/user triple for |
|/try ||Process |
Set the address processed by /parse or /try to H (Header), E (Envelope), S (Sender), or R (Recipient).
|/canon ||Canonify |
|/map ||Display the value for |
The most basic test is a ruleset number followed by an email address. The address is the test data, and the ruleset number is the ruleset to be tested. The address is easy to select; it is the one that was improperly rewritten. But how do you know which ruleset to specify?
Use Figure 10.4 to determine which rulesets to enter. Ruleset 3 is applied to all addresses. It is followed by different rulesets depending on whether the address is a delivery address, a sender address, or a recipient address. Furthermore, the rulesets used for sender and recipient addresses vary depending on the mailer that is used to deliver the mail. All addresses are then processed by ruleset 4.
There are two variables in determining the rulesets used to process an address: the type of address and the mailer through which it is processed. The three address types are delivery address, recipient address, and sender address. You know the address type because you select the address being tested. In our test mail we were concerned about the sender address. Which mailer is used is determined by the delivery address. To find out which mailer delivered the test mail, run sendmail with the -bv argument and the delivery address:
sendmail -bv firstname.lastname@example.org@ora.com... deliverable: mailer smtp, host ora.com., user email@example.com
Knowing the mailer, we can use sendmail with the -bt
option to process the sender
From: address. There are two types of
sender addresses: the sender address in the "envelope" and the sender
address in the message header. The message header address is the one
From: line sent with the message during the SMTP DATA transfer.
You probably see it in the mail headers when you view the message with
your mail reader. The "envelope" address is the address used during
the SMTP protocol interactions. sendmail allows us to view the
processing of both of these addresses:
/usr/lib/sendmail -bt -Ctest.cfADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >
/try smtp craigTrying header sender address craig for mailer smtp rewrite: ruleset 3 input: craig rewrite: ruleset 96 input: craig rewrite: ruleset 96 returns: craig rewrite: ruleset 3 returns: craig rewrite: ruleset 1 input: craig rewrite: ruleset 1 returns: craig rewrite: ruleset 31 input: craig rewrite: ruleset 51 input: craig rewrite: ruleset 51 returns: craig rewrite: ruleset 61 input: craig rewrite: ruleset 61 returns: craig < @ *LOCAL* > rewrite: ruleset 93 input: craig < @ *LOCAL* > rewrite: ruleset 93 returns: craig < @ nuts . com . > rewrite: ruleset 31 returns: craig < @ nuts . com . > rewrite: ruleset 4 input: craig < @ nuts . com . > rewrite: ruleset 4 returns: craig @ nuts . com Rcode = 0, addr = firstname.lastname@example.org >
/try smtp craigTrying envelope sender address craig for mailer smtp rewrite: ruleset 3 input: craig rewrite: ruleset 96 input: craig rewrite: ruleset 96 returns: craig rewrite: ruleset 3 returns: craig rewrite: ruleset 1 input: craig rewrite: ruleset 1 returns: craig rewrite: ruleset 11 input: craig rewrite: ruleset 51 input: craig rewrite: ruleset 51 returns: craig rewrite: ruleset 61 input: craig rewrite: ruleset 61 returns: craig < @ *LOCAL* > rewrite: ruleset 94 input: craig < @ *LOCAL* > rewrite: ruleset 94 returns: craig < @ peanut . nuts . com . > rewrite: ruleset 11 returns: craig < @ peanut . nuts . com . > rewrite: ruleset 4 input: craig < @ peanut . nuts . com . > rewrite: ruleset 4 returns: craig @ peanut . nuts . com Rcode = 0, addr = email@example.com >
The /tryflags command defines the type of address to be processed by a /try or a /parse command. Four flags are available for the /tryflags command: S for sender, R for recipient, H for header, and E for envelope. By combining two of these flags, the first /tryflags command says we will process a header sender (HS) address. The /try command tells sendmail to process the address through a specific mailer. In the example, we process the email address craig through the mailer smtp. First, we process it as the header sender address, and then as the envelope sender address. From this test, we can tell that the value that we entered in the M macro is used to rewrite the sender address in the message header but it is not used to rewrite the sender address in the envelope.
Unfortunately, older versions of sendmail, such as the version that comes
with Solaris 2.5.1, don't support /try and /tryflags. Testing
these older systems requires a little more effort. Knowing the mailer is
still the key to determining the rulesets called to process the sender
From: address. A grep of the test.cf file displays the
rulesets that the smtp mailer uses for sender addresses.
grep ^Msmtp /etc/sendmail.cfMsmtp, P=[IPC], F=mDFMuX, S=11/31, R=21, E=\r\n, L=990, Msmtp8, P=[IPC], F=mDFMuX8, S=11/31, R=21, E=\r\n, L=990,
Again, refer to Figure 10.4 It shows that the sender address goes through ruleset 3, ruleset 1, the ruleset specified by S, and ruleset 4. The mailer definition for smtp in our sample configuration defines two rulesets for S - 11 and 31.  The first ruleset is used for rewriting the sender address in the "envelope" and the second is used to rewrite the sender address in the message header.
 Many versions of sendmail define only one ruleset each for S and R.
Based on the information in Figure 10.4 and in the S field of the smtp mailer, we know that the rulesets that process the message header sender address are 3, 1, 31 and 4. So we run sendmail with the -bt option and enter 3,1,31,4 craig at the command prompt. This command processes the sender address through each of these rulesets in succession. We also know that the envelope sender address is processed by rulesets 3, 1, 11, and 4. To test that, we enter 3,1,11,4 craig.
The results of these tests are exactly the same as those shown in the example above. The value of the M macro rewrites the hostname in the message sender address just as we wanted. The hostname in the envelope sender address is not rewritten. Usually this is acceptable. However, we want to create exactly the same configuration as in the m4 example. The FEATURE(masquerade_envelope) command used in the m4 example causes the envelope sender address to be rewritten. Therefore, we want this configuration to also rewrite it.
The only difference between how the message and envelope addresses are processed is that one goes through ruleset 31 and the other goes through ruleset 11. The tests show that both rulesets call ruleset 51 and then ruleset 61. They diverge at that point because ruleset 31 calls ruleset 93 and ruleset 11 calls ruleset 94. The tests also show that ruleset 93 provides the address rewrite that we want for the message sender address, while the envelope sender address is not processed in the manner we desire by ruleset 94. The test.cf code for rulesets 94, 11, and 31 is shown below:
################################################################### ### Ruleset 94 -- convert envelope names to masquerade form ### ################################################################### S94 #R$+ $@ $>93 $1 R$* < @ *LOCAL* > $* $: $1 < @ $j . > $2 # # envelope sender rewriting # S11 R$+ $: $>51 $1 sender/recipient common R$* :; <@> $@ list:; special case R$* $: $>61 $1 qualify unqual'ed names R$+ $: $>94 $1 do masquerading # # header sender and masquerading header recipient rewriting # S31 R$+ $: $>51 $1 sender/recipient common R:; <@> $@ list:; special case R$* <@> $* $@ $1 <@> $2 pass null host through R< @ $* > $* $@ < @ $1 > $2 pass route-addr through R$* $: $>61 $1 qualify unqual'ed names R$+ $: $>93 $1 do masquerading
Clearly, ruleset 94 does not do what we want and ruleset 93 does. A quick inspection of ruleset 94 shows that it does not contain a single reference to macro M. Yet the comment on the line in ruleset 11 that calls it indicates that ruleset 94 should "do masquerading." The first line of ruleset 94 calls ruleset 93, but it is commented out. Our solution is to uncomment the first line of ruleset 94 so that it now calls ruleset 93, which is the ruleset that really does the masquerade processing.
Debugging a sendmail.cf file is more of an art than a science. Deciding to edit the first line of ruleset 94 to call ruleset 93 is little more than a hunch. The only way to verify the hunch is through testing. We run sendmail -bt -Ctest.cf again to test the addresses craig, craig@peanut, and craig@localhost through rulesets 3, 1, 11, and 4. All tests run successfully, rewriting the various input addresses into firstname.lastname@example.org. We then retest by sending mail via sendmail -v -t -Ctest.cf. Only when all of these tests run successfully do we really believe in our hunch and move on to the next task, which is to rewrite the user part of the email address into the user's first and last names.
The last feature we added to the m4 source file was FEATURE(genericstable), which adds a database process to the configuration that we use to convert the user portion of the email address from the user's login name to the user's first and last names. To do the same thing here, create a text file of login names and first and last names and build a database with makemap. 
 See the m4 section for more information about makemap.
# cat realnames dan Dan.Scribner tyler Tyler.McCafferty pat Pat.Stover willy Bill.Wright craig Craig.Hunt # makemap dbm realnames < realnames
Once the database is created, define it for sendmail. Use the K command to do this. To use the database that we have just built, insert the following lines into the Local Information section of the sendmail.cf file:
# define a database to map login names to firstname.lastname Krealnames dbm /etc/realnames
The K command defines realnames as the internal sendmail name of this database. Further, it identifies that this is a database of type dbm and that the path to the database is /etc/realnames. sendmail adds the correct filename extensions to the pathname depending on the type of the database, so you don't need to worry about it.
Finally, we add a new rule that uses the database to rewrite addresses. We add it to ruleset 11 and ruleset 31 immediately after the lines in those rulesets that call ruleset 93. This way, our new rule gets the address as soon as ruleset 93 finishes processing it.
# when masquerading convert login name to firstname.lastname R$-<@$M.>$* $:$(realnames $1 $)<@$2.>$3 user=>first.last
This rule is designed to process the output of ruleset 93, which rewrites
the hostname portion of the address. Addresses that meet the criteria
to have the hostname part rewritten are also the addresses for which we
want to rewrite the user part. Look at the output of ruleset 93 from
the earlier test. That address, craig<@nuts.com.>, matches the
$-<@$M.>$*. The address has exactly one token (craig)
before the literal <@, followed by the value of M (nuts.com),
the literal .> and zero tokens.
The transformation part of this rule takes the first token ($1) from the
input address and uses it as the key to the realnames database,
as indicated by the
$:$(realnames $1 $) syntax. For the sample address
craig<@nuts.com>, $1 is craig. When used as an index into
the database realnames shown at the beginning of this section,
it returns Craig.Hunt. This returned value is prepended to the
literal <@, the value of indefinite token $2, the literal .>, and the
value of $3, as indicated by the
<@$2.>$3 part of the transformation.
The effect of this new rule is to convert the username to the user's
real first and last names.
After adding the new rule to rulesets 11 and 31, a test yields the following results:
sendmail -bt -Ctest.cfADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> >
3,1,11,4 craigrewrite: ruleset 3 input: craig rewrite: ruleset 96 input: craig rewrite: ruleset 96 returns: craig rewrite: ruleset 3 returns: craig rewrite: ruleset 1 input: craig rewrite: ruleset 1 returns: craig rewrite: ruleset 11 input: craig rewrite: ruleset 51 input: craig rewrite: ruleset 51 returns: craig rewrite: ruleset 61 input: craig rewrite: ruleset 61 returns: craig < @ *LOCAL* > rewrite: ruleset 93 input: craig < @ *LOCAL* > rewrite: ruleset 93 returns: craig < @ nuts . com . > rewrite: ruleset 11 returns: Craig . Hunt < @ nuts . com . > rewrite: ruleset 4 input: Craig . Hunt < @ nuts . com . > rewrite: ruleset 4 returns: Craig . Hunt @ nuts . com >
3,1,31,4 craigrewrite: ruleset 3 input: craig rewrite: ruleset 96 input: craig rewrite: ruleset 96 returns: craig rewrite: ruleset 3 returns: craig rewrite: ruleset 1 input: craig rewrite: ruleset 1 returns: craig rewrite: ruleset 31 input: craig rewrite: ruleset 51 input: craig rewrite: ruleset 51 returns: craig rewrite: ruleset 61 input: craig rewrite: ruleset 61 returns: craig < @ *LOCAL* > rewrite: ruleset 93 input: craig < @ *LOCAL* > rewrite: ruleset 93 returns: craig < @ nuts . com . > rewrite: ruleset 31 returns: Craig . Hunt < @ nuts . com . > rewrite: ruleset 4 input: Craig . Hunt < @ nuts . com . > rewrite: ruleset 4 returns: Craig . Hunt @ nuts . com >
If the tests do not give the results you want, make sure that you have correctly entered the new rewrite rules and that you have correctly built the database. If sendmail complains that it can't lock the database file, you need to download a more recent release of sendmail V8. The following error message could also be displayed:
test.cf: line 116: readcf: map realnames: class dbm not available
This indicates that your system does not support dbm databases. Change the database type on the K command line to hash and rerun sendmail -bt. If it complains again, try it with btree. When you find a type of database that your sendmail likes, rerun makemap using that database type. If your sendmail doesn't support any database type, see Appendix E for information on re-compiling sendmail with database support.
Note that all of the changes made directly to the sendmail.cf file in the second half of this chapter (masquerading the sender address, masquerading the envelope address and converting usernames) were handled by just three lines in the m4 source file. These examples were used to demonstrate how to use the sendmail test tools. If you really need to make a new, custom configuration, use m4. It is easiest to maintain and enhance the sendmail configuration through the m4 source file.