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