Home | History | Annotate | Line # | Download | only in bounce
      1 /*	$NetBSD: bounce.c,v 1.6 2026/05/09 18:49:14 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 /* .PP
    140 /*	Available in Postfix 3.10 and later:
    141 /* .IP "\fBtls_required_enable (yes)\fR"
    142 /*	Enable support for the "TLS-Required: no" message header, defined
    143 /*	in RFC 8689.
    144 /* .PP
    145 /*	Available in Postfix 3.11 and later:
    146 /* .IP "\fBrequiretls_redact_dsn (yes)\fR"
    147 /*	When sending a delivery status notification for an original
    148 /*	message received with the REQUIRETLS option, do not send the original
    149 /*	message body (as if that message was received with "RET=HDRS") and
    150 /*	do not enforce REQUIRETLS (as if that message was received without
    151 /*	REQUIRETLS).
    152 /* FILES
    153 /*	/var/spool/postfix/bounce/* non-delivery records
    154 /*	/var/spool/postfix/defer/* non-delivery records
    155 /*	/var/spool/postfix/trace/* delivery status records
    156 /* SEE ALSO
    157 /*	bounce(5), bounce message template format
    158 /*	qmgr(8), queue manager
    159 /*	postconf(5), configuration parameters
    160 /*	master(5), generic daemon options
    161 /*	master(8), process manager
    162 /*	postlogd(8), Postfix logging
    163 /*	syslogd(8), system logging
    164 /* LICENSE
    165 /* .ad
    166 /* .fi
    167 /*	The Secure Mailer license must be distributed with this software.
    168 /* AUTHOR(S)
    169 /*	Wietse Venema
    170 /*	IBM T.J. Watson Research
    171 /*	P.O. Box 704
    172 /*	Yorktown Heights, NY 10598, USA
    173 /*
    174 /*	Wietse Venema
    175 /*	Google, Inc.
    176 /*	111 8th Avenue
    177 /*	New York, NY 10011, USA
    178 /*
    179 /*	Wietse Venema
    180 /*	porcupine.org
    181 /*--*/
    182 
    183 /* System library. */
    184 
    185 #include <sys_defs.h>
    186 #include <string.h>
    187 #include <stdlib.h>
    188 
    189 /* Utility library. */
    190 
    191 #include <msg.h>
    192 #include <vstring.h>
    193 #include <vstream.h>
    194 #include <stringops.h>
    195 #include <load_file.h>
    196 
    197 /* Global library. */
    198 
    199 #include <mail_proto.h>
    200 #include <mail_queue.h>
    201 #include <mail_params.h>
    202 #include <mail_version.h>
    203 #include <mail_conf.h>
    204 #include <bounce.h>
    205 #include <mail_addr.h>
    206 #include <rcpt_buf.h>
    207 #include <dsb_scan.h>
    208 #include <hfrom_format.h>
    209 #include <sendopts.h>
    210 #include <dsn_mask.h>
    211 
    212 /* Single-threaded server skeleton. */
    213 
    214 #include <mail_server.h>
    215 
    216 /* Application-specific. */
    217 
    218 #include <bounce_service.h>
    219 
    220  /*
    221   * Tunables.
    222   */
    223 int     var_bounce_limit;
    224 int     var_max_queue_time;
    225 int     var_delay_warn_time;
    226 char   *var_notify_classes;
    227 char   *var_bounce_rcpt;
    228 char   *var_2bounce_rcpt;
    229 char   *var_delay_rcpt;
    230 char   *var_bounce_tmpl;
    231 bool    var_threaded_bounce;
    232 char   *var_hfrom_format;		/* header_from_format */
    233 bool    var_reqtls_redact_dsn;
    234 
    235  /*
    236   * We're single threaded, so we can avoid some memory allocation overhead.
    237   */
    238 static VSTRING *queue_id;
    239 static VSTRING *queue_name;
    240 static RCPT_BUF *rcpt_buf;
    241 static VSTRING *encoding;
    242 static VSTRING *sender;
    243 static VSTRING *dsn_envid;
    244 static VSTRING *verp_delims;
    245 static DSN_BUF *dsn_buf;
    246 
    247  /*
    248   * Templates.
    249   */
    250 BOUNCE_TEMPLATES *bounce_templates;
    251 
    252  /*
    253   * From: header format.
    254   */
    255 int     bounce_hfrom_format;
    256 
    257 #define STR vstring_str
    258 
    259 #define VS_NEUTER(s) printable(vstring_str(s), '?')
    260 
    261 /* bounce_append_proto - bounce_append server protocol */
    262 
    263 static int bounce_append_proto(char *service_name, VSTREAM *client)
    264 {
    265     const char *myname = "bounce_append_proto";
    266     int     flags;
    267 
    268     /*
    269      * Read and validate the client request.
    270      */
    271     if (mail_command_server(client,
    272 			    RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
    273 			    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
    274 			    RECV_ATTR_FUNC(rcpb_scan, (void *) rcpt_buf),
    275 			    RECV_ATTR_FUNC(dsb_scan, (void *) dsn_buf),
    276 			    ATTR_TYPE_END) != 4) {
    277 	msg_warn("malformed request");
    278 	return (-1);
    279     }
    280 
    281     /*
    282      * Sanitize input.
    283      */
    284     if (mail_queue_id_ok(STR(queue_id)) == 0) {
    285 	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
    286 	return (-1);
    287     }
    288     VS_NEUTER(rcpt_buf->address);
    289     VS_NEUTER(rcpt_buf->orig_addr);
    290     VS_NEUTER(rcpt_buf->dsn_orcpt);
    291     VS_NEUTER(dsn_buf->status);
    292     VS_NEUTER(dsn_buf->action);
    293     VS_NEUTER(dsn_buf->reason);
    294     VS_NEUTER(dsn_buf->dtype);
    295     VS_NEUTER(dsn_buf->dtext);
    296     VS_NEUTER(dsn_buf->mtype);
    297     VS_NEUTER(dsn_buf->mname);
    298     (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
    299     (void) DSN_FROM_DSN_BUF(dsn_buf);
    300 
    301     /*
    302      * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and
    303      * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and
    304      * RECIPIENT_FROM_RCPT_BUF().
    305      */
    306     if (msg_verbose)
    307 	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",
    308 		 myname, flags, service_name, STR(queue_id),
    309 		 STR(rcpt_buf->orig_addr), STR(rcpt_buf->address),
    310 		 rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt),
    311 		 rcpt_buf->dsn_notify, STR(dsn_buf->status),
    312 		 STR(dsn_buf->action), STR(dsn_buf->reason));
    313 
    314     /*
    315      * On request by the client, set up a trap to delete the log file in case
    316      * of errors.
    317      */
    318     if (flags & BOUNCE_FLAG_CLEAN)
    319 	bounce_cleanup_register(service_name, STR(queue_id));
    320 
    321     /*
    322      * Execute the request.
    323      */
    324     return (bounce_append_service(flags, service_name, STR(queue_id),
    325 				  &rcpt_buf->rcpt, &dsn_buf->dsn));
    326 }
    327 
    328 /* edit_notification_properties - bounce message filter */
    329 
    330 static void edit_notification_properties(int *sendopts, int *dsn_ret)
    331 {
    332 
    333     /*
    334      * If REQUIRETLS is requested, do not propagate "TLS-Required: no". See
    335      * also down-stream code in bounce_header().
    336      */
    337     if (*sendopts & SOPT_REQUIRETLS_ESMTP)
    338 	*sendopts &= ~SOPT_REQUIRETLS_HEADER;
    339 
    340     /*
    341      * Redact delivery status notification for REQUIRETLS message.
    342      */
    343     if (var_reqtls_redact_dsn && (*sendopts & SOPT_REQUIRETLS_ESMTP)) {
    344 	*sendopts &= ~SOPT_REQUIRETLS_ESMTP;
    345 	*dsn_ret = DSN_RET_HDRS;
    346     }
    347 }
    348 
    349 /* bounce_notify_proto - bounce_notify server protocol */
    350 
    351 static int bounce_notify_proto(char *service_name, VSTREAM *client,
    352 			        int (*service) (int, char *, char *, char *,
    353 				           char *, int, char *, char *, int,
    354 						        BOUNCE_TEMPLATES *))
    355 {
    356     const char *myname = "bounce_notify_proto";
    357     int     flags;
    358     int     sendopts;
    359     int     dsn_ret;
    360 
    361     /*
    362      * Read and validate the client request.
    363      */
    364     if (mail_command_server(client,
    365 			    RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
    366 			    RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name),
    367 			    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
    368 			    RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
    369 			    RECV_ATTR_INT(MAIL_ATTR_SENDOPTS, &sendopts),
    370 			    RECV_ATTR_STR(MAIL_ATTR_SENDER, sender),
    371 			    RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
    372 			    RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret),
    373 			    ATTR_TYPE_END) != 8) {
    374 	msg_warn("malformed request");
    375 	return (-1);
    376     }
    377 
    378     /*
    379      * Sanitize input.
    380      */
    381     if (mail_queue_name_ok(STR(queue_name)) == 0) {
    382 	msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
    383 	return (-1);
    384     }
    385     if (mail_queue_id_ok(STR(queue_id)) == 0) {
    386 	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
    387 	return (-1);
    388     }
    389     VS_NEUTER(encoding);
    390     VS_NEUTER(sender);
    391     VS_NEUTER(dsn_envid);
    392     if (msg_verbose)
    393 	msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sendopts=%d sender=%s envid=%s ret=0x%x",
    394 		 myname, flags, service_name, STR(queue_name), STR(queue_id),
    395 		 STR(encoding), sendopts, STR(sender), STR(dsn_envid),
    396 		 dsn_ret);
    397 
    398     /*
    399      * On request by the client, set up a trap to delete the log file in case
    400      * of errors.
    401      */
    402     if (flags & BOUNCE_FLAG_CLEAN)
    403 	bounce_cleanup_register(service_name, STR(queue_id));
    404 
    405     /*
    406      * Handle REQUIRETLS etc. matters.
    407      */
    408     if (var_reqtls_enable && (sendopts & SOPT_REQUIRETLS_ALL))
    409 	edit_notification_properties(&sendopts, &dsn_ret);
    410 
    411     /*
    412      * Execute the request.
    413      */
    414     return (service(flags, service_name, STR(queue_name),
    415 		    STR(queue_id), STR(encoding), sendopts,
    416 		    STR(sender), STR(dsn_envid), dsn_ret,
    417 		    bounce_templates));
    418 }
    419 
    420 /* bounce_verp_proto - bounce_notify server protocol, VERP style */
    421 
    422 static int bounce_verp_proto(char *service_name, VSTREAM *client)
    423 {
    424     const char *myname = "bounce_verp_proto";
    425     int     flags;
    426     int     sendopts;
    427     int     dsn_ret;
    428 
    429     /*
    430      * Read and validate the client request.
    431      */
    432     if (mail_command_server(client,
    433 			    RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
    434 			    RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name),
    435 			    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
    436 			    RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
    437 			    RECV_ATTR_INT(MAIL_ATTR_SENDOPTS, &sendopts),
    438 			    RECV_ATTR_STR(MAIL_ATTR_SENDER, sender),
    439 			    RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
    440 			    RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret),
    441 			    RECV_ATTR_STR(MAIL_ATTR_VERPDL, verp_delims),
    442 			    ATTR_TYPE_END) != 9) {
    443 	msg_warn("malformed request");
    444 	return (-1);
    445     }
    446 
    447     /*
    448      * Sanitize input.
    449      */
    450     if (mail_queue_name_ok(STR(queue_name)) == 0) {
    451 	msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
    452 	return (-1);
    453     }
    454     if (mail_queue_id_ok(STR(queue_id)) == 0) {
    455 	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
    456 	return (-1);
    457     }
    458     VS_NEUTER(encoding);
    459     VS_NEUTER(sender);
    460     VS_NEUTER(dsn_envid);
    461     VS_NEUTER(verp_delims);
    462     if (strlen(STR(verp_delims)) != 2) {
    463 	msg_warn("malformed verp delimiter string: %s", STR(verp_delims));
    464 	return (-1);
    465     }
    466     if (msg_verbose)
    467 	msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sendopts=%d sender=%s envid=%s ret=0x%x delim=%s",
    468 		 myname, flags, service_name, STR(queue_name),
    469 		 STR(queue_id), STR(encoding), sendopts, STR(sender),
    470 		 STR(dsn_envid), dsn_ret, STR(verp_delims));
    471 
    472     /*
    473      * On request by the client, set up a trap to delete the log file in case
    474      * of errors.
    475      */
    476     if (flags & BOUNCE_FLAG_CLEAN)
    477 	bounce_cleanup_register(service_name, STR(queue_id));
    478 
    479     /*
    480      * Handle REQUIRETLS etc. matters.
    481      */
    482     if (var_reqtls_enable && (sendopts & SOPT_REQUIRETLS_ALL))
    483 	edit_notification_properties(&sendopts, &dsn_ret);
    484 
    485     /*
    486      * Execute the request. Fall back to traditional notification if a bounce
    487      * was returned as undeliverable, because we don't want to VERPify those.
    488      */
    489     if (!*STR(sender) || !strcasecmp_utf8(STR(sender),
    490 					  mail_addr_double_bounce())) {
    491 	msg_warn("request to send VERP-style notification of bounced mail");
    492 	return (bounce_notify_service(flags, service_name, STR(queue_name),
    493 				      STR(queue_id), STR(encoding), sendopts,
    494 				      STR(sender), STR(dsn_envid), dsn_ret,
    495 				      bounce_templates));
    496     } else
    497 	return (bounce_notify_verp(flags, service_name, STR(queue_name),
    498 				   STR(queue_id), STR(encoding), sendopts,
    499 				   STR(sender), STR(dsn_envid), dsn_ret,
    500 				   STR(verp_delims), bounce_templates));
    501 }
    502 
    503 /* bounce_one_proto - bounce_one server protocol */
    504 
    505 static int bounce_one_proto(char *service_name, VSTREAM *client)
    506 {
    507     const char *myname = "bounce_one_proto";
    508     int     flags;
    509     int     sendopts;
    510     int     dsn_ret;
    511 
    512     /*
    513      * Read and validate the client request.
    514      */
    515     if (mail_command_server(client,
    516 			    RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
    517 			    RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name),
    518 			    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
    519 			    RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
    520 			    RECV_ATTR_INT(MAIL_ATTR_SENDOPTS, &sendopts),
    521 			    RECV_ATTR_STR(MAIL_ATTR_SENDER, sender),
    522 			    RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
    523 			    RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret),
    524 			    RECV_ATTR_FUNC(rcpb_scan, (void *) rcpt_buf),
    525 			    RECV_ATTR_FUNC(dsb_scan, (void *) dsn_buf),
    526 			    ATTR_TYPE_END) != 10) {
    527 	msg_warn("malformed request");
    528 	return (-1);
    529     }
    530 
    531     /*
    532      * Sanitize input.
    533      */
    534     if (strcmp(service_name, MAIL_SERVICE_BOUNCE) != 0) {
    535 	msg_warn("wrong service name \"%s\" for one-recipient bouncing",
    536 		 service_name);
    537 	return (-1);
    538     }
    539     if (mail_queue_name_ok(STR(queue_name)) == 0) {
    540 	msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
    541 	return (-1);
    542     }
    543     if (mail_queue_id_ok(STR(queue_id)) == 0) {
    544 	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
    545 	return (-1);
    546     }
    547     VS_NEUTER(encoding);
    548     VS_NEUTER(sender);
    549     VS_NEUTER(dsn_envid);
    550     VS_NEUTER(rcpt_buf->address);
    551     VS_NEUTER(rcpt_buf->orig_addr);
    552     VS_NEUTER(rcpt_buf->dsn_orcpt);
    553     VS_NEUTER(dsn_buf->status);
    554     VS_NEUTER(dsn_buf->action);
    555     VS_NEUTER(dsn_buf->reason);
    556     VS_NEUTER(dsn_buf->dtype);
    557     VS_NEUTER(dsn_buf->dtext);
    558     VS_NEUTER(dsn_buf->mtype);
    559     VS_NEUTER(dsn_buf->mname);
    560     (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
    561     (void) DSN_FROM_DSN_BUF(dsn_buf);
    562 
    563     /*
    564      * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and
    565      * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and
    566      * RECIPIENT_FROM_RCPT_BUF().
    567      */
    568     if (msg_verbose)
    569 	msg_info("%s: flags=0x%x queue=%s id=%s encoding=%s sendopts=%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",
    570 		 myname, flags, STR(queue_name), STR(queue_id),
    571 		 STR(encoding), sendopts, STR(sender), STR(dsn_envid),
    572 		 dsn_ret, STR(rcpt_buf->orig_addr), STR(rcpt_buf->address),
    573 		 rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt),
    574 		 rcpt_buf->dsn_notify, STR(dsn_buf->status),
    575 		 STR(dsn_buf->action), STR(dsn_buf->reason));
    576 
    577     /*
    578      * Handle REQUIRETLS etc. matters.
    579      */
    580     if (var_reqtls_enable && (sendopts & SOPT_REQUIRETLS_ALL))
    581 	edit_notification_properties(&sendopts, &dsn_ret);
    582 
    583     /*
    584      * Execute the request.
    585      */
    586     return (bounce_one_service(flags, STR(queue_name), STR(queue_id),
    587 			       STR(encoding), sendopts, STR(sender),
    588 			       STR(dsn_envid), dsn_ret, rcpt_buf,
    589 			       dsn_buf, bounce_templates));
    590 }
    591 
    592 /* bounce_service - parse bounce command type and delegate */
    593 
    594 static void bounce_service(VSTREAM *client, char *service_name, char **argv)
    595 {
    596     int     command;
    597     int     status;
    598 
    599     /*
    600      * Sanity check. This service takes no command-line arguments. The
    601      * service name should be usable as a subdirectory name.
    602      */
    603     if (argv[0])
    604 	msg_fatal("unexpected command-line argument: %s", argv[0]);
    605     if (mail_queue_name_ok(service_name) == 0)
    606 	msg_fatal("malformed service name: %s", service_name);
    607 
    608     /*
    609      * Announce the protocol.
    610      */
    611     attr_print(client, ATTR_FLAG_NONE,
    612 	       SEND_ATTR_STR(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_BOUNCE),
    613 	       ATTR_TYPE_END);
    614     (void) vstream_fflush(client);
    615 
    616     /*
    617      * Read and validate the first parameter of the client request. Let the
    618      * request-specific protocol routines take care of the remainder.
    619      */
    620     if (attr_scan(client, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
    621 		  RECV_ATTR_INT(MAIL_ATTR_NREQ, &command), 0) != 1) {
    622 	msg_warn("malformed request");
    623 	status = -1;
    624     } else if (command == BOUNCE_CMD_VERP) {
    625 	status = bounce_verp_proto(service_name, client);
    626     } else if (command == BOUNCE_CMD_FLUSH) {
    627 	status = bounce_notify_proto(service_name, client,
    628 				     bounce_notify_service);
    629     } else if (command == BOUNCE_CMD_WARN) {
    630 	status = bounce_notify_proto(service_name, client,
    631 				     bounce_warn_service);
    632     } else if (command == BOUNCE_CMD_TRACE) {
    633 	status = bounce_notify_proto(service_name, client,
    634 				     bounce_trace_service);
    635     } else if (command == BOUNCE_CMD_APPEND) {
    636 	status = bounce_append_proto(service_name, client);
    637     } else if (command == BOUNCE_CMD_ONE) {
    638 	status = bounce_one_proto(service_name, client);
    639     } else {
    640 	msg_warn("unknown command: %d", command);
    641 	status = -1;
    642     }
    643 
    644     /*
    645      * When the request has completed, send the completion status to the
    646      * client.
    647      */
    648     attr_print(client, ATTR_FLAG_NONE,
    649 	       SEND_ATTR_INT(MAIL_ATTR_STATUS, status),
    650 	       ATTR_TYPE_END);
    651     vstream_fflush(client);
    652 
    653     /*
    654      * When a cleanup trap was set, delete the log file in case of error.
    655      * This includes errors while sending the completion status to the
    656      * client.
    657      */
    658     if (bounce_cleanup_path) {
    659 	if (status || vstream_ferror(client))
    660 	    bounce_cleanup_log();
    661 	bounce_cleanup_unregister();
    662     }
    663 }
    664 
    665 static void load_helper(VSTREAM *stream, void *context)
    666 {
    667     BOUNCE_TEMPLATES *templates = (BOUNCE_TEMPLATES *) context;
    668 
    669     bounce_templates_load(stream, templates);
    670 }
    671 
    672 /* pre_jail_init - pre-jail initialization */
    673 
    674 static void pre_jail_init(char *unused_name, char **unused_argv)
    675 {
    676 
    677     /*
    678      * Bundle up a bunch of bounce template information.
    679      */
    680     bounce_templates = bounce_templates_create();
    681 
    682     /*
    683      * Load the alternate message files (if specified) before entering the
    684      * chroot jail.
    685      */
    686     if (*var_bounce_tmpl)
    687 	load_file(var_bounce_tmpl, load_helper, (void *) bounce_templates);
    688 }
    689 
    690 /* post_jail_init - initialize after entering chroot jail */
    691 
    692 static void post_jail_init(char *service_name, char **unused_argv)
    693 {
    694     bounce_hfrom_format = hfrom_format_parse(VAR_HFROM_FORMAT, var_hfrom_format);
    695 
    696     /*
    697      * Special case: dump bounce templates. This is not part of the master(5)
    698      * public interface. This internal interface is used by the postconf
    699      * command. It was implemented before bounce templates were isolated into
    700      * modules that could have been called directly.
    701      */
    702     if (strcmp(service_name, "dump_templates") == 0) {
    703 	bounce_templates_dump(VSTREAM_OUT, bounce_templates);
    704 	vstream_fflush(VSTREAM_OUT);
    705 	exit(0);
    706     }
    707     if (strcmp(service_name, "expand_templates") == 0) {
    708 	bounce_templates_expand(VSTREAM_OUT, bounce_templates);
    709 	vstream_fflush(VSTREAM_OUT);
    710 	exit(0);
    711     }
    712 
    713     /*
    714      * Initialize. We're single threaded so we can reuse some memory upon
    715      * successive requests.
    716      */
    717     queue_id = vstring_alloc(10);
    718     queue_name = vstring_alloc(10);
    719     rcpt_buf = rcpb_create();
    720     encoding = vstring_alloc(10);
    721     sender = vstring_alloc(10);
    722     dsn_envid = vstring_alloc(10);
    723     verp_delims = vstring_alloc(10);
    724     dsn_buf = dsb_create();
    725 }
    726 
    727 MAIL_VERSION_STAMP_DECLARE;
    728 
    729 /* main - the main program */
    730 
    731 int     main(int argc, char **argv)
    732 {
    733     static const CONFIG_INT_TABLE int_table[] = {
    734 	VAR_BOUNCE_LIMIT, DEF_BOUNCE_LIMIT, &var_bounce_limit, 1, 0,
    735 	0,
    736     };
    737     static const CONFIG_TIME_TABLE time_table[] = {
    738 	VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 0, 8640000,
    739 	VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0,
    740 	0,
    741     };
    742     static const CONFIG_STR_TABLE str_table[] = {
    743 	VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
    744 	VAR_BOUNCE_RCPT, DEF_BOUNCE_RCPT, &var_bounce_rcpt, 1, 0,
    745 	VAR_2BOUNCE_RCPT, DEF_2BOUNCE_RCPT, &var_2bounce_rcpt, 1, 0,
    746 	VAR_DELAY_RCPT, DEF_DELAY_RCPT, &var_delay_rcpt, 1, 0,
    747 	VAR_BOUNCE_TMPL, DEF_BOUNCE_TMPL, &var_bounce_tmpl, 0, 0,
    748 	VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0,
    749 	0,
    750     };
    751     static const CONFIG_NBOOL_TABLE nbool_table[] = {
    752 	VAR_THREADED_BOUNCE, DEF_THREADED_BOUNCE, &var_threaded_bounce,
    753 	VAR_REQTLS_REDACT_DSN, DEF_REQTLS_REDACT_DSN, &var_reqtls_redact_dsn,
    754 	0,
    755     };
    756 
    757     /*
    758      * Fingerprint executables and core dumps.
    759      */
    760     MAIL_VERSION_STAMP_ALLOCATE;
    761 
    762     /*
    763      * Pass control to the single-threaded service skeleton.
    764      */
    765     single_server_main(argc, argv, bounce_service,
    766 		       CA_MAIL_SERVER_INT_TABLE(int_table),
    767 		       CA_MAIL_SERVER_STR_TABLE(str_table),
    768 		       CA_MAIL_SERVER_TIME_TABLE(time_table),
    769 		       CA_MAIL_SERVER_NBOOL_TABLE(nbool_table),
    770 		       CA_MAIL_SERVER_PRE_INIT(pre_jail_init),
    771 		       CA_MAIL_SERVER_POST_INIT(post_jail_init),
    772 		       CA_MAIL_SERVER_UNLIMITED,
    773 		       0);
    774 }
    775