1/* $NetBSD: cleanup_out_recipient.c,v 1.4 2022/10/08 16:12:45 christos Exp $ */ 2 3/*++ 4/* NAME 5/* cleanup_out_recipient 3 6/* SUMMARY 7/* envelope recipient output filter 8/* SYNOPSIS 9/* #include "cleanup.h" 10/* 11/* void cleanup_out_recipient(state, dsn_orig_recipient, 12/* dsn_notify, orig_recipient, 13/* recipient) 14/* CLEANUP_STATE *state; 15/* const char *dsn_orig_recipient; 16/* const char *dsn_notify; 17/* const char *orig_recipient; 18/* const char *recipient; 19/* DESCRIPTION 20/* This module implements an envelope recipient output filter. 21/* 22/* cleanup_out_recipient() performs virtual table expansion 23/* and recipient duplicate filtering, and appends the 24/* resulting recipients to the output stream. It also 25/* generates DSN SUCCESS notifications. 26/* 27/* Arguments: 28/* .IP state 29/* Cleanup server state. 30/* .IP dsn_orig_recipient 31/* DSN original recipient information. 32/* .IP dsn_notify 33/* DSN notify flags. 34/* .IP orig_recipient 35/* Envelope recipient as received by Postfix. 36/* .IP recipient 37/* Envelope recipient as rewritten by Postfix. 38/* CONFIGURATION 39/* .ad 40/* .fi 41/* .IP enable_original_recipient 42/* Enable orig_recipient support. 43/* .IP local_duplicate_filter_limit 44/* Upper bound to the size of the recipient duplicate filter. 45/* Zero means no limit; this may cause the mail system to 46/* become stuck. 47/* .IP virtual_alias_maps 48/* list of virtual address lookup tables. 49/* LICENSE 50/* .ad 51/* .fi 52/* The Secure Mailer license must be distributed with this software. 53/* AUTHOR(S) 54/* Wietse Venema 55/* IBM T.J. Watson Research 56/* P.O. Box 704 57/* Yorktown Heights, NY 10598, USA 58/* 59/* Wietse Venema 60/* Google, Inc. 61/* 111 8th Avenue 62/* New York, NY 10011, USA 63/*--*/ 64 65/* System library. */ 66 67#include <sys_defs.h> 68#include <string.h> 69 70/* Utility library. */ 71 72#include <argv.h> 73#include <msg.h> 74 75/* Global library. */ 76 77#include <been_here.h> 78#include <mail_params.h> 79#include <rec_type.h> 80#include <ext_prop.h> 81#include <cleanup_user.h> 82#include <dsn_mask.h> 83#include <recipient_list.h> 84#include <dsn.h> 85#include <trace.h> 86#include <verify.h> 87#include <mail_queue.h> /* cleanup_trace_path */ 88#include <mail_proto.h> 89#include <msg_stats.h> 90 91/* Application-specific. */ 92 93#include "cleanup.h" 94 95/* cleanup_trace_append - update trace logfile */ 96 97static void cleanup_trace_append(CLEANUP_STATE *state, RECIPIENT *rcpt, 98 DSN *dsn) 99{ 100 MSG_STATS stats; 101 102 if (cleanup_trace_path == 0) { 103 cleanup_trace_path = vstring_alloc(10); 104 mail_queue_path(cleanup_trace_path, MAIL_QUEUE_TRACE, 105 state->queue_id); 106 } 107 if (trace_append(BOUNCE_FLAG_CLEAN, state->queue_id, 108 CLEANUP_MSG_STATS(&stats, state), 109 rcpt, "none", dsn) != 0) { 110 msg_warn("%s: trace logfile update error", state->queue_id); 111 state->errs |= CLEANUP_STAT_WRITE; 112 } 113} 114 115/* cleanup_verify_append - update verify daemon */ 116 117static void cleanup_verify_append(CLEANUP_STATE *state, RECIPIENT *rcpt, 118 DSN *dsn, int verify_status) 119{ 120 MSG_STATS stats; 121 122 if (verify_append(state->queue_id, CLEANUP_MSG_STATS(&stats, state), 123 rcpt, "none", dsn, verify_status) != 0) { 124 msg_warn("%s: verify service update error", state->queue_id); 125 state->errs |= CLEANUP_STAT_WRITE; 126 } 127} 128 129/* cleanup_out_recipient - envelope recipient output filter */ 130 131void cleanup_out_recipient(CLEANUP_STATE *state, 132 const char *dsn_orcpt, 133 int dsn_notify, 134 const char *orcpt, 135 const char *recip) 136{ 137 ARGV *argv; 138 char **cpp; 139 140 /* 141 * XXX Not elegant, but eliminates complexity in the record reading loop. 142 */ 143 if (dsn_orcpt == 0) 144 dsn_orcpt = ""; 145 146 /* 147 * Distinguish between different original recipient addresses that map 148 * onto the same mailbox. The recipient will use our original recipient 149 * message header to figure things out. 150 * 151 * Postfix 2.2 compatibility: when ignoring differences in Postfix original 152 * recipient information, also ignore differences in DSN attributes. We 153 * do, however, keep the DSN attributes of the recipient that survives 154 * duplicate elimination. 155 */ 156#define STREQ(x, y) (strcmp((x), (y)) == 0) 157 158 if ((state->flags & CLEANUP_FLAG_MAP_OK) == 0 159 || cleanup_virt_alias_maps == 0) { 160 /* Matches been_here_drop{,_fixed}() calls cleanup_del_rcpt(). */ 161 if ((var_enable_orcpt ? 162 been_here(state->dups, "%s\n%d\n%s\n%s", 163 dsn_orcpt, dsn_notify, orcpt, recip) : 164 been_here_fixed(state->dups, recip)) == 0) { 165 if (dsn_notify) 166 cleanup_out_format(state, REC_TYPE_ATTR, "%s=%d", 167 MAIL_ATTR_DSN_NOTIFY, dsn_notify); 168 if (*dsn_orcpt) 169 cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s", 170 MAIL_ATTR_DSN_ORCPT, dsn_orcpt); 171 cleanup_out_string(state, REC_TYPE_ORCP, orcpt); 172 cleanup_out_string(state, REC_TYPE_RCPT, recip); 173 state->rcpt_count++; 174 } 175 } 176 177 /* 178 * XXX DSN. RFC 3461 gives us three options for multi-recipient aliases 179 * (we're treating single recipient aliases as a special case of 180 * multi-recipient aliases, one argument being that it is none of the 181 * sender's business). 182 * 183 * (a) Don't propagate ENVID, NOTIFY, RET, or ORCPT. If NOTIFY specified 184 * SUCCESS, send a "relayed" DSN. 185 * 186 * (b) Propagate ENVID, (NOTIFY minus SUCCESS), RET, and ORCPT. If NOTIFY 187 * specified SUCCESS, send an "expanded" DSN. 188 * 189 * (c) Propagate ENVID, NOTIFY, RET, and ORCPT to one recipient only. Send 190 * no DSN. 191 * 192 * In all three cases we are modifying at least one NOTIFY value. Either we 193 * have to record explicit dsn_notify records, or we must not allow the 194 * use of a per-message non-default NOTIFY value that applies to all 195 * recipient records. 196 * 197 * Alternatives (a) and (c) require that we store explicit per-recipient RET 198 * and ENVID records, at least for the recipients that are excluded from 199 * RET and ENVID propagation. This means storing explicit ENVID records 200 * to indicate that the information does not exist. All this makes 201 * alternative (b) more and more attractive. It is no surprise that we 202 * use (b) here and in the local delivery agent. 203 * 204 * In order to generate a SUCCESS notification from the cleanup server we 205 * have to write the trace logfile record now. We're NOT going to flush 206 * the trace file from the cleanup server; if we need to write bounce 207 * logfile records, and the bounce service fails, we must be able to 208 * cancel the entire cleanup request including any success or failure 209 * notifications. The queue manager will flush the trace (and bounce) 210 * logfile, possibly after it has generated its own success or failure 211 * notification records. 212 * 213 * Postfix 2.2 compatibility: when ignoring differences in Postfix original 214 * recipient information, also ignore differences in DSN attributes. We 215 * do, however, keep the DSN attributes of the recipient that survives 216 * duplicate elimination. 217 * 218 * In the case of a verify(8) request for a one-to-many alias, declare the 219 * alias address as "deliverable". Do not verify the individual addresses 220 * in the expansion because that results in multiple verify(8) updates 221 * for one verify(8) request. 222 * 223 * Multiple verify(8) updates for one verify(8) request would overwrite 224 * each other's status, and if the last status update is "undeliverable", 225 * then the whole alias is flagged as undeliverable. 226 */ 227 else { 228 RECIPIENT rcpt; 229 DSN dsn; 230 231 argv = cleanup_map1n_internal(state, recip, cleanup_virt_alias_maps, 232 cleanup_ext_prop_mask & EXT_PROP_VIRTUAL); 233 if (argv->argc > 1 && (state->tflags & DEL_REQ_FLAG_MTA_VRFY)) { 234 (void) DSN_SIMPLE(&dsn, "2.0.0", "aliased to multiple recipients"); 235 dsn.action = "deliverable"; 236 RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip); 237 cleanup_verify_append(state, &rcpt, &dsn, DEL_RCPT_STAT_OK); 238 argv_free(argv); 239 return; 240 } 241 if ((dsn_notify & DSN_NOTIFY_SUCCESS) 242 && (argv->argc > 1 || strcmp(recip, argv->argv[0]) != 0)) { 243 (void) DSN_SIMPLE(&dsn, "2.0.0", "alias expanded"); 244 dsn.action = "expanded"; 245 RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip); 246 cleanup_trace_append(state, &rcpt, &dsn); 247 dsn_notify = (dsn_notify == DSN_NOTIFY_SUCCESS ? DSN_NOTIFY_NEVER : 248 dsn_notify & ~DSN_NOTIFY_SUCCESS); 249 } 250 for (cpp = argv->argv; *cpp; cpp++) { 251 if ((var_enable_orcpt ? 252 been_here(state->dups, "%s\n%d\n%s\n%s", 253 dsn_orcpt, dsn_notify, orcpt, *cpp) : 254 been_here_fixed(state->dups, *cpp)) == 0) { 255 if (dsn_notify) 256 cleanup_out_format(state, REC_TYPE_ATTR, "%s=%d", 257 MAIL_ATTR_DSN_NOTIFY, dsn_notify); 258 if (*dsn_orcpt) 259 cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s", 260 MAIL_ATTR_DSN_ORCPT, dsn_orcpt); 261 cleanup_out_string(state, REC_TYPE_ORCP, orcpt); 262 cleanup_out_string(state, REC_TYPE_RCPT, *cpp); 263 state->rcpt_count++; 264 } 265 } 266 argv_free(argv); 267 } 268} 269