]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.0.16-20040101
authorWietse Venema <wietse@porcupine.org>
Thu, 1 Jan 2004 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:29:25 +0000 (06:29 +0000)
19 files changed:
postfix/HISTORY
postfix/README_FILES/SMTPD_POLICY_README
postfix/RELEASE_NOTES
postfix/conf/pcre_table
postfix/conf/regexp_table
postfix/conf/sample-smtpd.cf
postfix/html/faq.html
postfix/html/pcre_table.5.html
postfix/html/regexp_table.5.html
postfix/html/uce.html
postfix/man/man5/pcre_table.5
postfix/man/man5/regexp_table.5
postfix/proto/pcre_table
postfix/proto/regexp_table
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/global/resolve_clnt.h
postfix/src/smtpd/smtpd.h
postfix/src/smtpd/smtpd_check.c

index 81bcad50323b290134b13bf88f7b66a0746276dd..316d12058fc36e9eed49e9e51f4bd13c4262a2fb 100644 (file)
@@ -8942,6 +8942,22 @@ Apologies for any names omitted.
        Bugfix: stricter address syntax test broke "sendmail -bs".
        File: smtpd/smtpd.c.
 
+20040101
+
+       Cleanup: the Postfix SMTP server rejects a MAIL FROM address
+       that matches a local, virtual or relay domain, while the
+       address is not listed in the corresponding local, virtual
+       or relay recipient table.
+
+       Feature: the reject_unlisted_sender(recipient) SMTPD access
+       restriction rejects an address that matches a local, virtual
+       or relay domain, while the address is not listed in the
+       corresponding local, virtual or relay recipient table. 
+
+       Compatibility: the check_recipient_maps restriction works
+       like reject_unlisted_recipient, but will eventually be
+       removed from Postfix.
+
 Open problems:
 
        Low: in the SMTP client, pass the session, request and
index 172243ec5cfd8975c1ec7f23d47acae0cec46017..e59db0fd9f905c6d5140c105044489a3fc902363 100644 (file)
@@ -170,7 +170,9 @@ Greylisting mail from frequently forged domains
 -----------------------------------------------
 
 It is relatively safe to turn on greylisting for specific domains
-that often appear in forged email.
+that often appear in forged email. However, sites like AOL may
+blacklist you when they find that you are probing them too often
+and/or if you're probing them too often for non-existent addresses.
 
 /etc/postfix/main.cf:
     smtpd_recipient_restrictions =
@@ -229,7 +231,7 @@ database will eventually run your file system out of space.
 When the status file exceeds some reasonable size you can simply
 delete the file without adverse effects. In the worst case, new
 mail will be delayed by one hour. To lessen the impact, delete the
-file in the middle of the night.
+file in the middle of the night during a weekend.
 
 SAMPLE POLICY ROUTINE
 =====================
index 1977e41076eb3079b0dffb2fa95ed3aea853cbdc..09a6cf81da1f4d1ab52a337273f47e3a208e9245 100644 (file)
@@ -22,6 +22,13 @@ snapshot release).  Patches change the patchlevel and the release
 date. Snapshots change only the release date, unless they include
 the same bugfixes as a patch release.
 
+Incompatible changes with Postfix snapshot 2.0.16-2004XXXX
+==========================================================
+
+The SMTP server no longer accept sender addresses that match a
+local, virtual or relay domain while the address is not listed in
+the corresponding local, virtual or relay recipient table.
+
 Incompatible changes with Postfix snapshot 2.0.16-20031226
 ==========================================================
 
index ff03d585bd89412f6833bd801795f0645532bbca..b14e334c8f103b0a7c1116c68d4d1ea32187aa86 100644 (file)
@@ -41,6 +41,8 @@
 # 
 #        if /pattern/flags
 # 
+#        endif
+# 
 #        if !/pattern/flags
 # 
 #        endif  Match   the  search  string  against  the  patterns
index 56b7ad7db71431afb6edf8749af2046ea5723b5d..75565b2126fec114e6878529cb3f1502f226e519 100644 (file)
@@ -41,6 +41,8 @@
 # 
 #        if /pattern/flags
 # 
+#        endif
+# 
 #        if !/pattern/flags
 # 
 #        endif  Match   the  search  string  against  the  patterns
