xref: /netbsd-src/external/ibm-public/postfix/dist/src/smtp/smtp_trouble.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
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