1 This directory contains the source files for libmilter.
3 The sendmail Mail Filter API (Milter) is designed to allow third-party
4 programs access to mail messages as they are being processed in order to
5 filter meta-information and content.
7 This README file describes the steps needed to compile and run a filter,
8 through reference to a sample filter which is attached at the end of this
9 file. It is necessary to first build libmilter.a, which can be done by
10 issuing the './Build' command in SRCDIR/libmilter .
12 Starting with 8.13 sendmail is compiled by default with support for
15 Note: if you want to write a milter in Java, then see
16 http://sendmail-jilter.sourceforge.net/
22 Note: we strongly recommend not to run any milter as root. Libmilter
23 does not need root access to communicate with sendmail. It is a
24 good security practice to run a program only with root privileges
25 if really necessary. A milter should probably check first whether
26 it runs as root and refuse to start in that case. libmilter will
27 not unlink a socket when running as root.
29 +----------------------+
30 | CONFIGURATION MACROS |
31 +----------------------+
33 Libmilter uses a set of C preprocessor macros to specify platform specific
34 features of the C compiler and standard C libraries.
37 Set to 1 if poll(2) should be used instead of select(2).
43 The following command presumes that the sample code from the end of this
44 README is saved to a file named 'sample.c' and built in the local platform-
45 specific build subdirectory (SRCDIR/obj.*/libmilter).
47 cc -I../../include -o sample sample.c libmilter.a ../libsm/libsm.a -pthread
49 It is recommended that you build your filters in a location outside of
50 the sendmail source tree. Modify the compiler include references (-I)
51 and the library locations accordingly. Also, some operating systems may
52 require additional libraries. For example, SunOS 5.X requires '-lresolv
53 -lsocket -lnsl'. Depending on your operating system you may need a library
54 instead of the option -pthread, e.g., -lpthread.
56 Filters must be thread-safe! Many operating systems now provide support for
57 POSIX threads in the standard C libraries. The compiler flag to link with
58 threading support differs according to the compiler and linker used. Check
59 the Makefile in your appropriate obj.*/libmilter build subdirectory if you
60 are unsure of the local flag used.
62 Note that since filters use threads, it may be necessary to alter per
63 process limits in your filter. For example, you might look at using
64 setrlimit() to increase the number of open file descriptors if your filter
68 +----------------------------------------+
69 | SPECIFYING FILTERS IN SENDMAIL CONFIGS |
70 +----------------------------------------+
72 Filters are specified with a key letter ``X'' (for ``eXternal'').
76 Xfilter1, S=local:/var/run/f1.sock, F=R
77 Xfilter2, S=inet6:999@localhost, F=T, T=C:10m;S:1s;R:1s;E:5m
78 Xfilter3, S=inet:3333@localhost
80 specifies three filters. Filters can be specified in your .mc file using
83 INPUT_MAIL_FILTER(`filter1', `S=local:/var/run/f1.sock, F=R')
84 INPUT_MAIL_FILTER(`filter2', `S=inet6:999@localhost, F=T, T=C:10m;S:1s;R:1s;E:5m')
85 INPUT_MAIL_FILTER(`filter3', `S=inet:3333@localhost')
87 The first attaches to a Unix-domain socket in the /var/run directory; the
88 second uses an IPv6 socket on port 999 of localhost, and the third uses an
89 IPv4 socket on port 3333 of localhost. The current flags (F=) are:
91 R Reject connection if filter unavailable
92 T Temporary fail connection if filter unavailable
94 If neither F=R nor F=T is specified, the message is passed through sendmail
95 in case of filter errors as if the failing filters were not present.
97 Finally, you can override the default timeouts used by sendmail when
98 talking to the filters using the T= equate. There are four fields inside
102 C Timeout for connecting to a filter (if 0, use system timeout)
103 S Timeout for sending information from the MTA to a filter
104 R Timeout for reading reply from the filter
105 E Overall timeout between sending end-of-message to filter
106 and waiting for the final acknowledgment
108 Note the separator between each is a ';' as a ',' already separates equates
109 and therefore can't separate timeouts. The default values (if not set in
112 T=C:5m;S:10s;R:10s;E:5m
114 where 's' is seconds and 'm' is minutes.
116 Which filters are invoked and their sequencing is handled by the
117 InputMailFilters option. Note: if InputMailFilters is not defined no filters
120 O InputMailFilters=filter1, filter2, filter3
122 This is is set automatically according to the order of the
123 INPUT_MAIL_FILTER commands in your .mc file. Alternatively, you can
124 reset its value by setting confINPUT_MAIL_FILTERS in your .mc file.
125 This options causes the three filters to be called in the same order
126 they were specified. It allows for possible future filtering on output
127 (although this is not intended for this release).
129 Also note that a filter can be defined without adding it to the input
130 filter list by using MAIL_FILTER() instead of INPUT_MAIL_FILTER() in your
133 To test sendmail with the sample filter, the following might be added (in
134 the appropriate locations) to your .mc file:
136 INPUT_MAIL_FILTER(`sample', `S=local:/var/run/f1.sock')
143 Once you have compiled a filter, modified your .mc file and restarted
144 the sendmail process, you will want to test that the filter performs as
147 The sample filter takes one argument -p, which indicates the local port
148 on which to create a listening socket for the filter. Maintaining
149 consistency with the suggested options for sendmail.cf, this would be the
150 UNIX domain socket located in /var/run/f1.sock.
152 % ./sample -p local:/var/run/f1.sock
154 If the sample filter returns immediately to a command line, there was either
155 an error with your command or a problem creating the specified socket.
156 Further logging can be captured through the syslogd daemon. Using the
157 'netstat -a' command can ensure that your filter process is listening on
158 the appropriate local socket.
160 Email messages must be injected via SMTP to be filtered. There are two
161 simple means of doing this; either using the 'sendmail -bs' command, or
162 by telnetting to port 25 of the machine configured for milter. Once
163 connected via one of these options, the session can be continued through
164 the use of standard SMTP commands.
167 220 test.sendmail.com ESMTP Sendmail 8.11.0/8.11.0; Tue, 10 Nov 1970 13:05:23 -0500 (EST)
169 250 test.sendmail.com Hello testy@localhost, pleased to meet you
171 250 2.1.0 <testy>... Sender ok
173 250 2.1.5 <root>... Recipient ok
175 354 Enter mail, end with "." on a line by itself
176 From: testy@test.sendmail.com
177 To: root@test.sendmail.com
178 Subject: testing sample filter
182 250 2.0.0 dB73Zxi25236 Message accepted for delivery
184 221 2.0.0 test.sendmail.com closing connection
186 In the above example, the lines beginning with numbers are output by the
187 mail server, and those without are your input. If everything is working
188 properly, you will find a file in /tmp by the name of msg.XXXXXXXX (where
189 the Xs represent any combination of letters and numbers). This file should
190 contain the message body and headers from the test email entered above.
192 If the sample filter did not log your test email, there are a number of
193 methods to narrow down the source of the problem. Check your system
194 logs written by syslogd and see if there are any pertinent lines. You
195 may need to reconfigure syslogd to capture all relevant data. Additionally,
196 the logging level of sendmail can be raised with the LogLevel option.
197 See the sendmail(8) manual page for more information.
204 libmilter requires pthread support in the operating system. Moreover, it
205 requires that the library functions it uses are thread safe; which is true
206 for the operating systems libmilter has been developed and tested on. On
207 some operating systems this requires special compile time options (e.g.,
208 not just -pthread). libmilter is currently known to work on (modulo problems
209 in the pthread support of some specific versions):
215 Linux (recent versions/distributions)
217 libmilter is currently not supported on:
222 Feedback about problems (and possible fixes) is welcome.
224 +--------------------------+
225 | SOURCE FOR SAMPLE FILTER |
226 +--------------------------+
228 Note that the filter below may not be thread safe on some operating
229 systems. You should check your system man pages for the functions used
230 below to verify the functions are thread safe.
232 /* A trivial filter that logs all email to a file. */
234 #include <sys/types.h>
238 #include <sysexits.h>
241 #include "libmilter/mfapi.h"
255 #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
257 extern sfsistat mlfi_cleanup(SMFICTX *, bool);
260 mlfi_envfrom(ctx, envfrom)
264 struct mlfiPriv *priv;
267 /* allocate some private memory */
268 priv = malloc(sizeof *priv);
271 /* can't accept this message right now */
272 return SMFIS_TEMPFAIL;
274 memset(priv, '\0', sizeof *priv);
276 /* open a file to store this message */
277 priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
278 if (priv->mlfi_fname == NULL)
281 return SMFIS_TEMPFAIL;
283 if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
284 (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
288 free(priv->mlfi_fname);
290 return SMFIS_TEMPFAIL;
293 /* save the private data */
294 smfi_setpriv(ctx, priv);
296 /* continue processing */
297 return SMFIS_CONTINUE;
301 mlfi_header(ctx, headerf, headerv)
306 /* write the header to the log file */
307 fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
309 /* continue processing */
310 return SMFIS_CONTINUE;
317 /* output the blank line between the header and the body */
318 fprintf(MLFIPRIV->mlfi_fp, "\r\n");
320 /* continue processing */
321 return SMFIS_CONTINUE;
325 mlfi_body(ctx, bodyp, bodylen)
330 /* output body block to log file */
331 if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
334 (void) mlfi_cleanup(ctx, false);
335 return SMFIS_TEMPFAIL;
338 /* continue processing */
339 return SMFIS_CONTINUE;
346 return mlfi_cleanup(ctx, true);
360 return mlfi_cleanup(ctx, false);
364 mlfi_cleanup(ctx, ok)
368 sfsistat rstat = SMFIS_CONTINUE;
369 struct mlfiPriv *priv = MLFIPRIV;
377 /* close the archive file */
378 if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
380 /* failed; we have to wait until later */
381 rstat = SMFIS_TEMPFAIL;
382 (void) unlink(priv->mlfi_fname);
386 /* add a header to the message announcing our presence */
387 if (gethostname(host, sizeof host) < 0)
388 snprintf(host, sizeof host, "localhost");
389 p = strrchr(priv->mlfi_fname, '/');
391 p = priv->mlfi_fname;
394 snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
395 smfi_addheader(ctx, "X-Archived", hbuf);
399 /* message was aborted -- delete the archive file */
400 (void) unlink(priv->mlfi_fname);
403 /* release private memory */
404 free(priv->mlfi_fname);
406 smfi_setpriv(ctx, NULL);
412 struct smfiDesc smfilter =
414 "SampleFilter", /* filter name */
415 SMFI_VERSION, /* version code -- do not change */
416 SMFIF_ADDHDRS, /* flags */
417 NULL, /* connection info filter */
418 NULL, /* SMTP HELO command filter */
419 mlfi_envfrom, /* envelope sender filter */
420 NULL, /* envelope recipient filter */
421 mlfi_header, /* header filter */
422 mlfi_eoh, /* end of header */
423 mlfi_body, /* body block filter */
424 mlfi_eom, /* end of message */
425 mlfi_abort, /* message aborted */
426 mlfi_close /* connection cleanup */
435 bool setconn = false;
437 const char *args = "p:";
439 /* Process command line options */
440 while ((c = getopt(argc, argv, args)) != -1)
445 if (optarg == NULL || *optarg == '\0')
447 (void) fprintf(stderr, "Illegal conn: %s\n",
451 (void) smfi_setconn(optarg);
459 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
462 if (smfi_register(smfilter) == MI_FAILURE)
464 fprintf(stderr, "smfi_register failed\n");
465 exit(EX_UNAVAILABLE);
472 $Revision: 8.41 $, Last updated $Date: 2005/04/27 22:47:42 $