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