index a1f9a2c05748d079b8f540bae43568b65f51c44a..4e421987fb1738559dd2abbc94677b14e36f7d9e 100644 (file)
@@ -491,11 +491,15 @@ smtpd_helo_restrictions =
 # The smtpd_sender_restrictions parameter specifies optional restrictions
 # on sender addresses that SMTP clients can send in MAIL FROM commands.
 #
-# The default is to permit any sender address.  The following
-# restrictions are available:
+# The default is to permit any sender address that passes the restrictions
+# marked with (+).  The following restrictions are available:
 #
 #   permit_mynetworks: permit if the client address matches $mynetworks.
 #   reject_unknown_sender_domain: reject sender domain without A or MX record.
+#  +reject_unlisted_sender: reject a sender address in a local, virtual or
+#      relay domain that is not listed as a valid address.
+#      This restriction is automatically added at the end if not explicitly
+#      specified.
 #   reject_rhsbl_sender domain.tld: reject sender domain name if it is listed
 #      in an A record under domain.tld.
 #   check_sender_access maptype:mapname
@@ -559,10 +563,13 @@ smtpd_sender_restrictions =
 # - destinations that match $virtual_mailbox_domains.
 # These destinations do not need to be listed in $relay_domains.
 #
-# The following restrictions are available (* is part of default setting):
+# The following restrictions are available (* is part of default setting,
+# and + is appended by default if not explicitly specified):
 #
 #  *permit_mynetworks: permit if the client address matches $mynetworks.
 #   reject_unknown_sender_domain: reject sender domain without A or MX record.
+#   reject_unlisted_sender: reject a sender address in a local, virtual or
+#      relay domain that is not listed as a valid address.
 #   reject_unverified_sender: reject undeliverable sender address.
 #   reject_unverified_recipient: reject undeliverable recipient address.
 #   reject_multi_recipient_bounce: reject mail from <> with multiple recipients.
@@ -583,6 +590,12 @@ smtpd_sender_restrictions =
 #       Use the optional permit_mx_backup_networks parameter to also
 #      require that the primary MX hosts match a list of network blocks.
 #   reject_unknown_recipient_domain: reject domains without A or MX record.
+#  +reject_unlisted_recipient: reject a recipient address in a local, virtual
+#      or relay domain that is not listed as a valid address. 
+#      This restriction is automatically added at the end if not explicitly
+#      specified.
+#  +check_recipient_maps: a backwards compatibility alias for the
+#      reject_unlisted_recipient restriction.
 #   check_recipient_access maptype:mapname
 #      look up recipient address, parent domain, or localpart@.
 #      see access(5) for possible lookup results.
index a5ea4ecf20d6e6d51f1d9b9927e11da17c078b96..fb9e7b0d4a36505b2396af0868b441899cd5577e 100644 (file)
@@ -153,8 +153,6 @@ distribution list</a>
 
 <li><a href="#majordomo-approve">Postfix breaks the majordomo "approve" command</a>
 
-<li><a href="#skip_greeting">Postfix does not try all the MX addresses</a>
-
 <li><a href="#worm">Postfix accepts MAIL FROM and RCPT TO "| command"</a>
 
 </ul>
@@ -221,8 +219,6 @@ domains with "relay access denied"</a>
 
 <li><a href="#timeouts">Mail fails consistently with timeout or lost connection</a>
 
-<li><a href="#skip_greeting">Postfix does not try all the MX addresses</a>
-
 <li><a href="#noservice">What does "fatal: unknown service: smtp/tcp" mean?</a>
 
 <li><a href="#broken_transport">Mail delivery fails with: "unknown
@@ -1906,21 +1902,6 @@ and convince the person responsible for it to fix the configuration.
 
 <hr>
 
-<a name="skip_greeting"><h3>Postfix does not try all the MX
-addresses</h3>
-
-When delivering mail, Postfix tries all MX addresses in order of
-preference, and stops at the first server that speaks SMTP.  However,
-once an SMTP greeting is received, Postfix will not move on to the
-next MX host if the delivery fails.  
-
-<p>
-
-This will eventually be solved when Postfix implements SMTP
-connection caching.
-
-<hr>
-
 <a name="noservice"><h3>What does "fatal: unknown service: smtp/tcp"
 mean?</h3>
 
index 32155442a18692cb852fa2456cd9af3b3ad40858..3e17b5f75ed91bdef811438ac3a038cee36e8f92 100644 (file)
@@ -42,6 +42,8 @@ PCRE_TABLE(5)                                       PCRE_TABLE(5)
 
        <b>if /</b><i>pattern</i><b>/</b><i>flags</i>
 
