]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.9-20240227
authorWietse Z Venema <wietse@porcupine.org>
Tue, 27 Feb 2024 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Thu, 29 Feb 2024 02:02:54 +0000 (21:02 -0500)
25 files changed:
postfix/HISTORY
postfix/html/pcre_table.5.html
postfix/html/postconf.5.html
postfix/html/regexp_table.5.html
postfix/man/man5/pcre_table.5
postfix/man/man5/postconf.5
postfix/man/man5/regexp_table.5
postfix/proto/pcre_table
postfix/proto/postconf.proto
postfix/proto/regexp_table
postfix/proto/stop
postfix/proto/stop.double-history
postfix/proto/stop.double-proto-html
postfix/src/dns/Makefile.in
postfix/src/dns/dns.h
postfix/src/dns/dns_lookup.c
postfix/src/dns/dns_rr.c
postfix/src/dns/dns_rr_test.c [new file with mode: 0644]
postfix/src/dns/dns_strrecord.c
postfix/src/dns/test_dns_lookup.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/posttls-finger/posttls-finger.c
postfix/src/smtp/smtp_addr.c
postfix/src/smtpd/smtpd_check.c

index 5fec159d4de28f3775d856b2cfef85752808c346..5505fdc0b2dc3a8844ba88ccf32f8959bb4efd88 100644 (file)
@@ -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.
index 0216e3e9b414309c6ad7ebb15f3deaf22b235d0d..1e036447f7c940cf5c92a8a0e251a5d599f87bcf 100644 (file)
@@ -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 <b>$</b>, specify <b>$$</b> to keep Postfix from  trying  to
-       do <i>$name</i> expansion as it evaluates a parameter value.
+       Note: if an inlined rule contains <b>$</b>, specify <b>$$</b> to  keep  Postfix  from
+       trying to do <i>$name</i> expansion as it evaluates a parameter value.
+
+       Note: when using <i>$name</i> inside an inlined pattern, use \Q<i>$name</i>\E to dis-
+       able metacharacters such as '.' in the <i>$name</i> expansion. Otherwise,  the
+       pattern may have unexpected matches.
 
 <b>EXAMPLE SMTPD ACCESS MAP</b>
        # Protect your outgoing majordomo exploders
index 82e863647feeb0a56df7fd32ca9b37f2dbc10b28..0ac9b68c73e06a53ef2851831f282adf8642059c 100644 (file)
@@ -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.  </dd>
 
-<dt> </dt> <dd> 2 Also log levels during TLS negotiation.  </dd>
+<dt> </dt> <dd> 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. </dd>
 
 <dt> </dt> <dd> 3 Also log the hexadecimal and ASCII dump of the
 TLS negotiation process.  </dd>
@@ -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.  </dd>
 
-<dt> </dt> <dd> 2 Also log levels during TLS negotiation. </dd>
+<dt> </dt> <dd> 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. </dd>
 
 <dt> </dt> <dd> 3 Also log hexadecimal and ASCII dump of TLS negotiation
 process. </dd>
index 05cda133d0038d5a808d06ba76a83d40435f5eb2..96e4b382cfe7040cbb09552e1baf97fc65f1a8f3 100644 (file)
@@ -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 <b>$</b>, specify <b>$$</b> to keep Postfix from  trying  to
-       do <i>$name</i> expansion as it evaluates a parameter value.
+       Note: if an inlined rule contains <b>$</b>, specify <b>$$</b> to  keep  Postfix  from
+       trying to do <i>$name</i> expansion as it evaluates a parameter value.
+
+       Note: when using <i>$name</i> inside an inlined pattern, this will not disable
+       metacharacters such as '.' in the <i>$name</i>  expansion.  To  prevent  unex-
+       pected matches, use a <a href="pcre_table.5.html">pcre</a>: table, and specify \Q<i>$name</i>\E.
 
 <b>EXAMPLE SMTPD ACCESS MAP</b>
        # Disallow sender-specified routing. This is a must if you relay mail
