]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.0.16-20031217
authorWietse Venema <wietse@porcupine.org>
Wed, 17 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)
15 files changed:
postfix/HISTORY
postfix/README_FILES/LDAP_README
postfix/RELEASE_NOTES
postfix/conf/sample-ldap.cf
postfix/src/global/mail_version.h
postfix/src/smtp/Makefile.in
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_addr.c
postfix/src/smtp/smtp_addr.h
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtp/smtp_state.c
postfix/src/smtp/smtp_trouble.c
postfix/src/util/dict_ldap.c

index 421669a9973efd0cbf09772c5831c653ba7b1cfc..04f7799dc6b3625c5a5296dbeb3a21b2054e5af9 100644 (file)
@@ -8871,9 +8871,19 @@ Apologies for any names omitted.
        between short queue ID and message status).  File:
        showq/showq.c.
 
+20031216-7
+
        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.
+       or fallback relay when delivery fails in the middle of an
+       SMTP session. This includes not only broken connections
+       (easy) but also includes 4xx SMTP server replies (not easy).
+       Files:  smtp/smtp.c, smtp/smtp_connect.c, smtp_trouble.c.
+
+20031217
+
+       Update: LDAP client logging (Liviu Daia) and LDAP client
+       documentation (Victor Duchovni). Files: util/dict_ldap.c,
+       conf/sample-ldap.cf, README_FILES/LDAP_README.
 
 Open problems:
 
index 9aec01209d932d2de9758f17525049e06d286a33..72ce123636000cda21649d30565d414c04f5511d 100644 (file)
@@ -93,7 +93,7 @@ parameters. Defaults are given in parentheses:
                server_port = 778
 
     search_base (No default; you must configure this.)
-       The base at which to conduct the search, e.g. 
+       The RFC2253 base DN at which to conduct the search, e.g. 
                search_base = dc=your, dc=com
 
     timeout (10 seconds)
@@ -104,19 +104,23 @@ parameters. Defaults are given in parentheses:
        The RFC2254 filter used to search the directory, where %s is a 
        substitute for the address Postfix is trying to resolve, e.g.
                query_filter = (&(mail=%s)(paid_up=true))
+       See sample-ldap.cf for more details.
 
     result_filter (%s)
-       Filter applied to result attributes.  Supports the same expansions
-       as the query_filter, and can be easily used to append (or prepend)
-       text.
+       Format template applied to result attributes.  Supports the same
+       expansions as the query_filter, and can be easily used to append
+       (or prepend) text. See sample-ldap.cf for more details.
 
-    domain (Default is to ignore this.)
+    domain (Default is no domain list.)
        This is a list of domain names, paths to files, or dictionaries.
-       If specified, only lookups for the domains on this list will be
-       performed.  This means that the LDAP map won't get searched for
-       'user', nor will it get searched for any domain not listed.  This
-       can significantly reduce the query load on the LDAP server.
+       When specified, only fully qualified search keys with a
+       *non-empty* localpart and a matching domain are eligible for
+       lookup: 'user' lookups, bare domain lookups and "@domain" lookups
+       are not performed. This can significantly reduce the query load
+       on the LDAP server.
                domain = postfix.org, hash:/etc/postfix/searchdomains
+       It is best not to use LDAP to store the domains eligible for LDAP
+       lookups.
 
     result_attribute (maildrop)
        The attribute(s) Postfix will read from any directory entries
@@ -127,6 +131,12 @@ parameters. Defaults are given in parentheses:
        The attribute(s) of directory entries that can contain DNs or URLs.
        If found, a recursive subsequent search is done using their values.
                special_result_attribute = member
+       DN recursion retrieves the same result_attributes as the main
+       query, including the special attributes for further recursion.
+       URI processing retrieves only those attributes that are included
+       in the URI definition and are *also* listed in "result_attribute".
+       If the URI lists any of the map's special result attributes, these
+       are also retrieved and used recursively.
 
     scope (sub)
         The LDAP search scope: sub, base, or one. These translate into
index 05dcc72c511600137fe47200456a9fe752bbcbb1..8be73a9a48f98f249277633d3cc7792814f302ed 100644 (file)
@@ -22,6 +22,13 @@ snapshot release).  Patches change the patchlevel and the release
 date. Snapshots change only the release date, unless they include
 the same bugfixes as a patch release.
 
+Major changes with Postfix snapshot 2.0.16-20031217
+===================================================
+
+The SMTP client now moves on to the next MX host (or fallback relay)
+when delivery fails in the middle of a session. This includes both
+broken connections as well as 4XX replies to SMTP commands.
+
 Incompatible changes with Postfix snapshot 2.0.16-20031215
 ==========================================================
 
index e1eb3d00811e74bd6d45fcc3af0e8cb31ed9dbf3..79bbba1e0a6990768d8697fa50b589ed64649096 100644 (file)
 #version = 2
 
 # The query_filter parameter specifies the filter used for queries.
