1/*	$NetBSD: bounce.c,v 1.4 2022/10/08 16:12:45 christos Exp $	*/
2
3/*++
4/* NAME
5/*	bounce 8
6/* SUMMARY
7/*	Postfix delivery status reports
8/* SYNOPSIS
9/*	\fBbounce\fR [generic Postfix daemon options]
10/* DESCRIPTION
11/*	The \fBbounce\fR(8) daemon maintains per-message log files with
12/*	delivery status information. Each log file is named after the
13/*	queue file that it corresponds to, and is kept in a queue subdirectory
14/*	named after the service name in the \fBmaster.cf\fR file (either
15/*	\fBbounce\fR, \fBdefer\fR or \fBtrace\fR).
16/*	This program expects to be run from the \fBmaster\fR(8) process
17/*	manager.
18/*
19/*	The \fBbounce\fR(8) daemon processes two types of service requests:
20/* .IP \(bu
21/*	Append a recipient (non-)delivery status record to a per-message
22/*	log file.
23/* .IP \(bu
24/*	Enqueue a delivery status notification message, with a copy
25/*	of a per-message log file and of the corresponding message.
26/*	When the delivery status notification message is
27/*	enqueued successfully, the per-message log file is deleted.
28/* .PP
29/*	The software does a best notification effort. A non-delivery
30/*	notification is sent even when the log file or the original
31/*	message cannot be read.
32/*
33/*	Optionally, a bounce (defer, trace) client can request that the
34/*	per-message log file be deleted when the requested operation fails.
35/*	This is used by clients that cannot retry transactions by
36/*	themselves, and that depend on retry logic in their own client.
37/* STANDARDS
38/*	RFC 822 (ARPA Internet Text Messages)
39/*	RFC 2045 (Format of Internet Message Bodies)
40/*	RFC 2822 (Internet Message Format)
41/*	RFC 3462 (Delivery Status Notifications)
42/*	RFC 3464 (Delivery Status Notifications)
43/*	RFC 3834 (Auto-Submitted: message header)
44/*	RFC 5322 (Internet Message Format)
45/*	RFC 6531 (Internationalized SMTP)
46/*	RFC 6532 (Internationalized Message Format)
47/*	RFC 6533 (Internationalized Delivery Status Notifications)
48/* DIAGNOSTICS
49/*	Problems and transactions are logged to \fBsyslogd\fR(8)
50/*	or \fBpostlogd\fR(8).
51/* CONFIGURATION PARAMETERS
52/* .ad
53/* .fi
54/*	Changes to \fBmain.cf\fR are picked up automatically, as \fBbounce\fR(8)
55/*	processes run for only a limited amount of time. Use the command
56/*	"\fBpostfix reload\fR" to speed up a change.
57/*
58/*	The text below provides only a parameter summary. See
59/*	\fBpostconf\fR(5) for more details including examples.
60/* .IP "\fB2bounce_notice_recipient (postmaster)\fR"
61/*	The recipient of undeliverable mail that cannot be returned to
62/*	the sender.
63/* .IP "\fBbackwards_bounce_logfile_compatibility (yes)\fR"
64/*	Produce additional \fBbounce\fR(8) logfile records that can be read by
65/*	Postfix versions before 2.0.
66/* .IP "\fBbounce_notice_recipient (postmaster)\fR"
67/*	The recipient of postmaster notifications with the message headers
68/*	of mail that Postfix did not deliver and of SMTP conversation
69/*	transcripts of mail that Postfix did not receive.
70/* .IP "\fBbounce_size_limit (50000)\fR"
71/*	The maximal amount of original message text that is sent in a
72/*	non-delivery notification.
73/* .IP "\fBbounce_template_file (empty)\fR"
74/*	Pathname of a configuration file with bounce message templates.
75/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
76/*	The default location of the Postfix main.cf and master.cf
77/*	configuration files.
78/* .IP "\fBdaemon_timeout (18000s)\fR"
79/*	How much time a Postfix daemon process may take to handle a
80/*	request before it is terminated by a built-in watchdog timer.
81/* .IP "\fBdelay_notice_recipient (postmaster)\fR"
82/*	The recipient of postmaster notifications with the message headers
83/*	of mail that cannot be delivered within $delay_warning_time time
84/*	units.
85/* .IP "\fBdeliver_lock_attempts (20)\fR"
86/*	The maximal number of attempts to acquire an exclusive lock on a
87/*	mailbox file or \fBbounce\fR(8) logfile.
88/* .IP "\fBdeliver_lock_delay (1s)\fR"
89/*	The time between attempts to acquire an exclusive lock on a mailbox
90/*	file or \fBbounce\fR(8) logfile.
91/* .IP "\fBipc_timeout (3600s)\fR"
92/*	The time limit for sending or receiving information over an internal
93/*	communication channel.
94/* .IP "\fBinternal_mail_filter_classes (empty)\fR"
95/*	What categories of Postfix-generated mail are subject to
96/*	before-queue content inspection by non_smtpd_milters, header_checks
97/*	and body_checks.
98/* .IP "\fBmail_name (Postfix)\fR"
99/*	The mail system name that is displayed in Received: headers, in
100/*	the SMTP greeting banner, and in bounced mail.
101/* .IP "\fBmax_idle (100s)\fR"
102/*	The maximum amount of time that an idle Postfix daemon process waits
103/*	for an incoming connection before terminating voluntarily.
104/* .IP "\fBmax_use (100)\fR"
105/*	The maximal number of incoming connections that a Postfix daemon
106/*	process will service before terminating voluntarily.
107/* .IP "\fBnotify_classes (resource, software)\fR"
108/*	The list of error classes that are reported to the postmaster.
109/* .IP "\fBprocess_id (read-only)\fR"
110/*	The process ID of a Postfix command or daemon process.
111/* .IP "\fBprocess_name (read-only)\fR"
112/*	The process name of a Postfix command or daemon process.
113/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
114/*	The location of the Postfix top-level queue directory.
115/* .IP "\fBsyslog_facility (mail)\fR"
116/*	The syslog facility of Postfix logging.
117/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
118/*	A prefix that is prepended to the process name in syslog
119/*	records, so that, for example, "smtpd" becomes "prefix/smtpd".
120/* .PP
121/*	Available in Postfix 3.0 and later:
122/* .IP "\fBsmtputf8_autodetect_classes (sendmail, verify)\fR"
123/*	Detect that a message requires SMTPUTF8 support for the specified
124/*	mail origin classes.
125/* .PP
126/*	Available in Postfix 3.3 and later:
127/* .IP "\fBservice_name (read-only)\fR"
128/*	The master.cf service name of a Postfix daemon process.
129/* .PP
130/*	Available in Postfix 3.6 and later:
131/* .IP "\fBenable_threaded_bounces (no)\fR"
132/*	Enable non-delivery, success, and delay notifications that link
133/*	to the original message by including a References: and In-Reply-To:
134/*	header with the original Message-ID value.
135/* .PP
136/*	Available in Postfix 3.7 and later:
137/* .IP "\fBheader_from_format (standard)\fR"
138/*	The format of the Postfix-generated \fBFrom:\fR header.
139/* FILES
140/*	/var/spool/postfix/bounce/* non-delivery records
141/*	/var/spool/postfix/defer/* non-delivery records
142/*	/var/spool/postfix/trace/* delivery status records
143/* SEE ALSO
144/*	bounce(5), bounce message template format
145/*	qmgr(8), queue manager
146/*	postconf(5), configuration parameters
147/*	master(5), generic daemon options
148/*	master(8), process manager
149/*	postlogd(8), Postfix logging
150/*	syslogd(8), system logging
151/* LICENSE
152/* .ad
153/* .fi
154/*	The Secure Mailer license must be distributed with this software.
155/* AUTHOR(S)
156/*	Wietse Venema
157/*	IBM T.J. Watson Research
158/*	P.O. Box 704
159/*	Yorktown Heights, NY 10598, USA
160/*
161/*	Wietse Venema
162/*	Google, Inc.
163/*	111 8th Avenue
164/*	New York, NY 10011, USA
165/*--*/
166
167/* System library. */
168
169#include <sys_defs.h>
170#include <string.h>
171#include <stdlib.h>
172
173/* Utility library. */
174
175#include <msg.h>
176#include <vstring.h>
177#include <vstream.h>
178#include <stringops.h>
179#include <load_file.h>
180
181/* Global library. */
182
183#include <mail_proto.h>
184#include <mail_queue.h>
185#include <mail_params.h>
186#include <mail_version.h>
187#include <mail_conf.h>
188#include <bounce.h>
189#include <mail_addr.h>
190#include <rcpt_buf.h>
191#include <dsb_scan.h>
192#include <hfrom_format.h>
193
194/* Single-threaded server skeleton. */
195
196#include <mail_server.h>
197
198/* Application-specific. */
199
200#include <bounce_service.h>
201
202 /*
203  * Tunables.
204  */
205int     var_bounce_limit;
206int     var_max_queue_time;
207int     var_delay_warn_time;
208char   *var_notify_classes;
209char   *var_bounce_rcpt;
210char   *var_2bounce_rcpt;
211char   *var_delay_rcpt;
212char   *var_bounce_tmpl;
213bool    var_threaded_bounce;
214char   *var_hfrom_format;		/* header_from_format */
215
216 /*
217  * We're single threaded, so we can avoid some memory allocation overhead.
218  */
219static VSTRING *queue_id;
220static VSTRING *queue_name;
221static RCPT_BUF *rcpt_buf;
222static VSTRING *encoding;
223static VSTRING *sender;
224static VSTRING *dsn_envid;
225static VSTRING *verp_delims;
226static DSN_BUF *dsn_buf;
227
228 /*
229  * Templates.
230  */
231BOUNCE_TEMPLATES *bounce_templates;
232
233 /*
234  * From: header format.
235  */
236int     bounce_hfrom_format;
237
238#define STR vstring_str
239
240#define VS_NEUTER(s) printable(vstring_str(s), '?')
241
242/* bounce_append_proto - bounce_append server protocol */
243
244static int bounce_append_proto(char *service_name, VSTREAM *client)
245{
246    const char *myname = "bounce_append_proto";
247    int     flags;
248
249    /*
250     * Read and validate the client request.
251     */
252    if (mail_command_server(client,
253			    RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
254			    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
255			    RECV_ATTR_FUNC(rcpb_scan, (void *) rcpt_buf),
256			    RECV_ATTR_FUNC(dsb_scan, (void *) dsn_buf),
257			    ATTR_TYPE_END) != 4) {
258	msg_warn("malformed request");
259	return (-1);
260    }
261
262    /*
263     * Sanitize input.
264     */
265    if (mail_queue_id_ok(STR(queue_id)) == 0) {
266	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
267	return (-1);
268    }
269    VS_NEUTER(rcpt_buf->address);
270    VS_NEUTER(rcpt_buf->orig_addr);
271    VS_NEUTER(rcpt_buf->dsn_orcpt);
272    VS_NEUTER(dsn_buf->status);
273    VS_NEUTER(dsn_buf->action);
274    VS_NEUTER(dsn_buf->reason);
275    VS_NEUTER(dsn_buf->dtype);
276    VS_NEUTER(dsn_buf->dtext);
277    VS_NEUTER(dsn_buf->mtype);
278    VS_NEUTER(dsn_buf->mname);
279    (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
280    (void) DSN_FROM_DSN_BUF(dsn_buf);
281
282    /*
283     * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and
284     * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and
285     * RECIPIENT_FROM_RCPT_BUF().
286     */
287    if (msg_verbose)
288	msg_info("%s: flags=0x%x service=%s id=%s org_to=%s to=%s off=%ld dsn_org=%s, notif=0x%x stat=%s act=%s why=%s",
289		 myname, flags, service_name, STR(queue_id),
290		 STR(rcpt_buf->orig_addr), STR(rcpt_buf->address),
291		 rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt),
292		 rcpt_buf->dsn_notify, STR(dsn_buf->status),
293		 STR(dsn_buf->action), STR(dsn_buf->reason));
294
295    /*
296     * On request by the client, set up a trap to delete the log file in case
297     * of errors.
298     */
299    if (flags & BOUNCE_FLAG_CLEAN)
300	bounce_cleanup_register(service_name, STR(queue_id));
301
302    /*
303     * Execute the request.
304     */
305    return (bounce_append_service(flags, service_name, STR(queue_id),
306				  &rcpt_buf->rcpt, &dsn_buf->dsn));
307}
308
309/* bounce_notify_proto - bounce_notify server protocol */
310
311static int bounce_notify_proto(char *service_name, VSTREAM *client,
312			        int (*service) (int, char *, char *, char *,
313				           char *, int, char *, char *, int,
314						        BOUNCE_TEMPLATES *))
315{
316    const char *myname = "bounce_notify_proto";
317    int     flags;
318    int     smtputf8;
319    int     dsn_ret;
320
321    /*
322     * Read and validate the client request.
323     */
324    if (mail_command_server(client,
325			    RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
326			    RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name),
327			    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
328			    RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
329			    RECV_ATTR_INT(MAIL_ATTR_SMTPUTF8, &smtputf8),
330			    RECV_ATTR_STR(MAIL_ATTR_SENDER, sender),
331			    RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
332			    RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret),
333			    ATTR_TYPE_END) != 8) {
334	msg_warn("malformed request");
335	return (-1);
336    }
337
338    /*
339     * Sanitize input.
340     */
341    if (mail_queue_name_ok(STR(queue_name)) == 0) {
342	msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
343	return (-1);
344    }
345    if (mail_queue_id_ok(STR(queue_id)) == 0) {
346	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
347	return (-1);
348    }
349    VS_NEUTER(encoding);
350    VS_NEUTER(sender);
351    VS_NEUTER(dsn_envid);
352    if (msg_verbose)
353	msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s smtputf8=%d sender=%s envid=%s ret=0x%x",
354		 myname, flags, service_name, STR(queue_name), STR(queue_id),
355		 STR(encoding), smtputf8, STR(sender), STR(dsn_envid),
356		 dsn_ret);
357
358    /*
359     * On request by the client, set up a trap to delete the log file in case
360     * of errors.
361     */
362    if (flags & BOUNCE_FLAG_CLEAN)
363	bounce_cleanup_register(service_name, STR(queue_id));
364
365    /*
366     * Execute the request.
367     */
368    return (service(flags, service_name, STR(queue_name),
369		    STR(queue_id), STR(encoding), smtputf8,
370		    STR(sender), STR(dsn_envid), dsn_ret,
371		    bounce_templates));
372}
373
374/* bounce_verp_proto - bounce_notify server protocol, VERP style */
375
376static int bounce_verp_proto(char *service_name, VSTREAM *client)
377{
378    const char *myname = "bounce_verp_proto";
379    int     flags;
380    int     smtputf8;
381    int     dsn_ret;
382
383    /*
384     * Read and validate the client request.
385     */
386    if (mail_command_server(client,
387			    RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
388			    RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name),
389			    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
390			    RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
391			    RECV_ATTR_INT(MAIL_ATTR_SMTPUTF8, &smtputf8),
392			    RECV_ATTR_STR(MAIL_ATTR_SENDER, sender),
393			    RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
394			    RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret),
395			    RECV_ATTR_STR(MAIL_ATTR_VERPDL, verp_delims),
396			    ATTR_TYPE_END) != 9) {
397	msg_warn("malformed request");
398	return (-1);
399    }
400
401    /*
402     * Sanitize input.
403     */
404    if (mail_queue_name_ok(STR(queue_name)) == 0) {
405	msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
406	return (-1);
407    }
408    if (mail_queue_id_ok(STR(queue_id)) == 0) {
409	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
410	return (-1);
411    }
412    VS_NEUTER(encoding);
413    VS_NEUTER(sender);
414    VS_NEUTER(dsn_envid);
415    VS_NEUTER(verp_delims);
416    if (strlen(STR(verp_delims)) != 2) {
417	msg_warn("malformed verp delimiter string: %s", STR(verp_delims));
418	return (-1);
419    }
420    if (msg_verbose)
421	msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s smtputf8=%d sender=%s envid=%s ret=0x%x delim=%s",
422		 myname, flags, service_name, STR(queue_name),
423		 STR(queue_id), STR(encoding), smtputf8, STR(sender),
424		 STR(dsn_envid), dsn_ret, STR(verp_delims));
425
426    /*
427     * On request by the client, set up a trap to delete the log file in case
428     * of errors.
429     */
430    if (flags & BOUNCE_FLAG_CLEAN)
431	bounce_cleanup_register(service_name, STR(queue_id));
432
433    /*
434     * Execute the request. Fall back to traditional notification if a bounce
435     * was returned as undeliverable, because we don't want to VERPify those.
436     */
437    if (!*STR(sender) || !strcasecmp_utf8(STR(sender),
438					  mail_addr_double_bounce())) {
439	msg_warn("request to send VERP-style notification of bounced mail");
440	return (bounce_notify_service(flags, service_name, STR(queue_name),
441				      STR(queue_id), STR(encoding), smtputf8,
442				      STR(sender), STR(dsn_envid), dsn_ret,
443				      bounce_templates));
444    } else
445	return (bounce_notify_verp(flags, service_name, STR(queue_name),
446				   STR(queue_id), STR(encoding), smtputf8,
447				   STR(sender), STR(dsn_envid), dsn_ret,
448				   STR(verp_delims), bounce_templates));
449}
450
451/* bounce_one_proto - bounce_one server protocol */
452
453static int bounce_one_proto(char *service_name, VSTREAM *client)
454{
455    const char *myname = "bounce_one_proto";
456    int     flags;
457    int     smtputf8;
458    int     dsn_ret;
459
460    /*
461     * Read and validate the client request.
462     */
463    if (mail_command_server(client,
464			    RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
465			    RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name),
466			    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
467			    RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
468			    RECV_ATTR_INT(MAIL_ATTR_SMTPUTF8, &smtputf8),
469			    RECV_ATTR_STR(MAIL_ATTR_SENDER, sender),
470			    RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
471			    RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret),
472			    RECV_ATTR_FUNC(rcpb_scan, (void *) rcpt_buf),
473			    RECV_ATTR_FUNC(dsb_scan, (void *) dsn_buf),
474			    ATTR_TYPE_END) != 10) {
475	msg_warn("malformed request");
476	return (-1);
477    }
478
479    /*
480     * Sanitize input.
481     */
482    if (strcmp(service_name, MAIL_SERVICE_BOUNCE) != 0) {
483	msg_warn("wrong service name \"%s\" for one-recipient bouncing",
484		 service_name);
485	return (-1);
486    }
487    if (mail_queue_name_ok(STR(queue_name)) == 0) {
488	msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
489	return (-1);
490    }
491    if (mail_queue_id_ok(STR(queue_id)) == 0) {
492	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
493	return (-1);
494    }
495    VS_NEUTER(encoding);
496    VS_NEUTER(sender);
497    VS_NEUTER(dsn_envid);
498    VS_NEUTER(rcpt_buf->address);
499    VS_NEUTER(rcpt_buf->orig_addr);
500    VS_NEUTER(rcpt_buf->dsn_orcpt);
501    VS_NEUTER(dsn_buf->status);
502    VS_NEUTER(dsn_buf->action);
503    VS_NEUTER(dsn_buf->reason);
504    VS_NEUTER(dsn_buf->dtype);
505    VS_NEUTER(dsn_buf->dtext);
506    VS_NEUTER(dsn_buf->mtype);
507    VS_NEUTER(dsn_buf->mname);
508    (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
509    (void) DSN_FROM_DSN_BUF(dsn_buf);
510
511    /*
512     * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and
513     * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and
514     * RECIPIENT_FROM_RCPT_BUF().
515     */
516    if (msg_verbose)
517	msg_info("%s: flags=0x%x queue=%s id=%s encoding=%s smtputf8=%d sender=%s envid=%s dsn_ret=0x%x orig_to=%s to=%s off=%ld dsn_orig=%s notif=0x%x stat=%s act=%s why=%s",
518		 myname, flags, STR(queue_name), STR(queue_id),
519		 STR(encoding), smtputf8, STR(sender), STR(dsn_envid),
520		 dsn_ret, STR(rcpt_buf->orig_addr), STR(rcpt_buf->address),
521		 rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt),
522		 rcpt_buf->dsn_notify, STR(dsn_buf->status),
523		 STR(dsn_buf->action), STR(dsn_buf->reason));
524
525    /*
526     * Execute the request.
527     */
528    return (bounce_one_service(flags, STR(queue_name), STR(queue_id),
529			       STR(encoding), smtputf8, STR(sender),
530			       STR(dsn_envid), dsn_ret, rcpt_buf,
531			       dsn_buf, bounce_templates));
532}
533
534/* bounce_service - parse bounce command type and delegate */
535
536static void bounce_service(VSTREAM *client, char *service_name, char **argv)
537{
538    int     command;
539    int     status;
540
541    /*
542     * Sanity check. This service takes no command-line arguments. The
543     * service name should be usable as a subdirectory name.
544     */
545    if (argv[0])
546	msg_fatal("unexpected command-line argument: %s", argv[0]);
547    if (mail_queue_name_ok(service_name) == 0)
548	msg_fatal("malformed service name: %s", service_name);
549
550    /*
551     * Announce the protocol.
552     */
553    attr_print(client, ATTR_FLAG_NONE,
554	       SEND_ATTR_STR(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_BOUNCE),
555	       ATTR_TYPE_END);
556    (void) vstream_fflush(client);
557
558    /*
559     * Read and validate the first parameter of the client request. Let the
560     * request-specific protocol routines take care of the remainder.
561     */
562    if (attr_scan(client, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
563		  RECV_ATTR_INT(MAIL_ATTR_NREQ, &command), 0) != 1) {
564	msg_warn("malformed request");
565	status = -1;
566    } else if (command == BOUNCE_CMD_VERP) {
567	status = bounce_verp_proto(service_name, client);
568    } else if (command == BOUNCE_CMD_FLUSH) {
569	status = bounce_notify_proto(service_name, client,
570				     bounce_notify_service);
571    } else if (command == BOUNCE_CMD_WARN) {
572	status = bounce_notify_proto(service_name, client,
573				     bounce_warn_service);
574    } else if (command == BOUNCE_CMD_TRACE) {
575	status = bounce_notify_proto(service_name, client,
576				     bounce_trace_service);
577    } else if (command == BOUNCE_CMD_APPEND) {
578	status = bounce_append_proto(service_name, client);
579    } else if (command == BOUNCE_CMD_ONE) {
580	status = bounce_one_proto(service_name, client);
581    } else {
582	msg_warn("unknown command: %d", command);
583	status = -1;
584    }
585
586    /*
587     * When the request has completed, send the completion status to the
588     * client.
589     */
590    attr_print(client, ATTR_FLAG_NONE,
591	       SEND_ATTR_INT(MAIL_ATTR_STATUS, status),
592	       ATTR_TYPE_END);
593    vstream_fflush(client);
594
595    /*
596     * When a cleanup trap was set, delete the log file in case of error.
597     * This includes errors while sending the completion status to the
598     * client.
599     */
600    if (bounce_cleanup_path) {
601	if (status || vstream_ferror(client))
602	    bounce_cleanup_log();
603	bounce_cleanup_unregister();
604    }
605}
606
607static void load_helper(VSTREAM *stream, void *context)
608{
609    BOUNCE_TEMPLATES *templates = (BOUNCE_TEMPLATES *) context;
610
611    bounce_templates_load(stream, templates);
612}
613
614/* pre_jail_init - pre-jail initialization */
615
616static void pre_jail_init(char *unused_name, char **unused_argv)
617{
618
619    /*
620     * Bundle up a bunch of bounce template information.
621     */
622    bounce_templates = bounce_templates_create();
623
624    /*
625     * Load the alternate message files (if specified) before entering the
626     * chroot jail.
627     */
628    if (*var_bounce_tmpl)
629	load_file(var_bounce_tmpl, load_helper, (void *) bounce_templates);
630}
631
632/* post_jail_init - initialize after entering chroot jail */
633
634static void post_jail_init(char *service_name, char **unused_argv)
635{
636    bounce_hfrom_format = hfrom_format_parse(VAR_HFROM_FORMAT, var_hfrom_format);
637
638    /*
639     * Special case: dump bounce templates. This is not part of the master(5)
640     * public interface. This internal interface is used by the postconf
641     * command. It was implemented before bounce templates were isolated into
642     * modules that could have been called directly.
643     */
644    if (strcmp(service_name, "dump_templates") == 0) {
645	bounce_templates_dump(VSTREAM_OUT, bounce_templates);
646	vstream_fflush(VSTREAM_OUT);
647	exit(0);
648    }
649    if (strcmp(service_name, "expand_templates") == 0) {
650	bounce_templates_expand(VSTREAM_OUT, bounce_templates);
651	vstream_fflush(VSTREAM_OUT);
652	exit(0);
653    }
654
655    /*
656     * Initialize. We're single threaded so we can reuse some memory upon
657     * successive requests.
658     */
659    queue_id = vstring_alloc(10);
660    queue_name = vstring_alloc(10);
661    rcpt_buf = rcpb_create();
662    encoding = vstring_alloc(10);
663    sender = vstring_alloc(10);
664    dsn_envid = vstring_alloc(10);
665    verp_delims = vstring_alloc(10);
666    dsn_buf = dsb_create();
667}
668
669MAIL_VERSION_STAMP_DECLARE;
670
671/* main - the main program */
672
673int     main(int argc, char **argv)
674{
675    static const CONFIG_INT_TABLE int_table[] = {
676	VAR_BOUNCE_LIMIT, DEF_BOUNCE_LIMIT, &var_bounce_limit, 1, 0,
677	0,
678    };
679    static const CONFIG_TIME_TABLE time_table[] = {
680	VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 0, 8640000,
681	VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0,
682	0,
683    };
684    static const CONFIG_STR_TABLE str_table[] = {
685	VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
686	VAR_BOUNCE_RCPT, DEF_BOUNCE_RCPT, &var_bounce_rcpt, 1, 0,
687	VAR_2BOUNCE_RCPT, DEF_2BOUNCE_RCPT, &var_2bounce_rcpt, 1, 0,
688	VAR_DELAY_RCPT, DEF_DELAY_RCPT, &var_delay_rcpt, 1, 0,
689	VAR_BOUNCE_TMPL, DEF_BOUNCE_TMPL, &var_bounce_tmpl, 0, 0,
690	VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0,
691	0,
692    };
693    static const CONFIG_NBOOL_TABLE nbool_table[] = {
694	VAR_THREADED_BOUNCE, DEF_THREADED_BOUNCE, &var_threaded_bounce,
695	0,
696    };
697
698    /*
699     * Fingerprint executables and core dumps.
700     */
701    MAIL_VERSION_STAMP_ALLOCATE;
702
703    /*
704     * Pass control to the single-threaded service skeleton.
705     */
706    single_server_main(argc, argv, bounce_service,
707		       CA_MAIL_SERVER_INT_TABLE(int_table),
708		       CA_MAIL_SERVER_STR_TABLE(str_table),
709		       CA_MAIL_SERVER_TIME_TABLE(time_table),
710		       CA_MAIL_SERVER_NBOOL_TABLE(nbool_table),
711		       CA_MAIL_SERVER_PRE_INIT(pre_jail_init),
712		       CA_MAIL_SERVER_POST_INIT(post_jail_init),
713		       CA_MAIL_SERVER_UNLIMITED,
714		       0);
715}
716