xref: /freebsd/contrib/sendmail/libmilter/docs/sample.html (revision e6bfd18d21b225af6a0ed67ceeaf1293b7b9eba5)
1<HTML>
2<HEAD><TITLE>A Sample Filter</TITLE></HEAD>
3<BODY>
4<!--
5$Id: sample.html,v 1.23 2013-11-22 20:51:39 ca Exp $
6-->
7<H1>A Sample Filter</H1>
8
9The following sample logs each message to a separate temporary file,
10adds a recipient given with the -a flag,
11and rejects a disallowed recipient address given with the -r flag.
12It recognizes the following options:
13<P>
14<CENTER>
15<TABLE border="1" cellpadding=2 cellspacing=1>
16<TR><TD><CODE>-p port</CODE></TD><TD>The port through which the MTA will connect to the filter.</TD></TR>
17<TR><TD><CODE>-t sec</CODE></TD><TD>The timeout value.</TD></TR>
18<TR><TD><CODE>-r addr</CODE></TD><TD>A recipient to reject.</TD></TR>
19<TR><TD><CODE>-a addr</CODE></TD><TD>A recipient to add.</TD></TR>
20</TABLE>
21</CENTER>
22<HR>
23<PRE>
24#include &lt;sys/types.h&gt;
25#include &lt;sys/stat.h&gt;
26#include &lt;errno.h&gt;
27#include &lt;stdio.h&gt;
28#include &lt;stdlib.h&gt;
29#include &lt;string.h&gt;
30#include &lt;sysexits.h&gt;
31#include &lt;unistd.h&gt;
32
33#include "libmilter/mfapi.h"
34
35#ifndef bool
36# define bool	int
37# define TRUE	1
38# define FALSE	0
39#endif /* ! bool */
40
41
42struct mlfiPriv
43{
44	char	*mlfi_fname;
45	char	*mlfi_connectfrom;
46	char	*mlfi_helofrom;
47	FILE	*mlfi_fp;
48};
49
50#define MLFIPRIV	((struct mlfiPriv *) <A href="smfi_getpriv.html">smfi_getpriv</A>(ctx))
51
52extern sfsistat		mlfi_cleanup(SMFICTX *, bool);
53
54/* recipients to add and reject (set with -a and -r options) */
55char *add = NULL;
56char *reject = NULL;
57
58sfsistat
59<A href="xxfi_connect.html">mlfi_connect</A>(ctx, hostname, hostaddr)
60	 SMFICTX *ctx;
61	 char *hostname;
62	 _SOCK_ADDR *hostaddr;
63{
64	struct mlfiPriv *priv;
65	char *ident;
66
67	/* allocate some private memory */
68	priv = malloc(sizeof *priv);
69	if (priv == NULL)
70	{
71		/* can't accept this message right now */
72		return SMFIS_TEMPFAIL;
73	}
74	memset(priv, '\0', sizeof *priv);
75
76	/* save the private data */
77	<A href="smfi_setpriv.html">smfi_setpriv</A>(ctx, priv);
78
79	ident = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "_");
80	if (ident == NULL)
81		ident = "???";
82	if ((priv-&gt;mlfi_connectfrom = strdup(ident)) == NULL)
83	{
84		(void) mlfi_cleanup(ctx, FALSE);
85		return SMFIS_TEMPFAIL;
86	}
87
88	/* continue processing */
89	return SMFIS_CONTINUE;
90}
91
92sfsistat
93<A href="xxfi_helo.html">mlfi_helo</A>(ctx, helohost)
94	 SMFICTX *ctx;
95	 char *helohost;
96{
97	size_t len;
98	char *tls;
99	char *buf;
100	struct mlfiPriv *priv = MLFIPRIV;
101
102	tls = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{tls_version}");
103	if (tls == NULL)
104		tls = "No TLS";
105	if (helohost == NULL)
106		helohost = "???";
107	len = strlen(tls) + strlen(helohost) + 3;
108	if ((buf = (char*) malloc(len)) == NULL)
109	{
110		(void) mlfi_cleanup(ctx, FALSE);
111		return SMFIS_TEMPFAIL;
112	}
113	snprintf(buf, len, "%s, %s", helohost, tls);
114	if (priv-&gt;mlfi_helofrom != NULL)
115		free(priv-&gt;mlfi_helofrom);
116	priv-&gt;mlfi_helofrom = buf;
117
118	/* continue processing */
119	return SMFIS_CONTINUE;
120}
121
122sfsistat
123<A href="xxfi_envfrom.html">mlfi_envfrom</A>(ctx, argv)
124	 SMFICTX *ctx;
125	 char **argv;
126{
127	int fd = -1;
128	int argc = 0;
129	struct mlfiPriv *priv = MLFIPRIV;
130	char *mailaddr = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{mail_addr}");
131
132	/* open a file to store this message */
133	if ((priv-&gt;mlfi_fname = strdup("/tmp/msg.XXXXXX")) == NULL)
134	{
135		(void) mlfi_cleanup(ctx, FALSE);
136		return SMFIS_TEMPFAIL;
137	}
138
139	if ((fd = mkstemp(priv-&gt;mlfi_fname)) == -1)
140	{
141		(void) mlfi_cleanup(ctx, FALSE);
142		return SMFIS_TEMPFAIL;
143	}
144
145	if ((priv-&gt;mlfi_fp = fdopen(fd, "w+")) == NULL)
146	{
147		(void) close(fd);
148		(void) mlfi_cleanup(ctx, FALSE);
149		return SMFIS_TEMPFAIL;
150	}
151
152	/* count the arguments */
153	while (*argv++ != NULL)
154		++argc;
155
156	/* log the connection information we stored earlier: */
157	if (fprintf(priv-&gt;mlfi_fp, "Connect from %s (%s)\n\n",
158		    priv-&gt;mlfi_helofrom, priv-&gt;mlfi_connectfrom) == EOF)
159	{
160		(void) mlfi_cleanup(ctx, FALSE);
161		return SMFIS_TEMPFAIL;
162	}
163	/* log the sender */
164	if (fprintf(priv-&gt;mlfi_fp, "FROM %s (%d argument%s)\n",
165		    mailaddr ? mailaddr : "???", argc,
166		    (argc == 1) ? "" : "s") == EOF)
167	{
168		(void) mlfi_cleanup(ctx, FALSE);
169		return SMFIS_TEMPFAIL;
170	}
171
172	/* continue processing */
173	return SMFIS_CONTINUE;
174}
175
176sfsistat
177<A href="xxfi_envrcpt.html">mlfi_envrcpt</A>(ctx, argv)
178	 SMFICTX *ctx;
179	 char **argv;
180{
181	struct mlfiPriv *priv = MLFIPRIV;
182	char *rcptaddr = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{rcpt_addr}");
183	int argc = 0;
184
185	/* count the arguments */
186	while (*argv++ != NULL)
187		++argc;
188
189	/* log this recipient */
190	if (reject != NULL &amp;&amp; rcptaddr != NULL &amp;&amp;
191	    (strcasecmp(rcptaddr, reject) == 0))
192	{
193		if (fprintf(priv-&gt;mlfi_fp, "RCPT %s -- REJECTED\n",
194			    rcptaddr) == EOF)
195		{
196			(void) mlfi_cleanup(ctx, FALSE);
197			return SMFIS_TEMPFAIL;
198		}
199		return SMFIS_REJECT;
200	}
201	if (fprintf(priv-&gt;mlfi_fp, "RCPT %s (%d argument%s)\n",
202		    rcptaddr ? rcptaddr : "???", argc,
203		    (argc == 1) ? "" : "s") == EOF)
204	{
205		(void) mlfi_cleanup(ctx, FALSE);
206		return SMFIS_TEMPFAIL;
207	}
208
209	/* continue processing */
210	return SMFIS_CONTINUE;
211}
212
213sfsistat
214<A href="xxfi_header.html">mlfi_header</A>(ctx, headerf, headerv)
215	 SMFICTX *ctx;
216	 char *headerf;
217	 unsigned char *headerv;
218{
219	/* write the header to the log file */
220	if (fprintf(MLFIPRIV-&gt;mlfi_fp, "%s: %s\n", headerf, headerv) == EOF)
221	{
222		(void) mlfi_cleanup(ctx, FALSE);
223		return SMFIS_TEMPFAIL;
224	}
225
226	/* continue processing */
227	return SMFIS_CONTINUE;
228}
229
230sfsistat
231<A href="xxfi_eoh.html">mlfi_eoh</A>(ctx)
232	 SMFICTX *ctx;
233{
234	/* output the blank line between the header and the body */
235	if (fprintf(MLFIPRIV-&gt;mlfi_fp, "\n") == EOF)
236	{
237		(void) mlfi_cleanup(ctx, FALSE);
238		return SMFIS_TEMPFAIL;
239	}
240
241	/* continue processing */
242	return SMFIS_CONTINUE;
243}
244
245sfsistat
246<A href="xxfi_body.html">mlfi_body</A>(ctx, bodyp, bodylen)
247	 SMFICTX *ctx;
248	 unsigned char *bodyp;
249	 size_t bodylen;
250{
251        struct mlfiPriv *priv = MLFIPRIV;
252
253	/* output body block to log file */
254	if (fwrite(bodyp, bodylen, 1, priv-&gt;mlfi_fp) != 1)
255	{
256		/* write failed */
257		fprintf(stderr, "Couldn't write file %s: %s\n",
258			priv-&gt;mlfi_fname, strerror(errno));
259		(void) mlfi_cleanup(ctx, FALSE);
260		return SMFIS_TEMPFAIL;
261	}
262
263	/* continue processing */
264	return SMFIS_CONTINUE;
265}
266
267sfsistat
268<A href="xxfi_eom.html">mlfi_eom</A>(ctx)
269	 SMFICTX *ctx;
270{
271	bool ok = TRUE;
272
273	/* change recipients, if requested */
274	if (add != NULL)
275		ok = (<A href="smfi_addrcpt.html">smfi_addrcpt</A>(ctx, add) == MI_SUCCESS);
276	return mlfi_cleanup(ctx, ok);
277}
278
279sfsistat
280<A href="xxfi_abort.html">mlfi_abort</A>(ctx)
281	 SMFICTX *ctx;
282{
283	return mlfi_cleanup(ctx, FALSE);
284}
285
286sfsistat
287mlfi_cleanup(ctx, ok)
288	 SMFICTX *ctx;
289	 bool ok;
290{
291	sfsistat rstat = SMFIS_CONTINUE;
292	struct mlfiPriv *priv = MLFIPRIV;
293	char *p;
294	char host[512];
295	char hbuf[1024];
296
297	if (priv == NULL)
298		return rstat;
299
300	/* close the archive file */
301	if (priv-&gt;mlfi_fp != NULL &amp;&amp; fclose(priv-&gt;mlfi_fp) == EOF)
302	{
303		/* failed; we have to wait until later */
304		fprintf(stderr, "Couldn't close archive file %s: %s\n",
305			priv-&gt;mlfi_fname, strerror(errno));
306		rstat = SMFIS_TEMPFAIL;
307		(void) unlink(priv-&gt;mlfi_fname);
308	}
309	else if (ok)
310	{
311		/* add a header to the message announcing our presence */
312		if (gethostname(host, sizeof host) &lt; 0)
313			snprintf(host, sizeof host, "localhost");
314		p = strrchr(priv-&gt;mlfi_fname, '/');
315		if (p == NULL)
316			p = priv-&gt;mlfi_fname;
317		else
318			p++;
319		snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
320		if (<A href="smfi_addheader.html">smfi_addheader</A>(ctx, "X-Archived", hbuf) != MI_SUCCESS)
321		{
322			/* failed; we have to wait until later */
323			fprintf(stderr,
324				"Couldn't add header: X-Archived: %s\n",
325				hbuf);
326			ok = FALSE;
327			rstat = SMFIS_TEMPFAIL;
328			(void) unlink(priv-&gt;mlfi_fname);
329		}
330	}
331	else
332	{
333		/* message was aborted -- delete the archive file */
334		fprintf(stderr, "Message aborted.  Removing %s\n",
335			priv-&gt;mlfi_fname);
336		rstat = SMFIS_TEMPFAIL;
337		(void) unlink(priv-&gt;mlfi_fname);
338	}
339
340	/* release private memory */
341	if (priv-&gt;mlfi_fname != NULL)
342		free(priv-&gt;mlfi_fname);
343
344	/* return status */
345	return rstat;
346}
347
348sfsistat
349<A href="xxfi_close.html">mlfi_close</A>(ctx)
350	 SMFICTX *ctx;
351{
352	struct mlfiPriv *priv = MLFIPRIV;
353
354	if (priv == NULL)
355		return SMFIS_CONTINUE;
356	if (priv-&gt;mlfi_connectfrom != NULL)
357		free(priv-&gt;mlfi_connectfrom);
358	if (priv-&gt;mlfi_helofrom != NULL)
359		free(priv-&gt;mlfi_helofrom);
360	free(priv);
361	<A href="smfi_setpriv.html">smfi_setpriv</A>(ctx, NULL);
362	return SMFIS_CONTINUE;
363}
364
365sfsistat
366<A href="xxfi_unknown.html">mlfi_unknown</A>(ctx, cmd)
367	SMFICTX *ctx;
368	char *cmd;
369{
370	return SMFIS_CONTINUE;
371}
372
373sfsistat
374<A href="xxfi_data.html">mlfi_data</A>(ctx)
375	SMFICTX *ctx;
376{
377	return SMFIS_CONTINUE;
378}
379
380sfsistat
381<A href="xxfi_negotiate.html">mlfi_negotiate</A>(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
382	SMFICTX *ctx;
383	unsigned long f0;
384	unsigned long f1;
385	unsigned long f2;
386	unsigned long f3;
387	unsigned long *pf0;
388	unsigned long *pf1;
389	unsigned long *pf2;
390	unsigned long *pf3;
391{
392	return SMFIS_ALL_OPTS;
393}
394
395struct smfiDesc smfilter =
396{
397	"SampleFilter",	/* filter name */
398	SMFI_VERSION,	/* version code -- do not change */
399	SMFIF_ADDHDRS|SMFIF_ADDRCPT,
400			/* flags */
401	<A href="xxfi_connect.html">mlfi_connect</A>,	/* connection info filter */
402	<A href="xxfi_helo.html">mlfi_helo</A>,	/* SMTP HELO command filter */
403	<A href="xxfi_envfrom.html">mlfi_envfrom</A>,	/* envelope sender filter */
404	<A href="xxfi_envrcpt.html">mlfi_envrcpt</A>,	/* envelope recipient filter */
405	<A href="xxfi_header.html">mlfi_header</A>,	/* header filter */
406	<A href="xxfi_eoh.html">mlfi_eoh</A>,	/* end of header */
407	<A href="xxfi_body.html">mlfi_body</A>,	/* body block filter */
408	<A href="xxfi_eom.html">mlfi_eom</A>,	/* end of message */
409	<A href="xxfi_abort.html">mlfi_abort</A>,	/* message aborted */
410	<A href="xxfi_close.html">mlfi_close</A>,	/* connection cleanup */
411	<A href="xxfi_unknown.html">mlfi_unknown</A>,	/* unknown SMTP commands */
412	<A href="xxfi_data.html">mlfi_data</A>,	/* DATA command */
413	<A href="xxfi_negotiate.html">mlfi_negotiate</A>	/* Once, at the start of each SMTP connection */
414};
415
416static void
417usage(prog)
418	char *prog;
419{
420	fprintf(stderr,
421		"Usage: %s -p socket-addr [-t timeout] [-r reject-addr] [-a add-addr]\n",
422		prog);
423}
424
425int
426main(argc, argv)
427	 int argc;
428	 char **argv;
429{
430	bool setconn = FALSE;
431	int c;
432	const char *args = "p:t:r:a:h";
433	extern char *optarg;
434
435	/* Process command line options */
436	while ((c = getopt(argc, argv, args)) != -1)
437	{
438		switch (c)
439		{
440		  case 'p':
441			if (optarg == NULL || *optarg == '\0')
442			{
443				(void) fprintf(stderr, "Illegal conn: %s\n",
444					       optarg);
445				exit(EX_USAGE);
446			}
447			if (<A href="smfi_setconn.html">smfi_setconn</A>(optarg) == MI_FAILURE)
448			{
449				(void) fprintf(stderr,
450					       "smfi_setconn failed\n");
451				exit(EX_SOFTWARE);
452			}
453
454			/*
455			**  If we're using a local socket, make sure it
456			**  doesn't already exist.  Don't ever run this
457			**  code as root!!
458			*/
459
460			if (strncasecmp(optarg, "unix:", 5) == 0)
461				unlink(optarg + 5);
462			else if (strncasecmp(optarg, "local:", 6) == 0)
463				unlink(optarg + 6);
464			setconn = TRUE;
465			break;
466
467		  case 't':
468			if (optarg == NULL || *optarg == '\0')
469			{
470				(void) fprintf(stderr, "Illegal timeout: %s\n",
471					       optarg);
472				exit(EX_USAGE);
473			}
474			if (<A href="smfi_settimeout.html">smfi_settimeout</A>(atoi(optarg)) == MI_FAILURE)
475			{
476				(void) fprintf(stderr,
477					       "smfi_settimeout failed\n");
478				exit(EX_SOFTWARE);
479			}
480			break;
481
482		  case 'r':
483			if (optarg == NULL)
484			{
485				(void) fprintf(stderr,
486					       "Illegal reject rcpt: %s\n",
487					       optarg);
488				exit(EX_USAGE);
489			}
490			reject = optarg;
491			break;
492
493		  case 'a':
494			if (optarg == NULL)
495			{
496				(void) fprintf(stderr,
497					       "Illegal add rcpt: %s\n",
498					       optarg);
499				exit(EX_USAGE);
500			}
501			add = optarg;
502			smfilter.xxfi_flags |= SMFIF_ADDRCPT;
503			break;
504
505		  case 'h':
506		  default:
507			usage(argv[0]);
508			exit(EX_USAGE);
509		}
510	}
511	if (!setconn)
512	{
513		fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
514		usage(argv[0]);
515		exit(EX_USAGE);
516	}
517	if (<A href="smfi_register.html">smfi_register</A>(smfilter) == MI_FAILURE)
518	{
519		fprintf(stderr, "smfi_register failed\n");
520		exit(EX_UNAVAILABLE);
521	}
522	return <A href="smfi_main.html">smfi_main</A>();
523}
524
525/* eof */
526
527</PRE>
528<HR size="1">
529<FONT size="-1">
530Copyright (c) 2000-2004, 2006 Proofpoint, Inc. and its suppliers.
531All rights reserved.
532<BR>
533By using this file, you agree to the terms and conditions set
534forth in the LICENSE.
535</FONT>
536</BODY>
537</HTML>
538