Home | History | Annotate | Line # | Download | only in bounce
      1 /*	$NetBSD: bounce_notify_verp.c,v 1.4 2026/05/09 18:49:14 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	bounce_notify_verp 3
      6 /* SUMMARY
      7 /*	send non-delivery report to sender, server side
      8 /* SYNOPSIS
      9 /*	#include "bounce_service.h"
     10 /*
     11 /*	int     bounce_notify_verp(flags, service, queue_name, queue_id,
     12 /*					encoding, sendopts, sender,
     13 /*					dsn_envid, dsn_ret, verp_delims,
     14 /*					templates)
     15 /*	int	flags;
     16 /*	char	*service;
     17 /*	char	*queue_name;
     18 /*	char	*queue_id;
     19 /*	char	*encoding;
     20 /*	int	sendopts;
     21 /*	char	*sender;
     22 /*	char	*dsn_envid;
     23 /*	int	dsn_ret;
     24 /*	char	*verp_delims;
     25 /*	BOUNCE_TEMPLATES *templates;
     26 /* DESCRIPTION
     27 /*	This module implements the server side of the bounce_notify()
     28 /*	(send bounce message) request. The logfile
     29 /*	is removed after and a warning is posted.
     30 /*	The bounce recipient address is encoded in VERP format.
     31 /*	This routine must be used for single bounces only.
     32 /*
     33 /*	When a message bounces, a full copy is sent to the originator,
     34 /*	and an optional  copy of the diagnostics with message headers is
     35 /*	sent to the postmaster.  The result is non-zero when the operation
     36 /*	should be tried again.
     37 /*
     38 /*	When a bounce is sent, the sender address is the empty
     39 /*	address.
     40 /* DIAGNOSTICS
     41 /*	Fatal error: error opening existing file.
     42 /* SEE ALSO
     43 /*	bounce(3) basic bounce service client interface
     44 /* LICENSE
     45 /* .ad
     46 /* .fi
     47 /*	The Secure Mailer license must be distributed with this software.
     48 /* AUTHOR(S)
     49 /*	Wietse Venema
     50 /*	IBM T.J. Watson Research
     51 /*	P.O. Box 704
     52 /*	Yorktown Heights, NY 10598, USA
     53 /*--*/
     54 
     55 /* System library. */
     56 
     57 #include <sys_defs.h>
     58 #include <fcntl.h>
     59 #include <errno.h>
     60 #include <string.h>
     61 #include <ctype.h>
     62 
     63 /* Utility library. */
     64 
     65 #include <msg.h>
     66 #include <vstream.h>
     67 #include <name_mask.h>
     68 #include <stringops.h>
     69 
     70 /* Global library. */
     71 
     72 #include <mail_params.h>
     73 #include <mail_queue.h>
     74 #include <post_mail.h>
     75 #include <mail_addr.h>
     76 #include <mail_error.h>
     77 #include <verp_sender.h>
     78 #include <bounce.h>
     79 #include <dsn_mask.h>
     80 #include <rec_type.h>
     81 
     82 /* Application-specific. */
     83 
     84 #include "bounce_service.h"
     85 
     86 #define STR vstring_str
     87 
     88 /* bounce_notify_verp - send a bounce, VERP style */
     89 
     90 int     bounce_notify_verp(int flags, char *service, char *queue_name,
     91 			           char *queue_id, char *encoding,
     92 			           int sendopts, char *recipient,
     93 			           char *dsn_envid, int dsn_ret,
     94 			           char *verp_delims, BOUNCE_TEMPLATES *ts)
     95 {
     96     const char *myname = "bounce_notify_verp";
     97     BOUNCE_INFO *bounce_info;
     98     int     bounce_status = 0;
     99     int     postmaster_status;
    100     VSTREAM *bounce;
    101     int     notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
    102 				    var_notify_classes);
    103     char   *postmaster;
    104     VSTRING *verp_buf;
    105     VSTRING *new_id;
    106 
    107     /*
    108      * Sanity checks. We must be called only for undeliverable non-bounce
    109      * messages.
    110      */
    111     if (*recipient == 0)
    112 	msg_panic("%s: attempt to bounce a single bounce", myname);
    113     if (strcasecmp_utf8(recipient, mail_addr_double_bounce()) == 0)
    114 	msg_panic("%s: attempt to bounce a double bounce", myname);
    115 
    116     /*
    117      * Initialize. Open queue file, bounce log, etc.
    118      */
    119     bounce_info = bounce_mail_init(service, queue_name, queue_id,
    120 				   encoding, sendopts, dsn_envid,
    121 				   ts->failure);
    122 
    123     /*
    124      * If we have no recipient list then we can't send VERP replies. Send
    125      * *something* anyway so that the mail is not lost in a black hole.
    126      */
    127     if (bounce_info->log_handle == 0) {
    128 	DSN_BUF *dsn_buf = dsb_create();
    129 	RCPT_BUF *rcpt_buf = rcpb_create();
    130 
    131 	dsb_simple(dsn_buf, "5.0.0", "(error report unavailable)");
    132 	(void) DSN_FROM_DSN_BUF(dsn_buf);
    133 	vstring_strcpy(rcpt_buf->address, "(recipient address unavailable)");
    134 	(void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
    135 	bounce_status = bounce_one_service(flags, queue_name, queue_id,
    136 					   encoding, sendopts, recipient,
    137 					   dsn_envid, dsn_ret, rcpt_buf,
    138 					   dsn_buf, ts);
    139 	rcpb_free(rcpt_buf);
    140 	dsb_free(dsn_buf);
    141 	bounce_mail_free(bounce_info);
    142 	return (bounce_status);
    143     }
    144 #define NULL_SENDER		MAIL_ADDR_EMPTY	/* special address */
    145 #define NULL_TRACE_FLAGS	0
    146 
    147     /*
    148      * A non-bounce message was returned. Send a single bounce, one per
    149      * recipient.
    150      */
    151     verp_buf = vstring_alloc(100);
    152     new_id = vstring_alloc(10);
    153     while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
    154 			   bounce_info->dsn_buf) != 0) {
    155 	RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
    156 
    157 	/*
    158 	 * Notify the originator, subject to DSN NOTIFY restrictions.
    159 	 *
    160 	 * Fix 20090114: Use the Postfix original recipient, because that is
    161 	 * what the VERP consumer expects.
    162 	 */
    163 	if (rcpt->dsn_notify != 0		/* compat */
    164 	    && (rcpt->dsn_notify & DSN_NOTIFY_FAILURE) == 0) {
    165 	    bounce_status = 0;
    166 	} else {
    167 	    verp_sender(verp_buf, verp_delims, recipient, rcpt);
    168 	    if ((bounce = post_mail_fopen_nowait(NULL_SENDER, STR(verp_buf),
    169 						 MAIL_SRC_MASK_BOUNCE,
    170 						 NULL_TRACE_FLAGS,
    171 						 sendopts,
    172 						 new_id)) != 0) {
    173 
    174 		/*
    175 		 * Send the bounce message header, some boilerplate text that
    176 		 * pretends that we are a polite mail system, the text with
    177 		 * reason for the bounce, and a copy of the original message.
    178 		 */
    179 		msg_info("%s: sender non-delivery notification: %s",
    180 			 queue_id, STR(new_id));
    181 		if (bounce_header(bounce, bounce_info, STR(verp_buf),
    182 				  NO_POSTMASTER_COPY) == 0
    183 		    && bounce_boilerplate(bounce, bounce_info) == 0
    184 		    && bounce_recipient_log(bounce, bounce_info) == 0
    185 		    && bounce_header_dsn(bounce, bounce_info) == 0
    186 		    && bounce_recipient_dsn(bounce, bounce_info) == 0)
    187 		    bounce_original(bounce, bounce_info, dsn_ret ?
    188 				    dsn_ret : DSN_RET_FULL);
    189 		bounce_status = post_mail_fclose(bounce);
    190 		if (bounce_status)
    191 		    msg_warn("%s: sender notification failed to %s: %s",
    192 			     queue_id, STR(verp_buf),
    193 			     cleanup_strerror(bounce_status));
    194 	    } else {
    195 		msg_warn("%s: sender notification failed to %s",
    196 			 queue_id, STR(verp_buf));
    197 		bounce_status = 1;
    198 	    }
    199 
    200 	    /*
    201 	     * Stop at the first sign of trouble, instead of making the
    202 	     * problem worse.
    203 	     */
    204 	    if (bounce_status != 0)
    205 		break;
    206 
    207 	    /*
    208 	     * Optionally, mark this recipient as done.
    209 	     */
    210 	    if (flags & BOUNCE_FLAG_DELRCPT)
    211 		bounce_delrcpt_one(bounce_info);
    212 	}
    213 
    214 	/*
    215 	 * Optionally, send a postmaster notice, subject to notify_classes
    216 	 * restrictions.
    217 	 *
    218 	 * This postmaster notice is not critical, so if it fails don't
    219 	 * retransmit the bounce that we just generated, just log a warning.
    220 	 */
    221 #define SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE (notify_mask & MAIL_ERROR_BOUNCE)
    222 
    223 	if (SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE) {
    224 
    225 	    /*
    226 	     * Send the text with reason for the bounce, and the headers of
    227 	     * the original message. Don't bother sending the boiler-plate
    228 	     * text. This postmaster notice is not critical, so if it fails
    229 	     * don't retransmit the bounce that we just generated, just log a
    230 	     * warning.
    231 	     */
    232 	    postmaster = var_bounce_rcpt;
    233 	    if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
    234 						 postmaster,
    235 						 MAIL_SRC_MASK_BOUNCE,
    236 						 NULL_TRACE_FLAGS,
    237 						 sendopts,
    238 						 new_id)) != 0) {
    239 		msg_info("%s: postmaster non-delivery notification: %s",
    240 			 queue_id, STR(new_id));
    241 		if (bounce_header(bounce, bounce_info, postmaster,
    242 				  POSTMASTER_COPY) == 0
    243 		    && bounce_recipient_log(bounce, bounce_info) == 0
    244 		    && bounce_header_dsn(bounce, bounce_info) == 0
    245 		    && bounce_recipient_dsn(bounce, bounce_info) == 0)
    246 		    bounce_original(bounce, bounce_info, DSN_RET_HDRS);
    247 		postmaster_status = post_mail_fclose(bounce);
    248 	    } else
    249 		postmaster_status = 1;
    250 
    251 	    if (postmaster_status)
    252 		msg_warn("%s: postmaster notice failed while bouncing to %s",
    253 			 queue_id, recipient);
    254 	}
    255     }
    256 
    257     /*
    258      * Examine the completion status. Delete the bounce log file only when
    259      * the bounce was posted successfully, and only if we are bouncing for
    260      * real, not just warning.
    261      */
    262     if (bounce_status == 0 && mail_queue_remove(service, queue_id)
    263 	&& errno != ENOENT)
    264 	msg_fatal("remove %s %s: %m", service, queue_id);
    265 
    266     /*
    267      * Cleanup.
    268      */
    269     bounce_mail_free(bounce_info);
    270     vstring_free(verp_buf);
    271     vstring_free(new_id);
    272 
    273     return (bounce_status);
    274 }
    275