NetBSD/gnu/dist/postfix/smtp/smtp_trouble.c

309 lines
9.0 KiB
C
Raw Normal View History

/*++
/* NAME
/* smtp_trouble 3
/* SUMMARY
/* error handler policies
/* SYNOPSIS
/* #include "smtp.h"
/*
/* int smtp_site_fail(state, code, format, ...)
/* SMTP_STATE *state;
/* int code;
/* char *format;
/*
/* int smtp_mesg_fail(state, code, format, ...)
/* SMTP_STATE *state;
/* int code;
/* char *format;
/*
/* void smtp_rcpt_fail(state, code, recipient, format, ...)
/* SMTP_STATE *state;
/* int code;
/* RECIPIENT *recipient;
/* char *format;
/*
/* int smtp_stream_except(state, exception, description)
/* SMTP_STATE *state;
/* int exception;
/* char *description;
/* DESCRIPTION
/* This module handles all non-fatal errors that can happen while
/* attempting to deliver mail via SMTP, and implements the policy
/* of how to deal with the error. Depending on the nature of
/* the problem, delivery of a single message is deferred, delivery
/* of all messages to the same domain is deferred, or one or more
/* recipients are given up as non-deliverable and a bounce log is
/* updated.
/*
/* In addition, when an unexpected response code is seen such
/* as 3xx where only 4xx or 5xx are expected, or any error code
/* that suggests a syntax error or something similar, the
/* protocol error flag is set so that the postmaster receives
/* a transcript of the session. No notification is generated for
/* what appear to be configuration errors - very likely, they
/* would suffer the same problem and just cause more trouble.
/*
/* smtp_site_fail() handles the case where the program fails to
/* complete the initial SMTP handshake: the server is not reachable,
/* is not running, does not want talk to us, or we talk to ourselves.
/* The \fIcode\fR gives an error status code; the \fIformat\fR
/* argument gives a textual description. The policy is: soft
/* error: defer delivery of all messages to this domain; hard
/* error: bounce all recipients of this message.
/* The result is non-zero.
/*
/* smtp_mesg_fail() handles the case where the smtp server
/* does not accept the sender address or the message data.
/* The policy is: soft errors: defer delivery of this message;
/* hard error: bounce all recipients of this message.
/* The result is non-zero.
/*
/* smtp_rcpt_fail() handles the case where a recipient is not
/* accepted by the server for reasons other than that the server
/* recipient limit is reached. The policy is: soft error: defer
/* delivery to this recipient; hard error: bounce this recipient.
/*
/* smtp_stream_except() handles the exceptions generated by
/* the smtp_stream(3) module (i.e. timeouts and I/O errors).
/* The \fIexception\fR argument specifies the type of problem.
/* The \fIdescription\fR argument describes at what stage of
/* the SMTP dialog the problem happened. The policy is to defer
/* delivery of all messages to the same domain. The result is non-zero.
/* DIAGNOSTICS
/* Panic: unknown exception code.
/* SEE ALSO
/* smtp_proto(3) smtp high-level protocol
/* smtp_stream(3) smtp low-level protocol
/* defer(3) basic message defer interface
/* bounce(3) basic message bounce interface
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
#include <setjmp.h>
#include <stdarg.h>
/* Utility library. */
#include <msg.h>
#include <vstring.h>
#include <stringops.h>
#include <mymalloc.h>
/* Global library. */
#include <smtp_stream.h>
#include <deliver_request.h>
#include <deliver_completed.h>
#include <bounce.h>
#include <defer.h>
#include <mail_error.h>
/* Application-specific. */
#include "smtp.h"
#define SMTP_SOFT(code) (((code) / 100) == 4)
#define SMTP_HARD(code) (((code) / 100) == 5)
#define KEEP BOUNCE_FLAG_KEEP
/* smtp_check_code - check response code */
static void smtp_check_code(SMTP_STATE *state, int code)
{
/*
* The intention of this stuff is to alert the postmaster when the local
* Postfix SMTP client screws up, protocol wise. RFC 821 says that x0z
* replies "refer to syntax errors, syntactically correct commands that
* don't fit any functional category, and unimplemented or superfluous
* commands". Unfortunately, this also triggers postmaster notices when
* remote servers screw up, protocol wise. This is becoming a common
* problem now that response codes are configured manually as part of
* anti-UCE systems, by people who aren't aware of RFC details.
*/
if ((!SMTP_SOFT(code) && !SMTP_HARD(code))
|| code == 555 /* RFC 1869, section 6.1. */
|| (code >= 500 && code < 510))
state->error_mask |= MAIL_ERROR_PROTOCOL;
}
/* smtp_site_fail - defer site or bounce recipients */
int smtp_site_fail(SMTP_STATE *state, int code, char *format,...)
{
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
RECIPIENT *rcpt;
int status;
int nrcpt;
int soft_error = SMTP_SOFT(code);
va_list ap;
VSTRING *why = vstring_alloc(100);
/*
* Initialize.
*/
va_start(ap, format);
vstring_vsprintf(why, format, ap);
va_end(ap);
/*
* If this is a soft error, postpone further deliveries to this domain.
* Otherwise, generate a bounce record for each recipient.
*/
for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
if (rcpt->offset == 0)
continue;
status = (soft_error ? defer_append : bounce_append)
(KEEP, request->queue_id, rcpt->address,
session ? session->namaddr : "none",
request->arrival_time, "%s", vstring_str(why));
if (status == 0) {
deliver_completed(state->src, rcpt->offset);
rcpt->offset = 0;
}
state->status |= status;
}
if (soft_error && request->hop_status == 0)
request->hop_status = mystrdup(vstring_str(why));
/*
* Cleanup.
*/
vstring_free(why);
return (-1);
}
/* smtp_mesg_fail - defer message or bounce all recipients */
int smtp_mesg_fail(SMTP_STATE *state, int code, char *format,...)
{
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
RECIPIENT *rcpt;
int status;
int nrcpt;
va_list ap;
VSTRING *why = vstring_alloc(100);
/*
* Initialize.
*/
va_start(ap, format);
vstring_vsprintf(why, format, ap);
va_end(ap);
/*
* If this is a soft error, postpone delivery of this message. Otherwise,
* generate a bounce record for each recipient.
*/
for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
if (rcpt->offset == 0)
continue;
status = (SMTP_SOFT(code) ? defer_append : bounce_append)
(KEEP, request->queue_id, rcpt->address,
session->namaddr, request->arrival_time,
"%s", vstring_str(why));
if (status == 0) {
deliver_completed(state->src, rcpt->offset);
rcpt->offset = 0;
}
state->status |= status;
}
smtp_check_code(state, code);
/*
* Cleanup.
*/
vstring_free(why);
return (-1);
}
/* smtp_rcpt_fail - defer or bounce recipient */
void smtp_rcpt_fail(SMTP_STATE *state, int code, RECIPIENT *rcpt,
char *format,...)
{
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
int status;
va_list ap;
/*
* If this is a soft error, postpone delivery to this recipient.
* Otherwise, generate a bounce record for this recipient.
*/
va_start(ap, format);
status = (SMTP_SOFT(code) ? vdefer_append : vbounce_append)
(KEEP, request->queue_id, rcpt->address, session->namaddr,
request->arrival_time, format, ap);
va_end(ap);
if (status == 0) {
deliver_completed(state->src, rcpt->offset);
rcpt->offset = 0;
}
smtp_check_code(state, code);
state->status |= status;
}
/* smtp_stream_except - defer domain after I/O problem */
int smtp_stream_except(SMTP_STATE *state, int code, char *description)
{
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
RECIPIENT *rcpt;
int nrcpt;
VSTRING *why = vstring_alloc(100);
/*
* Initialize.
*/
switch (code) {
default:
msg_panic("smtp_stream_except: unknown exception %d", code);
case SMTP_ERR_EOF:
vstring_sprintf(why, "lost connection with %s while %s",
session->namaddr, description);
break;
case SMTP_ERR_TIME:
vstring_sprintf(why, "conversation with %s timed out while %s",
session->namaddr, description);
break;
}
/*
* At this point, the status of individual recipients remains unresolved.
* All we know is that we should stay away from this host for a while.
*/
for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
if (rcpt->offset == 0)
continue;
state->status |= defer_append(KEEP, request->queue_id,
rcpt->address, session->namaddr,
request->arrival_time,
"%s", vstring_str(why));
}
/*
* Cleanup.
*/
vstring_free(why);
return (-1);
}