index a9fd7b646b58a04ab7f71683583cace1cb0f64e5..b5adb94f3119a1366ee548b5d038e84eab524428 100644 (file)
@@ -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
index 7cf173fb52c13271df38121e1c1ca32b9d9f2ccb..cf96e48cb7f238ef5dd0f14cf1c1a0241d30d388 100644 (file)
@@ -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
index 9eeefe49b902cc47a91d823a603014ac12e6d111..e9698210bc2f46d7b6580d49b85701e429c44901 100644 (file)
@@ -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
index 0f58c2b969c9e0275e3bf875588d7f4c43f9d80d..e4c6607f5fb95b11720d6455464e17db047d4c98 100644 (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.
 # EXAMPLE SMTPD ACCESS MAP
 #      # Protect your outgoing majordomo exploders
 #      /^(?!owner-)(.*)-outgoing@(.*)/ 550 Use ${1}@${2} instead
index ef3fb553611910289eb7f419c50ea5daee63a583..d13719b4bca4072ae6806772b1d5b77a56a0dca5 100644 (file)
@@ -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.  </dd>
 
-<dt> </dt> <dd> 2 Also log levels during TLS negotiation. </dd>
+<dt> </dt> <dd> 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. </dd>
 
 <dt> </dt> <dd> 3 Also log hexadecimal and ASCII dump of TLS negotiation
 process. </dd>
@@ -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.  </dd>
 
-<dt> </dt> <dd> 2 Also log levels during TLS negotiation.  </dd>
+<dt> </dt> <dd> 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. </dd>
 
 <dt> </dt> <dd> 3 Also log the hexadecimal and ASCII dump of the
 TLS negotiation process.  </dd>
index 1c384721bdc82ca221ec42d84ff8038aedf893ab..5e8c15f855f94ca6d6c5ad17006da67f0a17c890 100644 (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.
 # EXAMPLE SMTPD ACCESS MAP
 #      # Disallow sender-specified routing. This is a must if you relay mail
 #      # for other domains.
index e3e8ac6c7f89be1f95703f5d06a31ca1d88d3d5a..1ef4ab4bf30ef91976dde1267d175faba7eee935 100644 (file)
@@ -1602,3 +1602,4 @@ GmbH
 Hamid
 LLC
 Maadani
+GTEST
index 95abfdddea2f146eea28d35de6879261a315e9cc..5583f891d79fbc2e3005758dfd3e10136b15122b 100644 (file)
@@ -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 
index d79c684ee2c6e39dc84be81c7971a90d3dc78678..a4b2332a2a387e2d8cccfca228f4007090f98119 100644 (file)
@@ -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
index 7693545081909b00fb7a23557aab5d172d168d0c..5ea09cf5a698c9a50a46bc6711b135746d147b3f 100644 (file)
@@ -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
index 2b77015cb748f271edc350a4e62c4ffdf6b7c807..5f2a050eab68dd866a1371eced17b777d2b475b3 100644 (file)
@@ -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
index bccdd83f501f534dc32c658e4005161a7c2c8a22..08bd0319dcaa88b230df5769bcd462959c3cdeea 100644 (file)
@@ -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;
index deba784b77caaf31c43f452d6548bebb023c20fc..882a42ffbeaa5a6a80df1561024b0e626b3ace77 100644 (file)
 /*
 /*     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
 #include <mymalloc.h>
 #include <myrand.h>
 
-/* Global library. */
-
-#include <mail_params.h>
-
 /* 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);
+    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 {
-       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 (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 (file)
index 0000000..562a3e5
--- /dev/null
@@ -0,0 +1,433 @@
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <stdlib.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <msg_vstream.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <vstring.h>
+
+ /*
+  * DNS library.
+  */
+#include <dns.h>
+
+#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);
+}
index 1e3b74389353f9540c22485a198d1f19610cf00c..bddf535ddfc6a9da6bf9b5bfd01bff2d94bcb159 100644 (file)
@@ -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) {
index 9c0692b0d9c703423dbc08a1d493a9bb5548d70f..6970124c775b15760a0047c9f3c27a0027d2bc8b 100644 (file)
@@ -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);
index fea312ad009ccaad4b6c79b477d0cc2dad08172d..1f03b0b34e7db79dc42f7557b701864ccbee0d0c 100644 (file)
@@ -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.
   */
index d1dd2cf8ddc6758325667b697c6214dd6ec57987..b33624384dc2be239d269b5352d8ce754b00194d 100644 (file)
@@ -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
index 9df5560824f2c93ad24f0da3e6dfa84a5e16a9f6..b474a40066e1521fcd805cf369b73533b5e39f6a 100644 (file)
@@ -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);
 }
index 5e23388d69da8b62e159516d03b445de29aff765..8c384fc377fe46b6d055fa3f671740841a428fad 100644 (file)
@@ -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.
      */
index d541e40a2a8aea4e05c56500469de8329c7efc4f..769f7c90bee5bf355074fc5540bb112c4c13b22b 100644 (file)
@@ -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",