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