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

20. The checkcompat() Cookbook

How checkcompat() Works
The Cookbook
Alphabetized V8.8 Subroutines

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:

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
char *q_paddrThe address in a form suitable for printing
char *q_userThe user part ($:) from rule set 0 (see Section 29.6, "Rule Set 0")
char *q_ruserThe login name for this user, if known
char *q_hostThe host part ($@) from rule set 0 (see Section 29.6)
struct mailer *q_mailerThe delivery agent ($#) from rule set 0 (see Section 29.6)
u_longq_flagsStatus flags (see Section 37.3.1, "The Output Produced by printaddr()" in Section 37.3.1)
uid_tq_uidThe uid of the q_ruser, if known
gid_tq_gidThe gid of the q_ruser, if known
char *q_homeThe home directory (path), if delivery is local
char *q_fullnameThe (gecos) full name of q_ruser, if known
struct address *q_nextLink to the next ADDRESS in the chain
struct address *q_aliasThe alias that yielded this address
char *q_ownerThe 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
HDR *e_headerLinked list of headers
time_te_ctimeTime message first placed into the queue
ADDRESSe_fromThe sender
ADDRESS *e_sendqueueLinked list of recipients
longe_msgsizeSize of the message in bytes
longe_flagsEnvelope flags (see Table 37.3 in Section 37.5.12, -d2.1)
inte_nrcptsNumber of recipients
shorte_hopcountThe 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().

Previous: 19.6 Alphabetized m4 MacrossendmailNext: 20.2 The Cookbook
19.6 Alphabetized m4 MacrosBook Index20.2 The Cookbook