+       <b>endif</b>
+
        <b>if !/</b><i>pattern</i><b>/</b><i>flags</i>
 
        <b>endif</b>  Match   the  search  string  against  the  patterns
index a0ab127b8c7ed1587e6e8a9de1c61c24dd2c7057..90fb8029ada913afe9d80d0edba2f79236e816e8 100644 (file)
@@ -1,4 +1,4 @@
-<html> <head> </head> <body> <pre>
+<html> <body> <pre>
 REGEXP_TABLE(5)                                   REGEXP_TABLE(5)
 
 <b>NAME</b>
@@ -42,6 +42,8 @@ REGEXP_TABLE(5)                                   REGEXP_TABLE(5)
 
        <b>if /</b><i>pattern</i><b>/</b><i>flags</i>
 
+       <b>endif</b>
+
        <b>if !/</b><i>pattern</i><b>/</b><i>flags</i>
 
        <b>endif</b>  Match   the  search  string  against  the  patterns
index afa38f2d062bd5d6150867739899173a66f7c1f3..9498a56978c063ef4e9b434c42c96247f7d4399c 100644 (file)
@@ -815,6 +815,42 @@ the MAIL FROM command.
 
 <p>
 
+<a name="reject_unlisted_sender">
+
+<dt> <b>reject_unlisted_sender</b> <dd> Reject the request when
+the sender address matches one of the domain lists below, but
+is not listed in one of the corresponding address lists:
+
+<blockquote>
+
+<table border="1">
+
+<tr><th>Domain list</th> <th>Address list</th> 
+
+</tr><tr><td><a href="basic.html#mydestination"> $mydestination</a> or
+<a href="basic.html#inet_interfaces">$inet_interfaces</a></td> 
+<td>$local_recipient_maps</td>
+
+</tr><tr><td>$virtual_alias_domains</td> <td>$virtual_alias_maps</td> 
+
+</tr><tr><td>$virtual_mailbox_domains</td> <td>$virtual_mailbox_maps</td> 
+
+</tr><tr><td>$relay_domains</td> <td>$relay_recipient_maps</td> 
+
+</tr></table>
+
+</blockquote>
+
+Note 1: a null $local_recipient_maps or $relay_recipient_maps setting
+means that no address check is done for the corresponding domains.
+
+<p>
+
+Note 2: Postfix applies an implicit <b>reject_unlisted_sender</b>
+restriction at the end of all sender restrictions.
+
+<p>
+
 <a name="reject_non_fqdn_sender">
 
 <dt> <b>reject_non_fqdn_sender</b> <dd> Reject the request when
@@ -1062,17 +1098,17 @@ with the RCPT TO command.
 
 <p>
 
-<a name="check_recipient_maps">
+<a name="reject_unlisted_recipient">
 
-<dt> <b>check_recipient_maps</b> <dd> Reject the request
-when the recipient address is not listed in one of the following
-lookup tables:
+<dt> <b>reject_unlisted_recipient</b> <dd> Reject the request when
+the recipient address matches one of the domain lists below, but
+is not listed in one of the corresponding lookup tables:
 
 <blockquote>
 
 <table border="1">
 
-<tr><th>Recipient domain matches</th> <th>Recipient lookup table</th> 
+<tr><th>Domain list</th> <th>Address list</th> 
 
 </tr><tr><td><a href="basic.html#mydestination"> $mydestination</a> or
 <a href="basic.html#inet_interfaces">$inet_interfaces</a></td> 
@@ -1089,15 +1125,21 @@ lookup tables:
 </blockquote>
 
 Note 1: a null $local_recipient_maps or $relay_recipient_maps setting
-means that no recipient check is done for the corresponding domains.
+means that no address check is done for the corresponding domains.
 
 <p>
 
-Note 2: Postfix applies an implicit <b>check_recipient_maps</b>
+Note 2: Postfix applies an implicit <b>reject_unlisted_recipient</b>
 restriction at the end of all recipient restrictions.
 
 <p>
 
+Note 3: the <b>check_recipient_maps</b> restriction is a backwards
+compatible alias for <b>reject_unlisted_recipient</b>. It will
+eventually be removed from Postfix.
+
+<p>
+
 <a name="reject_multi_recipient_bounce">
 
 <dt> <b>reject_multi_recipient_bounce</b> <dd> Reject the request
