1 /* $NetBSD: cleanup_out_recipient.c,v 1.3 2020/03/18 19:05:15 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 97 static 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 117 static 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 131 void 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 if ((var_enable_orcpt ? 161 been_here(state->dups, "%s\n%d\n%s\n%s", 162 dsn_orcpt, dsn_notify, orcpt, recip) : 163 been_here_fixed(state->dups, recip)) == 0) { 164 if (dsn_notify) 165 cleanup_out_format(state, REC_TYPE_ATTR, "%s=%d", 166 MAIL_ATTR_DSN_NOTIFY, dsn_notify); 167 if (*dsn_orcpt) 168 cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s", 169 MAIL_ATTR_DSN_ORCPT, dsn_orcpt); 170 cleanup_out_string(state, REC_TYPE_ORCP, orcpt); 171 cleanup_out_string(state, REC_TYPE_RCPT, recip); 172 state->rcpt_count++; 173 } 174 } 175 176 /* 177 * XXX DSN. RFC 3461 gives us three options for multi-recipient aliases 178 * (we're treating single recipient aliases as a special case of 179 * multi-recipient aliases, one argument being that it is none of the 180 * sender's business). 181 * 182 * (a) Don't propagate ENVID, NOTIFY, RET, or ORCPT. If NOTIFY specified 183 * SUCCESS, send a "relayed" DSN. 184 * 185 * (b) Propagate ENVID, (NOTIFY minus SUCCESS), RET, and ORCPT. If NOTIFY 186 * specified SUCCESS, send an "expanded" DSN. 187 * 188 * (c) Propagate ENVID, NOTIFY, RET, and ORCPT to one recipient only. Send 189 * no DSN. 190 * 191 * In all three cases we are modifying at least one NOTIFY value. Either we 192 * have to record explicit dsn_notify records, or we must not allow the 193 * use of a per-message non-default NOTIFY value that applies to all 194 * recipient records. 195 * 196 * Alternatives (a) and (c) require that we store explicit per-recipient RET 197 * and ENVID records, at least for the recipients that are excluded from 198 * RET and ENVID propagation. This means storing explicit ENVID records 199 * to indicate that the information does not exist. All this makes 200 * alternative (b) more and more attractive. It is no surprise that we 201 * use (b) here and in the local delivery agent. 202 * 203 * In order to generate a SUCCESS notification from the cleanup server we 204 * have to write the trace logfile record now. We're NOT going to flush 205 * the trace file from the cleanup server; if we need to write bounce 206 * logfile records, and the bounce service fails, we must be able to 207 * cancel the entire cleanup request including any success or failure 208 * notifications. The queue manager will flush the trace (and bounce) 209 * logfile, possibly after it has generated its own success or failure 210 * notification records. 211 * 212 * Postfix 2.2 compatibility: when ignoring differences in Postfix original 213 * recipient information, also ignore differences in DSN attributes. We 214 * do, however, keep the DSN attributes of the recipient that survives 215 * duplicate elimination. 216 * 217 * In the case of a verify(8) request for a one-to-many alias, declare the 218 * alias address as "deliverable". Do not verify the individual addresses 219 * in the expansion because that results in multiple verify(8) updates 220 * for one verify(8) request. 221 * 222 * Multiple verify(8) updates for one verify(8) request would overwrite 223 * each other's status, and if the last status update is "undeliverable", 224 * then the whole alias is flagged as undeliverable. 225 */ 226 else { 227 RECIPIENT rcpt; 228 DSN dsn; 229 230 argv = cleanup_map1n_internal(state, recip, cleanup_virt_alias_maps, 231 cleanup_ext_prop_mask & EXT_PROP_VIRTUAL); 232 if (argv->argc > 1 && (state->tflags & DEL_REQ_FLAG_MTA_VRFY)) { 233 (void) DSN_SIMPLE(&dsn, "2.0.0", "aliased to multiple recipients"); 234 dsn.action = "deliverable"; 235 RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip); 236 cleanup_verify_append(state, &rcpt, &dsn, DEL_RCPT_STAT_OK); 237 argv_free(argv); 238 return; 239 } 240 if ((dsn_notify & DSN_NOTIFY_SUCCESS) 241 && (argv->argc > 1 || strcmp(recip, argv->argv[0]) != 0)) { 242 (void) DSN_SIMPLE(&dsn, "2.0.0", "alias expanded"); 243 dsn.action = "expanded"; 244 RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip); 245 cleanup_trace_append(state, &rcpt, &dsn); 246 dsn_notify = (dsn_notify == DSN_NOTIFY_SUCCESS ? DSN_NOTIFY_NEVER : 247 dsn_notify & ~DSN_NOTIFY_SUCCESS); 248 } 249 for (cpp = argv->argv; *cpp; cpp++) { 250 if ((var_enable_orcpt ? 251 been_here(state->dups, "%s\n%d\n%s\n%s", 252 dsn_orcpt, dsn_notify, orcpt, *cpp) : 253 been_here_fixed(state->dups, *cpp)) == 0) { 254 if (dsn_notify) 255 cleanup_out_format(state, REC_TYPE_ATTR, "%s=%d", 256 MAIL_ATTR_DSN_NOTIFY, dsn_notify); 257 if (*dsn_orcpt) 258 cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s", 259 MAIL_ATTR_DSN_ORCPT, dsn_orcpt); 260 cleanup_out_string(state, REC_TYPE_ORCP, orcpt); 261 cleanup_out_string(state, REC_TYPE_RCPT, *cpp); 262 state->rcpt_count++; 263 } 264 } 265 argv_free(argv); 266 } 267 } 268