From: Wietse Venema Date: Fri, 10 Jul 2015 05:00:00 +0000 (-0500) Subject: postfix-3.1-20150710 X-Git-Tag: v3.1.0-RC1~21 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b62e4bad8a3573f977bd5609c5fff72e265e9fd2;p=thirdparty%2Fpostfix.git postfix-3.1-20150710 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 8ac693bcd..f1d589e3c 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -21757,3 +21757,52 @@ Apologies for any names omitted. Cleanup: updated the examples in MILTER_README. File: proto/MILTER_README.html + +20150529 + + Support for DNS reply TTL values in dnsblog and postscreen. + Files: dbsblog/dnsblog.c, postscreen/postscreen_early.c, + postscreen/postscreen_dnsbl.c. + +20150607 + + Support for DNS reply TTL values for "not found" responses + (negative reply caching). The postscreen daemon needs this to + accurately whitelist an SMTP client that is not found on any + DNSBL. Files: dns/dns_lookup.c, dns/dns_strrecord.c, dns/dns.h, + dns/test_dns_lookup.c. + +20150615 + + Two new parameters to limit how long a DNSBL or DNSWL lookup + result remains valid: postscreen_dnsbl_max_ttl is an upper + limit for the TTL from a DNS query, and postscreen_dnsbl_min_ttl + is a lower limit. The old postscreen_dnsbl_ttl provides a + backwards-compatible default for postscreen_dnsbl_max_ttl. + Files: global/mail_params.h, postscreen/postscreen.c, + postscreen/postscreen_early.c, mantools/postlink, + proto/postconf.proto. + +20150616 + + Refinement: the postscreen daemon now computes two combined + DNS reply TTLs: one combined TTL for replies that the client + should be blocked, and one combined TTL for replies that the + client should be allowed. This is more conservative than + simply combining all reply TTLs into one number. File: + +20150621 + + Feature: default_transport_rate_delay (and the transport-specific + *transport*_transport_rate_delay) to enforce a destination- + independent rate limit on deliveries. Files: mantools/postlink, + proto/postconf.proto, *qmgr/qmgr.h, *qmgr/qmgr_transport.c, + *qmgr/qmgr_deliver.c, *qmgr/qmgr.c. + + postscreen/postscreen_dnsbl.c. + +20150707 + + Workaround: some DNS servers reply with NXDOMAIN for type + NS queries with names that actually have an A record. This + broke check_mumble_ns_access. File: smtpd/smtpd_check.c. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index c47c503b2..758df223c 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -15,3 +15,54 @@ specifies the release date of a stable release or snapshot release. If you upgrade from Postfix 2.11 or earlier, read RELEASE_NOTES-3.0 before proceeding. + +Major changes with snaphot 20150710 +=================================== + +postscreen support for the TTL of DNSBL and DNSWL lookup results +---------------------------------------------------------------- + +Historically, the default setting "postscreen_dnsbl_ttl = 1h" assumes +that a "not found" result from a DNSBL server will be valid for one +hour. This may have been adequate five years ago when postscreen +was first implemented, but nowadays, that one hour can result in +missed opportunities to block new spambots. + +To address this, postscreen now respects the TTL of DNSBL "not +found" replies, as well as the TTL of DNSWL replies (both "found" +and "not found"). The TTL for a "not found" reply is determined +according to RFC 2308 (the TTL of an SOA record in the reply). + +Support for DNSBL or DNSWL reply TTL values is controlled by two +configuration parameters: + +postscreen_dnsbl_min_ttl (default: 60 seconds). + + This parameter specifies a minimum for the amount of time that + a DNSBL or DNSWL result will be cached in the postscreen_cache_map. + This prevents an excessive number of postscreen cache updates + when a DNSBL or DNSWL server specifies a very small reply TTL. + +postscreen_dnsbl_max_ttl (default: $postscreen_dnsbl_ttl or 1 hour) + + This parameter specifies a maximum for the amount of time that + a DNSBL or DNSWL result will be cached in the postscreen_cache_map. + This prevents cache pollution when a DNSBL or DNSWL server + specifies a very large reply TTL. + +The postscreen_dnsbl_ttl parameter is now obsolete, and has become +a default value for the new postscreen_dnsbl_max_ttl parameter. + +Destination-independent delivery rate delay +------------------------------------------- + +Support to enforce a destination-independent delay between meail +deliveries. The following example inserts 20 seconds of delay +between deliveries with the SMTP transport, limiting the delivery +rate to at most three messages per minute. + +/etc/postfix/main.cf: + smtp_transport_rate_delay = 20s + +For details, see the description of default_transport_rate_delay +and transport_transport_rate_delay in the postconf(5) manpage. diff --git a/postfix/WISHLIST b/postfix/WISHLIST index aa4d85243..eaeb18738 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -10,6 +10,8 @@ Wish list: Things to do after the stable release: + Make the dns_res_query() workaround on/off configurable. + TLS certificate provenance: indicate whether a subject name/issuer are verified or not (for example, change the attribute name to unverified_ccert_subject etc.). This is diff --git a/postfix/html/dnsblog.8.html b/postfix/html/dnsblog.8.html index c96f588ca..b1ca0e66a 100644 --- a/postfix/html/dnsblog.8.html +++ b/postfix/html/dnsblog.8.html @@ -22,9 +22,10 @@ DNSBLOG(8) DNSBLOG(8) list domain name, an IP address, and an ID. If the IP address is listed under the DNS white/blacklist, the dnsblog(8) server logs the match and replies with the query arguments plus an address list with - the resulting IP addresses separated by whitespace. Otherwise it - replies with the query arguments plus an empty address list. Finally, - The dnsblog(8) server closes the connection. + the resulting IP addresses, separated by whitespace, and the reply TTL. + Otherwise it replies with the query arguments plus an empty address + list and the reply TTL (-1 if unavailable). Finally, The dnsblog(8) + server closes the connection. DIAGNOSTICS Problems and transactions are logged to syslogd(8). @@ -34,15 +35,15 @@ DNSBLOG(8) DNSBLOG(8) run for only a limited amount of time. Use the command "postfix reload" to speed up a change. - The text below provides only a parameter summary. See postconf(5) for + The text below provides only a parameter summary. See postconf(5) for more details including examples. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and master.cf con- + The default location of the Postfix main.cf and master.cf con- figuration files. daemon_timeout (18000s) - How much time a Postfix daemon process may take to handle a + How much time a Postfix daemon process may take to handle a request before it is terminated by a built-in watchdog timer. postscreen_dnsbl_sites (empty) @@ -50,7 +51,7 @@ DNSBLOG(8) DNSBLOG(8) factors. ipc_timeout (3600s) - The time limit for sending or receiving information over an + The time limit for sending or receiving information over an internal communication channel. process_id (read-only) @@ -66,8 +67,8 @@ DNSBLOG(8) DNSBLOG(8) The syslog facility of Postfix logging. syslog_name (see 'postconf -d' output) - The mail system name that is prepended to the process name in - syslog records, so that "smtpd" becomes, for example, "post- + The mail system name that is prepended to the process name in + syslog records, so that "smtpd" becomes, for example, "post- fix/smtpd". SEE ALSO diff --git a/postfix/html/oqmgr.8.html b/postfix/html/oqmgr.8.html index f9a730210..3ad052dfb 100644 --- a/postfix/html/oqmgr.8.html +++ b/postfix/html/oqmgr.8.html @@ -295,6 +295,16 @@ OQMGR(8) OQMGR(8) transport_destination_rate_delay $default_destination_rate_delay Idem, for delivery via the named message transport. + Available in Postfix version 3.1 and later: + + default_transport_rate_delay (0s) + The default amount of delay that is inserted between individual + deliveries over the same message delivery transport, regardless + of destination. + + transport_transport_rate_delay $default_transport_rate_delay + Idem, for delivery via the named message transport. + SAFETY CONTROLS qmgr_daemon_timeout (1000s) How much time a Postfix queue manager process may take to handle diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index c14c4ea3b..160b0e425 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -2627,6 +2627,40 @@ Example: + + +
default_transport_rate_delay +(default: 0s)
+ +

The default amount of delay that is inserted between individual +deliveries over the same message delivery transport, regardless of +destination. If non-zero, all deliveries over the same message +delivery transport will happen one at a time.

+ +

Use transport_transport_rate_delay to specify a +transport-specific override, where the initial transport is +the master.cf name of the message delivery transport.

+ +

Example: throttle outbound SMTP mail to at most 3 deliveries +per minute.

+ +
+/etc/postfix/main.cf:
+    smtp_transport_rate_delay = 20s
+
+ +

To enable the delay, specify a non-zero time value (an integral +value plus an optional one-letter suffix that specifies the time +unit).

+ +

Time units: s (seconds), m (minutes), h (hours), d (days), w +(weeks). The default time unit is s (seconds).

+ +

NOTE: the delay is enforced by the queue manager.

+ +

This feature is available in Postfix 3.1 and later.

+ +
default_verp_delimiters @@ -7746,6 +7780,43 @@ this test the next time the client connects.

This feature is available in Postfix 2.8.

+ + +
postscreen_dnsbl_max_ttl +(default: ${postscreen_dnsbl_ttl?{$postscreen_dnsbl_ttl}:{1}}h)
+ +

The maximum amount of time that postscreen(8) will use the +result from a successful DNS-based reputation test before a +client IP address is required to pass that test again. If the DNS +reply specifies a shorter TTL value, that value will be used unless +it would be smaller than postscreen_dnsbl_min_ttl.

+ +

Specify a non-zero time value (an integral value plus an optional +one-letter suffix that specifies the time unit). Time units: s +(seconds), m (minutes), h (hours), d (days), w (weeks).

+ +

This feature is available in Postfix 3.1. The default setting +is backwards-compatible with older Postfix versions.

+ + +
+ +
postscreen_dnsbl_min_ttl +(default: 60s)
+ +

The minimum amount of time that postscreen(8) will use the +result from a successful DNS-based reputation test before a +client IP address is required to pass that test again. If the DNS +reply specifies a larger TTL value, that value will be used unless +it would be larger than postscreen_dnsbl_max_ttl.

+ +

Specify a non-zero time value (an integral value plus an optional +one-letter suffix that specifies the time unit). Time units: s +(seconds), m (minutes), h (hours), d (days), w (weeks).

+ +

This feature is available in Postfix 3.1.

+ +
postscreen_dnsbl_reply_map @@ -7869,16 +7940,15 @@ resolver(3) routines.

(default: 1h)

The amount of time that postscreen(8) will use the result from -a successful DNS blocklist test. During this time, the client IP address -is excluded from this test. The default is relatively short, because a -good client can immediately talk to a real Postfix SMTP server. -

+a successful DNS-based reputation test before a client +IP address is required to pass that test again.

Specify a non-zero time value (an integral value plus an optional one-letter suffix that specifies the time unit). Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).

-

This feature is available in Postfix 2.8.

+

This feature is available in Postfix 2.8-3.0. It was +replaced by postscreen_dnsbl_max_ttl in Postfix 3.1.

@@ -18563,6 +18633,16 @@ of a master.cf service name and a built-in suffix (i "_time_limit").

+ + +
transport_transport_rate_delay +(default: $default_transport_rate_delay)
+ +

A transport-specific override for the default_transport_rate_delay +parameter value, where the initial transport in the parameter +name is the master.cf name of the message delivery transport.

