]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.0.16-20031216
authorWietse Venema <wietse@porcupine.org>
Tue, 16 Dec 2003 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:29:20 +0000 (06:29 +0000)
12 files changed:
postfix/HISTORY
postfix/README_FILES/XFORWARD_README
postfix/html/showq.8.html
postfix/src/dns/dns_lookup.c
postfix/src/global/mail_version.h
postfix/src/showq/showq.c
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_state.c
postfix/src/smtp/smtp_trouble.c
postfix/src/smtpd/smtpd_check.c

index 0e2117e6928168ad0bae460144498baa8dbfc0e8..421669a9973efd0cbf09772c5831c653ba7b1cfc 100644 (file)
@@ -8818,13 +8818,14 @@ Apologies for any names omitted.
        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
 
@@ -8864,6 +8865,16 @@ Apologies for any names omitted.
        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
@@ -8886,9 +8897,8 @@ Open problems:
        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).
@@ -8929,8 +8939,8 @@ Open problems:
 
        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.
index f58b440005a87090f560e597273c749971b6bdea..bbd126bd23ee5b1d523e65bb7bac9a5e7be49439 100644 (file)
@@ -16,7 +16,7 @@ This extension is implemented as a separate command, so that it
 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
 ==============
@@ -32,7 +32,7 @@ names are shown in upper case, they are in fact case insensitive.
 
     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
index d694e57ceb57bfa8d7d75ab8f263f2bffc4c799c..f3b5c3194870e7ef278fc9497449b5ffdfbf24d7 100644 (file)
@@ -1,4 +1,4 @@
-<html> <head> </head> <body> <pre>
+<html> <body> <pre>
 SHOWQ(8)                                                 SHOWQ(8)
 
 <b>NAME</b>
index c2c3aeb46454070a32e3d9e78a8f2868eec82199..cef3694efb75d5bba26ca0e04317faa16624b7b2 100644 (file)
@@ -141,7 +141,7 @@ static int dns_query(const char *name, int type, int flags,
 {
     HEADER *reply_header;
     int     len;
-    int     saved_options = _res.options;
+    unsigned long saved_options = _res.options;
 
     /*
      * Initialize the name service.
index 7932805917c03639bdd413b6953c3821b593147d..3fc4b95bf2b14929839ef0ec5dffb8ceced79b47 100644 (file)
@@ -20,7 +20,7 @@
   * 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
index e514f396fcda878435c3a7e34ce1b27ea4bb1c1d..f3c8bd12eede5463ec003f77b247205b09185e62 100644 (file)
@@ -94,7 +94,7 @@ int     var_dup_filter_limit;
 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 *);
@@ -150,7 +150,9 @@ static void showq_report(VSTREAM *client, char *queue, char *id,
                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)),
index f7352287c90e7c94a21cec4c2a54e1c5ebcb4ce5..8bd9138b50d888b446769023f3631c3be3933bc0 100644 (file)
 #include <mail_params.h>
 #include <mail_conf.h>
 #include <debug_peer.h>
-#include <mail_error.h>
-#include <deliver_pass.h>
 
 /* Single server skeleton. */
 
@@ -327,7 +325,6 @@ int     smtp_host_lookup_mask;
 
 static int deliver_message(DELIVER_REQUEST *request)
 {
-    VSTRING *why;
     SMTP_STATE *state;
     int     result;
 
@@ -349,7 +346,6 @@ static int deliver_message(DELIVER_REQUEST *request)
      * 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;
@@ -360,35 +356,12 @@ static int deliver_message(DELIVER_REQUEST *request)
      * 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);
index 6db4ef950598a3720ab6f402bacf75b6cfaccf8d..687d9edf2f7a5a02fb70c9a0e320e1ac3e50460b 100644 (file)
@@ -55,6 +55,7 @@ typedef struct SMTP_STATE {
     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)
@@ -95,9 +96,7 @@ extern void smtp_session_free(SMTP_SESSION *);
  /*
   * 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
index 95bb9179cee1a0a925abf0861e68819cb8f7a7b4..79982ad9d446e991752d86da5b9a107fb1d374c1 100644 (file)
@@ -6,29 +6,16 @@
 /* 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;
@@ -146,11 +112,8 @@ static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port,
     /*
      * 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.
@@ -212,9 +175,8 @@ static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port,
        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);
     }
@@ -223,9 +185,8 @@ static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port,
      * 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);
     }
@@ -235,9 +196,9 @@ static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port,
      */
     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);
     }
@@ -247,9 +208,9 @@ static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port,
      * 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);
     }
@@ -258,69 +219,15 @@ static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port,
      * 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,
@@ -358,81 +265,136 @@ 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);
 }
index e6c97637166d0fff2cd79669006a8d8ff9b0e726..72c0d4a129e4ed5a06940f2b25008c08c7e13a83 100644 (file)
@@ -72,6 +72,7 @@ SMTP_STATE *smtp_state_alloc(void)
     state->size_limit = 0;
     state->space_left = 0;
     state->mime_state = 0;
+    state->final = 0;
     return (state);
 }
 
index e945fddc440c391b26ded13d504cfc6d460187ad..55b8af3ecc525283a624159d26d886d45d78bf4d 100644 (file)
 /*     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.
@@ -157,27 +161,38 @@ int     smtp_site_fail(SMTP_STATE *state, int code, char *format,...)
     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.
@@ -195,6 +210,7 @@ int     smtp_mesg_fail(SMTP_STATE *state, int code, char *format,...)
     RECIPIENT *rcpt;
     int     status;
     int     nrcpt;
+    int     soft_error = SMTP_SOFT(code);
     va_list ap;
     VSTRING *why = vstring_alloc(100);
 
@@ -205,24 +221,35 @@ int     smtp_mesg_fail(SMTP_STATE *state, int code, char *format,...)
     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);
 
@@ -241,21 +268,39 @@ void    smtp_rcpt_fail(SMTP_STATE *state, int code, RECIPIENT *rcpt,
     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;
@@ -287,20 +332,31 @@ int     smtp_stream_except(SMTP_STATE *state, int code, char *description)
        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));
+       }
     }
 
     /*
index c97383d0487e85d4f67af6d9060fde09bfc6ef44..d962a50964d85e80af0e3674ff7bb107ce0a490d 100644 (file)
 #include "smtpd_sasl_glue.h"
 #include "smtpd_check.h"
 
+#define RESTRICTION_SEPARATORS ", \t\r\n"
+
  /*
   * Eject seat in case of parsing problems.
   */
@@ -596,7 +598,7 @@ static ARGV *smtpd_check_parse(const char *checks)
      * 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);
@@ -767,7 +769,7 @@ void    smtpd_check_init(void)
     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,
@@ -2001,7 +2003,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
      */
 #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) {
@@ -2837,7 +2839,7 @@ static int reject_maps_rbl(SMTPD_STATE *state)
                 "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)
@@ -2875,7 +2877,7 @@ static int reject_auth_sender_login_mismatch(SMTPD_STATE *state, const char *sen
        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;
@@ -4179,7 +4181,7 @@ static void rest_class(char *class)
     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);