1 /* $NetBSD: smtp_trouble.c,v 1.2 2017/02/14 01:16:48 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 151 /* System library. */ 152 153 #include <sys_defs.h> 154 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */ 155 #include <stdarg.h> 156 #include <string.h> 157 158 /* Utility library. */ 159 160 #include <msg.h> 161 #include <vstring.h> 162 #include <stringops.h> 163 164 /* Global library. */ 165 166 #include <smtp_stream.h> 167 #include <deliver_request.h> 168 #include <deliver_completed.h> 169 #include <bounce.h> 170 #include <defer.h> 171 #include <mail_error.h> 172 #include <dsn_buf.h> 173 #include <dsn.h> 174 #include <mail_params.h> 175 176 /* Application-specific. */ 177 178 #include "smtp.h" 179 #include "smtp_sasl.h" 180 181 /* smtp_check_code - check response code */ 182 183 static void smtp_check_code(SMTP_SESSION *session, int code) 184 { 185 186 /* 187 * The intention of this code is to alert the postmaster when the local 188 * Postfix SMTP client screws up, protocol wise. RFC 821 says that x0z 189 * replies "refer to syntax errors, syntactically correct commands that 190 * don't fit any functional category, and unimplemented or superfluous 191 * commands". Unfortunately, this also triggers postmaster notices when 192 * remote servers screw up, protocol wise. This is becoming a common 193 * problem now that response codes are configured manually as part of 194 * anti-UCE systems, by people who aren't aware of RFC details. 195 */ 196 if (code < 400 || code > 599 197 || code == 555 /* RFC 1869, section 6.1. */ 198 || (code >= 500 && code < 510)) 199 session->error_mask |= MAIL_ERROR_PROTOCOL; 200 } 201 202 /* smtp_bulk_fail - skip, defer or bounce recipients, maybe throttle queue */ 203 204 static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue) 205 { 206 DELIVER_REQUEST *request = state->request; 207 SMTP_SESSION *session = state->session; 208 DSN_BUF *why = state->why; 209 RECIPIENT *rcpt; 210 int status; 211 int aggregate_status; 212 int soft_error = (STR(why->status)[0] == '4'); 213 int soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce); 214 int nrcpt; 215 216 /* 217 * Don't defer the recipients just yet when this error qualifies them for 218 * delivery to a backup server. Just log something informative to show 219 * why we're skipping this host. 220 */ 221 if ((soft_error || soft_bounce_error) 222 && (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) { 223 msg_info("%s: %s", request->queue_id, STR(why->reason)); 224 for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) { 225 rcpt = request->rcpt_list.info + nrcpt; 226 if (SMTP_RCPT_ISMARKED(rcpt)) 227 continue; 228 SMTP_RCPT_KEEP(state, rcpt); 229 } 230 } 231 232 /* 233 * Defer or bounce all the remaining recipients, and delete them from the 234 * delivery request. If a bounce fails, defer instead and do not qualify 235 * the recipient for delivery to a backup server. 236 */ 237 else { 238 239 /* 240 * If we are still in the connection set-up phase, update the set-up 241 * completion time here, otherwise the time spent in set-up latency 242 * will be attributed as message transfer latency. 243 * 244 * All remaining recipients have failed at this point, so we update the 245 * delivery completion time stamp so that multiple recipient status 246 * records show the same delay values. 247 */ 248 if (request->msg_stats.conn_setup_done.tv_sec == 0) { 249 GETTIMEOFDAY(&request->msg_stats.conn_setup_done); 250 request->msg_stats.deliver_done = 251 request->msg_stats.conn_setup_done; 252 } else 253 GETTIMEOFDAY(&request->msg_stats.deliver_done); 254 255 (void) DSN_FROM_DSN_BUF(why); 256 aggregate_status = 0; 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 status = (soft_error ? defer_append : bounce_append) 262 (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, 263 &request->msg_stats, rcpt, 264 session ? session->namaddrport : "none", &why->dsn); 265 if (status == 0) 266 deliver_completed(state->src, rcpt->offset); 267 SMTP_RCPT_DROP(state, rcpt); 268 aggregate_status |= status; 269 } 270 state->status |= aggregate_status; 271 if ((state->misc_flags & SMTP_MISC_FLAG_COMPLETE_SESSION) == 0 272 && throttle_queue && aggregate_status 273 && request->hop_status == 0) 274 request->hop_status = DSN_COPY(&why->dsn); 275 } 276 277 /* 278 * Don't cache this session. We can't talk to this server. 279 */ 280 if (throttle_queue && session) 281 DONT_CACHE_THROTTLED_SESSION; 282 283 return (-1); 284 } 285 286 /* smtp_sess_fail - skip site, defer or bounce all recipients */ 287 288 int smtp_sess_fail(SMTP_STATE *state) 289 { 290 291 /* 292 * We can't avoid copying copying lots of strings into VSTRING buffers, 293 * because this error information is collected by a routine that 294 * terminates BEFORE the error is reported. 295 */ 296 return (smtp_bulk_fail(state, SMTP_THROTTLE)); 297 } 298 299 /* vsmtp_fill_dsn - fill in temporary DSN structure */ 300 301 static void vsmtp_fill_dsn(SMTP_STATE *state, const char *mta_name, 302 const char *status, const char *reply, 303 const char *format, va_list ap) 304 { 305 DSN_BUF *why = state->why; 306 307 /* 308 * We could avoid copying lots of strings into VSTRING buffers, because 309 * this error information is given to us by a routine that terminates 310 * AFTER the error is reported. However, this results in ugly kludges 311 * when informal text needs to be formatted. So we maintain consistency 312 * with other error reporting in the SMTP client even if we waste a few 313 * cycles. 314 */ 315 VSTRING_RESET(why->reason); 316 if (mta_name && status && status[0] != '4' && status[0] != '5') { 317 vstring_strcpy(why->reason, "Protocol error: "); 318 status = "5.5.0"; 319 } 320 vstring_vsprintf_append(why->reason, format, ap); 321 dsb_formal(why, status, DSB_DEF_ACTION, 322 mta_name ? DSB_MTYPE_DNS : DSB_MTYPE_NONE, mta_name, 323 reply ? DSB_DTYPE_SMTP : DSB_DTYPE_NONE, reply); 324 } 325 326 /* smtp_misc_fail - maybe throttle queue; skip/defer/bounce all recipients */ 327 328 int smtp_misc_fail(SMTP_STATE *state, int throttle, const char *mta_name, 329 SMTP_RESP *resp, const char *format,...) 330 { 331 va_list ap; 332 333 /* 334 * Initialize. 335 */ 336 va_start(ap, format); 337 vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap); 338 va_end(ap); 339 340 if (state->session && mta_name) 341 smtp_check_code(state->session, resp->code); 342 343 /* 344 * Skip, defer or bounce recipients, and throttle this queue. 345 */ 346 return (smtp_bulk_fail(state, throttle)); 347 } 348 349 /* smtp_rcpt_fail - skip, defer, or bounce recipient */ 350 351 void smtp_rcpt_fail(SMTP_STATE *state, RECIPIENT *rcpt, const char *mta_name, 352 SMTP_RESP *resp, const char *format,...) 353 { 354 DELIVER_REQUEST *request = state->request; 355 SMTP_SESSION *session = state->session; 356 DSN_BUF *why = state->why; 357 int status; 358 int soft_error; 359 int soft_bounce_error; 360 va_list ap; 361 362 /* 363 * Sanity check. 364 */ 365 if (SMTP_RCPT_ISMARKED(rcpt)) 366 msg_panic("smtp_rcpt_fail: recipient <%s> is marked", rcpt->address); 367 368 /* 369 * Initialize. 370 */ 371 va_start(ap, format); 372 vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap); 373 va_end(ap); 374 soft_error = STR(why->status)[0] == '4'; 375 soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce); 376 377 if (state->session && mta_name) 378 smtp_check_code(state->session, resp->code); 379 380 /* 381 * Don't defer this recipient record just yet when this error qualifies 382 * for trying other mail servers. Just log something informative to show 383 * why we're skipping this recipient now. 384 */ 385 if ((soft_error || soft_bounce_error) 386 && (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) { 387 msg_info("%s: %s", request->queue_id, STR(why->reason)); 388 SMTP_RCPT_KEEP(state, rcpt); 389 } 390 391 /* 392 * Defer or bounce this recipient, and delete from the delivery request. 393 * If the bounce fails, defer instead and do not qualify the recipient 394 * for delivery to a backup server. 395 * 396 * Note: we may still make an SMTP connection to deliver other recipients 397 * that did qualify for delivery to a backup server. 398 */ 399 else { 400 (void) DSN_FROM_DSN_BUF(state->why); 401 status = (soft_error ? defer_append : bounce_append) 402 (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, 403 &request->msg_stats, rcpt, 404 session ? session->namaddrport : "none", &why->dsn); 405 if (status == 0) 406 deliver_completed(state->src, rcpt->offset); 407 SMTP_RCPT_DROP(state, rcpt); 408 state->status |= status; 409 } 410 } 411 412 /* smtp_stream_except - defer domain after I/O problem */ 413 414 int smtp_stream_except(SMTP_STATE *state, int code, const char *description) 415 { 416 SMTP_SESSION *session = state->session; 417 DSN_BUF *why = state->why; 418 419 /* 420 * Sanity check. 421 */ 422 if (session == 0) 423 msg_panic("smtp_stream_except: no session"); 424 425 /* 426 * Initialize. 427 */ 428 switch (code) { 429 default: 430 msg_panic("smtp_stream_except: unknown exception %d", code); 431 case SMTP_ERR_EOF: 432 dsb_simple(why, "4.4.2", "lost connection with %s while %s", 433 session->namaddr, description); 434 #ifdef USE_TLS 435 if (PLAINTEXT_FALLBACK_OK_AFTER_TLS_SESSION_FAILURE) 436 RETRY_AS_PLAINTEXT; 437 #endif 438 break; 439 case SMTP_ERR_TIME: 440 dsb_simple(why, "4.4.2", "conversation with %s timed out while %s", 441 session->namaddr, description); 442 #ifdef USE_TLS 443 if (PLAINTEXT_FALLBACK_OK_AFTER_TLS_SESSION_FAILURE) 444 RETRY_AS_PLAINTEXT; 445 #endif 446 break; 447 case SMTP_ERR_DATA: 448 session->error_mask |= MAIL_ERROR_DATA; 449 dsb_simple(why, "4.3.0", "local data error while talking to %s", 450 session->namaddr); 451 } 452 453 /* 454 * The smtp_bulk_fail() call below will not throttle the destination when 455 * falling back to plaintext, because RETRY_AS_PLAINTEXT clears the 456 * FINAL_SERVER flag. 457 */ 458 return (smtp_bulk_fail(state, SMTP_THROTTLE)); 459 } 460