+ +
trigger_timeout diff --git a/postfix/html/postscreen.8.html b/postfix/html/postscreen.8.html index 85761ea45..09cd0e170 100644 --- a/postfix/html/postscreen.8.html +++ b/postfix/html/postscreen.8.html @@ -292,9 +292,16 @@ POSTSCREEN(8) POSTSCREEN(8) The amount of time that postscreen(8) will use the result from a successful "bare newline" SMTP protocol test. - postscreen_dnsbl_ttl (1h) - The amount of time that postscreen(8) will use the result from a - successful DNS blocklist test. + postscreen_dnsbl_max_ttl + (${postscreen_dnsbl_ttl?{$postscreen_dnsbl_ttl}:{1}}h) + The maximum amount of time that postscreen(8) will use the + result from a successful DNS-based reputation test before a + client IP address is required to pass that test again. + + postscreen_dnsbl_min_ttl (60s) + The minimum amount of time that postscreen(8) will use the + result from a successful DNS-based reputation test before a + client IP address is required to pass that test again. postscreen_greet_ttl (1d) The amount of time that postscreen(8) will use the result from a @@ -310,34 +317,34 @@ POSTSCREEN(8) POSTSCREEN(8) RESOURCE CONTROLS line_length_limit (2048) - Upon input, long lines are chopped up into pieces of at most + Upon input, long lines are chopped up into pieces of at most this length; upon delivery, long lines are reconstructed. postscreen_client_connection_count_limit ($smtpd_client_connec- tion_count_limit) - How many simultaneous connections any remote SMTP client is + How many simultaneous connections any remote SMTP client is allowed to have with the postscreen(8) daemon. postscreen_command_count_limit (20) - The limit on the total number of commands per SMTP session for + The limit on the total number of commands per SMTP session for postscreen(8)'s built-in SMTP protocol engine. postscreen_command_time_limit (normal: 300s, overload: 10s) - The time limit to read an entire command line with + The time limit to read an entire command line with postscreen(8)'s built-in SMTP protocol engine. postscreen_post_queue_limit ($default_process_limit) - The number of clients that can be waiting for service from a + The number of clients that can be waiting for service from a real Postfix SMTP server process. postscreen_pre_queue_limit ($default_process_limit) - The number of non-whitelisted clients that can be waiting for a - decision whether they will receive service from a real Postfix + The number of non-whitelisted clients that can be waiting for a + decision whether they will receive service from a real Postfix SMTP server process. postscreen_watchdog_timeout (10s) - How much time a postscreen(8) process may take to respond to a - remote SMTP client command or to perform a cache operation + How much time a postscreen(8) process may take to respond to a + remote SMTP client command or to perform a cache operation before it is terminated by a built-in watchdog timer. STARTTLS CONTROLS @@ -350,11 +357,11 @@ POSTSCREEN(8) POSTSCREEN(8) The name of the tlsproxy(8) service entry in master.cf. OBSOLETE STARTTLS SUPPORT CONTROLS - These parameters are supported for compatibility with smtpd(8) legacy + These parameters are supported for compatibility with smtpd(8) legacy parameters. postscreen_use_tls ($smtpd_use_tls) - Opportunistic TLS: announce STARTTLS support to remote SMTP + Opportunistic TLS: announce STARTTLS support to remote SMTP clients, but do not require that clients use TLS encryption. postscreen_enforce_tls ($smtpd_enforce_tls) @@ -363,18 +370,18 @@ POSTSCREEN(8) POSTSCREEN(8) MISCELLANEOUS CONTROLS config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and master.cf con- + The default location of the Postfix main.cf and master.cf con- figuration files. delay_logging_resolution_limit (2) - The maximal number of digits after the decimal point when log- + The maximal number of digits after the decimal point when log- ging sub-second delay values. command_directory (see 'postconf -d' output) The location of all postfix administrative commands. max_idle (100s) - The maximum amount of time that an idle Postfix daemon process + The maximum amount of time that an idle Postfix daemon process waits for an incoming connection before terminating voluntarily. process_id (read-only) @@ -387,8 +394,8 @@ POSTSCREEN(8) POSTSCREEN(8) The syslog facility of Postfix logging. syslog_name (see 'postconf -d' output) - The mail system name that is prepended to the process name in - syslog records, so that "smtpd" becomes, for example, "post- + The mail system name that is prepended to the process name in + syslog records, so that "smtpd" becomes, for example, "post- fix/smtpd". SEE ALSO @@ -406,7 +413,7 @@ POSTSCREEN(8) POSTSCREEN(8) HISTORY This service was introduced with Postfix version 2.8. - Many ideas in postscreen(8) were explored in earlier work by Michael + Many ideas in postscreen(8) were explored in earlier work by Michael Tokarev, in OpenBSD spamd, and in MailChannels Traffic Control. AUTHOR(S) diff --git a/postfix/html/qmgr.8.html b/postfix/html/qmgr.8.html index 6225c559d..34a8ca760 100644 --- a/postfix/html/qmgr.8.html +++ b/postfix/html/qmgr.8.html @@ -357,6 +357,16 @@ QMGR(8) QMGR(8) transport_destination_rate_delay $default_destination_rate_delay Idem, for delivery via the named message transport. + Available in Postfix version 3.1 and later: + + default_transport_rate_delay (0s) + The default amount of delay that is inserted between individual + deliveries over the same message delivery transport, regardless + of destination. + + transport_transport_rate_delay $default_transport_rate_delay + Idem, for delivery via the named message transport. + SAFETY CONTROLS qmgr_daemon_timeout (1000s) How much time a Postfix queue manager process may take to handle diff --git a/postfix/makedefs b/postfix/makedefs index 052cf9b98..2ce0774db 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -817,7 +817,7 @@ CCARGS="$CCARGS -DSNAPSHOT" # Non-production: needs thorough testing, or major changes are still # needed before the code stabilizes. -#CCARGS="$CCARGS -DNONPROD" +CCARGS="$CCARGS -DNONPROD" # Workaround: prepend Postfix include files before other include files. CCARGS="-I. -I../../include $CCARGS" diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index 4c303432a..042cbdc53 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -1676,6 +1676,38 @@ default_transport = uucp:relayhostname .fi .ad .ft R +.SH default_transport_rate_delay (default: 0s) +The default amount of delay that is inserted between individual +deliveries over the same message delivery transport, regardless of +destination. If non\-zero, all deliveries over the same message +delivery transport will happen one at a time. +.PP +Use \fItransport\fR_transport_rate_delay to specify a +transport\-specific override, where the initial \fItransport\fR is +the master.cf name of the message delivery transport. +.PP +Example: throttle outbound SMTP mail to at most 3 deliveries +per minute. +.PP +.nf +.na +.ft C +/etc/postfix/main.cf: + smtp_transport_rate_delay = 20s +.fi +.ad +.ft R +.PP +To enable the delay, specify a non\-zero time value (an integral +value plus an optional one\-letter suffix that specifies the time +unit). +.PP +Time units: s (seconds), m (minutes), h (hours), d (days), w +(weeks). The default time unit is s (seconds). +.PP +NOTE: the delay is enforced by the queue manager. +.PP +This feature is available in Postfix 3.1 and later. .SH default_verp_delimiters (default: +=) The two default VERP delimiter characters. These are used when no explicit delimiters are specified with the SMTP XVERP command @@ -4719,6 +4751,31 @@ this test the next time the client connects. .br .PP This feature is available in Postfix 2.8. +.SH postscreen_dnsbl_max_ttl (default: ${postscreen_dnsbl_ttl?{$postscreen_dnsbl_ttl}:{1}}h) +The maximum amount of time that \fBpostscreen\fR(8) will use the +result from a successful DNS\-based reputation test before a +client IP address is required to pass that test again. If the DNS +reply specifies a shorter TTL value, that value will be used unless +it would be smaller than postscreen_dnsbl_min_ttl. +.PP +Specify a non\-zero time value (an integral value plus an optional +one\-letter suffix that specifies the time unit). Time units: s +(seconds), m (minutes), h (hours), d (days), w (weeks). +.PP +This feature is available in Postfix 3.1. The default setting +is backwards\-compatible with older Postfix versions. +.SH postscreen_dnsbl_min_ttl (default: 60s) +The minimum amount of time that \fBpostscreen\fR(8) will use the +result from a successful DNS\-based reputation test before a +client IP address is required to pass that test again. If the DNS +reply specifies a larger TTL value, that value will be used unless +it would be larger than postscreen_dnsbl_max_ttl. +.PP +Specify a non\-zero time value (an integral value plus an optional +one\-letter suffix that specifies the time unit). Time units: s +(seconds), m (minutes), h (hours), d (days), w (weeks). +.PP +This feature is available in Postfix 3.1. .SH postscreen_dnsbl_reply_map (default: empty) A mapping from actual DNSBL domain name which includes a secret password, to the DNSBL domain name that postscreen will reply with @@ -4825,15 +4882,15 @@ the timeouts in the \fBdnsblog\fR(8) daemon which are defined by system This feature is available in Postfix 3.0. .SH postscreen_dnsbl_ttl (default: 1h) The amount of time that \fBpostscreen\fR(8) will use the result from -a successful DNS blocklist test. During this time, the client IP address -is excluded from this test. The default is relatively short, because a -good client can immediately talk to a real Postfix SMTP server. +a successful DNS\-based reputation test before a client +IP address is required to pass that test again. .PP Specify a non\-zero time value (an integral value plus an optional one\-letter suffix that specifies the time unit). Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks). .PP -This feature is available in Postfix 2.8. +This feature is available in Postfix 2.8\-3.0. It was +replaced by postscreen_dnsbl_max_ttl in Postfix 3.1. .SH postscreen_dnsbl_whitelist_threshold (default: 0) Allow a remote SMTP client to skip "before" and "after 220 greeting" protocol tests, based on its combined DNSBL score as @@ -12741,6 +12798,10 @@ in "postconf" command output before Postfix version 2.9. This limitation applies to many parameters whose name is a combination of a master.cf service name and a built\-in suffix (in this case: "_time_limit"). +.SH transport_transport_rate_delay (default: $default_transport_rate_delay) +A transport\-specific override for the default_transport_rate_delay +parameter value, where the initial \fItransport\fR in the parameter +name is the master.cf name of the message delivery transport. .SH trigger_timeout (default: 10s) The time limit for sending a trigger to a Postfix daemon (for example, the \fBpickup\fR(8) or \fBqmgr\fR(8) daemon). This time limit prevents diff --git a/postfix/man/man8/dnsblog.8 b/postfix/man/man8/dnsblog.8 index 8be212d6e..c36bfbcb8 100644 --- a/postfix/man/man8/dnsblog.8 +++ b/postfix/man/man8/dnsblog.8 @@ -26,8 +26,9 @@ a DNS white/blacklist domain name, an IP address, and an ID. If the IP address is listed under the DNS white/blacklist, the \fBdnsblog\fR(8) server logs the match and replies with the query arguments plus an address list with the resulting IP -addresses separated by whitespace. Otherwise it replies -with the query arguments plus an empty address list. Finally, +addresses, separated by whitespace, and the reply TTL. +Otherwise it replies with the query arguments plus an empty +address list and the reply TTL (\-1 if unavailable). Finally, The \fBdnsblog\fR(8) server closes the connection. .SH DIAGNOSTICS .ad diff --git a/postfix/man/man8/oqmgr.8 b/postfix/man/man8/oqmgr.8 index 6bd15fe40..f9f07554e 100644 --- a/postfix/man/man8/oqmgr.8 +++ b/postfix/man/man8/oqmgr.8 @@ -289,6 +289,14 @@ deliveries to the same destination; the resulting behavior depends on the value of the corresponding per\-destination recipient limit. .IP "\fItransport\fB_destination_rate_delay $default_destination_rate_delay Idem, for delivery via the named message \fItransport\fR. +.PP +Available in Postfix version 3.1 and later: +.IP "\fBdefault_transport_rate_delay (0s)\fR" +The default amount of delay that is inserted between individual +deliveries over the same message delivery transport, regardless of +destination. +.IP "\fItransport\fB_transport_rate_delay $default_transport_rate_delay +Idem, for delivery via the named message \fItransport\fR. .SH "SAFETY CONTROLS" .na .nf diff --git a/postfix/man/man8/postscreen.8 b/postfix/man/man8/postscreen.8 index bf92fbe00..faeda7274 100644 --- a/postfix/man/man8/postscreen.8 +++ b/postfix/man/man8/postscreen.8 @@ -307,9 +307,14 @@ temporary whitelist entry before it is removed. .IP "\fBpostscreen_bare_newline_ttl (30d)\fR" The amount of time that \fBpostscreen\fR(8) will use the result from a successful "bare newline" SMTP protocol test. -.IP "\fBpostscreen_dnsbl_ttl (1h)\fR" -The amount of time that \fBpostscreen\fR(8) will use the result from -a successful DNS blocklist test. +.IP "\fBpostscreen_dnsbl_max_ttl (${postscreen_dnsbl_ttl?{$postscreen_dnsbl_ttl}:{1}}h)\fR" +The maximum amount of time that \fBpostscreen\fR(8) will use the +result from a successful DNS\-based reputation test before a +client IP address is required to pass that test again. +.IP "\fBpostscreen_dnsbl_min_ttl (60s)\fR" +The minimum amount of time that \fBpostscreen\fR(8) will use the +result from a successful DNS\-based reputation test before a +client IP address is required to pass that test again. .IP "\fBpostscreen_greet_ttl (1d)\fR" The amount of time that \fBpostscreen\fR(8) will use the result from a successful PREGREET test. diff --git a/postfix/man/man8/qmgr.8 b/postfix/man/man8/qmgr.8 index 6ecfed563..0e395c362 100644 --- a/postfix/man/man8/qmgr.8 +++ b/postfix/man/man8/qmgr.8 @@ -337,6 +337,14 @@ deliveries to the same destination; the resulting behavior depends on the value of the corresponding per\-destination recipient limit. .IP "\fItransport\fB_destination_rate_delay $default_destination_rate_delay Idem, for delivery via the named message \fItransport\fR. +.PP +Available in Postfix version 3.1 and later: +.IP "\fBdefault_transport_rate_delay (0s)\fR" +The default amount of delay that is inserted between individual +deliveries over the same message delivery transport, regardless of +destination. +.IP "\fItransport\fB_transport_rate_delay $default_transport_rate_delay +Idem, for delivery via the named message \fItransport\fR. .SH "SAFETY CONTROLS" .na .nf diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index fd55bb7a0..2557555e1 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -394,6 +394,7 @@ while (<>) { s;\bdefault_desti[-]*\n* *[]*na[-]*\n* *[]*tion_con[-]*\n* *[]*currency_failed_cohort_limit\b;$&;g; s;\bdestination_concurrency_feedback_debug\b;$&;g; s;\bdefault_destina[-]*\n* *[]*tion_rate_delay\b;$&;g; + s;\bdefault_trans[-<\/bB>]*\n*[ ]*port_rate_delay\b;$&;g; s;\bmeta_directory\b;$&;g; s;\bqmqpd_client_port_logging\b;$&;g; @@ -764,6 +765,7 @@ while (<>) { s;(transport)()?(_recipient_refill_limit)\b;$2$1$3;g; s;(transport)()?(_time_limit)\b;$2$1$3;g; s;(transport)()?(_destination_rate_delay)\b;$2$1$3;g; + s;(transport)()?(_transport_rate_delay)\b;$2$1$3;g; # Undo hyperlinks of manual pages with the same name as parameters. @@ -1004,6 +1006,8 @@ while (<>) { s;\bpostscreen_dnsbl_thresh[-]*\n* *[]*old\b;$&;g; s;\bpostscreen_dnsbl_whitelist_thresh[-]*\n* *[]*old\b;$&;g; s;\bpostscreen_dnsbl_action\b;$&;g; + s;\bpostscreen_dnsbl_max_ttl\b;$&;g; + s;\bpostscreen_dnsbl_min_ttl\b;$&;g; s;\bpostscreen_dnsbl_ttl\b;$&;g; s;\bpostscreen_dnsbl_timeout\b;$&;g; s;\bpostscreen_for[-]*\n*[ ]*bid[-]*\n* *[]*den_commands\b;$&;g; diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index d710b6d9a..08d3a95b5 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -13153,6 +13153,42 @@ this case: "_recipient_refill_delay").

This feature is available in Postfix 2.4 and later.

+%PARAM default_transport_rate_delay 0s + +

The default amount of delay that is inserted between individual +deliveries over the same message delivery transport, regardless of +destination. If non-zero, all deliveries over the same message +delivery transport will happen one at a time.

+ +

Use transport_transport_rate_delay to specify a +transport-specific override, where the initial transport is +the master.cf name of the message delivery transport.

+ +

Example: throttle outbound SMTP mail to at most 3 deliveries +per minute.

+ +
+/etc/postfix/main.cf:
+    smtp_transport_rate_delay = 20s
+
+ +

To enable the delay, specify a non-zero time value (an integral +value plus an optional one-letter suffix that specifies the time +unit).

+ +

Time units: s (seconds), m (minutes), h (hours), d (days), w +(weeks). The default time unit is s (seconds).

+ +

NOTE: the delay is enforced by the queue manager.

+ +

This feature is available in Postfix 3.1 and later.

+ +%PARAM transport_transport_rate_delay $default_transport_rate_delay + +

A transport-specific override for the default_transport_rate_delay +parameter value, where the initial transport in the parameter +name is the master.cf name of the message delivery transport.

+ %PARAM default_destination_rate_delay 0s

The default amount of delay that is inserted between individual @@ -14305,16 +14341,44 @@ built-in SMTP protocol engine.

%PARAM postscreen_dnsbl_ttl 1h

The amount of time that postscreen(8) will use the result from -a successful DNS blocklist test. During this time, the client IP address -is excluded from this test. The default is relatively short, because a -good client can immediately talk to a real Postfix SMTP server. -

+a successful DNS-based reputation test before a client +IP address is required to pass that test again.

Specify a non-zero time value (an integral value plus an optional one-letter suffix that specifies the time unit). Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).

-

This feature is available in Postfix 2.8.

+

This feature is available in Postfix 2.8-3.0. It was +replaced by postscreen_dnsbl_max_ttl in Postfix 3.1.

+ +%PARAM postscreen_dnsbl_min_ttl 60s + +

The minimum amount of time that postscreen(8) will use the +result from a successful DNS-based reputation test before a +client IP address is required to pass that test again. If the DNS +reply specifies a larger TTL value, that value will be used unless +it would be larger than postscreen_dnsbl_max_ttl.

+ +

Specify a non-zero time value (an integral value plus an optional +one-letter suffix that specifies the time unit). Time units: s +(seconds), m (minutes), h (hours), d (days), w (weeks).

+ +

This feature is available in Postfix 3.1.

+ +%PARAM postscreen_dnsbl_max_ttl ${postscreen_dnsbl_ttl?{$postscreen_dnsbl_ttl}:{1}}h + +

The maximum amount of time that postscreen(8) will use the +result from a successful DNS-based reputation test before a +client IP address is required to pass that test again. If the DNS +reply specifies a shorter TTL value, that value will be used unless +it would be smaller than postscreen_dnsbl_min_ttl.

+ +

Specify a non-zero time value (an integral value plus an optional +one-letter suffix that specifies the time unit). Time units: s +(seconds), m (minutes), h (hours), d (days), w (weeks).

+ +

This feature is available in Postfix 3.1. The default setting +is backwards-compatible with older Postfix versions.

