1 /* $NetBSD: smtp_trouble.c,v 1.4 2026/05/09 18:49:20 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* smtp_trouble 3 6 /* SUMMARY 7 /* error handler policies 8 /* SYNOPSIS 9 /* #include "smtp.h" 10 /* 11 /* int smtp_sess_fail(state) 12 /* SMTP_STATE *state; 13 /* 14 /* int smtp_site_fail(state, mta_name, resp, format, ...) 15 /* SMTP_STATE *state; 16 /* const char *mta_name; 17 /* SMTP_RESP *resp; 18 /* const char *format; 19 /* 20 /* int smtp_mesg_fail(state, mta_name, resp, format, ...) 21 /* SMTP_STATE *state; 22 /* const char *mta_name; 23 /* SMTP_RESP *resp; 24 /* const char *format; 25 /* 26 /* void smtp_rcpt_fail(state, recipient, mta_name, resp, format, ...) 27 /* SMTP_STATE *state; 28 /* RECIPIENT *recipient; 29 /* const char *mta_name; 30 /* SMTP_RESP *resp; 31 /* const char *format; 32 /* 33 /* int smtp_stream_except(state, exception, description) 34 /* SMTP_STATE *state; 35 /* int exception; 36 /* const char *description; 37 /* AUXILIARY FUNCTIONS 38 /* int smtp_misc_fail(state, flags, mta_name, resp, format, ...) 39 /* SMTP_STATE *state; 40 /* int flags; 41 /* const char *mta_name; 42 /* SMTP_RESP *resp; 43 /* const char *format; 44 /* DESCRIPTION 45 /* This module handles all non-fatal errors that can happen while 46 /* attempting to deliver mail via SMTP, and implements the policy 47 /* of how to deal with the error. Depending on the nature of 48 /* the problem, delivery of a single message is deferred, delivery 49 /* of all messages to the same domain is deferred, or one or more 50 /* recipients are given up as non-deliverable and a bounce log is 51 /* updated. In any case, the recipient is marked as either KEEP 52 /* (try again with a backup host) or DROP (delete recipient from 53 /* delivery request). 54 /* 55 /* In addition, when an unexpected response code is seen such 56 /* as 3xx where only 4xx or 5xx are expected, or any error code 57 /* that suggests a syntax error or something similar, the 58 /* protocol error flag is set so that the postmaster receives 59 /* a transcript of the session. No notification is generated for 60 /* what appear to be configuration errors - very likely, they 61 /* would suffer the same problem and just cause more trouble. 62 /* 63 /* In case of a soft error, action depends on whether the error 64 /* qualifies for trying the request with other mail servers (log 65 /* an informational record only and try a backup server) or 66 /* whether this is the final server (log recipient delivery status 67 /* records and delete the recipient from the request). 68 /* 69 /* smtp_sess_fail() takes a pre-formatted error report after 70 /* failure to complete some protocol handshake. The policy is 71 /* as with smtp_site_fail(). 72 /* 73 /* smtp_site_fail() handles the case where the program fails to 74 /* complete the initial handshake: the server is not reachable, 75 /* is not running, does not want talk to us, or we talk to ourselves. 76 /* The \fIcode\fR gives an error status code; the \fIformat\fR 77 /* argument gives a textual description. 78 /* The policy is: soft error, non-final server: log an informational 79 /* record why the host is being skipped; soft error, final server: 80 /* defer delivery of all remaining recipients and mark the destination 81 /* as problematic; hard error: bounce all remaining recipients. 82 /* The session is marked as "do not cache". 83 /* The result is non-zero. 84 /* 85 /* smtp_mesg_fail() handles the case where the smtp server 86 /* does not accept the sender address or the message data, 87 /* or when the local MTA is unable to convert the message data. 88 /* The policy is: soft error, non-final server: log an informational 89 /* record why the host is being skipped; soft error, final server: 90 /* defer delivery of all remaining recipients; hard error: bounce all 91 /* remaining recipients. 92 /* The result is non-zero. 93 /* 94 /* smtp_misc_fail() provides a more detailed interface than 95 /* smtp_site_fail() and smtp_mesg_fail(), which are convenience 96 /* wrappers around smtp_misc_fail(). See the flags argument below. 97 /* 98 /* smtp_rcpt_fail() handles the case where a recipient is not 99 /* accepted by the server for reasons other than that the server 100 /* recipient limit is reached. 101 /* The policy is: soft error, non-final server: log an informational 102 /* record why the recipient is being skipped; soft error, final server: 103 /* defer delivery of this recipient; hard error: bounce this 104 /* recipient. 105 /* 106 /* smtp_stream_except() handles the exceptions generated by 107 /* the smtp_stream(3) module (i.e. timeouts and I/O errors). 108 /* The \fIexception\fR argument specifies the type of problem. 109 /* The \fIdescription\fR argument describes at what stage of 110 /* the SMTP dialog the problem happened. 111 /* The policy is: non-final server: log an informational record 112 /* with the reason why the host is being skipped; final server: 113 /* defer delivery of all remaining recipients. 114 /* Retry plaintext delivery after TLS post-handshake session 115 /* failure, provided that at least one recipient was not 116 /* deferred or rejected during the TLS phase, and that global 117 /* preconditions for plaintext fallback are met. 118 /* The session is marked as "do not cache". 119 /* The result is non-zero. 120 /* 121 /* Arguments: 122 /* .IP state 123 /* SMTP client state per delivery request. 124 /* .IP flags 125 /* Either SMTP_MISC_FAIL_NONE or the bitwise OR of 126 /* .RS 127 /* .IP SMTP_MISC_FAIL_THROTTLE 128 /* Mark the destination as problematic. 129 /* .IP SMTP_MISC_FAIL_SOFT_NON_FINAL 130 /* If the server was not the last one to try, treat a hard error 131 /* as a soft error. 132 /* .IP SMTP_MISC_FAIL_DONT_CACHE 133 /* Do not save the connection to the cache. This flag is ignored 134 /* when SMTP_MISC_FAIL_THROTTLE is in effect. 135 /* .IP resp 136 /* Server response including reply code and text. 137 /* .IP recipient 138 /* Undeliverable recipient address information. 139 /* .IP format 140 /* Human-readable description of why mail is not deliverable. 141 /* DIAGNOSTICS 142 /* Panic: unknown exception code. 143 /* SEE ALSO 144 /* smtp_proto(3) smtp high-level protocol 145 /* smtp_stream(3) smtp low-level protocol 146 /* defer(3) basic message defer interface 147 /* bounce(3) basic message bounce interface 148 /* LICENSE 149 /* .ad 150 /* .fi 151 /* The Secure Mailer license must be distributed with this software. 152 /* AUTHOR(S) 153 /* Wietse Venema 154 /* IBM T.J. Watson Research 155 /* P.O. Box 704 156 /* Yorktown Heights, NY 10598, USA 157 /* 158 /* Wietse Venema 159 /* Google, Inc. 160 /* 111 8th Avenue 161 /* New York, NY 10011, USA 162 /*--*/ 163 164 /* System library. */ 165 166 #include <sys_defs.h> 167 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */ 168 #include <stdarg.h> 169 #include <string.h> 170 171 /* Utility library. */ 172 173 #include <msg.h> 174 #include <vstring.h> 175 #include <stringops.h> 176 177 /* Global library. */ 178 179 #include <smtp_stream.h> 180 #include <deliver_request.h> 181 #include <deliver_completed.h> 182 #include <bounce.h> 183 #include <defer.h> 184 #include <mail_error.h> 185 #include <dsn_buf.h> 186 #include <dsn.h> 187 #include <mail_params.h> 188 189 /* Application-specific. */ 190 191 #include "smtp.h" 192 #include "smtp_sasl.h" 193 194 /* smtp_check_code - check response code */ 195 196 static void smtp_check_code(SMTP_SESSION *session, int code) 197 { 198 199 /* 200 * The intention of this code is to alert the postmaster when the local 201 * Postfix SMTP client screws up, protocol wise. RFC 821 says that x0z 202 * replies "refer to syntax errors, syntactically correct commands that 203 * don't fit any functional category, and unimplemented or superfluous 204 * commands". Unfortunately, this also triggers postmaster notices when 205 * remote servers screw up, protocol wise. This is becoming a common 206 * problem now that response codes are configured manually as part of 207 * anti-UCE systems, by people who aren't aware of RFC details. 208 * 209 * Fix 20190621: don't cache an SMTP session after an SMTP protocol error. 210 * The protocol may be in a bad state. Disable caching here so that the 211 * protocol engine will send QUIT. 212 */ 213 if (code < 400 || code > 599 214 || code == 555 /* RFC 1869, section 6.1. */ 215 || (code >= 500 && code < 510)) { 216 session->error_mask |= MAIL_ERROR_PROTOCOL; 217 DONT_CACHE_THIS_SESSION; 218 } 219 } 220 221 /* smtp_bulk_fail - skip, defer or bounce recipients, maybe throttle queue */ 222 223 static int smtp_bulk_fail(SMTP_STATE *state, int flags) 224 { 225 DELIVER_REQUEST *request = state->request; 226 SMTP_SESSION *session = state->session; 227 DSN_BUF *why = state->why; 228 RECIPIENT *rcpt; 229 int status; 230 int aggregate_status; 231 int soft_error = (STR(why->status)[0] == '4'); 232 int soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce); 233 int throttle_queue = (flags & SMTP_MISC_FAIL_THROTTLE); 234 int dont_cache = (flags & SMTP_MISC_FAIL_DONT_CACHE); 235 int nrcpt; 236 237 /* 238 * Sanity check. 239 */ 240 if ((flags & SMTP_MISC_FAIL_SOFT_NON_FINAL) != 0) { 241 if (soft_error) { 242 msg_warn("smtp_bulk_fail: ignoring SMTP_MISC_FAIL_SOFT_NON_FINAL " 243 "for a soft error"); 244 } else { 245 soft_error = (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0; 246 } 247 } 248 249 /* 250 * Don't defer the recipients just yet when this error qualifies them for 251 * delivery to a backup server. Just log something informative to show 252 * why we're skipping this host. 253 */ 254 if ((soft_error || soft_bounce_error) 255 && (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) { 256 msg_info("%s: %s", request->queue_id, STR(why->reason)); 257 for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) { 258 rcpt = request->rcpt_list.info + nrcpt; 259 if (SMTP_RCPT_ISMARKED(rcpt)) 260 continue; 261 SMTP_RCPT_KEEP(state, rcpt); 262 } 263 } 264 265 /* 266 * Defer or bounce all the remaining recipients, and delete them from the 267 * delivery request. If a bounce fails, defer instead and do not qualify 268 * the recipient for delivery to a backup server. 269 */ 270 else { 271 272 /* 273 * If we are still in the connection set-up phase, update the set-up 274 * completion time here, otherwise the time spent in set-up latency 275 * will be attributed as message transfer latency. 276 * 277 * All remaining recipients have failed at this point, so we update the 278 * delivery completion time stamp so that multiple recipient status 279 * records show the same delay values. 280 */ 281 if (request->msg_stats.conn_setup_done.tv_sec == 0) { 282 GETTIMEOFDAY(&request->msg_stats.conn_setup_done); 283 request->msg_stats.deliver_done = 284 request->msg_stats.conn_setup_done; 285 } else 286 GETTIMEOFDAY(&request->msg_stats.deliver_done); 287 288 (void) DSN_FROM_DSN_BUF(why); 289 aggregate_status = 0; 290 for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) { 291 rcpt = request->rcpt_list.info + nrcpt; 292 if (SMTP_RCPT_ISMARKED(rcpt)) 293 continue; 294 status = (soft_error ? defer_append : bounce_append) 295 (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, 296 &request->msg_stats, rcpt, 297 session ? session->namaddrport : "none", state->tls_stats, 298 &why->dsn); 299 if (status == 0) 300 deliver_completed(state->src, rcpt->offset); 301 SMTP_RCPT_DROP(state, rcpt); 302 aggregate_status |= status; 303 } 304 state->status |= aggregate_status; 305 if ((state->misc_flags & SMTP_MISC_FLAG_COMPLETE_SESSION) == 0 306 && throttle_queue && aggregate_status 307 && request->hop_status == 0) 308 request->hop_status = DSN_COPY(&why->dsn); 309 } 310 311 /* 312 * Don't cache this session. We can't talk to this server. 313 */ 314 if (throttle_queue && session) 315 DONT_CACHE_THROTTLED_SESSION; 316 else if (dont_cache && session) 317 DONT_CACHE_THIS_SESSION; 318 319 return (-1); 320 } 321 322 /* smtp_sess_fail - skip site, defer or bounce all recipients */ 323 324 int smtp_sess_fail(SMTP_STATE *state) 325 { 326 327 /* 328 * We can't avoid copying copying lots of strings into VSTRING buffers, 329 * because this error information is collected by a routine that 330 * terminates BEFORE the error is reported. 331 */ 332 return (smtp_bulk_fail(state, SMTP_MISC_FAIL_THROTTLE)); 333 } 334 335 /* vsmtp_fill_dsn - fill in temporary DSN structure */ 336 337 static void vsmtp_fill_dsn(SMTP_STATE *state, const char *mta_name, 338 const char *status, const char *reply, 339 const char *format, va_list ap) 340 { 341 DSN_BUF *why = state->why; 342 343 /* 344 * We could avoid copying lots of strings into VSTRING buffers, because 345 * this error information is given to us by a routine that terminates 346 * AFTER the error is reported. However, this results in ugly kludges 347 * when informal text needs to be formatted. So we maintain consistency 348 * with other error reporting in the SMTP client even if we waste a few 349 * cycles. 350 * 351 * Fix 20190621: don't cache an SMTP session after an SMTP protocol error. 352 * The protocol may be in a bad state. Disable caching here so that the 353 * protocol engine will send QUIT. 354 */ 355 VSTRING_RESET(why->reason); 356 if (mta_name && status && status[0] != '4' && status[0] != '5') { 357 SMTP_SESSION *session = state->session; 358 359 session->error_mask |= MAIL_ERROR_PROTOCOL; 360 DONT_CACHE_THIS_SESSION; 361 vstring_strcpy(why->reason, "Protocol error: "); 362 status = "5.5.0"; 363 } 364 vstring_vsprintf_append(why->reason, format, ap); 365 dsb_formal(why, status, DSB_DEF_ACTION, 366 mta_name ? DSB_MTYPE_DNS : DSB_MTYPE_NONE, mta_name, 367 reply ? DSB_DTYPE_SMTP : DSB_DTYPE_NONE, reply); 368 } 369 370 /* smtp_misc_fail - maybe throttle queue; skip/defer/bounce all recipients */ 371 372 int smtp_misc_fail(SMTP_STATE *state, int flags, const char *mta_name, 373 SMTP_RESP *resp, const char *format,...) 374 { 375 va_list ap; 376 377 /* 378 * Initialize. 379 */ 380 va_start(ap, format); 381 vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap); 382 va_end(ap); 383 384 if (state->session && mta_name) 385 smtp_check_code(state->session, resp->code); 386 387 /* 388 * Skip, defer or bounce recipients, and throttle this queue. 389 */ 390 return (smtp_bulk_fail(state, flags)); 391 } 392 393 /* smtp_rcpt_fail - skip, defer, or bounce recipient */ 394 395 void smtp_rcpt_fail(SMTP_STATE *state, RECIPIENT *rcpt, const char *mta_name, 396 SMTP_RESP *resp, const char *format,...) 397 { 398 DELIVER_REQUEST *request = state->request; 399 SMTP_SESSION *session = state->session; 400 DSN_BUF *why = state->why; 401 int status; 402 int soft_error; 403 int soft_bounce_error; 404 va_list ap; 405 406 /* 407 * Sanity check. 408 */ 409 if (SMTP_RCPT_ISMARKED(rcpt)) 410 msg_panic("smtp_rcpt_fail: recipient <%s> is marked", rcpt->address); 411 412 /* 413 * Initialize. 414 */ 415 va_start(ap, format); 416 vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap); 417 va_end(ap); 418 soft_error = STR(why->status)[0] == '4'; 419 soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce); 420 421 if (state->session && mta_name) 422 smtp_check_code(state->session, resp->code); 423 424 /* 425 * Don't defer this recipient record just yet when this error qualifies 426 * for trying other mail servers. Just log something informative to show 427 * why we're skipping this recipient now. 428 */ 429 if ((soft_error || soft_bounce_error) 430 && (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) { 431 msg_info("%s: %s", request->queue_id, STR(why->reason)); 432 SMTP_RCPT_KEEP(state, rcpt); 433 } 434 435 /* 436 * Defer or bounce this recipient, and delete from the delivery request. 437 * If the bounce fails, defer instead and do not qualify the recipient 438 * for delivery to a backup server. 439 * 440 * Note: we may still make an SMTP connection to deliver other recipients 441 * that did qualify for delivery to a backup server. 442 */ 443 else { 444 (void) DSN_FROM_DSN_BUF(state->why); 445 status = (soft_error ? defer_append : bounce_append) 446 (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, 447 &request->msg_stats, rcpt, 448 session ? session->namaddrport : "none", state->tls_stats, 449 &why->dsn); 450 if (status == 0) 451 deliver_completed(state->src, rcpt->offset); 452 SMTP_RCPT_DROP(state, rcpt); 453 state->status |= status; 454 } 455 } 456 457 /* smtp_stream_except - defer domain after I/O problem */ 458 459 int smtp_stream_except(SMTP_STATE *state, int code, const char *description) 460 { 461 SMTP_SESSION *session = state->session; 462 DSN_BUF *why = state->why; 463 464 /* 465 * Sanity check. 466 */ 467 if (session == 0) 468 msg_panic("smtp_stream_except: no session"); 469 470 /* 471 * Initialize. 472 */ 473 switch (code) { 474 default: 475 msg_panic("smtp_stream_except: unknown exception %d", code); 476 case SMTP_ERR_EOF: 477 dsb_simple(why, "4.4.2", "lost connection with %s while %s", 478 session->namaddr, description); 479 #ifdef USE_TLS 480 if (PLAINTEXT_FALLBACK_OK_AFTER_TLS_SESSION_FAILURE) 481 RETRY_AS_PLAINTEXT; 482 #endif 483 break; 484 case SMTP_ERR_TIME: 485 dsb_simple(why, "4.4.2", "conversation with %s timed out while %s", 486 session->namaddr, description); 487 #ifdef USE_TLS 488 if (PLAINTEXT_FALLBACK_OK_AFTER_TLS_SESSION_FAILURE) 489 RETRY_AS_PLAINTEXT; 490 #endif 491 break; 492 case SMTP_ERR_DATA: 493 session->error_mask |= MAIL_ERROR_DATA; 494 dsb_simple(why, "4.3.0", "local data error while talking to %s", 495 session->namaddr); 496 } 497 498 /* 499 * The smtp_bulk_fail() call below will not throttle the destination when 500 * falling back to plaintext, because RETRY_AS_PLAINTEXT clears the 501 * FINAL_SERVER flag. 502 */ 503 return (smtp_bulk_fail(state, SMTP_MISC_FAIL_THROTTLE)); 504 } 505