1 /* $NetBSD: smtp_trouble.c,v 1.3 2020/03/18 19:05: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, throttle, mta_name, resp, format, ...) 39 /* SMTP_STATE *state; 40 /* int throttle; 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(). The throttle argument 97 /* is either SMTP_THROTTLE or SMTP_NOTHROTTLE; it is used only 98 /* in the "soft error, final server" policy, and determines 99 /* whether a destination will be marked as problematic. 100 /* 101 /* smtp_rcpt_fail() handles the case where a recipient is not 102 /* accepted by the server for reasons other than that the server 103 /* recipient limit is reached. 104 /* The policy is: soft error, non-final server: log an informational 105 /* record why the recipient is being skipped; soft error, final server: 106 /* defer delivery of this recipient; hard error: bounce this 107 /* recipient. 108 /* 109 /* smtp_stream_except() handles the exceptions generated by 110 /* the smtp_stream(3) module (i.e. timeouts and I/O errors). 111 /* The \fIexception\fR argument specifies the type of problem. 112 /* The \fIdescription\fR argument describes at what stage of 113 /* the SMTP dialog the problem happened. 114 /* The policy is: non-final server: log an informational record 115 /* with the reason why the host is being skipped; final server: 116 /* defer delivery of all remaining recipients. 117 /* Retry plaintext delivery after TLS post-handshake session 118 /* failure, provided that at least one recipient was not 119 /* deferred or rejected during the TLS phase, and that global 120 /* preconditions for plaintext fallback are met. 121 /* The session is marked as "do not cache". 122 /* The result is non-zero. 123 /* 124 /* Arguments: 125 /* .IP state 126 /* SMTP client state per delivery request. 127 /* .IP resp 128 /* Server response including reply code and text. 129 /* .IP recipient 130 /* Undeliverable recipient address information. 131 /* .IP format 132 /* Human-readable description of why mail is not deliverable. 133 /* DIAGNOSTICS 134 /* Panic: unknown exception code. 135 /* SEE ALSO 136 /* smtp_proto(3) smtp high-level protocol 137 /* smtp_stream(3) smtp low-level protocol 138 /* defer(3) basic message defer interface 139 /* bounce(3) basic message bounce interface 140 /* LICENSE 141 /* .ad 142 /* .fi 143 /* The Secure Mailer license must be distributed with this software. 144 /* AUTHOR(S) 145 /* Wietse Venema 146 /* IBM T.J. Watson Research 147 /* P.O. Box 704 148 /* Yorktown Heights, NY 10598, USA 149 /* 150 /* Wietse Venema 151 /* Google, Inc. 152 /* 111 8th Avenue 153 /* New York, NY 10011, USA 154 /*--*/ 155 156 /* System library. */ 157 158 #include <sys_defs.h> 159 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */ 160 #include <stdarg.h> 161 #include <string.h> 162 163 /* Utility library. */ 164 165 #include <msg.h> 166 #include <vstring.h> 167 #include <stringops.h> 168 169 /* Global library. */ 170 171 #include <smtp_stream.h> 172 #include <deliver_request.h> 173 #include <deliver_completed.h> 174 #include <bounce.h> 175 #include <defer.h> 176 #include <mail_error.h> 177 #include <dsn_buf.h> 178 #include <dsn.h> 179 #include <mail_params.h> 180 181 /* Application-specific. */ 182 183 #include "smtp.h" 184 #include "smtp_sasl.h" 185 186 /* smtp_check_code - check response code */ 187 188 static void smtp_check_code(SMTP_SESSION *session, int code) 189 { 190 191 /* 192 * The intention of this code is to alert the postmaster when the local 193 * Postfix SMTP client screws up, protocol wise. RFC 821 says that x0z 194 * replies "refer to syntax errors, syntactically correct commands that 195 * don't fit any functional category, and unimplemented or superfluous 196 * commands". Unfortunately, this also triggers postmaster notices when 197 * remote servers screw up, protocol wise. This is becoming a common 198 * problem now that response codes are configured manually as part of 199 * anti-UCE systems, by people who aren't aware of RFC details. 200 * 201 * Fix 20190621: don't cache an SMTP session after an SMTP protocol error. 202 * The protocol may be in a bad state. Disable caching here so that the 203 * protocol engine will send QUIT. 204 */ 205 if (code < 400 || code > 599 206 || code == 555 /* RFC 1869, section 6.1. */ 207 || (code >= 500 && code < 510)) { 208 session->error_mask |= MAIL_ERROR_PROTOCOL; 209 DONT_CACHE_THIS_SESSION; 210 } 211 } 212 213 /* smtp_bulk_fail - skip, defer or bounce recipients, maybe throttle queue */ 214 215 static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue) 216 { 217 DELIVER_REQUEST *request = state->request; 218 SMTP_SESSION *session = state->session; 219 DSN_BUF *why = state->why; 220 RECIPIENT *rcpt; 221 int status; 222 int aggregate_status; 223 int soft_error = (STR(why->status)[0] == '4'); 224 int soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce); 225 int nrcpt; 226 227 /* 228 * Don't defer the recipients just yet when this error qualifies them for 229 * delivery to a backup server. Just log something informative to show 230 * why we're skipping this host. 231 */ 232 if ((soft_error || soft_bounce_error) 233 && (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) { 234 msg_info("%s: %s", request->queue_id, STR(why->reason)); 235 for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) { 236 rcpt = request->rcpt_list.info + nrcpt; 237 if (SMTP_RCPT_ISMARKED(rcpt)) 238 continue; 239 SMTP_RCPT_KEEP(state, rcpt); 240 } 241 } 242 243 /* 244 * Defer or bounce all the remaining recipients, and delete them from the 245 * delivery request. If a bounce fails, defer instead and do not qualify 246 * the recipient for delivery to a backup server. 247 */ 248 else { 249 250 /* 251 * If we are still in the connection set-up phase, update the set-up 252 * completion time here, otherwise the time spent in set-up latency 253 * will be attributed as message transfer latency. 254 * 255 * All remaining recipients have failed at this point, so we update the 256 * delivery completion time stamp so that multiple recipient status 257 * records show the same delay values. 258 */ 259 if (request->msg_stats.conn_setup_done.tv_sec == 0) { 260 GETTIMEOFDAY(&request->msg_stats.conn_setup_done); 261 request->msg_stats.deliver_done = 262 request->msg_stats.conn_setup_done; 263 } else 264 GETTIMEOFDAY(&request->msg_stats.deliver_done); 265 266 (void) DSN_FROM_DSN_BUF(why); 267 aggregate_status = 0; 268 for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) { 269 rcpt = request->rcpt_list.info + nrcpt; 270 if (SMTP_RCPT_ISMARKED(rcpt)) 271 continue; 272 status = (soft_error ? defer_append : bounce_append) 273 (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, 274 &request->msg_stats, rcpt, 275 session ? session->namaddrport : "none", &why->dsn); 276 if (status == 0) 277 deliver_completed(state->src, rcpt->offset); 278 SMTP_RCPT_DROP(state, rcpt); 279 aggregate_status |= status; 280 } 281 state->status |= aggregate_status; 282 if ((state->misc_flags & SMTP_MISC_FLAG_COMPLETE_SESSION) == 0 283 && throttle_queue && aggregate_status 284 && request->hop_status == 0) 285 request->hop_status = DSN_COPY(&why->dsn); 286 } 287 288 /* 289 * Don't cache this session. We can't talk to this server. 290 */ 291 if (throttle_queue && session) 292 DONT_CACHE_THROTTLED_SESSION; 293 294 return (-1); 295 } 296 297 /* smtp_sess_fail - skip site, defer or bounce all recipients */ 298 299 int smtp_sess_fail(SMTP_STATE *state) 300 { 301 302 /* 303 * We can't avoid copying copying lots of strings into VSTRING buffers, 304 * because this error information is collected by a routine that 305 * terminates BEFORE the error is reported. 306 */ 307 return (smtp_bulk_fail(state, SMTP_THROTTLE)); 308 } 309 310 /* vsmtp_fill_dsn - fill in temporary DSN structure */ 311 312 static void vsmtp_fill_dsn(SMTP_STATE *state, const char *mta_name, 313 const char *status, const char *reply, 314 const char *format, va_list ap) 315 { 316 DSN_BUF *why = state->why; 317 318 /* 319 * We could avoid copying lots of strings into VSTRING buffers, because 320 * this error information is given to us by a routine that terminates 321 * AFTER the error is reported. However, this results in ugly kludges 322 * when informal text needs to be formatted. So we maintain consistency 323 * with other error reporting in the SMTP client even if we waste a few 324 * cycles. 325 * 326 * Fix 20190621: don't cache an SMTP session after an SMTP protocol error. 327 * The protocol may be in a bad state. Disable caching here so that the 328 * protocol engine will send QUIT. 329 */ 330 VSTRING_RESET(why->reason); 331 if (mta_name && status && status[0] != '4' && status[0] != '5') { 332 SMTP_SESSION *session = state->session; 333 334 session->error_mask |= MAIL_ERROR_PROTOCOL; 335 DONT_CACHE_THIS_SESSION; 336 vstring_strcpy(why->reason, "Protocol error: "); 337 status = "5.5.0"; 338 } 339 vstring_vsprintf_append(why->reason, format, ap); 340 dsb_formal(why, status, DSB_DEF_ACTION, 341 mta_name ? DSB_MTYPE_DNS : DSB_MTYPE_NONE, mta_name, 342 reply ? DSB_DTYPE_SMTP : DSB_DTYPE_NONE, reply); 343 } 344 345 /* smtp_misc_fail - maybe throttle queue; skip/defer/bounce all recipients */ 346 347 int smtp_misc_fail(SMTP_STATE *state, int throttle, const char *mta_name, 348 SMTP_RESP *resp, const char *format,...) 349 { 350 va_list ap; 351 352 /* 353 * Initialize. 354 */ 355 va_start(ap, format); 356 vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap); 357 va_end(ap); 358 359 if (state->session && mta_name) 360 smtp_check_code(state->session, resp->code); 361 362 /* 363 * Skip, defer or bounce recipients, and throttle this queue. 364 */ 365 return (smtp_bulk_fail(state, throttle)); 366 } 367 368 /* smtp_rcpt_fail - skip, defer, or bounce recipient */ 369 370 void smtp_rcpt_fail(SMTP_STATE *state, RECIPIENT *rcpt, const char *mta_name, 371 SMTP_RESP *resp, const char *format,...) 372 { 373 DELIVER_REQUEST *request = state->request; 374 SMTP_SESSION *session = state->session; 375 DSN_BUF *why = state->why; 376 int status; 377 int soft_error; 378 int soft_bounce_error; 379 va_list ap; 380 381 /* 382 * Sanity check. 383 */ 384 if (SMTP_RCPT_ISMARKED(rcpt)) 385 msg_panic("smtp_rcpt_fail: recipient <%s> is marked", rcpt->address); 386 387 /* 388 * Initialize. 389 */ 390 va_start(ap, format); 391 vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap); 392 va_end(ap); 393 soft_error = STR(why->status)[0] == '4'; 394 soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce); 395 396 if (state->session && mta_name) 397 smtp_check_code(state->session, resp->code); 398 399 /* 400 * Don't defer this recipient record just yet when this error qualifies 401 * for trying other mail servers. Just log something informative to show 402 * why we're skipping this recipient now. 403 */ 404 if ((soft_error || soft_bounce_error) 405 && (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) { 406 msg_info("%s: %s", request->queue_id, STR(why->reason)); 407 SMTP_RCPT_KEEP(state, rcpt); 408 } 409 410 /* 411 * Defer or bounce this recipient, and delete from the delivery request. 412 * If the bounce fails, defer instead and do not qualify the recipient 413 * for delivery to a backup server. 414 * 415 * Note: we may still make an SMTP connection to deliver other recipients 416 * that did qualify for delivery to a backup server. 417 */ 418 else { 419 (void) DSN_FROM_DSN_BUF(state->why); 420 status = (soft_error ? defer_append : bounce_append) 421 (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, 422 &request->msg_stats, rcpt, 423 session ? session->namaddrport : "none", &why->dsn); 424 if (status == 0) 425 deliver_completed(state->src, rcpt->offset); 426 SMTP_RCPT_DROP(state, rcpt); 427 state->status |= status; 428 } 429 } 430 431 /* smtp_stream_except - defer domain after I/O problem */ 432 433 int smtp_stream_except(SMTP_STATE *state, int code, const char *description) 434 { 435 SMTP_SESSION *session = state->session; 436 DSN_BUF *why = state->why; 437 438 /* 439 * Sanity check. 440 */ 441 if (session == 0) 442 msg_panic("smtp_stream_except: no session"); 443 444 /* 445 * Initialize. 446 */ 447 switch (code) { 448 default: 449 msg_panic("smtp_stream_except: unknown exception %d", code); 450 case SMTP_ERR_EOF: 451 dsb_simple(why, "4.4.2", "lost connection with %s while %s", 452 session->namaddr, description); 453 #ifdef USE_TLS 454 if (PLAINTEXT_FALLBACK_OK_AFTER_TLS_SESSION_FAILURE) 455 RETRY_AS_PLAINTEXT; 456 #endif 457 break; 458 case SMTP_ERR_TIME: 459 dsb_simple(why, "4.4.2", "conversation with %s timed out while %s", 460 session->namaddr, description); 461 #ifdef USE_TLS 462 if (PLAINTEXT_FALLBACK_OK_AFTER_TLS_SESSION_FAILURE) 463 RETRY_AS_PLAINTEXT; 464 #endif 465 break; 466 case SMTP_ERR_DATA: 467 session->error_mask |= MAIL_ERROR_DATA; 468 dsb_simple(why, "4.3.0", "local data error while talking to %s", 469 session->namaddr); 470 } 471 472 /* 473 * The smtp_bulk_fail() call below will not throttle the destination when 474 * falling back to plaintext, because RETRY_AS_PLAINTEXT clears the 475 * FINAL_SERVER flag. 476 */ 477 return (smtp_bulk_fail(state, SMTP_THROTTLE)); 478 } 479