1 /* $NetBSD: bounce_notify_verp.c,v 1.2 2017/02/14 01:16:44 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, smtputf8, 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 smtputf8; 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 smtputf8, 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, smtputf8, 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, smtputf8, 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 smtputf8, 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 if (bounce_header(bounce, bounce_info, STR(verp_buf), 180 NO_POSTMASTER_COPY) == 0 181 && bounce_boilerplate(bounce, bounce_info) == 0 182 && bounce_recipient_log(bounce, bounce_info) == 0 183 && bounce_header_dsn(bounce, bounce_info) == 0 184 && bounce_recipient_dsn(bounce, bounce_info) == 0) 185 bounce_original(bounce, bounce_info, dsn_ret ? 186 dsn_ret : DSN_RET_FULL); 187 bounce_status = post_mail_fclose(bounce); 188 if (bounce_status == 0) 189 msg_info("%s: sender non-delivery notification: %s", 190 queue_id, STR(new_id)); 191 } else 192 bounce_status = 1; 193 194 /* 195 * Stop at the first sign of trouble, instead of making the 196 * problem worse. 197 */ 198 if (bounce_status != 0) 199 break; 200 201 /* 202 * Optionally, mark this recipient as done. 203 */ 204 if (flags & BOUNCE_FLAG_DELRCPT) 205 bounce_delrcpt_one(bounce_info); 206 } 207 208 /* 209 * Optionally, send a postmaster notice, subject to notify_classes 210 * restrictions. 211 * 212 * This postmaster notice is not critical, so if it fails don't 213 * retransmit the bounce that we just generated, just log a warning. 214 */ 215 #define SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE (notify_mask & MAIL_ERROR_BOUNCE) 216 217 if (SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE) { 218 219 /* 220 * Send the text with reason for the bounce, and the headers of 221 * the original message. Don't bother sending the boiler-plate 222 * text. This postmaster notice is not critical, so if it fails 223 * don't retransmit the bounce that we just generated, just log a 224 * warning. 225 */ 226 postmaster = var_bounce_rcpt; 227 if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(), 228 postmaster, 229 MAIL_SRC_MASK_BOUNCE, 230 NULL_TRACE_FLAGS, 231 smtputf8, 232 new_id)) != 0) { 233 if (bounce_header(bounce, bounce_info, postmaster, 234 POSTMASTER_COPY) == 0 235 && bounce_recipient_log(bounce, bounce_info) == 0 236 && bounce_header_dsn(bounce, bounce_info) == 0 237 && bounce_recipient_dsn(bounce, bounce_info) == 0) 238 bounce_original(bounce, bounce_info, DSN_RET_HDRS); 239 postmaster_status = post_mail_fclose(bounce); 240 if (postmaster_status == 0) 241 msg_info("%s: postmaster non-delivery notification: %s", 242 queue_id, STR(new_id)); 243 } else 244 postmaster_status = 1; 245 246 if (postmaster_status) 247 msg_warn("%s: postmaster notice failed while bouncing to %s", 248 queue_id, recipient); 249 } 250 } 251 252 /* 253 * Examine the completion status. Delete the bounce log file only when 254 * the bounce was posted successfully, and only if we are bouncing for 255 * real, not just warning. 256 */ 257 if (bounce_status == 0 && mail_queue_remove(service, queue_id) 258 && errno != ENOENT) 259 msg_fatal("remove %s %s: %m", service, queue_id); 260 261 /* 262 * Cleanup. 263 */ 264 bounce_mail_free(bounce_info); 265 vstring_free(verp_buf); 266 vstring_free(new_id); 267 268 return (bounce_status); 269 } 270