%PARAM postscreen_pipelining_action enforce diff --git a/postfix/src/dns/Makefile.in b/postfix/src/dns/Makefile.in index d8551b0f8..878fd692c 100644 --- a/postfix/src/dns/Makefile.in +++ b/postfix/src/dns/Makefile.in @@ -30,7 +30,33 @@ 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 + error-filter-test nullmx_test nxdomain_test mxonly_test \ + dnsbl_tests + +dnsbl_tests: \ + dnsbl_ttl_127.0.0.2_bind_plain_test \ + dnsbl_ttl_127.0.0.2_bind_ncache_test \ + dnsbl_ttl_127.0.0.2_priv_plain_test \ + dnsbl_ttl_127.0.0.2_priv_ncache_test \ + dnsbl_ttl_127.0.0.1_bind_plain_test \ + dnsbl_ttl_127.0.0.1_bind_ncache_test \ + dnsbl_ttl_127.0.0.1_priv_plain_test \ + dnsbl_ttl_127.0.0.1_priv_ncache_test + +DNSBL_NEXIST_REPLY_FIX = \ + sed -e 's/ [0-9][0-9]* IN SOA / TTL IN SOA /' \ + -e 's/len=[0-9][0-9]* /len=LEN /' \ + -e 's/nscount=[1-9][0-9]*/nscount=N/' \ + -e 's/ [0-9]* [0-9]* [0-9]* [0-9]* [0-9]*/ D D D D D/' + +DNSBL_EXIST_REPLY_FIX = \ + sed -e 's/ [0-9][0-9]* IN A / TTL IN A /' \ + -e 's/len=[0-9][0-9]* /len=LEN /' \ + -e 's/ancount=[1-9][0-9]*/ancount=N/' \ + -e 's/nscount=[1-9][0-9]*/nscount=N/' \ + -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: @@ -134,6 +160,94 @@ mxonly_test: test_dns_lookup mxonly_test.ref diff mxonly_test.ref mxonly_test.tmp rm -f mxonly_test.tmp +# Non-existent record, libbind API, RFC 2308 disabled. + +dnsbl_ttl_127.0.0.1_bind_plain_test: test_dns_lookup dnsbl_ttl_127.0.0.1_bind_plain.ref + (set -e; \ + $(SHLIB_ENV) ./test_dns_lookup a 1.0.0.127.zen.spamhaus.org; \ + $(SHLIB_ENV) ./test_dns_lookup a 1.0.0.127.b.barracudacentral.org; \ + $(SHLIB_ENV) ./test_dns_lookup a 1.0.0.127.bl.spamcop.net; \ + ) 2>&1 | $(DNSBL_NEXIST_REPLY_FIX) >dnsbl_ttl_127.0.0.1_bind_plain.tmp + diff dnsbl_ttl_127.0.0.1_bind_plain.ref dnsbl_ttl_127.0.0.1_bind_plain.tmp + rm -f dnsbl_ttl_127.0.0.1_bind_plain.tmp + +# Non-existent record, private API, RFC 2308 disabled. + +dnsbl_ttl_127.0.0.1_priv_plain_test: test_dns_lookup dnsbl_ttl_127.0.0.1_bind_plain.ref + (set -e; \ + $(SHLIB_ENV) ./test_dns_lookup -p a 1.0.0.127.zen.spamhaus.org; \ + $(SHLIB_ENV) ./test_dns_lookup -p a 1.0.0.127.b.barracudacentral.org; \ + $(SHLIB_ENV) ./test_dns_lookup -p a 1.0.0.127.bl.spamcop.net; \ + ) 2>&1 | $(DNSBL_NEXIST_REPLY_FIX) >dnsbl_ttl_127.0.0.1_priv_plain.tmp + diff dnsbl_ttl_127.0.0.1_bind_plain.ref dnsbl_ttl_127.0.0.1_priv_plain.tmp + rm -f dnsbl_ttl_127.0.0.1_priv_plain.tmp + +# Non-existent record, libbind API, RFC 2308 enabled. + +dnsbl_ttl_127.0.0.1_bind_ncache_test: test_dns_lookup dnsbl_ttl_127.0.0.1_bind_ncache.ref + (set -e; \ + $(SHLIB_ENV) ./test_dns_lookup -n a 1.0.0.127.zen.spamhaus.org; \ + $(SHLIB_ENV) ./test_dns_lookup -n a 1.0.0.127.b.barracudacentral.org; \ + $(SHLIB_ENV) ./test_dns_lookup -n a 1.0.0.127.bl.spamcop.net; \ + ) 2>&1 | $(DNSBL_NEXIST_REPLY_FIX) >dnsbl_ttl_127.0.0.1_bind_ncache.tmp + diff dnsbl_ttl_127.0.0.1_bind_ncache.ref dnsbl_ttl_127.0.0.1_bind_ncache.tmp + rm -f dnsbl_ttl_127.0.0.1_bind_ncache.tmp + +# Non-existent record, private API, RFC 2308 enabled. + +dnsbl_ttl_127.0.0.1_priv_ncache_test: test_dns_lookup dnsbl_ttl_127.0.0.1_bind_ncache.ref + (set -e; \ + $(SHLIB_ENV) ./test_dns_lookup -n -p a 1.0.0.127.zen.spamhaus.org; \ + $(SHLIB_ENV) ./test_dns_lookup -n -p a 1.0.0.127.b.barracudacentral.org; \ + $(SHLIB_ENV) ./test_dns_lookup -n -p a 1.0.0.127.bl.spamcop.net; \ + ) 2>&1 | $(DNSBL_NEXIST_REPLY_FIX) >dnsbl_ttl_127.0.0.1_priv_ncache.tmp + diff dnsbl_ttl_127.0.0.1_bind_ncache.ref dnsbl_ttl_127.0.0.1_priv_ncache.tmp + rm -f dnsbl_ttl_127.0.0.1_priv_ncache.tmp + +# Existing record, libbind API, RFC 2308 disabled. + +dnsbl_ttl_127.0.0.2_bind_plain_test: test_dns_lookup dnsbl_ttl_127.0.0.2_bind_plain.ref + (set -e; \ + $(SHLIB_ENV) ./test_dns_lookup a 2.0.0.127.zen.spamhaus.org; \ + $(SHLIB_ENV) ./test_dns_lookup a 2.0.0.127.b.barracudacentral.org; \ + $(SHLIB_ENV) ./test_dns_lookup a 2.0.0.127.bl.spamcop.net; \ + ) 2>&1 | $(DNSBL_EXIST_REPLY_FIX) >dnsbl_ttl_127.0.0.2_bind_plain.tmp + diff dnsbl_ttl_127.0.0.2_bind_plain.ref dnsbl_ttl_127.0.0.2_bind_plain.tmp + rm -f dnsbl_ttl_127.0.0.2_bind_plain.tmp + +# Existing record, private API, RFC 2308 disabled. + +dnsbl_ttl_127.0.0.2_priv_plain_test: test_dns_lookup dnsbl_ttl_127.0.0.2_bind_plain.ref + (set -e; \ + $(SHLIB_ENV) ./test_dns_lookup -p a 2.0.0.127.zen.spamhaus.org; \ + $(SHLIB_ENV) ./test_dns_lookup -p a 2.0.0.127.b.barracudacentral.org; \ + $(SHLIB_ENV) ./test_dns_lookup -p a 2.0.0.127.bl.spamcop.net; \ + ) 2>&1 | $(DNSBL_EXIST_REPLY_FIX) >dnsbl_ttl_127.0.0.2_priv_plain.tmp + diff dnsbl_ttl_127.0.0.2_bind_plain.ref dnsbl_ttl_127.0.0.2_priv_plain.tmp + rm -f dnsbl_ttl_127.0.0.2_priv_plain.tmp + +# Existing record, libbind API, RFC 2308 enabled. + +dnsbl_ttl_127.0.0.2_bind_ncache_test: test_dns_lookup dnsbl_ttl_127.0.0.2_bind_plain.ref + (set -e; \ + $(SHLIB_ENV) ./test_dns_lookup -n a 2.0.0.127.zen.spamhaus.org; \ + $(SHLIB_ENV) ./test_dns_lookup -n a 2.0.0.127.b.barracudacentral.org; \ + $(SHLIB_ENV) ./test_dns_lookup -n a 2.0.0.127.bl.spamcop.net; \ + ) 2>&1 | $(DNSBL_EXIST_REPLY_FIX) >dnsbl_ttl_127.0.0.2_bind_ncache.tmp + diff dnsbl_ttl_127.0.0.2_bind_plain.ref dnsbl_ttl_127.0.0.2_bind_ncache.tmp + rm -f dnsbl_ttl_127.0.0.2_bind_ncache.tmp + +# Existing record, private API, RFC 2308 enabled. + +dnsbl_ttl_127.0.0.2_priv_ncache_test: test_dns_lookup dnsbl_ttl_127.0.0.2_bind_plain.ref + (set -e; \ + $(SHLIB_ENV) ./test_dns_lookup -n -p a 2.0.0.127.zen.spamhaus.org; \ + $(SHLIB_ENV) ./test_dns_lookup -n -p a 2.0.0.127.b.barracudacentral.org; \ + $(SHLIB_ENV) ./test_dns_lookup -n -p a 2.0.0.127.bl.spamcop.net; \ + ) 2>&1 | $(DNSBL_EXIST_REPLY_FIX) >dnsbl_ttl_127.0.0.2_priv_ncache.tmp + 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 + printfck: $(OBJS) $(PROG) rm -rf printfck mkdir printfck diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h index 7cfc58108..2938ac3d6 100644 --- a/postfix/src/dns/dns.h +++ b/postfix/src/dns/dns.h @@ -212,15 +212,20 @@ extern int dns_rr_eq_sa(DNS_RR *, struct sockaddr *); /* * dns_lookup.c */ -extern int dns_lookup_r(const char *, unsigned, unsigned, DNS_RR **, - VSTRING *, VSTRING *, int *); +extern int dns_lookup_x(const char *, unsigned, unsigned, DNS_RR **, + VSTRING *, VSTRING *, int *, unsigned); extern int dns_lookup_rl(const char *, unsigned, DNS_RR **, VSTRING *, VSTRING *, int *, int,...); extern int dns_lookup_rv(const char *, unsigned, DNS_RR **, VSTRING *, VSTRING *, int *, int, unsigned *); +extern int dns_ncache_ttl_fix_enable; #define dns_lookup(name, type, rflags, list, fqdn, why) \ - dns_lookup_r((name), (type), (rflags), (list), (fqdn), (why), (int *) 0) + dns_lookup_x((name), (type), (rflags), (list), (fqdn), (why), (int *) 0, \ + (unsigned) 0) +#define dns_lookup_r(name, type, rflags, list, fqdn, why, rcode) \ + dns_lookup_x((name), (type), (rflags), (list), (fqdn), (why), (rcode), \ + (unsigned) 0) #define dns_lookup_l(name, rflags, list, fqdn, why, lflags, ...) \ dns_lookup_rl((name), (rflags), (list), (fqdn), (why), (int *) 0, \ (lflags), __VA_ARGS__) @@ -235,6 +240,7 @@ extern int dns_lookup_rv(const char *, unsigned, DNS_RR **, VSTRING *, #define DNS_REQ_FLAG_STOP_INVAL (1<<1) #define DNS_REQ_FLAG_STOP_NULLMX (1<<2) #define DNS_REQ_FLAG_STOP_MX_POLICY (1<<3) +#define DNS_REQ_FLAG_NCACHE_TTL (1<<4) #define DNS_REQ_FLAG_NONE (0) /* diff --git a/postfix/src/dns/dns_lookup.c b/postfix/src/dns/dns_lookup.c index 3838ea6b1..a89d2a468 100644 --- a/postfix/src/dns/dns_lookup.c +++ b/postfix/src/dns/dns_lookup.c @@ -32,6 +32,8 @@ /* int lflags; /* unsigned *ltype; /* AUXILIARY FUNCTIONS +/* int dns_ncache_ttl_fix_enable; +/* /* int dns_lookup_r(name, type, rflags, list, fqdn, why, rcode) /* const char *name; /* unsigned type; @@ -62,6 +64,16 @@ /* int *rcode; /* int lflags; /* unsigned *ltype; +/* +/* int dns_lookup_x(name, type, rflags, list, fqdn, why, rcode, lflags) +/* const char *name; +/* unsigned type; +/* unsigned rflags; +/* DNS_RR **list; +/* VSTRING *fqdn; +/* VSTRING *why; +/* int *rcode; +/* unsigned lflags; /* DESCRIPTION /* dns_lookup() looks up DNS resource records. When requested to /* look up data other than type CNAME, it will follow a limited @@ -74,8 +86,14 @@ /* dns_lookup_l() and dns_lookup_v() allow the user to specify /* a list of resource types. /* -/* dns_lookup_r(), dns_lookup_rl() and dns_lookup_rv() provide -/* additional information. +/* dns_lookup_x, dns_lookup_r(), dns_lookup_rl() and dns_lookup_rv() +/* accept or return additional information. +/* +/* The dns_ncache_ttl_fix_enable variable controls a workaround +/* for res_search(3) implementations that break the +/* DNS_REQ_FLAG_NCACHE_TTL feature. The workaround does not +/* support EDNS0 or DNSSEC, but it should be sufficient for +/* DNSBL/DNSWL lookups. /* INPUTS /* .ad /* .fi @@ -100,25 +118,33 @@ /* implement DNSSEC. /* .RE /* .IP lflags -/* Multi-type request control for dns_lookup_l() and dns_lookup_v(). -/* For convenience, DNS_REQ_FLAG_NONE requests no special -/* processing. Invoke dns_lookup() for all specified resource -/* record types in the specified order, and merge their results. +/* Flags that control the operation of the dns_lookup*() +/* functions. DNS_REQ_FLAG_NONE requests no special processing. /* Otherwise, specify one or more of the following: /* .RS /* .IP DNS_REQ_FLAG_STOP_INVAL +/* This flag is used by dns_lookup_l() and dns_lookup_v(). /* Invoke dns_lookup() for the resource types in the order as /* specified, and return when dns_lookup() returns DNS_INVAL. /* .IP DNS_REQ_FLAG_STOP_NULLMX +/* This flag is used by dns_lookup_l() and dns_lookup_v(). /* Invoke dns_lookup() for the resource types in the order as /* specified, and return when dns_lookup() returns DNS_NULLMX. /* .IP DNS_REQ_FLAG_STOP_MX_POLICY +/* This flag is used by dns_lookup_l() and dns_lookup_v(). /* Invoke dns_lookup() for the resource types in the order as /* specified, and return when dns_lookup() returns DNS_POLICY /* for an MX query. /* .IP DNS_REQ_FLAG_STOP_OK +/* This flag is used by dns_lookup_l() and dns_lookup_v(). /* Invoke dns_lookup() for the resource types in the order as /* specified, and return when dns_lookup() returns DNS_OK. +/* .IP DNS_REQ_FLAG_NCACHE_TTL +/* When the lookup result status is DNS_NOTFOUND, return the +/* SOA record(s) from the authority section in the reply, if +/* available. The per-record reply TTL specifies how long the +/* DNS_NOTFOUND answer is valid. The caller should pass the +/* record(s) to dns_rr_free(). /* .RE /* .IP ltype /* The resource record types to be looked up. In the case of @@ -215,6 +241,7 @@ */ #define DEF_DNS_REPLY_SIZE 4096 /* in case we're using TCP */ #define MAX_DNS_REPLY_SIZE 65536 /* in case we're using TCP */ +#define MAX_DNS_QUERY_SIZE 2048 /* XXX */ typedef struct DNS_REPLY { unsigned char *buf; /* raw reply data */ @@ -223,22 +250,163 @@ typedef struct DNS_REPLY { int dnssec_ad; /* DNSSEC AD bit */ int query_count; /* number of queries */ int answer_count; /* number of answers */ + int auth_count; /* number of authority records */ unsigned char *query_start; /* start of query data */ unsigned char *answer_start; /* start of answer data */ unsigned char *end; /* first byte past reply */ } DNS_REPLY; + /* + * Test/set primitives to determine if the reply buffer contains a server + * response. We use this when the caller requests DNS_REQ_FLAG_NCACHE_TTL, + * and the DNS server replies that the requested record does not exist. + */ +#define TEST_HAVE_DNS_REPLY_PACKET(r) ((r)->end > (r)->buf) +#define SET_HAVE_DNS_REPLY_PACKET(r, l) ((r)->end = (r)->buf + (l)) +#define SET_NO_DNS_REPLY_PACKET(r) ((r)->end = (r)->buf) + #define INET_ADDR_LEN 4 /* XXX */ #define INET6_ADDR_LEN 16 /* XXX */ + /* + * To improve postscreen's whitelisting support, we need to know how long a + * DNSBL "not found" answer is valid. The 2010 implementation assumed it was + * valid for 3600 seconds. That is too long by 2015 standards. + * + * Instead of guessing, Postfix 3.1 and later implement RFC 2308 (DNS NCACHE), + * where a DNS server provides the TTL of a "not found" response as the TTL + * of an SOA record in the authority section. + * + * Unfortunately, the res_search() and res_query() API gets in the way. These + * functions overload their result value, the server reply length, and + * return -1 when the requested record does not exist. With libbind-based + * res_search() implementations, the server response is still available in a + * caller-supplied buffer, thanks to a promise made by res_send() and the + * functions that depend on it. With some creativity we can still use the + * server response. + * + * If this should stop working (for example, res_search() does not call + * res_send(), but some non-libbind implementation that updates the + * caller-supplied buffer only when the requested record exists), then we + * have a way out by setting the dns_ncache_ttl_fix_enable variable. This + * enables a limited res_query() clone that should be sufficient for DNSBL / + * DNSWL lookups. + * + * The libunbound API does not comingle the reply length and reply status + * information, but that will have to wait until it is safe to make + * libunbound a mandatory dependency for Postfix. + */ +int dns_ncache_ttl_fix_enable = 0; + +/* dns_res_query - a res_query() clone that can return negative replies */ + +static int dns_res_query(const char *name, int class, int type, + unsigned char *answer, int anslen) +{ + unsigned char msg_buf[MAX_DNS_QUERY_SIZE]; + HEADER *reply_header = (HEADER *) answer; + int len; + + /* + * Differences with res_query() from libbind: + * + * - This function returns a positive server reply length not only in case + * of success, but in all cases where a server reply is available that + * passes the preliminary checks in res_send(). + * + * - This function clears h_errno in case of success. The caller must use + * h_errno instead of the return value to decide if the lookup was + * successful. + * + * - No support for EDNS0 and DNSSEC (including turning off EDNS0 after + * error). That should be sufficient for DNS reputation lookups where the + * reply contains a small number of IP addresses. TXT records are out of + * scope for this workaround. + */ + reply_header->rcode = NOERROR; + +#define NO_MKQUERY_DATA_BUF ((unsigned char *) 0) +#define NO_MKQUERY_DATA_LEN ((int) 0) +#define NO_MKQUERY_NEWRR ((unsigned char *) 0) + + if ((len = res_mkquery(QUERY, name, class, type, NO_MKQUERY_DATA_BUF, + NO_MKQUERY_DATA_LEN, NO_MKQUERY_NEWRR, + msg_buf, sizeof(msg_buf))) < 0) { + SET_H_ERRNO(NO_RECOVERY); + if (msg_verbose) + msg_info("res_mkquery() failed"); + return (len); + } else if ((len = res_send(msg_buf, len, answer, anslen)) < 0) { + SET_H_ERRNO(TRY_AGAIN); + if (msg_verbose) + msg_info("res_send() failed"); + return (len); + } else { + switch (reply_header->rcode) { + case NXDOMAIN: + SET_H_ERRNO(HOST_NOT_FOUND); + break; + case NOERROR: + if (reply_header->ancount != 0) + SET_H_ERRNO(0); + else + SET_H_ERRNO(NO_DATA); + break; + case SERVFAIL: + SET_H_ERRNO(TRY_AGAIN); + break; + default: + SET_H_ERRNO(NO_RECOVERY); + break; + } + return (len); + } +} + +/* dns_res_search - res_search() that can return negative replies */ + +static int dns_res_search(const char *name, int class, int type, + unsigned char *answer, int anslen, int keep_notfound) +{ + int len; + + /* + * Differences with res_search() from libbind: + * + * - With a non-zero keep_notfound argument, this function returns a + * positive server reply length not only in case of success, but also in + * case of a "notfound" reply status. The keep_notfound argument is + * usually zero, which allows us to avoid an unnecessary memset() call in + * the most common use case. + * + * - This function clears h_errno in case of success. The caller must use + * h_errno instead of the return value to decide if a lookup was + * successful. + */ +#define NOT_FOUND_H_ERRNO(he) ((he) == HOST_NOT_FOUND || (he) == NO_DATA) + + if (keep_notfound) + /* Prepare for returning a null-padded server reply. */ + memset(answer, 0, anslen); + len = res_query(name, class, type, answer, anslen); + if (len > 0) { + SET_H_ERRNO(0); + } else if (keep_notfound && NOT_FOUND_H_ERRNO(h_errno)) { + /* Expect to return a null-padded server reply. */ + len = anslen; + } + return (len); +} + /* dns_query - query name server and pre-parse the reply */ -static int dns_query(const char *name, int type, int flags, - DNS_REPLY *reply, VSTRING *why) +static int dns_query(const char *name, int type, unsigned flags, + DNS_REPLY *reply, VSTRING *why, unsigned lflags) { HEADER *reply_header; int len; unsigned long saved_options; + int keep_notfound = (lflags & DNS_REQ_FLAG_NCACHE_TTL); /* * Initialize the reply buffer. @@ -289,12 +457,18 @@ static int dns_query(const char *name, int type, int flags, for (;;) { _res.options &= ~saved_options; _res.options |= flags; - len = res_search((char *) name, C_IN, type, reply->buf, reply->buf_len); + if (keep_notfound && dns_ncache_ttl_fix_enable) { + len = dns_res_query((char *) name, C_IN, type, reply->buf, + reply->buf_len); + } else { + len = dns_res_search((char *) name, C_IN, type, reply->buf, + reply->buf_len, keep_notfound); + } _res.options &= ~flags; _res.options |= saved_options; reply_header = (HEADER *) reply->buf; reply->rcode = reply_header->rcode; - if (len < 0) { + if (h_errno != 0) { if (why) vstring_sprintf(why, "Host or domain name not found. " "Name service error for name=%s type=%s: %s", @@ -307,13 +481,17 @@ static int dns_query(const char *name, int type, int flags, return (DNS_FAIL); case HOST_NOT_FOUND: case NO_DATA: + if (keep_notfound) + break; + SET_NO_DNS_REPLY_PACKET(reply); return (DNS_NOTFOUND); default: return (DNS_RETRY); } + } else { + if (msg_verbose) + msg_info("dns_query: %s (%s): OK", name, dns_strtype(type)); } - if (msg_verbose) - msg_info("dns_query: %s (%s): OK", name, dns_strtype(type)); if (reply_header->tc == 0 || reply->buf_len >= MAX_DNS_REPLY_SIZE) break; @@ -322,6 +500,14 @@ static int dns_query(const char *name, int type, int flags, reply->buf_len *= 2; } + /* + * Future proofing. If this reaches the panic call, then some code change + * introduced a bug. + */ + if (len < 0) + msg_panic("dns_query: bad length %d (h_errno=%s)", + len, dns_strerror(h_errno)); + /* * Paranoia. */ @@ -340,12 +526,28 @@ static int dns_query(const char *name, int type, int flags, #else reply->dnssec_ad = 0; #endif - reply->end = reply->buf + len; + SET_HAVE_DNS_REPLY_PACKET(reply, len); reply->query_start = reply->buf + sizeof(HEADER); reply->answer_start = 0; reply->query_count = ntohs(reply_header->qdcount); reply->answer_count = ntohs(reply_header->ancount); - return (DNS_OK); + reply->auth_count = ntohs(reply_header->nscount); + if (msg_verbose > 1) + msg_info("dns_query: reply len=%d ancount=%d nscount=%d", + len, reply->answer_count, reply->auth_count); + + /* + * Future proofing. If this reaches the panic call, then some code change + * introduced a bug. + */ + if (h_errno == 0) { + return (DNS_OK); + } else if (keep_notfound) { + return (DNS_NOTFOUND); + } else { + msg_panic("dns_query: unexpected reply status: %s", + dns_strerror(h_errno)); + } } /* dns_skip_query - skip query data in name server reply */ @@ -354,7 +556,6 @@ static int dns_skip_query(DNS_REPLY *reply) { int query_count = reply->query_count; unsigned char *pos = reply->query_start; - char temp[DNS_NAME_LEN]; int len; /* @@ -364,7 +565,7 @@ static int dns_skip_query(DNS_REPLY *reply) while (query_count-- > 0) { if (pos >= reply->end) return DNS_RETRY; - len = dn_expand(reply->buf, reply->end, pos, temp, DNS_NAME_LEN); + len = dn_skipname(pos, reply->end); if (len < 0) return (DNS_RETRY); pos += len + QFIXEDSZ; @@ -441,6 +642,8 @@ static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply, { char temp[DNS_NAME_LEN]; char *tempbuf = temp; + UINT32_TYPE soa_buf[5]; + int comp_len; ssize_t data_len; unsigned pref = 0; unsigned char *src; @@ -532,6 +735,35 @@ static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply, data_len = fixed->length; tempbuf = (char *) pos; break; + + /* + * We use the SOA record TTL to determine the negative reply TTL. We + * save the time fields in the SOA record for debugging, but for now + * we don't bother saving the source host and mailbox information, as + * that would require changes to the DNS_RR structure and APIs. See + * also code in dns_strrecord(). + */ + case T_SOA: + comp_len = dn_skipname(pos, reply->end); + if (comp_len < 0) + return (DNS_RETRY); + pos += comp_len; + comp_len = dn_skipname(pos, reply->end); + if (comp_len < 0) + return (DNS_RETRY); + pos += comp_len; + if (reply->end - pos < sizeof(soa_buf)) { + msg_warn("extract_answer: bad SOA length: %d", fixed->length); + return (DNS_RETRY); + } + GETLONG(soa_buf[0], pos); /* Serial */ + GETLONG(soa_buf[1], pos); /* Refresh */ + GETLONG(soa_buf[2], pos); /* Retry */ + GETLONG(soa_buf[3], pos); /* Expire */ + GETLONG(soa_buf[4], pos); /* Ncache TTL */ + tempbuf = (char *) soa_buf; + data_len = sizeof(soa_buf); + break; } *list = dns_rr_create(orig_name, rr_name, fixed->type, fixed->class, fixed->ttl, pref, tempbuf, data_len); @@ -576,8 +808,6 @@ static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type, if ((status = dns_skip_query(reply)) < 0) return (status); pos = reply->answer_start; - if (rrlist) - *rrlist = 0; /* * Either this, or use a GOTO for emergency exits. The purpose is to @@ -663,11 +893,11 @@ static int dns_get_answer(const char *orig_name, DNS_REPLY *reply, int type, return (not_found_status); } -/* dns_lookup_r - DNS lookup user interface */ +/* dns_lookup_x - DNS lookup user interface */ -int dns_lookup_r(const char *name, unsigned type, unsigned flags, +int dns_lookup_x(const char *name, unsigned type, unsigned flags, DNS_RR **rrlist, VSTRING *fqdn, VSTRING *why, - int *rcode) + int *rcode, unsigned lflags) { char cname[DNS_NAME_LEN]; int c_len = sizeof(cname); @@ -677,6 +907,13 @@ int dns_lookup_r(const char *name, unsigned type, unsigned flags, int maybe_secure = 1; /* Query name presumed secure */ const char *orig_name = name; + /* + * Reset results early. DNS_OK is not the only status that returns + * resource records; DNS_NOTFOUND will do that too, if requested. + */ + if (rrlist) + *rrlist = 0; + /* * DJBDNS produces a bogus A record when given a numerical hostname. */ @@ -685,6 +922,8 @@ int dns_lookup_r(const char *name, unsigned type, unsigned flags, vstring_sprintf(why, "Name service error for %s: invalid host or domain name", name); + if (rcode) + *rcode = NXDOMAIN; SET_H_ERRNO(HOST_NOT_FOUND); return (DNS_NOTFOUND); } @@ -697,6 +936,8 @@ int dns_lookup_r(const char *name, unsigned type, unsigned flags, vstring_sprintf(why, "Name service error for %s: invalid host or domain name", name); + if (rcode) + *rcode = NXDOMAIN; SET_H_ERRNO(HOST_NOT_FOUND); return (DNS_NOTFOUND); } @@ -710,11 +951,25 @@ int dns_lookup_r(const char *name, unsigned type, unsigned flags, /* * Perform the DNS lookup, and pre-parse the name server reply. */ - status = dns_query(name, type, flags, &reply, why); + status = dns_query(name, type, flags, &reply, why, lflags); if (rcode) *rcode = reply.rcode; - if (status != DNS_OK) + if (status != DNS_OK) { + + /* + * If the record does not exist, and we have a copy of the server + * response, try to extract the negative caching TTL for the SOA + * record in the authority section. DO NOT return an error if an + * SOA record is malformed. + */ + if (status == DNS_NOTFOUND && TEST_HAVE_DNS_REPLY_PACKET(&reply) + && reply.auth_count > 0) { + reply.answer_count = reply.auth_count; /* XXX TODO: Fix API */ + (void) dns_get_answer(orig_name, &reply, T_SOA, rrlist, fqdn, + cname, c_len, &maybe_secure); + } return (status); + } /* * Extract resource records of the requested type. Pick up CNAME @@ -733,10 +988,10 @@ int dns_lookup_r(const char *name, unsigned type, unsigned flags, if (why) vstring_sprintf(why, "Domain %s does not accept mail (nullMX)", name); - h_errno = NO_DATA; + SET_H_ERRNO(NO_DATA); return (status); case DNS_OK: - if (dns_rr_filter_maps) { + if (rrlist && dns_rr_filter_maps) { if (dns_rr_filter_execute(rrlist) < 0) { if (why) vstring_sprintf(why, @@ -790,6 +1045,7 @@ int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist, int hpref_status = INT_MIN; VSTRING *hpref_rtext = 0; int hpref_rcode; + int hpref_h_errno; DNS_RR *rr; /* Save intermediate highest-priority result. */ @@ -801,6 +1057,7 @@ int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist, vstring_strcpy(hpref_rtext ? hpref_rtext : \ (hpref_rtext = vstring_alloc(VSTRING_LEN(why))), \ vstring_str(why)); \ + hpref_h_errno = h_errno; \ } while (0) /* Restore intermediate highest-priority result. */ @@ -810,6 +1067,7 @@ int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist, *rcode = hpref_rcode; \ if (why && status != DNS_OK) \ vstring_strcpy(why, vstring_str(hpref_rtext)); \ + SET_H_ERRNO(hpref_h_errno); \ } while (0) if (rrlist) @@ -820,11 +1078,11 @@ int dns_lookup_rl(const char *name, unsigned flags, DNS_RR **rrlist, if (msg_verbose) msg_info("lookup %s type %s flags %d", name, dns_strtype(type), flags); - status = dns_lookup_r(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, - fqdn, why, rcode); + status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, + fqdn, why, rcode, lflags); + if (rrlist && rr) + *rrlist = dns_rr_append(*rrlist, rr); if (status == DNS_OK) { - if (rrlist) - *rrlist = dns_rr_append(*rrlist, rr); if (lflags & DNS_REQ_FLAG_STOP_OK) break; } else if (status == DNS_INVAL) { @@ -862,6 +1120,7 @@ int dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist, int hpref_status = INT_MIN; VSTRING *hpref_rtext = 0; int hpref_rcode; + int hpref_h_errno; DNS_RR *rr; if (rrlist) @@ -871,11 +1130,11 @@ int dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist, if (msg_verbose) msg_info("lookup %s type %s flags %d", name, dns_strtype(type), flags); - status = dns_lookup_r(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, - fqdn, why, rcode); + status = dns_lookup_x(name, type, flags, rrlist ? &rr : (DNS_RR **) 0, + fqdn, why, rcode, lflags); + if (rrlist && rr) + *rrlist = dns_rr_append(*rrlist, rr); if (status == DNS_OK) { - if (rrlist) - *rrlist = dns_rr_append(*rrlist, rr); if (lflags & DNS_REQ_FLAG_STOP_OK) break; } else if (status == DNS_INVAL) { diff --git a/postfix/src/dns/dns_strrecord.c b/postfix/src/dns/dns_strrecord.c index 318cdb90b..370850917 100644 --- a/postfix/src/dns/dns_strrecord.c +++ b/postfix/src/dns/dns_strrecord.c @@ -29,6 +29,7 @@ /* System library. */ #include +#include /* memcpy */ /* Utility library. */ @@ -45,6 +46,7 @@ char *dns_strrecord(VSTRING *buf, DNS_RR *rr) { const char myname[] = "dns_strrecord"; MAI_HOSTADDR_STR host; + UINT32_TYPE soa_buf[5]; vstring_sprintf(buf, "%s. %u IN %s ", rr->rname, rr->ttl, dns_strtype(rr->type)); @@ -88,6 +90,19 @@ char *dns_strrecord(VSTRING *buf, DNS_RR *rr) } else { vstring_sprintf_append(buf, "[truncated record]"); } + + /* + * We use the SOA record TTL to determine the negative reply TTL. We + * save the time fields in the SOA record for debugging, but for now + * we don't bother saving the source host and mailbox information, as + * that would require changes to the DNS_RR structure. See also code + * in dns_get_rr(). + */ + case T_SOA: + memcpy(soa_buf, rr->data, sizeof(soa_buf)); + vstring_sprintf_append(buf, "- - %u %u %u %u %u", + soa_buf[0], soa_buf[1], soa_buf[2], + soa_buf[3], soa_buf[4]); break; default: msg_fatal("%s: don't know how to print type %s", diff --git a/postfix/src/dns/dnsbl_ttl_127.0.0.1_bind_ncache.ref b/postfix/src/dns/dnsbl_ttl_127.0.0.1_bind_ncache.ref new file mode 100644 index 000000000..134309fd6 --- /dev/null +++ b/postfix/src/dns/dnsbl_ttl_127.0.0.1_bind_ncache.ref @@ -0,0 +1,15 @@ +./test_dns_lookup: lookup 1.0.0.127.zen.spamhaus.org type A flags 2097152 +./test_dns_lookup: dns_query: 1.0.0.127.zen.spamhaus.org (A): Host not found +./test_dns_lookup: dns_get_answer: type SOA for zen.spamhaus.org +./test_dns_lookup: warning: Host or domain name not found. Name service error for name=1.0.0.127.zen.spamhaus.org type=A: Host not found (rcode=3) +1.0.0.127.zen.spamhaus.org: fqdn: zen.spamhaus.org +ad: 0, rr: zen.spamhaus.org. TTL IN SOA - - D D D D D +./test_dns_lookup: lookup 1.0.0.127.b.barracudacentral.org type A flags 2097152 +./test_dns_lookup: dns_query: 1.0.0.127.b.barracudacentral.org (A): Host not found +./test_dns_lookup: warning: Host or domain name not found. Name service error for name=1.0.0.127.b.barracudacentral.org type=A: Host not found (rcode=3) +./test_dns_lookup: lookup 1.0.0.127.bl.spamcop.net type A flags 2097152 +./test_dns_lookup: dns_query: 1.0.0.127.bl.spamcop.net (A): Host not found +./test_dns_lookup: dns_get_answer: type SOA for bl.spamcop.net +./test_dns_lookup: warning: Host or domain name not found. Name service error for name=1.0.0.127.bl.spamcop.net type=A: Host not found (rcode=3) +1.0.0.127.bl.spamcop.net: fqdn: bl.spamcop.net +ad: 0, rr: bl.spamcop.net. TTL IN SOA - - D D D D D diff --git a/postfix/src/dns/dnsbl_ttl_127.0.0.1_bind_plain.ref b/postfix/src/dns/dnsbl_ttl_127.0.0.1_bind_plain.ref new file mode 100644 index 000000000..e5741d34c --- /dev/null +++ b/postfix/src/dns/dnsbl_ttl_127.0.0.1_bind_plain.ref @@ -0,0 +1,9 @@ +./test_dns_lookup: lookup 1.0.0.127.zen.spamhaus.org type A flags 2097152 +./test_dns_lookup: dns_query: 1.0.0.127.zen.spamhaus.org (A): Host not found +./test_dns_lookup: warning: Host or domain name not found. Name service error for name=1.0.0.127.zen.spamhaus.org type=A: Host not found (rcode=3) +./test_dns_lookup: lookup 1.0.0.127.b.barracudacentral.org type A flags 2097152 +./test_dns_lookup: dns_query: 1.0.0.127.b.barracudacentral.org (A): Host not found +./test_dns_lookup: warning: Host or domain name not found. Name service error for name=1.0.0.127.b.barracudacentral.org type=A: Host not found (rcode=3) +./test_dns_lookup: lookup 1.0.0.127.bl.spamcop.net type A flags 2097152 +./test_dns_lookup: dns_query: 1.0.0.127.bl.spamcop.net (A): Host not found +./test_dns_lookup: warning: Host or domain name not found. Name service error for name=1.0.0.127.bl.spamcop.net type=A: Host not found (rcode=3) diff --git a/postfix/src/dns/dnsbl_ttl_127.0.0.2_bind_plain.ref b/postfix/src/dns/dnsbl_ttl_127.0.0.2_bind_plain.ref new file mode 100644 index 000000000..06bc7d848 --- /dev/null +++ b/postfix/src/dns/dnsbl_ttl_127.0.0.2_bind_plain.ref @@ -0,0 +1,15 @@ +./test_dns_lookup: lookup 2.0.0.127.zen.spamhaus.org type A flags 2097152 +./test_dns_lookup: dns_query: 2.0.0.127.zen.spamhaus.org (A): OK +./test_dns_lookup: dns_get_answer: type A for 2.0.0.127.zen.spamhaus.org +2.0.0.127.zen.spamhaus.org: fqdn: 2.0.0.127.zen.spamhaus.org +ad: 0, rr: 2.0.0.127.zen.spamhaus.org. TTL IN A 127.0.0.D +./test_dns_lookup: lookup 2.0.0.127.b.barracudacentral.org type A flags 2097152 +./test_dns_lookup: dns_query: 2.0.0.127.b.barracudacentral.org (A): OK +./test_dns_lookup: dns_get_answer: type A for 2.0.0.127.b.barracudacentral.org +2.0.0.127.b.barracudacentral.org: fqdn: 2.0.0.127.b.barracudacentral.org +ad: 0, rr: 2.0.0.127.b.barracudacentral.org. TTL IN A 127.0.0.D +./test_dns_lookup: lookup 2.0.0.127.bl.spamcop.net type A flags 2097152 +./test_dns_lookup: dns_query: 2.0.0.127.bl.spamcop.net (A): OK +./test_dns_lookup: dns_get_answer: type A for 2.0.0.127.bl.spamcop.net +2.0.0.127.bl.spamcop.net: fqdn: 2.0.0.127.bl.spamcop.net +ad: 0, rr: 2.0.0.127.bl.spamcop.net. TTL IN A 127.0.0.D diff --git a/postfix/src/dns/error.ref b/postfix/src/dns/error.ref index 2e0f9e67a..4b19ee499 100644 --- a/postfix/src/dns/error.ref +++ b/postfix/src/dns/error.ref @@ -10,4 +10,4 @@ ./test_dns_lookup: dict_regexp_lookup: error.reg: spike.porcupine.org. 3600 IN AAAA 2604:8d00:189::2 ./test_dns_lookup: maps_find: DNS reply filter: regexp:error.reg(0,lock|fold_fix): spike.porcupine.org. 3600 IN AAAA 2604:8d00:189::2 = oops ./test_dns_lookup: warning: DNS reply filter: unknown DNS filter action: "oops" -./test_dns_lookup: fatal: Error looking up name=spike.porcupine.org type=AAAA: Invalid DNS reply filter syntax (rcode=0) +./test_dns_lookup: warning: Error looking up name=spike.porcupine.org type=AAAA: Invalid DNS reply filter syntax (rcode=0) diff --git a/postfix/src/dns/no-mx.ref b/postfix/src/dns/no-mx.ref index 6b0085d6f..314541b6a 100644 --- a/postfix/src/dns/no-mx.ref +++ b/postfix/src/dns/no-mx.ref @@ -5,7 +5,6 @@ ./test_dns_lookup: dns_get_answer: type MX for porcupine.org ./test_dns_lookup: dns_get_answer: type MX for porcupine.org ./test_dns_lookup: dns_query: porcupine.org (MX): OK -./test_dns_lookup: fatal: Error looking up name=porcupine.org type=MX: DNS reply filter drops all results (rcode=0) ./test_dns_lookup: ignoring DNS RR: porcupine.org. 3600 IN MX 10 spike.porcupine.org. ./test_dns_lookup: ignoring DNS RR: porcupine.org. 3600 IN MX 20 fist.porcupine.org. ./test_dns_lookup: ignoring DNS RR: porcupine.org. 3600 IN MX 30 m1.porcupine.org. @@ -13,3 +12,4 @@ ./test_dns_lookup: maps_find: DNS reply filter: regexp:no-mx.reg(0,lock|fold_fix): porcupine.org. 3600 IN MX 10 spike.porcupine.org. = ignore ./test_dns_lookup: maps_find: DNS reply filter: regexp:no-mx.reg(0,lock|fold_fix): porcupine.org. 3600 IN MX 20 fist.porcupine.org. = ignore ./test_dns_lookup: maps_find: DNS reply filter: regexp:no-mx.reg(0,lock|fold_fix): porcupine.org. 3600 IN MX 30 m1.porcupine.org. = ignore +./test_dns_lookup: warning: Error looking up name=porcupine.org type=MX: DNS reply filter drops all results (rcode=0) diff --git a/postfix/src/dns/nxdomain_test.ref b/postfix/src/dns/nxdomain_test.ref index 3bf8aa166..aecbeb454 100644 --- a/postfix/src/dns/nxdomain_test.ref +++ b/postfix/src/dns/nxdomain_test.ref @@ -2,4 +2,4 @@ ./test_dns_lookup: dns_query: nxdomain.porcupine.org (MX): Host not found ./test_dns_lookup: lookup nxdomain.porcupine.org type A flags 2097152 ./test_dns_lookup: dns_query: nxdomain.porcupine.org (A): Host not found -./test_dns_lookup: fatal: Host or domain name not found. Name service error for name=nxdomain.porcupine.org type=A: Host not found (rcode=3) +./test_dns_lookup: warning: Host or domain name not found. Name service error for name=nxdomain.porcupine.org type=A: Host not found (rcode=3) diff --git a/postfix/src/dns/test_dns_lookup.c b/postfix/src/dns/test_dns_lookup.c index e27250b63..e927eda24 100644 --- a/postfix/src/dns/test_dns_lookup.c +++ b/postfix/src/dns/test_dns_lookup.c @@ -51,7 +51,7 @@ static void print_rr(VSTRING *buf, DNS_RR *rr) static NORETURN usage(char **argv) { - msg_fatal("usage: %s [-v] [-f filter] types name", argv[0]); + msg_fatal("usage: %s [-npv] [-f filter] types name", argv[0]); } int main(int argc, char **argv) @@ -66,15 +66,23 @@ int main(int argc, char **argv) DNS_RR *rr; int i; int ch; + int lflags = DNS_REQ_FLAG_NONE; msg_vstream_init(argv[0], VSTREAM_ERR); - while ((ch = GETOPT(argc, argv, "vf:")) > 0) { + while ((ch = GETOPT(argc, argv, "f:npv")) > 0) { switch (ch) { + case 'v': msg_verbose++; break; case 'f': dns_rr_filter_compile("DNS reply filter", optarg); break; + case 'n': + lflags |= DNS_REQ_FLAG_NCACHE_TTL; + break; + case 'p': + dns_ncache_ttl_fix_enable = 1; + break; default: usage(argv); } @@ -91,16 +99,18 @@ int main(int argc, char **argv) name = argv[optind + 1]; msg_verbose = 1; switch (dns_lookup_rv(name, RES_USE_DNSSEC, &rr, fqdn, why, - &rcode, DNS_REQ_FLAG_NONE, types)) { + &rcode, lflags, types)) { default: - msg_fatal("%s (rcode=%d)", vstring_str(why), rcode); + msg_warn("%s (rcode=%d)", vstring_str(why), rcode); case DNS_OK: - vstream_printf("%s: fqdn: %s\n", name, vstring_str(fqdn)); - buf = vstring_alloc(100); - print_rr(buf, rr); - dns_rr_free(rr); - vstring_free(buf); - vstream_fflush(VSTREAM_OUT); + if (rr) { + vstream_printf("%s: fqdn: %s\n", name, vstring_str(fqdn)); + buf = vstring_alloc(100); + print_rr(buf, rr); + dns_rr_free(rr); + vstring_free(buf); + vstream_fflush(VSTREAM_OUT); + } } myfree((void *) types); exit(0); diff --git a/postfix/src/dnsblog/dnsblog.c b/postfix/src/dnsblog/dnsblog.c index 6b5c46a7d..a9de032d7 100644 --- a/postfix/src/dnsblog/dnsblog.c +++ b/postfix/src/dnsblog/dnsblog.c @@ -18,8 +18,9 @@ /* If the IP address is listed under the DNS white/blacklist, the /* \fBdnsblog\fR(8) server logs the match and replies with the /* query arguments plus an address list with the resulting IP -/* addresses separated by whitespace. Otherwise it replies -/* with the query arguments plus an empty address list. Finally, +/* addresses, separated by whitespace, and the reply TTL. +/* Otherwise it replies with the query arguments plus an empty +/* address list and the reply TTL (-1 if unavailable). Finally, /* The \fBdnsblog\fR(8) server closes the connection. /* DIAGNOSTICS /* Problems and transactions are logged to \fBsyslogd\fR(8). @@ -78,6 +79,7 @@ /* System library. */ #include +#include /* Utility library. */ @@ -128,7 +130,8 @@ static VSTRING *result; /* static void dnsblog_query - query DNSBL for client address */ -static VSTRING *dnsblog_query(VSTRING *result, const char *dnsbl_domain, +static VSTRING *dnsblog_query(VSTRING *result, int *result_ttl, + const char *dnsbl_domain, const char *addr) { const char *myname = "dnsblog_query"; @@ -183,8 +186,16 @@ static VSTRING *dnsblog_query(VSTRING *result, const char *dnsbl_domain, * Tack on the RBL domain name and query the DNS for an A record. */ vstring_strcat(query, dnsbl_domain); - dns_status = dns_lookup(STR(query), T_A, 0, &addr_list, (VSTRING *) 0, why); + dns_status = dns_lookup_x(STR(query), T_A, 0, &addr_list, (VSTRING *) 0, + why, (int *) 0, DNS_REQ_FLAG_NCACHE_TTL); + + /* + * We return the lowest TTL in the response from the A record(s) if + * found, or from the SOA record(s) if available. If the reply specifies + * no TTL, or if the query fails, we return a TTL of -1. + */ VSTRING_RESET(result); + *result_ttl = -1; if (dns_status == DNS_OK) { for (rr = addr_list; rr != 0; rr = rr->next) { if (dns_rr_to_pa(rr, &hostaddr) == 0) { @@ -196,6 +207,9 @@ static VSTRING *dnsblog_query(VSTRING *result, const char *dnsbl_domain, if (LEN(result) > 0) vstring_strcat(result, " "); vstring_strcat(result, hostaddr.buf); + /* Grab the positive reply TTL. */ + if (*result_ttl < 0 || *result_ttl > rr->ttl) + *result_ttl = rr->ttl; } } dns_rr_free(addr_list); @@ -203,6 +217,12 @@ static VSTRING *dnsblog_query(VSTRING *result, const char *dnsbl_domain, if (msg_verbose) msg_info("%s: addr %s not listed by domain %s", myname, addr, dnsbl_domain); + /* Grab the negative reply TTL. */ + for (rr = addr_list; rr != 0; rr = rr->next) { + if (rr->type == T_SOA && (*result_ttl < 0 || *result_ttl > rr->ttl)) + *result_ttl = rr->ttl; + } + dns_rr_free(addr_list); } else { msg_warn("%s: lookup error for DNS query %s: %s", myname, STR(query), STR(why)); @@ -217,6 +237,7 @@ static void dnsblog_service(VSTREAM *client_stream, char *unused_service, char **argv) { int request_id; + int result_ttl; /* * Sanity check. This service takes no command-line arguments. @@ -235,7 +256,7 @@ static void dnsblog_service(VSTREAM *client_stream, char *unused_service, RECV_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, addr), RECV_ATTR_INT(MAIL_ATTR_LABEL, &request_id), ATTR_TYPE_END) == 3) { - (void) dnsblog_query(result, STR(rbl_domain), STR(addr)); + (void) dnsblog_query(result, &result_ttl, STR(rbl_domain), STR(addr)); if (var_dnsblog_delay > 0) sleep(var_dnsblog_delay); attr_print(client_stream, ATTR_FLAG_NONE, @@ -243,6 +264,7 @@ static void dnsblog_service(VSTREAM *client_stream, char *unused_service, SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, STR(addr)), SEND_ATTR_INT(MAIL_ATTR_LABEL, request_id), SEND_ATTR_STR(MAIL_ATTR_RBL_ADDR, STR(result)), + SEND_ATTR_INT(MAIL_ATTR_TTL, result_ttl), ATTR_TYPE_END); vstream_fflush(client_stream); } diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 823fc0c85..d1d339b4d 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -3396,6 +3396,11 @@ extern bool var_conc_feedback_debug; #define DEF_DEST_RATE_DELAY "0s" extern int var_dest_rate_delay; +#define VAR_XPORT_RATE_DELAY "default_transport_rate_delay" +#define _XPORT_RATE_DELAY "_transport_rate_delay" +#define DEF_XPORT_RATE_DELAY "0s" +extern int var_xport_rate_delay; + /* * Stress handling. */ @@ -3523,9 +3528,13 @@ extern char *var_psc_dnsbl_enable; #define DEF_PSC_DNSBL_ACTION "ignore" extern char *var_psc_dnsbl_action; -#define VAR_PSC_DNSBL_TTL "postscreen_dnsbl_ttl" -#define DEF_PSC_DNSBL_TTL "1h" -extern int var_psc_dnsbl_ttl; +#define VAR_PSC_DNSBL_MIN_TTL "postscreen_dnsbl_min_ttl" +#define DEF_PSC_DNSBL_MIN_TTL "60s" +extern int var_psc_dnsbl_min_ttl; + +#define VAR_PSC_DNSBL_MAX_TTL "postscreen_dnsbl_max_ttl" +#define DEF_PSC_DNSBL_MAX_TTL "${postscreen_dnsbl_ttl?{$postscreen_dnsbl_ttl}:{1}}h" +extern int var_psc_dnsbl_max_ttl; #define VAR_PSC_DNSBL_REPLY "postscreen_dnsbl_reply_map" #define DEF_PSC_DNSBL_REPLY "" diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index b663dfcda..2d6b52a03 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 "20150523" +#define MAIL_RELEASE_DATE "20150710" #define MAIL_VERSION_NUMBER "3.1" #ifdef SNAPSHOT diff --git a/postfix/src/oqmgr/qmgr.c b/postfix/src/oqmgr/qmgr.c index b806df3dc..b291b8690 100644 --- a/postfix/src/oqmgr/qmgr.c +++ b/postfix/src/oqmgr/qmgr.c @@ -255,6 +255,14 @@ /* on the value of the corresponding per-destination recipient limit. /* .IP "\fItransport\fB_destination_rate_delay $default_destination_rate_delay /* Idem, for delivery via the named message \fItransport\fR. +/* .PP +/* Available in Postfix version 3.1 and later: +/* .IP "\fBdefault_transport_rate_delay (0s)\fR" +/* The default amount of delay that is inserted between individual +/* deliveries over the same message delivery transport, regardless of +/* destination. +/* .IP "\fItransport\fB_transport_rate_delay $default_transport_rate_delay +/* Idem, for delivery via the named message \fItransport\fR. /* SAFETY CONTROLS /* .ad /* .fi @@ -386,6 +394,7 @@ char *var_conc_pos_feedback; char *var_conc_neg_feedback; int var_conc_cohort_limit; int var_conc_feedback_debug; +int var_xport_rate_delay; int var_dest_rate_delay; char *var_def_filter_nexthop; int var_qmgr_daemon_timeout; @@ -634,6 +643,7 @@ int main(int argc, char **argv) VAR_DSN_QUEUE_TIME, DEF_DSN_QUEUE_TIME, &var_dsn_queue_time, 0, 8640000, VAR_XPORT_RETRY_TIME, DEF_XPORT_RETRY_TIME, &var_transport_retry_time, 1, 0, VAR_QMGR_CLOG_WARN_TIME, DEF_QMGR_CLOG_WARN_TIME, &var_qmgr_clog_warn_time, 0, 0, + VAR_XPORT_RATE_DELAY, DEF_XPORT_RATE_DELAY, &var_xport_rate_delay, 0, 0, VAR_DEST_RATE_DELAY, DEF_DEST_RATE_DELAY, &var_dest_rate_delay, 0, 0, VAR_QMGR_DAEMON_TIMEOUT, DEF_QMGR_DAEMON_TIMEOUT, &var_qmgr_daemon_timeout, 1, 0, VAR_QMGR_IPC_TIMEOUT, DEF_QMGR_IPC_TIMEOUT, &var_qmgr_ipc_timeout, 1, 0, diff --git a/postfix/src/oqmgr/qmgr.h b/postfix/src/oqmgr/qmgr.h index 6d61eb45b..b8c8d0e48 100644 --- a/postfix/src/oqmgr/qmgr.h +++ b/postfix/src/oqmgr/qmgr.h @@ -163,10 +163,12 @@ struct QMGR_TRANSPORT { QMGR_FEEDBACK pos_feedback; /* positive feedback control */ QMGR_FEEDBACK neg_feedback; /* negative feedback control */ int fail_cohort_limit; /* flow shutdown control */ + int xport_rate_delay; /* suspend per delivery */ int rate_delay; /* suspend per delivery */ }; #define QMGR_TRANSPORT_STAT_DEAD (1<<1) +#define QMGR_TRANSPORT_STAT_RATE_LOCK (1<<2) typedef void (*QMGR_TRANSPORT_ALLOC_NOTIFY) (QMGR_TRANSPORT *, VSTREAM *); extern QMGR_TRANSPORT *qmgr_transport_select(void); diff --git a/postfix/src/oqmgr/qmgr_deliver.c b/postfix/src/oqmgr/qmgr_deliver.c index cd6f35a1d..02db6b93a 100644 --- a/postfix/src/oqmgr/qmgr_deliver.c +++ b/postfix/src/oqmgr/qmgr_deliver.c @@ -77,6 +77,14 @@ #include "qmgr.h" + /* + * Important note on the _transport_rate_delay implementation: after + * qmgr_transport_alloc() sets the QMGR_TRANSPORT_STAT_RATE_LOCK flag, all + * code paths must directly or indirectly invoke qmgr_transport_unthrottle() + * or qmgr_transport_throttle(). Otherwise, transports with non-zero + * _transport_rate_delay will become stuck. + */ + int qmgr_deliver_concurrency; /* @@ -341,9 +349,10 @@ static void qmgr_deliver_update(int unused_event, void *context) * No problems detected. Mark the transport and queue as alive. The queue * itself won't go away before we dispose of the current queue entry. */ - if (status != DELIVER_STAT_CRASH && VSTRING_LEN(dsb->reason) == 0) { + if (status != DELIVER_STAT_CRASH) { qmgr_transport_unthrottle(transport); - qmgr_queue_unthrottle(queue); + if (VSTRING_LEN(dsb->reason) == 0) + qmgr_queue_unthrottle(queue); } /* diff --git a/postfix/src/oqmgr/qmgr_transport.c b/postfix/src/oqmgr/qmgr_transport.c index 58a644c03..cb26453f0 100644 --- a/postfix/src/oqmgr/qmgr_transport.c +++ b/postfix/src/oqmgr/qmgr_transport.c @@ -158,6 +158,14 @@ struct QMGR_TRANSPORT_ALLOC { #define QMGR_TRANSPORT_MAX_PEND 2 #endif + /* + * Important note on the _transport_rate_delay implementation: after + * qmgr_transport_alloc() sets the QMGR_TRANSPORT_STAT_RATE_LOCK flag, all + * code paths must directly or indirectly invoke qmgr_transport_unthrottle() + * or qmgr_transport_throttle(). Otherwise, transports with non-zero + * _transport_rate_delay will become stuck. + */ + /* qmgr_transport_unthrottle_wrapper - in case (char *) != (struct *) */ static void qmgr_transport_unthrottle_wrapper(int unused_event, void *context) @@ -175,7 +183,7 @@ void qmgr_transport_unthrottle(QMGR_TRANSPORT *transport) * This routine runs after expiration of the timer set by * qmgr_transport_throttle(), or whenever a delivery transport has been * used without malfunction. In either case, we enable delivery again if - * the transport was blocked, otherwise the request is ignored. + * the transport was throttled. We always reset the transport rate lock. */ if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0) { if (msg_verbose) @@ -189,6 +197,8 @@ void qmgr_transport_unthrottle(QMGR_TRANSPORT *transport) event_cancel_timer(qmgr_transport_unthrottle_wrapper, (void *) transport); } + if (transport->flags & QMGR_TRANSPORT_STAT_RATE_LOCK) + transport->flags &= ~QMGR_TRANSPORT_STAT_RATE_LOCK; } /* qmgr_transport_throttle - disable delivery process allocation */ @@ -225,6 +235,16 @@ static void qmgr_transport_abort(int unused_event, void *context) msg_fatal("timeout connecting to transport: %s", alloc->transport->name); } +/* qmgr_transport_rate_event - delivery process availability notice */ + +static void qmgr_transport_rate_event(int unused_event, void *context) +{ + QMGR_TRANSPORT_ALLOC *alloc = (QMGR_TRANSPORT_ALLOC *) context; + + alloc->notify(alloc->transport, alloc->stream); + myfree((void *) alloc); +} + /* qmgr_transport_event - delivery process availability notice */ static void qmgr_transport_event(int unused_event, void *context) @@ -256,8 +276,16 @@ static void qmgr_transport_event(int unused_event, void *context) /* * Notify the requestor. */ - alloc->notify(alloc->transport, alloc->stream); - myfree((void *) alloc); + if (alloc->transport->xport_rate_delay > 0) { + if ((alloc->transport->flags & QMGR_TRANSPORT_STAT_RATE_LOCK) == 0) + msg_panic("transport_event: missing rate lock for transport %s", + alloc->transport->name); + event_request_timer(qmgr_transport_rate_event, (void *) alloc, + alloc->transport->xport_rate_delay); + } else { + alloc->notify(alloc->transport, alloc->stream); + myfree((void *) alloc); + } } /* qmgr_transport_select - select transport for allocation */ @@ -282,6 +310,7 @@ QMGR_TRANSPORT *qmgr_transport_select(void) for (xport = qmgr_transport_list.next; xport; xport = xport->peers.next) { if ((xport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0 + || (xport->flags & QMGR_TRANSPORT_STAT_RATE_LOCK) != 0 || xport->pending >= QMGR_TRANSPORT_MAX_PEND) continue; need = xport->pending + 1; @@ -311,9 +340,18 @@ void qmgr_transport_alloc(QMGR_TRANSPORT *transport, QMGR_TRANSPORT_ALLOC_NOT */ if (transport->flags & QMGR_TRANSPORT_STAT_DEAD) msg_panic("qmgr_transport: dead transport: %s", transport->name); + if (transport->flags & QMGR_TRANSPORT_STAT_RATE_LOCK) + msg_panic("qmgr_transport: rate-locked transport: %s", transport->name); if (transport->pending >= QMGR_TRANSPORT_MAX_PEND) msg_panic("qmgr_transport: excess allocation: %s", transport->name); + /* + * When this message delivery transport is rate-limited, do not select it + * again before the end of a message delivery transaction. + */ + if (transport->xport_rate_delay > 0) + transport->flags |= QMGR_TRANSPORT_STAT_RATE_LOCK; + /* * Connect to the well-known port for this delivery service, and wake up * when a process announces its availability. Allow only a limited number @@ -387,6 +425,9 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name) transport->init_dest_concurrency = get_mail_conf_int2(name, _INIT_DEST_CON, var_init_dest_concurrency, 1, 0); + transport->xport_rate_delay = get_mail_conf_time2(name, _XPORT_RATE_DELAY, + var_xport_rate_delay, + 's', 0, 0); transport->rate_delay = get_mail_conf_time2(name, _DEST_RATE_DELAY, var_dest_rate_delay, 's', 0, 0); diff --git a/postfix/src/postconf/postconf_builtin.c b/postfix/src/postconf/postconf_builtin.c index a966ebaec..9b06c25f0 100644 --- a/postfix/src/postconf/postconf_builtin.c +++ b/postfix/src/postconf/postconf_builtin.c @@ -136,6 +136,7 @@ static const CONFIG_STR_TABLE pcf_legacy_str_table[] = { {"fallback_relay", ""}, {"authorized_verp_clients", ""}, {"smtpd_client_connection_limit_exceptions", ""}, + {"postscreen_dnsbl_ttl", ""}, 0, }; diff --git a/postfix/src/postconf/postconf_service.c b/postfix/src/postconf/postconf_service.c index bebf6de6b..9322c093a 100644 --- a/postfix/src/postconf/postconf_service.c +++ b/postfix/src/postconf/postconf_service.c @@ -134,6 +134,7 @@ void pcf_register_service_parameters(void) _CONC_NEG_FDBACK, VAR_CONC_NEG_FDBACK, _CONC_COHORT_LIM, VAR_CONC_COHORT_LIM, _DEST_RATE_DELAY, VAR_DEST_RATE_DELAY, + _XPORT_RATE_DELAY, VAR_XPORT_RATE_DELAY, 0, }; static const PCF_STRING_NV spawn_params[] = { diff --git a/postfix/src/postscreen/postscreen.c b/postfix/src/postscreen/postscreen.c index 95c70b44e..923cbff04 100644 --- a/postfix/src/postscreen/postscreen.c +++ b/postfix/src/postscreen/postscreen.c @@ -275,9 +275,14 @@ /* .IP "\fBpostscreen_bare_newline_ttl (30d)\fR" /* The amount of time that \fBpostscreen\fR(8) will use the result from /* a successful "bare newline" SMTP protocol test. -/* .IP "\fBpostscreen_dnsbl_ttl (1h)\fR" -/* The amount of time that \fBpostscreen\fR(8) will use the result from -/* a successful DNS blocklist test. +/* .IP "\fBpostscreen_dnsbl_max_ttl (${postscreen_dnsbl_ttl?{$postscreen_dnsbl_ttl}:{1}}h)\fR" +/* The maximum amount of time that \fBpostscreen\fR(8) will use the +/* result from a successful DNS-based reputation test before a +/* client IP address is required to pass that test again. +/* .IP "\fBpostscreen_dnsbl_min_ttl (60s)\fR" +/* The minimum amount of time that \fBpostscreen\fR(8) will use the +/* result from a successful DNS-based reputation test before a +/* client IP address is required to pass that test again. /* .IP "\fBpostscreen_greet_ttl (1d)\fR" /* The amount of time that \fBpostscreen\fR(8) will use the result from /* a successful PREGREET test. @@ -476,7 +481,8 @@ char *var_psc_dnsbl_reply; int var_psc_dnsbl_thresh; int var_psc_dnsbl_wthresh; char *var_psc_dnsbl_action; -int var_psc_dnsbl_ttl; +int var_psc_dnsbl_min_ttl; +int var_psc_dnsbl_max_ttl; int var_psc_dnsbl_tmout; bool var_psc_pipel_enable; @@ -524,7 +530,6 @@ int psc_pipel_action; /* PSC_ACT_DROP/ENFORCE/etc */ int psc_nsmtp_action; /* PSC_ACT_DROP/ENFORCE/etc */ int psc_barlf_action; /* PSC_ACT_DROP/ENFORCE/etc */ int psc_min_ttl; /* Update with new tests! */ -int psc_max_ttl; /* Update with new tests! */ STRING_LIST *psc_forbid_cmds; /* CONNECT GET POST */ int psc_stress_greet_wait; /* stressed greet wait */ int psc_normal_greet_wait; /* stressed greet wait */ @@ -1022,13 +1027,9 @@ static void post_jail_init(char *unused_name, char **unused_argv) * Pre-compute the minimal and maximal TTL. */ psc_min_ttl = - PSC_MIN(PSC_MIN(var_psc_pregr_ttl, var_psc_dnsbl_ttl), + PSC_MIN(PSC_MIN(var_psc_pregr_ttl, var_psc_dnsbl_min_ttl), PSC_MIN(PSC_MIN(var_psc_pipel_ttl, var_psc_nsmtp_ttl), var_psc_barlf_ttl)); - psc_max_ttl = - PSC_MAX(PSC_MAX(var_psc_pregr_ttl, var_psc_dnsbl_ttl), - PSC_MAX(PSC_MAX(var_psc_pipel_ttl, var_psc_nsmtp_ttl), - var_psc_barlf_ttl)); /* * Pre-compute the stress and normal command time limits. @@ -1122,7 +1123,8 @@ int main(int argc, char **argv) static const CONFIG_TIME_TABLE time_table[] = { VAR_PSC_GREET_WAIT, DEF_PSC_GREET_WAIT, &var_psc_greet_wait, 1, 0, VAR_PSC_PREGR_TTL, DEF_PSC_PREGR_TTL, &var_psc_pregr_ttl, 1, 0, - VAR_PSC_DNSBL_TTL, DEF_PSC_DNSBL_TTL, &var_psc_dnsbl_ttl, 1, 0, + VAR_PSC_DNSBL_MIN_TTL, DEF_PSC_DNSBL_MIN_TTL, &var_psc_dnsbl_min_ttl, 1, 0, + VAR_PSC_DNSBL_MAX_TTL, DEF_PSC_DNSBL_MAX_TTL, &var_psc_dnsbl_max_ttl, 1, 0, VAR_PSC_PIPEL_TTL, DEF_PSC_PIPEL_TTL, &var_psc_pipel_ttl, 1, 0, VAR_PSC_NSMTP_TTL, DEF_PSC_NSMTP_TTL, &var_psc_nsmtp_ttl, 1, 0, VAR_PSC_BARLF_TTL, DEF_PSC_BARLF_TTL, &var_psc_barlf_ttl, 1, 0, diff --git a/postfix/src/postscreen/postscreen.h b/postfix/src/postscreen/postscreen.h index 130b52770..ceb2a2571 100644 --- a/postfix/src/postscreen/postscreen.h +++ b/postfix/src/postscreen/postscreen.h @@ -75,6 +75,7 @@ typedef struct { time_t expire_time[PSC_TINDX_COUNT]; /* per-test expiration */ VSTRING *dnsbl_reply; /* dnsbl reject text */ int dnsbl_score; /* saved DNSBL score */ + int dnsbl_ttl; /* saved DNSBL TTL */ const char *dnsbl_name; /* DNSBL name with largest weight */ int dnsbl_index; /* dnsbl request index */ const char *rcpt_reply; /* how to reject recipients */ @@ -372,7 +373,6 @@ extern int psc_pipel_action; /* PSC_ACT_DROP etc. */ extern int psc_nsmtp_action; /* PSC_ACT_DROP etc. */ extern int psc_barlf_action; /* PSC_ACT_DROP etc. */ extern int psc_min_ttl; /* Update with new tests! */ -extern int psc_max_ttl; /* Update with new tests! */ extern STRING_LIST *psc_forbid_cmds; /* CONNECT GET POST */ extern int psc_stress_greet_wait; /* stressed greet wait */ extern int psc_normal_greet_wait; /* stressed greet wait */ @@ -480,7 +480,7 @@ const char *psc_maps_find(MAPS *, const char *, int); * postscreen_dnsbl.c */ extern void psc_dnsbl_init(void); -extern int psc_dnsbl_retrieve(const char *, const char **, int); +extern int psc_dnsbl_retrieve(const char *, const char **, int, int *); extern int psc_dnsbl_request(const char *, void (*) (int, void *), void *); /* diff --git a/postfix/src/postscreen/postscreen_dnsbl.c b/postfix/src/postscreen/postscreen_dnsbl.c index 76c21ed0d..34d586202 100644 --- a/postfix/src/postscreen/postscreen_dnsbl.c +++ b/postfix/src/postscreen/postscreen_dnsbl.c @@ -13,10 +13,12 @@ /* void (*callback)(int, char *); /* char *context; /* -/* int psc_dnsbl_retrieve(client_addr, dnsbl_name, dnsbl_index) +/* int psc_dnsbl_retrieve(client_addr, dnsbl_name, dnsbl_index, +/* dnsbl_ttl) /* char *client_addr; /* const char **dnsbl_name; /* int dnsbl_index; +/* int *dnsbl_ttl; /* DESCRIPTION /* This module implements preliminary support for DNSBL lookups. /* Multiple requests for the same information are handled with @@ -37,8 +39,10 @@ /* The result value is the index for the psc_dnsbl_retrieve() /* call. /* -/* psc_dnsbl_retrieve() retrieves the result score requested with -/* psc_dnsbl_request() and decrements the reference count. It +/* psc_dnsbl_retrieve() retrieves the result score and reply +/* TTL requested with psc_dnsbl_request(), and decrements the +/* reference count. The reply TTL value is clamped to +/* postscreen_dnsbl_min_ttl and postscreen_dnsbl_max_ttl. It /* is an error to retrieve a score without requesting it first. /* LICENSE /* .ad @@ -58,6 +62,7 @@ #include /* inet_pton() */ #include /* inet_pton() */ #include /* sscanf */ +#include /* Utility library. */ @@ -140,7 +145,9 @@ typedef struct { typedef struct { const char *dnsbl_name; /* DNSBL with largest contribution */ int dnsbl_weight; /* weight of largest contribution */ - int total; /* combined blocklist score */ + int total; /* combined white+blocklist score */ + int fail_ttl; /* combined reply TTL */ + int pass_ttl; /* combined reply TTL */ int refcount; /* score reference count */ int pending_lookups; /* nr of DNS requests in flight */ int request_id; /* duplicate suppression */ @@ -306,11 +313,12 @@ static int psc_dnsbl_match(const char *filter, ARGV *reply) /* psc_dnsbl_retrieve - retrieve blocklist score, decrement reference count */ int psc_dnsbl_retrieve(const char *client_addr, const char **dnsbl_name, - int dnsbl_index) + int dnsbl_index, int *dnsbl_ttl) { const char *myname = "psc_dnsbl_retrieve"; PSC_DNSBL_SCORE *score; int result_score; + int result_ttl; /* * Sanity check. @@ -329,6 +337,16 @@ int psc_dnsbl_retrieve(const char *client_addr, const char **dnsbl_name, */ result_score = score->total; *dnsbl_name = score->dnsbl_name; + result_ttl = (result_score > 0) ? score->fail_ttl : score->pass_ttl; + /* As with dnsblog(8), a value < 0 means no reply TTL. */ + if (result_ttl < var_psc_dnsbl_min_ttl) + result_ttl = var_psc_dnsbl_min_ttl; + if (result_ttl > var_psc_dnsbl_max_ttl) + result_ttl = var_psc_dnsbl_max_ttl; + *dnsbl_ttl = result_ttl; + if (msg_verbose) + msg_info("%s: addr=%s score=%d ttl=%d", + myname, client_addr, result_score, result_ttl); score->refcount -= 1; if (score->refcount < 1) { if (msg_verbose > 1) @@ -349,6 +367,7 @@ static void psc_dnsbl_receive(int event, void *context) PSC_DNSBL_SITE *site; ARGV *reply_argv; int request_id; + int dnsbl_ttl; PSC_CLEAR_EVENT_REQUEST(vstream_fileno(stream), psc_dnsbl_receive, context); @@ -374,7 +393,8 @@ static void psc_dnsbl_receive(int event, void *context) RECV_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, reply_client), RECV_ATTR_INT(MAIL_ATTR_LABEL, &request_id), RECV_ATTR_STR(MAIL_ATTR_RBL_ADDR, reply_addr), - ATTR_TYPE_END) == 4 + RECV_ATTR_INT(MAIL_ATTR_TTL, &dnsbl_ttl), + ATTR_TYPE_END) == 5 && (score = (PSC_DNSBL_SCORE *) htable_find(dnsbl_score_cache, STR(reply_client))) != 0 && score->request_id == request_id) { @@ -387,14 +407,17 @@ static void psc_dnsbl_receive(int event, void *context) * server may be messed up. */ if (msg_verbose > 1) - msg_info("%s: client=\"%s\" score=%d domain=\"%s\" reply=\"%s\"", + msg_info("%s: client=\"%s\" score=%d domain=\"%s\" reply=\"%d %s\"", myname, STR(reply_client), score->total, - STR(reply_dnsbl), STR(reply_addr)); - if (*STR(reply_addr) != 0) { - head = (PSC_DNSBL_HEAD *) - htable_find(dnsbl_site_cache, STR(reply_dnsbl)); - site = (head ? head->first : (PSC_DNSBL_SITE *) 0); - for (reply_argv = 0; site != 0; site = site->next) { + STR(reply_dnsbl), dnsbl_ttl, STR(reply_addr)); + head = (PSC_DNSBL_HEAD *) + htable_find(dnsbl_site_cache, STR(reply_dnsbl)); + if (head == 0) { + /* Bogus domain. Do nothing. */ + } else if (*STR(reply_addr) != 0) { + /* DNS reputation record(s) found. */ + reply_argv = 0; + for (site = head->first; site != 0; site = site->next) { if (site->byte_codes == 0 || psc_dnsbl_match(site->byte_codes, reply_argv ? reply_argv : (reply_argv = argv_split(STR(reply_addr), " ")))) { @@ -409,9 +432,29 @@ static void psc_dnsbl_receive(int event, void *context) myname, site->filter ? site->filter : "null", site->weight, score->total); } + /* As with dnsblog(8), a value < 0 means no reply TTL. */ + if (site->weight > 0) { + if (score->fail_ttl < 0 || score->fail_ttl > dnsbl_ttl) + score->fail_ttl = dnsbl_ttl; + } else { + if (score->pass_ttl < 0 || score->pass_ttl > dnsbl_ttl) + score->pass_ttl = dnsbl_ttl; + } } if (reply_argv != 0) argv_free(reply_argv); + } else { + /* No DNS reputation record found. */ + for (site = head->first; site != 0; site = site->next) { + /* As with dnsblog(8), a value < 0 means no reply TTL. */ + if (site->weight > 0) { + if (score->pass_ttl < 0 || score->pass_ttl > dnsbl_ttl) + score->pass_ttl = dnsbl_ttl; + } else { + if (score->fail_ttl < 0 || score->fail_ttl > dnsbl_ttl) + score->fail_ttl = dnsbl_ttl; + } + } } /* @@ -485,6 +528,9 @@ int psc_dnsbl_request(const char *client_addr, score->request_id = request_count++; score->dnsbl_name = 0; score->dnsbl_weight = 0; + /* As with dnsblog(8), a value < 0 means no reply TTL. */ + score->pass_ttl = -1; + score->fail_ttl = -1; score->total = 0; score->refcount = 1; score->pending_lookups = 0; diff --git a/postfix/src/postscreen/postscreen_early.c b/postfix/src/postscreen/postscreen_early.c index e46487bcb..c4e1a80d7 100644 --- a/postfix/src/postscreen/postscreen_early.c +++ b/postfix/src/postscreen/postscreen_early.c @@ -87,8 +87,8 @@ static void psc_whitelist_non_dnsbl(PSC_STATE *state) state->flags |= PSC_STATE_FLAG_BYTINDX_PASS(tindx); } /* Update expiration even if the test was completed or disabled. */ - if (state->expire_time[tindx] < now + var_psc_dnsbl_ttl) - state->expire_time[tindx] = now + var_psc_dnsbl_ttl; + if (state->expire_time[tindx] < now + state->dnsbl_ttl) + state->expire_time[tindx] = now + state->dnsbl_ttl; } } } @@ -164,12 +164,13 @@ static void psc_early_event(int event, void *context) state->dnsbl_score = psc_dnsbl_retrieve(state->smtp_client_addr, &state->dnsbl_name, - state->dnsbl_index); + state->dnsbl_index, + &state->dnsbl_ttl); if (var_psc_dnsbl_wthresh < 0) psc_whitelist_non_dnsbl(state); } if (state->dnsbl_score < var_psc_dnsbl_thresh) { - state->dnsbl_stamp = event_time() + var_psc_dnsbl_ttl; + state->dnsbl_stamp = event_time() + state->dnsbl_ttl; PSC_PASS_SESSION_STATE(state, "dnsbl test", PSC_STATE_FLAG_DNSBL_PASS); } else { @@ -228,7 +229,8 @@ static void psc_early_event(int event, void *context) && (state->flags & PSC_STATE_FLAG_DNSBL_TODO)) (void) psc_dnsbl_retrieve(state->smtp_client_addr, &state->dnsbl_name, - state->dnsbl_index); + state->dnsbl_index, + &state->dnsbl_ttl); /* XXX Wait for DNS replies to come in. */ psc_hangup_event(state); return; @@ -246,7 +248,8 @@ static void psc_early_event(int event, void *context) && (state->flags & PSC_STATE_FLAG_DNSBL_TODO)) (void) psc_dnsbl_retrieve(state->smtp_client_addr, &state->dnsbl_name, - state->dnsbl_index); + state->dnsbl_index, + &state->dnsbl_ttl); PSC_DROP_SESSION_STATE(state, "521 5.5.1 Protocol error\r\n"); return; case PSC_ACT_ENFORCE: @@ -298,7 +301,7 @@ static void psc_early_dnsbl_event(int unused_event, void *context) */ state->dnsbl_score = psc_dnsbl_retrieve(state->smtp_client_addr, &state->dnsbl_name, - state->dnsbl_index); + state->dnsbl_index, &state->dnsbl_ttl); if (var_psc_dnsbl_wthresh < 0) psc_whitelist_non_dnsbl(state); diff --git a/postfix/src/qmgr/qmgr.c b/postfix/src/qmgr/qmgr.c index 1b44ef1b1..25359df9d 100644 --- a/postfix/src/qmgr/qmgr.c +++ b/postfix/src/qmgr/qmgr.c @@ -301,6 +301,14 @@ /* on the value of the corresponding per-destination recipient limit. /* .IP "\fItransport\fB_destination_rate_delay $default_destination_rate_delay /* Idem, for delivery via the named message \fItransport\fR. +/* .PP +/* Available in Postfix version 3.1 and later: +/* .IP "\fBdefault_transport_rate_delay (0s)\fR" +/* The default amount of delay that is inserted between individual +/* deliveries over the same message delivery transport, regardless of +/* destination. +/* .IP "\fItransport\fB_transport_rate_delay $default_transport_rate_delay +/* Idem, for delivery via the named message \fItransport\fR. /* SAFETY CONTROLS /* .ad /* .fi @@ -446,6 +454,7 @@ char *var_conc_pos_feedback; char *var_conc_neg_feedback; int var_conc_cohort_limit; int var_conc_feedback_debug; +int var_xport_rate_delay; int var_dest_rate_delay; char *var_def_filter_nexthop; int var_qmgr_daemon_timeout; @@ -702,6 +711,7 @@ int main(int argc, char **argv) VAR_XPORT_RETRY_TIME, DEF_XPORT_RETRY_TIME, &var_transport_retry_time, 1, 0, VAR_QMGR_CLOG_WARN_TIME, DEF_QMGR_CLOG_WARN_TIME, &var_qmgr_clog_warn_time, 0, 0, VAR_XPORT_REFILL_DELAY, DEF_XPORT_REFILL_DELAY, &var_xport_refill_delay, 1, 0, + VAR_XPORT_RATE_DELAY, DEF_XPORT_RATE_DELAY, &var_xport_rate_delay, 0, 0, VAR_DEST_RATE_DELAY, DEF_DEST_RATE_DELAY, &var_dest_rate_delay, 0, 0, VAR_QMGR_DAEMON_TIMEOUT, DEF_QMGR_DAEMON_TIMEOUT, &var_qmgr_daemon_timeout, 1, 0, VAR_QMGR_IPC_TIMEOUT, DEF_QMGR_IPC_TIMEOUT, &var_qmgr_ipc_timeout, 1, 0, diff --git a/postfix/src/qmgr/qmgr.h b/postfix/src/qmgr/qmgr.h index d5f48453d..88ce7920e 100644 --- a/postfix/src/qmgr/qmgr.h +++ b/postfix/src/qmgr/qmgr.h @@ -204,10 +204,12 @@ struct QMGR_TRANSPORT { QMGR_FEEDBACK pos_feedback; /* positive feedback control */ QMGR_FEEDBACK neg_feedback; /* negative feedback control */ int fail_cohort_limit; /* flow shutdown control */ + int xport_rate_delay; /* suspend per delivery */ int rate_delay; /* suspend per delivery */ }; #define QMGR_TRANSPORT_STAT_DEAD (1<<1) +#define QMGR_TRANSPORT_STAT_RATE_LOCK (1<<2) typedef void (*QMGR_TRANSPORT_ALLOC_NOTIFY) (QMGR_TRANSPORT *, VSTREAM *); extern QMGR_TRANSPORT *qmgr_transport_select(void); diff --git a/postfix/src/qmgr/qmgr_deliver.c b/postfix/src/qmgr/qmgr_deliver.c index 2c7ca7f8d..575f75912 100644 --- a/postfix/src/qmgr/qmgr_deliver.c +++ b/postfix/src/qmgr/qmgr_deliver.c @@ -82,6 +82,14 @@ #include "qmgr.h" + /* + * Important note on the _transport_rate_delay implementation: after + * qmgr_transport_alloc() sets the QMGR_TRANSPORT_STAT_RATE_LOCK flag, all + * code paths must directly or indirectly invoke qmgr_transport_unthrottle() + * or qmgr_transport_throttle(). Otherwise, transports with non-zero + * _transport_rate_delay will become stuck. + */ + int qmgr_deliver_concurrency; /* @@ -346,9 +354,10 @@ static void qmgr_deliver_update(int unused_event, void *context) * No problems detected. Mark the transport and queue as alive. The queue * itself won't go away before we dispose of the current queue entry. */ - if (status != DELIVER_STAT_CRASH && VSTRING_LEN(dsb->reason) == 0) { + if (status != DELIVER_STAT_CRASH) { qmgr_transport_unthrottle(transport); - qmgr_queue_unthrottle(queue); + if (VSTRING_LEN(dsb->reason) == 0) + qmgr_queue_unthrottle(queue); } /* diff --git a/postfix/src/qmgr/qmgr_transport.c b/postfix/src/qmgr/qmgr_transport.c index 0611f3b22..a5089f493 100644 --- a/postfix/src/qmgr/qmgr_transport.c +++ b/postfix/src/qmgr/qmgr_transport.c @@ -163,6 +163,14 @@ struct QMGR_TRANSPORT_ALLOC { #define QMGR_TRANSPORT_MAX_PEND 2 #endif + /* + * Important note on the _transport_rate_delay implementation: after + * qmgr_transport_alloc() sets the QMGR_TRANSPORT_STAT_RATE_LOCK flag, all + * code paths must directly or indirectly invoke qmgr_transport_unthrottle() + * or qmgr_transport_throttle(). Otherwise, transports with non-zero + * _transport_rate_delay will become stuck. + */ + /* qmgr_transport_unthrottle_wrapper - in case (char *) != (struct *) */ static void qmgr_transport_unthrottle_wrapper(int unused_event, void *context) @@ -180,7 +188,7 @@ void qmgr_transport_unthrottle(QMGR_TRANSPORT *transport) * This routine runs after expiration of the timer set by * qmgr_transport_throttle(), or whenever a delivery transport has been * used without malfunction. In either case, we enable delivery again if - * the transport was blocked, otherwise the request is ignored. + * the transport was throttled. We always reset the transport rate lock. */ if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0) { if (msg_verbose) @@ -194,6 +202,8 @@ void qmgr_transport_unthrottle(QMGR_TRANSPORT *transport) event_cancel_timer(qmgr_transport_unthrottle_wrapper, (void *) transport); } + if (transport->flags & QMGR_TRANSPORT_STAT_RATE_LOCK) + transport->flags &= ~QMGR_TRANSPORT_STAT_RATE_LOCK; } /* qmgr_transport_throttle - disable delivery process allocation */ @@ -230,6 +240,16 @@ static void qmgr_transport_abort(int unused_event, void *context) msg_fatal("timeout connecting to transport: %s", alloc->transport->name); } +/* qmgr_transport_rate_event - delivery process availability notice */ + +static void qmgr_transport_rate_event(int unused_event, void *context) +{ + QMGR_TRANSPORT_ALLOC *alloc = (QMGR_TRANSPORT_ALLOC *) context; + + alloc->notify(alloc->transport, alloc->stream); + myfree((void *) alloc); +} + /* qmgr_transport_event - delivery process availability notice */ static void qmgr_transport_event(int unused_event, void *context) @@ -261,8 +281,16 @@ static void qmgr_transport_event(int unused_event, void *context) /* * Notify the requestor. */ - alloc->notify(alloc->transport, alloc->stream); - myfree((void *) alloc); + if (alloc->transport->xport_rate_delay > 0) { + if ((alloc->transport->flags & QMGR_TRANSPORT_STAT_RATE_LOCK) == 0) + msg_panic("transport_event: missing rate lock for transport %s", + alloc->transport->name); + event_request_timer(qmgr_transport_rate_event, (void *) alloc, + alloc->transport->xport_rate_delay); + } else { + alloc->notify(alloc->transport, alloc->stream); + myfree((void *) alloc); + } } /* qmgr_transport_select - select transport for allocation */ @@ -287,6 +315,7 @@ QMGR_TRANSPORT *qmgr_transport_select(void) for (xport = qmgr_transport_list.next; xport; xport = xport->peers.next) { if ((xport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0 + || (xport->flags & QMGR_TRANSPORT_STAT_RATE_LOCK) != 0 || xport->pending >= QMGR_TRANSPORT_MAX_PEND) continue; need = xport->pending + 1; @@ -316,9 +345,18 @@ void qmgr_transport_alloc(QMGR_TRANSPORT *transport, QMGR_TRANSPORT_ALLOC_NOT */ if (transport->flags & QMGR_TRANSPORT_STAT_DEAD) msg_panic("qmgr_transport: dead transport: %s", transport->name); + if (transport->flags & QMGR_TRANSPORT_STAT_RATE_LOCK) + msg_panic("qmgr_transport: rate-locked transport: %s", transport->name); if (transport->pending >= QMGR_TRANSPORT_MAX_PEND) msg_panic("qmgr_transport: excess allocation: %s", transport->name); + /* + * When this message delivery transport is rate-limited, do not select it + * again before the end of a message delivery transaction. + */ + if (transport->xport_rate_delay > 0) + transport->flags |= QMGR_TRANSPORT_STAT_RATE_LOCK; + /* * Connect to the well-known port for this delivery service, and wake up * when a process announces its availability. Allow only a limited number @@ -392,6 +430,9 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name) transport->init_dest_concurrency = get_mail_conf_int2(name, _INIT_DEST_CON, var_init_dest_concurrency, 1, 0); + transport->xport_rate_delay = get_mail_conf_time2(name, _XPORT_RATE_DELAY, + var_xport_rate_delay, + 's', 0, 0); transport->rate_delay = get_mail_conf_time2(name, _DEST_RATE_DELAY, var_dest_rate_delay, 's', 0, 0); diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index cafc69d1e..83d9466e3 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -2944,6 +2944,9 @@ static int check_server_access(SMTPD_STATE *state, const char *table, * * If the domain name exists but no NS record exists, look up parent domain * NS records. + * + * XXX 20150707 Work around broken DNS servers that reply with NXDOMAIN + * instead of "no data". */ if (type == T_A #ifdef HAS_IPV6 @@ -2962,7 +2965,7 @@ static int check_server_access(SMTPD_STATE *state, const char *table, server_list = dns_rr_create(domain, domain, type, C_IN, 0, 0, domain, strlen(domain) + 1); dns_status = DNS_OK; - } else if (type == T_NS && h_errno == NO_DATA) { + } else if (type == T_NS /* && h_errno == NO_DATA */ ) { while ((domain = strchr(domain, '.')) != 0 && domain[1]) { domain += 1; dns_status = dns_lookup(domain, type, 0, &server_list, diff --git a/postfix/src/smtpd/smtpd_dns_filter.ref b/postfix/src/smtpd/smtpd_dns_filter.ref index edef24650..92c910203 100644 --- a/postfix/src/smtpd/smtpd_dns_filter.ref +++ b/postfix/src/smtpd/smtpd_dns_filter.ref @@ -148,7 +148,7 @@ OK OK >>> # EXPECT reject + A record, "all TXT results dropped" warning. >>> client localhost 127.0.0.2 -./smtpd_check: ignoring DNS RR: 2.0.0.127.dnsbltest.porcupine.org. TTL IN TXT DNS blocklist test. +./smtpd_check: ignoring DNS RR: 2.0.0.127.dnsbltest.porcupine.org. TTL IN TXT DNS blocklist test ./smtpd_check: warning: 2.0.0.127.dnsbltest.porcupine.org: TXT lookup error: DNS reply filter drops all results ./smtpd_check: : reject: CONNECT from localhost[127.0.0.2]: 554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; from= proto=SMTP helo= 554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org diff --git a/postfix/src/smtpd/smtpd_server.ref b/postfix/src/smtpd/smtpd_server.ref index a35a15f8f..7ac0c8d52 100644 --- a/postfix/src/smtpd/smtpd_server.ref +++ b/postfix/src/smtpd/smtpd_server.ref @@ -51,7 +51,7 @@ OK ./smtpd_check: : reject: HELO from spike.porcupine.org[168.100.189.2]: 554 5.7.1 : Helo command rejected: Access denied; from= proto=SMTP helo= 554 5.7.1 : Helo command rejected: Access denied >>> helo example.tld -./smtpd_check: warning: Unable to look up NS host for example.tld: Host not found +./smtpd_check: warning: Unable to look up NS host for tld: Host not found OK >>> helo foo@postfix.org OK @@ -61,7 +61,7 @@ OK ./smtpd_check: : reject: MAIL from spike.porcupine.org[168.100.189.2]: 554 5.7.1 : Sender address rejected: Access denied; from= proto=SMTP helo= 554 5.7.1 : Sender address rejected: Access denied >>> mail example.tld -./smtpd_check: warning: Unable to look up NS host for example.tld: Host not found +./smtpd_check: warning: Unable to look up NS host for tld: Host not found OK >>> mail foo@postfix.org OK @@ -71,7 +71,7 @@ OK ./smtpd_check: : reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 : Recipient address rejected: Access denied; from= to= proto=SMTP helo= 554 5.7.1 : Recipient address rejected: Access denied >>> rcpt foo@example.tld -./smtpd_check: warning: Unable to look up NS host for example.tld: Host not found +./smtpd_check: warning: Unable to look up NS host for tld: Host not found OK >>> rcpt foo@postfix.org OK