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