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