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,