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 90int 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