1 /* $NetBSD: bounce.c,v 1.3 2022/10/08 16:12:45 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* bounce 3 6 /* SUMMARY 7 /* bounce service client 8 /* SYNOPSIS 9 /* #include <bounce.h> 10 /* 11 /* int bounce_append(flags, id, stats, recipient, relay, dsn) 12 /* int flags; 13 /* const char *id; 14 /* MSG_STATS *stats; 15 /* RECIPIENT *rcpt; 16 /* const char *relay; 17 /* DSN *dsn; 18 /* 19 /* int bounce_flush(flags, queue, id, encoding, smtputf8, sender, 20 /* dsn_envid, dsn_ret) 21 /* int flags; 22 /* const char *queue; 23 /* const char *id; 24 /* const char *encoding; 25 /* int smtputf8; 26 /* const char *sender; 27 /* const char *dsn_envid; 28 /* int dsn_ret; 29 /* 30 /* int bounce_flush_verp(flags, queue, id, encoding, smtputf8, 31 /* sender, dsn_envid, dsn_ret, verp_delims) 32 /* int flags; 33 /* const char *queue; 34 /* const char *id; 35 /* const char *encoding; 36 /* int smtputf8; 37 /* const char *sender; 38 /* const char *dsn_envid; 39 /* int dsn_ret; 40 /* const char *verp_delims; 41 /* 42 /* int bounce_one(flags, queue, id, encoding, smtputf8, sender, 43 /* dsn_envid, ret, stats, recipient, relay, dsn) 44 /* int flags; 45 /* const char *queue; 46 /* const char *id; 47 /* const char *encoding; 48 /* int smtputf8; 49 /* const char *sender; 50 /* const char *dsn_envid; 51 /* int dsn_ret; 52 /* MSG_STATS *stats; 53 /* RECIPIENT *rcpt; 54 /* const char *relay; 55 /* DSN *dsn; 56 /* 57 /* void bounce_client_init(title, maps) 58 /* const char *title; 59 /* const char *maps; 60 /* INTERNAL API 61 /* DSN_FILTER *delivery_status_filter; 62 /* 63 /* int bounce_append_intern(flags, id, stats, recipient, relay, dsn) 64 /* int flags; 65 /* const char *id; 66 /* MSG_STATS *stats; 67 /* RECIPIENT *rcpt; 68 /* const char *relay; 69 /* 70 /* int bounce_one_intern(flags, queue, id, encoding, smtputf8, sender, 71 /* dsn_envid, ret, stats, recipient, relay, dsn) 72 /* int flags; 73 /* const char *queue; 74 /* const char *id; 75 /* const char *encoding; 76 /* int smtputf8; 77 /* const char *sender; 78 /* const char *dsn_envid; 79 /* int dsn_ret; 80 /* MSG_STATS *stats; 81 /* RECIPIENT *rcpt; 82 /* const char *relay; 83 /* DSN *dsn; 84 /* DESCRIPTION 85 /* This module implements the client interface to the message 86 /* bounce service, which maintains a per-message log of status 87 /* records with recipients that were bounced, and the dsn_text why. 88 /* 89 /* bounce_append() appends a dsn_text for non-delivery to the 90 /* bounce log for the named recipient, updates the address 91 /* verification service, or updates a message delivery record 92 /* on request by the sender. The flags argument determines 93 /* the action. 94 /* 95 /* bounce_flush() actually bounces the specified message to 96 /* the specified sender, including the bounce log that was 97 /* built with bounce_append(). The bounce logfile is removed 98 /* upon successful completion. 99 /* 100 /* bounce_flush_verp() is like bounce_flush(), but sends one 101 /* notification per recipient, with the failed recipient encoded 102 /* into the sender address. 103 /* 104 /* bounce_one() bounces one recipient and immediately sends a 105 /* notification to the sender. This procedure does not append 106 /* the recipient and dsn_text to the per-message bounce log, and 107 /* should be used when a delivery agent changes the error 108 /* return address in a manner that depends on the recipient 109 /* address. 110 /* 111 /* bounce_client_init() initializes an optional DSN filter. 112 /* 113 /* bounce_append_intern() and bounce_one_intern() are for use 114 /* after the DSN filter. 115 /* 116 /* Arguments: 117 /* .IP flags 118 /* The bitwise OR of zero or more of the following (specify 119 /* BOUNCE_FLAG_NONE to request no special processing): 120 /* .RS 121 /* .IP BOUNCE_FLAG_CLEAN 122 /* Delete the bounce log in case of an error (as in: pretend 123 /* that we never even tried to bounce this message). 124 /* .IP BOUNCE_FLAG_DELRCPT 125 /* When specified with a flush request, request that 126 /* recipients be deleted from the queue file. 127 /* 128 /* Note: the bounce daemon ignores this request when the 129 /* recipient queue file offset is <= 0. 130 /* .IP DEL_REQ_FLAG_MTA_VRFY 131 /* The message is an MTA-requested address verification probe. 132 /* Update the address verification database instead of bouncing 133 /* mail. 134 /* .IP DEL_REQ_FLAG_USR_VRFY 135 /* The message is a user-requested address expansion probe. 136 /* Update the message delivery record instead of bouncing mail. 137 /* .IP DEL_REQ_FLAG_RECORD 138 /* This is a normal message with logged delivery. Update the 139 /* message delivery record and bounce the mail. 140 /* .RE 141 /* .IP queue 142 /* The message queue name of the original message file. 143 /* .IP id 144 /* The message queue id if the original message file. The bounce log 145 /* file has the same name as the original message file. 146 /* .IP stats 147 /* Time stamps from different message delivery stages 148 /* and session reuse count. 149 /* .IP rcpt 150 /* Recipient information. See recipient_list(3). 151 /* .IP relay 152 /* Name of the host that the message could not be delivered to. 153 /* This information is used for syslogging only. 154 /* .IP encoding 155 /* The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}. 156 /* .IP smtputf8 157 /* The level of SMTPUTF8 support (to be defined). 158 /* .IP sender 159 /* The sender envelope address. 160 /* .IP dsn_envid 161 /* Optional DSN envelope ID. 162 /* .IP dsn_ret 163 /* Optional DSN return full/headers option. 164 /* .IP dsn 165 /* Delivery status. See dsn(3). The specified action is ignored. 166 /* .IP verp_delims 167 /* VERP delimiter characters, used when encoding the failed 168 /* sender into the envelope sender address. 169 /* DIAGNOSTICS 170 /* In case of success, these functions log the action, and return a 171 /* zero value. Otherwise, the functions return a non-zero result, 172 /* and when BOUNCE_FLAG_CLEAN is disabled, log that message 173 /* delivery is deferred. 174 /* .IP title 175 /* The origin of the optional DSN filter lookup table names. 176 /* .IP maps 177 /* The optional "type:table" DSN filter lookup table names, 178 /* separated by comma or whitespace. 179 /* BUGS 180 /* Should be replaced by routines with an attribute-value based 181 /* interface instead of an interface that uses a rigid argument list. 182 /* LICENSE 183 /* .ad 184 /* .fi 185 /* The Secure Mailer license must be distributed with this software. 186 /* AUTHOR(S) 187 /* Wietse Venema 188 /* IBM T.J. Watson Research 189 /* P.O. Box 704 190 /* Yorktown Heights, NY 10598, USA 191 /* 192 /* Wietse Venema 193 /* Google, Inc. 194 /* 111 8th Avenue 195 /* New York, NY 10011, USA 196 /*--*/ 197 198 /* System library. */ 199 200 #include <sys_defs.h> 201 #include <string.h> 202 203 /* Utility library. */ 204 205 #include <msg.h> 206 #include <vstring.h> 207 #include <mymalloc.h> 208 209 /* Global library. */ 210 211 #define DSN_INTERN 212 #include <mail_params.h> 213 #include <mail_proto.h> 214 #include <log_adhoc.h> 215 #include <dsn_util.h> 216 #include <rcpt_print.h> 217 #include <dsn_print.h> 218 #include <verify.h> 219 #include <defer.h> 220 #include <trace.h> 221 #include <bounce.h> 222 223 /* Shared internally, between bounce and defer clients. */ 224 225 DSN_FILTER *delivery_status_filter; 226 227 /* bounce_append - append delivery status to per-message bounce log */ 228 229 int bounce_append(int flags, const char *id, MSG_STATS *stats, 230 RECIPIENT *rcpt, const char *relay, 231 DSN *dsn) 232 { 233 DSN my_dsn = *dsn; 234 DSN *dsn_res; 235 236 /* 237 * Sanity check. If we're really confident, change this into msg_panic 238 * (remember, this information may be under control by a hostile server). 239 */ 240 if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) { 241 msg_warn("bounce_append: ignoring dsn code \"%s\"", my_dsn.status); 242 my_dsn.status = "5.0.0"; 243 } 244 245 /* 246 * DSN filter (Postfix 3.0). 247 */ 248 if (delivery_status_filter != 0 249 && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) { 250 if (dsn_res->status[0] == '4') 251 return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res)); 252 my_dsn = *dsn_res; 253 } 254 return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn)); 255 } 256 257 /* bounce_append_intern - append delivery status to per-message bounce log */ 258 259 int bounce_append_intern(int flags, const char *id, MSG_STATS *stats, 260 RECIPIENT *rcpt, const char *relay, 261 DSN *dsn) 262 { 263 DSN my_dsn = *dsn; 264 int status; 265 266 /* 267 * MTA-requested address verification information is stored in the verify 268 * service database. 269 */ 270 if (flags & DEL_REQ_FLAG_MTA_VRFY) { 271 my_dsn.action = "undeliverable"; 272 status = verify_append(id, stats, rcpt, relay, &my_dsn, 273 DEL_RCPT_STAT_BOUNCE); 274 return (status); 275 } 276 277 /* 278 * User-requested address verification information is logged and mailed 279 * to the requesting user. 280 */ 281 if (flags & DEL_REQ_FLAG_USR_VRFY) { 282 my_dsn.action = "undeliverable"; 283 status = trace_append(flags, id, stats, rcpt, relay, &my_dsn); 284 return (status); 285 } 286 287 /* 288 * Normal (well almost) delivery. When we're pretending that we can't 289 * bounce, don't create a defer log file when we wouldn't keep the bounce 290 * log file. That's a lot of negatives in one sentence. 291 */ 292 else if (var_soft_bounce && (flags & BOUNCE_FLAG_CLEAN)) { 293 return (-1); 294 } 295 296 /* 297 * Normal mail delivery. May also send a delivery record to the user. 298 * 299 * XXX DSN We write all recipients to the bounce logfile regardless of DSN 300 * NOTIFY options, because those options don't apply to postmaster 301 * notifications. 302 */ 303 else { 304 char *my_status = mystrdup(my_dsn.status); 305 const char *log_status = var_soft_bounce ? "SOFTBOUNCE" : "bounced"; 306 307 /* 308 * Supply default action. 309 */ 310 my_dsn.status = my_status; 311 if (var_soft_bounce) { 312 my_status[0] = '4'; 313 my_dsn.action = "delayed"; 314 } else { 315 my_dsn.action = "failed"; 316 } 317 318 if (mail_command_client(MAIL_CLASS_PRIVATE, var_soft_bounce ? 319 var_defer_service : var_bounce_service, 320 MAIL_ATTR_PROTO_BOUNCE, 321 SEND_ATTR_INT(MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND), 322 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags), 323 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id), 324 SEND_ATTR_FUNC(rcpt_print, (const void *) rcpt), 325 SEND_ATTR_FUNC(dsn_print, (const void *) &my_dsn), 326 ATTR_TYPE_END) == 0 327 && ((flags & DEL_REQ_FLAG_RECORD) == 0 328 || trace_append(flags, id, stats, rcpt, relay, 329 &my_dsn) == 0)) { 330 log_adhoc(id, stats, rcpt, relay, &my_dsn, log_status); 331 status = (var_soft_bounce ? -1 : 0); 332 } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) { 333 VSTRING *junk = vstring_alloc(100); 334 335 my_dsn.status = "4.3.0"; 336 vstring_sprintf(junk, "%s or %s service failure", 337 var_bounce_service, var_trace_service); 338 my_dsn.reason = vstring_str(junk); 339 status = defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn); 340 vstring_free(junk); 341 } else { 342 status = -1; 343 } 344 myfree(my_status); 345 return (status); 346 } 347 } 348 349 /* bounce_flush - flush the bounce log and deliver to the sender */ 350 351 int bounce_flush(int flags, const char *queue, const char *id, 352 const char *encoding, int smtputf8, 353 const char *sender, const char *dsn_envid, 354 int dsn_ret) 355 { 356 357 /* 358 * When we're pretending that we can't bounce, don't send a bounce 359 * message. 360 */ 361 if (var_soft_bounce) 362 return (-1); 363 if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service, 364 MAIL_ATTR_PROTO_BOUNCE, 365 SEND_ATTR_INT(MAIL_ATTR_NREQ, BOUNCE_CMD_FLUSH), 366 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags), 367 SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue), 368 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id), 369 SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding), 370 SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, smtputf8), 371 SEND_ATTR_STR(MAIL_ATTR_SENDER, sender), 372 SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid), 373 SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret), 374 ATTR_TYPE_END) == 0) { 375 return (0); 376 } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) { 377 msg_info("%s: status=deferred (bounce failed)", id); 378 return (-1); 379 } else { 380 return (-1); 381 } 382 } 383 384 /* bounce_flush_verp - verpified notification */ 385 386 int bounce_flush_verp(int flags, const char *queue, const char *id, 387 const char *encoding, int smtputf8, 388 const char *sender, const char *dsn_envid, 389 int dsn_ret, const char *verp_delims) 390 { 391 392 /* 393 * When we're pretending that we can't bounce, don't send a bounce 394 * message. 395 */ 396 if (var_soft_bounce) 397 return (-1); 398 if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service, 399 MAIL_ATTR_PROTO_BOUNCE, 400 SEND_ATTR_INT(MAIL_ATTR_NREQ, BOUNCE_CMD_VERP), 401 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags), 402 SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue), 403 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id), 404 SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding), 405 SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, smtputf8), 406 SEND_ATTR_STR(MAIL_ATTR_SENDER, sender), 407 SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid), 408 SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret), 409 SEND_ATTR_STR(MAIL_ATTR_VERPDL, verp_delims), 410 ATTR_TYPE_END) == 0) { 411 return (0); 412 } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) { 413 msg_info("%s: status=deferred (bounce failed)", id); 414 return (-1); 415 } else { 416 return (-1); 417 } 418 } 419 420 /* bounce_one - send notice for one recipient */ 421 422 int bounce_one(int flags, const char *queue, const char *id, 423 const char *encoding, int smtputf8, 424 const char *sender, const char *dsn_envid, 425 int dsn_ret, MSG_STATS *stats, RECIPIENT *rcpt, 426 const char *relay, DSN *dsn) 427 { 428 DSN my_dsn = *dsn; 429 DSN *dsn_res; 430 431 /* 432 * Sanity check. 433 */ 434 if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) { 435 msg_warn("bounce_one: ignoring dsn code \"%s\"", my_dsn.status); 436 my_dsn.status = "5.0.0"; 437 } 438 439 /* 440 * DSN filter (Postfix 3.0). 441 */ 442 if (delivery_status_filter != 0 443 && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) { 444 if (dsn_res->status[0] == '4') 445 return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res)); 446 my_dsn = *dsn_res; 447 } 448 return (bounce_one_intern(flags, queue, id, encoding, smtputf8, sender, 449 dsn_envid, dsn_ret, stats, rcpt, relay, &my_dsn)); 450 } 451 452 /* bounce_one_intern - send notice for one recipient */ 453 454 int bounce_one_intern(int flags, const char *queue, const char *id, 455 const char *encoding, int smtputf8, 456 const char *sender, const char *dsn_envid, 457 int dsn_ret, MSG_STATS *stats, 458 RECIPIENT *rcpt, const char *relay, 459 DSN *dsn) 460 { 461 DSN my_dsn = *dsn; 462 int status; 463 464 /* 465 * MTA-requested address verification information is stored in the verify 466 * service database. 467 */ 468 if (flags & DEL_REQ_FLAG_MTA_VRFY) { 469 my_dsn.action = "undeliverable"; 470 status = verify_append(id, stats, rcpt, relay, &my_dsn, 471 DEL_RCPT_STAT_BOUNCE); 472 return (status); 473 } 474 475 /* 476 * User-requested address verification information is logged and mailed 477 * to the requesting user. 478 */ 479 if (flags & DEL_REQ_FLAG_USR_VRFY) { 480 my_dsn.action = "undeliverable"; 481 status = trace_append(flags, id, stats, rcpt, relay, &my_dsn); 482 return (status); 483 } 484 485 /* 486 * When we're not bouncing, then use the standard multi-recipient logfile 487 * based procedure. 488 */ 489 else if (var_soft_bounce) { 490 return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn)); 491 } 492 493 /* 494 * Normal mail delivery. May also send a delivery record to the user. 495 * 496 * XXX DSN We send all recipients regardless of DSN NOTIFY options, because 497 * those options don't apply to postmaster notifications. 498 */ 499 else { 500 501 /* 502 * Supply default action. 503 */ 504 my_dsn.action = "failed"; 505 506 if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service, 507 MAIL_ATTR_PROTO_BOUNCE, 508 SEND_ATTR_INT(MAIL_ATTR_NREQ, BOUNCE_CMD_ONE), 509 SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags), 510 SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue), 511 SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id), 512 SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding), 513 SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, smtputf8), 514 SEND_ATTR_STR(MAIL_ATTR_SENDER, sender), 515 SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid), 516 SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret), 517 SEND_ATTR_FUNC(rcpt_print, (const void *) rcpt), 518 SEND_ATTR_FUNC(dsn_print, (const void *) &my_dsn), 519 ATTR_TYPE_END) == 0 520 && ((flags & DEL_REQ_FLAG_RECORD) == 0 521 || trace_append(flags, id, stats, rcpt, relay, 522 &my_dsn) == 0)) { 523 log_adhoc(id, stats, rcpt, relay, &my_dsn, "bounced"); 524 status = 0; 525 } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) { 526 VSTRING *junk = vstring_alloc(100); 527 528 my_dsn.status = "4.3.0"; 529 vstring_sprintf(junk, "%s or %s service failure", 530 var_bounce_service, var_trace_service); 531 my_dsn.reason = vstring_str(junk); 532 status = defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn); 533 vstring_free(junk); 534 } else { 535 status = -1; 536 } 537 return (status); 538 } 539 } 540 541 /* bounce_client_init - initialize bounce/defer DSN filter */ 542 543 void bounce_client_init(const char *title, const char *maps) 544 { 545 static const char myname[] = "bounce_client_init"; 546 547 if (delivery_status_filter != 0) 548 msg_panic("%s: duplicate initialization", myname); 549 if (*maps) 550 delivery_status_filter = dsn_filter_create(title, maps); 551 } 552