index 2b05c2fe4e0601b8bed93a9ef8098c3a7bfee22d..a4d7032609ee89b60789f27d4bd8e1a19ca810cb 100644 (file)
@@ -41,6 +41,7 @@ are lines whose first non-whitespace character is a `#'.
 A logical line starts with non-whitespace text. A line that
 starts with whitespace continues a logical line.
 .IP "\fBif /\fIpattern\fB/\fIflags\fR"
+.IP "\fBendif\fR"
 .IP "\fBif !/\fIpattern\fB/\fIflags\fR"
 .IP "\fBendif\fR"
 Match the search string against the patterns between \fBif\fR
index 5b9b078e16e4d7e84ff0c1b17cbbd11f54335028..03bcc7cc723ce8ff3fd9d8b0f6390c905fd4ce0d 100644 (file)
@@ -41,6 +41,7 @@ are lines whose first non-whitespace character is a `#'.
 A logical line starts with non-whitespace text. A line that
 starts with whitespace continues a logical line.
 .IP "\fBif /\fIpattern\fB/\fIflags\fR"
+.IP "\fBendif\fR"
 .IP "\fBif !/\fIpattern\fB/\fIflags\fR"
 .IP "\fBendif\fR"
 Match the search string against the patterns between \fBif\fR
index 0ace904daa1cf6d5e90d15d5dd48ed89f4d09d2d..b182b54c8fd1d9b6eaec1301d27626d4c279dc80 100644 (file)
@@ -33,6 +33,7 @@
 #      A logical line starts with non-whitespace text. A line that
 #      starts with whitespace continues a logical line.
 # .IP "\fBif /\fIpattern\fB/\fIflags\fR"
+# .IP "\fBendif\fR"
 # .IP "\fBif !/\fIpattern\fB/\fIflags\fR"
 # .IP "\fBendif\fR"
 #      Match the search string against the patterns between \fBif\fR
index ca4b9406799b5d21e5a1dfd44f7fc0275b240a31..e12b0f78921c5c7966ac2d85621eadb690e64837 100644 (file)
@@ -33,6 +33,7 @@
 #      A logical line starts with non-whitespace text. A line that
 #      starts with whitespace continues a logical line.
 # .IP "\fBif /\fIpattern\fB/\fIflags\fR"
+# .IP "\fBendif\fR"
 # .IP "\fBif !/\fIpattern\fB/\fIflags\fR"
 # .IP "\fBendif\fR"
 #       Match the search string against the patterns between \fBif\fR
index 4d5ee846de5b3f55d739565b3820bf6cfea65c04..6ab8730f4c0ce2af0f47b46b3bfb2351493d8fbd 100644 (file)
@@ -1315,6 +1315,8 @@ extern int var_non_fqdn_code;
 #define REJECT_UNKNOWN_SENDDOM "reject_unknown_sender_domain"
 #define REJECT_UNKNOWN_RCPTDOM "reject_unknown_recipient_domain"
 #define REJECT_UNKNOWN_ADDRESS "reject_unknown_address"
+#define REJECT_UNLISTED_SENDER "reject_unlisted_sender"
+#define REJECT_UNLISTED_RCPT   "reject_unlisted_recipient"
 #define CHECK_RCPT_MAPS                "check_recipient_maps"
 #define VAR_UNK_ADDR_CODE      "unknown_address_reject_code"
 #define DEF_UNK_ADDR_CODE      450
index 734fd52db8816f497326d857e8d77b2e012d3d23..c7e7a0bba03454fbc98a5d3e3c88bd6c9f24319c 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change the patchlevel and the release date. Snapshots change the
   * release date only, unless they include the same bugfix as a patch release.
   */
-#define MAIL_RELEASE_DATE      "20031231"
+#define MAIL_RELEASE_DATE      "20040101"
 
 #define VAR_MAIL_VERSION       "mail_version"
 #define DEF_MAIL_VERSION       "2.0.16-" MAIL_RELEASE_DATE
index d96ea18635edffe1e42ab783f4bf69050e272206..ace680731617a64df44a92c4f8307518ee288429 100644 (file)
 #define RESOLVE_CLASS_FINAL \
        (RESOLVE_CLASS_LOCAL | RESOLVE_CLASS_ALIAS | RESOLVE_CLASS_VIRTUAL)
 
