diff --git a/postfix/HISTORY b/postfix/HISTORY
index 5fec159d4..5505fdc0b 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -27756,10 +27756,11 @@ Apologies for any names omitted.
or access control limitations. Files: smtpd/smtpd.[hc],
util/argv.[hc].
- Workaround: some OS lies under load: it says that a socket
- is readable, then it says that the socket has unread data,
- and then it says that read returns EOF, causing Postfix to
- spam the log with a warning message. File: tlsmgr/tlsmgr.c.
+ Workaround: tlsmgr logfile spam. Some OS lies under load:
+ it says that a socket is readable, then it says that the
+ socket has unread data, and then it says that read returns
+ EOF, causing Postfix to spam the log with a warning message.
+ File: tlsmgr/tlsmgr.c.
20240125
@@ -27829,17 +27830,6 @@ Apologies for any names omitted.
20240209
- Safety: enforce a sane but generous upper bound (100) for
- the number of DNS resource records that the Postfix DNS
- client library will admit into a list. This is 20x the
- default smtp_mx_address_limit value for the number of DNS
- records that the Postfix SMTP client is willing to consider.
- Log a warning "dropping records after qname=X qtype=Y" when
- a list cannot accept more elements. This prevents a tail
- recursion crash that degrades mail delivery performance.
- Problem report by Toshifumi Sakaguchi. Files: dns/dns_rr.[hc],
- global/mail_params.h.
-
Performance: eliminate worst-case behavior where the queue
manager deferred delivery to all destinations over a specific
delivery transport, after only a single delivery agent
@@ -27920,3 +27910,27 @@ Apologies for any names omitted.
status. Files: postconf/postconf.c, postconf/postconf_dbms.c,
postconf/postconf.h, conf/postfix-script, conf/post-install,
postfix-install.
+
+20240221
+
+ Documentation: the text for TLS loglevel 2 was incomplete.
+ File: proto/postconf.proto.
+
+20240226
+
+ Safety: drop and log over-size DNS responses resulting in
+ more than 100 records. This 20x larger than the number of
+ server addresses that the Postfix SMTP client is willing
+ to consider when delivering mail, and is well below the
+ number of records that could cause a tail recursion crash
+ in dns_rr_append() as reported by Toshifumi Sakaguchi. This
+ also limits the number of DNS requests from check_*_*_access
+ restrictions. Files: dns/dns.h, dns/dns_lookup.c, dns/dns_rr.c,
+ dns/test_dns_lookup.c, posttls-finger/posttls-finger.c,
+ smtp/smtp_addr.c, smtpd/smtpd_check.c.
+
+20240227
+
+ Documentation: document the need to disable regular expression
+ special characters when using $name inside an inlined
+ pattern. Files: proto/pcre_table, proto/regexp_table.
diff --git a/postfix/html/pcre_table.5.html b/postfix/html/pcre_table.5.html
index 0216e3e9b..1e036447f 100644
--- a/postfix/html/pcre_table.5.html
+++ b/postfix/html/pcre_table.5.html
@@ -191,8 +191,12 @@ PCRE_TABLE(5) PCRE_TABLE(5)
Postfix parses the result as if it is a file in /etc/postfix.
- Note: if a rule contains $, specify $$ to keep Postfix from trying to
- do $name expansion as it evaluates a parameter value.
+ Note: if an inlined rule contains $, specify $$ to keep Postfix from
+ trying to do $name expansion as it evaluates a parameter value.
+
+ Note: when using $name inside an inlined pattern, use \Q$name\E to dis-
+ able metacharacters such as '.' in the $name expansion. Otherwise, the
+ pattern may have unexpected matches.
EXAMPLE SMTPD ACCESS MAP
# Protect your outgoing majordomo exploders
diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html
index 82e863647..0ac9b68c7 100644
--- a/postfix/html/postconf.5.html
+++ b/postfix/html/postconf.5.html
@@ -13597,7 +13597,9 @@ verification errors if server certificate verification is not required.
With Postfix 2.8 and earlier, log the summary message and unconditionally
log trust-chain verification errors.
-
2 Also log levels during TLS negotiation.
+ 2 Also enable verbose logging in the Postfix TLS
+library, log session cache operations, and enable OpenSSL logging
+of the progress of the SSL handshake.
3 Also log the hexadecimal and ASCII dump of the
TLS negotiation process.
@@ -18851,7 +18853,9 @@ if client certificate verification is not required. With Postfix 2.8 and
earlier, log the summary message, peer certificate summary information
and unconditionally log trust-chain verification errors.
- 2 Also log levels during TLS negotiation.
+ 2 Also enable verbose logging in the Postfix TLS
+library, log session cache operations, and enable OpenSSL logging
+of the progress of the SSL handshake.
3 Also log hexadecimal and ASCII dump of TLS negotiation
process.
diff --git a/postfix/html/regexp_table.5.html b/postfix/html/regexp_table.5.html
index 05cda133d..96e4b382c 100644
--- a/postfix/html/regexp_table.5.html
+++ b/postfix/html/regexp_table.5.html
@@ -148,8 +148,12 @@ REGEXP_TABLE(5) REGEXP_TABLE(5)
Postfix parses the result as if it is a file in /etc/postfix.
- Note: if a rule contains $, specify $$ to keep Postfix from trying to
- do $name expansion as it evaluates a parameter value.
+ Note: if an inlined rule contains $, specify $$ to keep Postfix from
+ trying to do $name expansion as it evaluates a parameter value.
+
+ Note: when using $name inside an inlined pattern, this will not disable
+ metacharacters such as '.' in the $name expansion. To prevent unex-
+ pected matches, use a pcre: table, and specify \Q$name\E.
EXAMPLE SMTPD ACCESS MAP
# Disallow sender-specified routing. This is a must if you relay mail
diff --git a/postfix/man/man5/pcre_table.5 b/postfix/man/man5/pcre_table.5
index a9fd7b646..b5adb94f3 100644
--- a/postfix/man/man5/pcre_table.5
+++ b/postfix/man/man5/pcre_table.5
@@ -206,9 +206,14 @@ in\-memory file:
Postfix parses the result as if it is a file in /etc/postfix.
-Note: if a rule contains \fB$\fR, specify \fB$$\fR to keep
-Postfix from trying to do \fI$name\fR expansion as it
-evaluates a parameter value.
+Note: if an inlined rule contains \fB$\fR, specify \fB$$\fR
+to keep Postfix from trying to do \fI$name\fR expansion as
+it evaluates a parameter value.
+
+Note: when using \fI$name\fR inside an inlined pattern, use
+\eQ\fI$name\fR\eE to disable metacharacters such as '.' in
+the \fI$name\fR expansion. Otherwise, the pattern may have
+unexpected matches.
.SH "EXAMPLE SMTPD ACCESS MAP"
.na
.nf
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5
index 7cf173fb5..cf96e48cb 100644
--- a/postfix/man/man5/postconf.5
+++ b/postfix/man/man5/postconf.5
@@ -9025,7 +9025,9 @@ With Postfix 2.8 and earlier, log the summary message and unconditionally
log trust\-chain verification errors.
.br
.IP ""
-2 Also log levels during TLS negotiation.
+2 Also enable verbose logging in the Postfix TLS
+library, log session cache operations, and enable OpenSSL logging
+of the progress of the SSL handshake.
.br
.IP ""
3 Also log the hexadecimal and ASCII dump of the
@@ -13412,7 +13414,9 @@ earlier, log the summary message, peer certificate summary information
and unconditionally log trust\-chain verification errors.
.br
.IP ""
-2 Also log levels during TLS negotiation.
+2 Also enable verbose logging in the Postfix TLS
+library, log session cache operations, and enable OpenSSL logging
+of the progress of the SSL handshake.
.br
.IP ""
3 Also log hexadecimal and ASCII dump of TLS negotiation
diff --git a/postfix/man/man5/regexp_table.5 b/postfix/man/man5/regexp_table.5
index 9eeefe49b..e9698210b 100644
--- a/postfix/man/man5/regexp_table.5
+++ b/postfix/man/man5/regexp_table.5
@@ -163,9 +163,14 @@ in\-memory file:
Postfix parses the result as if it is a file in /etc/postfix.
-Note: if a rule contains \fB$\fR, specify \fB$$\fR to keep
-Postfix from trying to do \fI$name\fR expansion as it
-evaluates a parameter value.
+Note: if an inlined rule contains \fB$\fR, specify \fB$$\fR
+to keep Postfix from trying to do \fI$name\fR expansion as
+it evaluates a parameter value.
+
+Note: when using \fI$name\fR inside an inlined pattern,
+this will not disable metacharacters such as '.' in the
+\fI$name\fR expansion. To prevent unexpected matches, use
+a pcre: table, and specify \eQ\fI$name\fR\eE.
.SH "EXAMPLE SMTPD ACCESS MAP"
.na
.nf
diff --git a/postfix/proto/pcre_table b/postfix/proto/pcre_table
index 0f58c2b96..e4c6607f5 100644
--- a/postfix/proto/pcre_table
+++ b/postfix/proto/pcre_table
@@ -190,9 +190,14 @@
#
# Postfix parses the result as if it is a file in /etc/postfix.
#
-# Note: if a rule contains \fB$\fR, specify \fB$$\fR to keep
-# Postfix from trying to do \fI$name\fR expansion as it
-# evaluates a parameter value.
+# Note: if an inlined rule contains \fB$\fR, specify \fB$$\fR
+# to keep Postfix from trying to do \fI$name\fR expansion as
+# it evaluates a parameter value.
+#
+# Note: when using \fI$name\fR inside an inlined pattern, use
+# \eQ\fI$name\fR\eE to disable metacharacters such as '.' in
+# the \fI$name\fR expansion. Otherwise, the pattern may have
+# unexpected matches.
# EXAMPLE SMTPD ACCESS MAP
# # Protect your outgoing majordomo exploders
# /^(?!owner-)(.*)-outgoing@(.*)/ 550 Use ${1}@${2} instead
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index ef3fb5536..d13719b4b 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -9798,7 +9798,9 @@ if client certificate verification is not required. With Postfix 2.8 and
earlier, log the summary message, peer certificate summary information
and unconditionally log trust-chain verification errors.
- 2 Also log levels during TLS negotiation.
+ 2 Also enable verbose logging in the Postfix TLS
+library, log session cache operations, and enable OpenSSL logging
+of the progress of the SSL handshake.
3 Also log hexadecimal and ASCII dump of TLS negotiation
process.
@@ -10285,7 +10287,9 @@ verification errors if server certificate verification is not required.
With Postfix 2.8 and earlier, log the summary message and unconditionally
log trust-chain verification errors.
- 2 Also log levels during TLS negotiation.
+ 2 Also enable verbose logging in the Postfix TLS
+library, log session cache operations, and enable OpenSSL logging
+of the progress of the SSL handshake.
3 Also log the hexadecimal and ASCII dump of the
TLS negotiation process.
diff --git a/postfix/proto/regexp_table b/postfix/proto/regexp_table
index 1c384721b..5e8c15f85 100644
--- a/postfix/proto/regexp_table
+++ b/postfix/proto/regexp_table
@@ -147,9 +147,14 @@
#
# Postfix parses the result as if it is a file in /etc/postfix.
#
-# Note: if a rule contains \fB$\fR, specify \fB$$\fR to keep
-# Postfix from trying to do \fI$name\fR expansion as it
-# evaluates a parameter value.
+# Note: if an inlined rule contains \fB$\fR, specify \fB$$\fR
+# to keep Postfix from trying to do \fI$name\fR expansion as
+# it evaluates a parameter value.
+#
+# Note: when using \fI$name\fR inside an inlined pattern,
+# this will not disable metacharacters such as '.' in the
+# \fI$name\fR expansion. To prevent unexpected matches, use
+# a pcre: table, and specify \eQ\fI$name\fR\eE.
# EXAMPLE SMTPD ACCESS MAP
# # Disallow sender-specified routing. This is a must if you relay mail
# # for other domains.
diff --git a/postfix/proto/stop b/postfix/proto/stop
index e3e8ac6c7..1ef4ab4bf 100644
--- a/postfix/proto/stop
+++ b/postfix/proto/stop
@@ -1602,3 +1602,4 @@ GmbH
Hamid
LLC
Maadani
+GTEST
diff --git a/postfix/proto/stop.double-history b/postfix/proto/stop.double-history
index 95abfddde..5583f891d 100644
--- a/postfix/proto/stop.double-history
+++ b/postfix/proto/stop.double-history
@@ -116,3 +116,5 @@ proto proto aliases proto virtual proto ADDRESS_REWRITING_README html
status Files postconf postconf c postconf postconf_dbms c
postconf postconf h conf postfix script conf post install
postconf postconf c postconf postconf_dbms c
+ File tlsmgr tlsmgr c
+ restrictions Files dns dns h dns dns_lookup c dns dns_rr c
diff --git a/postfix/proto/stop.double-proto-html b/postfix/proto/stop.double-proto-html
index d79c684ee..a4b2332a2 100644
--- a/postfix/proto/stop.double-proto-html
+++ b/postfix/proto/stop.double-proto-html
@@ -357,3 +357,4 @@ the form of a domain name hostname hostname service hostname service
expected to become a list of comma separated names br br This
Postfix Postfix can use MongoDB as a source for any of its lookups aliases 5 virtual 5 canonical 5 etc This allows you to keep information for your mail service in a replicated noSQL database with fine grained access controls By not storing it
CCARGS CCARGS DHAS_MONGODB I usr include libmongoc 1 0
+ dt dt dd 2 Also enable verbose logging in the Postfix TLS
diff --git a/postfix/src/dns/Makefile.in b/postfix/src/dns/Makefile.in
index 769354508..5ea09cf5a 100644
--- a/postfix/src/dns/Makefile.in
+++ b/postfix/src/dns/Makefile.in
@@ -11,7 +11,8 @@ DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
INCL =
LIB = lib$(LIB_PREFIX)dns$(LIB_SUFFIX)
-TESTPROG= test_dns_lookup dns_rr_to_pa dns_rr_to_sa dns_sa_to_rr dns_rr_eq_sa
+TESTPROG= test_dns_lookup dns_rr_to_pa dns_rr_to_sa dns_sa_to_rr dns_rr_eq_sa \
+ dns_rr_test
LIBS = ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \
../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
LIB_DIR = ../../lib
@@ -31,7 +32,7 @@ test: $(TESTPROG)
tests: test dns_rr_to_pa_test dns_rr_to_sa_test dns_sa_to_rr_test \
dns_rr_eq_sa_test no-a-test no-aaaa-test no-mx-test \
error-filter-test nullmx_test nxdomain_test mxonly_test \
- dnsbl_tests
+ dnsbl_tests dns_rr_tests
dnsbl_tests: \
dnsbl_ttl_127.0.0.2_bind_plain_test \
@@ -57,7 +58,7 @@ DNSBL_EXIST_REPLY_FIX = \
-e 's/ [0-9]* [0-9]* [0-9]* [0-9]* [0-9]*/ D D D D D/' \
-e 's/127.0.0.[0-9]*$$/127.0.0.D/' \
| uniq
-
+
root_tests:
$(LIB): $(OBJS)
@@ -240,6 +241,12 @@ dnsbl_ttl_127.0.0.2_priv_ncache_test: test_dns_lookup dnsbl_ttl_127.0.0.2_bind_p
diff dnsbl_ttl_127.0.0.2_bind_plain.ref dnsbl_ttl_127.0.0.2_priv_ncache.tmp
rm -f dnsbl_ttl_127.0.0.2_priv_ncache.tmp
+dns_rr_tests: dns_rr_test
+ $(SHLIB_ENV) $(VALGRIND) ./dns_rr_test
+
+dns_rr_test: dns_rr_test.o $(LIB) $(LIBS)
+ $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
@@ -286,7 +293,6 @@ dns_lookup.o: ../../include/vstring.h
dns_lookup.o: dns.h
dns_lookup.o: dns_lookup.c
dns_rr.o: ../../include/check_arg.h
-dns_rr.o: ../../include/mail_params.h
dns_rr.o: ../../include/msg.h
dns_rr.o: ../../include/myaddrinfo.h
dns_rr.o: ../../include/mymalloc.h
@@ -320,6 +326,19 @@ dns_rr_filter.o: ../../include/vstream.h
dns_rr_filter.o: ../../include/vstring.h
dns_rr_filter.o: dns.h
dns_rr_filter.o: dns_rr_filter.c
+dns_rr_test.o: ../../include/check_arg.h
+dns_rr_test.o: ../../include/msg.h
+dns_rr_test.o: ../../include/msg_vstream.h
+dns_rr_test.o: ../../include/myaddrinfo.h
+dns_rr_test.o: ../../include/mymalloc.h
+dns_rr_test.o: ../../include/sock_addr.h
+dns_rr_test.o: ../../include/stringops.h
+dns_rr_test.o: ../../include/sys_defs.h
+dns_rr_test.o: ../../include/vbuf.h
+dns_rr_test.o: ../../include/vstream.h
+dns_rr_test.o: ../../include/vstring.h
+dns_rr_test.o: dns.h
+dns_rr_test.o: dns_rr_test.c
dns_rr_to_pa.o: ../../include/check_arg.h
dns_rr_to_pa.o: ../../include/msg.h
dns_rr_to_pa.o: ../../include/myaddrinfo.h
diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h
index 2b77015cb..5f2a050ea 100644
--- a/postfix/src/dns/dns.h
+++ b/postfix/src/dns/dns.h
@@ -164,9 +164,14 @@ typedef struct DNS_RR {
struct DNS_RR *next; /* linkage */
size_t data_len; /* actual data size */
char *data; /* a bunch of data */
+ int flags; /* DNS_RR_FLAG_XX, see below */
/* Add new fields at the end, for ABI forward compatibility. */
} DNS_RR;
+#define DNS_RR_FLAG_TRUNCATED (1<<0)
+
+#define DNS_RR_IS_TRUNCATED(rr) ((rr)->flags & DNS_RR_FLAG_TRUNCATED)
+
/*
* dns_strerror.c
*/
@@ -215,6 +220,7 @@ extern int dns_rr_compare_pref_any(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 *);
+extern int var_dns_rr_list_limit;
/*
* dns_rr_to_pa.c
diff --git a/postfix/src/dns/dns_lookup.c b/postfix/src/dns/dns_lookup.c
index bccdd83f5..08bd0319d 100644
--- a/postfix/src/dns/dns_lookup.c
+++ b/postfix/src/dns/dns_lookup.c
@@ -984,6 +984,8 @@ static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type,
resource_found++;
rr->dnssec_valid = *maybe_secure ? reply->dnssec_ad : 0;
*rrlist = dns_rr_append(*rrlist, rr);
+ if (DNS_RR_IS_TRUNCATED(*rrlist))
+ break;
} else if (status == DNS_NULLMX || status == DNS_NULLSRV) {
CORRUPT(status); /* TODO: use better name */
} else if (not_found_status != DNS_RETRY)
@@ -1214,8 +1216,11 @@ int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist,
name, dns_strtype(type), dns_str_resflags(flags));
status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
fqdn, why, rcode, lflags);
- if (rrlist && rr)
+ if (rrlist && rr) {
*rrlist = dns_rr_append(*rrlist, rr);
+ if (DNS_RR_IS_TRUNCATED(*rrlist))
+ break;
+ }
if (status == DNS_OK) {
if (lflags & DNS_REQ_FLAG_STOP_OK)
break;
@@ -1266,8 +1271,11 @@ int dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist,
name, dns_strtype(type), dns_str_resflags(flags));
status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0,
fqdn, why, rcode, lflags);
- if (rrlist && rr)
+ if (rrlist && rr) {
*rrlist = dns_rr_append(*rrlist, rr);
+ if (DNS_RR_IS_TRUNCATED(*rrlist))
+ break;
+ }
if (status == DNS_OK) {
if (lflags & DNS_REQ_FLAG_STOP_OK)
break;
diff --git a/postfix/src/dns/dns_rr.c b/postfix/src/dns/dns_rr.c
index deba784b7..882a42ffb 100644
--- a/postfix/src/dns/dns_rr.c
+++ b/postfix/src/dns/dns_rr.c
@@ -97,12 +97,17 @@
/*
/* dns_rr_copy() makes a copy of a resource record.
/*
-/* dns_rr_append() appends a resource record to a (list of) resource
-/* record(s).
-/* A null input list is explicitly allowed.
-/* This function will log a warning and will discard the
-/* resource record, when a list already contains var_dns_rr_list_limit
-/* elements (default: 100).
+/* dns_rr_append() appends an input resource record list to
+/* an output list. Null arguments are explicitly allowed.
+/* When the result would be longer than var_dns_rr_list_limit
+/* (default: 100), dns_rr_append() logs a warning, flags the
+/* output list as truncated, and discards the excess elements.
+/* Once an output list is flagged as truncated (test with
+/* DNS_RR_IS_TRUNCATED()), the caller is expected to stop
+/* trying to append records to that list. Note: the 'truncated'
+/* flag is transitive, i.e. when appending a input list that
+/* was flagged as truncated to an output list, the output list
+/* will also be flagged as truncated.
/*
/* dns_rr_sort() sorts a list of resource records into ascending
/* order according to a user-specified criterion. The result is the
@@ -151,18 +156,19 @@
#include
#include
-/* Global library. */
-
-#include
-
/* DNS library. */
#include "dns.h"
/*
- * Global, to make code testable.
+ * A generous safety limit for the number of DNS resource records that the
+ * Postfix DNS client library will admit into a list. The default value 100
+ * is 20x the default limit on the number address records that the Postfix
+ * SMTP client is willing to consider.
+ *
+ * Mutable, to make code testable.
*/
-int var_dns_rr_list_limit = DEF_DNS_RR_LIST_LIMIT;
+int var_dns_rr_list_limit = 100;
/* dns_rr_create - fill in resource record structure */
@@ -195,6 +201,7 @@ DNS_RR *dns_rr_create(const char *qname, const char *rname,
}
rr->data_len = data_len;
rr->next = 0;
+ rr->flags = 0;
return (rr);
}
@@ -234,36 +241,58 @@ DNS_RR *dns_rr_copy(DNS_RR *src)
/* dns_rr_append_with_limit - append resource record to limited list */
-static DNS_RR *dns_rr_append_with_limit(DNS_RR *list, DNS_RR *rr, int limit)
+static void dns_rr_append_with_limit(DNS_RR *list, DNS_RR *rr, int limit)
{
/*
- * To avoid log spam, remember a limited amount of information about a
- * past warning. When anomalies happen frequently, then it is OK that
- * some anomaly will not be logged, as long as the limit is enforced.
+ * Pre: list != 0, all lists are concatenated with dns_rr_append().
+ *
+ * Post: all elements have the DNS_RR_FLAG_TRUNCATED flag value set, or all
+ * elements have it cleared, so that there is no need to update code in
+ * legacy stable releases that deletes or reorders elements.
*/
- if (list == 0) {
- list = rr;
- } else if (limit > 1) {
- list->next = dns_rr_append_with_limit(list->next, rr, limit - 1);
- } else {
- static DNS_RR *logged_node;
-
- if (logged_node != list) {
- logged_node = list;
- msg_warn("dns_rr_append: dropping records after qname=%s qtype=%s",
- list->qname, dns_strtype(list->type));
+ if (limit <= 1) {
+ if (list->next || rr) {
+ msg_warn("DNS record count limit (%d) exceeded -- dropping"
+ " excess record(s) after qname=%s qtype=%s",
+ var_dns_rr_list_limit, list->qname,
+ dns_strtype(list->type));
+ list->flags |= DNS_RR_FLAG_TRUNCATED;
+ dns_rr_free(list->next);
+ dns_rr_free(rr);
+ list->next = 0;
+ }
+ } else {
+ if (list->next == 0 && rr) {
+ list->next = rr;
+ rr = 0;
+ }
+ if (list->next) {
+ dns_rr_append_with_limit(list->next, rr, limit - 1);
+ list->flags |= list->next->flags;
}
- dns_rr_free(rr);
}
- return (list);
}
-/* dns_rr_append - append resource record to list */
+/* dns_rr_append - append resource record(s) to list, or discard */
DNS_RR *dns_rr_append(DNS_RR *list, DNS_RR *rr)
{
- return (dns_rr_append_with_limit(list, rr, var_dns_rr_list_limit));
+
+ /*
+ * Note: rr is not length checked; when multiple lists are concatenated,
+ * the output length may be a small multiple of var_dns_rr_list_limit.
+ */
+ if (rr == 0)
+ return (list);
+ if (list == 0)
+ return (rr);
+ if (!DNS_RR_IS_TRUNCATED(list)) {
+ dns_rr_append_with_limit(list, rr, var_dns_rr_list_limit);
+ } else {
+ dns_rr_free(rr);
+ }
+ return (list);
}
/* dns_rr_compare_pref_ipv6 - compare records by preference, ipv6 preferred */
diff --git a/postfix/src/dns/dns_rr_test.c b/postfix/src/dns/dns_rr_test.c
new file mode 100644
index 000000000..562a3e5f0
--- /dev/null
+++ b/postfix/src/dns/dns_rr_test.c
@@ -0,0 +1,433 @@
+ /*
+ * System library.
+ */
+#include
+#include
+
+ /*
+ * Utility library.
+ */
+#include
+#include
+#include
+#include
+#include
+
+ /*
+ * DNS library.
+ */
+#include
+
+#define STR(x) vstring_str(x)
+
+ /*
+ * Test helpers. TODO: move eq_dns_rr() to testing/dns_rr_testers.c; need to
+ * verify that the expected difference is reported, or use a GTEST matcher.
+ */
+
+/* print_dns_rr - format as { qname, reply, flags } */
+
+static char *print_dns_rr(VSTRING *buf, DNS_RR *rr)
+{
+ static VSTRING *tmp;
+
+ if (tmp == 0)
+ tmp = vstring_alloc(100);
+ vstring_sprintf(buf, "{qname=%s, reply='%s', flags=0x%x}",
+ rr->qname, dns_strrecord(tmp, rr), rr->flags);
+ return (STR(buf));
+}
+
+/* eq_dns_rr - predicate that two lists are equivalent */
+
+static int eq_dns_rr(DNS_RR *got, DNS_RR *want)
+{
+ VSTRING *got_buf = 0;
+ VSTRING *want_buf = 0;
+
+#define EQ_DNS_RR_RETURN(val) do { \
+ if (got_buf) \
+ vstring_free(got_buf); \
+ if (want_buf) \
+ vstring_free(want_buf); \
+ return (val); \
+ } while (0)
+
+ /* Same length. */
+ if (got == 0 && want == 0)
+ EQ_DNS_RR_RETURN(1);
+ if (want == 0) {
+ msg_warn("got %s, want null",
+ print_dns_rr(got_buf = vstring_alloc(100), got));
+ }
+ if (got == 0) {
+ msg_warn("got null, want %s",
+ print_dns_rr(want_buf = vstring_alloc(100), want));
+ EQ_DNS_RR_RETURN(0);
+ }
+ /* Same query name, resource record, flags. */
+ if (strcmp(print_dns_rr(got_buf = vstring_alloc(100), got),
+ print_dns_rr(want_buf = vstring_alloc(100), want)) != 0) {
+ msg_warn("got %s, want %s", STR(want_buf), STR(got_buf));
+ EQ_DNS_RR_RETURN(0);
+ }
+ /* Same children. */
+ EQ_DNS_RR_RETURN(eq_dns_rr(got->next, want->next));
+}
+
+static int eq_dns_rr_free(DNS_RR *got, DNS_RR *want)
+{
+ int res = eq_dns_rr(got, want);
+
+ dns_rr_free(got);
+ dns_rr_free(want);
+ return (res);
+}
+
+ /*
+ * Tests and test cases.
+ */
+typedef struct TEST_CASE {
+ const char *label; /* identifies test case */
+ int (*fn) (void);
+} TEST_CASE;
+
+#define PASS (0)
+#define FAIL (1)
+
+ /*
+ * Begin helper tests. TODO: move these to testing/dns_rr_testers_test.c.
+ */
+
+static int eq_dns_rr_qname_differ(void)
+{
+ DNS_RR *got = dns_rr_create("qa", "ra", T_SRV, C_IN, 3600, 1, 25, 1, "mxa", 3);
+ DNS_RR *want = dns_rr_copy(got);
+
+ myfree(want->qname);
+ want->qname = mystrdup("qb");
+ return (!eq_dns_rr_free(got, want));
+}
+
+static int eq_dns_rr_reply_differ(void)
+{
+ DNS_RR *got = dns_rr_create("qa", "ra", T_SRV, C_IN, 3600, 1, 25, 1, "mxa", 3);
+ DNS_RR *want = dns_rr_copy(got);
+
+ want->port += 1;
+ return (!eq_dns_rr_free(got, want));
+}
+
+ /*
+ * End helper tests.
+ */
+
+ /*
+ * Begin DNS_RR tests.
+ */
+
+static int eq_dns_rr_flags_differ(void)
+{
+ DNS_RR *got = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *want = dns_rr_copy(got);
+
+ want->flags |= DNS_RR_FLAG_TRUNCATED;
+ return (!eq_dns_rr_free(got, want));
+}
+
+static int append_to_null_from_null(void)
+{
+ DNS_RR *got = dns_rr_append((DNS_RR *) 0, (DNS_RR *) 0);
+ DNS_RR *want = 0;
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int append_to_elem_from_null(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *got, *want;
+
+ got = dns_rr_append(dns_rr_copy(a), (DNS_RR *) 0);
+
+ want = a;
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int appent_to_null_from_elem(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *got, *want;
+
+ got = dns_rr_append((DNS_RR *) 0, dns_rr_copy(a));
+
+ want = a;
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int append_to_elem_from_elem(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
+ DNS_RR *got, *want;
+
+ got = dns_rr_append(dns_rr_copy(a), dns_rr_copy(b));
+
+ (want = a)->next = b;
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int append_to_elem_from_list(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
+ DNS_RR *got, *want;
+
+ got = dns_rr_append(dns_rr_copy(a),
+ dns_rr_append(dns_rr_copy(b),
+ dns_rr_copy(c)));
+
+ ((want = a)->next = b)->next = c;
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int append_to_list_from_elem(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
+ DNS_RR *got, *want;
+
+ got = dns_rr_append(dns_rr_append(dns_rr_copy(a),
+ dns_rr_copy(b)),
+ dns_rr_copy(c));
+
+ ((want = a)->next = b)->next = c;
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int append_to_list_from_list(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
+ DNS_RR *d = dns_rr_create_noport("qd", "rd", T_MX, C_IN, 3600, 1, "mxd", 3);
+ DNS_RR *got, *want;
+
+ got = dns_rr_append(dns_rr_append(dns_rr_copy(a),
+ dns_rr_copy(b)),
+ dns_rr_append(dns_rr_copy(c),
+ dns_rr_copy(d)));
+
+ (((want = a)->next = b)->next = c)->next = d;
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int append_propagates_flags(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
+ DNS_RR *d = dns_rr_create_noport("qd", "rd", T_MX, C_IN, 3600, 1, "mxd", 3);
+ DNS_RR *left = dns_rr_append(dns_rr_copy(a), dns_rr_copy(b));
+ DNS_RR *rite = dns_rr_append(dns_rr_copy(c), dns_rr_copy(d));
+ DNS_RR *got, *want, *rr;
+
+ for (rr = rite; rr; rr = rr->next)
+ rr->flags |= DNS_RR_FLAG_TRUNCATED;
+
+ got = dns_rr_append(left, rite);
+
+ (((want = a)->next = b)->next = c)->next = d;
+ for (rr = want; rr; rr = rr->next)
+ rr->flags |= DNS_RR_FLAG_TRUNCATED;
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int append_to_list_from_list_truncate(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
+ DNS_RR *d = dns_rr_create_noport("qd", "rd", T_MX, C_IN, 3600, 1, "mxd", 3);
+ DNS_RR *got, *want, *rr;
+
+ var_dns_rr_list_limit = 3;
+
+ ((want = dns_rr_copy(a))->next = dns_rr_copy(b))->next = dns_rr_copy(c);
+ for (rr = want; rr; rr = rr->next)
+ rr->flags |= DNS_RR_FLAG_TRUNCATED;
+
+ got = dns_rr_append(dns_rr_append(a, b),
+ dns_rr_append(c, d));
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int append_to_list_from_elem_elem_truncate(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
+ DNS_RR *d = dns_rr_create_noport("qd", "rd", T_MX, C_IN, 3600, 1, "mxd", 3);
+ DNS_RR *got, *want, *rr;
+
+ var_dns_rr_list_limit = 2;
+
+ (want = dns_rr_copy(a))->next = dns_rr_copy(b);
+ for (rr = want; rr; rr = rr->next)
+ rr->flags |= DNS_RR_FLAG_TRUNCATED;
+
+ got = dns_rr_append(a, b);
+ got = dns_rr_append(got, c); /* should be logged */
+ got = dns_rr_append(got, d); /* should be silent */
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int append_to_list_from_elem_truncate(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
+ DNS_RR *got, *want, *rr;
+
+ var_dns_rr_list_limit = 2;
+
+ (want = dns_rr_copy(a))->next = dns_rr_copy(b);
+ for (rr = want; rr; rr = rr->next)
+ rr->flags |= DNS_RR_FLAG_TRUNCATED;
+
+ got = dns_rr_append(dns_rr_append(a, b), c);
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int append_to_elem_from_list_truncate(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
+ DNS_RR *got, *want, *rr;
+
+ var_dns_rr_list_limit = 2;
+
+ (want = dns_rr_copy(a))->next = dns_rr_copy(b);
+ for (rr = want; rr; rr = rr->next)
+ rr->flags |= DNS_RR_FLAG_TRUNCATED;
+
+ got = dns_rr_append(a, dns_rr_append(b, c));
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int append_to_list_from_elem_exact_fit(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
+ DNS_RR *got, *want;
+
+ var_dns_rr_list_limit = 3;
+
+ ((want = dns_rr_copy(a))->next = dns_rr_copy(b))->next = dns_rr_copy(c);
+
+ got = dns_rr_append(dns_rr_append(a, b), c);
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int append_to_elem_from_list_exact_fit(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 3);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 3);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 3);
+ DNS_RR *got, *want;
+
+ var_dns_rr_list_limit = 3;
+
+ ((want = dns_rr_copy(a))->next = dns_rr_copy(b))->next = dns_rr_copy(c);
+
+ got = dns_rr_append(a, dns_rr_append(b, c));
+
+ return (eq_dns_rr_free(got, want));
+}
+
+ /*
+ * The test cases.
+ */
+static const TEST_CASE test_cases[] = {
+
+ /*
+ * Test eq_dns_rr; TODO: move to testing/dns_rr_testers_test.c
+ */
+ "eq_dns_rr qname differ", eq_dns_rr_qname_differ,
+ "eq_dns_rr reply differ", eq_dns_rr_reply_differ,
+ "eq_dns_rr flags differ", eq_dns_rr_flags_differ,
+
+ /*
+ * Test dns_rr_append() without truncation.
+ */
+ "append to null from null", append_to_null_from_null,
+ "append to null from element", appent_to_null_from_elem,
+ "append to element from null", append_to_elem_from_null,
+ "append to element from element", append_to_elem_from_elem,
+ "append to element from list", append_to_elem_from_list,
+ "append to list from element", append_to_list_from_elem,
+ "append to list from list", append_to_list_from_list,
+
+ /*
+ * Test dns_rr_append() flag propagation.
+ */
+ "append propagates flags", append_propagates_flags,
+
+ /*
+ * Test dns_rr_append() with truncation.
+ */
+ "append to list from list truncate", append_to_list_from_list_truncate,
+ "append to list from element element truncate", append_to_list_from_elem_elem_truncate,
+ "append to list from element truncate", append_to_list_from_elem_truncate,
+ "append to element from list truncate", append_to_elem_from_list_truncate,
+ "append to list from element exact fit", append_to_list_from_elem_exact_fit,
+ "append to element from list exact fit", append_to_elem_from_list_exact_fit,
+
+ /*
+ * TODO: tests dns_rr_sort(), dns_rr_srv_sort(), dns_rr_remove(),
+ * dns_rr_shuffle(), etc.
+ */
+ 0,
+};
+
+int main(int argc, char **argv)
+{
+ const TEST_CASE *tp;
+ int pass = 0;
+ int fail = 0;
+ VSTRING *res_buf = vstring_alloc(100);
+ int saved_limit = var_dns_rr_list_limit;
+
+ msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
+
+ for (tp = test_cases; tp->label != 0; tp++) {
+ msg_info("RUN %s", tp->label);
+ if (tp->fn() == 0) {
+ fail++;
+ msg_info("FAIL %s", tp->label);
+ } else {
+ msg_info("PASS %s", tp->label);
+ pass++;
+ }
+ var_dns_rr_list_limit = saved_limit;
+ }
+ msg_info("PASS=%d FAIL=%d", pass, fail);
+ vstring_free(res_buf);
+ exit(fail != 0);
+}
diff --git a/postfix/src/dns/dns_strrecord.c b/postfix/src/dns/dns_strrecord.c
index 1e3b74389..bddf535dd 100644
--- a/postfix/src/dns/dns_strrecord.c
+++ b/postfix/src/dns/dns_strrecord.c
@@ -78,11 +78,12 @@ char *dns_strrecord(VSTRING *buf, DNS_RR *rr)
vstring_sprintf_append(buf, "%s", rr->data);
break;
case T_MX:
- vstring_sprintf_append(buf, "%u %s.", rr->pref, rr->data);
+ vstring_sprintf_append(buf, "%u %.*s.", rr->pref,
+ (int) rr->data_len, rr->data);
break;
case T_SRV:
- vstring_sprintf_append(buf, "%u %u %u %s.", rr->pref, rr->weight,
- rr->port, rr->data);
+ vstring_sprintf_append(buf, "%u %u %u %.*s.", rr->pref, rr->weight,
+ rr->port, (int) rr->data_len, rr->data);
break;
case T_TLSA:
if (rr->data_len >= 3) {
diff --git a/postfix/src/dns/test_dns_lookup.c b/postfix/src/dns/test_dns_lookup.c
index 9c0692b0d..6970124c7 100644
--- a/postfix/src/dns/test_dns_lookup.c
+++ b/postfix/src/dns/test_dns_lookup.c
@@ -124,9 +124,11 @@ int main(int argc, char **argv)
vstream_printf("%s: fqdn: %s\n", name, vstring_str(fqdn));
buf = vstring_alloc(100);
print_rr(buf, rr);
+ vstream_fflush(VSTREAM_OUT);
+ if (DNS_RR_IS_TRUNCATED(rr))
+ msg_warn("one or more excess DNS_RR records were dropped");
dns_rr_free(rr);
vstring_free(buf);
- vstream_fflush(VSTREAM_OUT);
}
}
myfree((void *) types);
diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h
index fea312ad0..1f03b0b34 100644
--- a/postfix/src/global/mail_params.h
+++ b/postfix/src/global/mail_params.h
@@ -4384,16 +4384,6 @@ extern int var_idna2003_compat;
#define DEF_DNS_NCACHE_TTL_FIX 0
extern bool var_dns_ncache_ttl_fix;
- /*
- * A generous safety limit for the number of DNS resource records that the
- * Postfix DNS client library will admit into a list. The default is 20x the
- * default limit on the number address records that the Postfix SMTP client
- * is willing to consider.
- */
-#define VAR_DNS_RR_LIST_LIMIT "dns_resource_list_limit"
-#define DEF_DNS_RR_LIST_LIMIT 100
-extern int var_dns_rr_list_limit;
-
/*
* Logging. As systems evolve over time, logging becomes more challenging.
*/
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index d1dd2cf8d..b33624384 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 "20240218"
+#define MAIL_RELEASE_DATE "20240227"
#define MAIL_VERSION_NUMBER "3.9"
#ifdef SNAPSHOT
diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c
index 9df556082..b474a4006 100644
--- a/postfix/src/posttls-finger/posttls-finger.c
+++ b/postfix/src/posttls-finger/posttls-finger.c
@@ -1280,6 +1280,8 @@ static DNS_RR *addr_one(STATE *state, DNS_RR *addr_list, const char *host,
msg_fatal("host %s: conversion error for address family %d: %m",
host, ((struct sockaddr *) (res0->ai_addr))->sa_family);
addr_list = dns_rr_append(addr_list, addr);
+ if (DNS_RR_IS_TRUNCATED(addr_list))
+ break;
}
freeaddrinfo(res0);
if (found == 0) {
@@ -1317,6 +1319,8 @@ static DNS_RR *mx_addr_list(STATE *state, DNS_RR *mx_names)
msg_panic("%s: bad resource type: %d", myname, rr->type);
addr_list = addr_one(state, addr_list, (char *) rr->data, res_opt,
rr->pref, rr->port);
+ if (addr_list && DNS_RR_IS_TRUNCATED(addr_list))
+ break;
}
return (addr_list);
}
diff --git a/postfix/src/smtp/smtp_addr.c b/postfix/src/smtp/smtp_addr.c
index 5e23388d6..8c384fc37 100644
--- a/postfix/src/smtp/smtp_addr.c
+++ b/postfix/src/smtp/smtp_addr.c
@@ -179,10 +179,10 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt,
if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0)
msg_fatal("host %s: conversion error for address family "
"%d: %m", host, res0->ai_addr->sa_family);
- addr_list = dns_rr_append(addr_list, addr);
addr->pref = pref;
addr->port = port;
- if (msg_verbose)
+ addr_list = dns_rr_append(addr_list, addr);
+ if (msg_verbose && !DNS_RR_IS_TRUNCATED(addr_list))
msg_info("%s: using numerical host %s", myname, host);
freeaddrinfo(res0);
return (addr_list);
@@ -262,6 +262,8 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, int res_opt,
msg_fatal("host %s: conversion error for address family "
"%d: %m", host, res0->ai_addr->sa_family);
addr_list = dns_rr_append(addr_list, addr);
+ if (DNS_RR_IS_TRUNCATED(addr_list))
+ break;
if (msg_verbose) {
MAI_HOSTADDR_STR hostaddr_str;
@@ -327,6 +329,8 @@ static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_BUF *why)
msg_panic("smtp_addr_list: bad resource type: %d", rr->type);
addr_list = smtp_addr_one(addr_list, (char *) rr->data, res_opt,
rr->pref, rr->port, why);
+ if (addr_list && DNS_RR_IS_TRUNCATED(addr_list))
+ break;
}
return (addr_list);
}
@@ -420,6 +424,13 @@ static DNS_RR *smtp_balance_inet_proto(DNS_RR *addr_list, int misc_flags,
* relative list order is unchanged, but some elements are removed.
*/
+ /*
+ * Ensure that dns_rr_append() won't interfere with the protocol
+ * balancing goals.
+ */
+ if (addr_limit > var_dns_rr_list_limit)
+ addr_limit = var_dns_rr_list_limit;
+
/*
* Count the number of IPv6 and IPv4 addresses.
*/
diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c
index d541e40a2..769f7c90b 100644
--- a/postfix/src/smtpd/smtpd_check.c
+++ b/postfix/src/smtpd/smtpd_check.c
@@ -3021,6 +3021,7 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
struct addrinfo *res;
int status;
const INET_PROTO_INFO *proto_info;
+ int server_addr_count = 0;
/*
* Sanity check.
@@ -3172,6 +3173,15 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
msg_info("%s: %s host address check: %s",
myname, dns_strtype(type), (char *) server->data);
for (res = res0; res != 0; res = res->ai_next) {
+ server_addr_count += 1;
+ if (server_addr_count > var_dns_rr_list_limit) {
+ msg_warn("%s: %s server address count limit (%d) exceeded"
+ " for %s %s -- ignoring the remainder", myname,
+ dns_strtype(type), var_dns_rr_list_limit,
+ reply_class, reply_name);
+ freeaddrinfo(res0);
+ CHECK_SERVER_RETURN(SMTPD_CHECK_DUNNO);
+ }
if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
if (msg_verbose)
msg_info("skipping address family %d for host %s",