Home | History | Annotate | Line # | Download | only in bounce
      1 /*	$NetBSD: bounce_one_service.c,v 1.4 2026/05/09 18:49:14 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	bounce_one_service 3
      6 /* SUMMARY
      7 /*	send non-delivery report to sender, server side
      8 /* SYNOPSIS
      9 /*	#include "bounce_service.h"
     10 /*
     11 /*	int     bounce_one_service(flags, queue_name, queue_id, encoding,
     12 /*					sendopts, orig_sender, envid, ret,
     13 /*					rcpt_buf, dsn_buf, templates)
     14 /*	int	flags;
     15 /*	char	*queue_name;
     16 /*	char	*queue_id;
     17 /*	char	*encoding;
     18 /*	int	sendopts;
     19 /*	char	*orig_sender;
     20 /*	char	*envid;
     21 /*	int	ret;
     22 /*	RCPT_BUF *rcpt_buf;
     23 /*	DSN_BUF	*dsn_buf;
     24 /*	BOUNCE_TEMPLATES *templates;
     25 /* DESCRIPTION
     26 /*	This module implements the server side of the bounce_one()
     27 /*	(send bounce message for one recipient) request.
     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 <post_mail.h>
     73 #include <mail_addr.h>
     74 #include <mail_error.h>
     75 #include <bounce.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_one_service - send a bounce for one recipient */
     86 
     87 int     bounce_one_service(int flags, char *queue_name, char *queue_id,
     88 			           char *encoding, int sendopts,
     89 			           char *orig_sender, char *dsn_envid,
     90 			           int dsn_ret, RCPT_BUF *rcpt_buf,
     91 			           DSN_BUF *dsn_buf, 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 
    101     /*
    102      * Initialize. Open queue file, bounce log, etc.
    103      */
    104     bounce_info = bounce_mail_one_init(queue_name, queue_id, encoding,
    105 				       sendopts, dsn_envid, rcpt_buf,
    106 				       dsn_buf, ts->failure);
    107 
    108 #define NULL_SENDER		MAIL_ADDR_EMPTY	/* special address */
    109 #define NULL_TRACE_FLAGS	0
    110 
    111     /*
    112      * The choice of bounce sender address depends on the original sender
    113      * address. For a single bounce (a non-delivery notification to the
    114      * message originator), the sender address is the empty string. For a
    115      * double bounce (typically a failed single bounce, or a postmaster
    116      * notification that was produced by any of the mail processes) the
    117      * sender address is defined by the var_double_bounce_sender
    118      * configuration variable. When a double bounce cannot be delivered, the
    119      * queue manager blackholes the resulting triple bounce message.
    120      */
    121 
    122     /*
    123      * Double bounce failed. Never send a triple bounce.
    124      *
    125      * However, this does not prevent double bounces from bouncing on other
    126      * systems. In order to cope with this, either the queue manager must
    127      * recognize the double-bounce original sender address and discard mail,
    128      * or every delivery agent must recognize the double-bounce sender
    129      * address and substitute something else so mail does not come back at
    130      * us.
    131      */
    132     if (strcasecmp_utf8(orig_sender, mail_addr_double_bounce()) == 0) {
    133 	msg_warn("%s: undeliverable postmaster notification discarded",
    134 		 queue_id);
    135 	bounce_status = 0;
    136     }
    137 
    138     /*
    139      * Single bounce failed. Optionally send a double bounce to postmaster,
    140      * subject to notify_classes restrictions.
    141      */
    142 #define ANY_BOUNCE (MAIL_ERROR_2BOUNCE | MAIL_ERROR_BOUNCE)
    143 #define SEND_POSTMASTER_ANY_BOUNCE_NOTICE (notify_mask & ANY_BOUNCE)
    144 
    145     else if (*orig_sender == 0) {
    146 	if (!SEND_POSTMASTER_ANY_BOUNCE_NOTICE) {
    147 	    bounce_status = 0;
    148 	} else {
    149 	    if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
    150 						 var_2bounce_rcpt,
    151 						 MAIL_SRC_MASK_BOUNCE,
    152 						 NULL_TRACE_FLAGS,
    153 						 sendopts,
    154 						 new_id)) != 0) {
    155 
    156 		/*
    157 		 * Double bounce to Postmaster. This is the last opportunity
    158 		 * for this message to be delivered. Send the text with
    159 		 * reason for the bounce, and the headers of the original
    160 		 * message. Don't bother sending the boiler-plate text.
    161 		 */
    162 		msg_info("%s: postmaster non-delivery notification: %s",
    163 			 queue_id, STR(new_id));
    164 		if (!bounce_header(bounce, bounce_info, var_2bounce_rcpt,
    165 				   POSTMASTER_COPY)
    166 		    && bounce_recipient_log(bounce, bounce_info) == 0
    167 		    && bounce_header_dsn(bounce, bounce_info) == 0
    168 		    && bounce_recipient_dsn(bounce, bounce_info) == 0)
    169 		    bounce_original(bounce, bounce_info, DSN_RET_FULL);
    170 		bounce_status = post_mail_fclose(bounce);
    171 		if (bounce_status)
    172 		    msg_warn("%s: postmaster notification failed: %s",
    173 			     queue_id, cleanup_strerror(bounce_status));
    174 	    } else {
    175 		msg_warn("%s: postmaster notification failed", queue_id);
    176 	    }
    177 	}
    178     }
    179 
    180     /*
    181      * Non-bounce failed. Send a single bounce, subject to DSN NOTIFY
    182      * restrictions.
    183      */
    184     else {
    185 	RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
    186 
    187 	if (rcpt->dsn_notify != 0		/* compat */
    188 	    && (rcpt->dsn_notify & DSN_NOTIFY_FAILURE) == 0) {
    189 	    bounce_status = 0;
    190 	} else {
    191 	    if ((bounce = post_mail_fopen_nowait(NULL_SENDER, orig_sender,
    192 						 MAIL_SRC_MASK_BOUNCE,
    193 						 NULL_TRACE_FLAGS,
    194 						 sendopts,
    195 						 new_id)) != 0) {
    196 
    197 		/*
    198 		 * Send the bounce message header, some boilerplate text that
    199 		 * pretends that we are a polite mail system, the text with
    200 		 * reason for the bounce, and a copy of the original message.
    201 		 */
    202 		msg_info("%s: sender non-delivery notification: %s",
    203 			 queue_id, STR(new_id));
    204 		if (bounce_header(bounce, bounce_info, orig_sender,
    205 				  NO_POSTMASTER_COPY) == 0
    206 		    && bounce_boilerplate(bounce, bounce_info) == 0
    207 		    && bounce_recipient_log(bounce, bounce_info) == 0
    208 		    && bounce_header_dsn(bounce, bounce_info) == 0
    209 		    && bounce_recipient_dsn(bounce, bounce_info) == 0)
    210 		    bounce_original(bounce, bounce_info, dsn_ret ?
    211 				    dsn_ret : DSN_RET_FULL);
    212 		bounce_status = post_mail_fclose(bounce);
    213 		if (bounce_status)
    214 		    msg_warn("%s: sender notification failed to %s: %s",
    215 			     queue_id, orig_sender,
    216 			     cleanup_strerror(bounce_status));
    217 	    } else {
    218 		msg_warn("%s: sender notification failed to %s",
    219 			 queue_id, orig_sender);
    220 	    }
    221 	}
    222 
    223 	/*
    224 	 * Optionally send a postmaster notice, subject to notify_classes
    225 	 * restrictions.
    226 	 *
    227 	 * This postmaster notice is not critical, so if it fails don't
    228 	 * retransmit the bounce that we just generated, just log a warning.
    229 	 */
    230 #define SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE (notify_mask & MAIL_ERROR_BOUNCE)
    231 
    232 	if (bounce_status == 0 && SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE
    233 	  && strcasecmp_utf8(orig_sender, mail_addr_double_bounce()) != 0) {
    234 
    235 	    /*
    236 	     * Send the text with reason for the bounce, and the headers of
    237 	     * the original message. Don't bother sending the boiler-plate
    238 	     * text. This postmaster notice is not critical, so if it fails
    239 	     * don't retransmit the bounce that we just generated, just log a
    240 	     * warning.
    241 	     */
    242 	    if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
    243 						 var_bounce_rcpt,
    244 						 MAIL_SRC_MASK_BOUNCE,
    245 						 NULL_TRACE_FLAGS,
    246 						 sendopts,
    247 						 new_id)) != 0) {
    248 		msg_info("%s: postmaster non-delivery notification: %s",
    249 			 queue_id, STR(new_id));
    250 		if (bounce_header(bounce, bounce_info, var_bounce_rcpt,
    251 				  POSTMASTER_COPY) == 0
    252 		    && bounce_recipient_log(bounce, bounce_info) == 0
    253 		    && bounce_header_dsn(bounce, bounce_info) == 0
    254 		    && bounce_recipient_dsn(bounce, bounce_info) == 0)
    255 		    bounce_original(bounce, bounce_info, DSN_RET_HDRS);
    256 		postmaster_status = post_mail_fclose(bounce);
    257 	    }
    258 	    if (postmaster_status)
    259 		msg_warn("%s: postmaster notice failed while bouncing to %s",
    260 			 queue_id, orig_sender);
    261 	}
    262     }
    263 
    264     /*
    265      * Optionally, delete the recipient from the queue file.
    266      */
    267     if (bounce_status == 0 && (flags & BOUNCE_FLAG_DELRCPT))
    268 	bounce_delrcpt_one(bounce_info);
    269 
    270     /*
    271      * Cleanup.
    272      */
    273     bounce_mail_free(bounce_info);
    274     vstring_free(new_id);
    275 
    276     return (bounce_status);
    277 }
    278