of a sender address. Code by Liviu Daia. Files:
smtpd/smtpd_check.c and documentation.
- The reject_sender_login_mismatch feature is now implemented
- by elementary features reject_unauth_sender_login_mismatch
+ reject_sender_login_mismatch is now implemented by elementary
+ features reject_unauthenticated_sender_login_mismatch
(reject if the client is not SASL logged in but the sender
address has an owner in smtpd_sender_login_maps) and
- reject_auth_sender_login_mismatch (reject if the client is
- SASL logged in but does not own the sender address). Code
- by Liviu Daia. Files: smtpd/smtpd_check.c and documentation.
+ reject_authenticated_sender_login_mismatch (reject if the
+ client is SASL logged in but does not own the sender
+ address). Code by Liviu Daia. Files: smtpd/smtpd_check.c
+ and documentation.
20031207
settings before and after DNS lookup, to avoid surprises
in third-party code. File: dns/dns_lookup.c.
+20031216
+
+ Cleanup: easier to parse mailq output (no more space
+ between short queue ID and message status). File:
+ showq/showq.c.
+
+ Cleanup: the SMTP client now moves on to the next MX host
+ when delivery fails in the middle of an SMTP session.
+ Files: smtp/smtp.c, smtp/smtp_connect.c.
+
Open problems:
High: when virtual aliasing is turned off after content
Low: postmap/postalias should not try to open a bogus file
when given an unsupported dictionary type.
- Med: do not postpone rejected "MAIL FROM" size information,
- and find a way to log the sender address in the rejected
- command.
+ Med: find a way to log the sender address when MAIL FROM
+ is rejected due to lack of disk space.
Low: after successful delivery, per-queue window += 1/window,
after failure, queue window -= 1 (Victor).
Low: postconf -e edits parameters that postconf won't list.
- Low: with quoted-printable, perhaps use =46rom instead of
- >From.
+ Low: while convering 8bit text to quoted-printable, perhaps
+ use =46rom instead of >From.
virtual_mailbox_path expression like forward_path, so that
people can specify prefix and suffix.
can be used to transmit client or message attributes incrementally.
It is not implemented by passing additional parameters via the MAIL
FROM command, because doing so would require extending the MAIL
-FROM command length limit by another 600 or more characters beyond.
+FROM command length limit by another 600 or more characters.
Command syntax
==============
xforward-command = XFORWARD 1*( SP name"="value )
- name = ( NAME | ADDR | PROTO | HELO | IDENT )
+ name = ( NAME | ADDR | PROTO | HELO )
The XFORWARD command can be sent at any time except in the middle
of a mail delivery transaction (i.e. between MAIL and DOT). The
-<html> <head> </head> <body> <pre>
+<html> <body> <pre>
SHOWQ(8) SHOWQ(8)
<b>NAME</b>
{
HEADER *reply_header;
int len;
- int saved_options = _res.options;
+ unsigned long saved_options = _res.options;
/*
* Initialize the name service.
* Patches change the patchlevel and the release date. Snapshots change the
* release date only, unless they include the same bugfix as a patch release.
*/
-#define MAIL_RELEASE_DATE "20031215"
+#define MAIL_RELEASE_DATE "20031216"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "2.0.16-" MAIL_RELEASE_DATE
char *var_empty_addr;
#define STRING_FORMAT "%-10s %8s %-20s %s\n"
-#define DATA_FORMAT "%-10s%c%8ld %20.20s %s\n"
+#define SENDER_FORMAT "%-11s%8ld %20.20s %s\n"
#define DROP_FORMAT "%-10s%c%8ld %20.20s (maildrop queue, sender UID %u)\n"
static void showq_reasons(VSTREAM *, BOUNCE_LOG *, HTABLE *);
start = var_empty_addr;
quote_822_local(printable_quoted_addr, start);
printable(STR(printable_quoted_addr), '?');
- vstream_fprintf(client, DATA_FORMAT, id, status,
+ /* quote_822_local() saves buf, so we can reuse its space. */
+ vstring_sprintf(buf, "%s%c", id, status);
+ vstream_fprintf(client, SENDER_FORMAT, STR(buf),
msg_size > 0 ? msg_size : size, arrival_time > 0 ?
asctime(localtime(&arrival_time)) :
asctime(localtime(&mtime)),
#include <mail_params.h>
#include <mail_conf.h>
#include <debug_peer.h>
-#include <mail_error.h>
-#include <deliver_pass.h>
/* Single server skeleton. */
static int deliver_message(DELIVER_REQUEST *request)
{
- VSTRING *why;
SMTP_STATE *state;
int result;
* we can produce understandable diagnostics when something goes wrong
* many levels below. The alternative would be to make everything global.
*/
- why = vstring_alloc(100);
state = smtp_state_alloc();
state->request = request;
state->src = request->fp;
* Optionally deliver mail locally when this machine is the best mail
* exchanger.
*/
- if ((state->session = smtp_connect(request->nexthop, why)) == 0) {
- if (smtp_errno == SMTP_OK) {
- if (*var_bestmx_transp == 0)
- msg_panic("smtp_errno botch");
- state->status = deliver_pass_all(MAIL_CLASS_PRIVATE,
- var_bestmx_transp,
- request);
- } else
- smtp_site_fail(state, smtp_errno == SMTP_RETRY ? 450 : 550,
- "%s", vstring_str(why));
- } else {
- debug_peer_check(state->session->host, state->session->addr);
- if (smtp_helo(state) == 0)
- smtp_xfer(state);
- if (state->history != 0
- && (state->error_mask & name_mask(VAR_NOTIFY_CLASSES,
- mail_error_masks, var_notify_classes)))
- smtp_chat_notify(state);
- /* XXX smtp_xfer() may abort in the middle of DATA. */
- smtp_session_free(state->session);
- debug_peer_restore();
- }
+ result = smtp_connect(state);
/*
* Clean up.
*/
- vstring_free(why);
smtp_chat_reset(state);
- result = state->status;
smtp_state_free(state);
return (result);
off_t size_limit; /* server limit or unknown */
int space_left; /* output length control */
struct MIME_STATE *mime_state; /* mime state machine */
+ int final; /* last possibility to deliver */
} SMTP_STATE;
#define SMTP_FEATURE_ESMTP (1<<0)
/*
* smtp_connect.c
*/
-extern SMTP_SESSION *smtp_connect(char *, VSTRING *);
-extern SMTP_SESSION *smtp_connect_host(char *, unsigned, VSTRING *);
-extern SMTP_SESSION *smtp_connect_domain(char *, unsigned, VSTRING *, int *);
+extern int smtp_connect(SMTP_STATE *);
/*
* smtp_proto.c
/* SYNOPSIS
/* #include "smtp.h"
/*
-/* SMTP_SESSION *smtp_connect(destination, why)
-/* char *destination;
-/* VSTRING *why;
-/* AUXILIARY FUNCTIONS
-/* SMTP_SESSION *smtp_connect_domain(name, port, why)
-/* char *name;
-/* unsigned port;
-/* VSTRING *why;
-/*
-/* SMTP_SESSION *smtp_connect_host(name, port, why)
-/* char *name;
-/* unsigned port;
-/* VSTRING *why;
+/* int smtp_connect(state)
+/* SMTP_STATE *state;
/* DESCRIPTION
-/* This module implements SMTP connection management.
+/* This module implements SMTP connection management and mail
+/* delivery.
/*
/* smtp_connect() attempts to establish an SMTP session with a host
-/* that represents the named domain.
-/*
-/* No session and an smtp_errno of SMTP_OK means that the local
-/* machine is the best mail exchanger for the specified destination.
-/* It is left up to the caller to decide if this is a mailer loop
-/* or if this is a "do what I mean" request.
+/* that represents the destination domain, or with an optional fallback
+/* relay when the destination cannot be found, or when all the
+/* destination servers are unavailable.
/*
/* The destination is either a host (or domain) name or a numeric
/* address. Symbolic or numeric service port information may be
/* suppress mail exchanger lookups.
/*
/* Numerical address information should always be quoted with `[]'.
-/*
-/* smtp_connect_domain() attempts to make an SMTP connection to
-/* the named host or domain and network port (network byte order).
-/* \fIname\fR is used to look up mail exchanger information via
-/* the Internet domain name system (DNS).
-/* When no mail exchanger is listed for \fIname\fR, the request
-/* is passed to smtp_connect_host().
-/* Otherwise, mail exchanger hosts are tried in order of preference,
-/* until one is found that responds. In order to avoid mailer loops,
-/* the search for mail exchanger hosts stops when a host is found
-/* that has the same preference as the sending machine.
-/*
-/* smtp_connect_host() makes an SMTP connection without looking up
-/* mail exchanger information. The host can be specified as an
-/* Internet network address or as a symbolic host name.
/* DIAGNOSTICS
-/* All routines either return an SMTP_SESSION pointer, or
-/* return a null pointer and set the \fIsmtp_errno\fR
-/* global variable accordingly:
-/* .IP SMTP_RETRY
-/* The connection attempt failed, but should be retried later.
-/* .IP SMTP_FAIL
-/* The connection attempt failed.
-/* .PP
-/* In addition, a textual description of the error is made available
-/* via the \fIwhy\fR argument.
+/* The delivery status is the result value.
/* SEE ALSO
/* smtp_proto(3) SMTP client protocol
/* LICENSE
#include <mail_params.h>
#include <own_inet_addr.h>
+#include <deliver_pass.h>
+#include <debug_peer.h>
+#include <mail_error.h>
/* DNS library. */
/* smtp_connect_addr - connect to explicit address */
-static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port,
- VSTRING *why)
+static SMTP_SESSION *smtp_connect_addr(SMTP_STATE *state, DNS_RR *addr,
+ unsigned port)
{
char *myname = "smtp_connect_addr";
struct sockaddr_in sin;
/*
* Sanity checks.
*/
- if (addr->data_len > sizeof(sin.sin_addr)) {
- msg_warn("%s: skip address with length %d", myname, addr->data_len);
- smtp_errno = SMTP_RETRY;
- return (0);
- }
+ if (addr->data_len > sizeof(sin.sin_addr))
+ msg_panic("%s: unexpected address length %d", myname, addr->data_len);
/*
* Initialize.
conn_stat = sane_connect(sock, (struct sockaddr *) & sin, sizeof(sin));
}
if (conn_stat < 0) {
- vstring_sprintf(why, "connect to %s[%s]: %m",
- addr->name, inet_ntoa(sin.sin_addr));
- smtp_errno = SMTP_RETRY;
+ smtp_site_fail(state, 450, "connect to %s[%s] port %u: %m",
+ addr->name, inet_ntoa(sin.sin_addr), ntohs(port));
close(sock);
return (0);
}
* Skip this host if it takes no action within some time limit.
*/
if (read_wait(sock, var_smtp_helo_tmout) < 0) {
- vstring_sprintf(why, "connect to %s[%s]: read timeout",
- addr->name, inet_ntoa(sin.sin_addr));
- smtp_errno = SMTP_RETRY;
+ smtp_site_fail(state, 450, "connect to %s[%s] port %u: read timeout",
+ addr->name, inet_ntoa(sin.sin_addr), ntohs(port));
close(sock);
return (0);
}
*/
stream = vstream_fdopen(sock, O_RDWR);
if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) {
- vstring_sprintf(why, "connect to %s[%s]: server dropped connection without sending the initial SMTP greeting",
- addr->name, inet_ntoa(sin.sin_addr));
- smtp_errno = SMTP_RETRY;
+ smtp_site_fail(state, 450, "connect to %s[%s] port %u: "
+ "server dropped connection without sending the initial SMTP greeting",
+ addr->name, inet_ntoa(sin.sin_addr), ntohs(port));
vstream_fclose(stream);
return (0);
}
* Skip this host if it sends a 4xx greeting.
*/
if (ch == '4' && var_smtp_skip_4xx_greeting) {
- vstring_sprintf(why, "connect to %s[%s]: server refused mail service",
- addr->name, inet_ntoa(sin.sin_addr));
- smtp_errno = SMTP_RETRY;
+ smtp_site_fail(state, 450, "connect to %s[%s] port %u: "
+ "server refused mail service",
+ addr->name, inet_ntoa(sin.sin_addr), ntohs(port));
vstream_fclose(stream);
return (0);
}
* Skip this host if it sends a 5xx greeting.
*/
if (ch == '5' && var_smtp_skip_5xx_greeting) {
- vstring_sprintf(why, "connect to %s[%s]: server refused mail service",
- addr->name, inet_ntoa(sin.sin_addr));
- smtp_errno = SMTP_RETRY;
+ smtp_site_fail(state, 450, "connect to %s[%s] port %u: "
+ "server refused mail service",
+ addr->name, inet_ntoa(sin.sin_addr), ntohs(port));
vstream_fclose(stream);
return (0);
}
return (smtp_session_alloc(stream, addr->name, inet_ntoa(sin.sin_addr)));
}
-/* smtp_connect_host - direct connection to host */
-
-SMTP_SESSION *smtp_connect_host(char *host, unsigned port, VSTRING *why)
-{
- SMTP_SESSION *session = 0;
- DNS_RR *addr_list;
- DNS_RR *addr;
-
- /*
- * Try each address in the specified order until we find one that works.
- * The addresses belong to the same A record, so we have no information
- * on what address is "best".
- */
- addr_list = smtp_host_addr(host, why);
- for (addr = addr_list; addr; addr = addr->next) {
- if ((session = smtp_connect_addr(addr, port, why)) != 0) {
- session->best = 1;
- break;
- }
- msg_info("%s (port %d)", vstring_str(why), ntohs(port));
- }
- dns_rr_free(addr_list);
- return (session);
-}
-
-/* smtp_connect_domain - connect to smtp server for domain */
-
-SMTP_SESSION *smtp_connect_domain(char *name, unsigned port, VSTRING *why,
- int *found_myself)
-{
- SMTP_SESSION *session = 0;
- DNS_RR *addr_list;
- DNS_RR *addr;
-
- /*
- * Try each mail exchanger in order of preference until we find one that
- * responds. Once we find a server that responds we never try
- * alternative mail exchangers. The benefit of this is that we will use
- * backup hosts only when we are unable to reach the primary MX host. If
- * the primary MX host is reachable but does not want to receive our
- * mail, there is no point in trying the backup hosts.
- */
- addr_list = smtp_domain_addr(name, why, found_myself);
- for (addr = addr_list; addr; addr = addr->next) {
- if ((session = smtp_connect_addr(addr, port, why)) != 0) {
- session->best = (addr->pref == addr_list->pref);
- break;
- }
- msg_info("%s (port %d)", vstring_str(why), ntohs(port));
- }
- dns_rr_free(addr_list);
- return (session);
-}
-
/* smtp_parse_destination - parse destination */
static char *smtp_parse_destination(char *destination, char *def_service,
/* smtp_connect - establish SMTP connection */
-SMTP_SESSION *smtp_connect(char *destination, VSTRING *why)
+int smtp_connect(SMTP_STATE *state)
{
- SMTP_SESSION *session = 0;
- char *dest_buf = 0;
+ VSTRING *why = vstring_alloc(100);
+ DELIVER_REQUEST *request = state->request;
+ char *dest_buf;
char *host;
unsigned port;
char *def_service = "smtp"; /* XXX configurable? */
ARGV *sites;
char *dest;
char **cpp;
- int found_myself = 0;
+ int found_myself;
+ DNS_RR *addr_list;
+ DNS_RR *addr;
/*
* First try to deliver to the indicated destination, then try to deliver
- * to the optional fall-back relays.
+ * to the optional fall-back relays. Sanity check in case we allow the
+ * primary destination to be a list (we did for some time in the past).
+ *
+ * After a soft error, log recipient deferrals only when there are no
+ * further possibilities to deliver.
*/
sites = argv_alloc(1);
- argv_add(sites, destination, (char *) 0);
+ argv_add(sites, request->nexthop, (char *) 0);
+ if (sites->argc == 0)
+ msg_panic("null destination: \"%s\"", request->nexthop);
argv_split_append(sites, var_fallback_relay, ", \t\r\n");
for (cpp = sites->argv; (dest = *cpp) != 0; cpp++) {
+ found_myself = 0;
+ state->final = (cpp[1] == 0);
/*
- * Parse the destination. Default is to use the SMTP port.
+ * Parse the destination. Default is to use the SMTP port. Look up
+ * the address instead of the mail exchanger when a quoted host is
+ * specified, or when DNS lookups are disabled.
*/
dest_buf = smtp_parse_destination(dest, def_service, &host, &port);
-
- /*
- * Connect to an SMTP server. Skip mail exchanger lookups when a
- * quoted host is specified, or when DNS lookups are disabled.
- */
if (msg_verbose)
- msg_info("connecting to %s port %d", host, ntohs(port));
+ msg_info("connecting to \"%s\" port \"%d\"", host, ntohs(port));
if (var_disable_dns || *dest == '[') {
- session = smtp_connect_host(host, port, why);
+ addr_list = smtp_host_addr(host, why);
} else {
- session = smtp_connect_domain(host, port, why, &found_myself);
+ addr_list = smtp_domain_addr(host, why, &found_myself);
}
myfree(dest_buf);
/*
- * Done if we have a session, or if we have no session and this host
- * is the best MX relay for the destination. Agreed, an errno of OK
- * after failure is a weird way to reporting progress.
+ * Handle host lookup problems. XXX It would be nice if the address
+ * lookup routines could do their own smtp_site_fail() calls instead
+ * of having us make sense of things from a distance. The
+ * complication is that an unrecoverable host lookup error may still
+ * be followed by an attempt to deliver via a fallback relay.
*/
- if (session != 0 || smtp_errno == SMTP_OK || found_myself)
- break;
- }
-
- /*
- * Sanity check. The destination must not be empty or all blanks.
- */
- if (session == 0 && dest_buf == 0)
- msg_panic("null destination: \"%s\"", destination);
-
- /*
- * Pay attention to what could be configuration problems, and pretend
- * that these are recoverable rather than bouncing the mail.
- */
- if (session == 0 && smtp_errno == SMTP_FAIL) {
- if (strcmp(destination, var_relayhost) == 0) {
- msg_warn("%s configuration problem: %s",
- VAR_RELAYHOST, var_relayhost);
- smtp_errno = SMTP_RETRY;
+ if (addr_list == 0) {
+
+ /*
+ * Mail loops back to myself. An smtp_errno of OK means that we
+ * should hand off the mail to a local transport. This hand-off
+ * should not happen with fallback relays.
+ */
+ if (smtp_errno == SMTP_OK && cpp == sites->argv) {
+ state->status =
+ deliver_pass_all(MAIL_CLASS_PRIVATE, var_bestmx_transp,
+ request);
+ break;
+ }
+ if (found_myself) {
+ smtp_site_fail(state, 450,
+ "%s: %s", request->queue_id, vstring_str(why));
+ break;
+ }
+
+ /*
+ * Pay attention to what could be configuration problems, and
+ * pretend that these are recoverable rather than bouncing the
+ * mail.
+ */
+ if (strcmp(request->nexthop, var_relayhost) == 0) {
+ msg_warn("%s configuration problem: %s",
+ VAR_RELAYHOST, var_relayhost);
+ smtp_site_fail(state, 450,
+ "%s: %s", request->queue_id, vstring_str(why));
+ continue;
+ }
+ if (cpp > sites->argv && sites->argc > 1) {
+ msg_warn("%s problem: %s",
+ VAR_FALLBACK_RELAY, var_fallback_relay);
+ smtp_site_fail(state, 450,
+ "%s: %s", request->queue_id, vstring_str(why));
+ continue;
+ }
+ smtp_site_fail(state, cpp[1] || smtp_errno == SMTP_RETRY ? 450 : 550,
+ "%s: %s", request->queue_id, vstring_str(why));
}
- if (cpp > sites->argv && sites->argc > 1) {
- msg_warn("%s problem: %s",
- VAR_FALLBACK_RELAY, var_fallback_relay);
- smtp_errno = SMTP_RETRY;
+
+ /*
+ * Connect to an SMTP server. XXX Limit the number of addresses that
+ * we're willing to try for a non-fallback destination.
+ *
+ * After a soft error, log deferrals and update delivery status values
+ * only when there are no further attempts.
+ */
+ for (addr = addr_list; addr; addr = addr->next) {
+ if ((state->session = smtp_connect_addr(state, addr, port)) != 0) {
+ state->status = 0;
+ state->session->best = (addr->pref == addr_list->pref);
+ debug_peer_check(state->session->host, state->session->addr);
+ state->final = (cpp[1] == 0 && addr->next == 0);
+ if (smtp_helo(state) == 0)
+ smtp_xfer(state);
+ if (state->history != 0
+ && (state->error_mask & name_mask(VAR_NOTIFY_CLASSES,
+ mail_error_masks, var_notify_classes)))
+ smtp_chat_notify(state);
+ /* XXX smtp_xfer() may abort in the middle of DATA. */
+ smtp_session_free(state->session);
+ debug_peer_restore();
+ if (state->status == 0)
+ break;
+ }
}
+ dns_rr_free(addr_list);
}
/*
* Cleanup.
*/
+ vstring_free(why);
argv_free(sites);
- return (session);
+ return (state->status);
}
state->size_limit = 0;
state->space_left = 0;
state->mime_state = 0;
+ state->final = 0;
return (state);
}
/* what appear to be configuration errors - very likely, they
/* would suffer the same problem and just cause more trouble.
/*
+/* In case of a soft error, action depends on whether there are
+/* more possibilities to deliver (log one generic record) or whether
+/* the error happens with the last possibility (log each recipient).
+/*
/* 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.
vstring_vsprintf(why, format, ap);
va_end(ap);
+ /*
+ * Don't log deferred recipients yet when there are still untried
+ * possibilities to deliver. Just log why we're abandoning this host.
+ */
+ if (soft_error && state->final == 0) {
+ msg_info("%s: %s", request->queue_id, vstring_str(why));
+ state->status |= -1;
+ }
+
/*
* 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)
- (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
- rcpt->orig_addr, rcpt->address, rcpt->offset,
- session ? session->namaddr : "none",
- request->arrival_time, "%s", vstring_str(why));
- if (status == 0) {
- deliver_completed(state->src, rcpt->offset);
- rcpt->offset = 0;
+ else {
+ 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)
+ (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
+ rcpt->orig_addr, rcpt->address, rcpt->offset,
+ 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;
}
- state->status |= status;
+ if (soft_error && request->hop_status == 0)
+ request->hop_status = mystrdup(vstring_str(why));
}
- if (soft_error && request->hop_status == 0)
- request->hop_status = mystrdup(vstring_str(why));
/*
* Cleanup.
RECIPIENT *rcpt;
int status;
int nrcpt;
+ int soft_error = SMTP_SOFT(code);
va_list ap;
VSTRING *why = vstring_alloc(100);
vstring_vsprintf(why, format, ap);
va_end(ap);
+ /*
+ * Don't log deferred recipients yet when there are still untried
+ * possibilities to deliver. Just log why we're abandoning this host.
+ */
+ if (soft_error && state->final == 0) {
+ msg_info("%s: %s", request->queue_id, vstring_str(why));
+ state->status |= -1;
+ }
+
/*
* 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)
- (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
- rcpt->orig_addr, rcpt->address, rcpt->offset,
- session->namaddr, request->arrival_time,
- "%s", vstring_str(why));
- if (status == 0) {
- deliver_completed(state->src, rcpt->offset);
- rcpt->offset = 0;
+ else {
+ 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)
+ (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
+ rcpt->orig_addr, rcpt->address, rcpt->offset,
+ session->namaddr, request->arrival_time,
+ "%s", vstring_str(why));
+ if (status == 0) {
+ deliver_completed(state->src, rcpt->offset);
+ rcpt->offset = 0;
+ }
+ state->status |= status;
}
- state->status |= status;
}
smtp_check_code(state, code);
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
int status;
+ int soft_error = SMTP_SOFT(code);
va_list ap;
+ /*
+ * Don't log deferred recipients yet when there are still untried
+ * possibilities to deliver.
+ */
+ if (soft_error && state->final == 0) {
+ VSTRING *buf = vstring_alloc(10);
+
+ va_start(ap, format);
+ vstring_vsprintf(buf, format, ap);
+ va_end(ap);
+ msg_info("%s: %s", request->queue_id, vstring_str(buf));
+ vstring_free(buf);
+ status = -1;
+ }
+
/*
* 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)
- (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
- rcpt->orig_addr, rcpt->address, rcpt->offset,
- session->namaddr, request->arrival_time, format, ap);
- va_end(ap);
- if (status == 0) {
- deliver_completed(state->src, rcpt->offset);
- rcpt->offset = 0;
+ else {
+ va_start(ap, format);
+ status = (soft_error ? vdefer_append : vbounce_append)
+ (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
+ rcpt->orig_addr, rcpt->address, rcpt->offset,
+ 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;
break;
}
+ /*
+ * Don't log deferred recipients yet when there are still untried
+ * possibilities to deliver. Just log why we're abandoning this host.
+ */
+ if (state->final == 0) {
+ msg_info("%s: %s", request->queue_id, vstring_str(why));
+ state->status |= -1;
+ }
+
/*
* 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(DEL_REQ_TRACE_FLAGS(request->flags),
- request->queue_id,
- rcpt->orig_addr, rcpt->address,
- rcpt->offset, session->namaddr,
- request->arrival_time,
- "%s", vstring_str(why));
+ else {
+ for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
+ rcpt = request->rcpt_list.info + nrcpt;
+ if (rcpt->offset == 0)
+ continue;
+ state->status |= defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
+ request->queue_id,
+ rcpt->orig_addr, rcpt->address,
+ rcpt->offset, session->namaddr,
+ request->arrival_time,
+ "%s", vstring_str(why));
+ }
}
/*
#include "smtpd_sasl_glue.h"
#include "smtpd_check.h"
+#define RESTRICTION_SEPARATORS ", \t\r\n"
+
/*
* Eject seat in case of parsing problems.
*/
* encounter. Dictionaries must be opened before entering the chroot
* jail.
*/
- while ((name = mystrtok(&bp, " \t\r\n,")) != 0) {
+ while ((name = mystrtok(&bp, RESTRICTION_SEPARATORS)) != 0) {
argv_add(argv, name, (char *) 0);
if (last && strcasecmp(last, CHECK_POLICY_SERVICE) == 0)
policy_client_register(name);
smtpd_rest_classes = htable_create(1);
if (*var_rest_classes) {
cp = saved_classes = mystrdup(var_rest_classes);
- while ((name = mystrtok(&cp, " \t\r\n,")) != 0) {
+ while ((name = mystrtok(&cp, RESTRICTION_SEPARATORS)) != 0) {
if ((value = mail_conf_lookup_eval(name)) == 0 || *value == 0)
msg_fatal("restriction class `%s' needs a definition", name);
htable_enter(smtpd_rest_classes, name,
*/
#define ADDROF(x) ((char *) &(x))
- restrictions = argv_split(value, " \t\r\n,");
+ restrictions = argv_split(value, RESTRICTION_SEPARATORS);
memcpy(ADDROF(savebuf), ADDROF(smtpd_check_buf), sizeof(savebuf));
status = setjmp(smtpd_check_buf);
if (status != 0) {
"use \"%s domain-name\" instead",
REJECT_MAPS_RBL, var_mail_name, REJECT_RBL_CLIENT);
}
- while ((rbl_domain = mystrtok(&bp, " \t\r\n,")) != 0) {
+ while ((rbl_domain = mystrtok(&bp, RESTRICTION_SEPARATORS)) != 0) {
result = reject_rbl_addr(state, rbl_domain, state->addr,
SMTPD_NAME_CLIENT);
if (result != SMTPD_CHECK_DUNNO)
if ((owners = check_mail_addr_find(state, sender, smtpd_sender_login_maps,
STR(reply->recipient), (char **) 0)) != 0) {
cp = saved_owners = mystrdup(owners);
- while ((name = mystrtok(&cp, ", \t\r\n")) != 0) {
+ while ((name = mystrtok(&cp, RESTRICTION_SEPARATORS)) != 0) {
if (strcasecmp(state->sasl_username, name) == 0) {
found = 1;
break;
if (smtpd_rest_classes == 0)
smtpd_rest_classes = htable_create(1);
- if ((name = mystrtok(&cp, " \t\r\n,")) == 0)
+ if ((name = mystrtok(&cp, RESTRICTION_SEPARATORS)) == 0)
msg_panic("rest_class: null class name");
if ((entry = htable_locate(smtpd_rest_classes, name)) != 0)
argv_free((ARGV *) entry->value);