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