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