diff --git a/postfix/HISTORY b/postfix/HISTORY
index a2c77fa9f..6e43b4d9f 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -11050,6 +11050,18 @@ Apologies for any names omitted.
client IP address). Files: global/mail_params.h,
smtpd/smtpd_peer.c, smtpd/smtpd.c, smtpd/smtpd_check.c.
+20050726
+
+ Horror: total rewrite of DNS client error handling because
+ some misguided proposal attempts to give special meaning
+ to some syntactically invalid MX hostname lookup result.
+ Not only that, people expect sensible results with
+ reject_unknown_sender_domain etc. Files: dns/dns_lookup.c,
+ smtp/smtp_addr.c smtpd/smtpd_check.c, lmtp/lmtp_addr.c.
+
+ Cleanup: HOLD action executes only once, to reduce noise
+ in the logfile. Files: cleanup/cleanup_message.c, smtpd/smtpd.c.
+
Open problems:
Med: when the cleanup server bounces local mail that should
diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES
index f1ef928e0..0106ff4cb 100644
--- a/postfix/RELEASE_NOTES
+++ b/postfix/RELEASE_NOTES
@@ -17,7 +17,32 @@ 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 20050715
+Incompatibility with snapshot 20050726
+======================================
+
+Name server replies that contain a malformed hostname are now flagged
+as permanent errors instead of transient errors. This change works
+around a questionable proposal to use syntactically invalid hostnames
+in MX records.
+
+Major changes with snapshot 20050724
+====================================
+
+SMTPD Access control based on the existence of an address->name
+mapping, with reject_unknown_reverse_client_hostname. There is
+no corresponding access table lookup feature, because the name
+is not validated in any way (except that it has proper syntax).
+
+Several confusing SMTPD access restrictions were renamed:
+
+ reject_unknown_client -> reject_unknown_client_hostname,
+ reject_unknown_hostname -> reject_unknown_helo_hostname,
+ reject_invalid_hostname -> reject_invalid_helo_hostname,
+ reject_non_fqdn_hostname -> reject_non_fqdn_helo_hostname.
+
+The old names are still recognized and documented.
+
+Incompatibility with snapshot 20050716
======================================
Internal interfaces have changed; this may break third-party patches
@@ -40,7 +65,7 @@ report "(unsigned) int" versus "(s)size_t" format string argument
mis-matches on 32-bit systems; they can be found only on 64-bit
systems.
-Major changes with snapshot 20050715
+Major changes with snapshot 20050716
====================================
Improved portability to LP64 systems, by converting the type of
diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html
index 63556b069..3da5f89e0 100644
--- a/postfix/html/postconf.5.html
+++ b/postfix/html/postconf.5.html
@@ -8132,11 +8132,13 @@ code for rejected requests (default: 554).
reject_unknown_recipient_domain
-Reject the request when the RCPT TO address has no DNS A or MX
-record and Postfix is not final destination for the recipient
-address.
The unknown_address_reject_code parameter specifies
-the response code for rejected requests (default: 450). The response
-is always 450 in case of a temporary DNS error.
+Reject the request when Postfix is not final destination for
+the recipient address, and the RCPT TO address has no DNS A or MX
+record, or when it has a malformed MX record such as a record with
+a zero-length MX hostname (Postfix 2.3 and later).
The
+unknown_address_reject_code parameter specifies the response code
+for rejected requests (default: 450). The response is always 450
+in case of a temporary DNS error.
reject_unlisted_recipient (with Postfix 2.0: check_recipient_maps)
@@ -8602,11 +8604,13 @@ Postfix version 2.1 and later.
reject_unknown_sender_domain
-Reject the request when the MAIL FROM address has no DNS A or
-MX record and Postfix is not final destination for the sender
-address.
The unknown_address_reject_code parameter specifies
-the response code for rejected requests (default: 450). The response
-is always 450 in case of a temporary DNS error.
+Reject the request when Postfix is not final destination for
+the sender address, and the MAIL FROM address has no DNS A or MX
+record, or when it has a malformed MX record such as a record with
+a zero-length MX hostname (Postfix 2.3 and later).
The
+unknown_address_reject_code parameter specifies the response code
+for rejected requests (default: 450). The response is always 450
+in case of a temporary DNS error.
reject_unlisted_sender
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5
index 9963a365f..0ab835d27 100644
--- a/postfix/man/man5/postconf.5
+++ b/postfix/man/man5/postconf.5
@@ -4648,13 +4648,15 @@ no sender-specified routing (user@elsewhere@domain).
The relay_domains_reject_code parameter specifies the response
code for rejected requests (default: 554).
.IP "\fBreject_unknown_recipient_domain\fR"
-Reject the request when the RCPT TO address has no DNS A or MX
-record and Postfix is not final destination for the recipient
-address.
+Reject the request when Postfix is not final destination for
+the recipient address, and the RCPT TO address has no DNS A or MX
+record, or when it has a malformed MX record such as a record with
+a zero-length MX hostname (Postfix 2.3 and later).
.br
-The unknown_address_reject_code parameter specifies
-the response code for rejected requests (default: 450). The response
-is always 450 in case of a temporary DNS error.
+The
+unknown_address_reject_code parameter specifies the response code
+for rejected requests (default: 450). The response is always 450
+in case of a temporary DNS error.
.IP "\fBreject_unlisted_recipient\fR (with Postfix 2.0: check_recipient_maps)"
Reject the request when the RCPT TO address is not listed in
the list of valid recipients for its domain class. See the
@@ -4953,13 +4955,15 @@ Enforces the reject_sender_login_mismatch restriction for
unauthenticated clients only. This feature is available in
Postfix version 2.1 and later.
.IP "\fBreject_unknown_sender_domain\fR"
-Reject the request when the MAIL FROM address has no DNS A or
-MX record and Postfix is not final destination for the sender
-address.
+Reject the request when Postfix is not final destination for
+the sender address, and the MAIL FROM address has no DNS A or MX
+record, or when it has a malformed MX record such as a record with
+a zero-length MX hostname (Postfix 2.3 and later).
.br
-The unknown_address_reject_code parameter specifies
-the response code for rejected requests (default: 450). The response
-is always 450 in case of a temporary DNS error.
+The
+unknown_address_reject_code parameter specifies the response code
+for rejected requests (default: 450). The response is always 450
+in case of a temporary DNS error.
.IP "\fBreject_unlisted_sender\fR"
Reject the request when the MAIL FROM address is not listed in
the list of valid recipients for its domain class. See the
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index 09b0a5569..7875e54e9 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -5150,11 +5150,13 @@ code for rejected requests (default: 554).
reject_unknown_recipient_domain
-Reject the request when the RCPT TO address has no DNS A or MX
-record and Postfix is not final destination for the recipient
-address.
The unknown_address_reject_code parameter specifies
-the response code for rejected requests (default: 450). The response
-is always 450 in case of a temporary DNS error.
+Reject the request when Postfix is not final destination for
+the recipient address, and the RCPT TO address has no DNS A or MX
+record, or when it has a malformed MX record such as a record with
+a zero-length MX hostname (Postfix 2.3 and later).
The
+unknown_address_reject_code parameter specifies the response code
+for rejected requests (default: 450). The response is always 450
+in case of a temporary DNS error.
reject_unlisted_recipient (with Postfix 2.0: check_recipient_maps)
@@ -5488,11 +5490,13 @@ Postfix version 2.1 and later.
reject_unknown_sender_domain
-Reject the request when the MAIL FROM address has no DNS A or
-MX record and Postfix is not final destination for the sender
-address.
The unknown_address_reject_code parameter specifies
-the response code for rejected requests (default: 450). The response
-is always 450 in case of a temporary DNS error.
+Reject the request when Postfix is not final destination for
+the sender address, and the MAIL FROM address has no DNS A or MX
+record, or when it has a malformed MX record such as a record with
+a zero-length MX hostname (Postfix 2.3 and later).
The
+unknown_address_reject_code parameter specifies the response code
+for rejected requests (default: 450). The response is always 450
+in case of a temporary DNS error.
reject_unlisted_sender
diff --git a/postfix/src/cleanup/cleanup_message.c b/postfix/src/cleanup/cleanup_message.c
index 81ab2e145..95d54fc7e 100644
--- a/postfix/src/cleanup/cleanup_message.c
+++ b/postfix/src/cleanup/cleanup_message.c
@@ -316,8 +316,8 @@ static const char *cleanup_act(CLEANUP_STATE *state, char *context,
if (*optional_text) {
state->reason = dsn_prepend("5.7.1", optional_text);
if (*state->reason != '4' && *state->reason != '5') {
- msg_warn("bad DSN action in %s -- need 4.x.x or 5.x.x",
- optional_text);
+ msg_warn("bad DSN action in %s -- need 4.x.x or 5.x.x",
+ optional_text);
*state->reason = '4';
}
} else {
@@ -356,8 +356,10 @@ static const char *cleanup_act(CLEANUP_STATE *state, char *context,
return (buf);
}
if (STREQUAL(value, "HOLD", command_len)) {
- cleanup_act_log(state, "hold", context, buf, optional_text);
- state->flags |= CLEANUP_FLAG_HOLD;
+ if ((state->flags & CLEANUP_FLAG_HOLD) == 0) {
+ cleanup_act_log(state, "hold", context, buf, optional_text);
+ state->flags |= CLEANUP_FLAG_HOLD;
+ }
return (buf);
}
if (STREQUAL(value, "PREPEND", command_len)) {
diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h
index a54d9a808..ab32fe444 100644
--- a/postfix/src/dns/dns.h
+++ b/postfix/src/dns/dns.h
@@ -157,13 +157,15 @@ extern int dns_lookup_v(const char *, unsigned, DNS_RR **, VSTRING *,
/*
* Request flags.
*/
-#define DNS_REQ_FLAG_ANY (1<<0)
-#define DNS_REQ_FLAG_ALL (1<<1)
+#define DNS_REQ_FLAG_STOP_OK (1<<0)
+#define DNS_REQ_FLAG_STOP_INVAL (1<<1)
+#define DNS_REQ_FLAG_NONE (0)
/*
* Status codes. Failures must have negative codes so they will not collide
* with valid counts of answer records etc.
*/
+#define DNS_INVAL (-5) /* query ok, malformed reply */
#define DNS_FAIL (-4) /* query failed, don't retry */
#define DNS_NOTFOUND (-3) /* query ok, data not found */
#define DNS_RETRY (-2) /* query failed, try again */
diff --git a/postfix/src/dns/dns_lookup.c b/postfix/src/dns/dns_lookup.c
index 98676b55a..573994769 100644
--- a/postfix/src/dns/dns_lookup.c
+++ b/postfix/src/dns/dns_lookup.c
@@ -37,7 +37,8 @@
/* number of CNAME indirections. All result names (including
/* null terminator) will fit a buffer of size DNS_NAME_LEN.
/* All name results are validated by \fIvalid_hostname\fR();
-/* an invalid name is reported as a transient error.
+/* an invalid name is reported as a DNS_INVAL result, while
+/* malformed replies are reported as transient errors.
/*
/* dns_lookup_l() and dns_lookup_v() allow the user to specify
/* a list of resource types.
@@ -46,6 +47,8 @@
/* .fi
/* .IP name
/* The name to be looked up in the domain name system.
+/* This name must pass the valid_hostname() test; it
+/* must not be an IP address.
/* .IP type
/* The resource record type to be looked up (T_A, T_MX etc.).
/* .IP rflags
@@ -59,16 +62,18 @@
/* Append local domain to unqualified names.
/* .RE
/* .IP lflags
-/* Multi-type request control for dns_lookup_l() and
-/* dns_lookup_v(). This is one of the following:
+/* Multi-type request control for dns_lookup_l() and dns_lookup_v().
+/* For convenience, DNS_REQ_FLAG_NONE requests no special
+/* processing. Invoke dns_lookup() for all specified resource
+/* record types in the specified order, and merge their results.
+/* Otherwise, specify one or more of the following:
/* .RS
-/* .IP DNS_REQ_FLAG_ANY
-/* Call dns_lookup() for each specified resource record type
-/* in the specified order, until the list is exhausted or
-/* until some result is DNS_OK.
-/* .IP DNS_REQ_FLAG_ALL
-/* Call dns_lookup() for all specified resource record types
-/* in the specified order, and merge their results.
+/* .IP DNS_REQ_FLAG_STOP_INVAL
+/* Invoke dns_lookup() for the resource types in the order as
+/* specified, and return when dns_lookup() returns DNS_INVAL.
+/* .IP DNS_REQ_FLAG_STOP_OK
+/* Invoke dns_lookup() for the resource types in the order as
+/* specified, and return when dns_lookup() returns DNS_OK.
/* .RE
/* .IP ltype
/* The resource record types to be looked up. In the case of
@@ -93,8 +98,11 @@
/* The DNS query succeeded.
/* .IP DNS_NOTFOUND
/* The DNS query succeeded; the requested information was not found.
+/* .IP DNS_INVAL
+/* The DNS query succeeded; the result failed the valid_hostname() test.
/* .IP DNS_RETRY
-/* The query failed; the problem is transient.
+/* The query failed, or the reply was malformed.
+/* The problem is considered transient.
/* .IP DNS_FAIL
/* The query failed.
/* BUGS
@@ -142,8 +150,8 @@
/*
* Structure to keep track of things while decoding a name server reply.
*/
-#define DEF_DNS_REPLY_SIZE 4096 /* in case we're using TCP */
-#define MAX_DNS_REPLY_SIZE 32768 /* in case we're using TCP */
+#define DEF_DNS_REPLY_SIZE 4096 /* in case we're using TCP */
+#define MAX_DNS_REPLY_SIZE 32768 /* in case we're using TCP */
typedef struct DNS_REPLY {
unsigned char *buf; /* raw reply data */
@@ -341,8 +349,8 @@ static int valid_rr_name(const char *name, const char *location,
/* dns_get_rr - extract resource record from name server reply */
-static DNS_RR *dns_get_rr(DNS_REPLY *reply, unsigned char *pos,
- char *rr_name, DNS_FIXED *fixed)
+static int dns_get_rr(DNS_RR **list, DNS_REPLY *reply, unsigned char *pos,
+ char *rr_name, DNS_FIXED *fixed)
{
char temp[DNS_NAME_LEN];
ssize_t data_len;
@@ -353,8 +361,9 @@ static DNS_RR *dns_get_rr(DNS_REPLY *reply, unsigned char *pos,
#define MIN2(a, b) ((unsigned)(a) < (unsigned)(b) ? (a) : (b))
+ *list = 0;
if (pos + fixed->length > reply->end)
- return (0);
+ return (DNS_RETRY);
switch (fixed->type) {
default:
@@ -367,23 +376,23 @@ static DNS_RR *dns_get_rr(DNS_REPLY *reply, unsigned char *pos,
case T_NS:
case T_PTR:
if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0)
- return (0);
+ return (DNS_RETRY);
if (!valid_rr_name(temp, "resource data", fixed->type, reply))
- return (0);
+ return (DNS_INVAL);
data_len = strlen(temp) + 1;
break;
case T_MX:
GETSHORT(pref, pos);
if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0)
- return (0);
+ return (DNS_RETRY);
if (!valid_rr_name(temp, "resource data", fixed->type, reply))
- return (0);
+ return (DNS_INVAL);
data_len = strlen(temp) + 1;
break;
case T_A:
if (fixed->length != INET_ADDR_LEN) {
msg_warn("extract_answer: bad address length: %d", fixed->length);
- return (0);
+ return (DNS_RETRY);
}
if (fixed->length > sizeof(temp))
msg_panic("dns_get_rr: length %d > DNS_NAME_LEN",
@@ -395,7 +404,7 @@ static DNS_RR *dns_get_rr(DNS_REPLY *reply, unsigned char *pos,
case T_AAAA:
if (fixed->length != INET6_ADDR_LEN) {
msg_warn("extract_answer: bad address length: %d", fixed->length);
- return (0);
+ return (DNS_RETRY);
}
if (fixed->length > sizeof(temp))
msg_panic("dns_get_rr: length %d > DNS_NAME_LEN",
@@ -414,8 +423,9 @@ static DNS_RR *dns_get_rr(DNS_REPLY *reply, unsigned char *pos,
*dst = 0;
break;
}
- return (dns_rr_create(rr_name, fixed->type, fixed->class, fixed->ttl,
- pref, temp, data_len));
+ *list = dns_rr_create(rr_name, fixed->type, fixed->class, fixed->ttl,
+ pref, temp, data_len);
+ return (DNS_OK);
}
/* dns_get_alias - extract CNAME from name server reply */
@@ -428,7 +438,7 @@ static int dns_get_alias(DNS_REPLY *reply, unsigned char *pos,
if (dn_expand(reply->buf, reply->end, pos, cname, c_len) < 0)
return (DNS_RETRY);
if (!valid_rr_name(cname, "resource data", fixed->type, reply))
- return (DNS_RETRY);
+ return (DNS_INVAL);
return (DNS_OK);
}
@@ -445,14 +455,15 @@ static int dns_get_answer(DNS_REPLY *reply, int type,
DNS_RR *rr;
int resource_found = 0;
int cname_found = 0;
- int not_found_status = DNS_NOTFOUND;
+ int not_found_status = DNS_RETRY; /* can't happen */
+ int status;
/*
* Initialize. Skip over the name server query if we haven't yet.
*/
if (reply->answer_start == 0)
- if (dns_skip_query(reply) < 0)
- return (DNS_RETRY);
+ if ((status = dns_skip_query(reply)) < 0)
+ return (status);
pos = reply->answer_start;
if (rrlist)
*rrlist = 0;
@@ -461,12 +472,12 @@ static int dns_get_answer(DNS_REPLY *reply, int type,
* Either this, or use a GOTO for emergency exits. The purpose is to
* prevent incomplete answers from being passed back to the caller.
*/
-#define CORRUPT { \
+#define CORRUPT(status) { \
if (rrlist && *rrlist) { \
dns_rr_free(*rrlist); \
*rrlist = 0; \
} \
- return (DNS_RETRY); \
+ return (status); \
}
/*
@@ -478,21 +489,21 @@ static int dns_get_answer(DNS_REPLY *reply, int type,
* Optionally extract the fully-qualified domain name.
*/
if (pos >= reply->end)
- CORRUPT;
+ CORRUPT(DNS_RETRY);
len = dn_expand(reply->buf, reply->end, pos, rr_name, DNS_NAME_LEN);
if (len < 0)
- CORRUPT;
+ CORRUPT(DNS_RETRY);
pos += len;
/*
* Extract the fixed reply data: type, class, ttl, length.
*/
if (pos + RRFIXEDSZ > reply->end)
- CORRUPT;
- if (dns_get_fixed(pos, &fixed) != DNS_OK)
- CORRUPT;
+ CORRUPT(DNS_RETRY);
+ if ((status = dns_get_fixed(pos, &fixed)) != DNS_OK)
+ CORRUPT(status);
if (!valid_rr_name(rr_name, "resource name", fixed.type, reply))
- CORRUPT;
+ CORRUPT(DNS_INVAL);
if (fqdn)
vstring_strcpy(fqdn, rr_name);
if (msg_verbose)
@@ -504,21 +515,21 @@ static int dns_get_answer(DNS_REPLY *reply, int type,
* Optionally extract the requested resource or CNAME data.
*/
if (pos + fixed.length > reply->end)
- CORRUPT;
+ CORRUPT(DNS_RETRY);
if (type == fixed.type || type == T_ANY) { /* requested type */
if (rrlist) {
- if ((rr = dns_get_rr(reply, pos, rr_name, &fixed)) != 0) {
+ if ((status = dns_get_rr(&rr, reply, pos, rr_name, &fixed)) == DNS_OK) {
resource_found++;
*rrlist = dns_rr_append(*rrlist, rr);
- } else
- not_found_status = DNS_RETRY;
+ } else if (not_found_status != DNS_RETRY)
+ not_found_status = status;
} else
resource_found++;
} else if (fixed.type == T_CNAME) { /* cname resource */
cname_found++;
if (cname && c_len > 0)
- if (dns_get_alias(reply, pos, &fixed, cname, c_len) != DNS_OK)
- CORRUPT;
+ if ((status = dns_get_alias(reply, pos, &fixed, cname, c_len)) != DNS_OK)
+ CORRUPT(status);
}
pos += fixed.length;
}
@@ -591,10 +602,9 @@ int dns_lookup(const char *name, unsigned type, unsigned flags,
default:
if (why)
vstring_sprintf(why, "Name service error for name=%s type=%s: "
- "Malformed name server reply",
+ "Malformed or unexpected name server reply",
name, dns_strtype(type));
case DNS_OK:
- case DNS_NOTFOUND:
return (status);
case DNS_RECURSE:
if (msg_verbose)
@@ -633,7 +643,10 @@ int dns_lookup_l(const char *name, unsigned flags, DNS_RR **rrlist,
non_err = 1;
if (rrlist)
*rrlist = dns_rr_append(*rrlist, rr);
- if (lflags == DNS_REQ_FLAG_ANY)
+ if (lflags & DNS_REQ_FLAG_STOP_OK)
+ break;
+ } else if (status == DNS_INVAL) {
+ if (lflags & DNS_REQ_FLAG_STOP_INVAL)
break;
} else if (status == DNS_RETRY) {
soft_err = 1;
@@ -667,7 +680,10 @@ int dns_lookup_v(const char *name, unsigned flags, DNS_RR **rrlist,
non_err = 1;
if (rrlist)
*rrlist = dns_rr_append(*rrlist, rr);
- if (lflags == DNS_REQ_FLAG_ANY)
+ if (lflags & DNS_REQ_FLAG_STOP_OK)
+ break;
+ } else if (status == DNS_INVAL) {
+ if (lflags & DNS_REQ_FLAG_STOP_INVAL)
break;
} else if (status == DNS_RETRY) {
soft_err = 1;
diff --git a/postfix/src/dns/test_dns_lookup.c b/postfix/src/dns/test_dns_lookup.c
index a5388f009..e1350fd75 100644
--- a/postfix/src/dns/test_dns_lookup.c
+++ b/postfix/src/dns/test_dns_lookup.c
@@ -100,7 +100,7 @@ int main(int argc, char **argv)
name = argv[2];
msg_verbose = 1;
switch (dns_lookup_v(name, RES_DEFNAMES | RES_DEBUG, &rr, fqdn, why,
- DNS_REQ_FLAG_ALL, types)) {
+ DNS_REQ_FLAG_NONE, types)) {
default:
msg_fatal("%s", vstring_str(why));
case DNS_OK:
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 9e97d7f5f..fc0ae8993 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 "20050724"
+#define MAIL_RELEASE_DATE "20050726"
#define MAIL_VERSION_NUMBER "2.3"
#ifdef SNAPSHOT
diff --git a/postfix/src/lmtp/lmtp_addr.c b/postfix/src/lmtp/lmtp_addr.c
index 75736af47..e23849b28 100644
--- a/postfix/src/lmtp/lmtp_addr.c
+++ b/postfix/src/lmtp/lmtp_addr.c
@@ -191,7 +191,7 @@ static DNS_RR *lmtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
* Append the addresses for this host to the address list.
*/
switch (dns_lookup_v(host, RES_DEFNAMES, &addr, (VSTRING *) 0, why->reason,
- DNS_REQ_FLAG_ALL, proto_info->dns_atype_list)) {
+ DNS_REQ_FLAG_NONE, proto_info->dns_atype_list)) {
case DNS_OK:
for (rr = addr; rr; rr = rr->next)
rr->pref = pref;
@@ -207,6 +207,7 @@ static DNS_RR *lmtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
"5.4.3", 550, "550 Name server failure");
lmtp_errno = LMTP_FAIL;
break;
+ case DNS_INVAL:
case DNS_NOTFOUND:
lmtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
"5.4.4", 550, "550 Host not found");
diff --git a/postfix/src/smtp/smtp_addr.c b/postfix/src/smtp/smtp_addr.c
index 55fcfdc7c..855007efc 100644
--- a/postfix/src/smtp/smtp_addr.c
+++ b/postfix/src/smtp/smtp_addr.c
@@ -162,7 +162,7 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
*/
if (smtp_host_lookup_mask & SMTP_HOST_FLAG_DNS) {
switch (dns_lookup_v(host, RES_DEFNAMES, &addr, (VSTRING *) 0,
- why->reason, DNS_REQ_FLAG_ALL,
+ why->reason, DNS_REQ_FLAG_NONE,
proto_info->dns_atype_list)) {
case DNS_OK:
for (rr = addr; rr; rr = rr->next)
@@ -180,6 +180,7 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
if (smtp_errno != SMTP_ERR_RETRY)
smtp_errno = SMTP_ERR_FAIL;
return (addr_list);
+ case DNS_INVAL:
case DNS_NOTFOUND:
smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
"5.4.4", 550, "550 Host not found");
@@ -474,6 +475,11 @@ DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_BUF *why,
addr_list = dns_rr_sort(addr_list, smtp_compare_pref);
}
break;
+ case DNS_INVAL:
+ smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
+ "5.4.4", 550, "550 Host not found");
+ smtp_errno = SMTP_ERR_FAIL;
+ break;
case DNS_NOTFOUND:
addr_list = smtp_host_addr(name, misc_flags, why);
break;
diff --git a/postfix/src/smtp/smtp_unalias.c b/postfix/src/smtp/smtp_unalias.c
index 4e9a81ace..1c4d34d94 100644
--- a/postfix/src/smtp/smtp_unalias.c
+++ b/postfix/src/smtp/smtp_unalias.c
@@ -86,7 +86,7 @@ const char *smtp_unalias_name(const char *name)
if ((result = htable_find(cache, name)) == 0) {
fqdn = vstring_alloc(10);
if (dns_lookup_l(name, smtp_unalias_flags, (DNS_RR **) 0, fqdn,
- (VSTRING *) 0, DNS_REQ_FLAG_ANY, T_MX, T_A,
+ (VSTRING *) 0, DNS_REQ_FLAG_NONE, T_MX, T_A,
#ifdef HAS_IPV6
T_AAAA,
#endif
diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c
index 9a44c443f..00e0dd60a 100644
--- a/postfix/src/smtpd/smtpd_check.c
+++ b/postfix/src/smtpd/smtpd_check.c
@@ -966,7 +966,7 @@ static int reject_unknown_client(SMTPD_STATE *state)
if (state->name_status != SMTPD_PEER_CODE_OK)
return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
- state->name_status == SMTPD_PEER_CODE_PERM ?
+ state->name_status == SMTPD_PEER_CODE_PERM ?
var_unk_client_code : 450, "4.7.1",
"Client host rejected: cannot find your hostname, [%s]",
state->addr));
@@ -1155,18 +1155,20 @@ static int reject_unknown_hostname(SMTPD_STATE *state, char *name,
#endif
dns_status = dns_lookup_l(name, 0, (DNS_RR **) 0, (VSTRING *) 0,
- (VSTRING *) 0, DNS_REQ_FLAG_ANY,
+ (VSTRING *) 0, DNS_REQ_FLAG_STOP_OK,
RR_ADDR_TYPES, T_MX, 0);
- if (dns_status == DNS_NOTFOUND)
- return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
- var_unk_name_code, "4.7.1",
- "<%s>: %s rejected: Host not found",
- reply_name, reply_class));
- else if (dns_status != DNS_OK)
- DEFER_IF_PERMIT2(state, MAIL_ERROR_POLICY,
- 450, "4.7.1",
- "<%s>: %s rejected: Host not found",
- reply_name, reply_class);
+ if (dns_status != DNS_OK) { /* incl. DNS_INVAL */
+ if (dns_status != DNS_RETRY)
+ return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
+ var_unk_name_code, "4.7.1",
+ "<%s>: %s rejected: Host not found",
+ reply_name, reply_class));
+ else
+ DEFER_IF_PERMIT2(state, MAIL_ERROR_POLICY,
+ 450, "4.7.1",
+ "<%s>: %s rejected: Host not found",
+ reply_name, reply_class);
+ }
return (SMTPD_CHECK_DUNNO);
}
@@ -1181,22 +1183,26 @@ static int reject_unknown_mailhost(SMTPD_STATE *state, const char *name,
if (msg_verbose)
msg_info("%s: %s", myname, name);
+#define MAILHOST_LOOKUP_FLAGS (DNS_REQ_FLAG_STOP_OK | DNS_REQ_FLAG_STOP_INVAL)
+
dns_status = dns_lookup_l(name, 0, (DNS_RR **) 0, (VSTRING *) 0,
- (VSTRING *) 0, DNS_REQ_FLAG_ANY,
- RR_ADDR_TYPES, T_MX, 0);
- if (dns_status == DNS_NOTFOUND)
- return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
- var_unk_addr_code,
+ (VSTRING *) 0, MAILHOST_LOOKUP_FLAGS,
+ T_MX, RR_ADDR_TYPES, 0);
+ if (dns_status != DNS_OK) { /* incl. DNS_INVAL */
+ if (dns_status != DNS_RETRY)
+ return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
+ var_unk_addr_code,
strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
- "4.1.8" : "4.1.2",
- "<%s>: %s rejected: Domain not found",
- reply_name, reply_class));
- else if (dns_status != DNS_OK)
- DEFER_IF_PERMIT2(state, MAIL_ERROR_POLICY,
- 450, strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
- "4.1.8" : "4.1.2",
- "<%s>: %s rejected: Domain not found",
- reply_name, reply_class);
+ "4.1.8" : "4.1.2",
+ "<%s>: %s rejected: Domain not found",
+ reply_name, reply_class));
+ else
+ DEFER_IF_PERMIT2(state, MAIL_ERROR_POLICY,
+ 450, strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
+ "4.1.8" : "4.1.2",
+ "<%s>: %s rejected: Domain not found",
+ reply_name, reply_class);
+ }
return (SMTPD_CHECK_DUNNO);
}
@@ -1395,8 +1401,8 @@ static int all_auth_mx_addr(SMTPD_STATE *state, char *host,
* Verify that all host addresses are within permit_mx_backup_networks.
*/
dns_status = dns_lookup_v(host, 0, &addr_list, (VSTRING *) 0, (VSTRING *) 0,
- DNS_REQ_FLAG_ALL, inet_proto_info()->dns_atype_list);
- if (dns_status != DNS_OK) {
+ DNS_REQ_FLAG_NONE, inet_proto_info()->dns_atype_list);
+ if (dns_status != DNS_OK) { /* incl. DNS_INVAL */
DEFER_IF_REJECT3(state, MAIL_ERROR_POLICY,
450, "4.4.4",
"<%s>: %s rejected: Unable to look up host %s as mail exchanger",
@@ -1622,11 +1628,12 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient,
if (dns_status == DNS_NOTFOUND)
return (has_my_addr(state, domain, reply_name, reply_class) ?
SMTPD_CHECK_OK : SMTPD_CHECK_DUNNO);
- if (dns_status != DNS_OK) {
- DEFER_IF_REJECT2(state, MAIL_ERROR_POLICY,
- 450, "4.4.4",
- "<%s>: %s rejected: Unable to look up mail exchanger information",
- reply_name, reply_class);
+ if (dns_status != DNS_OK) { /* incl. DNS_INVAL */
+ if (dns_status == DNS_RETRY)
+ DEFER_IF_REJECT2(state, MAIL_ERROR_POLICY,
+ 450, "4.4.4",
+ "<%s>: %s rejected: Unable to look up mail exchanger information",
+ reply_name, reply_class);
return (SMTPD_CHECK_DUNNO);
}
@@ -1961,7 +1968,8 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
*/
if (STREQUAL(value, "HOLD", cmd_len)) {
#ifndef TEST
- if (can_delegate_action(state, table, "HOLD", reply_class) == 0)
+ if (can_delegate_action(state, table, "HOLD", reply_class) == 0
+ || (state->saved_flags & CLEANUP_FLAG_HOLD))
return (SMTPD_CHECK_DUNNO);
#endif
vstring_sprintf(error_text, "<%s>: %s %s", reply_name, reply_class,
@@ -3502,7 +3510,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
forbid_whitelist(state, name, status, state->helo_name);
}
} else if (strcasecmp(name, REJECT_NON_FQDN_HELO_HOSTNAME) == 0
- ||strcasecmp(name, REJECT_NON_FQDN_HOSTNAME) == 0) {
+ || strcasecmp(name, REJECT_NON_FQDN_HOSTNAME) == 0) {
if (state->helo_name) {
if (*state->helo_name != '[')
status = reject_non_fqdn_hostname(state, state->helo_name,