From: Wietse Venema Date: Wed, 17 Dec 2003 05:00:00 +0000 (-0500) Subject: postfix-2.0.16-20031217 X-Git-Tag: v2.1-RC1-20040331~19 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=92cfbcfea395b840d37445dc43a6a753a9a830b7;p=thirdparty%2Fpostfix.git postfix-2.0.16-20031217 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 421669a99..04f7799dc 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -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: diff --git a/postfix/README_FILES/LDAP_README b/postfix/README_FILES/LDAP_README index 9aec01209..72ce12363 100644 --- a/postfix/README_FILES/LDAP_README +++ b/postfix/README_FILES/LDAP_README @@ -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 diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 05dcc72c5..8be73a9a4 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -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 ========================================================== diff --git a/postfix/conf/sample-ldap.cf b/postfix/conf/sample-ldap.cf index e1eb3d008..79bbba1e0 100644 --- a/postfix/conf/sample-ldap.cf +++ b/postfix/conf/sample-ldap.cf @@ -27,18 +27,62 @@ #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 @@ -49,10 +93,23 @@ # 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. # @@ -113,7 +170,11 @@ #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 = diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 3fc4b95bf..d83a0dda2 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -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 diff --git a/postfix/src/smtp/Makefile.in b/postfix/src/smtp/Makefile.in index dceecd09c..2ae10116b 100644 --- a/postfix/src/smtp/Makefile.in +++ b/postfix/src/smtp/Makefile.in @@ -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 diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index 8bd9138b5..d39fb17e2 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -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 */ diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index 687d9edf2..f713f8b4c 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -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) diff --git a/postfix/src/smtp/smtp_addr.c b/postfix/src/smtp/smtp_addr.c index 123b4cb53..0bf04b827 100644 --- a/postfix/src/smtp/smtp_addr.c +++ b/postfix/src/smtp/smtp_addr.c @@ -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 @@ -47,16 +46,7 @@ /* 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 #include +#include /* 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); } diff --git a/postfix/src/smtp/smtp_addr.h b/postfix/src/smtp/smtp_addr.h index 1e7621c9f..d9f72833f 100644 --- a/postfix/src/smtp/smtp_addr.h +++ b/postfix/src/smtp/smtp_addr.h @@ -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 diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c index 79982ad9d..547f88e4c 100644 --- a/postfix/src/smtp/smtp_connect.c +++ b/postfix/src/smtp/smtp_connect.c @@ -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 #include -#include #include #include @@ -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); } diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index d44d7027a..849c31f79 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -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 "." so it diff --git a/postfix/src/smtp/smtp_state.c b/postfix/src/smtp/smtp_state.c index 72c0d4a12..ac63d27f6 100644 --- a/postfix/src/smtp/smtp_state.c +++ b/postfix/src/smtp/smtp_state.c @@ -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); } diff --git a/postfix/src/smtp/smtp_trouble.c b/postfix/src/smtp/smtp_trouble.c index 55b8af3ec..53a1b1ec2 100644 --- a/postfix/src/smtp/smtp_trouble.c +++ b/postfix/src/smtp/smtp_trouble.c @@ -44,35 +44,53 @@ /* 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++) { diff --git a/postfix/src/util/dict_ldap.c b/postfix/src/util/dict_ldap.c index 77da52494..2a819be41 100644 --- a/postfix/src/util/dict_ldap.c +++ b/postfix/src/util/dict_ldap.c @@ -154,6 +154,7 @@ #include #include #include +#include /* * 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. */ /*