-# The replacement for "%s" is the address input into the map; e.g.
-# for alias maps, the "user" part (the RFC 2822 local-part) of
-# "user@domain.com" for To: addresses destined for local delivery 
-# (those matching $mydestination or a virtual domain), and all of
-# "user@domain.com" (the RFC 2822 addr-spec) for other addresses.
-# "%u" provides just the user portion of the input, and "%d" provides
-# just the hostname.
+#
+# This parameter supports the following '%' expansions:
+#
+# %s - This is replaced by the input key. RFC 2254 quoting is
+#      used to make sure that the input key does not add
+#      unexpected metacharacters.
+#
+# %u - When the input key is an address of the form user@domain,
+#      %u is replaced by the (2254) quoted local part of the address.
+#      if no domain is specified, %u is replaced by the entire search
+#      string.
+#
+# %d - When the input key is an address of the form user@domain,
+#      %d is replaced by the (2254) quoted domain part of the address.
+#      When the input key has no domain qualifier, %d is replaced by the 
+#      entire search string.
+#
+# The "domain" parameter described below limits the input keys to
+# addresses in matching domains. When the "domain parameter is non-empty,
+# LDAP queries for unqualified addresses or addresses in non-matching
+# domains are suppressed and return no results.
+#
+# A non-empty value must be specified explicitly for this parameter. The empty
+# default will not work.
+#
+# NOTE: DO NOT put double-quotes around the query filter!
 #
 #query_filter = (mailacceptinggeneralid=%s)
 
-# The result_filter parameter specifies the filter to be applied
-# to the result attribute(s).  See query_filter for valid expansions.
+# The result_filter parameter is printf-like format template that
+# is applied separately to each result attribute of matching entries.
+#
+# This parameter supports the following '%' expansions:
+#
+# %s - This is replaced by the value of the result attribute.
+#
+# %u - When the result attribute is an address of the form user@domain,
+#      %u is replaced local part of the address, if the result attribute
+#      is unqualified, %u is replaced by the entire attribute value.
+#
+# %d - When a result attribute is an address of the form user@domain,
+#      %d is replaced by the domain part of the attribute value.
+#      If an attribute value is unqualified %d is replaced by the entire
+#      attribute value.
+#
+# For example, using "result_filter = smtp:[%s]" allows one to use a
+# mailHost attribute as the basis of a transport(5) table. After applying
+# the result filter, multiple values are concatenated as comma separated
+# strings. The expansion_limit and size_limit parameters explained below 
+# allow one to restrict the number of values in the result, which is 
+# especially useful for maps that should return a single value.
+#
+# The default value %s (no quotes!) specifies that each attribute value 
+# should be used as is.
+#
+# NOTE: DO NOT put double-quotes around the result filter!
 #
 #result_filter = %s
 
 
 # The special_result_attribute lists the attribute(s) of an
 # entry which contain links, either ldap url's or distinguished names.
+#
 # The entries referenced by these links are (recursively) treated as if
-# they were contained in the referencing entity.
+# they were contained in the referencing entity. The result_attributes
+# of the referenced entries are retrieved and added to the query result.  
+# The special result attributes of the referenced entries are evaluated
+# recursively.
+#
+# Note that for URI references, only the search base, scope, query filter
+# and attributes are taken from the URI, the scheme, host and port are
+# taken from the map definition. Extensions (even critical) are ignored.
+# Of the attributes listed in the URI, only those listed in result_attribute
+# are used in the search results, and only those listed in
+# special_result_attribute are used for further recursion.
+#
+# The default value of this parameter is empty.
 #
-#special_result_attribute =
+#special_result_attribute = uniquemember
 
 # The scope parameter specifies the LDAP search scope: sub, base, or one.
 #
 #dereference = 0
 
 # The domain parameter limits the LDAP searches to just things in
-# (exactly) the specified list of domains.
+# (exactly) the specified list of domains. When the "domain" parameter
+# is non-empty, LDAP queries for unqualified addresses or addresses in
+# non-matching domains are suppressed and return no results. This includes
+# queries for catchall addresses with no local part (i.e. @domain.tld),
+# which are also skipped.
 #
 #domain =
 
index 3fc4b95bf2b14929839ef0ec5dffb8ceced79b47..d83a0dda2892e34f87af02670e99e4e4da0b1394 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      "20031216"
+#define MAIL_RELEASE_DATE      "20031217"
 
 #define VAR_MAIL_VERSION       "mail_version"
 #define DEF_MAIL_VERSION       "2.0.16-" MAIL_RELEASE_DATE
index dceecd09c902058d328b42fc3a01e7c3c3daff7e..2ae10116b49fc084adc200d79974ce2afad05e6f 100644 (file)
@@ -75,11 +75,6 @@ smtp.o: ../../include/recipient_list.h
 smtp.o: ../../include/mail_params.h
 smtp.o: ../../include/mail_conf.h
 smtp.o: ../../include/debug_peer.h
-smtp.o: ../../include/mail_error.h
-smtp.o: ../../include/deliver_pass.h
-smtp.o: ../../include/mail_proto.h
-smtp.o: ../../include/iostuff.h
-smtp.o: ../../include/attr.h
 smtp.o: ../../include/mail_server.h
 smtp.o: smtp.h
 smtp.o: smtp_sasl.h
@@ -94,12 +89,16 @@ smtp_addr.o: ../../include/stringops.h
 smtp_addr.o: ../../include/myrand.h
 smtp_addr.o: ../../include/mail_params.h
 smtp_addr.o: ../../include/own_inet_addr.h
