7.6 The Milter LibraryBeginning with V8.12, sendmail offers hooks to access external programs via sockets, and a library to build external programs to listen on sockets.[23] We first discuss the hooks inside the configuration file in support of external programs, and after that briefly discuss building your own program.
7.6.1 Enable with -DMILTERTo access external programs via sockets from sendmail, you need to compile sendmail with the MILTER compile-time macro defined. To build sendmail in this way, simply add a line such as this to your m4 Build file: APPENDDEF(`confENVDEF', `-DMILTER') Then, build sendmail in the usual manner. If you are using precompiled sendmail, you can detect if it was built with the MILTER compile-time macro defined by running the following command:[24]
% /usr/sbin/sendmail -bt -d0.4 < /dev/null If MILTER was defined, it will appear among a list of other defined macros in a line that will look something like this: Compiled with: DNSMAP LOG MAP_REGEX MILTER MIME7TO8 MIME8TO7 note If it doesn't appear, you will need to either download the sendmail source and build it yourself, or contact your operating system vendor and request a properly compiled version in binary form. 7.6.2 The X Configuration CommandWith MILTER enabled, sendmail offers a way to submit messages to external programs that can be used to screen messages for spam indicators, viruses, or other content that you might want to reject. At the end of this chapter, we will show you the library routines to use when making this decision. Here, we discuss the hooks inside the configuration file that allow you to run external programs. External programs are defined for use by sendmail using the X configuration file command. The form for that command looks like this: Xname, equates ... cf file INPUT_MAIL_FILTER(`name', `equates ...') mc file MAIL_FILTER(`name', `equates ...') mc file The X in the first line, like all configuration commands, must begin a line. It is immediately followed by the name you will assign the external program, with no intervening spaces. That name is for sendmail's use only, and does not need to be the actual name of the program. The name is followed by a comma. If you accidentally prefix the name with a space (in the cf or mc form), or omit the name, the following error will print and the sendmail program will exit: cf file: line number name required for mail filter The equates in this configuration command is a sequence of comma-separated expressions that are formed by a key-letter, an equal sign, and a value: key-letter=value The recognized key-letters and their meanings are shown in Table 7-4.
For example, the following three mc file lines define three possible external program hooks: INPUT_MAIL_FILTER(`progA', `S=local:/var/run/f1.sock, F=R') INPUT_MAIL_FILTER(`progB', `S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m') INPUT_MAIL_FILTER(`progC', `S=inet:3333@localhost') The first example shows how to attach to a Unix-domain socket in the /var/run directory. The second example shows how to connect to an IPv6 socket on port 999 of the local host. The third example shows how to connect to an IPv4 socket on port 3333 of the local host. We will describe each equate in detail in the following three sections, but first, the following details should be noted. If the = is missing from an equate, the following error is printed and sendmail exits: cf file: line number Xname `=' expected If the key-letter prefixing the = character is not one of the three shown in Table 7-4, the following error is printed and sendmail exits: cf file: line number Xname unknown filter equate badequate= The three external programs will be used in the order declared. First progA will be contacted on a Unix-domain socket. If it accepts the message, progB will be contacted on a network socket. If progB accepts the message, progC will be given the final crack at the message. When the nature of the sockets allows, some of these connections might be in parallel. If you want to declare external programs, but don't want to set the order in which they are called, use the MAIL_FILTER mc macro instead: MAIL_FILTER(`progA', `S=local:/var/run/f1.sock, F=R') MAIL_FILTER(`progB', `S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m') MAIL_FILTER(`progC', `S=inet:3333@localhost') This is the same declaration as before, except that it omits the declaration of the order in which the programs sockets will be called. With this form, you will have to separately declare the order with the InputMailFilters option (Section 7.6.3): define(`confINPUT_MAIL_FILTERS', `progB, progA, progC') Note that if sendmail was not compiled with -DMILTER,[25] declaring a socket with these commands will cause the following error to be printed, and sendmail will exit:
Warning: Filter usage ('X') requires Milter support (-DMILTER) 7.6.2.1 The X configuration command F= equateThe F= equate, which stands for "Flags," can cause a message to be rejected or deferred if the connection to the socket fails or if the filter program gives a nonstandard response. If you want the message rejected on failure, you use the letter R, which stands for "reject." If you want the message to be deferred, you use the letter T, which stands for "temporary failure": F=R reject connection if the filter is unavailable or if it has an error F=T temporary fail and defer if the filter is unavailable or if it has an error If any letter other than R or T is specified following the F=, or if the F= equate is missing, the message is passed through sendmail as if the entire X configuration-file command were omitted, or as if the socket could not be contacted. 7.6.2.2 The X configuration command S= equateThe S= equate, which stands for "Socket," is mandatory and specifies one of three different types of sockets as its value: local a Unix-domain socket unix synonym for local inet an IPv4 network socket inet6 an IPv6 network socket If you use a socket type other than one of those listed, the following error will print and sendmail will exit: cf file: line number Xname unknown socket type type: Protocol not supported The format for the S= equate looks like this: S=type:specification
The type is one of the three main types shown earlier. The colon is literal and must be present. The specification is particular to each type. For the local (or unix) type, the specification is the full pathname to a Unix-domain socket. For example: S=local:/var/run/progA.soc Note that the socket must not already exist. The MILTER library will automatically create a socket when one is needed. The inet and inet6-type sockets use a specification that is a port number, immediately followed by an @ character, which is again immediately followed by a host or address specification. For example: S=inet:1099@localhost port 1099 on the local machine, using IPv4 S=inet:1099@host.your.domain port 1099 on another machine on your network, using IPv4 S=inet6:1099@localhost port 1099 on the local machine, using IPv6 S=inet:1099@123.45.67.89 port 1099 at IPv4 number 123.45.67.89 S=inet6:1099@2002:c0a8:51d2::23f4 port 1099 at IPv6 number 2002:c0a8:51d2::23f4 As we have seen in the previous section, the F= equate determines what will happen to a message should the connection to a socket fail. 7.6.2.3 The X configuration command T= equateThere are four timeouts that can affect the use of an external program connected via a socket.[26] They are tunable in your configuration file. Table 7-5 shows all four timeouts, the key-letter for each, and the default value for each.
The form for each key-letter looks like this: letter:value Space can surround the colon. If you specify more than one key-letter with a value, you must separate each from the other with a semicolon. Again, space can surround each semicolon: letter:value;letter :value For example, the following code sets a timeout of 600 seconds for the connection to the socket, and 20 seconds for reads and writes: T=C:600s; R:20s; S:20s The letter s following each number stands for seconds. Instead, you can choose to use the letter m, which stands for minutes. The letters h for hours, d for days, and w for weeks are also available, but they don't make sense for use with this equate. Note that for the C: key-letter, if you set the value to zero, the default timeout for the connect(2) system call will be used. See your system documentation to determine that default. 7.6.3 The InputMailFilters OptionFilters to connect to for processing messages through external programs are declared with the X configuration command (Section 7.6.2). One form of that command (for use in your mc file) not only declares the filter, but also defines the order in which the filters will be called: INPUT_MAIL_FILTER(`progA', `S=local:/var/run/f1.sock, F=R') INPUT_MAIL_FILTER(`progB', `S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m') Here, the filters will be called in the order progA first and progB second, for each phase of the message. Table 7-6 shows which portion of the message is checked by each filter in time order. Note the change in order when the DATA phase begins (header/body).
Each filter is handed portions of a message envelope and body, in phases. For each phase, the filter can advise sendmail of one decision among several possible decisions. See libmilter/docs for a full description of this process. Filters can also be declared with the MAIL_FILTER mc macro, but it does not set the order: MAIL_FILTER(`progA', `S=local:/var/run/f1.sock, F=R') MAIL_FILTER(`progB', `S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m') When the order is not set, or when it is set but you wish to change it, you can use the InputMailFilters option. It defines the order for calling filters: O InputMailFilters=progB, progA cf file define(`confINPUT_MAIL_FILTERS', `progB, progA') mc file Here, the InputMailFilters option defines the order that the filters will be called to be the reverse of what was defined with the MAIL_FILTER mc command. If you fail to define an order for the filters, no filters will be called, and no message screening will happen. If your version of sendmail was not compiled with -DMILTER defined and you declare this option, you will get the following error, and sendmail will exit: Warning: Option: InputMailFilters requires Milter support (-DMILTER) If you list more than the number of filters permitted by MAXFILTERS (which defaults to 25), the following error will print and the extra filters will be ignored: Too many filters defined, 25 max If you misspell one of the filter names, the following error will print and that filter will be ignored: InputFilter probB not defined
Note that all external programs and filters are connected to when the message is received via SMTP (either over the network or with the -bs command-line switch). None is called just before the message is transmitted. Such output filtering might appear in a future release of sendmail. 7.6.4 Build a FilterA mail filter is a program that listens on a socket. It receives a message on that socket from sendmail interactively and in pieces. The sendmail program first offers the connection information, and the filter can take it for review, or decline it. If it accepts, it will screen that information and either reject the message based on its review, or allow the message. Then the next piece is offered and reviewed in the same manner. The order of the review is:
The program must quickly (within the timeouts defined by the X configuration command) parse the pieces and decide if the message should be accepted or rejected. The program then advises sendmail of its decision, using the libmilter API. The sendmail source distribution includes a library and sample program that you should use to create your own filter program. Look in the directory libmilter. It contains the source for the library, a README file with the latest information, and a subdirectory libmilter/docs that contains all the documentation you will need in HTML format. We recommend you build your filter program using the supplied library. Don't dig through the source to divine the current protocol because that protocol will evolve from version to version. Instead, use the API provided by the library. If you don't wish to write your own filter program, consider the following, and search the Web for others:
|