Inside sendmail is the often-overlooked checkcompat( ) routine. It has existed since V3, and is intended to allow the site administrator to accept, reject, and log mail delivery attempts. As sendmail continues to evolve, the need for this checkcompat( ) routine diminishes. It is no longer, for example, needed to screen for spam rejection because much of that can now be done in rule sets and the access database. On modern machines that support POSIX threads, the Milter API allows external programs to perform all the tasks that formerly could be handled only by the checkcompat( ) routine.
But the checkcompat( ) routine still has a number of uses. Here are a few:
Capture the message body for each outbound message and send it via TCP/IP to a central archive host. Be sure to detect multiple recipients to avoid duplicate archived messages.[455]
Check the Received:
headers on messages sent from one of your MX servers
to see who sent it. This allows you to reject spam
messages that try to do an end run around your
access database. Sort the
Received:
headers by date and examine the second most
recent.
Monitor a port for incoming commands, or a database of times. You might use this to defer delivery for particular recipients during selected windows of time.
Check for a particular header that indicates a copy of the message should be archived. You might use this to add a recipient (if not already present) that results in archival of the message (such as archiver@archive.host).
Note that because the checkcompat( ) routine is called for every delivery attempt a cascade of errors can propagate if you are not careful with your design. Logging a warning based on the sender, for example, can result in multiple warnings when there are multiple recipients.
Finally, note that V8.8 and above sendmail also
offer a check_compat rule set
(see The check_compat Rule Set on page
259) 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.
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 (M= on page 746). V8.8 and
above sendmail then call the check_compat rule set
(The check_compat Rule Set on
page 259). After that, 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 the already aliased recipient addresses are available at the same time. Because it is invoked immediately before 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 OK 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 already aliased recipient.
The checkcompat( ) is found in the C-language source file sendmail/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 which
contains information about the recipient. And
e is a pointer
to a structure of typedef
ENVELOPE which contains information about the
current envelope. (Actually, both are linked lists
of structures.)
The members of the ADDRESS
*to structure are shown in Table C-1. Note that
these members are correct for V8.14
sendmail only. Also note that
the table shows only those members that can be
useful in a checkcompat( )
routine (see sendmail.h for the
other members of *to).
|
Member |
Description | |
|
|
|
The alias that yielded this address |
|
|
|
This is a |
|
|
|
Address flags |
|
|
|
The (GECOS) full name of |
|
|
|
The gid of the |
|
|
|
The home directory (path), if |
|
|
|
The host part ( |
|
|
|
The delivery agent ( |
|
|
|
Message regarding address (not always an error) |
|
|
|
Link to the next ADDRESS in the chain |
|
|
|
The ORCPT parameter from RCPT TO: line was set |
|
|
|
The owner of |
|
|
|
The address in a form suitable for printing |
|
|
|
Queue directory inside group |
|
|
|
Index into queue groups |
|
|
|
The login name for this user, if known |
|
|
|
The date of the status change |
|
|
|
The state of the address |
|
|
|
Which MTA generated |
|
|
|
The uid of the |
|
|
|
The user part ( |
The members of the ENVELOPE
*e structure are shown in Table C-2. Note that
these members are correct for V8.14
sendmail only. Also note that
the table shows only those members that can be
useful in a checkcompat( )
routine (see sendmail.h for
other members of *e).
|
Member |
Description | |
|
|
|
The parameters set by AUTH= |
|
|
|
The type of message body |
|
|
|
The message class ( |
|
|
|
The time this message was accepted |
|
|
|
The DELIVERYBY BY= interval |
|
|
|
The DELIVERYBY BY= flags |
|
|
|
The datafile |
|
|
|
The datafile’s queue group index |
|
|
|
The datafile’s queue directory index |
|
|
|
The time of the last delivery attempt |
|
|
|
Envelope ID from MAIL FROM: |
|
|
|
The error return mode |
|
|
|
The queue for error responses |
|
|
|
Envelope flags |
|
|
|
The sender address structure |
|
|
|
The domain part of the sender |
|
|
|
Linked list of headers |
|
|
|
The hop count for the message |
|
|
|
The ID for this entry |
|
|
|
The error message |
|
|
|
The message ID (for logging) |
|
|
|
The adjusted priority of this message |
|
|
|
The size of the message in bytes |
|
|
|
The number of recipients |
|
|
|
The number of delivery attempts |
|
|
|
The queue group (index into queues) |
|
|
|
The index into queue directories |
|
|
|
Sender address with comments stripped |
|
|
|
Linked list of recipients |
|
|
|
The status message (changes per delivery) |
|
|
|
The DSN status for this message |
|
|
|
The message timeout class |
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).
V8.14 sendmail uses more than 100 variables. 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 five most interesting are:
RealHostAddrThe IP address of the sending host. This is a union of several sockaddr_ types depending on your selection of protocol types. This can be zero for locally submitted mail.
RealHostNameA 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.
LogLevelThis variable determines the amount of
logging that sendmail does,
and is set using the LogLevel option (LogLevel on page 1040). You can use
this LogLevel
variable to decide how much, if anything, you wish
to log about what you are doing inside the
checkcompat( )
function.
CurrentLAAn integer representation of the current load average. You might want to use checkcompat( ) to defer mail between selected senders and recipients when the load is very high.
VerboseAn integer that, when nonzero, means that you allow checkcompat( ) to show (print to the standard output) what it is doing.
[455] * Of course, if the archive host supports POSIX threads, these tasks would be better handled by a Milter running on that host.