+smtp_addr.o: ../../include/deliver_pass.h
+smtp_addr.o: ../../include/deliver_request.h
+smtp_addr.o: ../../include/vstream.h
+smtp_addr.o: ../../include/recipient_list.h
+smtp_addr.o: ../../include/mail_proto.h
+smtp_addr.o: ../../include/iostuff.h
+smtp_addr.o: ../../include/attr.h
 smtp_addr.o: ../../include/dns.h
 smtp_addr.o: smtp.h
-smtp_addr.o: ../../include/vstream.h
 smtp_addr.o: ../../include/argv.h
-smtp_addr.o: ../../include/deliver_request.h
-smtp_addr.o: ../../include/recipient_list.h
 smtp_addr.o: smtp_addr.h
 smtp_chat.o: smtp_chat.c
 smtp_chat.o: ../../include/sys_defs.h
@@ -137,6 +136,9 @@ smtp_connect.o: ../../include/host_port.h
 smtp_connect.o: ../../include/sane_connect.h
 smtp_connect.o: ../../include/mail_params.h
 smtp_connect.o: ../../include/own_inet_addr.h
+smtp_connect.o: ../../include/debug_peer.h
+smtp_connect.o: ../../include/mail_error.h
+smtp_connect.o: ../../include/name_mask.h
 smtp_connect.o: ../../include/dns.h
 smtp_connect.o: smtp.h
 smtp_connect.o: ../../include/argv.h
index 8bd9138b50d888b446769023f3631c3be3933bc0..d39fb17e2c930cdff2ea70bea363dfbeac1bb5f1 100644 (file)
@@ -315,10 +315,8 @@ bool    var_smtp_defer_mxaddr;
 bool    var_smtp_send_xforward;
 
  /*
-  * Global variables. smtp_errno is set by the address lookup routines and by
-  * the connection management routines.
+  * Global variables.
   */
-int     smtp_errno;
 int     smtp_host_lookup_mask;
 
 /* deliver_message - deliver message with extreme prejudice */
index 687d9edf2f7a5a02fb70c9a0e320e1ac3e50460b..f713f8b4cf5eb1118cbae639cb9078c7da644df2 100644 (file)
@@ -55,7 +55,8 @@ 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 */
+    int     final_server;              /* final mail server */
+    int     backup_server;             /* relayhost or fallback relay */
 } SMTP_STATE;
 
 #define SMTP_FEATURE_ESMTP     (1<<0)
@@ -73,7 +74,6 @@ typedef struct SMTP_STATE {
  /*
   * smtp.c
   */
-extern int smtp_errno;                 /* XXX can we get rid of this? */
 extern int smtp_host_lookup_mask;      /* host lookup methods to use */
 
 #define SMTP_MASK_DNS          (1<<0)
index 123b4cb5332a996b417cdafe64b637d15ccf53c1..0bf04b827097e0eb682c63be5d982137cca6ee93 100644 (file)
@@ -6,14 +6,13 @@
 /* SYNOPSIS
 /*     #include "smtp_addr.h"
 /*
-/*     DNS_RR *smtp_domain_addr(name, why, found_myself)
+/*     DNS_RR *smtp_domain_addr(state, name)
+/*     SMTP_STATE *state;
 /*     char    *name;
-/*     VSTRING *why;
-/*     int     *found_myself;
 /*
-/*     DNS_RR *smtp_host_addr(name, why)
+/*     DNS_RR *smtp_host_addr(state, name)
+/*     SMTP_STATE *state;
 /*     char    *name;
-/*     VSTRING *why;
 /* DESCRIPTION
 /*     This module implements Internet address lookups. By default,
 /*     lookups are done via the Internet domain name service (DNS).
@@ -27,7 +26,7 @@
 /*     so that it contains only hosts that are more preferred than the
 /*     local mail server itself. When the "best MX is local" feature
 /*     is enabled, the local system is allowed to be the best mail
-/*     exchanger, and the result is a null list pointer. Otherwise,
+/*     exchanger, and mail is delivered accordingly. Otherwise,
 /*     mailer loops are treated as an error.
 /*
 /*     When no mail exchanger is listed in the DNS for \fIname\fR, the
 /*     when DNS lookups are explicitly disabled.
 /*
 /*     All routines either return a DNS_RR pointer, or return a null
-/*     pointer and set the \fIsmtp_errno\fR global variable accordingly:
-/* .IP SMTP_RETRY
-/*     The request failed due to a soft error, and should be retried later.
-/* .IP SMTP_FAIL
-/*     The request attempt failed due to a hard error.
-/* .IP SMTP_OK
-/*     The local machine is the best mail exchanger.
-/* .PP
-/*     In addition, a textual description of the problem is made available
-/*     via the \fIwhy\fR argument.
+/*     pointer and report any problems via the smtp_trouble(3) module.
 /* LICENSE
 /* .ad
 /* .fi
@@ -119,6 +109,7 @@ static int h_errno = TRY_AGAIN;
 
 #include <mail_params.h>
 #include <own_inet_addr.h>
+#include <deliver_pass.h>
 
 /* DNS library. */
 
@@ -129,6 +120,9 @@ static int h_errno = TRY_AGAIN;
 #include "smtp.h"
 #include "smtp_addr.h"
 
+#define ERROR_CLASS_RETRY      450
+#define ERROR_CLASS_FAIL       550
+
 /* smtp_print_addr - print address list */
 
 static void smtp_print_addr(char *what, DNS_RR *addr_list)
@@ -152,7 +146,8 @@ static void smtp_print_addr(char *what, DNS_RR *addr_list)
 
 /* smtp_addr_one - address lookup for one host name */
 
-static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, VSTRING *why)
+static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
+                                    VSTRING *why, int *error_class)
 {
     char   *myname = "smtp_addr_one";
     struct in_addr inaddr;
@@ -185,13 +180,13 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, VSTRI
            addr_list = dns_rr_append(addr_list, addr);
            return (addr_list);
        default:
-           smtp_errno = SMTP_RETRY;
+           *error_class = ERROR_CLASS_RETRY;
            return (addr_list);
        case DNS_FAIL:
-           smtp_errno = SMTP_FAIL;
+           *error_class = ERROR_CLASS_FAIL;
            return (addr_list);
        case DNS_NOTFOUND:
-           smtp_errno = SMTP_FAIL;
+           *error_class = ERROR_CLASS_FAIL;
            /* maybe gethostbyname() will succeed */
            break;
        }
