home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


sendmailSearch this book
Previous: 19.6 Alphabetized m4 Macros Chapter 20 Next: 20.2 The Cookbook

20. The checkcompat() Cookbook

Inside sendmail is the often overlooked checkcompat () routine. It has existed since Version 3 and is intended to allow the site administrator to accept, reject, and log mail delivery attempts. Among its uses, and those we will illustrate, are the following:

  • Refuse to act as a mail gateway. Reject any mail that is both from and to any outside address.

  • Limit the size of guest messages. Screen messages based on user uid , and reject those for the class of users who are guests when the message is over a specified limit in bytes.

  • Verify identd (8) information. Compare the host information in $s to that in $_ and log a warning if they differ.

  • Prune Received: headers at a firewall. Make all outgoing mail appear fresh as it exits the internal network via a firewall.

  • Reject mail from spamming or mail-bombing sites. Look up the sender in an external database and bounce the message if the sender is listed as "bad."

The checkcompat () routine is located in the file src/conf.c . That file contains comments describing one way to code checkcompat (). In this chapter we show you other ways to code it.

The checkcompat () routine is inherently "internal," meaning that it must understand internal data structures that may change. [1] Since you are modifying source code, you have to be prepared to read source code. In this chapter we offer examples of ways to use checkcompat (). Be aware that they are examples only, and you will need C programming skill to extend them to real-world situations.

[1] V8.8 sendmail also offers a check_compat rule set (see Section 29.10.4, "The check_compat Rule Set" ) that can perform some of the checkcompat () routine's functionality at the rule set level. This is one way to avoid having to program in the C language.

The checkcompat () routine is called for every delivery attempt to every recipient. When designing a checkcompat () routine of your own, be aware that a cascade of errors may propagate if you are not careful with your design. Logging a warning based on the sender, for example, may result in multiple warnings when there are multiple recipients.

20.1 How checkcompat() Works

When sendmail prepares to deliver mail, it first checks the size of the mail message and rejects (bounces) it if it is larger than the limit imposed by the M= delivery agent equate (see Section 30.4.7, M= ). V8.8 sendmail then calls the check_compat rule set (see Section 29.10.4 ). Next, all versions of sendmail call the checkcompat () routine.

The checkcompat () routine lies in a unique position within the sendmail code. It is the one place where both the sender and recipient addresses are available at the same time. Since it precedes actual delivery, all the information needed for delivery is available to you for checking.

If checkcompat () returns EX_OK, as defined in <sysexits.h> , the mail message is considered okay and delivered. Otherwise, the message is bounced. If you wish the message to be requeued instead of bounced, you can return EX_TEMPFAIL.

Again note that the checkcompat () routine is called once for each recipient.

20.1.1 Arguments Passed to checkcompat()

The checkcompat () is found in the C language source file src/conf.c . Inside that file you will find it declared like this:

checkcompat(to, e)
        register ADDRESS *to;
        register ENVELOPE *e;

Here, to is a pointer to a structure of typedef ADDRESS that contains information about the recipient. And e is a pointer to a structure (actually a linked list of structures) of typedef ENVELOPE that contains information about the current envelope.

The members of the ADDRESS *to structure are shown in Table 20.1 . Note that these members are correct for V8.8 sendmail only. Also note that the table shows only those members that may be useful in a checkcompat () routine (see sendmail.h for the other members of *to ).

Table 20.1: ADDRESS *to Members
Type Member Description
char * q_paddr The address in a form suitable for printing
char * q_user The user part ( $: ) from rule set 0 (see Section 29.6, "Rule Set 0" )
char * q_ruser The login name for this user, if known
char * q_host The host part ( $@ ) from rule set 0 (see Section 29.6 )
struct mailer * q_mailer The delivery agent ( $# ) from rule set 0 (see Section 29.6 )
u_long q_flags Status flags (see Section 37.3.1, "The Output Produced by printaddr()" in Section 37.3.1 )
uid_t q_uid The uid of the q_ruser , if known
gid_t q_gid The gid of the q_ruser , if known
char * q_home The home directory (path), if delivery is local
char * q_fullname The (gecos) full name of q_ruser , if known
struct address * q_next Link to the next ADDRESS in the chain
struct address * q_alias The alias that yielded this address
char * q_owner The owner of q_alias

The members of the ENVELOPE *e structure are shown in Table 20.2 . Note that these members are correct for V8.8 sendmail only. Also note that the table shows only those members that may be useful in a checkcompat () routine (see sendmail.h for other members of *e ).

Table 20.2: ENVELOPE *e Members
Type Member Description
HDR * e_header Linked list of headers
time_t e_ctime Time message first placed into the queue
ADDRESS e_from The sender
ADDRESS * e_sendqueue Linked list of recipients
long e_msgsize Size of the message in bytes
long e_flags Envelope flags (see Table 37.3 in Section 37.5.12, -d2.1 )
int e_nrcpts Number of recipients
short e_hopcount The hop count for the message

The checkcompat () routine is a powerful internal hook inside sendmail . It is so internal and powerful, in fact, that if you are truly clever, you can even use checkcompat () to modify rewrite rules at runtime (scary but possible).

20.1.2 Global Variables

Over 100 global variables are used by V8.8 sendmail . They are all listed in sendmail.h and conf.c with "lite" comments. Global variables store information such as sendmail 's option values, file descriptor values, macro values, class lists, and database access information. Any can be modified inside checkcompat (), but before attempting to do so, study the sendmail C source code to anticipate any unexpected side effects.

In general, you can use almost any of the global variables when designing your own checkcompat () routine. The four most interesting are the following:


The IP address of the sending host. This is a union of several sockaddr_ types depending on your selection of protocol type. This can be zero for locally submitted mail.


A string containing the definitive canonical name of the sending host. If it can't be resolved to a name, it will contain the host's IP number in text form, surrounded by square brackets.


This variable determines the amount of logging that sendmail does. It is initially set with the LogLevel ( L ) option (see Section 34.8.33, LogLevel (L) ). You might want to use checkcompat () to detect questionable connections and, if any are detected, to increase the value in LogLevel to 12. This will cause both sides of every subsequent SMTP connection to be logged.


Whether or not unmatched local looking names are looked up in the passwd (5) file is under the control of the MatchGECOS ( G ) option (see Section 34.8.34, MatchGECOS (G) ). Because this kind of lookup is expensive, you might wish to enable it only during nonbusiness hours. One way to do this would be by modifying the MatchGecos variable inside checkcompat ().