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