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