Deleted Added
sdiff udiff text old ( 73188 ) new ( 77349 )
full compact
1This directory contains the source files for libmilter.
2
3The sendmail Mail Filter API (Milter) is designed to allow third-party
4programs access to mail messages as they are being processed in order to
5filter meta-information and content.
6
7This README file describes the steps needed to compile and run a filter,
8through reference to a sample filter which is attached at the end of this
9file. It is necessary to first build libmilter.a, which can be done by
10issuing the './Build' command in SRCDIR/libmilter .
11
12NOTE: Both libmilter and the callouts in sendmail are marked as an FFR (For
13Future Release). If you intend to use them in 8.11.X, you must compiled
14both libmilter and sendmail with -D_FFR_MILTER defined. You can do this by
15adding the following to your devtools/Site/site.config.m4 file:
16
17 dnl Milter
18 APPENDDEF(`conf_sendmail_ENVDEF', `-D_FFR_MILTER=1')
19 APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_MILTER=1')
20
21You will also need to define _FFR_MILTER when building your .cf file using
22m4.
23
24+-------------------+
25| BUILDING A FILTER |
26+-------------------+
27
28The following command presumes that the sample code from the end of this
29README is saved to a file named 'sample.c' and built in the local platform-
30specific build subdirectory (SRCDIR/obj.*/libmilter).
31
32 cc -I../../sendmail -I../../include -o sample sample.c libmilter.a ../libsmutil/libsmutil.a -pthread
33
34It is recommended that you build your filters in a location outside of
35the sendmail source tree. Modify the compiler include references (-I)
36and the library locations accordingly. Also, some operating systems may
37require additional libraries. For example, SunOS 5.X requires '-lresolv
38-lsocket -lnsl'. Depending on your OS you may need a library instead
39of the option -pthread, e.g., -lpthread.
40
41Filters must be thread-safe! Many operating systems now provide support for
42POSIX threads in the standard C libraries. The compiler flag to link with
43threading support differs according to the compiler and linker used. Check
44the Makefile in your appropriate obj.*/libmilter build subdirectory if you
45are unsure of the local flag used.
46
47Note that since filters use threads, it may be necessary to alter per
48process limits in your filter. For example, you might look at using
49setrlimit() to increase the number of open file descriptors if your filter
50is going to be busy.
51
52
53+----------------------------------------+
54| SPECIFYING FILTERS IN SENDMAIL CONFIGS |
55+----------------------------------------+
56
57Filters are specified with a key letter ``X'' (for ``eXternal'').
58
59For example:
60
61 Xfilter1, S=local:/var/run/f1.sock, F=R
62 Xfilter2, S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m
63 Xfilter3, S=inet:3333@localhost
64
65specifies three filters. Filters can be specified in your .mc file using
66the following:
67
68 INPUT_MAIL_FILTER(`filter1', `S=local:/var/run/f1.sock, F=R')
69 INPUT_MAIL_FILTER(`filter2', `S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m')
70 INPUT_MAIL_FILTER(`filter3', `S=inet:3333@localhost')
71
72The first attaches to a Unix-domain socket in the /var/run directory; the
73second uses an IPv6 socket on port 999 of localhost, and the third uses an
74IPv4 socket on port 3333 of localhost. The current flags (F=) are:
75
76 R Reject connection if filter unavailable
77 T Temporary fail connection if filter unavailable
78
79If neither F=R nor F=T is specified, the message is passed through sendmail
80as if the filter were not present.
81
82Finally, you can override the default timeouts used by sendmail when
83talking to the filters using the T= equate. There are three fields inside
84of the T= equate:
85
86Letter Meaning
87 S Timeout for sending information from the MTA to a filter
88 R Timeout for reading reply from the filter
89 E Overall timeout between sending end-of-message to filter
90 and waiting for the final acknowledgment
91
92Note the separator between each is a ';' as a ',' already separates equates
93and therefore can't separate timeouts. The default values (if not set in the config) are:
94
95T=S:10s;R:10s;E:5m
96
97where 's' is seconds and 'm' is minutes.
98
99Which filters are invoked and their sequencing is handled by the
100InputMailFilters option. Note: if InputMailFilters is not defined no filters
101will be used.
102
103 O InputMailFilters=filter1, filter2, filter3
104
105This is is set automatically according to the order of the
106INPUT_MAIL_FILTER commands in your .mc file. Alternatively, you can
107reset its value by setting confINPUT_MAIL_FILTERS in your .mc file.
108This options causes the three filters to be called in the same order
109they were specified. It allows for possible future filtering on output
110(although this is not intended for this release).
111
112Also note that a filter can be defined without adding it to the input
113filter list by using MAIL_FILTER() instead of INPUT_MAIL_FILTER() in your
114.mc file.
115
116To test sendmail with the sample filter, the following might be added (in
117the appropriate locations) to your .mc file:
118
119 INPUT_MAIL_FILTER(`sample', `S=local:/var/run/f1.sock')
120
121
122+------------------+
123| TESTING A FILTER |
124+------------------+
125
126Once you have compiled a filter, modified your .mc file and restarted
127the sendmail process, you will want to test that the filter performs as
128intended.
129
130The sample filter takes one argument -p, which indicates the local port
131on which to create a listening socket for the filter. Maintaining
132consistency with the suggested options for sendmail.cf, this would be the
133UNIX domain socket located in /var/run/f1.sock.
134
135 % ./sample -p local:/var/run/f1.sock
136
137If the sample filter returns immediately to a command line, there was either
138an error with your command or a problem creating the specified socket.
139Further logging can be captured through the syslogd daemon. Using the
140'netstat -a' command can ensure that your filter process is listening on
141the appropriate local socket.
142
143Email messages must be injected via SMTP to be filtered. There are two
144simple means of doing this; either using the 'sendmail -bs' command, or
145by telnetting to port 25 of the machine configured for milter. Once
146connected via one of these options, the session can be continued through
147the use of standard SMTP commands.
148
149% sendmail -bs
150220 test.sendmail.com ESMTP Sendmail 8.11.0/8.11.0; Tue, 10 Nov 1970 13:05:23 -0500 (EST)
151HELO localhost
152250 test.sendmail.com Hello testy@localhost, pleased to meet you
153MAIL From:<testy>
154250 2.1.0 <testy>... Sender ok
155RCPT To:<root>
156250 2.1.5 <root>... Recipient ok
157DATA
158354 Enter mail, end with "." on a line by itself
159From: testy@test.sendmail.com
160To: root@test.sendmail.com
161Subject: testing sample filter
162
163Sample body
164.
165250 2.0.0 dB73Zxi25236 Message accepted for delivery
166QUIT
167221 2.0.0 test.sendmail.com closing connection
168
169In the above example, the lines beginning with numbers are output by the
170mail server, and those without are your input. If everything is working
171properly, you will find a file in /tmp by the name of msg.XXXXXXXX (where
172the Xs represent any combination of letters and numbers). This file should
173contain the message body and headers from the test email entered above.
174
175If the sample filter did not log your test email, there are a number of
176methods to narrow down the source of the problem. Check your system
177logs written by syslogd and see if there are any pertinent lines. You
178may need to reconfigure syslogd to capture all relevant data. Additionally,
179the logging level of sendmail can be raised with the LogLevel option.
180See the sendmail(8) manual page for more information.
181
182
183+--------------------------+
184| SOURCE FOR SAMPLE FILTER |
185+--------------------------+
186
187Note that the filter below may not be thread safe on some operating
188systems. You should check your system man pages for the functions used
189below to verify the functions are thread safe.
190
191/* A trivial filter that logs all email to a file. */
192
193#include <sys/types.h>
194#include <stdio.h>
195#include <stdlib.h>
196#include <string.h>
197#include <sysexits.h>
198#include <unistd.h>
199
200#include "libmilter/mfapi.h"
201
202typedef int bool;
203
204#ifndef FALSE
205# define FALSE 0
206#endif /* ! FALSE*/
207#ifndef TRUE
208# define TRUE 1
209#endif /* ! TRUE*/
210
211struct mlfiPriv
212{
213 char *mlfi_fname;
214 FILE *mlfi_fp;
215};
216
217#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
218
219extern sfsistat mlfi_cleanup(SMFICTX *, bool);
220
221sfsistat
222mlfi_envfrom(ctx, envfrom)
223 SMFICTX *ctx;
224 char **envfrom;
225{
226 struct mlfiPriv *priv;
227 int fd = -1;
228
229 /* allocate some private memory */
230 priv = malloc(sizeof *priv);
231 if (priv == NULL)
232 {
233 /* can't accept this message right now */
234 return SMFIS_TEMPFAIL;
235 }
236 memset(priv, '\0', sizeof *priv);
237
238 /* open a file to store this message */
239 priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
240 if (priv->mlfi_fname == NULL)
241 {
242 free(priv);
243 return SMFIS_TEMPFAIL;
244 }
245 if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
246 (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
247 {
248 if (fd >= 0)
249 (void) close(fd);
250 free(priv->mlfi_fname);
251 free(priv);
252 return SMFIS_TEMPFAIL;
253 }
254
255 /* save the private data */
256 smfi_setpriv(ctx, priv);
257
258 /* continue processing */
259 return SMFIS_CONTINUE;
260}
261
262sfsistat
263mlfi_header(ctx, headerf, headerv)
264 SMFICTX *ctx;
265 char *headerf;
266 char *headerv;
267{
268 /* write the header to the log file */
269 fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
270
271 /* continue processing */
272 return SMFIS_CONTINUE;
273}
274
275sfsistat
276mlfi_eoh(ctx)
277 SMFICTX *ctx;
278{
279 /* output the blank line between the header and the body */
280 fprintf(MLFIPRIV->mlfi_fp, "\r\n");
281
282 /* continue processing */
283 return SMFIS_CONTINUE;
284}
285
286sfsistat
287mlfi_body(ctx, bodyp, bodylen)
288 SMFICTX *ctx;
289 u_char *bodyp;
290 size_t bodylen;
291{
292 /* output body block to log file */
293 if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
294 {
295 /* write failed */
296 (void) mlfi_cleanup(ctx, FALSE);
297 return SMFIS_TEMPFAIL;
298 }
299
300 /* continue processing */
301 return SMFIS_CONTINUE;
302}
303
304sfsistat
305mlfi_eom(ctx)
306 SMFICTX *ctx;
307{
308 return mlfi_cleanup(ctx, TRUE);
309}
310
311sfsistat
312mlfi_close(ctx)
313 SMFICTX *ctx;
314{
315 return SMFIS_ACCEPT;
316}
317
318sfsistat
319mlfi_abort(ctx)
320 SMFICTX *ctx;
321{
322 return mlfi_cleanup(ctx, FALSE);
323}
324
325sfsistat
326mlfi_cleanup(ctx, ok)
327 SMFICTX *ctx;
328 bool ok;
329{
330 sfsistat rstat = SMFIS_CONTINUE;
331 struct mlfiPriv *priv = MLFIPRIV;
332 char *p;
333 char host[512];
334 char hbuf[1024];
335
336 if (priv == NULL)
337 return rstat;
338
339 /* close the archive file */
340 if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
341 {
342 /* failed; we have to wait until later */
343 rstat = SMFIS_TEMPFAIL;
344 (void) unlink(priv->mlfi_fname);
345 }
346 else if (ok)
347 {
348 /* add a header to the message announcing our presence */
349 if (gethostname(host, sizeof host) < 0)
350 strlcpy(host, "localhost", sizeof host);
351 p = strrchr(priv->mlfi_fname, '/');
352 if (p == NULL)
353 p = priv->mlfi_fname;
354 else
355 p++;
356 snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
357 smfi_addheader(ctx, "X-Archived", hbuf);
358 }
359 else
360 {
361 /* message was aborted -- delete the archive file */
362 (void) unlink(priv->mlfi_fname);
363 }
364
365 /* release private memory */
366 free(priv->mlfi_fname);
367 free(priv);
368 smfi_setpriv(ctx, NULL);
369
370 /* return status */
371 return rstat;
372}
373
374struct smfiDesc smfilter =
375{
376 "SampleFilter", /* filter name */
377 SMFI_VERSION, /* version code -- do not change */
378 SMFIF_ADDHDRS, /* flags */
379 NULL, /* connection info filter */
380 NULL, /* SMTP HELO command filter */
381 mlfi_envfrom, /* envelope sender filter */
382 NULL, /* envelope recipient filter */
383 mlfi_header, /* header filter */
384 mlfi_eoh, /* end of header */
385 mlfi_body, /* body block filter */
386 mlfi_eom, /* end of message */
387 mlfi_abort, /* message aborted */
388 mlfi_close /* connection cleanup */
389};
390
391
392int
393main(argc, argv)
394 int argc;
395 char *argv[];
396{
397 int c;
398 const char *args = "p:";
399
400 /* Process command line options */
401 while ((c = getopt(argc, argv, args)) != -1)
402 {
403 switch (c)
404 {
405 case 'p':
406 if (optarg == NULL || *optarg == '\0')
407 {
408 (void) fprintf(stderr, "Illegal conn: %s\n",
409 optarg);
410 exit(EX_USAGE);
411 }
412 (void) smfi_setconn(optarg);
413 break;
414
415 }
416 }
417 if (smfi_register(smfilter) == MI_FAILURE)
418 {
419 fprintf(stderr, "smfi_register failed\n");
420 exit(EX_UNAVAILABLE);
421 }
422 return smfi_main();
423}
424
425/* eof */
426
427$Revision: 8.9.2.1.2.17 $, Last updated $Date: 2001/04/11 18:32:58 $