]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/libmilter/README
This commit was generated by cvs2svn to compensate for changes in r166124,
[FreeBSD/FreeBSD.git] / contrib / sendmail / libmilter / README
1 This directory contains the source files for libmilter.
2
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.
6
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 .
11
12 Starting with 8.13 sendmail is compiled by default with support for
13 the milter API.
14
15 Note: if you want to write a milter in Java, then see
16 http://sendmail-jilter.sourceforge.net/
17
18 +----------------+
19 | SECURITY HINTS |
20 +----------------+
21
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.
28
29 +----------------------+
30 | CONFIGURATION MACROS |
31 +----------------------+
32
33 Libmilter uses a set of C preprocessor macros to specify platform specific
34 features of the C compiler and standard C libraries.
35
36 SM_CONF_POLL
37         Set to 1 if poll(2) should be used instead of select(2).
38
39 +-------------------+
40 | BUILDING A FILTER |
41 +-------------------+
42
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).
46
47         cc -I../../include -o sample sample.c libmilter.a ../libsm/libsm.a -pthread
48
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.
55
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.
61
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
65 is going to be busy.
66
67
68 +----------------------------------------+
69 | SPECIFYING FILTERS IN SENDMAIL CONFIGS |
70 +----------------------------------------+
71
72 Filters are specified with a key letter ``X'' (for ``eXternal'').
73
74 For example:
75
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
79
80 specifies three filters.  Filters can be specified in your .mc file using
81 the following:
82
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')
86
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:
90
91         R               Reject connection if filter unavailable
92         T               Temporary fail connection if filter unavailable
93
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.
96
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
99 of the T= equate:
100
101 Letter          Meaning
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
107
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
110 the config) are:
111
112 T=C:5m;S:10s;R:10s;E:5m
113
114 where 's' is seconds and 'm' is minutes.
115
116 Which filters are invoked and their sequencing is handled by the 
117 InputMailFilters option. Note: if InputMailFilters is not defined no filters
118 will be used.
119
120         O InputMailFilters=filter1, filter2, filter3
121
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).
128
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
131 .mc file.
132
133 To test sendmail with the sample filter, the following might be added (in
134 the appropriate locations) to your .mc file:
135
136         INPUT_MAIL_FILTER(`sample', `S=local:/var/run/f1.sock')
137
138
139 +------------------+
140 | TESTING A FILTER |
141 +------------------+
142
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
145 intended.
146
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.
151
152         % ./sample -p local:/var/run/f1.sock
153
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.
159
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.
165
166 % sendmail -bs
167 220 test.sendmail.com ESMTP Sendmail 8.11.0/8.11.0; Tue, 10 Nov 1970 13:05:23 -0500 (EST)
168 HELO localhost
169 250 test.sendmail.com Hello testy@localhost, pleased to meet you
170 MAIL From:<testy>
171 250 2.1.0 <testy>... Sender ok
172 RCPT To:<root>
173 250 2.1.5 <root>... Recipient ok
174 DATA
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
179
180 Sample body
181 .
182 250 2.0.0 dB73Zxi25236 Message accepted for delivery
183 QUIT
184 221 2.0.0 test.sendmail.com closing connection
185
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.
191
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.
198
199
200 +--------------+
201 | REQUIREMENTS |
202 +--------------+
203
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):
210
211 FreeBSD 3.x, 4.x
212 SunOS 5.x (x >= 5)
213 AIX 4.3.x
214 HP UX 11.x
215 Linux (recent versions/distributions)
216
217 libmilter is currently not supported on:
218
219 IRIX 6.x
220 Ultrix
221
222 Feedback about problems (and possible fixes) is welcome.
223
224 +--------------------------+
225 | SOURCE FOR SAMPLE FILTER |
226 +--------------------------+
227
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.
231
232 /* A trivial filter that logs all email to a file. */
233
234 #include <sys/types.h>
235 #include <stdio.h>
236 #include <stdlib.h>
237 #include <string.h>
238 #include <sysexits.h>
239 #include <unistd.h>
240
241 #include "libmilter/mfapi.h"
242
243 #ifndef true
244 typedef int bool;
245 # define false  0
246 # define true   1
247 #endif /* ! true */
248
249 struct mlfiPriv
250 {
251         char    *mlfi_fname;
252         FILE    *mlfi_fp;
253 };
254
255 #define MLFIPRIV        ((struct mlfiPriv *) smfi_getpriv(ctx))
256
257 extern sfsistat  mlfi_cleanup(SMFICTX *, bool);
258
259 sfsistat
260 mlfi_envfrom(ctx, envfrom)
261         SMFICTX *ctx;
262         char **envfrom;
263 {
264         struct mlfiPriv *priv;
265         int fd = -1;
266
267         /* allocate some private memory */
268         priv = malloc(sizeof *priv);
269         if (priv == NULL)
270         {
271                 /* can't accept this message right now */
272                 return SMFIS_TEMPFAIL;
273         }
274         memset(priv, '\0', sizeof *priv);
275
276         /* open a file to store this message */
277         priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
278         if (priv->mlfi_fname == NULL)
279         {
280                 free(priv);
281                 return SMFIS_TEMPFAIL;
282         }
283         if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
284             (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
285         {
286                 if (fd >= 0)
287                         (void) close(fd);
288                 free(priv->mlfi_fname);
289                 free(priv);
290                 return SMFIS_TEMPFAIL;
291         }
292
293         /* save the private data */
294         smfi_setpriv(ctx, priv);
295
296         /* continue processing */
297         return SMFIS_CONTINUE;
298 }
299
300 sfsistat
301 mlfi_header(ctx, headerf, headerv)
302         SMFICTX *ctx;
303         char *headerf;
304         char *headerv;
305 {
306         /* write the header to the log file */
307         fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
308
309         /* continue processing */
310         return SMFIS_CONTINUE;
311 }
312
313 sfsistat
314 mlfi_eoh(ctx)
315         SMFICTX *ctx;
316 {
317         /* output the blank line between the header and the body */
318         fprintf(MLFIPRIV->mlfi_fp, "\r\n");
319
320         /* continue processing */
321         return SMFIS_CONTINUE;
322 }
323
324 sfsistat
325 mlfi_body(ctx, bodyp, bodylen)
326         SMFICTX *ctx;
327         u_char *bodyp;
328         size_t bodylen;
329 {
330         /* output body block to log file */
331         if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
332         {
333                 /* write failed */
334                 (void) mlfi_cleanup(ctx, false);
335                 return SMFIS_TEMPFAIL;
336         }
337
338         /* continue processing */
339         return SMFIS_CONTINUE;
340 }
341
342 sfsistat
343 mlfi_eom(ctx)
344         SMFICTX *ctx;
345 {
346         return mlfi_cleanup(ctx, true);
347 }
348
349 sfsistat
350 mlfi_close(ctx)
351         SMFICTX *ctx;
352 {
353         return SMFIS_ACCEPT;
354 }
355
356 sfsistat
357 mlfi_abort(ctx)
358         SMFICTX *ctx;
359 {
360         return mlfi_cleanup(ctx, false);
361 }
362
363 sfsistat
364 mlfi_cleanup(ctx, ok)
365         SMFICTX *ctx;
366         bool ok;
367 {
368         sfsistat rstat = SMFIS_CONTINUE;
369         struct mlfiPriv *priv = MLFIPRIV;
370         char *p;
371         char host[512];
372         char hbuf[1024];
373
374         if (priv == NULL)
375                 return rstat;
376
377         /* close the archive file */
378         if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
379         {
380                 /* failed; we have to wait until later */
381                 rstat = SMFIS_TEMPFAIL;
382                 (void) unlink(priv->mlfi_fname);
383         }
384         else if (ok)
385         {
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, '/');
390                 if (p == NULL)
391                         p = priv->mlfi_fname;
392                 else
393                         p++;
394                 snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
395                 smfi_addheader(ctx, "X-Archived", hbuf);
396         }
397         else
398         {
399                 /* message was aborted -- delete the archive file */
400                 (void) unlink(priv->mlfi_fname);
401         }
402
403         /* release private memory */
404         free(priv->mlfi_fname);
405         free(priv);
406         smfi_setpriv(ctx, NULL);
407
408         /* return status */
409         return rstat;
410 }
411
412 struct smfiDesc smfilter =
413 {
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 */
427 };
428
429
430 int
431 main(argc, argv)
432         int argc;
433         char *argv[];
434 {
435         bool setconn = false;
436         int c;
437         const char *args = "p:";
438
439         /* Process command line options */
440         while ((c = getopt(argc, argv, args)) != -1)
441         {
442                 switch (c)
443                 {
444                   case 'p':
445                         if (optarg == NULL || *optarg == '\0')
446                         {
447                                 (void) fprintf(stderr, "Illegal conn: %s\n",
448                                                optarg);
449                                 exit(EX_USAGE);
450                         }
451                         (void) smfi_setconn(optarg);
452                         setconn = true;
453                         break;
454
455                 }
456         }
457         if (!setconn)
458         {
459                 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
460                 exit(EX_USAGE);
461         }
462         if (smfi_register(smfilter) == MI_FAILURE)
463         {
464                 fprintf(stderr, "smfi_register failed\n");
465                 exit(EX_UNAVAILABLE);
466         }
467         return smfi_main();
468 }
469
470 /* eof */
471
472 $Revision: 8.41 $, Last updated $Date: 2005/04/27 22:47:42 $