reject_unknown_sender/recipient_domain. This introduces a
new SMTP server configuration parameter nullmx_reject_code
(default: 556). Files: src/dns/dns_lookup.[hc], dns/Makefile,in,
- dns/nullmx_test.ref, src/smtp/smtp_addr.c, src/smtpd/smtpd_check.c,
+ dns/nullmx_test.ref, src/smtp/smtp_addr.c, smtpd/smtpd_check.c,
smtpd/smtpd_check_nullmx.in, smtpd/smtpd_check_nullmx.ref,
mantools/postlink, proto/postconf.proto, smtpd/smtpd.c.
Cleanup: libglobal "make test" had suffered from bitrot.
Files: global/mime_state.c, global/header_body_checks.c.
+
+20141127
+
+ Feature: DNS reply filter, configured with smtp_dns_reply_filter,
+ smtpd_dns_reply_filter, and lmtp_dns_reply_filter. Files:
+ mantools/postlink, proto/postconf.proto, dns/dns.h,
+ dns/dns_lookup.c, dns/dns_rr_filter.c, dns/dns_strrecord.c,
+ dns/error.ref, dns/error.reg, dns/mxonly_test.ref, dns/no-a.ref,
+ dns/no-a.reg, dns/no-aaaa.ref, dns/no-aaaa.reg, dns/no-mx.ref,
+ dns/no-mx.reg, dns/nullmx_test.ref, dns/test_dns_lookup.c,
+ global/mail_params.h, smtp/lmtp_params.c, smtp/smtp.c,
+ smtp/smtp_addr.c, smtp/smtp_params.c, smtpd/smtpd.c,
+ smtpd/smtpd_check.c, smtpd/smtpd_dns_filter.{in,ref}.
+
+20141130
+
+ Cleanup: when searching multiple DNS record types for a
+ specific name, and the result status is not DNS_OK, return
+ the rcode and diagnostic text for that status instead of
+ the last rcode and last diagnostic text.
+
+ Cleanup: un-broke several smtpd regression tests (work in
+ progress, with three more to go). Files: smtpd/smtpd_check.c,
+ smtpd/smtpd_server.{in,ref}, smtpd/smtpd_exp.{in,ref}.
+ smtpd/smtpd_dnswl.{in,ref}.
(mantools/srctoman - makedefs | nroff -man | less) with information
about build options that are not described in the INSTALL instructions.
+Major changes with snapshot 20141128
+====================================
+
+Support for DNS server reply filters in the Postfix SMTP client
+and server. This renders the result in standard zone file format:
+
+ name ttl class type preference value
+
+The class field is always "IN", the preference field exists only
+for MX records, the names of hosts, domains, etc. end in ".", and
+those names are in ASCII form (xn--mumble form in the case of UTF8
+names).
+
+When a match is found, the table lookup result specifies an action.
+By default, the table query and the action name are case-insensitive.
+Currently, only the IGNORE action is implemented.
+
+For safety reasons, Postfix logs a warning or defers mail delivery
+when a filter removes all lookup results from a successful query.
+
+The smtp_dns_reply_filter and lmtp_dns_reply_filter features are
+used only for Postfix SMTP client lookups of MX, A, and AAAAA records
+to locate a remote SMTP or LMTP server, including lookups that are
+made to implement the features reject_unverified_sender and
+reject_unverified_recipient.
+
+The smtpd_dns_reply_filter feature is used only for Postfix SMTP
+server lookups of MX, A, AAAAA, and TXT records to implement the
+features reject_unknown_helo_hostname, reject_unknown_sender_domain,
+reject_unknown_recipient_domain, reject_rbl_*, and reject_rhsbl_*.
+
+Implicit DNS lookups through nsswitch.conf or equivalent mechanisms
+are not filtered.
+
Major changes with snapshot 20141126
====================================
delivery status code or explanatory text of successful or unsuc-
cessful deliveries.
+ <b><a href="postconf.5.html#smtp_dns_reply_filter">smtp_dns_reply_filter</a> ($<a href="postconf.5.html#default_dns_reply_filter">default_dns_reply_filter</a>)</b>
+ Optional filter for Postfix SMTP client DNS lookup results.
+
<b>MIME PROCESSING CONTROLS</b>
Available in Postfix version 2.0 and later:
</ul>
+</DD>
+
+<DT><b><a name="lmtp_dns_reply_filter">lmtp_dns_reply_filter</a>
+(default: empty)</b></DT><DD>
+
+<p> Optional filter for Postfix LMTP client DNS lookup results.
+See <a href="postconf.5.html#smtp_dns_reply_filter">smtp_dns_reply_filter</a> for details including an example. </p>
+
+<p> This feature is available in Postfix 2.12 and later. </p>
+
+
</DD>
<DT><b><a name="lmtp_dns_resolver_options">lmtp_dns_resolver_options</a>
<p> The numerical reply code when the Postfix SMTP server rejects
a sender or recipient address because its domain has a nullmx DNS
record (an MX record with an empty hostname). This is one of the
-possible replies from <a href="postconf.5.html#reject_unknown_sender_domain">reject_unknown_sender_domain</a> and
-<a href="postconf.5.html#reject_unknown_recipient_domain">reject_unknown_recipient_domain</a>. </p>
+possible replies from the restrictions <a href="postconf.5.html#reject_unknown_sender_domain">reject_unknown_sender_domain</a>
+and <a href="postconf.5.html#reject_unknown_recipient_domain">reject_unknown_recipient_domain</a>. </p>
<p> This feature is available in Postfix 2.12 and later. </p>
</ul>
+</DD>
+
+<DT><b><a name="smtp_dns_reply_filter">smtp_dns_reply_filter</a>
+(default: empty)</b></DT><DD>
+
+<p> Optional filter for Postfix SMTP client DNS lookup results.
+Specify zero or more lookup tables. The lookup tables are searched
+in the given order for a match with the DNS lookup result, converted
+to the following form: </p>
+
+<pre>
+ <i>name ttl class type preference value</i>
+</pre>
+
+<p> The <i>class</i> field is always "IN", the <i>preference</i>
+field exists only for MX records, the names of hosts, domains, etc.
+end in ".", and those names are in ASCII form (xn--mumble form in
+the case of UTF8 names). </p>
+
+<p> When a match is found, the table lookup result specifies an
+action. By default, the table query and the action name are
+case-insensitive. Currently, only the <b>IGNORE</b> action is
+implemented. </p>
+
+<p> Notes: </p>
+
+<ul>
+
+<li> <p> The <a href="postconf.5.html#smtp_dns_reply_filter">smtp_dns_reply_filter</a> and <a href="postconf.5.html#lmtp_dns_reply_filter">lmtp_dns_reply_filter</a> features
+are used only for Postfix SMTP or LMTP client DNS lookups of MX,
+A, and AAAAA records to locate a remote SMTP or LMTP server, including
+lookups that are made to implement the features <a href="postconf.5.html#reject_unverified_sender">reject_unverified_sender</a>
+and <a href="postconf.5.html#reject_unverified_recipient">reject_unverified_recipient</a>. </p>
+
+<li> <p> The Postfix SMTP or LMTP client defers mail delivery when
+a filter removes all lookup results from a successful query. </p>
+
+<li> <p> The <a href="postconf.5.html#smtpd_dns_reply_filter">smtpd_dns_reply_filter</a> feature is used only for Postfix
+SMTP server DNS lookups of MX, A, AAAAA, and TXT records to implement
+the features <a href="postconf.5.html#reject_unknown_helo_hostname">reject_unknown_helo_hostname</a>, <a href="postconf.5.html#reject_unknown_sender_domain">reject_unknown_sender_domain</a>,
+<a href="postconf.5.html#reject_unknown_recipient_domain">reject_unknown_recipient_domain</a>, reject_rbl_*, and reject_rhsbl_*.
+</p>
+
+<li> <p> The Postfix SMTP server logs a warning or defers mail
+delivery when a filter removes all lookup results from a successful
+query. </p>
+
+<li> <p> Implicit DNS lookups through nsswitch.conf or equivalent
+mechanisms are not filtered. </p>
+
+</ul>
+
+<p> Example: ignore Google AAAA records in Postfix SMTP client DNS
+lookups, because Google sometimes hard-rejects mail from IPv6 clients
+with valid PTR etc. records. </p>
+
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ <a href="postconf.5.html#smtp_dns_reply_filter">smtp_dns_reply_filter</a> = <a href="pcre_table.5.html">pcre</a>:/etc/postfix/smtp_dns_reply_filter
+</pre>
+
+<pre>
+/etc/postfix/smtp_dns_reply_filter:
+ # /domain ttl IN AAAA address/ action, all case-insensitive.
+ # Note: the domain name ends in ".".
+ /^\S+\.google.com\.\s+\S+\s+\S+\s+AAAA\s+/ IGNORE
+</pre>
+
+<p> This feature is available in Postfix 2.12 and later. </p>
+
+
</DD>
<DT><b><a name="smtp_dns_resolver_options">smtp_dns_resolver_options</a>
(default: $<a href="postconf.5.html#myhostname">myhostname</a>)</b></DT><DD>
<p>
-The hostname to send in the SMTP EHLO or HELO command.
+The hostname to send in the SMTP HELO or EHLO command.
</p>
<p>
deliveries.
<br> With Postfix 2.6 and later, the SMTP server sets a per-session
flag whenever it detects illegal pipelining, including pipelined
-EHLO or HELO commands. The <a href="postconf.5.html#reject_unauth_pipelining">reject_unauth_pipelining</a> feature simply
+HELO or EHLO commands. The <a href="postconf.5.html#reject_unauth_pipelining">reject_unauth_pipelining</a> feature simply
tests whether the flag was set at any point in time during the
session.
<br> With older Postfix versions, <a href="postconf.5.html#reject_unauth_pipelining">reject_unauth_pipelining</a> checks
</ul>
+</DD>
+
+<DT><b><a name="smtpd_dns_reply_filter">smtpd_dns_reply_filter</a>
+(default: empty)</b></DT><DD>
+
+<p> Optional filter for Postfix SMTP server DNS lookup results.
+See <a href="postconf.5.html#smtp_dns_reply_filter">smtp_dns_reply_filter</a> for details including an example.
+</p>
+
+<p> This feature is available in Postfix 2.12 and later. </p>
+
+
</DD>
<DT><b><a name="smtpd_end_of_data_restrictions">smtpd_end_of_data_restrictions</a>
<dt><b><a name="reject_unknown_helo_hostname">reject_unknown_helo_hostname</a></b> (with Postfix < 2.3: reject_unknown_hostname)</dt>
<dd>Reject the request when the HELO or EHLO hostname has no DNS A
-or MX record. <br> The <a href="postconf.5.html#unknown_hostname_reject_code">unknown_hostname_reject_code</a> parameter
-specifies the numerical response code for rejected requests (default:
-450). <br> The <a href="postconf.5.html#unknown_helo_hostname_tempfail_action">unknown_helo_hostname_tempfail_action</a> parameter
-specifies the action after a temporary DNS error (default:
-<a href="postconf.5.html#defer_if_permit">defer_if_permit</a>). Note: specify "<a href="postconf.5.html#smtpd_helo_required">smtpd_helo_required</a> = yes" to fully
+or MX record. <br> The reply is specified with the
+<a href="postconf.5.html#unknown_hostname_reject_code">unknown_hostname_reject_code</a> parameter (default: 450) or
+<a href="postconf.5.html#unknown_helo_hostname_tempfail_action">unknown_helo_hostname_tempfail_action</a> (default: <a href="postconf.5.html#defer_if_permit">defer_if_permit</a>).
+See the respective parameter descriptions for details. <br>
+Note: specify "<a href="postconf.5.html#smtpd_helo_required">smtpd_helo_required</a> = yes" to fully
enforce this restriction (without "<a href="postconf.5.html#smtpd_helo_required">smtpd_helo_required</a> = yes", a
client can simply skip <a href="postconf.5.html#reject_unknown_helo_hostname">reject_unknown_helo_hostname</a> by not sending
HELO or EHLO). </dd>
no DNS A
record or 2) a malformed MX record such as a record with
a zero-length MX hostname (Postfix version 2.3 and later). <br> The
-<a href="postconf.5.html#unknown_address_reject_code">unknown_address_reject_code</a> parameter specifies the numerical
-response code for rejected requests (default: 450). The <a href="postconf.5.html#nullmx_reject_code">nullmx_reject_code</a>
-parameter specifies the response code for domains with a nullmx
-DNS record (default: 556, Postfix 2.12 and later). The response
-is always 450 in case of a temporary DNS error. <br> The
-<a href="postconf.5.html#unknown_address_tempfail_action">unknown_address_tempfail_action</a> parameter specifies the action
-after a temporary DNS error (default: <a href="postconf.5.html#defer_if_permit">defer_if_permit</a>). </dd>
+reply is specified with the <a href="postconf.5.html#unknown_address_reject_code">unknown_address_reject_code</a> parameter
+(default: 450), <a href="postconf.5.html#unknown_address_tempfail_action">unknown_address_tempfail_action</a> (default:
+<a href="postconf.5.html#defer_if_permit">defer_if_permit</a>), or <a href="postconf.5.html#nullmx_reject_code">nullmx_reject_code</a> (default 556, Postfix 2.12 and
+later). See the respective parameter descriptions for details.
+</dd>
<dt><b><a name="reject_unlisted_recipient">reject_unlisted_recipient</a></b> (with Postfix version 2.0: check_recipient_maps)</dt>
no DNS A
record, or 2) a malformed MX record such as a record with
a zero-length MX hostname (Postfix version 2.3 and later). <br> The
-<a href="postconf.5.html#unknown_address_reject_code">unknown_address_reject_code</a> parameter specifies the numerical
-response code for rejected requests (default: 450). The <a href="postconf.5.html#nullmx_reject_code">nullmx_reject_code</a>
-parameter specifies the response code for domains with a nullmx
-DNS record (default 556, Postfix 2.12 and later). The response
-is always 450 in case of a temporary DNS error. <br> The
-<a href="postconf.5.html#unknown_address_tempfail_action">unknown_address_tempfail_action</a> parameter specifies the action
-after a temporary DNS error (default: <a href="postconf.5.html#defer_if_permit">defer_if_permit</a>). </dd>
+reply is specified with the <a href="postconf.5.html#unknown_address_reject_code">unknown_address_reject_code</a> parameter
+(default: 450), <a href="postconf.5.html#unknown_address_tempfail_action">unknown_address_tempfail_action</a> (default:
+<a href="postconf.5.html#defer_if_permit">defer_if_permit</a>), or <a href="postconf.5.html#nullmx_reject_code">nullmx_reject_code</a> (default 556, Postfix 2.12 and
+later). See the respective parameter descriptions for details.
+</dd>
<dt><b><a name="reject_unlisted_sender">reject_unlisted_sender</a></b></dt>
(default: 450)</b></DT><DD>
<p>
-The numerical Postfix SMTP server response code when a sender or
-recipient address is rejected by the <a href="postconf.5.html#reject_unknown_sender_domain">reject_unknown_sender_domain</a>
-or <a href="postconf.5.html#reject_unknown_recipient_domain">reject_unknown_recipient_domain</a> restriction. The <a href="postconf.5.html#nullmx_reject_code">nullmx_reject_code</a>
-is used for domains with a nullmx DNS record (Postfix 2.12 and
-later). The response is always 450 in case of a temporary DNS
-error.
+The numerical response code when the Postfix SMTP server rejects a
+sender or recipient address because its domain is unknown. This
+is one of the possible replies from the restrictions
+<a href="postconf.5.html#reject_unknown_sender_domain">reject_unknown_sender_domain</a> and <a href="postconf.5.html#reject_unknown_recipient_domain">reject_unknown_recipient_domain</a>.
</p>
<p>
syslog records, so that "smtpd" becomes, for example, "post-
fix/smtpd".
+ Available in Postfix version 2.12 and later:
+
+ <b><a href="postconf.5.html#postscreen_dns_reply_filter">postscreen_dns_reply_filter</a> ($<a href="postconf.5.html#default_dns_reply_filter">default_dns_reply_filter</a>)</b>
+ Optional filter for <a href="postscreen.8.html"><b>postscreen</b>(8)</a> DNS lookup results.
+
<b>SEE ALSO</b>
<a href="smtpd.8.html">smtpd(8)</a>, Postfix SMTP server
<a href="tlsproxy.8.html">tlsproxy(8)</a>, Postfix TLS proxy server
delivery status code or explanatory text of successful or unsuc-
cessful deliveries.
+ <b><a href="postconf.5.html#smtp_dns_reply_filter">smtp_dns_reply_filter</a> ($<a href="postconf.5.html#default_dns_reply_filter">default_dns_reply_filter</a>)</b>
+ Optional filter for Postfix SMTP client DNS lookup results.
+
<b>MIME PROCESSING CONTROLS</b>
Available in Postfix version 2.0 and later:
record (an SMTP command line, SMTP response line, SMTP message
content line, or TLS protocol message).
+ Available in Postfix version 2.12 and later:
+
+ <b><a href="postconf.5.html#smtpd_dns_reply_filter">smtpd_dns_reply_filter</a> (empty)</b>
+ Optional filter for Postfix SMTP server DNS lookup results.
+
<b>ADDRESS REWRITING CONTROLS</b>
See the <a href="ADDRESS_REWRITING_README.html">ADDRESS_REWRITING_README</a> document for a detailed discussion of
Postfix address rewriting.
restriction.
<b><a href="postconf.5.html#unknown_address_reject_code">unknown_address_reject_code</a> (450)</b>
- The numerical Postfix SMTP server response code when a sender or
- recipient address is rejected by the
- <a href="postconf.5.html#reject_unknown_sender_domain">reject_unknown_sender_domain</a> or <a href="postconf.5.html#reject_unknown_recipient_domain">reject_unknown_recipient_domain</a>
- restriction.
+ The numerical response code when the Postfix SMTP rejects a
+ sender or recipient address because its domain is unknown.
<b><a href="postconf.5.html#unknown_client_reject_code">unknown_client_reject_code</a> (450)</b>
The numerical Postfix SMTP server response code when a client
Use the lmtp_discard_lhlo_keyword_address_maps feature to
discard LHLO keywords selectively.
.br
+.SH lmtp_dns_reply_filter (default: empty)
+Optional filter for Postfix LMTP client DNS lookup results.
+See smtp_dns_reply_filter for details including an example.
+.PP
+This feature is available in Postfix 2.12 and later.
.SH lmtp_dns_resolver_options (default: empty)
The LMTP-specific version of the smtp_dns_resolver_options
configuration parameter. See there for details.
The numerical reply code when the Postfix SMTP server rejects
a sender or recipient address because its domain has a nullmx DNS
record (an MX record with an empty hostname). This is one of the
-possible replies from reject_unknown_sender_domain and
-reject_unknown_recipient_domain.
+possible replies from the restrictions reject_unknown_sender_domain
+and reject_unknown_recipient_domain.
.PP
This feature is available in Postfix 2.12 and later.
.SH owner_request_special (default: yes)
Use the smtp_discard_ehlo_keyword_address_maps feature to
discard EHLO keywords selectively.
.br
+.SH smtp_dns_reply_filter (default: empty)
+Optional filter for Postfix SMTP client DNS lookup results.
+Specify zero or more lookup tables. The lookup tables are searched
+in the given order for a match with the DNS lookup result, converted
+to the following form:
+.PP
+.nf
+.na
+.ft C
+ \fIname ttl class type preference value\fR
+.fi
+.ad
+.ft R
+.PP
+The \fIclass\fR field is always "IN", the \fIpreference\fR
+field exists only for MX records, the names of hosts, domains, etc.
+end in ".", and those names are in ASCII form (xn--mumble form in
+the case of UTF8 names).
+.PP
+When a match is found, the table lookup result specifies an
+action. By default, the table query and the action name are
+case-insensitive. Currently, only the \fBIGNORE\fR action is
+implemented.
+.PP
+Notes:
+.IP \(bu
+The smtp_dns_reply_filter and lmtp_dns_reply_filter features
+are used only for Postfix SMTP or LMTP client DNS lookups of MX,
+A, and AAAAA records to locate a remote SMTP or LMTP server, including
+lookups that are made to implement the features reject_unverified_sender
+and reject_unverified_recipient.
+.IP \(bu
+The Postfix SMTP or LMTP client defers mail delivery when
+a filter removes all lookup results from a successful query.
+.IP \(bu
+The smtpd_dns_reply_filter feature is used only for Postfix
+SMTP server DNS lookups of MX, A, AAAAA, and TXT records to implement
+the features reject_unknown_helo_hostname, reject_unknown_sender_domain,
+reject_unknown_recipient_domain, reject_rbl_*, and reject_rhsbl_*.
+.IP \(bu
+The Postfix SMTP server logs a warning or defers mail
+delivery when a filter removes all lookup results from a successful
+query.
+.IP \(bu
+Implicit DNS lookups through nsswitch.conf or equivalent
+mechanisms are not filtered.
+.br
+.PP
+Example: ignore Google AAAA records in Postfix SMTP client DNS
+lookups, because Google sometimes hard-rejects mail from IPv6 clients
+with valid PTR etc. records.
+.PP
+.nf
+.na
+.ft C
+/etc/postfix/main.cf:
+ smtp_dns_reply_filter = pcre:/etc/postfix/smtp_dns_reply_filter
+.fi
+.ad
+.ft R
+.PP
+.nf
+.na
+.ft C
+/etc/postfix/smtp_dns_reply_filter:
+ # /domain ttl IN AAAA address/ action, all case-insensitive.
+ # Note: the domain name ends in ".".
+ /^\eS+\e.google.com\e.\es+\eS+\es+\eS+\es+AAAA\es+/ IGNORE
+.fi
+.ad
+.ft R
+.PP
+This feature is available in Postfix 2.12 and later.
.SH smtp_dns_resolver_options (default: empty)
DNS Resolver options for the Postfix SMTP client. Specify zero
or more of the following options, separated by comma or whitespace.
.PP
This feature is available in Postfix 2.5 and later.
.SH smtp_helo_name (default: $myhostname)
-The hostname to send in the SMTP EHLO or HELO command.
+The hostname to send in the SMTP HELO or EHLO command.
.PP
The default value is the machine hostname. Specify a hostname or
[ip.add.re.ss].
.br
With Postfix 2.6 and later, the SMTP server sets a per-session
flag whenever it detects illegal pipelining, including pipelined
-EHLO or HELO commands. The reject_unauth_pipelining feature simply
+HELO or EHLO commands. The reject_unauth_pipelining feature simply
tests whether the flag was set at any point in time during the
session.
.br
Use the smtpd_discard_ehlo_keyword_address_maps feature
to discard EHLO keywords selectively.
.br
+.SH smtpd_dns_reply_filter (default: empty)
+Optional filter for Postfix SMTP server DNS lookup results.
+See smtp_dns_reply_filter for details including an example.
+.PP
+This feature is available in Postfix 2.12 and later.
.SH smtpd_end_of_data_restrictions (default: empty)
Optional access restrictions that the Postfix SMTP server
applies in the context of the SMTP END-OF-DATA command.
Reject the request when the HELO or EHLO hostname has no DNS A
or MX record.
.br
-The unknown_hostname_reject_code parameter
-specifies the numerical response code for rejected requests (default:
-450).
+The reply is specified with the
+unknown_hostname_reject_code parameter (default: 450) or
+unknown_helo_hostname_tempfail_action (default: defer_if_permit).
+See the respective parameter descriptions for details.
.br
-The unknown_helo_hostname_tempfail_action parameter
-specifies the action after a temporary DNS error (default:
-defer_if_permit). Note: specify "smtpd_helo_required = yes" to fully
+Note: specify "smtpd_helo_required = yes" to fully
enforce this restriction (without "smtpd_helo_required = yes", a
client can simply skip reject_unknown_helo_hostname by not sending
HELO or EHLO).
a zero-length MX hostname (Postfix version 2.3 and later).
.br
The
-unknown_address_reject_code parameter specifies the numerical
-response code for rejected requests (default: 450). The nullmx_reject_code
-parameter specifies the response code for domains with a nullmx
-DNS record (default: 556, Postfix 2.12 and later). The response
-is always 450 in case of a temporary DNS error.
-.br
-The
-unknown_address_tempfail_action parameter specifies the action
-after a temporary DNS error (default: defer_if_permit).
+reply is specified with the unknown_address_reject_code parameter
+(default: 450), unknown_address_tempfail_action (default:
+defer_if_permit), or nullmx_reject_code (default 556, Postfix 2.12 and
+later). See the respective parameter descriptions for details.
.br
.IP "\fBreject_unlisted_recipient\fR (with Postfix version 2.0: check_recipient_maps)"
Reject the request when the RCPT TO address is not listed in
a zero-length MX hostname (Postfix version 2.3 and later).
.br
The
-unknown_address_reject_code parameter specifies the numerical
-response code for rejected requests (default: 450). The nullmx_reject_code
-parameter specifies the response code for domains with a nullmx
-DNS record (default 556, Postfix 2.12 and later). The response
-is always 450 in case of a temporary DNS error.
-.br
-The
-unknown_address_tempfail_action parameter specifies the action
-after a temporary DNS error (default: defer_if_permit).
+reply is specified with the unknown_address_reject_code parameter
+(default: 450), unknown_address_tempfail_action (default:
+defer_if_permit), or nullmx_reject_code (default 556, Postfix 2.12 and
+later). See the respective parameter descriptions for details.
.br
.IP "\fBreject_unlisted_sender\fR"
Reject the request when the MAIL FROM address is not listed in
.ad
.ft R
.SH unknown_address_reject_code (default: 450)
-The numerical Postfix SMTP server response code when a sender or
-recipient address is rejected by the reject_unknown_sender_domain
-or reject_unknown_recipient_domain restriction. The nullmx_reject_code
-is used for domains with a nullmx DNS record (Postfix 2.12 and
-later). The response is always 450 in case of a temporary DNS
-error.
+The numerical response code when the Postfix SMTP server rejects a
+sender or recipient address because its domain is unknown. This
+is one of the possible replies from the restrictions
+reject_unknown_sender_domain and reject_unknown_recipient_domain.
.PP
Do not change this unless you have a complete understanding of RFC 5321.
.SH unknown_address_tempfail_action (default: $reject_tempfail_action)
.IP "\fBsyslog_name (see 'postconf -d' output)\fR"
The mail system name that is prepended to the process name in syslog
records, so that "smtpd" becomes, for example, "postfix/smtpd".
+.PP
+Available in Postfix version 2.12 and later:
+.IP "\fBpostscreen_dns_reply_filter ($default_dns_reply_filter)\fR"
+Optional filter for \fBpostscreen\fR(8) DNS lookup results.
.SH "SEE ALSO"
.na
.nf
Optional filter for the \fBsmtp\fR(8) delivery agent to change the
delivery status code or explanatory text of successful or unsuccessful
deliveries.
+.IP "\fBsmtp_dns_reply_filter ($default_dns_reply_filter)\fR"
+Optional filter for Postfix SMTP client DNS lookup results.
.SH "MIME PROCESSING CONTROLS"
.na
.nf
time limit per read or write system call, to a time limit to send
or receive a complete record (an SMTP command line, SMTP response
line, SMTP message content line, or TLS protocol message).
+.PP
+Available in Postfix version 2.12 and later:
+.IP "\fBsmtpd_dns_reply_filter (empty)\fR"
+Optional filter for Postfix SMTP server DNS lookup results.
.SH "ADDRESS REWRITING CONTROLS"
.na
.nf
request is rejected by the reject_unauth_destination recipient
restriction.
.IP "\fBunknown_address_reject_code (450)\fR"
-The numerical Postfix SMTP server response code when a sender or
-recipient address is rejected by the reject_unknown_sender_domain
-or reject_unknown_recipient_domain restriction.
+The numerical response code when the Postfix SMTP rejects a sender
+or recipient address because its domain is unknown.
.IP "\fBunknown_client_reject_code (450)\fR"
The numerical Postfix SMTP server response code when a client
without valid address <=> name mapping is rejected by the
s;\bsmtp_discard_ehlo_keywords\b;<a href="postconf.5.html#smtp_discard_ehlo_keywords">$&</a>;g;
s;\bsmtp_dns_resolver_options\b;<a href="postconf.5.html#smtp_dns_resolver_options">$&</a>;g;
s;\bsmtp_dns_support_level\b;<a href="postconf.5.html#smtp_dns_support_level">$&</a>;g;
+ s;\blmtp_dns_reply_filter\b;<a href="postconf.5.html#lmtp_dns_reply_filter">$&</a>;g;
+ s;\bsmtp_dns_reply_filter\b;<a href="postconf.5.html#smtp_dns_reply_filter">$&</a>;g;
+ s;\bsmtpd_dns_reply_filter\b;<a href="postconf.5.html#smtpd_dns_reply_filter">$&</a>;g;
s;\bsmtp_helo_name\b;<a href="postconf.5.html#smtp_helo_name">$&</a>;g;
s;\bsmtp_helo_timeout\b;<a href="postconf.5.html#smtp_helo_timeout">$&</a>;g;
s;\bsmtp_host_lookup\b;<a href="postconf.5.html#smtp_host_lookup">$&</a>;g;
%PARAM smtp_helo_name $myhostname
<p>
-The hostname to send in the SMTP EHLO or HELO command.
+The hostname to send in the SMTP HELO or EHLO command.
</p>
<p>
deliveries.
<br> With Postfix 2.6 and later, the SMTP server sets a per-session
flag whenever it detects illegal pipelining, including pipelined
-EHLO or HELO commands. The reject_unauth_pipelining feature simply
+HELO or EHLO commands. The reject_unauth_pipelining feature simply
tests whether the flag was set at any point in time during the
session.
<br> With older Postfix versions, reject_unauth_pipelining checks
<dt><b><a name="reject_unknown_helo_hostname">reject_unknown_helo_hostname</a></b> (with Postfix < 2.3: reject_unknown_hostname)</dt>
<dd>Reject the request when the HELO or EHLO hostname has no DNS A
-or MX record. <br> The unknown_hostname_reject_code parameter
-specifies the numerical response code for rejected requests (default:
-450). <br> The unknown_helo_hostname_tempfail_action parameter
-specifies the action after a temporary DNS error (default:
-defer_if_permit). Note: specify "smtpd_helo_required = yes" to fully
+or MX record. <br> The reply is specified with the
+unknown_hostname_reject_code parameter (default: 450) or
+unknown_helo_hostname_tempfail_action (default: defer_if_permit).
+See the respective parameter descriptions for details. <br>
+Note: specify "smtpd_helo_required = yes" to fully
enforce this restriction (without "smtpd_helo_required = yes", a
client can simply skip reject_unknown_helo_hostname by not sending
HELO or EHLO). </dd>
no DNS A
record or 2) a malformed MX record such as a record with
a zero-length MX hostname (Postfix version 2.3 and later). <br> The
-unknown_address_reject_code parameter specifies the numerical
-response code for rejected requests (default: 450). The nullmx_reject_code
-parameter specifies the response code for domains with a nullmx
-DNS record (default: 556, Postfix 2.12 and later). The response
-is always 450 in case of a temporary DNS error. <br> The
-unknown_address_tempfail_action parameter specifies the action
-after a temporary DNS error (default: defer_if_permit). </dd>
+reply is specified with the unknown_address_reject_code parameter
+(default: 450), unknown_address_tempfail_action (default:
+defer_if_permit), or nullmx_reject_code (default 556, Postfix 2.12 and
+later). See the respective parameter descriptions for details.
+</dd>
<dt><b><a name="reject_unlisted_recipient">reject_unlisted_recipient</a></b> (with Postfix version 2.0: check_recipient_maps)</dt>
no DNS A
record, or 2) a malformed MX record such as a record with
a zero-length MX hostname (Postfix version 2.3 and later). <br> The
-unknown_address_reject_code parameter specifies the numerical
-response code for rejected requests (default: 450). The nullmx_reject_code
-parameter specifies the response code for domains with a nullmx
-DNS record (default 556, Postfix 2.12 and later). The response
-is always 450 in case of a temporary DNS error. <br> The
-unknown_address_tempfail_action parameter specifies the action
-after a temporary DNS error (default: defer_if_permit). </dd>
+reply is specified with the unknown_address_reject_code parameter
+(default: 450), unknown_address_tempfail_action (default:
+defer_if_permit), or nullmx_reject_code (default 556, Postfix 2.12 and
+later). See the respective parameter descriptions for details.
+</dd>
<dt><b><a name="reject_unlisted_sender">reject_unlisted_sender</a></b></dt>
%PARAM unknown_address_reject_code 450
<p>
-The numerical Postfix SMTP server response code when a sender or
-recipient address is rejected by the reject_unknown_sender_domain
-or reject_unknown_recipient_domain restriction. The nullmx_reject_code
-is used for domains with a nullmx DNS record (Postfix 2.12 and
-later). The response is always 450 in case of a temporary DNS
-error.
+The numerical response code when the Postfix SMTP server rejects a
+sender or recipient address because its domain is unknown. This
+is one of the possible replies from the restrictions
+reject_unknown_sender_domain and reject_unknown_recipient_domain.
</p>
<p>
<p> The numerical reply code when the Postfix SMTP server rejects
a sender or recipient address because its domain has a nullmx DNS
record (an MX record with an empty hostname). This is one of the
-possible replies from reject_unknown_sender_domain and
-reject_unknown_recipient_domain. </p>
+possible replies from the restrictions reject_unknown_sender_domain
+and reject_unknown_recipient_domain. </p>
+
+<p> This feature is available in Postfix 2.12 and later. </p>
+
+%PARAM smtpd_dns_reply_filter
+
+<p> Optional filter for Postfix SMTP server DNS lookup results.
+See smtp_dns_reply_filter for details including an example.
+</p>
+
+<p> This feature is available in Postfix 2.12 and later. </p>
+
+%PARAM lmtp_dns_reply_filter
+
+<p> Optional filter for Postfix LMTP client DNS lookup results.
+See smtp_dns_reply_filter for details including an example. </p>
+
+<p> This feature is available in Postfix 2.12 and later. </p>
+
+#%PARAM postscreen_dns_reply_filter
+#
+#<p> Optional filter for postscreen(8) DNS lookup results.
+#See smtp_dns_reply_filter for details including an example.
+#</p>
+#
+#<p> This feature is available in Postfix 2.12 and later. </p>
+
+%PARAM smtp_dns_reply_filter
+
+<p> Optional filter for Postfix SMTP client DNS lookup results.
+Specify zero or more lookup tables. The lookup tables are searched
+in the given order for a match with the DNS lookup result, converted
+to the following form: </p>
+
+<pre>
+ <i>name ttl class type preference value</i>
+</pre>
+
+<p> The <i>class</i> field is always "IN", the <i>preference</i>
+field exists only for MX records, the names of hosts, domains, etc.
+end in ".", and those names are in ASCII form (xn--mumble form in
+the case of UTF8 names). </p>
+
+<p> When a match is found, the table lookup result specifies an
+action. By default, the table query and the action name are
+case-insensitive. Currently, only the <b>IGNORE</b> action is
+implemented. </p>
+
+<p> Notes: </p>
+
+<ul>
+
+<li> <p> The smtp_dns_reply_filter and lmtp_dns_reply_filter features
+are used only for Postfix SMTP or LMTP client DNS lookups of MX,
+A, and AAAAA records to locate a remote SMTP or LMTP server, including
+lookups that are made to implement the features reject_unverified_sender
+and reject_unverified_recipient. </p>
+
+<li> <p> The Postfix SMTP or LMTP client defers mail delivery when
+a filter removes all lookup results from a successful query. </p>
+
+<li> <p> The smtpd_dns_reply_filter feature is used only for Postfix
+SMTP server DNS lookups of MX, A, AAAAA, and TXT records to implement
+the features reject_unknown_helo_hostname, reject_unknown_sender_domain,
+reject_unknown_recipient_domain, reject_rbl_*, and reject_rhsbl_*.
+</p>
+
+<li> <p> The Postfix SMTP server logs a warning or defers mail
+delivery when a filter removes all lookup results from a successful
+query. </p>
+
+<li> <p> Implicit DNS lookups through nsswitch.conf or equivalent
+mechanisms are not filtered. </p>
+
+</ul>
+
+<p> Example: ignore Google AAAA records in Postfix SMTP client DNS
+lookups, because Google sometimes hard-rejects mail from IPv6 clients
+with valid PTR etc. records. </p>
+
+<pre>
+/etc/postfix/main.cf:
+ smtp_dns_reply_filter = pcre:/etc/postfix/smtp_dns_reply_filter
+</pre>
+
+<pre>
+/etc/postfix/smtp_dns_reply_filter:
+ # /domain ttl IN AAAA address/ action, all case-insensitive.
+ # Note: the domain name ends in ".".
+ /^\S+\.google.com\.\s+\S+\s+\S+\s+AAAA\s+/ IGNORE
+</pre>
<p> This feature is available in Postfix 2.12 and later. </p>
SHELL = /bin/sh
SRCS = dns_lookup.c dns_rr.c dns_strerror.c dns_strtype.c dns_rr_to_pa.c \
- dns_sa_to_rr.c dns_rr_eq_sa.c dns_rr_to_sa.c
+ dns_sa_to_rr.c dns_rr_eq_sa.c dns_rr_to_sa.c dns_strrecord.c \
+ dns_rr_filter.c
OBJS = dns_lookup.o dns_rr.o dns_strerror.o dns_strtype.o dns_rr_to_pa.o \
- dns_sa_to_rr.o dns_rr_eq_sa.o dns_rr_to_sa.o
+ dns_sa_to_rr.o dns_rr_eq_sa.o dns_rr_to_sa.o dns_strrecord.o \
+ dns_rr_filter.o
HDRS = dns.h
TESTSRC = test_dns_lookup.c test_alias_token.c
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
INCL =
LIB = lib$(LIB_PREFIX)dns$(LIB_SUFFIX)
TESTPROG= test_dns_lookup dns_rr_to_pa dns_rr_to_sa dns_sa_to_rr dns_rr_eq_sa
-LIBS = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
+LIBS = ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \
+ ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
LIB_DIR = ../../lib
INC_DIR = ../../include
test: $(TESTPROG)
tests: test dns_rr_to_pa_test dns_rr_to_sa_test dns_sa_to_rr_test \
- dns_rr_eq_sa_test nullmx_test nxdomain_test mxonly_test
+ dns_rr_eq_sa_test no-a-test no-aaaa-test no-mx-test \
+ error-filter-test nullmx_test nxdomain_test mxonly_test
root_tests:
diff dns_rr_eq_sa.ref dns_rr_eq_sa.tmp
rm -f dns_rr_eq_sa.tmp
+no-a-test: no-a.reg test_dns_lookup no-a.ref
+ $(SHLIB_ENV) ./test_dns_lookup -f regexp:no-a.reg a,aaaa spike.porcupine.org >test_dns_lookup.tmp 2>&1
+ diff no-a.ref test_dns_lookup.tmp
+ rm -f test_dns_lookup.tmp
+
+no-aaaa-test: no-aaaa.reg test_dns_lookup no-aaaa.ref
+ $(SHLIB_ENV) ./test_dns_lookup -f regexp:no-aaaa.reg a,aaaa spike.porcupine.org >test_dns_lookup.tmp 2>&1
+ diff no-aaaa.ref test_dns_lookup.tmp
+ rm -f test_dns_lookup.tmp
+
+no-mx-test: no-mx.reg test_dns_lookup no-mx.ref
+ set -e; $(SHLIB_ENV) ./test_dns_lookup -f regexp:no-mx.reg mx porcupine.org 2>&1 | sort >test_dns_lookup.tmp || true
+ diff no-mx.ref test_dns_lookup.tmp
+ rm -f test_dns_lookup.tmp
+
+error-filter-test: error.reg test_dns_lookup error.ref
+ set -e; $(SHLIB_ENV) ./test_dns_lookup -f regexp:error.reg a,aaaa spike.porcupine.org >test_dns_lookup.tmp 2>&1 || true
+ diff error.ref test_dns_lookup.tmp
+ rm -f test_dns_lookup.tmp
+
nullmx_test: test_dns_lookup nullmx_test.ref
(set -e; \
$(SHLIB_ENV) ./test_dns_lookup mx,a nullmx.porcupine.org; \
@$(EXPORT) make -f Makefile.in Makefile 1>&2
# do not edit below this line - it is generated by 'make depend'
+dns_lookup.o: ../../include/argv.h
+dns_lookup.o: ../../include/dict.h
+dns_lookup.o: ../../include/maps.h
dns_lookup.o: ../../include/msg.h
dns_lookup.o: ../../include/myaddrinfo.h
+dns_lookup.o: ../../include/myflock.h
dns_lookup.o: ../../include/mymalloc.h
dns_lookup.o: ../../include/sock_addr.h
dns_lookup.o: ../../include/stringops.h
dns_lookup.o: ../../include/sys_defs.h
dns_lookup.o: ../../include/valid_hostname.h
dns_lookup.o: ../../include/vbuf.h
+dns_lookup.o: ../../include/vstream.h
dns_lookup.o: ../../include/vstring.h
dns_lookup.o: dns.h
dns_lookup.o: dns_lookup.c
dns_rr_eq_sa.o: ../../include/vstring.h
dns_rr_eq_sa.o: dns.h
dns_rr_eq_sa.o: dns_rr_eq_sa.c
+dns_rr_filter.o: ../../include/argv.h
+dns_rr_filter.o: ../../include/dict.h
+dns_rr_filter.o: ../../include/maps.h
+dns_rr_filter.o: ../../include/msg.h
+dns_rr_filter.o: ../../include/myaddrinfo.h
+dns_rr_filter.o: ../../include/myflock.h
+dns_rr_filter.o: ../../include/sock_addr.h
+dns_rr_filter.o: ../../include/sys_defs.h
+dns_rr_filter.o: ../../include/vbuf.h
+dns_rr_filter.o: ../../include/vstream.h
+dns_rr_filter.o: ../../include/vstring.h
+dns_rr_filter.o: dns.h
+dns_rr_filter.o: dns_rr_filter.c
dns_rr_to_pa.o: ../../include/msg.h
dns_rr_to_pa.o: ../../include/myaddrinfo.h
dns_rr_to_pa.o: ../../include/sock_addr.h
dns_strerror.o: ../../include/vstring.h
dns_strerror.o: dns.h
dns_strerror.o: dns_strerror.c
+dns_strrecord.o: ../../include/msg.h
+dns_strrecord.o: ../../include/myaddrinfo.h
+dns_strrecord.o: ../../include/sock_addr.h
+dns_strrecord.o: ../../include/sys_defs.h
+dns_strrecord.o: ../../include/vbuf.h
+dns_strrecord.o: ../../include/vstring.h
+dns_strrecord.o: dns.h
+dns_strrecord.o: dns_strrecord.c
dns_strtype.o: ../../include/myaddrinfo.h
dns_strtype.o: ../../include/sock_addr.h
dns_strtype.o: ../../include/sys_defs.h
extern const char *dns_strtype(unsigned);
extern unsigned dns_type(const char *);
+ /*
+ * dns_strrecord.c
+ */
+extern char *dns_strrecord(VSTRING *, DNS_RR *);
+
/*
* dns_rr.c
*/
#define DNS_REQ_FLAG_STOP_OK (1<<0)
#define DNS_REQ_FLAG_STOP_INVAL (1<<1)
#define DNS_REQ_FLAG_STOP_UNAVAIL (1<<2)
+#define DNS_REQ_FLAG_STOP_MX_POLICY (1<<3)
#define DNS_REQ_FLAG_NONE (0)
/*
* Status codes. Failures must have negative codes so they will not collide
* with valid counts of answer records etc.
+ *
+ * When a function queries multiple record types for one name, it issues one
+ * query for each query record type. Each query returns a (status, rcode,
+ * text). Only one of these (status, rcode, text) will be returned to the
+ * caller. The selection is based on the status code precedence.
+ *
+ * - Return DNS_OK (and the corresponding rcode) as long as any query returned
+ * DNS_OK. If this is changed, then code needs to be added to prevent memory
+ * leaks.
+ *
+ * - Return DNS_RETRY (and the corresponding rcode and text) instead of any
+ * hard negative result.
+ *
+ * - Return DNS_NOTFOUND (and the corresponding rcode and text) only when all
+ * queries returned DNS_NOTFOUND.
+ *
+ * DNS_POLICY ranks higher than DNS_RETRY because there was a DNS_OK result,
+ * but the reply filter dropped it. This is a very soft error.
+ *
+ * Below is the precedence order. The order between DNS_RETRY and DNS_NOTFOUND
+ * is arbitrary.
*/
-#define DNS_UNAVAIL (-6) /* query ok, service unavailable */
-#define DNS_INVAL (-5) /* query ok, malformed reply */
+#define DNS_RECURSE (-7) /* internal only: recursion needed */
+#define DNS_NOTFOUND (-6) /* query ok, data not found */
+#define DNS_UNAVAIL (-5) /* query ok, service unavailable */
#define DNS_FAIL (-4) /* query failed, don't retry */
-#define DNS_NOTFOUND (-3) /* query ok, data not found */
+#define DNS_INVAL (-3) /* query ok, malformed reply */
#define DNS_RETRY (-2) /* query failed, try again */
-#define DNS_RECURSE (-1) /* recursion needed */
+#define DNS_POLICY (-1) /* query ok, all records dropped */
#define DNS_OK 0 /* query succeeded */
/*
*/
#define DNS_NAME_LEN 1024
+ /*
+ * dns_rr_filter.c.
+ */
+extern void dns_rr_filter_compile(const char *, const char *);
+
+#ifdef LIBDNS_INTERNAL
+#include <maps.h>
+extern MAPS *dns_rr_filter_maps;
+extern int dns_rr_filter_execute(DNS_RR **);
+
+#endif
+
/* LICENSE
/* .ad
/* .fi
/* .IP DNS_REQ_FLAG_STOP_UNAVAIL
/* Invoke dns_lookup() for the resource types in the order as
/* specified, and return when dns_lookup() returns DNS_UNAVAIL.
+/* .IP DNS_REQ_FLAG_STOP_MX_POLICY
+/* 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
/* Invoke dns_lookup() for the resource types in the order as
/* specified, and return when dns_lookup() returns DNS_OK.
/* \fIwhy\fR argument accordingly:
/* .IP DNS_OK
/* The DNS query succeeded.
+/* .IP DNS_POLICY
+/* The DNS query succeeded, but the answer did not pass the
+/* policy filter.
/* .IP DNS_NOTFOUND
/* The DNS query succeeded; the requested information was not found.
/* .IP DNS_UNAVAIL
/* DNS library. */
+#define LIBDNS_INTERNAL
#include "dns.h"
/* Local stuff. */
return (DNS_RETRY);
/* Don't even think of returning an invalid hostname to the caller. */
if (*temp == 0)
- return (DNS_UNAVAIL); /* XXX TODO: return descriptive text here */
+ return (DNS_UNAVAIL); /* TODO: descriptive text */
if (!valid_rr_name(temp, "resource data", fixed->type, reply))
return (DNS_INVAL);
data_len = strlen(temp) + 1;
rr->dnssec_valid = *maybe_secure ? reply->dnssec_ad : 0;
*rrlist = dns_rr_append(*rrlist, rr);
} else if (status == DNS_UNAVAIL) {
- CORRUPT(status); /* XXX TODO: use better name here */
+ CORRUPT(status); /* TODO: use better name */
} else if (not_found_status != DNS_RETRY)
not_found_status = status;
} else
return (status);
case DNS_UNAVAIL:
if (why)
- vstring_sprintf(why, type == T_MX ? /* XXX TODO: move this */
- "Domain %s does not accept mail (null %s)" :
+ vstring_sprintf(why, type == T_MX ? /* TODO: move this */
+ "Domain %s does not accept mail (null %s)" :
"Domain %s does not provide %s service",
name, dns_strtype(type));
h_errno = NO_DATA;
return (status);
case DNS_OK:
+ if (dns_rr_filter_maps) {
+ if (dns_rr_filter_execute(rrlist) < 0) {
+ if (why)
+ vstring_sprintf(why,
+ "Error looking up name=%s type=%s: "
+ "Invalid DNS reply filter syntax",
+ name, dns_strtype(type));
+ dns_rr_free(*rrlist);
+ *rrlist = 0;
+ status = DNS_RETRY;
+ } else if (*rrlist == 0) {
+ if (why)
+ vstring_sprintf(why,
+ "Error looking up name=%s type=%s: "
+ "DNS reply filter drops all results",
+ name, dns_strtype(type));
+ status = DNS_POLICY;
+ }
+ }
return (status);
case DNS_RECURSE:
if (msg_verbose)
int lflags,...)
{
va_list ap;
- unsigned type;
+ unsigned type, next;
int status = DNS_NOTFOUND;
+ int hpref_status = INT_MIN;
+ VSTRING *hpref_rtext = 0;
+ int hpref_rcode;
DNS_RR *rr;
- int non_err = 0;
- int soft_err = 0;
+
+ /* Save intermediate highest-priority result. */
+#define SAVE_HPREF_STATUS() do { \
+ hpref_status = status; \
+ if (rcode) \
+ hpref_rcode = *rcode; \
+ if (why && status != DNS_OK) \
+ vstring_strcpy(hpref_rtext ? hpref_rtext : \
+ vstring_alloc(VSTRING_LEN(why)), \
+ vstring_str(why)); \
+ } while (0)
+
+ /* Restore intermediate highest-priority result. */
+#define RESTORE_HPREF_STATUS() do { \
+ status = hpref_status; \
+ if (rcode) \
+ *rcode = hpref_rcode; \
+ if (why && status != DNS_OK) \
+ vstring_strcpy(why, vstring_str(hpref_rtext)); \
+ } while (0)
if (rrlist)
*rrlist = 0;
va_start(ap, lflags);
- while ((type = va_arg(ap, unsigned)) != 0) {
+ for (type = va_arg(ap, unsigned); type != 0; type = next) {
+ next = va_arg(ap, unsigned);
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);
if (status == DNS_OK) {
- non_err = 1;
if (rrlist)
*rrlist = dns_rr_append(*rrlist, rr);
if (lflags & DNS_REQ_FLAG_STOP_OK)
} else if (status == DNS_INVAL) {
if (lflags & DNS_REQ_FLAG_STOP_INVAL)
break;
+ } else if (status == DNS_POLICY) {
+ if (type == T_MX && (lflags & DNS_REQ_FLAG_STOP_MX_POLICY))
+ break;
} else if (status == DNS_UNAVAIL) {
if (lflags & DNS_REQ_FLAG_STOP_UNAVAIL)
break;
- } else if (status == DNS_RETRY) {
- soft_err = 1;
}
/* XXX Stop after NXDOMAIN error. */
+ if (next == 0)
+ break;
+ if (status >= hpref_status)
+ SAVE_HPREF_STATUS(); /* save last info */
}
va_end(ap);
- return (non_err ? DNS_OK : soft_err ? DNS_RETRY : status);
+ if (status < hpref_status)
+ RESTORE_HPREF_STATUS(); /* else report last info */
+ if (hpref_rtext)
+ vstring_free(hpref_rtext);
+ return (status);
}
/* dns_lookup_rv - DNS lookup interface with types vector */
VSTRING *fqdn, VSTRING *why, int *rcode,
int lflags, unsigned *types)
{
- unsigned type;
+ unsigned type, next;
int status = DNS_NOTFOUND;
+ int hpref_status = INT_MIN;
+ VSTRING *hpref_rtext = 0;
+ int hpref_rcode;
DNS_RR *rr;
- int non_err = 0;
- int soft_err = 0;
if (rrlist)
*rrlist = 0;
- while ((type = *types++) != 0) {
+ for (type = *types++; type != 0; type = next) {
+ next = *types++;
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);
if (status == DNS_OK) {
- non_err = 1;
if (rrlist)
*rrlist = dns_rr_append(*rrlist, rr);
if (lflags & DNS_REQ_FLAG_STOP_OK)
} else if (status == DNS_INVAL) {
if (lflags & DNS_REQ_FLAG_STOP_INVAL)
break;
+ } else if (status == DNS_POLICY) {
+ if (type == T_MX && (lflags & DNS_REQ_FLAG_STOP_MX_POLICY))
+ break;
} else if (status == DNS_UNAVAIL) {
if (lflags & DNS_REQ_FLAG_STOP_UNAVAIL)
break;
- } else if (status == DNS_RETRY) {
- soft_err = 1;
}
/* XXX Stop after NXDOMAIN error. */
+ if (next == 0)
+ break;
+ if (status >= hpref_status)
+ SAVE_HPREF_STATUS(); /* save last info */
}
- return (non_err ? DNS_OK : soft_err ? DNS_RETRY : status);
+ if (status < hpref_status)
+ RESTORE_HPREF_STATUS(); /* else report last info */
+ if (hpref_rtext)
+ vstring_free(hpref_rtext);
+ return (status);
}
--- /dev/null
+/*++
+/* NAME
+/* dns_rr_filter 3
+/* SUMMARY
+/* DNS resource record filter
+/* SYNOPSIS
+/* #include <dns.h>
+/*
+/* void dns_rr_filter_compile(title, map_names)
+/* const char *title;
+/* const char *map_names;
+/* INTERNAL INTERFACES
+/* int dns_rr_filter_execute(rrlist)
+/* DNS_RR **rrlist;
+/*
+/* MAPS *dns_rr_filter_maps;
+/* DESCRIPTION
+/* This module implements a simple filter for dns_lookup*()
+/* results.
+/*
+/* dns_rr_filter_compile() initializes a result filter. The
+/* title and map_names arguments are as with maps_create().
+/* This function may be invoked more than once; only the last
+/* filter takes effect.
+/*
+/* dns_rr_filter_execute() converts each resource record in the
+/* specified list with dns_strrecord to ASCII form and matches
+/* that against the specified maps. If a match is found it
+/* executes the corresponding action. Currently, only the
+/* "ignore" action is implemented. This removes the matched
+/* record from the list. The result is 0 in case of success,
+/* -1 in case of error.
+/*
+/* dns_rr_filter_maps is updated by dns_rr_filter_compile().
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+ /*
+ * System library.
+ */
+#include <sys_defs.h>
+#include <ctype.h>
+
+ /*
+ * Utility library.
+ */
+#include <msg.h>
+#include <vstring.h>
+#include <myaddrinfo.h>
+
+ /*
+ * Global library.
+ */
+#include <maps.h>
+
+ /*
+ * DNS library.
+ */
+#define LIBDNS_INTERNAL
+#include <dns.h>
+
+ /*
+ * Application-specific.
+ */
+MAPS *dns_rr_filter_maps;
+
+static DNS_RR dns_rr_filter_error[1];
+
+#define STR vstring_str
+
+/* dns_rr_filter_compile - compile dns result filter */
+
+void dns_rr_filter_compile(const char *title, const char *map_names)
+{
+ if (dns_rr_filter_maps != 0)
+ maps_free(dns_rr_filter_maps);
+ dns_rr_filter_maps = maps_create(title, map_names,
+ DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+}
+
+/* dns_rr_action - execute action from filter map */
+
+static DNS_RR *dns_rr_action(const char *cmd, DNS_RR *rr, const char *rr_text)
+{
+ const char *cmd_args = cmd + strcspn(cmd, " \t");
+ int cmd_len = cmd_args - cmd;
+
+ while (*cmd_args && ISSPACE(*cmd_args))
+ cmd_args++;
+
+#define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0)
+
+ if (STREQUAL(cmd, "IGNORE", cmd_len)) {
+ msg_info("ignoring DNS RR: %s", rr_text);
+ return (0);
+ } else {
+ msg_warn("%s: unknown DNS filter action: \"%s\"",
+ dns_rr_filter_maps->title, cmd);
+ return (dns_rr_filter_error);
+ }
+ return (rr);
+}
+
+/* dns_rr_filter_execute - filter DNS lookup result */
+
+int dns_rr_filter_execute(DNS_RR **rrlist)
+{
+ static VSTRING *buf = 0;
+ DNS_RR **rrp;
+ DNS_RR *rr;
+ const char *map_res;
+ DNS_RR *act_res;
+
+ /*
+ * Convert the resource record to string form, then search the maps for a
+ * matching action.
+ */
+ if (buf == 0)
+ buf = vstring_alloc(100);
+ for (rrp = rrlist; (rr = *rrp) != 0; /* see below */ ) {
+ map_res = maps_find(dns_rr_filter_maps, dns_strrecord(buf, rr),
+ DICT_FLAG_NONE);
+ if (map_res != 0) {
+ if ((act_res = dns_rr_action(map_res, rr, STR(buf))) == 0) {
+ *rrp = rr->next; /* do not advance in the list */
+ rr->next = 0;
+ dns_rr_free(rr);
+ continue;
+ } else if (act_res == dns_rr_filter_error) {
+ return (-1);
+ }
+ } else if (dns_rr_filter_maps->error) {
+ return (-1);
+ }
+ rrp = &(rr->next); /* do advance in the list */
+ }
+ return (0);
+}
--- /dev/null
+/*++
+/* NAME
+/* dns_strtype 3
+/* SUMMARY
+/* name service resource record printable forms
+/* SYNOPSIS
+/* #include <dns.h>
+/*
+/* char *dns_strrecord(buf, record)
+/* VSTRING *buf;
+/* DNS_RR *record;
+/* DESCRIPTION
+/* dns_strrecord() formats a DNS resource record as "name ttl
+/* class type preference value", where the class field is
+/* always "IN", the preference field exists only for MX records,
+/* and all names end in ".". The result value is the payload
+/* of the buffer argument.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+#include <msg.h>
+
+/* DNS library. */
+
+#include <dns.h>
+
+/* dns_strrecord - format resource record as generic string */
+
+char *dns_strrecord(VSTRING *buf, DNS_RR *rr)
+{
+ const char myname[] = "dns_strrecord";
+ MAI_HOSTADDR_STR host;
+
+ vstring_sprintf(buf, "%s. %u IN %s ",
+ rr->rname, rr->ttl, dns_strtype(rr->type));
+ switch (rr->type) {
+ case T_A:
+#ifdef T_AAAA
+ case T_AAAA:
+#endif
+ if (dns_rr_to_pa(rr, &host) == 0)
+ msg_fatal("%s: conversion error for resource record type %s: %m",
+ myname, dns_strtype(rr->type));
+ vstring_sprintf_append(buf, "%s", host.buf);
+ break;
+ case T_CNAME:
+ case T_DNAME:
+ case T_MB:
+ case T_MG:
+ case T_MR:
+ case T_NS:
+ case T_PTR:
+ case T_TXT:
+ vstring_sprintf_append(buf, "%s.", rr->data);
+ break;
+ case T_MX:
+ vstring_sprintf_append(buf, "%u %s.", rr->pref, rr->data);
+ break;
+ case T_TLSA:
+ if (rr->data_len >= 3) {
+ uint8_t *ip = (uint8_t *) rr->data;
+ uint8_t usage = *ip++;
+ uint8_t selector = *ip++;
+ uint8_t mtype = *ip++;
+ unsigned i;
+
+ vstring_sprintf_append(buf, "%d %d %d", usage, selector, mtype);
+ for (i = 3; i < rr->data_len; ++i)
+ vstring_sprintf_append(buf, "%02x", *ip++);
+ } else {
+ vstring_sprintf_append(buf, "[truncated record]");
+ }
+ break;
+ default:
+ msg_fatal("%s: don't know how to print type %s",
+ myname, dns_strtype(rr->type));
+ }
+ return (vstring_str(buf));
+}
--- /dev/null
+./test_dns_lookup: lookup spike.porcupine.org type A flags 2097152
+./test_dns_lookup: dns_query: spike.porcupine.org (A): OK
+./test_dns_lookup: dns_get_answer: type A for spike.porcupine.org
+./test_dns_lookup: dict_regexp_lookup: error.reg: spike.porcupine.org. 3600 IN A 168.100.189.2
+./test_dns_lookup: maps_find: DNS reply filter: regexp:error.reg(0,lock|fold_fix): spike.porcupine.org. 3600 IN A 168.100.189.2 = oops
+./test_dns_lookup: warning: DNS reply filter: unknown DNS filter action: "oops"
+./test_dns_lookup: lookup spike.porcupine.org type AAAA flags 2097152
+./test_dns_lookup: dns_query: spike.porcupine.org (AAAA): OK
+./test_dns_lookup: dns_get_answer: type AAAA for spike.porcupine.org
+./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: dns_get_answer: type MX for porcupine.org
./test_dns_lookup: lookup porcupine.org type A flags 2097152
./test_dns_lookup: dns_query: porcupine.org (A): Host found but no data record of requested type
-porcupine.org: ad: 0, ttl: 3600 pref: 10 MX: spike.porcupine.org
-porcupine.org: ad: 0, ttl: 3600 pref: 20 MX: fist.porcupine.org
-porcupine.org: ad: 0, ttl: 3600 pref: 30 MX: m1.porcupine.org
+ad: 0, rr: porcupine.org. 3600 IN MX 10 spike.porcupine.org.
+ad: 0, rr: porcupine.org. 3600 IN MX 20 fist.porcupine.org.
+ad: 0, rr: porcupine.org. 3600 IN MX 30 m1.porcupine.org.
porcupine.org: fqdn: porcupine.org
--- /dev/null
+./test_dns_lookup: lookup spike.porcupine.org type A flags 2097152
+./test_dns_lookup: dns_query: spike.porcupine.org (A): OK
+./test_dns_lookup: dns_get_answer: type A for spike.porcupine.org
+./test_dns_lookup: dict_regexp_lookup: no-a.reg: spike.porcupine.org. 3600 IN A 168.100.189.2
+./test_dns_lookup: maps_find: DNS reply filter: regexp:no-a.reg(0,lock|fold_fix): spike.porcupine.org. 3600 IN A 168.100.189.2 = ignore
+./test_dns_lookup: ignoring DNS RR: spike.porcupine.org. 3600 IN A 168.100.189.2
+./test_dns_lookup: lookup spike.porcupine.org type AAAA flags 2097152
+./test_dns_lookup: dns_query: spike.porcupine.org (AAAA): OK
+./test_dns_lookup: dns_get_answer: type AAAA for spike.porcupine.org
+./test_dns_lookup: dict_regexp_lookup: no-a.reg: spike.porcupine.org. 3600 IN AAAA 2604:8d00:189::2
+./test_dns_lookup: maps_find: DNS reply filter: spike.porcupine.org. 3600 IN AAAA 2604:8d00:189::2: not found
+spike.porcupine.org: fqdn: spike.porcupine.org
+ad: 0, rr: spike.porcupine.org. 3600 IN AAAA 2604:8d00:189::2
--- /dev/null
+/ +a +/ ignore
--- /dev/null
+./test_dns_lookup: lookup spike.porcupine.org type A flags 2097152
+./test_dns_lookup: dns_query: spike.porcupine.org (A): OK
+./test_dns_lookup: dns_get_answer: type A for spike.porcupine.org
+./test_dns_lookup: dict_regexp_lookup: no-aaaa.reg: spike.porcupine.org. 3600 IN A 168.100.189.2
+./test_dns_lookup: maps_find: DNS reply filter: spike.porcupine.org. 3600 IN A 168.100.189.2: not found
+./test_dns_lookup: lookup spike.porcupine.org type AAAA flags 2097152
+./test_dns_lookup: dns_query: spike.porcupine.org (AAAA): OK
+./test_dns_lookup: dns_get_answer: type AAAA for spike.porcupine.org
+./test_dns_lookup: dict_regexp_lookup: no-aaaa.reg: spike.porcupine.org. 3600 IN AAAA 2604:8d00:189::2
+./test_dns_lookup: maps_find: DNS reply filter: regexp:no-aaaa.reg(0,lock|fold_fix): spike.porcupine.org. 3600 IN AAAA 2604:8d00:189::2 = ignore
+./test_dns_lookup: ignoring DNS RR: spike.porcupine.org. 3600 IN AAAA 2604:8d00:189::2
+spike.porcupine.org: fqdn: spike.porcupine.org
+ad: 0, rr: spike.porcupine.org. 3600 IN A 168.100.189.2
--- /dev/null
+/ +aaaa +/ ignore
--- /dev/null
+./test_dns_lookup: dict_regexp_lookup: no-mx.reg: porcupine.org. 3600 IN MX 10 spike.porcupine.org.
+./test_dns_lookup: dict_regexp_lookup: no-mx.reg: porcupine.org. 3600 IN MX 20 fist.porcupine.org.
+./test_dns_lookup: dict_regexp_lookup: no-mx.reg: porcupine.org. 3600 IN MX 30 m1.porcupine.org.
+./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_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.
+./test_dns_lookup: lookup porcupine.org type MX flags 2097152
+./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
--- /dev/null
+/ +mx +/ ignore
--- /dev/null
+/ +txt +/ ignore
./test_dns_lookup: dns_query: nullmx.porcupine.org (A): OK
./test_dns_lookup: dns_get_answer: type A for nullmx.porcupine.org
nullmx.porcupine.org: fqdn: nullmx.porcupine.org
-nullmx.porcupine.org: ad: 0, ttl: 3600 A: 168.100.189.13
+ad: 0, rr: nullmx.porcupine.org. 3600 IN A 168.100.189.13
#include "dns.h"
-static void print_rr(DNS_RR *rr)
+static void print_rr(VSTRING *buf, DNS_RR *rr)
{
- MAI_HOSTADDR_STR host;
- size_t i;
-
while (rr) {
- printf("%s: ad: %u, ttl: %9u ", rr->rname, rr->dnssec_valid, rr->ttl);
- switch (rr->type) {
- case T_A:
-#ifdef T_AAAA
- case T_AAAA:
-#endif
- if (dns_rr_to_pa(rr, &host) == 0)
- msg_fatal("conversion error for resource record type %s: %m",
- dns_strtype(rr->type));
- printf("%s: %s\n", dns_strtype(rr->type), host.buf);
- break;
- case T_CNAME:
- case T_DNAME:
- case T_MB:
- case T_MG:
- case T_MR:
- case T_NS:
- case T_PTR:
- case T_TXT:
- printf("%s: %s\n", dns_strtype(rr->type), rr->data);
- break;
- case T_MX:
- printf("pref: %d %s: %s\n",
- rr->pref, dns_strtype(rr->type), rr->data);
- break;
- case T_TLSA:
- if (rr->data_len >= 3) {
- uint8_t *ip = (uint8_t *) rr->data;
- uint8_t usage = *ip++;
- uint8_t selector = *ip++;
- uint8_t mtype = *ip++;
-
- printf("%s: %d %d %d ", dns_strtype(rr->type),
- usage, selector, mtype);
- for (i = 3; i < rr->data_len; ++i)
- printf("%02x", *ip++);
- putchar('\n');
- } else {
- printf("%s: truncated record\n", dns_strtype(rr->type));
- }
- break;
- default:
- msg_fatal("print_rr: don't know how to print type %s",
- dns_strtype(rr->type));
- }
+ vstream_printf("ad: %u, rr: %s\n",
+ rr->dnssec_valid, dns_strrecord(buf, rr));
rr = rr->next;
}
}
+static NORETURN usage(char **argv)
+{
+ msg_fatal("usage: %s [-v] [-f filter] types name", argv[0]);
+}
+
int main(int argc, char **argv)
{
ARGV *types_argv;
char *name;
VSTRING *fqdn = vstring_alloc(100);
VSTRING *why = vstring_alloc(100);
+ VSTRING *buf;
int rcode;
DNS_RR *rr;
int i;
+ int ch;
msg_vstream_init(argv[0], VSTREAM_ERR);
- if (argc != 3)
- msg_fatal("usage: %s types name", argv[0]);
- types_argv = argv_split(argv[1], CHARS_COMMA_SP);
+ while ((ch = GETOPT(argc, argv, "vf:")) > 0) {
+ switch (ch) {
+ msg_verbose++;
+ break;
+ case 'f':
+ dns_rr_filter_compile("DNS reply filter", optarg);
+ break;
+ default:
+ usage(argv);
+ }
+ }
+ if (argc != optind + 2)
+ usage(argv);
+ types_argv = argv_split(argv[optind], CHARS_COMMA_SP);
types = (unsigned *) mymalloc(sizeof(*types) * (types_argv->argc + 1));
for (i = 0; i < types_argv->argc; i++)
if ((types[i] = dns_type(types_argv->argv[i])) == 0)
msg_fatal("invalid query type: %s", types_argv->argv[i]);
types[i] = 0;
argv_free(types_argv);
- name = argv[2];
+ name = argv[optind + 1];
msg_verbose = 1;
switch (dns_lookup_rv(name, RES_USE_DNSSEC, &rr, fqdn, why,
&rcode, DNS_REQ_FLAG_NONE, types)) {
default:
msg_fatal("%s (rcode=%d)", vstring_str(why), rcode);
case DNS_OK:
- printf("%s: fqdn: %s\n", name, vstring_str(fqdn));
- print_rr(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((char *) types);
exit(0);
#define DEF_LOCAL_DSN_FILTER "$" VAR_DSN_FILTER
extern char *var_local_dsn_filter;
+ /*
+ * Optional DNS reply filter.
+ */
+#define VAR_SMTP_DNS_RE_FILTER "smtp_dns_reply_filter"
+#define DEF_SMTP_DNS_RE_FILTER ""
+#define VAR_LMTP_DNS_RE_FILTER "lmtp_dns_reply_filter"
+#define DEF_LMTP_DNS_RE_FILTER ""
+extern char *var_smtp_dns_re_filter;
+
+#define VAR_SMTPD_DNS_RE_FILTER "smtpd_dns_reply_filter"
+#define DEF_SMTPD_DNS_RE_FILTER ""
+extern char *var_smtpd_dns_re_filter;
+
/*
* Location of shared-library files.
*
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20141126"
+#define MAIL_RELEASE_DATE "20141130"
#define MAIL_VERSION_NUMBER "2.12"
#ifdef SNAPSHOT
VAR_LMTP_ADDR_PREF, DEF_LMTP_ADDR_PREF, &var_smtp_addr_pref, 1, 0,
VAR_LMTP_DNS_RES_OPT, DEF_LMTP_DNS_RES_OPT, &var_smtp_dns_res_opt, 0, 0,
VAR_LMTP_DSN_FILTER, DEF_LMTP_DSN_FILTER, &var_smtp_dsn_filter, 0, 0,
+ VAR_LMTP_DNS_RE_FILTER, DEF_LMTP_DNS_RE_FILTER, &var_smtp_dns_re_filter, 0, 0,
0,
};
static const CONFIG_TIME_TABLE lmtp_time_table[] = {
/* Optional filter for the \fBsmtp\fR(8) delivery agent to change the
/* delivery status code or explanatory text of successful or unsuccessful
/* deliveries.
+/* .IP "\fBsmtp_dns_reply_filter ($default_dns_reply_filter)\fR"
+/* Optional filter for Postfix SMTP client DNS lookup results.
/* MIME PROCESSING CONTROLS
/* .ad
/* .fi
bool var_smtp_rec_deadline;
bool var_smtp_dummy_mail_auth;
char *var_smtp_dsn_filter;
+char *var_smtp_dns_re_filter;
/* Special handling of 535 AUTH errors. */
char *var_smtp_sasl_auth_cache_name;
msg_fatal("bad %s value: %s", VAR_LMTP_SMTP(ADDR_PREF),
var_smtp_addr_pref);
}
+
+ /*
+ * DNS reply filter.
+ */
+ if (*var_smtp_dns_re_filter)
+ dns_rr_filter_compile(VAR_LMTP_SMTP(DNS_RE_FILTER),
+ var_smtp_dns_re_filter);
}
/* pre_accept - see if tables have changed */
case DNS_INVAL:
dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4");
return (addr_list);
+ case DNS_POLICY:
+ dsb_status(why, "4.7.0");
+ return (addr_list);
case DNS_NOTFOUND:
dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4");
/* maybe native naming service will succeed */
case DNS_UNAVAIL:
dsb_status(why, "5.1.0");
break;
+ case DNS_POLICY:
+ dsb_status(why, "4.7.0");
+ break;
case DNS_FAIL:
dsb_status(why, "5.4.3");
if (var_ign_mx_lookup_err)
VAR_SMTP_ADDR_PREF, DEF_SMTP_ADDR_PREF, &var_smtp_addr_pref, 1, 0,
VAR_SMTP_DNS_RES_OPT, DEF_SMTP_DNS_RES_OPT, &var_smtp_dns_res_opt, 0, 0,
VAR_SMTP_DSN_FILTER, DEF_SMTP_DSN_FILTER, &var_smtp_dsn_filter, 0, 0,
+ VAR_SMTP_DNS_RE_FILTER, DEF_SMTP_DNS_RE_FILTER, &var_smtp_dns_re_filter, 0, 0,
0,
};
static const CONFIG_TIME_TABLE smtp_time_table[] = {
tidy: clean
-tests: smtpd_check_test smtpd_check_test2 smtpd_acl_test smtpd_exp_test \
+broken-tests: smtpd_check_test smtpd_check_test2
+
+tests: smtpd_acl_test smtpd_exp_test \
smtpd_token_test smtpd_check_test4 smtpd_check_dsn_test \
smtpd_check_backup_test smtpd_dnswl_test smtpd_error_test \
- smtpd_server_test smtpd_nullmx_test
+ smtpd_server_test smtpd_nullmx_test smtpd_dns_filter_test
root_tests:
rm -f smtpd_exp.tmp smtpd_check_access.*
smtpd_server_test: smtpd_check smtpd_server.in smtpd_server.ref
- $(SHLIB_ENV) ../postmap/postmap hash:smtpd_check_access
$(SHLIB_ENV) ./smtpd_check <smtpd_server.in >smtpd_server.tmp 2>&1
diff smtpd_server.ref smtpd_server.tmp
rm -f smtpd_server.tmp smtpd_check_access.*
diff smtpd_nullmx.ref smtpd_nullmx.tmp
rm -f smtpd_nullmx.tmp smtpd_check_access.*
+smtpd_dns_filter_test: smtpd_check smtpd_dns_filter.in smtpd_dns_filter.ref \
+ ../dns/no-mx.reg ../dns/no-a.reg ../dns/error.reg
+ $(SHLIB_ENV) ./smtpd_check <smtpd_dns_filter.in 2>&1 | \
+ sed 's/\. [0-9]* IN/. TTL IN/' >smtpd_dns_filter.tmp
+ diff smtpd_dns_filter.ref smtpd_dns_filter.tmp
+ rm -f smtpd_dns_filter.tmp
+
smtpd_check_dsn_test: smtpd_check smtpd_check_dsn.in smtpd_check_dsn.ref smtpd_check_access
$(SHLIB_ENV) ../postmap/postmap hash:smtpd_check_access
$(SHLIB_ENV) ./smtpd_check <smtpd_check_dsn.in >smtpd_check.tmp 2>&1
/* time limit per read or write system call, to a time limit to send
/* or receive a complete record (an SMTP command line, SMTP response
/* line, SMTP message content line, or TLS protocol message).
+/* .PP
+/* Available in Postfix version 2.12 and later:
+/* .IP "\fBsmtpd_dns_reply_filter (empty)\fR"
+/* Optional filter for Postfix SMTP server DNS lookup results.
/* ADDRESS REWRITING CONTROLS
/* .ad
/* .fi
/* request is rejected by the reject_unauth_destination recipient
/* restriction.
/* .IP "\fBunknown_address_reject_code (450)\fR"
-/* The numerical Postfix SMTP server response code when a sender or
-/* recipient address is rejected by the reject_unknown_sender_domain
-/* or reject_unknown_recipient_domain restriction.
+/* The numerical response code when the Postfix SMTP rejects a sender
+/* or recipient address because its domain is unknown.
/* .IP "\fBunknown_client_reject_code (450)\fR"
/* The numerical Postfix SMTP server response code when a client
/* without valid address <=> name mapping is rejected by the
char *var_smtpd_cmd_filter;
char *var_smtpd_rej_footer;
char *var_smtpd_acl_perm_log;
+char *var_smtpd_dns_re_filter;
#ifdef USE_TLS
char *var_smtpd_relay_ccerts;
ehlo_discard_maps = maps_create(VAR_SMTPD_EHLO_DIS_MAPS,
var_smtpd_ehlo_dis_maps,
DICT_FLAG_LOCK);
+
+ /*
+ * DNS reply filter.
+ */
+ if (*var_smtpd_dns_re_filter)
+ dns_rr_filter_compile(VAR_SMTPD_DNS_RE_FILTER,
+ var_smtpd_dns_re_filter);
}
/* post_jail_init - post-jail initialization */
VAR_SMTPD_ACL_PERM_LOG, DEF_SMTPD_ACL_PERM_LOG, &var_smtpd_acl_perm_log, 0, 0,
VAR_SMTPD_UPROXY_PROTO, DEF_SMTPD_UPROXY_PROTO, &var_smtpd_uproxy_proto, 0, 0,
VAR_SMTPD_POLICY_DEF_ACTION, DEF_SMTPD_POLICY_DEF_ACTION, &var_smtpd_policy_def_action, 1, 0,
+ VAR_SMTPD_DNS_RE_FILTER, DEF_SMTPD_DNS_RE_FILTER, &var_smtpd_dns_re_filter, 0, 0,
0,
};
static const CONFIG_RAW_TABLE raw_table[] = {
RR_ADDR_TYPES, T_MX, 0);
if (dummy)
dns_rr_free(dummy);
- if (dns_status != DNS_OK) { /* incl. DNS_INVAL */
- /* We don't care about DNS_UNAVAIL. */
+ /* Allow MTA names to have nullMX records. */
+ if (dns_status != DNS_OK && dns_status != DNS_UNAVAIL) {
+ if (dns_status == DNS_POLICY) {
+ msg_warn("%s: address or MX lookup error: %s",
+ name, "DNS reply filter drops all results");
+ return (SMTPD_CHECK_DUNNO);
+ }
if (dns_status != DNS_RETRY)
return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
var_unk_name_code, "4.7.1",
#endif
#define MAILHOST_LOOKUP_FLAGS \
- (DNS_REQ_FLAG_STOP_OK | DNS_REQ_FLAG_STOP_INVAL | DNS_REQ_FLAG_STOP_UNAVAIL)
+ (DNS_REQ_FLAG_STOP_OK | DNS_REQ_FLAG_STOP_INVAL | \
+ DNS_REQ_FLAG_STOP_UNAVAIL | DNS_REQ_FLAG_STOP_MX_POLICY)
dns_status = dns_lookup_l(name, 0, &dummy, (VSTRING *) 0,
(VSTRING *) 0, MAILHOST_LOOKUP_FLAGS,
if (dummy)
dns_rr_free(dummy);
if (dns_status != DNS_OK) { /* incl. DNS_INVAL */
+ if (dns_status == DNS_POLICY) {
+ msg_warn("%s: MX or address lookup error: %s",
+ name, "DNS reply filter drops all results");
+ return (SMTPD_CHECK_DUNNO);
+ }
if (dns_status == DNS_UNAVAIL)
return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
var_nullmx_rcode,
DNS_REQ_FLAG_NONE, inet_proto_info()->dns_atype_list);
/* DNS_UNAVAIL is not applicable here. */
if (dns_status != DNS_OK) { /* incl. DNS_INVAL */
- DEFER_IF_REJECT3(state, MAIL_ERROR_POLICY,
+ DEFER_IF_REJECT4(state, MAIL_ERROR_POLICY,
450, "4.4.4",
- "<%s>: %s rejected: Unable to look up host %s as mail exchanger",
- reply_name, reply_class, host);
+ "<%s>: %s rejected: Unable to look up host "
+ "%s as mail exchanger: %s",
+ reply_name, reply_class, host,
+ dns_status == DNS_POLICY ?
+ "DNS reply filter policy" : dns_strerror(h_errno));
return (NOPE);
}
for (rr = addr_list; rr != 0; rr = rr->next) {
#endif
if (dns_status != DNS_OK) { /* incl. DNS_INVAL */
/* We don't care about DNS_UNAVAIL. */
- if (dns_status == DNS_RETRY)
- DEFER_IF_REJECT2(state, MAIL_ERROR_POLICY,
+ if (dns_status == DNS_RETRY || dns_status == DNS_POLICY)
+ DEFER_IF_REJECT3(state, MAIL_ERROR_POLICY,
450, "4.4.4",
- "<%s>: %s rejected: Unable to look up mail exchanger information",
- reply_name, reply_class);
+ "<%s>: %s rejected: Unable to look up mail "
+ "exchanger information: %s",
+ reply_name, reply_class, dns_status == DNS_POLICY ?
+ "DNS reply filter policy" : dns_strerror(h_errno));
return (SMTPD_CHECK_DUNNO);
}
}
if (dns_status != DNS_OK) {
msg_warn("Unable to look up %s host for %s: %s", dns_strtype(type),
- domain && domain[1] ? domain : name, dns_strerror(h_errno));
+ domain && domain[1] ? domain : name, dns_status == DNS_POLICY ?
+ "DNS reply filter policy" : dns_strerror(h_errno));
return (SMTPD_CHECK_DUNNO);
}
}
#define RBL_TXT_LIMIT 500
rbl = (SMTPD_RBL_STATE *) mymalloc(sizeof(*rbl));
- if (dns_lookup(query, T_TXT, 0, &txt_list,
- (VSTRING *) 0, (VSTRING *) 0) == DNS_OK) {
+ dns_status = dns_lookup(query, T_TXT, 0, &txt_list,
+ (VSTRING *) 0, (VSTRING *) 0);
+ if (dns_status == DNS_OK) {
buf = vstring_alloc(1);
space_left = RBL_TXT_LIMIT;
for (rr = txt_list; rr != 0 && space_left > 0; rr = next) {
}
rbl->txt = vstring_export(buf);
dns_rr_free(txt_list);
- } else
+ } else {
+ if (dns_status == DNS_POLICY)
+ msg_warn("%s: TXT lookup error: %s",
+ query, "DNS reply filter drops all results");
rbl->txt = 0;
+ }
rbl->a = addr_list;
return ((void *) rbl);
}
#include <mail_conf.h>
#include <rewrite_clnt.h>
+#include <dns.h>
#include <smtpd_chat.h>
VAR_UNV_FROM_TF_ACT, DEF_REJECT_TMPF_ACT, &var_unv_from_tf_act,
/* XXX Can't use ``$name'' type default values above. */
VAR_SMTPD_ACL_PERM_LOG, DEF_SMTPD_ACL_PERM_LOG, &var_smtpd_acl_perm_log,
+ VAR_SMTPD_DNS_RE_FILTER, DEF_SMTPD_DNS_RE_FILTER, &var_smtpd_dns_re_filter,
0,
};
bool var_smtpd_peername_lookup;
bool var_smtpd_client_port_log;
int var_nullmx_rcode;
+char *var_smtpd_dns_re_filter;
#define int_table test_int_table
* Special case: rewrite context.
*/
case 1:
- if (strcasecmp(args->argv[0], "rewrite") == 0)
+ if (strcasecmp(args->argv[0], "rewrite") == 0) {
resp = smtpd_check_rewrite(&state);
- break;
+ break;
+ }
+
+ /*
+ * Other parameter-less commands.
+ */
+ if (strcasecmp(args->argv[0], "flush_dnsxl_cache") == 0) {
+ if (smtpd_rbl_cache) {
+ ctable_free(smtpd_rbl_cache);
+ ctable_free(smtpd_rbl_byte_cache);
+ }
+ smtpd_rbl_cache = ctable_create(100, rbl_pagein,
+ rbl_pageout, (void *) 0);
+ smtpd_rbl_byte_cache = ctable_create(1000, rbl_byte_pagein,
+ rbl_byte_pageout, (void *) 0);
+ resp = 0;
+ break;
+ }
/*
* Special case: client identity.
resp = 0;
break;
}
+ if (strcasecmp(args->argv[0], VAR_SMTPD_DNS_RE_FILTER) == 0) {
+ /* NOT: UPDATE_STRING */
+ dns_rr_filter_compile(VAR_SMTPD_DNS_RE_FILTER, args->argv[1]);
+ resp = 0;
+ break;
+ }
#ifdef USE_TLS
if (strcasecmp(args->argv[0], VAR_RELAY_CCERTS) == 0) {
UPDATE_STRING(var_smtpd_relay_ccerts, args->argv[1]);
sender_restrictions <restrictions>\n\
recipient_restrictions <restrictions>\n\
restriction_class name,<restrictions>\n\
+ flush_dnsxl_cache\n\
\n\
Note: no address rewriting \n";
break;
rbl_code=$rbl_code rbl_domain=$rbl_domain rbl_txt=$rbl_txt rbl_what=$rbl_what
rbl_class=$rbl_class
-dsn.rfc-ignorant.org $rbl_code client=$client
+rhsbl.porcupine.org $rbl_code client=$client
+ client_address=$client_address
+ client_name=$client_name helo_name=$helo_name
+ sender=$sender sender_name=$sender_name sender_domain=$sender_domain
+ recipient=$recipient recipient_name=$recipient_name recipient_domain=$recipient_domain
+ rbl_code=$rbl_code rbl_domain=$rbl_domain rbl_txt=$rbl_txt rbl_what=$rbl_what
+ rbl_class=$rbl_class
+
+dnswl.porcupine.org $rbl_code client=$client
client_address=$client_address
client_name=$client_name helo_name=$helo_name
sender=$sender sender_name=$sender_name sender_domain=$sender_domain
--- /dev/null
+#
+# Initialize
+#
+client localhost 127.0.0.1
+smtpd_delay_reject 0
+#
+# Test reject_unknown_helo_hostname
+#
+smtpd_dns_reply_filter regexp:../dns/no-mx.reg
+helo_restrictions reject_unknown_helo_hostname,permit
+# EXPECT OK + "all MX records dropped" warning.
+helo xn--1xa.porcupine.org
+# EXPECT OK (nullmx has A record)
+helo nullmx.porcupine.org
+# EXPECT reject (nxdomain is not filtered).
+helo nxdomain.porcupine.org
+smtpd_dns_reply_filter regexp:../dns/no-a.reg
+# EXPECT OK (host has AAAA record).
+mail user@spike.porcupine.org
+helo spike.porcupine.org
+# EXPECT OK + "all A records dropped" warning + no delayed reject.
+helo umbilical.porcupine.org
+mail user@spike.porcupine.org
+rcpt user@spike.porcupine.org
+smtpd_dns_reply_filter regexp:../dns/error.reg
+# EXPECT OK + "filter config error" warning + delayed reject.
+helo spike.porcupine.org
+mail user@spike.porcupine.org
+rcpt user@spike.porcupine.org
+# EXPECT OK + "filter config error" warning (nullmx has A record) + delayed reject.
+helo nullmx.porcupine.org
+mail user@spike.porcupine.org
+rcpt user@spike.porcupine.org
+# EXPECT reject (nxdomain is not filtered).
+helo nxdomain.porcupine.org
+#
+# Test reject_unknown_sender_domain (same code as
+# reject_unknown_recipient_domain).
+#
+smtpd_dns_reply_filter regexp:../dns/no-mx.reg
+helo localhost
+sender_restrictions reject_unknown_sender_domain
+# EXPECT OK + "all MX records dropped" warning.
+mail user@xn--1xa.porcupine.org
+# EXPECT reject (nullmx is not filtered).
+mail user@nullmx.porcupine.org
+# EXPECT reject (nxdomain is not filtered).
+mail user@nxdomain.porcupine.org
+# EXPECT OK
+mail user@localhost
+smtpd_dns_reply_filter regexp:../dns/no-a.reg
+# EXPECT OK (host has AAAA record).
+mail user@spike.porcupine.org
+# EXPECT OK + "all A records dropped" warning.
+mail user@umbilical.porcupine.org
+smtpd_dns_reply_filter regexp:../dns/error.reg
+# EXPECT OK + "filter config error" warning + delayed reject.
+mail user@xn--1xa.porcupine.org
+rcpt user
+# EXPECT reject (nullmx is not filtered).
+mail user@nullmx.porcupine.org
+# EXPECT reject (nxdomain is not filtered).
+mail user@nxdomain.porcupine.org
+#
+# Test reject_rbl_client
+#
+client_restrictions reject_rbl_client,dnsbltest.porcupine.org
+smtpd_dns_reply_filter regexp:../dns/no-mx.reg
+flush_dnsxl_cache
+# EXPECT reject + A and TXT record.
+client localhost 127.0.0.2
+smtpd_dns_reply_filter regexp:../dns/no-a.reg
+flush_dnsxl_cache
+# EXPECT OK + "all A results dropped" warning.
+client localhost 127.0.0.2
+smtpd_dns_reply_filter regexp:../dns/no-txt.reg
+flush_dnsxl_cache
+# EXPECT reject + A record, "all TXT results dropped" warning.
+client localhost 127.0.0.2
+smtpd_dns_reply_filter regexp:../dns/error.reg
+flush_dnsxl_cache
+# EXPECT OK + "filter configuration error"
+client localhost 127.0.0.2
--- /dev/null
+>>> #
+>>> # Initialize
+>>> #
+>>> client localhost 127.0.0.1
+OK
+>>> smtpd_delay_reject 0
+OK
+>>> #
+>>> # Test reject_unknown_helo_hostname
+>>> #
+>>> smtpd_dns_reply_filter regexp:../dns/no-mx.reg
+OK
+>>> helo_restrictions reject_unknown_helo_hostname,permit
+OK
+>>> # EXPECT OK + "all MX records dropped" warning.
+>>> helo xn--1xa.porcupine.org
+./smtpd_check: ignoring DNS RR: xn--1xa.porcupine.org. TTL IN MX 10 spike.porcupine.org.
+./smtpd_check: warning: xn--1xa.porcupine.org: address or MX lookup error: DNS reply filter drops all results
+OK
+>>> # EXPECT OK (nullmx has A record)
+>>> helo nullmx.porcupine.org
+OK
+>>> # EXPECT reject (nxdomain is not filtered).
+>>> helo nxdomain.porcupine.org
+./smtpd_check: <queue id>: reject: HELO from localhost[127.0.0.1]: 450 4.7.1 <nxdomain.porcupine.org>: Helo command rejected: Host not found; proto=SMTP helo=<nxdomain.porcupine.org>
+450 4.7.1 <nxdomain.porcupine.org>: Helo command rejected: Host not found
+>>> smtpd_dns_reply_filter regexp:../dns/no-a.reg
+OK
+>>> # EXPECT OK (host has AAAA record).
+>>> mail user@spike.porcupine.org
+OK
+>>> helo spike.porcupine.org
+./smtpd_check: ignoring DNS RR: spike.porcupine.org. TTL IN A 168.100.189.2
+OK
+>>> # EXPECT OK + "all A records dropped" warning + no delayed reject.
+>>> helo umbilical.porcupine.org
+./smtpd_check: ignoring DNS RR: umbilical.porcupine.org. TTL IN A 168.100.189.1
+./smtpd_check: warning: umbilical.porcupine.org: address or MX lookup error: DNS reply filter drops all results
+OK
+>>> mail user@spike.porcupine.org
+OK
+>>> rcpt user@spike.porcupine.org
+OK
+>>> smtpd_dns_reply_filter regexp:../dns/error.reg
+OK
+>>> # EXPECT OK + "filter config error" warning + delayed reject.
+>>> helo spike.porcupine.org
+./smtpd_check: warning: smtpd_dns_reply_filter: unknown DNS filter action: "oops"
+./smtpd_check: warning: smtpd_dns_reply_filter: unknown DNS filter action: "oops"
+OK
+>>> mail user@spike.porcupine.org
+OK
+>>> rcpt user@spike.porcupine.org
+./smtpd_check: <queue id>: reject: RCPT from localhost[127.0.0.1]: 450 4.7.1 <spike.porcupine.org>: Helo command rejected: Host not found; from=<user@spike.porcupine.org> to=<user@spike.porcupine.org> proto=SMTP helo=<spike.porcupine.org>
+450 4.7.1 <spike.porcupine.org>: Helo command rejected: Host not found
+>>> # EXPECT OK + "filter config error" warning (nullmx has A record) + delayed reject.
+>>> helo nullmx.porcupine.org
+./smtpd_check: warning: smtpd_dns_reply_filter: unknown DNS filter action: "oops"
+OK
+>>> mail user@spike.porcupine.org
+OK
+>>> rcpt user@spike.porcupine.org
+./smtpd_check: <queue id>: reject: RCPT from localhost[127.0.0.1]: 450 4.7.1 <nullmx.porcupine.org>: Helo command rejected: Host not found; from=<user@spike.porcupine.org> to=<user@spike.porcupine.org> proto=SMTP helo=<nullmx.porcupine.org>
+450 4.7.1 <nullmx.porcupine.org>: Helo command rejected: Host not found
+>>> # EXPECT reject (nxdomain is not filtered).
+>>> helo nxdomain.porcupine.org
+./smtpd_check: <queue id>: reject: HELO from localhost[127.0.0.1]: 450 4.7.1 <nxdomain.porcupine.org>: Helo command rejected: Host not found; from=<user@spike.porcupine.org> proto=SMTP helo=<nxdomain.porcupine.org>
+450 4.7.1 <nxdomain.porcupine.org>: Helo command rejected: Host not found
+>>> #
+>>> # Test reject_unknown_sender_domain (same code as
+>>> # reject_unknown_recipient_domain).
+>>> #
+>>> smtpd_dns_reply_filter regexp:../dns/no-mx.reg
+OK
+>>> helo localhost
+OK
+>>> sender_restrictions reject_unknown_sender_domain
+OK
+>>> # EXPECT OK + "all MX records dropped" warning.
+>>> mail user@xn--1xa.porcupine.org
+./smtpd_check: ignoring DNS RR: xn--1xa.porcupine.org. TTL IN MX 10 spike.porcupine.org.
+./smtpd_check: warning: xn--1xa.porcupine.org: MX or address lookup error: DNS reply filter drops all results
+OK
+>>> # EXPECT reject (nullmx is not filtered).
+>>> mail user@nullmx.porcupine.org
+./smtpd_check: <queue id>: reject: MAIL from localhost[127.0.0.1]: 556 5.7.0 <user@nullmx.porcupine.org>: Sender address rejected: Domain nullmx.porcupine.org does not accept mail; from=<user@nullmx.porcupine.org> proto=SMTP helo=<localhost>
+556 5.7.0 <user@nullmx.porcupine.org>: Sender address rejected: Domain nullmx.porcupine.org does not accept mail
+>>> # EXPECT reject (nxdomain is not filtered).
+>>> mail user@nxdomain.porcupine.org
+./smtpd_check: <queue id>: reject: MAIL from localhost[127.0.0.1]: 450 4.1.8 <user@nxdomain.porcupine.org>: Sender address rejected: Domain not found; from=<user@nxdomain.porcupine.org> proto=SMTP helo=<localhost>
+450 4.1.8 <user@nxdomain.porcupine.org>: Sender address rejected: Domain not found
+>>> # EXPECT OK
+>>> mail user@localhost
+OK
+>>> smtpd_dns_reply_filter regexp:../dns/no-a.reg
+OK
+>>> # EXPECT OK (host has AAAA record).
+>>> mail user@spike.porcupine.org
+./smtpd_check: ignoring DNS RR: spike.porcupine.org. TTL IN A 168.100.189.2
+OK
+>>> # EXPECT OK + "all A records dropped" warning.
+>>> mail user@umbilical.porcupine.org
+./smtpd_check: ignoring DNS RR: umbilical.porcupine.org. TTL IN A 168.100.189.1
+./smtpd_check: warning: umbilical.porcupine.org: MX or address lookup error: DNS reply filter drops all results
+OK
+>>> smtpd_dns_reply_filter regexp:../dns/error.reg
+OK
+>>> # EXPECT OK + "filter config error" warning + delayed reject.
+>>> mail user@xn--1xa.porcupine.org
+./smtpd_check: warning: smtpd_dns_reply_filter: unknown DNS filter action: "oops"
+OK
+>>> rcpt user
+./smtpd_check: <queue id>: reject: RCPT from localhost[127.0.0.1]: 450 4.1.8 <user@xn--1xa.porcupine.org>: Sender address rejected: Domain not found; from=<user@xn--1xa.porcupine.org> to=<user> proto=SMTP helo=<localhost>
+450 4.1.8 <user@xn--1xa.porcupine.org>: Sender address rejected: Domain not found
+>>> # EXPECT reject (nullmx is not filtered).
+>>> mail user@nullmx.porcupine.org
+./smtpd_check: <queue id>: reject: MAIL from localhost[127.0.0.1]: 556 5.7.0 <user@nullmx.porcupine.org>: Sender address rejected: Domain nullmx.porcupine.org does not accept mail; from=<user@nullmx.porcupine.org> proto=SMTP helo=<localhost>
+556 5.7.0 <user@nullmx.porcupine.org>: Sender address rejected: Domain nullmx.porcupine.org does not accept mail
+>>> # EXPECT reject (nxdomain is not filtered).
+>>> mail user@nxdomain.porcupine.org
+./smtpd_check: <queue id>: reject: MAIL from localhost[127.0.0.1]: 450 4.1.8 <user@nxdomain.porcupine.org>: Sender address rejected: Domain not found; from=<user@nxdomain.porcupine.org> proto=SMTP helo=<localhost>
+450 4.1.8 <user@nxdomain.porcupine.org>: Sender address rejected: Domain not found
+>>> #
+>>> # Test reject_rbl_client
+>>> #
+>>> client_restrictions reject_rbl_client,dnsbltest.porcupine.org
+OK
+>>> smtpd_dns_reply_filter regexp:../dns/no-mx.reg
+OK
+>>> flush_dnsxl_cache
+OK
+>>> # EXPECT reject + A and TXT record.
+>>> client localhost 127.0.0.2
+./smtpd_check: <queue id>: 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; DNS blocklist test; from=<user@nxdomain.porcupine.org> proto=SMTP helo=<localhost>
+554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; DNS blocklist test
+>>> smtpd_dns_reply_filter regexp:../dns/no-a.reg
+OK
+>>> flush_dnsxl_cache
+OK
+>>> # EXPECT OK + "all A results dropped" warning.
+>>> client localhost 127.0.0.2
+./smtpd_check: ignoring DNS RR: 2.0.0.127.dnsbltest.porcupine.org. TTL IN A 127.0.0.2
+./smtpd_check: warning: 2.0.0.127.dnsbltest.porcupine.org: RBL lookup error: Error looking up name=2.0.0.127.dnsbltest.porcupine.org type=A: DNS reply filter drops all results
+OK
+>>> smtpd_dns_reply_filter regexp:../dns/no-txt.reg
+OK
+>>> flush_dnsxl_cache
+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: warning: 2.0.0.127.dnsbltest.porcupine.org: TXT lookup error: DNS reply filter drops all results
+./smtpd_check: <queue id>: 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=<user@nxdomain.porcupine.org> proto=SMTP helo=<localhost>
+554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org
+>>> smtpd_dns_reply_filter regexp:../dns/error.reg
+OK
+>>> flush_dnsxl_cache
+OK
+>>> # EXPECT OK + "filter configuration error"
+>>> client localhost 127.0.0.2
+./smtpd_check: warning: smtpd_dns_reply_filter: unknown DNS filter action: "oops"
+./smtpd_check: warning: 2.0.0.127.dnsbltest.porcupine.org: RBL lookup error: Error looking up name=2.0.0.127.dnsbltest.porcupine.org type=A: Invalid DNS reply filter syntax
+OK
#
# Whitelist overrides reject.
-client_restrictions permit_rhswl_client,dsn.rfc-ignorant.org,reject
+client_restrictions permit_rhswl_client,dnswl.porcupine.org,reject
# Non-whitelisted client name - reject.
client spike.porcupine.org 168.100.189.2
# Whitelisted client name - accept.
# Whitelist does not override reject_unauth_destination.
client_restrictions permit
-recipient_restrictions permit_rhswl_client,dsn.rfc-ignorant.org,reject_unauth_destination
+recipient_restrictions permit_rhswl_client,dnswl.porcupine.org,reject_unauth_destination
# Non-whitelisted client name.
client spike.porcupine.org 168.100.189.2
# Unauthorized destination - reject.
>>> #
>>>
>>> # Whitelist overrides reject.
->>> client_restrictions permit_rhswl_client,dsn.rfc-ignorant.org,reject
+>>> client_restrictions permit_rhswl_client,dnswl.porcupine.org,reject
OK
>>> # Non-whitelisted client name - reject.
>>> client spike.porcupine.org 168.100.189.2
>>> # Whitelist does not override reject_unauth_destination.
>>> client_restrictions permit
OK
->>> recipient_restrictions permit_rhswl_client,dsn.rfc-ignorant.org,reject_unauth_destination
+>>> recipient_restrictions permit_rhswl_client,dnswl.porcupine.org,reject_unauth_destination
OK
>>> # Non-whitelisted client name.
>>> client spike.porcupine.org 168.100.189.2
#
# RHSBL sender domain name
#
-recipient_restrictions reject_rhsbl_sender,dsn.rfc-ignorant.org
+recipient_restrictions reject_rhsbl_sender,rhsbl.porcupine.org
client spike.porcupine.org 168.100.189.2
mail sname@example.tld
rcpt rname@rdomain
#
# RHSBL client domain name
#
-recipient_restrictions reject_rhsbl_client,dsn.rfc-ignorant.org
+recipient_restrictions reject_rhsbl_client,rhsbl.porcupine.org
client example.tld 1.2.3.4
mail sname@sdomain
rcpt rname@rdomain
#
# RHSBL recipient domain name
#
-recipient_restrictions reject_rhsbl_recipient,dsn.rfc-ignorant.org
+recipient_restrictions reject_rhsbl_recipient,rhsbl.porcupine.org
client spike.porcupine.org 168.100.189.2
mail sname@sdomain
rcpt rname@rdomain
#
# RHSBL helo domain name
#
-recipient_restrictions reject_rhsbl_helo,abuse.rfc-ignorant.org
+recipient_restrictions reject_rhsbl_helo,rhsbl.porcupine.org
helo example.tld
mail sname@sdomain
rcpt rname@rdomain
>>> client foo 127.0.0.2
OK
>>> rcpt rname@rdomain
-./smtpd_check: <queue id>: reject: RCPT from foo[127.0.0.2]: 554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar>
-554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org
+./smtpd_check: <queue id>: reject: RCPT from foo[127.0.0.2]: 554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; DNS blocklist test; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar>
+554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; DNS blocklist test
>>> #
>>> recipient_restrictions reject_rbl_client,dnsbltest.porcupine.org
OK
>>> client foo 127.0.0.2
OK
>>> rcpt rname@rdomain
-./smtpd_check: <queue id>: reject: RCPT from foo[127.0.0.2]: 554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar>
-554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org
+./smtpd_check: <queue id>: reject: RCPT from foo[127.0.0.2]: 554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; DNS blocklist test; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar>
+554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; DNS blocklist test
>>> recipient_restrictions reject_rbl_client,dnsbltest.porcupine.org=127.0.0.2
OK
>>> client foo 127.0.0.2
OK
>>> rcpt rname@rdomain
-./smtpd_check: <queue id>: reject: RCPT from foo[127.0.0.2]: 554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar>
-554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org
+./smtpd_check: <queue id>: reject: RCPT from foo[127.0.0.2]: 554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; DNS blocklist test; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar>
+554 5.7.1 Service unavailable; Client host [127.0.0.2] blocked using dnsbltest.porcupine.org; DNS blocklist test
>>> client foo 127.0.0.1
OK
>>> rcpt rname@rdomain
>>> #
>>> # RHSBL sender domain name
>>> #
->>> recipient_restrictions reject_rhsbl_sender,dsn.rfc-ignorant.org
+>>> recipient_restrictions reject_rhsbl_sender,rhsbl.porcupine.org
OK
>>> client spike.porcupine.org 168.100.189.2
OK
>>> mail sname@example.tld
OK
>>> rcpt rname@rdomain
-./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 client=spike.porcupine.org[168.100.189.2] client_address=168.100.189.2 client_name=spike.porcupine.org helo_name=foobar sender=sname@example.tld sender_name=sname sender_domain=example.tld recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=dsn.rfc-ignorant.org rbl_txt=Not supporting null originator (DSN) rbl_what=sname@example.tld rbl_class=Sender address; from=<sname@example.tld> to=<rname@rdomain> proto=SMTP helo=<foobar>
-554 5.7.1 client=spike.porcupine.org[168.100.189.2] client_address=168.100.189.2 client_name=spike.porcupine.org helo_name=foobar sender=sname@example.tld sender_name=sname sender_domain=example.tld recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=dsn.rfc-ignorant.org rbl_txt=Not supporting null originator (DSN) rbl_what=sname@example.tld rbl_class=Sender address
+./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 client=spike.porcupine.org[168.100.189.2] client_address=168.100.189.2 client_name=spike.porcupine.org helo_name=foobar sender=sname@example.tld sender_name=sname sender_domain=example.tld recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=rhsbl.porcupine.org rbl_txt=RHSBL test rbl_what=sname@example.tld rbl_class=Sender address; from=<sname@example.tld> to=<rname@rdomain> proto=SMTP helo=<foobar>
+554 5.7.1 client=spike.porcupine.org[168.100.189.2] client_address=168.100.189.2 client_name=spike.porcupine.org helo_name=foobar sender=sname@example.tld sender_name=sname sender_domain=example.tld recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=rhsbl.porcupine.org rbl_txt=RHSBL test rbl_what=sname@example.tld rbl_class=Sender address
>>> mail sname@sdomain
OK
>>> rcpt rname@rdomain
>>> #
>>> # RHSBL client domain name
>>> #
->>> recipient_restrictions reject_rhsbl_client,dsn.rfc-ignorant.org
+>>> recipient_restrictions reject_rhsbl_client,rhsbl.porcupine.org
OK
>>> client example.tld 1.2.3.4
OK
>>> mail sname@sdomain
OK
>>> rcpt rname@rdomain
-./smtpd_check: <queue id>: reject: RCPT from example.tld[1.2.3.4]: 554 5.7.1 client=example.tld[1.2.3.4] client_address=1.2.3.4 client_name=example.tld helo_name=foobar sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=dsn.rfc-ignorant.org rbl_txt=Not supporting null originator (DSN) rbl_what=example.tld rbl_class=Client host; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar>
-554 5.7.1 client=example.tld[1.2.3.4] client_address=1.2.3.4 client_name=example.tld helo_name=foobar sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=dsn.rfc-ignorant.org rbl_txt=Not supporting null originator (DSN) rbl_what=example.tld rbl_class=Client host
+./smtpd_check: <queue id>: reject: RCPT from example.tld[1.2.3.4]: 554 5.7.1 client=example.tld[1.2.3.4] client_address=1.2.3.4 client_name=example.tld helo_name=foobar sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=rhsbl.porcupine.org rbl_txt=RHSBL test rbl_what=example.tld rbl_class=Client host; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<foobar>
+554 5.7.1 client=example.tld[1.2.3.4] client_address=1.2.3.4 client_name=example.tld helo_name=foobar sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=rhsbl.porcupine.org rbl_txt=RHSBL test rbl_what=example.tld rbl_class=Client host
>>> #
>>> # RHSBL recipient domain name
>>> #
->>> recipient_restrictions reject_rhsbl_recipient,dsn.rfc-ignorant.org
+>>> recipient_restrictions reject_rhsbl_recipient,rhsbl.porcupine.org
OK
>>> client spike.porcupine.org 168.100.189.2
OK
>>> rcpt rname@rdomain
OK
>>> rcpt rname@example.tld
-./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 client=spike.porcupine.org[168.100.189.2] client_address=168.100.189.2 client_name=spike.porcupine.org helo_name=foobar sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@example.tld recipient_name=rname recipient_domain=example.tld rbl_code=554 rbl_domain=dsn.rfc-ignorant.org rbl_txt=Not supporting null originator (DSN) rbl_what=rname@example.tld rbl_class=Recipient address; from=<sname@sdomain> to=<rname@example.tld> proto=SMTP helo=<foobar>
-554 5.7.1 client=spike.porcupine.org[168.100.189.2] client_address=168.100.189.2 client_name=spike.porcupine.org helo_name=foobar sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@example.tld recipient_name=rname recipient_domain=example.tld rbl_code=554 rbl_domain=dsn.rfc-ignorant.org rbl_txt=Not supporting null originator (DSN) rbl_what=rname@example.tld rbl_class=Recipient address
+./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 client=spike.porcupine.org[168.100.189.2] client_address=168.100.189.2 client_name=spike.porcupine.org helo_name=foobar sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@example.tld recipient_name=rname recipient_domain=example.tld rbl_code=554 rbl_domain=rhsbl.porcupine.org rbl_txt=RHSBL test rbl_what=rname@example.tld rbl_class=Recipient address; from=<sname@sdomain> to=<rname@example.tld> proto=SMTP helo=<foobar>
+554 5.7.1 client=spike.porcupine.org[168.100.189.2] client_address=168.100.189.2 client_name=spike.porcupine.org helo_name=foobar sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@example.tld recipient_name=rname recipient_domain=example.tld rbl_code=554 rbl_domain=rhsbl.porcupine.org rbl_txt=RHSBL test rbl_what=rname@example.tld rbl_class=Recipient address
>>> #
>>> # RHSBL helo domain name
>>> #
->>> recipient_restrictions reject_rhsbl_helo,abuse.rfc-ignorant.org
+>>> recipient_restrictions reject_rhsbl_helo,rhsbl.porcupine.org
OK
>>> helo example.tld
OK
>>> mail sname@sdomain
OK
>>> rcpt rname@rdomain
-./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 Service unavailable; Helo command [example.tld] blocked using abuse.rfc-ignorant.org; Not supporting abuse@domain; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<example.tld>
-554 5.7.1 Service unavailable; Helo command [example.tld] blocked using abuse.rfc-ignorant.org; Not supporting abuse@domain
+./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 client=spike.porcupine.org[168.100.189.2] client_address=168.100.189.2 client_name=spike.porcupine.org helo_name=example.tld sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=rhsbl.porcupine.org rbl_txt=RHSBL test rbl_what=example.tld rbl_class=Helo command; from=<sname@sdomain> to=<rname@rdomain> proto=SMTP helo=<example.tld>
+554 5.7.1 client=spike.porcupine.org[168.100.189.2] client_address=168.100.189.2 client_name=spike.porcupine.org helo_name=example.tld sender=sname@sdomain sender_name=sname sender_domain=sdomain recipient=rname@rdomain recipient_name=rname recipient_domain=rdomain rbl_code=554 rbl_domain=rhsbl.porcupine.org rbl_txt=RHSBL test rbl_what=example.tld rbl_class=Helo command
/* across SMTP sessions (not process life times). Addresses
/* are always resolved in local rewriting context.
/*
-/* smtpd_resolve_init() initializes the cache and must
+/* smtpd_resolve_init() initializes the cache and must be
/* called before the cache can be used. This function may also
/* be called to flush the cache after an address class update.
/*
{
/*
- * Sanity check.
+ * Flush a pre-existing cache. The smtpd_check test program requires this
+ * after an address class change.
*/
if (smtpd_resolve_cache)
ctable_free(smtpd_resolve_cache);
#! ../bin/postmap smtpd_check_access
#msg_verbose 1
smtpd_delay_reject 0
-mynetworks 127.0.0.0/8,168.100.189.0/28
relay_domains porcupine.org
-maps_rbl_domains dnsbltest.porcupine.org
-rbl_reply_maps hash:smtpd_check_access
client spike.porcupine.org 168.100.189.2
#
# Check MX access
#
-helo_restrictions check_helo_mx_access,hash:smtpd_check_access
-#helo verisign-wildcard.com
-helo verisign.com
+helo_restrictions check_helo_mx_access,inline:{168.100.189.2=reject}
+helo www.porcupine.org
helo example.tld
-sender_restrictions check_sender_mx_access,hash:smtpd_check_access
-mail foo@pls.net.au
-#mail foo@verisign-wildcard.com
-mail foo@verisign.com
-recipient_restrictions check_recipient_mx_access,hash:smtpd_check_access
-#rcpt foo@verisign-wildcard.com
-rcpt foo@verisign.com
-rcpt foo@1.2.3.porcupine.org
+helo foo@postfix.org
+sender_restrictions check_sender_mx_access,inline:{168.100.189.2=reject}
+mail foo@www.porcupine.org
+mail example.tld
+mail foo@postfix.org
+recipient_restrictions check_recipient_mx_access,inline:{168.100.189.2=reject}
+rcpt foo@www.porcupine.org
+rcpt foo@example.tld
+rcpt foo@postfix.org
#
# Check NS access
#
-helo_restrictions check_helo_ns_access,hash:smtpd_check_access
-helo email-publisher.com
-helo ns1.topica.com
-#helo verisign-wildcard.com
+helo_restrictions check_helo_ns_access,inline:{168.100.189.2=reject}
+helo www.porcupine.org
helo example.tld
-sender_restrictions check_sender_ns_access,hash:smtpd_check_access
-mail foo@email-publisher.com
-mail foo@ns1.topica.com
-#mail foo@verisign-wildcard.com
-recipient_restrictions check_recipient_ns_access,hash:smtpd_check_access
-rcpt foo@email-publisher.com
-rcpt foo@ns1.topica.com
-#rcpt foo@verisign-wildcard.com
-rcpt foo@1.2.3.porcupine.org
+helo foo@postfix.org
+sender_restrictions check_sender_ns_access,inline:{168.100.189.2=reject}
+mail foo@www.porcupine.org
+mail example.tld
+mail foo@postfix.org
+recipient_restrictions check_recipient_ns_access,inline:{168.100.189.2=reject}
+rcpt foo@www.porcupine.org
+rcpt foo@example.tld
+rcpt foo@postfix.org
#
# Check A access
#
-helo_restrictions check_helo_a_access,hash:smtpd_check_access
-helo help.gypsysoul.org
-helo gypsysoul.org
-client_restrictions check_client_a_access,hash:smtpd_check_access
-client help.gypsysoul.org 1.2.3.4
-client gypsysoul.org 1.2.3.4
-#reverse_client_restrictions check_reverse_client_a_access,hash:smtpd_check_access
-#client help.gypsysoul.org 1.2.3.4
-#client gypsysoul.org 1.2.3.4
-sender_restrictions check_sender_a_access,hash:smtpd_check_access
-mail foo@gypsysoul.org
-recipient_restrictions check_recipient_a_access,hash:smtpd_check_access
-mail foo@gypsysoul.org
+helo_restrictions check_helo_a_access,inline:{168.100.189.2=reject}
+helo spike.porcupine.org
+helo www.porcupine.org
+client_restrictions check_client_a_access,inline:{168.100.189.2=reject}
+client spike.porcupine.org 1.2.3.4
+client www.porcupine.org 1.2.3.4
+reverse_client_restrictions check_reverse_client_a_access,inline:{168.100.189.2=reject}
+client spike.porcupine.org 1.2.3.4
+client www.porcupine.org 1.2.3.4
+sender_restrictions check_sender_a_access,inline:{168.100.189.2=reject}
+mail foo@spike.porcupine.org
+mail foo@www.porcupine.org
+recipient_restrictions check_recipient_a_access,inline:{168.100.189.2=reject}
+rcpt foo@spike.porcupine.org
+rcpt foo@www.porcupine.org
>>> #msg_verbose 1
>>> smtpd_delay_reject 0
OK
->>> mynetworks 127.0.0.0/8,168.100.189.0/28
-OK
>>> relay_domains porcupine.org
OK
->>> maps_rbl_domains dnsbltest.porcupine.org
-OK
->>> rbl_reply_maps hash:smtpd_check_access
-OK
>>> client spike.porcupine.org 168.100.189.2
OK
>>> #
>>> # Check MX access
>>> #
->>> helo_restrictions check_helo_mx_access,hash:smtpd_check_access
-OK
->>> #helo verisign-wildcard.com
->>> helo verisign.com
+>>> helo_restrictions check_helo_mx_access,inline:{168.100.189.2=reject}
OK
+>>> helo www.porcupine.org
+./smtpd_check: <queue id>: reject: HELO from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <www.porcupine.org>: Helo command rejected: Access denied; proto=SMTP helo=<www.porcupine.org>
+554 5.7.1 <www.porcupine.org>: Helo command rejected: Access denied
>>> helo example.tld
./smtpd_check: warning: Unable to look up MX host example.tld for Helo command example.tld: hostname nor servname provided, or not known
OK
->>> sender_restrictions check_sender_mx_access,hash:smtpd_check_access
+>>> helo foo@postfix.org
+OK
+>>> sender_restrictions check_sender_mx_access,inline:{168.100.189.2=reject}
OK
->>> mail foo@pls.net.au
+>>> mail foo@www.porcupine.org
+./smtpd_check: <queue id>: reject: MAIL from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <foo@www.porcupine.org>: Sender address rejected: Access denied; from=<foo@www.porcupine.org> proto=SMTP helo=<foo@postfix.org>
+554 5.7.1 <foo@www.porcupine.org>: Sender address rejected: Access denied
+>>> mail example.tld
+./smtpd_check: warning: Unable to look up MX host example.tld for Sender address example.tld: hostname nor servname provided, or not known
OK
->>> #mail foo@verisign-wildcard.com
->>> mail foo@verisign.com
+>>> mail foo@postfix.org
OK
->>> recipient_restrictions check_recipient_mx_access,hash:smtpd_check_access
+>>> recipient_restrictions check_recipient_mx_access,inline:{168.100.189.2=reject}
OK
->>> #rcpt foo@verisign-wildcard.com
->>> rcpt foo@verisign.com
+>>> rcpt foo@www.porcupine.org
+./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <foo@www.porcupine.org>: Recipient address rejected: Access denied; from=<foo@postfix.org> to=<foo@www.porcupine.org> proto=SMTP helo=<foo@postfix.org>
+554 5.7.1 <foo@www.porcupine.org>: Recipient address rejected: Access denied
+>>> rcpt foo@example.tld
+./smtpd_check: warning: Unable to look up MX host example.tld for Recipient address foo@example.tld: hostname nor servname provided, or not known
+OK
+>>> rcpt foo@postfix.org
OK
->>> rcpt foo@1.2.3.porcupine.org
-./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <foo@1.2.3.porcupine.org>: Recipient address rejected: mail server 10.10.10.10; from=<foo@verisign.com> to=<foo@1.2.3.porcupine.org> proto=SMTP helo=<example.tld>
-554 5.7.1 <foo@1.2.3.porcupine.org>: Recipient address rejected: mail server 10.10.10.10
>>> #
>>> # Check NS access
>>> #
->>> helo_restrictions check_helo_ns_access,hash:smtpd_check_access
-OK
->>> helo email-publisher.com
-./smtpd_check: <queue id>: reject: HELO from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <email-publisher.com>: Helo command rejected: Access denied; from=<foo@verisign.com> proto=SMTP helo=<email-publisher.com>
-554 5.7.1 <email-publisher.com>: Helo command rejected: Access denied
->>> helo ns1.topica.com
-./smtpd_check: <queue id>: reject: HELO from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <ns1.topica.com>: Helo command rejected: Access denied; from=<foo@verisign.com> proto=SMTP helo=<ns1.topica.com>
-554 5.7.1 <ns1.topica.com>: Helo command rejected: Access denied
->>> #helo verisign-wildcard.com
+>>> helo_restrictions check_helo_ns_access,inline:{168.100.189.2=reject}
+OK
+>>> helo www.porcupine.org
+./smtpd_check: <queue id>: reject: HELO from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <www.porcupine.org>: Helo command rejected: Access denied; from=<foo@postfix.org> proto=SMTP helo=<www.porcupine.org>
+554 5.7.1 <www.porcupine.org>: Helo command rejected: Access denied
>>> helo example.tld
./smtpd_check: warning: Unable to look up NS host for example.tld: Host not found
OK
->>> sender_restrictions check_sender_ns_access,hash:smtpd_check_access
-OK
->>> mail foo@email-publisher.com
-./smtpd_check: <queue id>: reject: MAIL from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <foo@email-publisher.com>: Sender address rejected: Access denied; from=<foo@email-publisher.com> proto=SMTP helo=<example.tld>
-554 5.7.1 <foo@email-publisher.com>: Sender address rejected: Access denied
->>> mail foo@ns1.topica.com
-./smtpd_check: <queue id>: reject: MAIL from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <foo@ns1.topica.com>: Sender address rejected: Access denied; from=<foo@ns1.topica.com> proto=SMTP helo=<example.tld>
-554 5.7.1 <foo@ns1.topica.com>: Sender address rejected: Access denied
->>> #mail foo@verisign-wildcard.com
->>> recipient_restrictions check_recipient_ns_access,hash:smtpd_check_access
-OK
->>> rcpt foo@email-publisher.com
-./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <foo@email-publisher.com>: Recipient address rejected: Access denied; from=<foo@ns1.topica.com> to=<foo@email-publisher.com> proto=SMTP helo=<example.tld>
-554 5.7.1 <foo@email-publisher.com>: Recipient address rejected: Access denied
->>> rcpt foo@ns1.topica.com
-./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <foo@ns1.topica.com>: Recipient address rejected: Access denied; from=<foo@ns1.topica.com> to=<foo@ns1.topica.com> proto=SMTP helo=<example.tld>
-554 5.7.1 <foo@ns1.topica.com>: Recipient address rejected: Access denied
->>> #rcpt foo@verisign-wildcard.com
->>> rcpt foo@1.2.3.porcupine.org
-./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <foo@1.2.3.porcupine.org>: Recipient address rejected: ns or mx server spike.porcupine.org; from=<foo@ns1.topica.com> to=<foo@1.2.3.porcupine.org> proto=SMTP helo=<example.tld>
-554 5.7.1 <foo@1.2.3.porcupine.org>: Recipient address rejected: ns or mx server spike.porcupine.org
+>>> helo foo@postfix.org
+OK
+>>> sender_restrictions check_sender_ns_access,inline:{168.100.189.2=reject}
+OK
+>>> mail foo@www.porcupine.org
+./smtpd_check: <queue id>: reject: MAIL from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <foo@www.porcupine.org>: Sender address rejected: Access denied; from=<foo@www.porcupine.org> proto=SMTP helo=<foo@postfix.org>
+554 5.7.1 <foo@www.porcupine.org>: Sender address rejected: Access denied
+>>> mail example.tld
+./smtpd_check: warning: Unable to look up NS host for example.tld: Host not found
+OK
+>>> mail foo@postfix.org
+OK
+>>> recipient_restrictions check_recipient_ns_access,inline:{168.100.189.2=reject}
+OK
+>>> rcpt foo@www.porcupine.org
+./smtpd_check: <queue id>: reject: RCPT from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <foo@www.porcupine.org>: Recipient address rejected: Access denied; from=<foo@postfix.org> to=<foo@www.porcupine.org> proto=SMTP helo=<foo@postfix.org>
+554 5.7.1 <foo@www.porcupine.org>: Recipient address rejected: Access denied
+>>> rcpt foo@example.tld
+./smtpd_check: warning: Unable to look up NS host for example.tld: Host not found
+OK
+>>> rcpt foo@postfix.org
+OK
>>> #
>>> # Check A access
>>> #
->>> helo_restrictions check_helo_a_access,hash:smtpd_check_access
+>>> helo_restrictions check_helo_a_access,inline:{168.100.189.2=reject}
+OK
+>>> helo spike.porcupine.org
+./smtpd_check: <queue id>: reject: HELO from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <spike.porcupine.org>: Helo command rejected: Access denied; from=<foo@postfix.org> proto=SMTP helo=<spike.porcupine.org>
+554 5.7.1 <spike.porcupine.org>: Helo command rejected: Access denied
+>>> helo www.porcupine.org
+OK
+>>> client_restrictions check_client_a_access,inline:{168.100.189.2=reject}
+OK
+>>> client spike.porcupine.org 1.2.3.4
+./smtpd_check: <queue id>: reject: CONNECT from spike.porcupine.org[1.2.3.4]: 554 5.7.1 <spike.porcupine.org[1.2.3.4]>: Client host rejected: Access denied; from=<foo@postfix.org> proto=SMTP helo=<www.porcupine.org>
+554 5.7.1 <spike.porcupine.org[1.2.3.4]>: Client host rejected: Access denied
+>>> client www.porcupine.org 1.2.3.4
OK
->>> helo help.gypsysoul.org
+>>> reverse_client_restrictions check_reverse_client_a_access,inline:{168.100.189.2=reject}
+bad command
+>>> client spike.porcupine.org 1.2.3.4
+./smtpd_check: <queue id>: reject: CONNECT from spike.porcupine.org[1.2.3.4]: 554 5.7.1 <spike.porcupine.org[1.2.3.4]>: Client host rejected: Access denied; from=<foo@postfix.org> proto=SMTP helo=<www.porcupine.org>
+554 5.7.1 <spike.porcupine.org[1.2.3.4]>: Client host rejected: Access denied
+>>> client www.porcupine.org 1.2.3.4
OK
->>> helo gypsysoul.org
-./smtpd_check: <queue id>: reject: HELO from spike.porcupine.org[168.100.189.2]: 554 5.7.1 <gypsysoul.org>: Helo command rejected: bizsat.net, gypsysoul.org spam; from=<foo@ns1.topica.com> proto=SMTP helo=<gypsysoul.org>
-554 5.7.1 <gypsysoul.org>: Helo command rejected: bizsat.net, gypsysoul.org spam
->>> client_restrictions check_client_a_access,hash:smtpd_check_access
+>>> sender_restrictions check_sender_a_access,inline:{168.100.189.2=reject}
OK
->>> client help.gypsysoul.org 1.2.3.4
+>>> mail foo@spike.porcupine.org
+./smtpd_check: <queue id>: reject: MAIL from www.porcupine.org[1.2.3.4]: 554 5.7.1 <foo@spike.porcupine.org>: Sender address rejected: Access denied; from=<foo@spike.porcupine.org> proto=SMTP helo=<www.porcupine.org>
+554 5.7.1 <foo@spike.porcupine.org>: Sender address rejected: Access denied
+>>> mail foo@www.porcupine.org
OK
->>> client gypsysoul.org 1.2.3.4
-./smtpd_check: <queue id>: reject: CONNECT from gypsysoul.org[1.2.3.4]: 554 5.7.1 <gypsysoul.org[1.2.3.4]>: Client host rejected: bizsat.net, gypsysoul.org spam; from=<foo@ns1.topica.com> proto=SMTP helo=<gypsysoul.org>
-554 5.7.1 <gypsysoul.org[1.2.3.4]>: Client host rejected: bizsat.net, gypsysoul.org spam
->>> #reverse_client_restrictions check_reverse_client_a_access,hash:smtpd_check_access
->>> #client help.gypsysoul.org 1.2.3.4
->>> #client gypsysoul.org 1.2.3.4
->>> sender_restrictions check_sender_a_access,hash:smtpd_check_access
+>>> recipient_restrictions check_recipient_a_access,inline:{168.100.189.2=reject}
OK
->>> mail foo@gypsysoul.org
-./smtpd_check: <queue id>: reject: MAIL from gypsysoul.org[1.2.3.4]: 554 5.7.1 <foo@gypsysoul.org>: Sender address rejected: bizsat.net, gypsysoul.org spam; from=<foo@gypsysoul.org> proto=SMTP helo=<gypsysoul.org>
-554 5.7.1 <foo@gypsysoul.org>: Sender address rejected: bizsat.net, gypsysoul.org spam
->>> recipient_restrictions check_recipient_a_access,hash:smtpd_check_access
+>>> rcpt foo@spike.porcupine.org
+./smtpd_check: <queue id>: reject: RCPT from www.porcupine.org[1.2.3.4]: 554 5.7.1 <foo@spike.porcupine.org>: Recipient address rejected: Access denied; from=<foo@www.porcupine.org> to=<foo@spike.porcupine.org> proto=SMTP helo=<www.porcupine.org>
+554 5.7.1 <foo@spike.porcupine.org>: Recipient address rejected: Access denied
+>>> rcpt foo@www.porcupine.org
OK
->>> mail foo@gypsysoul.org
-./smtpd_check: <queue id>: reject: MAIL from gypsysoul.org[1.2.3.4]: 554 5.7.1 <foo@gypsysoul.org>: Sender address rejected: bizsat.net, gypsysoul.org spam; from=<foo@gypsysoul.org> proto=SMTP helo=<gypsysoul.org>
-554 5.7.1 <foo@gypsysoul.org>: Sender address rejected: bizsat.net, gypsysoul.org spam