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