1/* $NetBSD: bounce_workaround.c,v 1.2 2017/02/14 01:16:45 christos Exp $ */ 2 3/*++ 4/* NAME 5/* bounce_workaround 3 6/* SUMMARY 7/* Send non-delivery notification with sender override 8/* SYNOPSIS 9/* #include "local.h" 10/* 11/* int bounce_workaround(state) 12/* LOCAL_STATE state; 13/* DESCRIPTION 14/* This module works around a limitation in the bounce daemon 15/* protocol, namely, the assumption that the envelope sender 16/* address in a queue file is the delivery status notification 17/* address for all recipients in that queue file. The assumption 18/* is not valid when the local(8) delivery agent overrides the 19/* envelope sender address by an owner- alias, for one or more 20/* recipients in the queue file. 21/* 22/* Sender address override is a problem only when delivering 23/* to command or file, or when breaking a Delivered-To loop. 24/* The local(8) delivery agent saves normal recipients to a 25/* new queue file, together with the replacement envelope 26/* sender address; delivery then proceeds from that new queue 27/* file, and no workaround is needed. 28/* 29/* The workaround sends one non-delivery notification for each 30/* failed delivery that has a replacement sender address. The 31/* notifications are not aggregated, unlike notifications to 32/* non-replaced sender addresses. In practice, a local alias 33/* rarely has more than one file or command destination (if 34/* only because soft error handling is problematic). 35/* 36/* Arguments: 37/* .IP state 38/* The attributes that specify the message, recipient and more. 39/* Attributes describing alias, include or forward expansion. 40/* A table with the results from expanding aliases or lists. 41/* A table with delivered-to: addresses taken from the message. 42/* The non-delivery status must be either 4.X.X or 5.X.X. 43/* DIAGNOSTICS 44/* Fatal errors: out of memory. The result is non-zero when 45/* the operation should be tried again. Warnings: malformed 46/* address. 47/* BUGS 48/* The proper fix is to record in the bounce logfile an error 49/* return address for each individual recipient. This would 50/* eliminate the need for VERP-specific bounce protocol code, 51/* and would move complexity from the bounce client side to 52/* the bounce server side where it more likely belongs. 53/* LICENSE 54/* .ad 55/* .fi 56/* The Secure Mailer license must be distributed with this 57/* software. 58/* AUTHOR(S) 59/* Wietse Venema 60/* IBM T.J. Watson Research 61/* P.O. Box 704 62/* Yorktown Heights, NY 10598, USA 63/*--*/ 64 65/* System library. */ 66 67#include <sys_defs.h> 68#include <strings.h> 69 70/* Utility library. */ 71 72#include <msg.h> 73#include <mymalloc.h> 74#include <vstring.h> 75#include <split_at.h> 76 77/* Global library. */ 78 79#include <mail_params.h> 80#include <strip_addr.h> 81#include <stringops.h> 82#include <bounce.h> 83#include <defer.h> 84#include <split_addr.h> 85#include <canon_addr.h> 86 87/* Application-specific. */ 88 89#include "local.h" 90 91int bounce_workaround(LOCAL_STATE state) 92{ 93 const char *myname = "bounce_workaround"; 94 VSTRING *canon_owner = 0; 95 int rcpt_stat; 96 97 /* 98 * Look up the substitute sender address. 99 */ 100 if (var_ownreq_special) { 101 char *stripped_recipient; 102 char *owner_alias; 103 const char *owner_expansion; 104 105#define FIND_OWNER(lhs, rhs, addr) { \ 106 lhs = concatenate("owner-", addr, (char *) 0); \ 107 (void) split_at_right(lhs, '@'); \ 108 rhs = maps_find(alias_maps, lhs, DICT_FLAG_NONE); \ 109 } 110 111 FIND_OWNER(owner_alias, owner_expansion, state.msg_attr.rcpt.address); 112 if (alias_maps->error == 0 && owner_expansion == 0 113 && (stripped_recipient = strip_addr(state.msg_attr.rcpt.address, 114 (char **) 0, 115 var_rcpt_delim)) != 0) { 116 myfree(owner_alias); 117 FIND_OWNER(owner_alias, owner_expansion, stripped_recipient); 118 myfree(stripped_recipient); 119 } 120 if (alias_maps->error == 0 && owner_expansion != 0) { 121 canon_owner = canon_addr_internal(vstring_alloc(10), 122 var_exp_own_alias ? 123 owner_expansion : owner_alias); 124 SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level); 125 } 126 myfree(owner_alias); 127 if (alias_maps->error != 0) { 128 /* At this point, canon_owner == 0. */ 129 dsb_simple(state.msg_attr.why, "4.3.0", 130 "alias database unavailable"); 131 return (defer_append(BOUNCE_FLAGS(state.request), 132 BOUNCE_ATTR(state.msg_attr))); 133 } 134 } 135 136 /* 137 * Send a delivery status notification with a single recipient to the 138 * substitute sender address, before completion of the delivery request. 139 */ 140 if (canon_owner) { 141 rcpt_stat = 142 (STR(state.msg_attr.why->status)[0] == '4' ? 143 defer_one : bounce_one) 144 (BOUNCE_FLAGS(state.request), 145 BOUNCE_ONE_ATTR(state.msg_attr)); 146 vstring_free(canon_owner); 147 } 148 149 /* 150 * Send a regular delivery status notification, after completion of the 151 * delivery request. 152 */ 153 else { 154 rcpt_stat = 155 (STR(state.msg_attr.why->status)[0] == '4' ? 156 defer_append : bounce_append) 157 (BOUNCE_FLAGS(state.request), 158 BOUNCE_ATTR(state.msg_attr)); 159 } 160 return (rcpt_stat); 161} 162