From f38f9d82f36bcf63d9b592bd6db59acf5e5e6355 Mon Sep 17 00:00:00 2001 From: Wietse Z Venema Date: Tue, 27 Feb 2024 00:00:00 -0500 Subject: [PATCH] postfix-3.9-20240227 --- postfix/HISTORY | 44 +- postfix/html/pcre_table.5.html | 8 +- postfix/html/postconf.5.html | 8 +- postfix/html/regexp_table.5.html | 8 +- postfix/man/man5/pcre_table.5 | 11 +- postfix/man/man5/postconf.5 | 8 +- postfix/man/man5/regexp_table.5 | 11 +- postfix/proto/pcre_table | 11 +- postfix/proto/postconf.proto | 8 +- postfix/proto/regexp_table | 11 +- postfix/proto/stop | 1 + postfix/proto/stop.double-history | 2 + postfix/proto/stop.double-proto-html | 1 + postfix/src/dns/Makefile.in | 27 +- postfix/src/dns/dns.h | 6 + postfix/src/dns/dns_lookup.c | 12 +- postfix/src/dns/dns_rr.c | 91 ++-- postfix/src/dns/dns_rr_test.c | 433 ++++++++++++++++++++ postfix/src/dns/dns_strrecord.c | 7 +- postfix/src/dns/test_dns_lookup.c | 4 +- postfix/src/global/mail_params.h | 10 - postfix/src/global/mail_version.h | 2 +- postfix/src/posttls-finger/posttls-finger.c | 4 + postfix/src/smtp/smtp_addr.c | 15 +- postfix/src/smtpd/smtpd_check.c | 10 + 25 files changed, 662 insertions(+), 91 deletions(-) create mode 100644 postfix/src/dns/dns_rr_test.c 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",