diff --git a/postfix/HISTORY b/postfix/HISTORY
index 915915c95..bc68e68ed 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -11405,6 +11405,25 @@ Apologies for any names omitted.
Bugfix: new bounce template code did not return after
template syntax error. File: bounce/bounce_template.c
+ Safety: permit_mx_backup now requires that the local MTA
+ is not listed as primary MX for the recipient domain. This
+ prevents mail loops when someone points the primary MX
+ record to Postfix.
+
+20051119
+
+ Workaround: some SMTP servers announce multiple but different
+ lists of SASL methods. Postfix now concatenates the lists
+ instead of logging a warning and remembering only one. File:
+ smtp/smtp_sasl_proto.c.
+
+ Bugfix: the queue manager did not write a per-recipient
+ defer logfile record when the delivery agent crashed between
+ receiving a delivery request, and reporting the delivery
+ status to the queue manager. Found while redesigning the
+ code that handles unavailable transports or destinations.
+ Files: *qmgr/qmgr_deliver.c.
+
Open problems:
"postsuper -r" no longer resets the message arrival time,
diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES
index 1ae66908b..a5f0336fd 100644
--- a/postfix/RELEASE_NOTES
+++ b/postfix/RELEASE_NOTES
@@ -17,6 +17,14 @@ Incompatibility with Postfix 2.1 and earlier
If you upgrade from Postfix 2.1 or earlier, read RELEASE_NOTES-2.2
before proceeding.
+Incompatibility with snapshot 20051120
+======================================
+
+The permit_mx_backup feature now requires that the local MTA is not
+listed as primary MX host for the recipient domain. This prevents
+mail loop problems when someone points the primary MX record at
+Postfix.
+
Major changes with snapshot 20051113
====================================
diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html
index d3ff4cfdf..242d8643e 100644
--- a/postfix/html/postconf.5.html
+++ b/postfix/html/postconf.5.html
@@ -8305,9 +8305,16 @@ system is the final destination. However, the SMTP server will not
forward mail with addresses that have sender-specified routing
information (example: user@elsewhere@domain). Use the optional
permit_mx_backup_networks parameter to require that the primary
-MX hosts match a list of network blocks.
Note: prior to
+MX hosts match a list of network blocks.
NOTE: prior to
Postfix version 2.0, use of permit_mx_backup is not recommended;
-mail may be rejected in case of a temporary DNS lookup problem.
+mail may be rejected in case of a temporary DNS lookup problem.
+
+
NOTE: as of Postfix version 2.3, permit_mx_backup requires
+that the local MTA is not listed as primary MX for the recipient
+domain. This is for safety reasons.
+
+
NOTE: use of permit_mx_backup is not recommended without
+restricting its use with permit_mx_backup_networks.
reject_non_fqdn_recipient
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5
index e8d4599fe..14fe1e75b 100644
--- a/postfix/man/man5/postconf.5
+++ b/postfix/man/man5/postconf.5
@@ -4777,9 +4777,16 @@ information (example: user@elsewhere@domain). Use the optional
permit_mx_backup_networks parameter to require that the primary
MX hosts match a list of network blocks.
.br
-Note: prior to
+NOTE: prior to
Postfix version 2.0, use of permit_mx_backup is not recommended;
mail may be rejected in case of a temporary DNS lookup problem.
+.br
+NOTE: as of Postfix version 2.3, permit_mx_backup requires
+that the local MTA is not listed as primary MX for the recipient
+domain. This is for safety reasons.
+.br
+NOTE: use of permit_mx_backup is not recommended without
+restricting its use with permit_mx_backup_networks.
.IP "\fBreject_non_fqdn_recipient\fR"
Reject the request when the RCPT TO address is not in
fully-qualified domain form, as required by the RFC.
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index b9b29e345..fb3172d14 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -5198,9 +5198,16 @@ system is the final destination. However, the SMTP server will not
forward mail with addresses that have sender-specified routing
information (example: user@elsewhere@domain). Use the optional
permit_mx_backup_networks parameter to require that the primary
-MX hosts match a list of network blocks.
Note: prior to
+MX hosts match a list of network blocks.
NOTE: prior to
Postfix version 2.0, use of permit_mx_backup is not recommended;
-mail may be rejected in case of a temporary DNS lookup problem.
+mail may be rejected in case of a temporary DNS lookup problem.
+
+
NOTE: as of Postfix version 2.3, permit_mx_backup requires
+that the local MTA is not listed as primary MX for the recipient
+domain. This is for safety reasons.
+
+
NOTE: use of permit_mx_backup is not recommended without
+restricting its use with permit_mx_backup_networks.
reject_non_fqdn_recipient
diff --git a/postfix/src/bounce/bounce_template.c b/postfix/src/bounce/bounce_template.c
index 90f1e8b07..eb2e0167d 100644
--- a/postfix/src/bounce/bounce_template.c
+++ b/postfix/src/bounce/bounce_template.c
@@ -47,7 +47,9 @@
/* BOUNCE_TEMPLATE *template;
/* DESCRIPTION
/* This module implements the built-in and external bounce
-/* message template support.
+/* message template support. The content of a template are
+/* private. To access information within a template, use
+/* the API described in this document.
/*
/* bounce_template_create() creates a template, with the
/* specified default settings. The template defaults are not
@@ -81,7 +83,7 @@
/* specified stream.
/*
/* The IS_MUMBLE_TEMPLATE() macros are predicates that
-/* return when the template is of the specified type.
+/* determine whether the template is of the specified type.
/* DIAGNOSTICS
/* Fatal error: out of memory, undefined macro name in template.
/* SEE ALSO
@@ -136,9 +138,9 @@
* Ideally, the bounce template processor would strip the _days etc. suffix
* from the parameter name, and use the parameter name to look up the actual
* parameter value and its default value (the default value specifies the
- * default time unit of that parameter (seconds, minutes, etc.), and allows
- * us to convert the parameter string value into the corresponding number of
- * seconds). The bounce template processor would then use the _hours etc.
+ * default time unit of that parameter (seconds, minutes, etc.)), and use
+ * this to convert the parameter string value into the corresponding number
+ * of seconds. The bounce template processor would then use the _hours etc.
* suffix from the bounce template to divide this number by the number of
* seconds in an hour, etc. and produce the number that is needed for the
* template.
diff --git a/postfix/src/bounce/bounce_templates.c b/postfix/src/bounce/bounce_templates.c
index 7038e3f2d..292ea04a7 100644
--- a/postfix/src/bounce/bounce_templates.c
+++ b/postfix/src/bounce/bounce_templates.c
@@ -6,6 +6,15 @@
/* SYNOPSIS
/* #include
/*
+/* typedef struct {
+/* .in +4
+/* BOUNCE_TEMPLATE *failure;
+/* BOUNCE_TEMPLATE *delay;
+/* BOUNCE_TEMPLATE *success;
+/* BOUNCE_TEMPLATE *verify;
+/* .in -4
+/* } BOUNCE_TEMPLATES;
+/*
/* BOUNCE_TEMPLATES *bounce_templates_create(void)
/*
/* void bounce_templates_free(templates)
diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h
index b1db34f37..e84ce8abc 100644
--- a/postfix/src/dns/dns.h
+++ b/postfix/src/dns/dns.h
@@ -86,7 +86,7 @@ typedef struct DNS_RR {
unsigned int ttl; /* always */
unsigned short pref; /* T_MX only */
struct DNS_RR *next; /* linkage */
- size_t data_len; /* actual data size */
+ size_t data_len; /* actual data size */
char data[1]; /* actually a bunch of data */
} DNS_RR;
@@ -111,6 +111,7 @@ extern void dns_rr_free(DNS_RR *);
extern DNS_RR *dns_rr_copy(DNS_RR *);
extern DNS_RR *dns_rr_append(DNS_RR *, DNS_RR *);
extern DNS_RR *dns_rr_sort(DNS_RR *, int (*) (DNS_RR *, DNS_RR *));
+extern int dns_rr_compare_pref(DNS_RR *, DNS_RR *);
extern DNS_RR *dns_rr_shuffle(DNS_RR *);
extern DNS_RR *dns_rr_remove(DNS_RR *, DNS_RR *);
diff --git a/postfix/src/dns/dns_rr.c b/postfix/src/dns/dns_rr.c
index 01732d0dd..0063a201e 100644
--- a/postfix/src/dns/dns_rr.c
+++ b/postfix/src/dns/dns_rr.c
@@ -30,6 +30,10 @@
/* DNS_RR *list
/* int (*compar)(DNS_RR *, DNS_RR *);
/*
+/* int dns_rr_compare_pref(DNS_RR *a, DNS_RR *b)
+/* DNS_RR *list
+/* DNS_RR *list
+/*
/* DNS_RR *dns_rr_shuffle(list)
/* DNS_RR *list;
/*
@@ -58,6 +62,9 @@
/* order according to a user-specified criterion. The result is the
/* sorted list.
/*
+/* dns_rr_compare_pref() is a dns_rr_sort() helper to sort records
+/* by their MX preference.
+/*
/* dns_rr_shuffle() randomly permutes a list of resource records.
/*
/* dns_rr_remove() removes the specified record from the specified list.
@@ -151,6 +158,23 @@ DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr)
return (list);
}
+/* dns_rr_compare_pref - compare resource records by preference */
+
+int dns_rr_compare_pref(DNS_RR *a, DNS_RR *b)
+{
+ if (a->pref != b->pref)
+ return (a->pref - b->pref);
+#ifdef HAS_IPV6
+ if (a->type == b->type) /* 200412 */
+ return 0;
+ if (a->type == T_AAAA)
+ return (-1);
+ if (b->type == T_AAAA)
+ return (+1);
+#endif
+ return 0;
+}
+
/* dns_rr_sort_callback - glue function */
static int (*dns_rr_sort_user) (DNS_RR *, DNS_RR *);
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index a530b409b..95a6c3009 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20051118"
+#define MAIL_RELEASE_DATE "20051120"
#define MAIL_VERSION_NUMBER "2.3"
#ifdef SNAPSHOT
diff --git a/postfix/src/oqmgr/qmgr_deliver.c b/postfix/src/oqmgr/qmgr_deliver.c
index b3e4cd656..7f392d319 100644
--- a/postfix/src/oqmgr/qmgr_deliver.c
+++ b/postfix/src/oqmgr/qmgr_deliver.c
@@ -219,6 +219,8 @@ static void qmgr_deliver_update(int unused_event, char *context)
static DSN_BUF *dsb;
int status;
DSN dsn;
+ RECIPIENT *recipient;
+ int nrcpt;
if (dsb == 0)
dsb = dsb_create();
@@ -254,6 +256,21 @@ static void qmgr_deliver_update(int unused_event, char *context)
"unknown mail transport error"));
msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
transport->name);
+
+ /*
+ * Assume the worst and write a defer logfile record for each
+ * recipient. This omission was already present in the first queue
+ * manager implementation of 199703, and was fixed 200511.
+ *
+ * Don't move this queue entry back to the todo queue so that
+ * qmgr_defer_transport() can update the defer log. The queue entry
+ * is still hot, and making it cold would involve duplicating most
+ * but not all code at the end of this routine. That's too tricky.
+ */
+ for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) {
+ recipient = entry->rcpt_list.info + nrcpt;
+ qmgr_defer_recipient(message, recipient, &dsn);
+ }
qmgr_defer_transport(transport, &dsn);
}
@@ -287,7 +304,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
* No problems detected. Mark the transport and queue as alive. The queue
* itself won't go away before we dispose of the current queue entry.
*/
- if (VSTRING_LEN(dsb->reason) == 0) {
+ if (status != DELIVER_STAT_CRASH && VSTRING_LEN(dsb->reason) == 0) {
qmgr_transport_unthrottle(transport);
qmgr_queue_unthrottle(queue);
}
diff --git a/postfix/src/qmgr/qmgr_deliver.c b/postfix/src/qmgr/qmgr_deliver.c
index 28f0f2c46..a2d96f511 100644
--- a/postfix/src/qmgr/qmgr_deliver.c
+++ b/postfix/src/qmgr/qmgr_deliver.c
@@ -224,6 +224,8 @@ static void qmgr_deliver_update(int unused_event, char *context)
static DSN_BUF *dsb;
int status;
DSN dsn;
+ RECIPIENT *recipient;
+ int nrcpt;
if (dsb == 0)
dsb = dsb_create();
@@ -259,6 +261,21 @@ static void qmgr_deliver_update(int unused_event, char *context)
"unknown mail transport error"));
msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
transport->name);
+
+ /*
+ * Assume the worst and write a defer logfile record for each
+ * recipient. This omission was already present in the first queue
+ * manager implementation of 199703, and was fixed 200511.
+ *
+ * Don't move this queue entry back to the todo queue so that
+ * qmgr_defer_transport() can update the defer log. The queue entry
+ * is still hot, and making it cold would involve duplicating most
+ * but not all code at the end of this routine. That's too tricky.
+ */
+ for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) {
+ recipient = entry->rcpt_list.info + nrcpt;
+ qmgr_defer_recipient(message, recipient, &dsn);
+ }
qmgr_defer_transport(transport, &dsn);
}
@@ -292,7 +309,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
* No problems detected. Mark the transport and queue as alive. The queue
* itself won't go away before we dispose of the current queue entry.
*/
- if (VSTRING_LEN(dsb->reason) == 0) {
+ if (status != DELIVER_STAT_CRASH && VSTRING_LEN(dsb->reason) == 0) {
qmgr_transport_unthrottle(transport);
qmgr_queue_unthrottle(queue);
}
diff --git a/postfix/src/smtp/smtp_addr.c b/postfix/src/smtp/smtp_addr.c
index 855007efc..ddc06c87e 100644
--- a/postfix/src/smtp/smtp_addr.c
+++ b/postfix/src/smtp/smtp_addr.c
@@ -338,23 +338,6 @@ static DNS_RR *smtp_truncate_self(DNS_RR *addr_list, unsigned pref)
return (addr_list);
}
-/* smtp_compare_pref - compare resource records by preference */
-
-static int smtp_compare_pref(DNS_RR *a, DNS_RR *b)
-{
- if (a->pref != b->pref)
- return (a->pref - b->pref);
-#ifdef HAS_IPV6
- if (a->type == b->type) /* 200412 */
- return 0;
- if (a->type == T_AAAA)
- return (-1);
- if (b->type == T_AAAA)
- return (+1);
-#endif
- return 0;
-}
-
/* smtp_domain_addr - mail exchanger address lookup */
DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_BUF *why,
@@ -437,7 +420,7 @@ DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_BUF *why,
addr_list = smtp_host_addr(name, misc_flags, why);
break;
case DNS_OK:
- mx_names = dns_rr_sort(mx_names, smtp_compare_pref);
+ mx_names = dns_rr_sort(mx_names, dns_rr_compare_pref);
best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE);
addr_list = smtp_addr_list(mx_names, why);
dns_rr_free(mx_names);
@@ -472,7 +455,7 @@ DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_BUF *why,
}
if (addr_list && addr_list->next && var_smtp_rand_addr) {
addr_list = dns_rr_shuffle(addr_list);
- addr_list = dns_rr_sort(addr_list, smtp_compare_pref);
+ addr_list = dns_rr_sort(addr_list, dns_rr_compare_pref);
}
break;
case DNS_INVAL:
@@ -521,7 +504,7 @@ DNS_RR *smtp_host_addr(char *host, int misc_flags, DSN_BUF *why)
addr_list = dns_rr_shuffle(addr_list);
/* The following changes the order of equal-preference hosts. */
if (inet_proto_info()->ai_family_list[1] != 0)
- addr_list = dns_rr_sort(addr_list, smtp_compare_pref);
+ addr_list = dns_rr_sort(addr_list, dns_rr_compare_pref);
}
if (msg_verbose)
smtp_print_addr(host, addr_list);
diff --git a/postfix/src/smtp/smtp_sasl_proto.c b/postfix/src/smtp/smtp_sasl_proto.c
index 87b049129..7b8ed12f0 100644
--- a/postfix/src/smtp/smtp_sasl_proto.c
+++ b/postfix/src/smtp/smtp_sasl_proto.c
@@ -115,18 +115,25 @@ static const char *smtp_sasl_compat_mechs(const char *words)
void smtp_sasl_helo_auth(SMTP_SESSION *session, const char *words)
{
const char *mech_list = smtp_sasl_compat_mechs(words);
+ char *junk;
/*
* XXX If the server offers no compatible authentication mechanisms, then
* pretend that the server doesn't support SASL authentication.
+ *
+ * XXX If the server offers multiple different lists, concatenate them. Let
+ * the SASL library worry about duplicates.
*/
if (session->sasl_mechanism_list) {
- if (strcasecmp(session->sasl_mechanism_list, mech_list) == 0)
- return;
- myfree(session->sasl_mechanism_list);
- msg_warn("%s offered AUTH option multiple times", session->namaddr);
- session->sasl_mechanism_list = 0;
- session->features &= ~SMTP_FEATURE_AUTH;
+ if (strcasecmp(session->sasl_mechanism_list, mech_list) != 0
+ && strlen(mech_list) > 0
+ && strlen(session->sasl_mechanism_list) < var_line_limit) {
+ junk = concatenate(session->sasl_mechanism_list, " ", mech_list,
+ (char *) 0);
+ myfree(session->sasl_mechanism_list);
+ session->sasl_mechanism_list = junk;
+ }
+ return;
}
if (strlen(mech_list) > 0) {
session->sasl_mechanism_list = mystrdup(mech_list);
diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c
index a95685d2a..0d9b1c6fa 100644
--- a/postfix/src/smtpd/smtpd_check.c
+++ b/postfix/src/smtpd/smtpd_check.c
@@ -1515,25 +1515,15 @@ static int permit_mx_primary(SMTPD_STATE *state, DNS_RR *mx_list,
{
const char *myname = "permit_mx_primary";
DNS_RR *mx;
- unsigned int best_pref;
if (msg_verbose)
msg_info("%s", myname);
- /*
- * Find the preference of the primary MX hosts.
- */
- for (best_pref = 0xffff, mx = mx_list; mx != 0; mx = mx->next)
- if (mx->pref < best_pref)
- best_pref = mx->pref;
-
/*
* See if each best MX host has all IP addresses in
* permit_mx_backup_networks.
*/
for (mx = mx_list; mx != 0; mx = mx->next) {
- if (mx->pref != best_pref)
- continue;
if (!all_auth_mx_addr(state, (char *) mx->data, reply_name, reply_class))
return (NOPE);
}
@@ -1553,8 +1543,9 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient,
char *myname = "permit_mx_backup";
const RESOLVE_REPLY *reply;
const char *domain;
-
DNS_RR *mx_list;
+ DNS_RR *middle;
+ DNS_RR *rest;
int dns_status;
if (msg_verbose)
@@ -1607,9 +1598,11 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient,
*/
dns_status = dns_lookup(domain, T_MX, 0, &mx_list,
(VSTRING *) 0, (VSTRING *) 0);
+#if 0
if (dns_status == DNS_NOTFOUND)
return (has_my_addr(state, domain, reply_name, reply_class) ?
SMTPD_CHECK_OK : SMTPD_CHECK_DUNNO);
+#endif
if (dns_status != DNS_OK) { /* incl. DNS_INVAL */
if (dns_status == DNS_RETRY)
DEFER_IF_REJECT2(state, MAIL_ERROR_POLICY,
@@ -1620,28 +1613,50 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient,
}
/*
- * First, see if we match any of the MX host names listed.
+ * Separate MX list into primaries and backups.
*/
- if (!i_am_mx(state, mx_list, reply_name, reply_class)) {
- dns_rr_free(mx_list);
- return (SMTPD_CHECK_DUNNO);
+ mx_list = dns_rr_sort(mx_list, dns_rr_compare_pref);
+ for (middle = mx_list; /* see below */ ; middle = rest) {
+ rest = middle->next;
+ if (rest == 0)
+ break;
+ if (rest->pref != mx_list->pref) {
+ middle->next = 0;
+ break;
+ }
}
+ /* postcondition: middle->next = 0, rest may be 0. */
+
+#define PERMIT_MX_BACKUP_RETURN(x) do { \
+ middle->next = rest; \
+ dns_rr_free(mx_list); \
+ return (x); \
+ } while (0)
+
+ /*
+ * First, see if we match any of the primary MX servers.
+ */
+ if (i_am_mx(state, mx_list, reply_name, reply_class))
+ PERMIT_MX_BACKUP_RETURN(SMTPD_CHECK_DUNNO);
+
+ /*
+ * Then, see if we match any of the backup MX servers.
+ */
+ if (rest && !i_am_mx(state, rest, reply_name, reply_class))
+ PERMIT_MX_BACKUP_RETURN(SMTPD_CHECK_DUNNO);
/*
* Optionally, see if the primary MX hosts are in a restricted list of
* networks.
*/
if (*var_perm_mx_networks
- && !permit_mx_primary(state, mx_list, reply_name, reply_class)) {
- dns_rr_free(mx_list);
- return (SMTPD_CHECK_DUNNO);
- }
+ && !permit_mx_primary(state, mx_list, reply_name, reply_class))
+ PERMIT_MX_BACKUP_RETURN(SMTPD_CHECK_DUNNO);
/*
* The destination passed all requirements.
*/
- dns_rr_free(mx_list);
- return (SMTPD_CHECK_OK);
+ PERMIT_MX_BACKUP_RETURN(SMTPD_CHECK_OK);
}
/* reject_non_fqdn_address - fail if address is not in fqdn form */