xref: /netbsd-src/external/ibm-public/postfix/dist/src/milter/milter.c (revision 82ad575716605df31379cf04a2f3efbc97b8a6f5)
1 /*	$NetBSD: milter.c,v 1.1.1.2 2010/06/17 18:06:55 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	milter 3
6 /* SUMMARY
7 /*	generic MTA-side mail filter interface
8 /* SYNOPSIS
9 /*	#include <milter.h>
10 /*
11 /*	MILTERS	*milter_create(milter_names, conn_timeout, cmd_timeout,
12 /*					msg_timeout, protocol, def_action,
13 /*					conn_macros, helo_macros,
14 /*					mail_macros, rcpt_macros,
15 /*					data_macros, eoh_macros,
16 /*					eod_macros, unk_macros)
17 /*	const char *milter_names;
18 /*	int	conn_timeout;
19 /*	int	cmd_timeout;
20 /*	int	msg_timeout;
21 /*	const char *protocol;
22 /*	const char *def_action;
23 /*	const char *conn_macros;
24 /*	const char *helo_macros;
25 /*	const char *mail_macros;
26 /*	const char *rcpt_macrps;
27 /*	const char *data_macros;
28 /*	const char *eoh_macros;
29 /*	const char *eod_macros;
30 /*	const char *unk_macros;
31 /*
32 /*	void	milter_free(milters)
33 /*	MILTERS	*milters;
34 /*
35 /*	void	milter_macro_callback(milters, mac_lookup, mac_context)
36 /*	const char *(*mac_lookup)(const char *name, void *context);
37 /*	void	*mac_context;
38 /*
39 /*	void	milter_edit_callback(milters, add_header, upd_header,
40 /*					ins_header, del_header, chg_from,
41 /*					add_rcpt, add_rcpt_par, del_rcpt,
42 /*					repl_body, context)
43 /*	MILTERS	*milters;
44 /*	MILTER_ADD_HEADER_FN add_header;
45 /*	MILTER_EDIT_HEADER_FN upd_header;
46 /*	MILTER_EDIT_HEADER_FN ins_header;
47 /*	MILTER_DEL_HEADER_FN del_header;
48 /*	MILTER_EDIT_FROM_FN chg_from;
49 /*	MILTER_EDIT_RCPT_FN add_rcpt;
50 /*	MILTER_EDIT_RCPT_PAR_FN add_rcpt_par;
51 /*	MILTER_EDIT_RCPT_FN del_rcpt;
52 /*	MILTER_EDIT_BODY_FN repl_body;
53 /*	void	*context;
54 /*
55 /*	const char *milter_conn_event(milters, client_name, client_addr,
56 /*					client_port, addr_family)
57 /*	MILTERS	*milters;
58 /*	const char *client_name;
59 /*	const char *client_addr;
60 /*	const char *client_port;
61 /*	int	addr_family;
62 /*
63 /*	const char *milter_disc_event(milters)
64 /*	MILTERS	*milters;
65 /*
66 /*	const char *milter_helo_event(milters, helo_name, esmtp_flag)
67 /*	MILTERS	*milters;
68 /*	const char *helo_name;
69 /*	int	esmtp_flag;
70 /*
71 /*	const char *milter_mail_event(milters, argv)
72 /*	MILTERS	*milters;
73 /*	const char **argv;
74 /*
75 /*	const char *milter_rcpt_event(milters, flags, argv)
76 /*	MILTERS	*milters;
77 /*	int	flags;
78 /*	const char **argv;
79 /*
80 /*	const char *milter_data_event(milters)
81 /*	MILTERS	*milters;
82 /*
83 /*	const char *milter_unknown_event(milters, command)
84 /*	MILTERS	*milters;
85 /*	const char *command;
86 /*
87 /*	const char *milter_other_event(milters)
88 /*	MILTERS	*milters;
89 /*
90 /*	const char *milter_message(milters, qfile, data_offset)
91 /*	MILTERS	*milters;
92 /*	VSTREAM *qfile;
93 /*	off_t	data_offset;
94 /*
95 /*	const char *milter_abort(milters)
96 /*	MILTERS	*milters;
97 /*
98 /*	int	milter_send(milters, fp)
99 /*	MILTERS	*milters;
100 /*	VSTREAM *fp;
101 /*
102 /*	MILTERS	*milter_receive(fp, count)
103 /*	VSTREAM	*fp;
104 /*	int	count;
105 /*
106 /*	int	milter_dummy(milters, fp)
107 /*	MILTERS	*milters;
108 /*	VSTREAM *fp;
109 /* DESCRIPTION
110 /*	The functions in this module manage one or more milter (mail
111 /*	filter) clients. Currently, only the Sendmail 8 filter
112 /*	protocol is supported.
113 /*
114 /*	The functions that inspect content or envelope commands
115 /*	return either an SMTP reply ([45]XX followed by enhanced
116 /*	status code and text), "D" (discard), "H" (quarantine),
117 /*	"S" (shutdown connection), or a null pointer, which means
118 /*	"no news is good news".
119 /*
120 /*	milter_create() instantiates the milter clients specified
121 /*	with the milter_names argument.  The conn_macros etc.
122 /*	arguments specify the names of macros that are sent to the
123 /*	mail filter applications upon a connect etc. event. This
124 /*	function should be called during process initialization,
125 /*	before entering a chroot jail. The timeout parameters specify
126 /*	time limits for the completion of the specified request
127 /*	classes. The protocol parameter specifies a protocol version
128 /*	and optional extensions.  When the milter application is
129 /*	unavailable, the milter client will go into a suitable error
130 /*	state as specified with the def_action parameter (i.e.
131 /*	reject, tempfail or accept all subsequent events).
132 /*
133 /*	milter_free() disconnects from the milter instances that
134 /*	are still opened, and destroys the data structures created
135 /*	by milter_create(). This function is safe to call at any
136 /*	point after milter_create().
137 /*
138 /*	milter_macro_callback() specifies a call-back function and
139 /*	context for macro lookup. This function must be called
140 /*	before milter_conn_event().
141 /*
142 /*	milter_edit_callback() specifies call-back functions and
143 /*	context for editing the queue file after the end-of-data
144 /*	is received. This function must be called before milter_message();
145 /*
146 /*	milter_conn_event() reports an SMTP client connection event
147 /*	to the specified milter instances, after sending the macros
148 /*	specified with the milter_create() conn_macros argument.
149 /*	This function must be called before reporting any other
150 /*	events.
151 /*
152 /*	milter_disc_event() reports an SMTP client disconnection
153 /*	event to the specified milter instances. No events can
154 /*	reported after this call. To simplify usage, redundant calls
155 /*	of this function are NO-OPs and don't raise a run-time
156 /*	error.
157 /*
158 /*	milter_helo_event() reports a HELO or EHLO event to the
159 /*	specified milter instances, after sending the macros that
160 /*	were specified with the milter_create() helo_macros argument.
161 /*
162 /*	milter_mail_event() reports a MAIL FROM event to the specified
163 /*	milter instances, after sending the macros that were specified
164 /*	with the milter_create() mail_macros argument.
165 /*
166 /*	milter_rcpt_event() reports an RCPT TO event to the specified
167 /*	milter instances, after sending the macros that were specified
168 /*	with the milter_create() rcpt_macros argument. The flags
169 /*	argument supports the following:
170 /* .IP MILTER_FLAG_WANT_RCPT_REJ
171 /*	When this flag is cleared, invoke all milters.  When this
172 /*	flag is set, invoke only milters that want to receive
173 /*	rejected recipients; with Sendmail V8 Milters, {rcpt_mailer}
174 /*	is set to "error", {rcpt_host} is set to an enhanced status
175 /*	code, and {rcpt_addr} is set to descriptive text.
176 /* .PP
177 /*	milter_data_event() reports a DATA event to the specified
178 /*	milter instances, after sending the macros that were specified
179 /*	with the milter_create() data_macros argument.
180 /*
181 /*	milter_unknown_event() reports an unknown command event to
182 /*	the specified milter instances, after sending the macros
183 /*	that were specified with the milter_create() unk_macros
184 /*	argument.
185 /*
186 /*	milter_other_event() returns the current default mail filter
187 /*	reply for the current SMTP connection state; it does not
188 /*	change milter states. A null pointer result means that all
189 /*	is well. This function can be used for SMTP commands such
190 /*	as AUTH, STARTTLS that don't have their own milter event
191 /*	routine.
192 /*
193 /*	milter_message() sends the message header and body to the
194 /*	to the specified milter instances, and sends the macros
195 /*	specified with the milter_create() eoh_macros after the
196 /*	message header, and with the eod_macros argument at
197 /*	the end.  Each milter sees the result of any changes made
198 /*	by a preceding milter. This function must be called with
199 /*	as argument an open Postfix queue file.
200 /*
201 /*	milter_abort() cancels a mail transaction in progress.  To
202 /*	simplify usage, redundant calls of this function are NO-OPs
203 /*	and don't raise a run-time error.
204 /*
205 /*	milter_send() sends a list of mail filters over the specified
206 /*	stream. When given a null list pointer, a "no filter"
207 /*	indication is sent.  The result is non-zero in case of
208 /*	error.
209 /*
210 /*	milter_receive() receives the specified number of mail
211 /*	filters over the specified stream. The result is a null
212 /*	pointer when no milters were sent, or when an error happened.
213 /*
214 /*	milter_dummy() is like milter_send(), except that it sends
215 /*	a dummy, but entirely valid, mail filter list.
216 /* SEE ALSO
217 /*	milter8(3) Sendmail 8 Milter protocol
218 /* DIAGNOSTICS
219 /*	Panic: interface violation.
220 /*	Fatal errors: memory allocation problem.
221 /* LICENSE
222 /* .ad
223 /* .fi
224 /*	The Secure Mailer license must be distributed with this software.
225 /* AUTHOR(S)
226 /*	Wietse Venema
227 /*	IBM T.J. Watson Research
228 /*	P.O. Box 704
229 /*	Yorktown Heights, NY 10598, USA
230 /*--*/
231 
232 /* System library. */
233 
234 #include <sys_defs.h>
235 
236 /* Utility library. */
237 
238 #include <msg.h>
239 #include <mymalloc.h>
240 #include <stringops.h>
241 #include <argv.h>
242 #include <attr.h>
243 
244 /* Global library. */
245 
246 #include <mail_proto.h>
247 #include <record.h>
248 #include <rec_type.h>
249 
250 /* Postfix Milter library. */
251 
252 #include <milter.h>
253 
254 /* Application-specific. */
255 
256  /*
257   * SLMs.
258   */
259 #define STR(x)	vstring_str(x)
260 
261 /* milter_macro_lookup - look up macros */
262 
263 static ARGV *milter_macro_lookup(MILTERS *milters, const char *macro_names)
264 {
265     const char *myname = "milter_macro_lookup";
266     char   *saved_names = mystrdup(macro_names);
267     char   *cp = saved_names;
268     ARGV   *argv = argv_alloc(10);
269     const char *value;
270     const char *name;
271 
272     while ((name = mystrtok(&cp, ", \t\r\n")) != 0) {
273 	if (msg_verbose)
274 	    msg_info("%s: \"%s\"", myname, name);
275 	if ((value = milters->mac_lookup(name, milters->mac_context)) != 0) {
276 	    if (msg_verbose)
277 		msg_info("%s: result \"%s\"", myname, value);
278 	    argv_add(argv, name, value, (char *) 0);
279 	}
280     }
281     myfree(saved_names);
282     return (argv);
283 }
284 
285 /* milter_macro_callback - specify macro lookup */
286 
287 void    milter_macro_callback(MILTERS *milters,
288 		           const char *(*mac_lookup) (const char *, void *),
289 			              void *mac_context)
290 {
291     milters->mac_lookup = mac_lookup;
292     milters->mac_context = mac_context;
293 }
294 
295 /* milter_edit_callback - specify queue file edit call-back information */
296 
297 void    milter_edit_callback(MILTERS *milters,
298 			             MILTER_ADD_HEADER_FN add_header,
299 			             MILTER_EDIT_HEADER_FN upd_header,
300 			             MILTER_EDIT_HEADER_FN ins_header,
301 			             MILTER_DEL_HEADER_FN del_header,
302 			             MILTER_EDIT_FROM_FN chg_from,
303 			             MILTER_EDIT_RCPT_FN add_rcpt,
304 			             MILTER_EDIT_RCPT_PAR_FN add_rcpt_par,
305 			             MILTER_EDIT_RCPT_FN del_rcpt,
306 			             MILTER_EDIT_BODY_FN repl_body,
307 			             void *chg_context)
308 {
309     milters->add_header = add_header;
310     milters->upd_header = upd_header;
311     milters->ins_header = ins_header;
312     milters->del_header = del_header;
313     milters->chg_from = chg_from;
314     milters->add_rcpt = add_rcpt;
315     milters->add_rcpt_par = add_rcpt_par;
316     milters->del_rcpt = del_rcpt;
317     milters->repl_body = repl_body;
318     milters->chg_context = chg_context;
319 }
320 
321 /* milter_conn_event - report connect event */
322 
323 const char *milter_conn_event(MILTERS *milters,
324 			              const char *client_name,
325 			              const char *client_addr,
326 			              const char *client_port,
327 			              unsigned addr_family)
328 {
329     const char *resp;
330     MILTER *m;
331     ARGV   *global_macros = 0;
332     ARGV   *any_macros;
333 
334 #define MILTER_MACRO_EVAL(global_macros, m, milters, member) \
335 	((m->macros && m->macros->member[0]) ? \
336 	    milter_macro_lookup(milters, m->macros->member) : \
337 		global_macros ? global_macros : \
338 		    (global_macros = \
339 		         milter_macro_lookup(milters, milters->macros->member)))
340 
341     if (msg_verbose)
342 	msg_info("report connect to all milters");
343     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
344 	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, conn_macros);
345 	resp = m->conn_event(m, client_name, client_addr, client_port,
346 			     addr_family, any_macros);
347 	if (any_macros != global_macros)
348 	    argv_free(any_macros);
349     }
350     if (global_macros)
351 	argv_free(global_macros);
352     return (resp);
353 }
354 
355 /* milter_helo_event - report helo event */
356 
357 const char *milter_helo_event(MILTERS *milters, const char *helo_name,
358 			              int esmtp_flag)
359 {
360     const char *resp;
361     MILTER *m;
362     ARGV   *global_macros = 0;
363     ARGV   *any_macros;
364 
365     if (msg_verbose)
366 	msg_info("report helo to all milters");
367     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
368 	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, helo_macros);
369 	resp = m->helo_event(m, helo_name, esmtp_flag, any_macros);
370 	if (any_macros != global_macros)
371 	    argv_free(any_macros);
372     }
373     if (global_macros)
374 	argv_free(global_macros);
375     return (resp);
376 }
377 
378 /* milter_mail_event - report mail from event */
379 
380 const char *milter_mail_event(MILTERS *milters, const char **argv)
381 {
382     const char *resp;
383     MILTER *m;
384     ARGV   *global_macros = 0;
385     ARGV   *any_macros;
386 
387     if (msg_verbose)
388 	msg_info("report sender to all milters");
389     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
390 	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, mail_macros);
391 	resp = m->mail_event(m, argv, any_macros);
392 	if (any_macros != global_macros)
393 	    argv_free(any_macros);
394     }
395     if (global_macros)
396 	argv_free(global_macros);
397     return (resp);
398 }
399 
400 /* milter_rcpt_event - report rcpt to event */
401 
402 const char *milter_rcpt_event(MILTERS *milters, int flags, const char **argv)
403 {
404     const char *resp;
405     MILTER *m;
406     ARGV   *global_macros = 0;
407     ARGV   *any_macros;
408 
409     if (msg_verbose)
410 	msg_info("report recipient to all milters (flags=0x%x)", flags);
411     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
412 	if ((flags & MILTER_FLAG_WANT_RCPT_REJ) == 0
413 	    || (m->flags & MILTER_FLAG_WANT_RCPT_REJ) != 0) {
414 	    any_macros =
415 		MILTER_MACRO_EVAL(global_macros, m, milters, rcpt_macros);
416 	    resp = m->rcpt_event(m, argv, any_macros);
417 	    if (any_macros != global_macros)
418 		argv_free(any_macros);
419 	}
420     }
421     if (global_macros)
422 	argv_free(global_macros);
423     return (resp);
424 }
425 
426 /* milter_data_event - report data event */
427 
428 const char *milter_data_event(MILTERS *milters)
429 {
430     const char *resp;
431     MILTER *m;
432     ARGV   *global_macros = 0;
433     ARGV   *any_macros;
434 
435     if (msg_verbose)
436 	msg_info("report data to all milters");
437     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
438 	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, data_macros);
439 	resp = m->data_event(m, any_macros);
440 	if (any_macros != global_macros)
441 	    argv_free(any_macros);
442     }
443     if (global_macros)
444 	argv_free(global_macros);
445     return (resp);
446 }
447 
448 /* milter_unknown_event - report unknown command */
449 
450 const char *milter_unknown_event(MILTERS *milters, const char *command)
451 {
452     const char *resp;
453     MILTER *m;
454     ARGV   *global_macros = 0;
455     ARGV   *any_macros;
456 
457     if (msg_verbose)
458 	msg_info("report unknown command to all milters");
459     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
460 	any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, unk_macros);
461 	resp = m->unknown_event(m, command, any_macros);
462 	if (any_macros != global_macros)
463 	    argv_free(any_macros);
464     }
465     if (global_macros)
466 	argv_free(global_macros);
467     return (resp);
468 }
469 
470 /* milter_other_event - other SMTP event */
471 
472 const char *milter_other_event(MILTERS *milters)
473 {
474     const char *resp;
475     MILTER *m;
476 
477     if (msg_verbose)
478 	msg_info("query milter states for other event");
479     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next)
480 	resp = m->other_event(m);
481     return (resp);
482 }
483 
484 /* milter_message - inspect message content */
485 
486 const char *milter_message(MILTERS *milters, VSTREAM *fp, off_t data_offset)
487 {
488     const char *resp;
489     MILTER *m;
490     ARGV   *global_eoh_macros = 0;
491     ARGV   *global_eod_macros = 0;
492     ARGV   *any_eoh_macros;
493     ARGV   *any_eod_macros;
494 
495     if (msg_verbose)
496 	msg_info("inspect content by all milters");
497     for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) {
498 	any_eoh_macros = MILTER_MACRO_EVAL(global_eoh_macros, m, milters, eoh_macros);
499 	any_eod_macros = MILTER_MACRO_EVAL(global_eod_macros, m, milters, eod_macros);
500 	resp = m->message(m, fp, data_offset, any_eoh_macros, any_eod_macros);
501 	if (any_eoh_macros != global_eoh_macros)
502 	    argv_free(any_eoh_macros);
503 	if (any_eod_macros != global_eod_macros)
504 	    argv_free(any_eod_macros);
505     }
506     if (global_eoh_macros)
507 	argv_free(global_eoh_macros);
508     if (global_eod_macros)
509 	argv_free(global_eod_macros);
510     return (resp);
511 }
512 
513 /* milter_abort - cancel message receiving state, all milters */
514 
515 void    milter_abort(MILTERS *milters)
516 {
517     MILTER *m;
518 
519     if (msg_verbose)
520 	msg_info("abort all milters");
521     for (m = milters->milter_list; m != 0; m = m->next)
522 	m->abort(m);
523 }
524 
525 /* milter_disc_event - report client disconnect event to all milters */
526 
527 void    milter_disc_event(MILTERS *milters)
528 {
529     MILTER *m;
530 
531     if (msg_verbose)
532 	msg_info("disconnect event to all milters");
533     for (m = milters->milter_list; m != 0; m = m->next)
534 	m->disc_event(m);
535 }
536 
537 /* milter_new - create milter list */
538 
539 MILTERS *milter_new(const char *names,
540 		            int conn_timeout,
541 		            int cmd_timeout,
542 		            int msg_timeout,
543 		            const char *protocol,
544 		            const char *def_action,
545 		            MILTER_MACROS *macros)
546 {
547     MILTERS *milters;
548     MILTER *head = 0;
549     MILTER *tail = 0;
550     char   *name;
551     MILTER *milter;
552     const char *sep = ", \t\r\n";
553 
554     /*
555      * Parse the milter list.
556      */
557     milters = (MILTERS *) mymalloc(sizeof(*milters));
558     if (names != 0) {
559 	char   *saved_names = mystrdup(names);
560 	char   *cp = saved_names;
561 
562 	while ((name = mystrtok(&cp, sep)) != 0) {
563 	    milter = milter8_create(name, conn_timeout, cmd_timeout,
564 				    msg_timeout, protocol, def_action,
565 				    milters);
566 	    if (head == 0) {
567 		head = milter;
568 	    } else {
569 		tail->next = milter;
570 	    }
571 	    tail = milter;
572 	}
573 	myfree(saved_names);
574     }
575     milters->milter_list = head;
576     milters->mac_lookup = 0;
577     milters->mac_context = 0;
578     milters->macros = macros;
579     milters->add_header = 0;
580     milters->upd_header = milters->ins_header = 0;
581     milters->del_header = 0;
582     milters->add_rcpt = milters->del_rcpt = 0;
583     milters->repl_body = 0;
584     milters->chg_context = 0;
585     return (milters);
586 }
587 
588 /* milter_free - destroy all milters */
589 
590 void    milter_free(MILTERS *milters)
591 {
592     MILTER *m;
593     MILTER *next;
594 
595     if (msg_verbose)
596 	msg_info("free all milters");
597     for (m = milters->milter_list; m != 0; m = next)
598 	next = m->next, m->free(m);
599     if (milters->macros)
600 	milter_macros_free(milters->macros);
601     myfree((char *) milters);
602 }
603 
604 /* milter_dummy - send empty milter list */
605 
606 int     milter_dummy(MILTERS *milters, VSTREAM *stream)
607 {
608     MILTERS dummy = *milters;
609 
610     dummy.milter_list = 0;
611     return (milter_send(&dummy, stream));
612 }
613 
614 /* milter_send - send Milter instances over stream */
615 
616 int     milter_send(MILTERS *milters, VSTREAM *stream)
617 {
618     MILTER *m;
619     int     status = 0;
620     int     count = 0;
621 
622     /*
623      * XXX Optimization: send only the filters that are actually used in the
624      * remote process. No point sending a filter that looks at HELO commands
625      * to a cleanup server. For now we skip only the filters that are known
626      * to be disabled (either in global error state or in global accept
627      * state).
628      *
629      * XXX We must send *some* information, even when there are no active
630      * filters, otherwise the cleanup server would try to apply its own
631      * non_smtpd_milters settings.
632      */
633     if (milters != 0)
634 	for (m = milters->milter_list; m != 0; m = m->next)
635 	    if (m->active(m))
636 		count++;
637     (void) rec_fprintf(stream, REC_TYPE_MILT_COUNT, "%d", count);
638 
639     /*
640      * XXX Optimization: don't send or receive further information when there
641      * aren't any active filters.
642      */
643     if (count <= 0)
644 	return (0);
645 
646     /*
647      * Send the filter macro name lists.
648      */
649     (void) attr_print(stream, ATTR_FLAG_MORE,
650 		      ATTR_TYPE_FUNC, milter_macros_print,
651 		      (void *) milters->macros,
652 		      ATTR_TYPE_END);
653 
654     /*
655      * Send the filter instances.
656      */
657     for (m = milters->milter_list; m != 0; m = m->next)
658 	if (m->active(m) && (status = m->send(m, stream)) != 0)
659 	    break;
660 
661     /*
662      * Over to you.
663      */
664     if (status != 0
665 	|| attr_scan(stream, ATTR_FLAG_STRICT,
666 		     ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
667 		     ATTR_TYPE_END) != 1
668 	|| status != 0) {
669 	msg_warn("cannot send milters to service %s", VSTREAM_PATH(stream));
670 	return (-1);
671     }
672     return (0);
673 }
674 
675 /* milter_receive - receive milters from stream */
676 
677 MILTERS *milter_receive(VSTREAM *stream, int count)
678 {
679     MILTERS *milters;
680     MILTER *head = 0;
681     MILTER *tail = 0;
682     MILTER *milter = 0;
683 
684     /*
685      * XXX We must instantiate a MILTERS structure even when the sender has
686      * no active filters, otherwise the cleanup server would try to use its
687      * own non_smtpd_milters settings.
688      */
689 #define NO_MILTERS	((char *) 0)
690 #define NO_TIMEOUTS	0, 0, 0
691 #define NO_PROTOCOL	((char *) 0)
692 #define NO_ACTION	((char *) 0)
693 #define NO_MACROS	((MILTER_MACROS *) 0)
694 
695     milters = milter_new(NO_MILTERS, NO_TIMEOUTS, NO_PROTOCOL, NO_ACTION,
696 			 NO_MACROS);
697 
698     /*
699      * XXX Optimization: don't send or receive further information when there
700      * aren't any active filters.
701      */
702     if (count <= 0)
703 	return (milters);
704 
705     /*
706      * Receive the global macro name lists.
707      */
708     milters->macros = milter_macros_alloc(MILTER_MACROS_ALLOC_ZERO);
709     if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
710 		  ATTR_TYPE_FUNC, milter_macros_scan,
711 		  (void *) milters->macros,
712 		  ATTR_TYPE_END) != 1) {
713 	milter_free(milters);
714 	return (0);
715     }
716 
717     /*
718      * Receive the filters.
719      */
720     for (; count > 0; count--) {
721 	if ((milter = milter8_receive(stream, milters)) == 0) {
722 	    msg_warn("cannot receive milters via service %s socket",
723 		     VSTREAM_PATH(stream));
724 	    milter_free(milters);
725 	    return (0);
726 	}
727 	if (head == 0) {
728 	    /* Coverity: milter_free() depends on milters->milter_list. */
729 	    milters->milter_list = head = milter;
730 	} else {
731 	    tail->next = milter;
732 	}
733 	tail = milter;
734     }
735 
736     /*
737      * Over to you.
738      */
739     (void) attr_print(stream, ATTR_FLAG_NONE,
740 		      ATTR_TYPE_INT, MAIL_ATTR_STATUS, 0,
741 		      ATTR_TYPE_END);
742     return (milters);
743 }
744 
745 #ifdef TEST
746 
747  /*
748   * Proof-of-concept test program. This can be used interactively, but is
749   * typically used for automated regression tests from a script.
750   */
751 
752 /* System library. */
753 
754 #include <sys/socket.h>
755 #include <stdlib.h>
756 #include <string.h>
757 
758 /* Utility library. */
759 
760 #include "msg_vstream.h"
761 #include "vstring_vstream.h"
762 
763 /* Global library. */
764 
765 #include <mail_params.h>
766 
767 int     var_milt_conn_time = 10;
768 int     var_milt_cmd_time = 10;
769 int     var_milt_msg_time = 100;
770 char   *var_milt_protocol = DEF_MILT_PROTOCOL;
771 char   *var_milt_def_action = DEF_MILT_DEF_ACTION;
772 
773 static void usage(void)
774 {
775     vstream_fprintf(VSTREAM_ERR, "usage: \n"
776 		    "	create names...		create and connect\n"
777 #if 0
778 		    "	conn_macros names...	define connect macros\n"
779 		    "	helo_macros names...	define helo command macros\n"
780 		    "	mail_macros names...	define mail command macros\n"
781 		    "	rcpt_macros names...	define rcpt command macros\n"
782 		    "	data_macros names...	define data command macros\n"
783 		    "	unk_macros names...	unknown command macros\n"
784 		    "	message_macros names...	define message macros\n"
785 #endif
786 		    "	free			disconnect and destroy\n"
787 		    "	connect name addr port family\n"
788 		    "	helo hostname\n"
789 		    "	ehlo hostname\n"
790 		    "	mail from sender...\n"
791 		    "	rcpt to recipient...\n"
792 		    "	data\n"
793 		    "	disconnect\n"
794 		    "	unknown command\n");
795     vstream_fflush(VSTREAM_ERR);
796 }
797 
798 int     main(int argc, char **argv)
799 {
800     MILTERS *milters = 0;
801     char   *conn_macros, *helo_macros, *mail_macros, *rcpt_macros;
802     char   *data_macros, *eoh_macros, *eod_macros, *unk_macros;
803     VSTRING *inbuf = vstring_alloc(100);
804     char   *bufp;
805     char   *cmd;
806     int     ch;
807     int     istty = isatty(vstream_fileno(VSTREAM_IN));
808 
809     conn_macros = helo_macros = mail_macros = rcpt_macros = data_macros
810 	= eoh_macros = eod_macros = unk_macros = "";
811 
812     msg_vstream_init(argv[0], VSTREAM_ERR);
813     while ((ch = GETOPT(argc, argv, "V:v")) > 0) {
814 	switch (ch) {
815 	default:
816 	    msg_fatal("usage: %s [-a action] [-p protocol] [-v]", argv[0]);
817 	case 'a':
818 	    var_milt_def_action = optarg;
819 	    break;
820 	case 'p':
821 	    var_milt_protocol = optarg;
822 	    break;
823 	case 'v':
824 	    msg_verbose++;
825 	    break;
826 	}
827     }
828     optind = OPTIND;
829 
830     for (;;) {
831 	const char *resp = 0;
832 	ARGV   *argv;
833 	char  **args;
834 
835 	if (istty) {
836 	    vstream_printf("- ");
837 	    vstream_fflush(VSTREAM_OUT);
838 	}
839 	if (vstring_fgets_nonl(inbuf, VSTREAM_IN) <= 0)
840 	    break;
841 	bufp = vstring_str(inbuf);
842 	if (!istty) {
843 	    vstream_printf("> %s\n", bufp);
844 	    vstream_fflush(VSTREAM_OUT);
845 	}
846 	if (*bufp == '#')
847 	    continue;
848 	cmd = mystrtok(&bufp, " ");
849 	if (cmd == 0) {
850 	    usage();
851 	    continue;
852 	}
853 	argv = argv_split(bufp, " ");
854 	args = argv->argv;
855 	if (strcmp(cmd, "create") == 0 && argv->argc == 1) {
856 	    if (milters != 0) {
857 		msg_warn("deleting existing milters");
858 		milter_free(milters);
859 	    }
860 	    milters = milter_create(args[0], var_milt_conn_time,
861 				    var_milt_cmd_time, var_milt_msg_time,
862 				    var_milt_protocol, var_milt_def_action,
863 				    conn_macros, helo_macros, mail_macros,
864 				    rcpt_macros, data_macros, eoh_macros,
865 				    eod_macros, unk_macros);
866 	} else if (strcmp(cmd, "free") == 0 && argv->argc == 0) {
867 	    if (milters == 0) {
868 		msg_warn("no milters");
869 		continue;
870 	    }
871 	    milter_free(milters);
872 	    milters = 0;
873 	} else if (strcmp(cmd, "connect") == 0 && argv->argc == 4) {
874 	    if (milters == 0) {
875 		msg_warn("no milters");
876 		continue;
877 	    }
878 	    resp = milter_conn_event(milters, args[0], args[1], args[2],
879 				 strcmp(args[3], "AF_INET") == 0 ? AF_INET :
880 			       strcmp(args[3], "AF_INET6") == 0 ? AF_INET6 :
881 				 strcmp(args[3], "AF_UNIX") == 0 ? AF_UNIX :
882 				     AF_UNSPEC);
883 	} else if (strcmp(cmd, "helo") == 0 && argv->argc == 1) {
884 	    if (milters == 0) {
885 		msg_warn("no milters");
886 		continue;
887 	    }
888 	    resp = milter_helo_event(milters, args[0], 0);
889 	} else if (strcmp(cmd, "ehlo") == 0 && argv->argc == 1) {
890 	    if (milters == 0) {
891 		msg_warn("no milters");
892 		continue;
893 	    }
894 	    resp = milter_helo_event(milters, args[0], 1);
895 	} else if (strcmp(cmd, "mail") == 0 && argv->argc > 0) {
896 	    if (milters == 0) {
897 		msg_warn("no milters");
898 		continue;
899 	    }
900 	    resp = milter_mail_event(milters, (const char **) args);
901 	} else if (strcmp(cmd, "rcpt") == 0 && argv->argc > 0) {
902 	    if (milters == 0) {
903 		msg_warn("no milters");
904 		continue;
905 	    }
906 	    resp = milter_rcpt_event(milters, 0, (const char **) args);
907 	} else if (strcmp(cmd, "unknown") == 0 && argv->argc > 0) {
908 	    if (milters == 0) {
909 		msg_warn("no milters");
910 		continue;
911 	    }
912 	    resp = milter_unknown_event(milters, args[0]);
913 	} else if (strcmp(cmd, "data") == 0 && argv->argc == 0) {
914 	    if (milters == 0) {
915 		msg_warn("no milters");
916 		continue;
917 	    }
918 	    resp = milter_data_event(milters);
919 	} else if (strcmp(cmd, "disconnect") == 0 && argv->argc == 0) {
920 	    if (milters == 0) {
921 		msg_warn("no milters");
922 		continue;
923 	    }
924 	    milter_disc_event(milters);
925 	} else {
926 	    usage();
927 	}
928 	if (resp != 0)
929 	    msg_info("%s", resp);
930 	argv_free(argv);
931     }
932     if (milters != 0)
933 	milter_free(milters);
934     vstring_free(inbuf);
935     return (0);
936 }
937 
938 #endif
939