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