@@ -204,12 +199,13 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, VSTRI
        memset((char *) &fixed, 0, sizeof(fixed));
        if ((hp = gethostbyname(host)) == 0) {
            vstring_sprintf(why, "%s: %s", host, HSTRERROR(h_errno));
-           smtp_errno = (h_errno == TRY_AGAIN ? SMTP_RETRY : SMTP_FAIL);
+           *error_class = (h_errno == TRY_AGAIN ?
+                           ERROR_CLASS_RETRY : ERROR_CLASS_FAIL);
        } else if (hp->h_addrtype != AF_INET) {
            vstring_sprintf(why, "%s: host not found", host);
            msg_warn("%s: unknown address family %d for %s",
                     myname, hp->h_addrtype, host);
-           smtp_errno = SMTP_FAIL;
+           *error_class = ERROR_CLASS_FAIL;
        } else {
            while (hp->h_addr_list[0]) {
                addr_list = dns_rr_append(addr_list,
@@ -230,16 +226,17 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, VSTRI
 
 /* smtp_addr_list - address lookup for a list of mail exchangers */
 
-static DNS_RR *smtp_addr_list(DNS_RR *mx_names, VSTRING *why)
+static DNS_RR *smtp_addr_list(DNS_RR *mx_names, VSTRING *why, int *error_class)
 {
     DNS_RR *addr_list = 0;
     DNS_RR *rr;
 
     /*
      * As long as we are able to look up any host address, we ignore problems
-     * with DNS lookups.
+     * with DNS lookups (except if we're backup MX, and all the better MX
+     * hosts can't be found).
      * 
-     * XXX 2821: update smtp_errno (0->FAIL upon unrecoverable lookup error,
+     * XXX 2821: update error_class (0->FAIL upon unrecoverable lookup error,
      * any->RETRY upon temporary lookup error) so that we can correctly
      * handle the case of no resolvable MX host. Currently this is always
      * treated as a soft error. RFC 2821 wants a more precise response.
@@ -247,7 +244,8 @@ static DNS_RR *smtp_addr_list(DNS_RR *mx_names, VSTRING *why)
     for (rr = mx_names; rr; rr = rr->next) {
        if (rr->type != T_MX)
            msg_panic("smtp_addr_list: bad resource type: %d", rr->type);
-       addr_list = smtp_addr_one(addr_list, (char *) rr->data, rr->pref, why);
+       addr_list = smtp_addr_one(addr_list, (char *) rr->data, rr->pref,
+                                 why, error_class);
     }
     return (addr_list);
 }
@@ -330,13 +328,16 @@ static int smtp_compare_pref(DNS_RR *a, DNS_RR *b)
 
 /* smtp_domain_addr - mail exchanger address lookup */
 
-DNS_RR *smtp_domain_addr(char *name, VSTRING *why, int *found_myself)
+DNS_RR *smtp_domain_addr(SMTP_STATE *state, char *name)
 {
+    DELIVER_REQUEST *request = state->request;
     DNS_RR *mx_names;
     DNS_RR *addr_list = 0;
     DNS_RR *self = 0;
     unsigned best_pref;
     unsigned best_found;
+    int     error_class;
+    VSTRING *why = vstring_alloc(1);
 
     /*
      * Preferences from DNS use 0..32767, fall-backs use 32768+.
@@ -390,26 +391,40 @@ DNS_RR *smtp_domain_addr(char *name, VSTRING *why, int *found_myself)
      * that an IP address is listed only under one hostname. However, looking
      * at hostnames provides a partial solution for MX hosts behind a NAT
      * gateway.
+     * 
+     * Defer host lookup errors if a) there are more mail servers or b) we are
+     * looking up a relayhost or fallback relay.
      */
+#define DEFER_HOST_LOOKUP_ERROR(s) \
+       ((s)->final_server == 0 || (s)->backup_server)
+
     switch (dns_lookup(name, T_MX, 0, &mx_names, (VSTRING *) 0, why)) {
     default:
-       smtp_errno = SMTP_RETRY;
        if (var_ign_mx_lookup_err)
-           addr_list = smtp_host_addr(name, why);
+           addr_list = smtp_host_addr(state, name);
+       else
+           smtp_site_fail(state, ERROR_CLASS_RETRY,
+                          "%s: %s", request->queue_id, vstring_str(why));
        break;
     case DNS_FAIL:
-       smtp_errno = SMTP_FAIL;
        if (var_ign_mx_lookup_err)
-           addr_list = smtp_host_addr(name, why);
+           addr_list = smtp_host_addr(state, name);
+       else {
+           smtp_site_fail(state, DEFER_HOST_LOOKUP_ERROR(state) ?
+                          ERROR_CLASS_RETRY : ERROR_CLASS_FAIL,
+                          "%s: %s", request->queue_id, vstring_str(why));
+       }
        break;
     case DNS_OK:
        mx_names = dns_rr_sort(mx_names, smtp_compare_pref);
        best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE);
-       addr_list = smtp_addr_list(mx_names, why);
+       addr_list = smtp_addr_list(mx_names, why, &error_class);
        dns_rr_free(mx_names);
        if (addr_list == 0) {
-           if (var_smtp_defer_mxaddr)
-               smtp_errno = SMTP_RETRY;
+           if (var_smtp_defer_mxaddr || DEFER_HOST_LOOKUP_ERROR(state))
+               error_class = ERROR_CLASS_RETRY;
+           smtp_site_fail(state, error_class,
+                          "%s: %s", request->queue_id, vstring_str(why));
            msg_warn("no MX host for %s has a valid A record", name);
            break;
        }
@@ -420,18 +435,23 @@ DNS_RR *smtp_domain_addr(char *name, VSTRING *why, int *found_myself)
            addr_list = smtp_truncate_self(addr_list, self->pref);
            if (addr_list == 0) {
                if (best_pref != best_found) {
-                   vstring_sprintf(why, "unable to find primary relay for %s",
-                                   name);
-                   smtp_errno = SMTP_RETRY;
+                   smtp_site_fail(state, ERROR_CLASS_RETRY,
+                                  "%s: unable to find primary relay for %s",
+                                  request->queue_id, name);
                } else if (*var_bestmx_transp != 0) {   /* we're best MX */
-                   smtp_errno = SMTP_OK;
+                   state->status =
+                       deliver_pass_all(MAIL_CLASS_PRIVATE, var_bestmx_transp,
+                                        request);
+                   state->final_server = 1;
                } else {
-                   msg_warn("mailer loop: best MX host for %s is local",
-                            name);
-                   vstring_sprintf(why, "mail for %s loops back to myself",
-                                   name);
-                   smtp_errno = SMTP_FAIL;
+                   msg_warn("%s is best MX host for %s but no local, virtual "
+                        "or remote delivery is configured for that domain",
+                            var_myhostname, request->nexthop);
+                   smtp_site_fail(state, ERROR_CLASS_FAIL,
+                                  "%s: mail for %s loops back to myself",
+                                  request->queue_id, name);
                }
+               break;
            }
        }
        if (addr_list && addr_list->next && var_smtp_rand_addr) {
@@ -440,32 +460,43 @@ DNS_RR *smtp_domain_addr(char *name, VSTRING *why, int *found_myself)
        }
        break;
     case DNS_NOTFOUND:
-       addr_list = smtp_host_addr(name, why);
+       addr_list = smtp_host_addr(state, name);
        break;
     }
 
     /*
      * Clean up.
      */
-    *found_myself = (self != 0);
+    vstring_free(why);
     return (addr_list);
 }
 
 /* smtp_host_addr - direct host lookup */
 
-DNS_RR *smtp_host_addr(char *host, VSTRING *why)
+DNS_RR *smtp_host_addr(SMTP_STATE *state, char *host)
 {
+    DELIVER_REQUEST *request = state->request;
     DNS_RR *addr_list;
+    int     error_class;
+    VSTRING *why = vstring_alloc(1);
 
     /*
      * If the host is specified by numerical address, just convert the
      * address to internal form. Otherwise, the host is specified by name.
      */
 #define PREF0  0
-    addr_list = smtp_addr_one((DNS_RR *) 0, host, PREF0, why);
-    if (addr_list && addr_list->next && var_smtp_rand_addr)
-       addr_list = dns_rr_shuffle(addr_list);
-    if (msg_verbose)
-       smtp_print_addr(host, addr_list);
+    addr_list = smtp_addr_one((DNS_RR *) 0, host, PREF0, why, &error_class);
+    if (addr_list == 0) {
+       if (DEFER_HOST_LOOKUP_ERROR(state))
+           error_class = ERROR_CLASS_RETRY;
+       smtp_site_fail(state, error_class,
+                      "%s: %s", request->queue_id, vstring_str(why));
+    } else {
+       if (addr_list->next && var_smtp_rand_addr)
+           addr_list = dns_rr_shuffle(addr_list);
+       if (msg_verbose)
+           smtp_print_addr(host, addr_list);
+    }
+    vstring_free(why);
     return (addr_list);
 }
index 1e7621c9fcc53cc512a89fffaffba8b67883a2a6..d9f72833f730b4b4f6b1bb0e225e06159a01799d 100644 (file)
@@ -16,8 +16,8 @@
  /*
   * Internal interfaces.
   */
-extern DNS_RR *smtp_host_addr(char *, VSTRING *);
-extern DNS_RR *smtp_domain_addr(char *, VSTRING *, int *);
+extern DNS_RR *smtp_host_addr(SMTP_STATE *state, char *);
+extern DNS_RR *smtp_domain_addr(SMTP_STATE *state, char *);
 
 /* LICENSE
 /* .ad
index 79982ad9d446e991752d86da5b9a107fb1d374c1..547f88e4c882505852c7d0019c28967d978107fa 100644 (file)
@@ -2,7 +2,7 @@
 /* NAME
 /*     smtp_connect 3
 /* SUMMARY
-/*     connect to SMTP server
+/*     connect to SMTP server and deliver
 /* SYNOPSIS
 /*     #include "smtp.h"
 /*
@@ -81,7 +81,6 @@
 
 #include <mail_params.h>
 #include <own_inet_addr.h>
-#include <deliver_pass.h>
 #include <debug_peer.h>
 #include <mail_error.h>
 
@@ -204,27 +203,6 @@ static SMTP_SESSION *smtp_connect_addr(SMTP_STATE *state, DNS_RR *addr,
     }
     vstream_ungetc(stream, ch);
 
-    /*
-     * Skip this host if it sends a 4xx greeting.
-     */
-    if (ch == '4' && var_smtp_skip_4xx_greeting) {
-       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) {
-       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)));
 }
 