+#define RESOLVE_CLASS_MASK \
+       (RESOLVE_CLASS_LOCAL | RESOLVE_CLASS_ALIAS | RESOLVE_CLASS_VIRTUAL \
+       | RESOLVE_CLASS_RELAY | RESOLVE_CLASS_DEFAULT)
+
 typedef struct RESOLVE_REPLY {
     VSTRING *transport;
     VSTRING *nexthop;
index 9c9bea0a6e547a1421304134d3281d7c584941c3..c5bbe139d3fe56a9a8aba8ef72d27a5252e0fb86 100644 (file)
@@ -106,7 +106,8 @@ typedef struct SMTPD_STATE {
     /*
      * Specific to smtpd access checks.
      */
-    int     rcptmap_checked;
+    int     sender_rcptmap_checked;    /* sender validated against maps */
+    int     recipient_rcptmap_checked; /* recipient validated against maps */
     int     warn_if_reject;            /* force reject into warning */
     SMTPD_DEFER defer_if_reject;       /* force reject into deferral */
     SMTPD_DEFER defer_if_permit;       /* force permit into deferral */
index dda63db5083cc85ac75a06d6fda1f205aac324f4..79255a53da728a717a959f450a5274204f2ee6ea 100644 (file)
 /*     Apply the specified access table to the DNS server host name and IP
 /*     addresses for the helo hostname, sender, or recipient, respectively.
 /*     If no NS record is found, the parent domain is used instead.
-/* .IP "check_recipient_maps"
-/*     Reject recipients not listed as valid local, virtual or relay
-/*     recipients.
+/* .IP "reject_unlisted_sender"
+/*     Reject senders in local, virtual or relay domains that are not
+/*     listed as a valid address.
+/* .IP "reject_unlisted_recipient"
+/*     Reject recipients in local, virtual or relay domains that are
+/*     not listed as a valid address.
 /* .IP reject_multi_recipient_bounce
 /*     Reject mail from <> with multiple envelope recipients.
 /* .IP reject_rbl_client rbl.domain.tld
@@ -442,7 +445,9 @@ static int generic_checks(SMTPD_STATE *, ARGV *, const char *, const char *, con
  /*
   * Recipient table check.
   */
-static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient);
+static int check_sender_rcpt_maps(SMTPD_STATE *, const char *);
+static int check_recipient_rcpt_maps(SMTPD_STATE *, const char *);
+static int check_rcpt_maps(SMTPD_STATE *, const char *, const char *);
 
  /*
   * Reject context.
@@ -3282,6 +3287,9 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
                    status = reject_rbl_domain(state, *cpp, state->sender,
                                               SMTPD_NAME_SENDER);
            }
+       } else if (strcasecmp(name, REJECT_UNLISTED_SENDER) == 0) {
+           if (state->sender && *state->sender)
+               status = check_sender_rcpt_maps(state, state->sender);
        }
 
        /*
@@ -3348,9 +3356,10 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
                    status = reject_rbl_domain(state, *cpp, state->recipient,
                                               SMTPD_NAME_RECIPIENT);
            }
-       } else if (strcasecmp(name, CHECK_RCPT_MAPS) == 0) {
+       } else if (strcasecmp(name, CHECK_RCPT_MAPS) == 0
+                  || strcasecmp(name, REJECT_UNLISTED_RCPT) == 0) {
            if (state->recipient && *state->recipient)
-               status = check_rcpt_maps(state, state->recipient);
+               status = check_recipient_rcpt_maps(state, state->recipient);
        } else if (strcasecmp(name, REJECT_MUL_RCPT_BOUNCE) == 0) {
            if (state->sender && *state->sender == 0 && state->rcpt_count
                > (strcmp(state->where, "DATA") ? 0 : 1))
@@ -3416,7 +3425,6 @@ int     smtpd_check_addr(const char *addr)
 {
     const RESOLVE_REPLY *resolve_reply;
     char   *myname = "smtpd_check_addr";
-    int     status;
 
     if (msg_verbose)
        msg_info("%s: addr=%s", myname, addr);
@@ -3556,6 +3564,7 @@ char   *smtpd_check_mail(SMTPD_STATE *state, char *sender)
      */
     state->defer_if_permit.active = state->defer_if_permit_client
        | state->defer_if_permit_helo;
+    state->sender_rcptmap_checked = 0;
 
     /*
      * Apply restrictions in the order as specified.
@@ -3567,6 +3576,14 @@ char   *smtpd_check_mail(SMTPD_STATE *state, char *sender)
                                SMTPD_NAME_SENDER, CHECK_SENDER_ACL);
     state->defer_if_permit_sender = state->defer_if_permit.active;
 
+    /*
+     * If the "check_recipient_maps" restriction was not applied, and if mail
+     * is not being rejected or discarded, validate the recipient here.
+     */
+    if (status != SMTPD_CHECK_REJECT && state->sender_rcptmap_checked == 0
+       && state->discard == 0 && *sender)
+       status = check_sender_rcpt_maps(state, sender);
+
     SMTPD_CHECK_MAIL_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
 }
 
