Sync usage with man page.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / smtp / smtp_trouble.c
blob8b1b4405a39d2e618e60bb4f8b2b4e4fa222d06d
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* smtp_trouble 3
6 /* SUMMARY
7 /* error handler policies
8 /* SYNOPSIS
9 /* #include "smtp.h"
11 /* int smtp_sess_fail(state)
12 /* SMTP_STATE *state;
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;
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;
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;
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).
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.
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).
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().
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.
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.
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.
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.
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 /*--*/
133 /* System library. */
135 #include <sys_defs.h>
136 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */
137 #include <stdarg.h>
138 #include <string.h>
140 /* Utility library. */
142 #include <msg.h>
143 #include <vstring.h>
144 #include <stringops.h>
146 /* Global library. */
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>
157 /* Application-specific. */
159 #include "smtp.h"
161 #define SMTP_THROTTLE 1
162 #define SMTP_NOTHROTTLE 0
164 /* smtp_check_code - check response code */
166 static void smtp_check_code(SMTP_SESSION *session, int code)
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.
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;
185 /* smtp_bulk_fail - skip, defer or bounce recipients, maybe throttle queue */
187 static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue)
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;
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.
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);
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.
217 else {
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.
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.
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);
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;
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);
255 * Don't cache this session. We can't talk to this server.
257 if (throttle_queue && session)
258 DONT_CACHE_BAD_SESSION;
260 return (-1);
263 /* smtp_sess_fail - skip site, defer or bounce all recipients */
265 int smtp_sess_fail(SMTP_STATE *state)
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.
273 return (smtp_bulk_fail(state, SMTP_THROTTLE));
276 /* vsmtp_fill_dsn - fill in temporary DSN structure */
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)
282 DSN_BUF *why = state->why;
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.
292 VSTRING_RESET(why->reason);
293 if (mta_name && reply && reply[0] != '4' && reply[0] != '5') {
294 vstring_strcpy(why->reason, "Protocol error: ");
295 status = "5.5.0";
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);
303 /* smtp_site_fail - throttle this queue; skip, defer or bounce all recipients */
305 int smtp_site_fail(SMTP_STATE *state, const char *mta_name, SMTP_RESP *resp,
306 const char *format,...)
308 va_list ap;
311 * Initialize.
313 va_start(ap, format);
314 vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap);
315 va_end(ap);
317 if (state->session && mta_name)
318 smtp_check_code(state->session, resp->code);
321 * Skip, defer or bounce recipients, and throttle this queue.
323 return (smtp_bulk_fail(state, SMTP_THROTTLE));
326 /* smtp_mesg_fail - skip, defer or bounce all recipients; no queue throttle */
328 int smtp_mesg_fail(SMTP_STATE *state, const char *mta_name, SMTP_RESP *resp,
329 const char *format,...)
331 va_list ap;
334 * Initialize.
336 va_start(ap, format);
337 vsmtp_fill_dsn(state, mta_name, resp->dsn, resp->str, format, ap);
338 va_end(ap);
340 if (state->session && mta_name)
341 smtp_check_code(state->session, resp->code);
344 * Skip, defer or bounce recipients, but don't throttle this queue.
346 return (smtp_bulk_fail(state, SMTP_NOTHROTTLE));
349 /* smtp_rcpt_fail - skip, defer, or bounce recipient */
351 void smtp_rcpt_fail(SMTP_STATE *state, RECIPIENT *rcpt, const char *mta_name,
352 SMTP_RESP *resp, const char *format,...)
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;
362 * Sanity check.
364 if (SMTP_RCPT_ISMARKED(rcpt))
365 msg_panic("smtp_rcpt_fail: recipient <%s> is marked", rcpt->address);
368 * Initialize.
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';
375 if (state->session && mta_name)
376 smtp_check_code(state->session, resp->code);
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.
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);
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.
393 * Note: we may still make an SMTP connection to deliver other recipients
394 * that did qualify for delivery to a backup server.
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;
409 /* smtp_stream_except - defer domain after I/O problem */
411 int smtp_stream_except(SMTP_STATE *state, int code, const char *description)
413 SMTP_SESSION *session = state->session;
414 DSN_BUF *why = state->why;
417 * Sanity check.
419 if (session == 0)
420 msg_panic("smtp_stream_except: no session");
423 * Initialize.
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;
437 return (smtp_bulk_fail(state, SMTP_THROTTLE));