Home | History | Annotate | Line # | Download | only in bounce
      1 /*	$NetBSD: bounce_warn_service.c,v 1.4 2026/05/09 18:49:15 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	bounce_warn_service 3
      6 /* SUMMARY
      7 /*	send non-delivery report to sender, server side
      8 /* SYNOPSIS
      9 /*	#include "bounce_service.h"
     10 /*
     11 /*	int     bounce_warn_service(flags, service, queue_name, queue_id,
     12 /*					encoding, sendopts, sender, envid,
     13 /*					dsn_ret, templates)
     14 /*	int	flags;
     15 /*	char	*service;
     16 /*	char	*queue_name;
     17 /*	char	*queue_id;
     18 /*	char	*encoding;
     19 /*	int	sendopts;
     20 /*	char	*sender;
     21 /*	char	*envid;
     22 /*	int	dsn_ret;
     23 /*	BOUNCE_TEMPLATES *ts;
     24 /* DESCRIPTION
     25 /*	This module implements the server side of the bounce_warn()
     26 /*	(send delay notice) request. The logfile
     27 /*	is not removed, and a warning is sent instead of a bounce.
     28 /*
     29 /*	When a message bounces, a full copy is sent to the originator,
     30 /*	and an optional  copy of the diagnostics with message headers is
     31 /*	sent to the postmaster.  The result is non-zero when the operation
     32 /*	should be tried again.
     33 /*
     34 /*	When a bounce is sent, the sender address is the empty
     35 /*	address.  When a bounce bounces, an optional double bounce
     36 /*	with the entire undeliverable mail is sent to the postmaster,
     37 /*	with as sender address the double bounce address.
     38 /* DIAGNOSTICS
     39 /*	Fatal error: error opening existing file.
     40 /* BUGS
     41 /* SEE ALSO
     42 /*	bounce(3) basic bounce service client interface
     43 /* LICENSE
     44 /* .ad
     45 /* .fi
     46 /*	The Secure Mailer license must be distributed with this software.
     47 /* AUTHOR(S)
     48 /*	Wietse Venema
     49 /*	IBM T.J. Watson Research
     50 /*	P.O. Box 704
     51 /*	Yorktown Heights, NY 10598, USA
     52 /*--*/
     53 
     54 /* System library. */
     55 
     56 #include <sys_defs.h>
     57 #include <fcntl.h>
     58 #include <errno.h>
     59 #include <string.h>
     60 #include <ctype.h>
     61 
     62 /* Utility library. */
     63 
     64 #include <msg.h>
     65 #include <vstream.h>
     66 #include <name_mask.h>
     67 #include <stringops.h>
     68 
     69 /* Global library. */
     70 
     71 #include <mail_params.h>
     72 #include <mail_queue.h>
     73 #include <post_mail.h>
     74 #include <mail_addr.h>
     75 #include <mail_error.h>
     76 #include <dsn_mask.h>
     77 #include <rec_type.h>
     78 
     79 /* Application-specific. */
     80 
     81 #include "bounce_service.h"
     82 
     83 #define STR vstring_str
     84 
     85 /* bounce_warn_service - send a delayed mail notice */
     86 
     87 int     bounce_warn_service(int unused_flags, char *service, char *queue_name,
     88 			            char *queue_id, char *encoding,
     89 			            int sendopts, char *recipient,
     90 			            char *dsn_envid, int dsn_ret,
     91 			            BOUNCE_TEMPLATES *ts)
     92 {
     93     BOUNCE_INFO *bounce_info;
     94     int     bounce_status = 1;
     95     int     postmaster_status = 1;
     96     VSTREAM *bounce;
     97     int     notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
     98 				    var_notify_classes);
     99     VSTRING *new_id = vstring_alloc(10);
    100     char   *postmaster;
    101     int     count;
    102 
    103     /*
    104      * Initialize. Open queue file, bounce log, etc.
    105      *
    106      * XXX DSN This service produces RFC 3464-style "delayed mail" reports from
    107      * information in the defer logfile. That same file is used for three
    108      * different types of report:
    109      *
    110      * a) On-demand reports of all delayed deliveries by the mailq(1) command.
    111      * This reports all recipients that have a transient delivery error.
    112      *
    113      * b) RFC 3464-style "delayed mail" notifications by the defer(8) service.
    114      * This reports to the sender all recipients that have no DSN NOTIFY
    115      * information (compatibility) and all recipients that have DSN
    116      * NOTIFY=DELAY; this reports to postmaster all recipients, subject to
    117      * notify_classes restrictions.
    118      *
    119      * c) RFC 3464-style bounce reports by the bounce(8) service when mail is
    120      * too old. This reports to the sender all recipients that have no DSN
    121      * NOTIFY information (compatibility) and all recipients that have DSN
    122      * NOTIFY=FAILURE; this reports to postmaster all recipients, subject to
    123      * notify_classes restrictions.
    124      */
    125     bounce_info = bounce_mail_init(service, queue_name, queue_id,
    126 				   encoding, sendopts, dsn_envid, ts->delay);
    127 
    128 #define NULL_SENDER		MAIL_ADDR_EMPTY	/* special address */
    129 #define NULL_TRACE_FLAGS	0
    130 
    131     /*
    132      * The choice of sender address depends on the recipient address. For a
    133      * single bounce (a non-delivery notification to the message originator),
    134      * the sender address is the empty string. For a double bounce (typically
    135      * a failed single bounce, or a postmaster notification that was produced
    136      * by any of the mail processes) the sender address is defined by the
    137      * var_double_bounce_sender configuration variable. When a double bounce
    138      * cannot be delivered, the queue manager blackholes the resulting triple
    139      * bounce message.
    140      */
    141 
    142     /*
    143      * Double bounce failed. Never send a triple bounce.
    144      *
    145      * However, this does not prevent double bounces from bouncing on other
    146      * systems. In order to cope with this, either the queue manager must
    147      * recognize the double-bounce recipient address and discard mail, or
    148      * every delivery agent must recognize the double-bounce sender address
    149      * and substitute something else so mail does not come back at us.
    150      */
    151     if (strcasecmp_utf8(recipient, mail_addr_double_bounce()) == 0) {
    152 	msg_warn("%s: undeliverable postmaster notification discarded",
    153 		 queue_id);
    154 	bounce_status = 0;
    155     }
    156 
    157     /*
    158      * Single bounce failed. Optionally send a double bounce to postmaster,
    159      * subject to notify_classes restrictions.
    160      */
    161 #define ANY_BOUNCE (MAIL_ERROR_2BOUNCE | MAIL_ERROR_BOUNCE)
    162 #define SEND_POSTMASTER_DELAY_NOTICE  (notify_mask & MAIL_ERROR_DELAY)
    163 
    164     else if (*recipient == 0) {
    165 	if (!SEND_POSTMASTER_DELAY_NOTICE) {
    166 	    bounce_status = 0;
    167 	} else {
    168 	    postmaster = var_delay_rcpt;
    169 	    if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
    170 						 postmaster,
    171 						 MAIL_SRC_MASK_BOUNCE,
    172 						 NULL_TRACE_FLAGS,
    173 						 sendopts,
    174 						 new_id)) != 0) {
    175 
    176 		/*
    177 		 * Double bounce to Postmaster. This is the last opportunity
    178 		 * for this message to be delivered. Send the text with
    179 		 * reason for the bounce, and the headers of the original
    180 		 * message. Don't bother sending the boiler-plate text.
    181 		 */
    182 		msg_info("%s: postmaster delay notification: %s",
    183 			 queue_id, STR(new_id));
    184 		count = -1;
    185 		if (!bounce_header(bounce, bounce_info, postmaster,
    186 				   POSTMASTER_COPY)
    187 		    && (count = bounce_diagnostic_log(bounce, bounce_info,
    188 						   DSN_NOTIFY_OVERRIDE)) > 0
    189 		    && bounce_header_dsn(bounce, bounce_info) == 0
    190 		    && bounce_diagnostic_dsn(bounce, bounce_info,
    191 					     DSN_NOTIFY_OVERRIDE) > 0) {
    192 		    bounce_original(bounce, bounce_info, DSN_RET_FULL);
    193 		    bounce_status = post_mail_fclose(bounce);
    194 		    if (bounce_status)
    195 			msg_warn("%s: postmaster notification failed: %s",
    196 				 queue_id, cleanup_strerror(bounce_status));
    197 		} else {
    198 		    (void) vstream_fclose(bounce);
    199 		    if (count == 0)
    200 			bounce_status = 0;
    201 		}
    202 	    } else {
    203 		msg_warn("%s: postmaster notification failed", queue_id);
    204 	    }
    205 	}
    206     }
    207 
    208     /*
    209      * Non-bounce failed. Send a single bounce, subject to DSN NOTIFY
    210      * restrictions.
    211      */
    212     else {
    213 	if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient,
    214 					     MAIL_SRC_MASK_BOUNCE,
    215 					     NULL_TRACE_FLAGS,
    216 					     sendopts,
    217 					     new_id)) != 0) {
    218 
    219 	    /*
    220 	     * Send the bounce message header, some boilerplate text that
    221 	     * pretends that we are a polite mail system, the text with
    222 	     * reason for the bounce, and a copy of the original message.
    223 	     */
    224 	    msg_info("%s: sender delay notification: %s",
    225 		     queue_id, STR(new_id));
    226 	    count = -1;
    227 	    if (bounce_header(bounce, bounce_info, recipient,
    228 			      NO_POSTMASTER_COPY) == 0
    229 		&& bounce_boilerplate(bounce, bounce_info) == 0
    230 		&& (count = bounce_diagnostic_log(bounce, bounce_info,
    231 						  DSN_NOTIFY_DELAY)) > 0
    232 		&& bounce_header_dsn(bounce, bounce_info) == 0
    233 		&& bounce_diagnostic_dsn(bounce, bounce_info,
    234 					 DSN_NOTIFY_DELAY) > 0) {
    235 		bounce_original(bounce, bounce_info, DSN_RET_HDRS);
    236 		bounce_status = post_mail_fclose(bounce);
    237 		if (bounce_status)
    238 		    msg_warn("%s: sender notification failed to %s: %s",
    239 			     queue_id, recipient,
    240 			     cleanup_strerror(bounce_status));
    241 	    } else {
    242 		(void) vstream_fclose(bounce);
    243 		if (count == 0)
    244 		    bounce_status = 0;
    245 	    }
    246 	} else {
    247 	    msg_warn("%s: sender notification failed to %s",
    248 		     queue_id, recipient);
    249 	}
    250 
    251 	/*
    252 	 * Optionally send a postmaster notice, subject to notify_classes
    253 	 * restrictions.
    254 	 *
    255 	 * This postmaster notice is not critical, so if it fails don't
    256 	 * retransmit the bounce that we just generated, just log a warning.
    257 	 */
    258 	if (bounce_status == 0 && SEND_POSTMASTER_DELAY_NOTICE
    259 	    && strcasecmp_utf8(recipient, mail_addr_double_bounce()) != 0) {
    260 
    261 	    /*
    262 	     * Send the text with reason for the bounce, and the headers of
    263 	     * the original message. Don't bother sending the boiler-plate
    264 	     * text. This postmaster notice is not critical, so if it fails
    265 	     * don't retransmit the bounce that we just generated, just log a
    266 	     * warning.
    267 	     */
    268 	    postmaster = var_delay_rcpt;
    269 	    if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
    270 						 postmaster,
    271 						 MAIL_SRC_MASK_BOUNCE,
    272 						 NULL_TRACE_FLAGS,
    273 						 sendopts,
    274 						 new_id)) != 0) {
    275 		msg_info("%s: postmaster delay notification: %s",
    276 			 queue_id, STR(new_id));
    277 		count = -1;
    278 		if (bounce_header(bounce, bounce_info, postmaster,
    279 				  POSTMASTER_COPY) == 0
    280 		    && (count = bounce_diagnostic_log(bounce, bounce_info,
    281 						   DSN_NOTIFY_OVERRIDE)) > 0
    282 		    && bounce_header_dsn(bounce, bounce_info) == 0
    283 		    && bounce_diagnostic_dsn(bounce, bounce_info,
    284 					     DSN_NOTIFY_OVERRIDE) > 0) {
    285 		    bounce_original(bounce, bounce_info, DSN_RET_HDRS);
    286 		    postmaster_status = post_mail_fclose(bounce);
    287 		} else {
    288 		    (void) vstream_fclose(bounce);
    289 		    if (count == 0)
    290 			postmaster_status = 0;
    291 		}
    292 	    }
    293 	    if (postmaster_status)
    294 		msg_warn("%s: postmaster notice failed while warning %s",
    295 			 queue_id, recipient);
    296 	}
    297     }
    298 
    299     /*
    300      * Cleanup.
    301      */
    302     bounce_mail_free(bounce_info);
    303     vstring_free(new_id);
    304 
    305     return (bounce_status);
    306 }
    307