@@ -3623,7 +3640,7 @@ char   *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient)
      * The "check_recipient_maps" restriction is relevant only when
      * responding to RCPT TO or VRFY.
      */
-    state->rcptmap_checked = 0;
+    state->recipient_rcptmap_checked = 0;
 
     /*
      * Apply delayed restrictions.
@@ -3662,9 +3679,9 @@ char   *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient)
      * If the "check_recipient_maps" restriction was not applied, and if mail
      * is not being rejected or discarded, validate the recipient here.
      */
-    if (status != SMTPD_CHECK_REJECT && state->rcptmap_checked == 0
+    if (status != SMTPD_CHECK_REJECT && state->recipient_rcptmap_checked == 0
        && state->discard == 0)
-       status = check_rcpt_maps(state, recipient);
+       status = check_recipient_rcpt_maps(state, recipient);
 
     SMTPD_CHECK_RCPT_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
 }
@@ -3730,11 +3747,10 @@ char   *smtpd_check_etrn(SMTPD_STATE *state, char *domain)
     SMTPD_CHECK_ETRN_RETURN(status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
 }
 
-/* check_rcpt_maps - generic_checks() interface for recipient table check */
+/* check_recipient_rcpt_maps - generic_checks() recipient table check */
 
-static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient)
+static int check_recipient_rcpt_maps(SMTPD_STATE *state, const char *recipient)
 {
-    const RESOLVE_REPLY *reply;
 
     /*
      * Duplicate suppression. There's an implicit check_recipient_maps
@@ -3742,9 +3758,35 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient)
      */
     if (smtpd_input_transp_mask & INPUT_TRANSP_UNKNOWN_RCPT)
        return (0);
-    if (state->rcptmap_checked == 1)
+    if (state->recipient_rcptmap_checked == 1)
+       return (0);
+    state->recipient_rcptmap_checked = 1;
+    return (check_rcpt_maps(state, recipient, SMTPD_NAME_RECIPIENT));
+}
+
+/* check_sender_rcpt_maps - generic_checks() sender table check */
+
+static int check_sender_rcpt_maps(SMTPD_STATE *state, const char *sender)
+{
+
+    /*
+     * Duplicate suppression. There's an implicit check_sender_maps
+     * restriction at the end of all sender restrictions.
+     */
+    if (smtpd_input_transp_mask & INPUT_TRANSP_UNKNOWN_RCPT)
+       return (0);
+    if (state->sender_rcptmap_checked == 1)
        return (0);
-    state->rcptmap_checked = 1;
+    state->sender_rcptmap_checked = 1;
+    return (check_rcpt_maps(state, sender, SMTPD_NAME_SENDER));
+}
+
+/* check_rcpt_maps - generic_checks() interface for recipient table check */
+
+static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient,
+                                  const char *reply_class)
+{
+    const RESOLVE_REPLY *reply;
 
     if (msg_verbose)
        msg_info(">>> CHECKING RECIPIENT MAPS <<<");
@@ -3788,10 +3830,11 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient)
      */
     if (strcmp(STR(reply->transport), MAIL_SERVICE_ERROR) == 0)
        return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
-                                  "%d <%s>: %s",
+                                  "%d <%s>: %s rejected: %s",
                                   (reply->flags & RESOLVE_CLASS_ALIAS) ?
                                   var_virt_alias_code : 550,
-                                  recipient, STR(reply->nexthop)));
+                                  recipient, reply_class,
+                                  STR(reply->nexthop)));
 
     /*
      * Reject mail to unknown addresses in local domains (domains that match
@@ -3807,52 +3850,60 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient)
      */
 #define MATCH_LEFT(l, r, n) (strncasecmp((l), (r), (n)) == 0 && (r)[n] == '@')
 