@@ -267,7 +245,6 @@ static char *smtp_parse_destination(char *destination, char *def_service,
 
 int     smtp_connect(SMTP_STATE *state)
 {
-    VSTRING *why = vstring_alloc(100);
     DELIVER_REQUEST *request = state->request;
     char   *dest_buf;
     char   *host;
@@ -276,27 +253,41 @@ int     smtp_connect(SMTP_STATE *state)
     ARGV   *sites;
     char   *dest;
     char  **cpp;
-    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. Sanity check in case we allow the
-     * primary destination to be a list (we did for some time in the past).
+     * to the optional fall-back relays.
      * 
-     * After a soft error, log recipient deferrals only when there are no
-     * further possibilities to deliver.
+     * Future proofing: do a null destination sanity check in case we allow the
+     * primary destination to be a list, as it could be just a bunch of
+     * separators.
      */
     sites = argv_alloc(1);
     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");
+    if (*var_fallback_relay)
+       argv_split_append(sites, var_fallback_relay, ", \t\r\n");
 
+    /*
+     * Don't give up after any soft error until we have tried all servers.
+     * 
+     * Don't give up after a hard host lookup error until we have tried the
+     * fallback relay servers.
+     * 
+     * Don't bounce mail after host lookup problems with a relayhost or with
+     * fallback relays.
+     * 
+     * All this means that error handling and error reporting depends on whether
+     * there are more mail servers (state->final_server), or whether we're
+     * looking up a relayhost or fallback relay (state->backup_server).
+     */
     for (cpp = sites->argv; (dest = *cpp) != 0; cpp++) {
-       found_myself = 0;
-       state->final = (cpp[1] == 0);
+       state->final_server = (cpp[1] == 0);
+       state->backup_server =
+           (cpp > sites->argv || strcmp(request->nexthop, var_relayhost) == 0);
 
        /*
         * Parse the destination. Default is to use the SMTP port. Look up
@@ -307,59 +298,21 @@ int     smtp_connect(SMTP_STATE *state)
        if (msg_verbose)
            msg_info("connecting to \"%s\" port \"%d\"", host, ntohs(port));
        if (var_disable_dns || *dest == '[') {
-           addr_list = smtp_host_addr(host, why);
+           addr_list = smtp_host_addr(state, host);
        } else {
-           addr_list = smtp_domain_addr(host, why, &found_myself);
+           addr_list = smtp_domain_addr(state, host);
        }
        myfree(dest_buf);
 
        /*
-        * 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.
+        * No address list. The mail has / has not been delivered. What to do
+        * next (skip remaining hosts / try to deliver) is recorded in the
+        * state->final_server attribute.
         */
        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 (state->backup_server)
+               msg_warn("%s or %s configuration problem",
+                        VAR_RELAYHOST, VAR_FALLBACK_RELAY);
        }
 
        /*
@@ -371,10 +324,10 @@ int     smtp_connect(SMTP_STATE *state)
         */
        for (addr = addr_list; addr; addr = addr->next) {
            if ((state->session = smtp_connect_addr(state, addr, port)) != 0) {
+               state->final_server = (cpp[1] == 0 && addr->next == 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
@@ -384,17 +337,23 @@ int     smtp_connect(SMTP_STATE *state)
                /* XXX smtp_xfer() may abort in the middle of DATA. */
                smtp_session_free(state->session);
                debug_peer_restore();
-               if (state->status == 0)
+               if (state->status == 0 || state->final_server)
                    break;
            }
        }
        dns_rr_free(addr_list);
+
+       /*
+        * In case someone has raised the "final server" flag before we have
+        * tried all fallback servers.
+        */
+       if (state->final_server)
+           break;
     }
 
     /*
      * Cleanup.
      */
-    vstring_free(why);
     argv_free(sites);
     return (state->status);
 }
index d44d7027a4b945c33ed5caf91f5d7cfe3462c64f..849c31f79b5f8408abde62de3e256a2092a76b85 100644 (file)
@@ -197,10 +197,13 @@ int     smtp_helo(SMTP_STATE *state)
     /*
      * Read and parse the server's SMTP greeting banner.
      */
-    if (((resp = smtp_chat_resp(state))->code / 100) != 2)
+    if ((resp = smtp_chat_resp(state))->code / 100 != 2) {
+       if (var_smtp_skip_5xx_greeting && resp->code / 100 == '5')
+           resp->code -= 100;
        return (smtp_site_fail(state, resp->code,
                               "host %s refused to talk to me: %s",
                         session->namaddr, translit(resp->str, "\n", " ")));
+    }
 
     /*
      * XXX Some PIX firewall versions require flush before ".<CR><LF>" so it
index 72c0d4a129e4ed5a06940f2b25008c08c7e13a83..ac63d27f61cccbe356dbfb0ef64c38cec529c20b 100644 (file)
@@ -72,7 +72,8 @@ SMTP_STATE *smtp_state_alloc(void)
     state->size_limit = 0;
     state->space_left = 0;
     state->mime_state = 0;
-    state->final = 0;
+    state->final_server = 0;
+    state->backup_server = 0;
     return (state);
 }
 
index 55b8af3ecc525283a624159d26d886d45d78bf4d..53a1b1ec2a521bd9d18a7420c8939c799d7dfe45 100644 (file)
 /*     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).
+/*     more mail servers (log an informational record only and try
+/*     the other servers) or whether this is the final server (log
+/*     recipient delivery status records).
+/*
+/*     In the case of a hard error that affects all recipients,
+/*     recipient delivery status records are logged, and the
+/*     final server flag is raised so that any remaining mail
+/*     servers are skipped.
 /*
 /*     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.
+/*     argument gives a textual description.
+/*     The policy is: soft error, non-final server: log an informational
+/*     record why the host is being skipped; soft error, final server:
+/*     defer delivery of all remaining recipients; hard error: bounce all
+/*     remaining recipients and set the "final server" flag so that any
+/*     remaining mail servers will be skipped.
 /*     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 policy is: soft error, non-final server: log an informational
+/*     record why the host is being skipped; soft error, final server:
+/*     defer delivery of all remaining recipients; hard error: bounce all
+/*     remaining recipients and set the "final server" flag so that any
+/*     remaining mail servers will be skipped.
 /*     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.
+/*     recipient limit is reached.
+/*     The policy is: soft error, non-final server: log an informational
+/*     record why the recipient is being skipped; soft error, final server:
+/*     defer delivery of this recipient; hard error: bounce this
+/*     recipient. This routine does not change the "final server" flag.
 /*
 /*     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.
+/*     the SMTP dialog the problem happened.
+/*     The policy is: non-final server: log an informational record
+/*     with the reason why the host is being skipped; final server:
+/*     defer delivery of all remaining recipients.
+/*     The result is non-zero.
 /* DIAGNOSTICS
 /*     Panic: unknown exception code.
 /* SEE ALSO
@@ -141,7 +159,7 @@ static void smtp_check_code(SMTP_STATE *state, int code)
        state->error_mask |= MAIL_ERROR_PROTOCOL;
 }
 
-/* smtp_site_fail - defer site or bounce recipients */
+/* smtp_site_fail - skip site, defer all recipients, or bounce all recipients */
 
 int     smtp_site_fail(SMTP_STATE *state, int code, char *format,...)
 {
@@ -162,17 +180,18 @@ int     smtp_site_fail(SMTP_STATE *state, int code, char *format,...)
     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.
+     * Don't defer the recipients just yet when there are still more mail
+     * servers. Just log something informative to show why we're skipping
+     * this host.
      */
-    if (soft_error && state->final == 0) {
+    if (soft_error && state->final_server == 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.
+     * Defer or bounce all the remaining recipients and raise the final mail
+     * server flag.
      */
     else {
        for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
@@ -192,7 +211,9 @@ int     smtp_site_fail(SMTP_STATE *state, int code, char *format,...)
        }
        if (soft_error && request->hop_status == 0)
            request->hop_status = mystrdup(vstring_str(why));
+       state->final_server = 1;
     }
+    smtp_check_code(state, code);
 
     /*
      * Cleanup.
@@ -201,7 +222,7 @@ int     smtp_site_fail(SMTP_STATE *state, int code, char *format,...)
     return (-1);
 }
 
-/* smtp_mesg_fail - defer message or bounce all recipients */
+/* smtp_mesg_fail - skip site, defer all recipients, or bounce all recipients */
 
 int     smtp_mesg_fail(SMTP_STATE *state, int code, char *format,...)
 {
@@ -222,17 +243,18 @@ int     smtp_mesg_fail(SMTP_STATE *state, int code, char *format,...)
     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.
+     * Don't defer the recipients just yet when there are still more mail
+     * servers. Just log something informative to show why we're skipping
+     * this host.
      */
-    if (soft_error && state->final == 0) {
+    if (soft_error && state->final_server == 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.
+     * Defer or bounce all the remaining recipients and raise the final mail
+     * server flag.
      */
     else {
        for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
@@ -250,6 +272,7 @@ int     smtp_mesg_fail(SMTP_STATE *state, int code, char *format,...)
            }
            state->status |= status;
        }
+       state->final_server = 1;
     }
     smtp_check_code(state, code);
 
@@ -260,7 +283,7 @@ int     smtp_mesg_fail(SMTP_STATE *state, int code, char *format,...)
     return (-1);
 }
 
-/* smtp_rcpt_fail - defer or bounce recipient */
+/* smtp_rcpt_fail - skip, defer, or bounce recipient */
 
 void    smtp_rcpt_fail(SMTP_STATE *state, int code, RECIPIENT *rcpt,
                               char *format,...)
@@ -272,10 +295,11 @@ void    smtp_rcpt_fail(SMTP_STATE *state, int code, RECIPIENT *rcpt,
     va_list ap;
 
     /*
-     * Don't log deferred recipients yet when there are still untried
-     * possibilities to deliver.
+     * Don't defer this recipient record just yet when there are still more
+     * mail servers. Just log something informative to show why we're
+     * skipping this recipient now.
      */
-    if (soft_error && state->final == 0) {
+    if (soft_error && state->final_server == 0) {
        VSTRING *buf = vstring_alloc(10);
 
        va_start(ap, format);
@@ -287,8 +311,15 @@ void    smtp_rcpt_fail(SMTP_STATE *state, int code, RECIPIENT *rcpt,
     }
 
     /*
-     * If this is a soft error, postpone delivery to this recipient.
-     * Otherwise, generate a bounce record for this recipient.
+     * Defer or bounce this specific recipient.
+     * 
+     * If this is a hard error, we must not raise the final mail server flag. We
+     * may still make another SMTP connection to deliver deferred recipients.
+     * 
+     * If this is a soft error, we got here because the final mail server flag
+     * was already set.
+     * 
+     * So don't touch that final mail server flag!
      */
     else {
        va_start(ap, format);
@@ -333,17 +364,16 @@ int     smtp_stream_except(SMTP_STATE *state, int code, char *description)
     }
 
     /*
-     * Don't log deferred recipients yet when there are still untried
-     * possibilities to deliver. Just log why we're abandoning this host.
+     * Don't defer the recipients just yet when there are still more mail
+     * servers. Just log why we're abandoning this host.
      */
-    if (state->final == 0) {
+    if (state->final_server == 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.
+     * Final server. Defer all the remaining recipients.
      */
     else {
        for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
index 77da52494c7576d6e2deb3996fbff4fe2346195e..2a819be41ecfcf52193aa9926da988e22d555f4d 100644 (file)
 #include <lber.h>
 #include <ldap.h>
 #include <string.h>
+#include <ctype.h>
 
  /*
   * Older APIs have weird memory freeing behavior.
@@ -259,8 +260,17 @@ static void dict_ldap_timeout(int unused_sig)
 static void dict_ldap_logprint(LDAP_CONST char *data)
 {
     char   *myname = "dict_ldap_debug";
-
-    msg_info("%s: %s", myname, data);
+    char   *buf,
+           *p;
+
+    buf = mystrdup(data);
+    if (*buf) {
+       p = buf + strlen(buf) - 1;
+       while (p - buf >= 0 && ISSPACE(*p))
+           *p-- = 0;
+    }
+    msg_info("%s: %s", myname, buf);
+    myfree(buf);
 }
 
 
@@ -481,6 +491,21 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
 
 #endif
 
+#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
+    if (dict_ldap->debuglevel > 0 &&
+       ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN,
+                    (LDAP_CONST *) dict_ldap_logprint) != LBER_OPT_SUCCESS)
+       msg_warn("%s: Unable to set ber logprint function.", myname);
+#if defined(LBER_OPT_DEBUG_LEVEL)
+    if (ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL,
+                      &(dict_ldap->debuglevel)) != LBER_OPT_SUCCESS)
+       msg_warn("%s: Unable to set BER debug level.", myname);
+#endif
+    if (ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL,
+                       &(dict_ldap->debuglevel)) != LDAP_OPT_SUCCESS)
+       msg_warn("%s: Unable to set LDAP debug level.", myname);
+#endif
+
     dict_errno = 0;
 
     if (msg_verbose)
@@ -573,16 +598,6 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
                        &(dict_ldap->dereference)) != LDAP_OPT_SUCCESS)
        msg_warn("%s: Unable to set dereference option.", myname);
 
-#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
-    if (dict_ldap->debuglevel > 0 &&
-       ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN,
-                    (LDAP_CONST *) dict_ldap_logprint) != LBER_OPT_SUCCESS)
-       msg_warn("%s: Unable to set ber logprint function.", myname);
-    if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEBUG_LEVEL,
-                       &(dict_ldap->debuglevel)) != LDAP_OPT_SUCCESS)
-       msg_warn("%s: Unable to set LDAP debug level.", myname);
-#endif
-
     /* Chase referrals. */
 
     /*