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