-    if ((reply->flags & RESOLVE_CLASS_LOCAL)
-       && *var_local_rcpt_maps
-    /* Generated by bounce, absorbed by qmgr. */
+    switch (reply->flags & RESOLVE_CLASS_MASK) {
+
+    case RESOLVE_CLASS_LOCAL:
+       if (*var_local_rcpt_maps
+       /* Generated by bounce, absorbed by qmgr. */
        && !MATCH_LEFT(var_double_bounce_sender, CONST_STR(reply->recipient),
                       strlen(var_double_bounce_sender))
-    /* Absorbed by qmgr. */
-       && !MATCH_LEFT(MAIL_ADDR_POSTMASTER, CONST_STR(reply->recipient),
-                      strlen(MAIL_ADDR_POSTMASTER))
-    /* Generated by bounce. */
-       && !MATCH_LEFT(MAIL_ADDR_MAIL_DAEMON, CONST_STR(reply->recipient),
-                      strlen(MAIL_ADDR_MAIL_DAEMON))
-       && NOMATCH(local_rcpt_maps, CONST_STR(reply->recipient)))
-       return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
-                                  "%d <%s>: User unknown%s",
-                                  var_local_rcpt_code, recipient,
-                                  var_show_unk_rcpt_table ?
-                                  " in local recipient table" : ""));
+       /* Absorbed by qmgr. */
+           && !MATCH_LEFT(MAIL_ADDR_POSTMASTER, CONST_STR(reply->recipient),
+                          strlen(MAIL_ADDR_POSTMASTER))
+       /* Generated by bounce. */
+         && !MATCH_LEFT(MAIL_ADDR_MAIL_DAEMON, CONST_STR(reply->recipient),
+                        strlen(MAIL_ADDR_MAIL_DAEMON))
+           && NOMATCH(local_rcpt_maps, CONST_STR(reply->recipient)))
+           return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
+                                    "%d <%s>: %s rejected: User unknown%s",
+                                      var_local_rcpt_code, recipient,
+                                      reply_class, var_show_unk_rcpt_table ?
+                                      " in local recipient table" : ""));
+       break;
 
-    /*
-     * Reject mail to unknown addresses in virtual mailbox domains.
-     */
-    if ((reply->flags & RESOLVE_CLASS_VIRTUAL)
-       && *var_virt_mailbox_maps
-       && NOMATCH(virt_mailbox_maps, CONST_STR(reply->recipient)))
-       return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
-                                  "%d <%s>: User unknown%s",
-                                  var_virt_mailbox_code, recipient,
-                                  var_show_unk_rcpt_table ?
-                                  " in virtual mailbox table" : ""));
+       /*
+        * Reject mail to unknown addresses in virtual mailbox domains.
+        */
+    case RESOLVE_CLASS_VIRTUAL:
+       if (*var_virt_mailbox_maps
+           && NOMATCH(virt_mailbox_maps, CONST_STR(reply->recipient)))
+           return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
+                                    "%d <%s>: %s rejected: User unknown%s",
+                                      var_virt_mailbox_code, recipient,
+                                      reply_class, var_show_unk_rcpt_table ?
+                                      " in virtual mailbox table" : ""));
+       break;
 
-    /*
-     * Reject mail to unknown addresses in relay domains.
-     */
-    if ((reply->flags & RESOLVE_CLASS_RELAY)
-       && *var_relay_rcpt_maps
-       && NOMATCH(relay_rcpt_maps, CONST_STR(reply->recipient)))
-       return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
-                                  "%d <%s>: User unknown%s",
-                                  var_relay_rcpt_code, recipient,
-                                  var_show_unk_rcpt_table ?
-                                  " in relay recipient table" : ""));
+       /*
+        * Reject mail to unknown addresses in relay domains.
+        */
+    case RESOLVE_CLASS_RELAY:
+       if (*var_relay_rcpt_maps
+           && NOMATCH(relay_rcpt_maps, CONST_STR(reply->recipient)))
+           return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
+                                    "%d <%s>: %s rejected: User unknown%s",
+                                      var_relay_rcpt_code, recipient,
+                                      reply_class, var_show_unk_rcpt_table ?
+                                      " in relay recipient table" : ""));
+       break;
 
-    /*
-     * Accept all other addresses - including addresses that passed the above
-     * tests because of some table lookup problem.
-     */
+       /*
+        * Accept all other addresses - including addresses that passed the
+        * above tests because of some table lookup problem.
+        */
+    default:
+       break;
+    }
     return (0);
 }