]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.0.3-20030125
authorWietse Venema <wietse@porcupine.org>
Sat, 25 Jan 2003 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:28:44 +0000 (06:28 +0000)
37 files changed:
postfix/HISTORY
postfix/RELEASE_NOTES
postfix/conf/access
postfix/conf/sample-filter.cf
postfix/conf/sample-pcre-body.cf
postfix/conf/sample-pcre-header.cf
postfix/conf/sample-regexp-body.cf
postfix/conf/sample-regexp-header.cf
postfix/conf/sample-smtpd.cf
postfix/html/access.5.html
postfix/html/uce.html
postfix/man/man5/access.5
postfix/proto/access
postfix/src/cleanup/cleanup.h
postfix/src/cleanup/cleanup_extracted.c
postfix/src/cleanup/cleanup_message.c
postfix/src/cleanup/cleanup_state.c
postfix/src/global/Makefile.in
postfix/src/global/mail_version.h
postfix/src/global/rec_type.h
postfix/src/nqmgr/Makefile.in
postfix/src/nqmgr/qmgr.h
postfix/src/nqmgr/qmgr_deliver.c
postfix/src/nqmgr/qmgr_entry.c
postfix/src/nqmgr/qmgr_message.c
postfix/src/nqmgr/qmgr_queue.c
postfix/src/proxymap/Makefile.in
postfix/src/proxymap/proxymap.c
postfix/src/qmgr/Makefile.in
postfix/src/qmgr/qmgr.h
postfix/src/qmgr/qmgr_deliver.c
postfix/src/qmgr/qmgr_entry.c
postfix/src/qmgr/qmgr_message.c
postfix/src/qmgr/qmgr_queue.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd_check.c
postfix/src/util/Makefile.in

index 67ed9c452e34f78b4bd07ac6df69ad3901f9d2c0..af0d3efe2e32752d2bd8452b7c5dc79014829d2f 100644 (file)
@@ -7796,6 +7796,22 @@ Apologies for any names omitted.
        Postfix now uses TIME.DEVICE_INODE.HOST. Files: local/maildir.c,
        virtual/maildir.c.
 
+20030124
+
+       Cleanup: queue structures no longer overload queue name
+       and nexthop destination. Files: *qmgr/qmgr_message.c,
+       *qmgr/qmgr_queue.c, *qmgr/qmgr_deliver.c.
+
+20030125
+
+       Feature: "REDIRECT user@domain" action in access maps or
+       in header/body_checks causes mail to be sent to the specified
+       address instead of the intended recipient(s). I would never
+       recommend that people use this to redirect (bounced) SPAM
+       to the beneficiaries of an advertisement campaign.  Files:
+       smtpd/smtpd_check.c, cleanup/cleanup_message.c,
+       *qmgr/qmgr_message.c.
+
 Open problems:
 
        Med: make qmgr recipient bounce/defer activity asynchronous
index e4e8cf33b6ae58e46a2e6264fe96e17723a70c13..342194db452c4f933c558caec6c6f5067c4026e6 100644 (file)
@@ -22,6 +22,27 @@ 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.3-20030125
+=========================================================
+
+This release adds a new queue file record type for the address
+specified in "REDIRECT user@domain" actions in access maps or
+header/body_checks.
+
+Major changes with Postfix snapshot 2.0.3-20030125
+==================================================
+
+Code cleanup up of queue manager internals. Queue names are no
+longer mixed up with the next-hop destination, and the address
+resolver loop is now easier to understand.
+
+New "REDIRECT user@domain" action for access maps and header/body_checks
+that overrides all the originally specified recipients of a message.
+I would never recommend that people use this to redirect (bounced)
+SPAM to the beneficiaries of an advertisement campaign. It would
+have helped when someone began spamming the network with sender
+addresses in one of my domains, and I got all the bounces.
+
 Incompatible changes with Postfix snapshot 2.0.1-20030112
 =========================================================
 
index 0b4a8ad63eb60d25e173d850d4a8641abaeaffaf..1f4b38ba72c21e8ae558aaa817f92b4f94007387 100644 (file)
 #               about  content  filters  is  in  the  Postfix  FIL-
 #               TER_README file.
 # 
-#               Note:  this action currently affects all recipients
-#               of the message.
+#               Note:   this  action  overrides  the  main.cf  con-
+#               tent_filter  setting,  and  currently  affects  all
+#               recipients of the message.
+# 
+#        REDIRECT user@domain
+#               After  the  message  is queued, send the message to
+#               the  specified  address  instead  of  the  intended
+#               recipient(s).
+# 
+#               Note:  this action overrides the FILTER action, and
+#               currently affects all recipients of the message.
 # 
 #        restriction...
 #               Apply the named UCE restriction(s) (permit, reject,
index 3ab0d02e4ec4348489e43a03efdfdb7394e52aa0..115fdcb7fd2223cf215e95cb04303239a5607883 100644 (file)
@@ -38,6 +38,9 @@
 #       off in the second cleanup server. More info about content
 #      filtering is in the Postfix FILTER_README file. This feature
 #      overrides the main.cf content_filter setting.
+# REDIRECT user@domain
+#      Send the message to the specified address instead of the
+#      intended recipient(s). This feature overrides the FILTER action.
 #
 # By default, these patterns apply the primary message headers, to
 # MIME headers, and to the headers of attached messages. With older
@@ -82,6 +85,9 @@ header_checks = regexp:/etc/postfix/header_checks
 #       off in the second cleanup server. More info about content
 #      filtering is in the Postfix FILTER_README file. This feature
 #      overrides the main.cf content_filter setting.
+# REDIRECT user@domain
+#      Send the message to the specified address instead of the
+#      intended recipient(s). This feature overrides the FILTER action.
 #
 # By default, the same patterns are applied as for header_checks.
 #
index 14e151225f1b1c83060c29d7e4408aca1c81e117..0ece30ca0fb24c10c8e0410ffdf67e076eb8f1a8 100644 (file)
 #                      and after the filter, with header/body
 #                      checks turned off in the second cleanup
 #                      server. More information about content filters
-#                      is in the Postfix FILTER_README file.
+#                      is in the Postfix FILTER_README file. This feature
+#                      overrides the main.cf content_filter setting.
+#      REDIRECT user@domain
+#                      Send the message to the specified address instead
+#                      of the intended recipient(s). This feature overrides
+#                      the FILTER action.
 #
 #      Substitution of sub-strings from the matched expression is
 #      possible using the conventional perl syntax. The macros in the
index 9dc72813a6dcfa427507587abd5b442d9ae7f44f..58fde12265a757f35fe5faa73c50bd9fcf61175d 100644 (file)
 #                       and after the filter, with header/body
 #                       checks turned off in the second cleanup
 #                       server. More information about content filters
-#                       is in the Postfix FILTER_README file.
+#                       is in the Postfix FILTER_README file. This feature
+#                      overrides the main.cf content_filter setting.
+#      REDIRECT user@domain
+#                      Send the message to the specified address instead
+#                      of the intended recipient(s). This feature overrides
+#                      the FILTER action.
 #
 #      Substitution of sub-strings from the matched expression is
 #      possible using the conventional perl syntax. The macros in the
index e86c306fb744f11455a79c8dbcd1fdd31f928c92..89357f397bac22e15498c36ac8c81498780b7bbe 100644 (file)
 #       After the message is queued, send the entire message through
 #       a content filter. This requires different cleanup servers
 #       before and after the filter, with header/body checks turned
-#       off in the second cleanup server.
+#       off in the second cleanup server. This overrides the main.cf
+#      content filter setting.
+# REDIRECT user@domain
+#      Send the message to the specified address instead of the
+#      intended recipient(s). This overrides the FILTER action.
 
 # Skip over base 64 encoded blocks. This saves lots of CPU cycles.
 # Expressions by Liviu Daia. Amended by Victor Duchovni.
index bc29fdf6d45c91d7b678acd887d3afee72e5041f..10e5290094f77ff9d320ebd2ba33cbdefc409f31 100644 (file)
 #       After the message is queued, send the entire message through
 #       a content filter. This requires different cleanup servers
 #       before and after the filter, with header/body checks turned
-#       off in the second cleanup server.
+#       off in the second cleanup server. This overrides the main.cf
+#      content filter setting.
+# REDIRECT user@domain
+#      Send the message to the specified address instead of the
+#      intended recipient(s). This overrides the FILTER action.
 
 /^Subject: Make Money Fast/    REJECT
 /^To: friend@public.com/       REJECT
index 73c3cd79744baa485486f05c389b08afb9eaca78..b6a6643d28e29da962d3e3865659dcb85b920d70 100644 (file)
@@ -266,6 +266,8 @@ mynetworks_style = subnet
 #      Discard the message if the result is DISCARD text...
 #      Hold the message in the queue if the result is HOLD text... 
 #      Release mail "on hold" with the postsuper(1) command.
+#      Filter the message if the result is FILTER transport:nexthop.
+#      Redirect the message if the result is REDIRECT user@domain.
 #      Permit the SMTP client if the result is OK or all numerical.
 #   reject_rbl_client domain.tld: reject if the reversed client IP address
 #      is listed in an A record under domain.tld.
@@ -312,6 +314,8 @@ smtpd_helo_required = no
 #      Discard the message if the result is DISCARD text...
 #      Hold the message in the queue if the result is HOLD text... 
 #      Release mail "on hold" with the postsuper(1) command.
+#       Filter the message if the result is FILTER transport:nexthop.
+#       Redirect the message if the result is REDIRECT user@domain.
 #      Permit the HELO command if the result is OK or all numerical.
 #   reject: reject the request. Place this at the end of a restriction.
 #   permit: permit the request. Place this at the end of a restriction.
@@ -349,6 +353,8 @@ smtpd_helo_restrictions =
 #      Discard the message if the result is DISCARD text...
 #      Hold the message in the queue if the result is HOLD text... 
 #      Release mail "on hold" with the postsuper(1) command.
+#       Filter the message if the result is FILTER transport:nexthop.
+#       Redirect the message if the result is REDIRECT user@domain.
 #      Permit the sender if the result is OK or all numerical.
 #   reject_sender_login_mismatch: reject if $smtpd_sender_login_maps specifies
 #      a MAIL FROM address owner, but the client is not (SASL) logged in as
@@ -423,6 +429,8 @@ smtpd_sender_restrictions =
 #      Discard the message if the result is DISCARD text...
 #      Hold the message in the queue if the result is HOLD text... 
 #      Release mail "on hold" with the postsuper(1) command.
+#       Filter the message if the result is FILTER transport:nexthop.
+#       Redirect the message if the result is REDIRECT user@domain.
 #      Permit the recipient if the result is OK or all numerical.
 #   reject_non_fqdn_recipient: reject recipient address that is not in FQDN form
 #   reject: reject the request. Place this at the end of a restriction.
index 5158b390c80bc2582fb3f891a9c8e8a631afecb3..b841dfeab06c93582388198eb332a6ba5f88eaa9 100644 (file)
@@ -165,8 +165,17 @@ ACCESS(5)                                               ACCESS(5)
               about  content  filters  is  in  the  Postfix  FIL-
               TER_README file.
 
-              Note:  this action currently affects all recipients
-              of the message.
+              Note:   this  action  overrides  the  <b>main.cf</b>  <b>con-</b>
+              <b>tent</b><i>_</i><b>filter</b>  setting,  and  currently  affects  all
+              recipients of the message.
+
+       <b>REDIRECT</b> <i>user@domain</i>
+              After  the  message  is queued, send the message to
+              the  specified  address  instead  of  the  intended
+              recipient(s).
+
+              Note:  this action overrides the FILTER action, and
+              currently affects all recipients of the message.
 
        <i>restriction...</i>
               Apply the named UCE restriction(s) (<b>permit</b>, <b>reject</b>,
index eee2d501dc0285ef8655195d83b4dc00868deb1a..9ca51eb41b8de2df62104a407052df436d2f8664 100644 (file)
@@ -168,6 +168,11 @@ off in the second cleanup server. More details about content
 filtering are in the Postfix FILTER_README file. This feature
 overrides the main.cf <b>content_filter</b> setting.
 
+<dt>REDIRECT <i>user</i>@<i>domain</i> <dd>
+After the message is queued, send the message to the
+specified address instead of the intended recipients.
+overrides the FILTER action.
+
 </dl>
 
 <p>
@@ -267,6 +272,11 @@ off in the second cleanup server. More details about content
 filtering are in the Postfix FILTER_README file. This feature
 overrides the main.cf <b>content_filter</b> setting.
 
+<dt>REDIRECT <i>user</i>@<i>domain</i> <dd>
+After the message is queued, send the message to the 
+specified address instead of the intended recipients.
+overrides the FILTER action.
+
 </dl>
 
 <p>
index 12528f65948cdd3519de758fb8cf8bb4e7e6662f..fcdca96b4ca531ef1c7f1506feed90a1a218476d 100644 (file)
@@ -152,7 +152,14 @@ After the message is queued, send the entire message through
 a content filter.  More information about content filters
 is in the Postfix FILTER_README file.
 .sp
-Note: this action currently affects all recipients of the message.
+Note: this action overrides the \fBmain.cf content_filter\fR setting,
+and currently affects all recipients of the message.
+.IP "\fBREDIRECT \fIuser@domain\fR"
+After the message is queued, send the message to the specified
+address instead of the intended recipient(s).
+.sp
+Note: this action overrides the FILTER action, and currently affects
+all recipients of the message.
 .IP \fIrestriction...\fR
 Apply the named UCE restriction(s) (\fBpermit\fR, \fBreject\fR,
 \fBreject_unauth_destination\fR, and so on).
index eef4a4bf1ab65e8b14a25ee107d0aaa413d582fa..f1bbc99f16b881747f22c0bfef8b272ec6891a06 100644 (file)
@@ -63,7 +63,7 @@
 #      Note: lookup of the null sender address is not possible with
 #      some types of lookup table. By default, Postfix uses \fB<>\fR
 #      as the lookup key for such addresses. The value is specified with
-#      the \fBsmtpd_null_access_lookup_key\fR parameter in the Postfix 
+#      the \fBsmtpd_null_access_lookup_key\fR parameter in the Postfix
 #      \fBmain.cf\fR file.
 # ADDRESS EXTENSION
 # .fi
 #      the numerical code and text.
 # .IP \fBREJECT\fR
 # .IP "\fBREJECT \fIoptional text...\fR
-#      Reject the address etc. that matches the pattern. Reply with 
-#      \fI$reject_code optional text...\fR when the optional text is 
+#      Reject the address etc. that matches the pattern. Reply with
+#      \fI$reject_code optional text...\fR when the optional text is
 #      specified, otherwise reply with a generic error response message.
 # .IP \fBOK\fR
 #      Accept the address etc. that matches the pattern.
 #       a content filter.  More information about content filters
 #      is in the Postfix FILTER_README file.
 # .sp
-#      Note: this action currently affects all recipients of the message.
+#      Note: this action overrides the \fBmain.cf content_filter\fR setting,
+#      and currently affects all recipients of the message.
+# .IP "\fBREDIRECT \fIuser@domain\fR"
+#      After the message is queued, send the message to the specified
+#       address instead of the intended recipient(s).
+# .sp
+#      Note: this action overrides the FILTER action, and currently affects
+#      all recipients of the message.
 # .IP \fIrestriction...\fR
 #      Apply the named UCE restriction(s) (\fBpermit\fR, \fBreject\fR,
 #      \fBreject_unauth_destination\fR, and so on).
index 8a4083d3c8a1af790daac10823837b899258843d..88636ef12cfc67882227a0d3a1c83d82ddf9c63d 100644 (file)
@@ -67,6 +67,7 @@ typedef struct CLEANUP_STATE {
     MIME_STATE *mime_state;            /* MIME state engine */
     int     mime_errs;                 /* MIME error flags */
     char   *filter;                    /* from header/body patterns */
+    char   *redirect;                  /* from header/body patterns */
 } CLEANUP_STATE;
 
  /*
index 19131642d11718a7068958665e9749a89d21e15d..50b3d12edda3367cc223921fb85abc80884463aa 100644 (file)
@@ -89,6 +89,14 @@ void    cleanup_extracted(CLEANUP_STATE *state, int type, char *buf, int len)
     if (state->filter != 0)
        cleanup_out_string(state, REC_TYPE_FILT, state->filter);
 
+    /*
+     * Output the optional redirect target address before the mandatory
+     * Return-Receipt-To and Errors-To queue file records so that the queue
+     * manager will pick up the address before starting deliveries.
+     */
+    if (state->redirect != 0)
+       cleanup_out_string(state, REC_TYPE_RDR, state->redirect);
+
     /*
      * Older Postfix versions didn't emit encoding information, so this
      * record can only be optional. Putting this before the mandatory
index b7283fde0fdcfbdcf4ec56f6946735cb7eff7ab1..28ac89b10d861fc6c1c20bb76561e0abc9b75bf2 100644 (file)
@@ -322,8 +322,8 @@ static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
        } else {
            if (state->filter)
                myfree(state->filter);
-           /* XXX should log something? */
            state->filter = mystrdup(optional_text);
+           cleanup_act_log(state, "filter", context, buf, optional_text);
        }
        return (CLEANUP_ACT_KEEP);
     }
@@ -338,6 +338,19 @@ static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
        state->flags |= CLEANUP_FLAG_HOLD;
        return (CLEANUP_ACT_KEEP);
     }
+    if (STREQUAL(value, "REDIRECT", command_len)) {
+       if (strchr(optional_text, '@') == 0) {
+           msg_warn("bad REDIRECT target \"%s\" in %s map, need user@domain",
+                    optional_text, map_class);
+       } else {
+           if (state->redirect)
+               myfree(state->redirect);
+           state->redirect = mystrdup(optional_text);
+           cleanup_act_log(state, "redirect", context, buf, optional_text);
+           state->flags &= ~CLEANUP_FLAG_FILTER;
+       }
+       return (CLEANUP_ACT_KEEP);
+    }
     if (*optional_text)
        msg_warn("unexpected text after command in %s map: %s",
                 map_class, value);
index 522db49c238b0ef36685bb7668985b884c1e4045..2f17fd050a183d95bba9309e08704f3ec5ee1a61 100644 (file)
@@ -92,6 +92,7 @@ CLEANUP_STATE *cleanup_state_alloc(void)
     state->mime_state = 0;
     state->mime_errs = 0;
     state->filter = 0;
+    state->redirect = 0;
     return (state);
 }
 
@@ -131,5 +132,7 @@ void    cleanup_state_free(CLEANUP_STATE *state)
        mime_state_free(state->mime_state);
     if (state->filter)
        myfree(state->filter);
+    if (state->redirect)
+       myfree(state->redirect);
     myfree((char *) state);
 }
index c88e30bdf8b9f4c9b87e85ad2464182508f52766..267ee712b2e376cd5164258c858ead9bd3e47ee1 100644 (file)
@@ -1157,6 +1157,12 @@ resolve_local.o: mail_params.h
 resolve_local.o: own_inet_addr.h
 resolve_local.o: resolve_local.h
 resolve_local.o: match_parent_style.h
+resover.o: resover.c
+resover.o: ../../include/sys_defs.h
+resover.o: ../../include/msg.h
+resover.o: ../../include/vstring.h
+resover.o: ../../include/vbuf.h
+resover.o: ../../include/split_at.h
 rewrite_clnt.o: rewrite_clnt.c
 rewrite_clnt.o: ../../include/sys_defs.h
 rewrite_clnt.o: ../../include/msg.h
index 9eb5d9876d6ee6c6bf3245ebe9a648213ae073d2..718590173fc2140d03ef359360da9b8160600f40 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      "20030124"
+#define MAIL_RELEASE_DATE      "20030125"
 
 #define VAR_MAIL_VERSION       "mail_version"
 #define DEF_MAIL_VERSION       "2.0.3-" MAIL_RELEASE_DATE
index a146569153637c1c9247e03d60e392dfdbedad36..0e54d7ee9ed23702afda8ea5f8073e463d8fa8e5 100644 (file)
@@ -37,6 +37,7 @@
 #define REC_TYPE_WARN  'W'             /* warning message time */
 #define REC_TYPE_ATTR  'A'             /* named attribute for extensions */
 
+#define REC_TYPE_RDR   '>'             /* redirect target */
 #define REC_TYPE_FLGS  'f'             /* cleanup processing flags */
 
 #define REC_TYPE_MESG  'M'             /* start message records */
@@ -64,9 +65,9 @@
   * allow for the presence of A records in the extracted segment, because it
   * can be requested to re-process already queued mail with `postsuper -r'.
   */
-#define REC_TYPE_ENVELOPE      "MCTFILSDROWVA"
+#define REC_TYPE_ENVELOPE      "MCTFILSDROWVA>"
 #define REC_TYPE_CONTENT       "XLN"
-#define REC_TYPE_EXTRACT       "EDROPreAFI"
+#define REC_TYPE_EXTRACT       "EDROPreAFI>"
 
  /*
   * The record at the beginning of the envelope segment specifies the message
index 1b1b139b83a9261cad47adc9b016083b9b1d277d..c9a01a6f355ef32c3e3a8f3c96c6c26bca130def 100644 (file)
@@ -199,6 +199,7 @@ qmgr_message.o: ../../include/verp_sender.h
 qmgr_message.o: ../../include/mail_proto.h
 qmgr_message.o: ../../include/iostuff.h
 qmgr_message.o: ../../include/attr.h
+qmgr_message.o: ../../include/rewrite_clnt.h
 qmgr_message.o: ../../include/resolve_clnt.h
 qmgr_message.o: qmgr.h
 qmgr_message.o: ../../include/scan_dir.h
index 54d91e588948b0ef90278f6d41516968f960f7e9..65037f65713852852f9587842fff03348f4f944f 100644 (file)
@@ -176,7 +176,8 @@ struct QMGR_ENTRY_LIST {
 };
 
 struct QMGR_QUEUE {
-    char   *name;                      /* domain name */
+    char   *name;                      /* domain name or address */
+    char   *nexthop;                   /* domain name */
     int     todo_refcount;             /* queue entries (todo list) */
     int     busy_refcount;             /* queue entries (busy list) */
     int     window;                    /* slow open algorithm */
@@ -194,7 +195,7 @@ struct QMGR_QUEUE {
 
 extern int qmgr_queue_count;
 
-extern QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *, const char *);
+extern QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *, const char *, const char *);
 extern void qmgr_queue_done(QMGR_QUEUE *);
 extern void qmgr_queue_throttle(QMGR_QUEUE *, const char *);
 extern void qmgr_queue_unthrottle(QMGR_QUEUE *);
@@ -271,6 +272,7 @@ struct QMGR_MESSAGE {
     char   *return_receipt;            /* confirm receipt address */
     char   *filter_xport;              /* filtering transport */
     char   *inspect_xport;             /* inspecting transport */
+    char   *redirect_addr;             /* info@spammer.tld */
     long    data_size;                 /* message content size */
     long    rcpt_offset;               /* more recipients here */
     long    unread_offset;             /* more unread recipients here */
index c26efff611097f29d24baa651c79c6badab056e2..3f7e78bb5d306a0c5ffb14028b40f413ab2a0c40 100644 (file)
@@ -129,11 +129,9 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
     QMGR_RCPT_LIST list = entry->rcpt_list;
     QMGR_RCPT *recipient;
     QMGR_MESSAGE *message = entry->message;
-    char   *cp;
     VSTRING *sender_buf = 0;
     char   *sender;
     int     flags;
-    char   *nexthop;
 
     /*
      * If variable envelope return path is requested, change prefix+@origin
@@ -149,28 +147,15 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
        sender = vstring_str(sender_buf);
     }
 
-    /*
-     * With mail transports that accept only one recipient per delivery, the
-     * queue name is user@nexthop, so that we can implement per-recipient
-     * concurrency limits. However, the delivery agent protocol expects
-     * nexthop only, so we must strip off the recipient local part.
-     * 
-     * XXX Should have separate fields for queue name and for destination, so
-     * that we don't have to make a special case for the error delivery agent
-     * (where nexthop is arbitrary text). See also: qmgr_message.c.
-     */
     flags = message->tflags
        | (message->inspect_xport ? DEL_REQ_FLAG_BOUNCE : DEL_REQ_FLAG_DEFLT);
-    nexthop = strcmp(entry->queue->transport->name, MAIL_SERVICE_ERROR) != 0
-       && (cp = strrchr(entry->queue->name, '@')) != 0 && cp[1] ?
-       cp + 1 : entry->queue->name;
     attr_print(stream, ATTR_FLAG_MORE,
               ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
               ATTR_TYPE_STR, MAIL_ATTR_QUEUE, message->queue_name,
               ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, message->queue_id,
               ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, message->data_offset,
               ATTR_TYPE_LONG, MAIL_ATTR_SIZE, message->data_size,
-              ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop,
+              ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, entry->queue->nexthop,
               ATTR_TYPE_STR, MAIL_ATTR_ENCODING, message->encoding,
               ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
               ATTR_TYPE_STR, MAIL_ATTR_ERRTO, message->errors_to,
index 5032a45563beac7f99f507e08b3ba41a7539575c..c26d2932549c681f8ca9ad497b194ff39771604e 100644 (file)
@@ -300,7 +300,7 @@ QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *peer, QMGR_MESSAGE *message)
            && (now = event_time()) >= queue->clog_time_to_warn) {
            active_share = queue_length / (double) qmgr_message_count;
            msg_warn("mail for %s is using up %d of %d active queue entries",
-                    queue->name, queue_length, qmgr_message_count);
+                    queue->nexthop, queue_length, qmgr_message_count);
            if (active_share < 0.9)
                msg_warn("this may slow down other mail deliveries");
            transport = queue->transport;
@@ -314,7 +314,7 @@ QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *peer, QMGR_MESSAGE *message)
                         VAR_QMGR_ACT_LIMIT, var_qmgr_active_limit);
            else if (queue->peers.next != queue->peers.prev)
                msg_warn("you may need a separate master.cf transport for %s",
-                        queue->name);
+                        queue->nexthop);
            else {
                msg_warn("you may need to reduce %s connect and helo timeouts",
                         transport->name);
index 60d051c9f2f59d489cc10d340983a8a97963ce29..3be89bfcfd9b098ea1f71db22a3ed990ffa53e86 100644 (file)
 
 /* Client stubs. */
 
+#include <rewrite_clnt.h>
 #include <resolve_clnt.h>
 
 /* Application-specific. */
@@ -159,6 +160,7 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name,
     message->return_receipt = 0;
     message->filter_xport = 0;
     message->inspect_xport = 0;
+    message->redirect_addr = 0;
     message->data_size = 0;
     message->warn_offset = 0;
     message->warn_time = 0;
@@ -385,6 +387,10 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
            if (message->inspect_xport != 0)
                myfree(message->inspect_xport);
            message->inspect_xport = mystrdup(start);
+       } else if (rec_type == REC_TYPE_RDR) {
+           if (message->redirect_addr != 0)
+               myfree(message->redirect_addr);
+           message->redirect_addr = mystrdup(start);
        } else if (rec_type == REC_TYPE_FROM) {
            if (message->sender == 0) {
                message->sender = mystrdup(start);
@@ -577,7 +583,7 @@ static int qmgr_message_sort_compare(const void *p1, const void *p2)
            return (result);
 
        /*
-        * Compare (already lowercased) next-hop hostname.
+        * Compare queue name (nexthop or recipient@nexthop).
         */
        if ((result = strcmp(queue1->name, queue2->name)) != 0)
            return (result);
@@ -619,6 +625,24 @@ static void qmgr_message_sort(QMGR_MESSAGE *message)
     }
 }
 
+/* qmgr_resolve_one - resolve or skip one recipient */
+
+static int qmgr_resolve_one(QMGR_MESSAGE *message, QMGR_RCPT *recipient,
+                                   const char *addr, RESOLVE_REPLY *reply)
+{
+    resolve_clnt_query(addr, reply);
+    if (reply->flags & RESOLVE_FLAG_FAIL) {
+       qmgr_defer_recipient(message, recipient, "address resolver failure");
+       return (-1);
+    } else if (reply->flags & RESOLVE_FLAG_ERROR) {
+       qmgr_bounce_recipient(message, recipient,
+                             "bad address syntax: \"%s\"", addr);
+       return (-1);
+    } else {
+       return (0);
+    }
+}
+
 /* qmgr_message_resolve - resolve recipients */
 
 static void qmgr_message_resolve(QMGR_MESSAGE *message)
@@ -629,6 +653,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
     QMGR_TRANSPORT *transport = 0;
     QMGR_QUEUE *queue = 0;
     RESOLVE_REPLY reply;
+    VSTRING *queue_name;
     char   *at;
     char  **cpp;
     char   *nexthop;
@@ -641,63 +666,75 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
 #define UPDATE(ptr,new)        { myfree(ptr); ptr = mystrdup(new); }
 
     resolve_clnt_init(&reply);
+    queue_name = vstring_alloc(1);
     for (recipient = list.info; recipient < list.info + list.len; recipient++) {
 
        /*
-        * Resolve the destination to (transport, nexthop, address). The
-        * result address may differ from the one specified by the sender.
+        * Redirect overrides all else. But only once (per batch of
+        * recipients). For consistency with the remainder of Postfix,
+        * rewrite the address to canonical form before resolving it.
         */
-       if (var_sender_routing == 0) {
-           resolve_clnt_query(recipient->address, &reply);
-           if (reply.flags & RESOLVE_FLAG_FAIL) {
-               qmgr_defer_recipient(message, recipient,
-                                    "address resolver failure");
+       if (message->redirect_addr) {
+           if (recipient > list.info) {
+               recipient->queue = 0;
                continue;
            }
-           if (reply.flags & RESOLVE_FLAG_ERROR) {
-               qmgr_bounce_recipient(message, recipient,
-                                     "bad address syntax: \"%s\"",
-                                     recipient->address);
+           rewrite_clnt_internal(REWRITE_CANON, message->redirect_addr,
+                                 reply.recipient);
+           UPDATE(recipient->address, STR(reply.recipient));
+           if (qmgr_resolve_one(message, recipient,
+                                recipient->address, &reply) < 0)
                continue;
-           }
-       } else {
-           resolve_clnt_query(message->sender, &reply);
-           if (reply.flags & RESOLVE_FLAG_FAIL) {
-               qmgr_defer_recipient(message, recipient,
-                                    "address resolver failure");
-               continue;
-           }
-           if (reply.flags & RESOLVE_FLAG_ERROR) {
-               qmgr_bounce_recipient(message, recipient,
-                                     "bad address syntax: \"%s\"",
-                                     message->sender);
-               continue;
-           }
-           vstring_strcpy(reply.recipient, recipient->address);
+           if (!STREQ(recipient->address, STR(reply.recipient)))
+               UPDATE(recipient->address, STR(reply.recipient));
        }
-       if (message->filter_xport) {
+
+       /*
+        * Content filtering overrides the address resolver.
+        */
+       else if (message->filter_xport) {
            vstring_strcpy(reply.transport, message->filter_xport);
            if ((nexthop = split_at(STR(reply.transport), ':')) == 0
                || *nexthop == 0)
                nexthop = var_myhostname;
            vstring_strcpy(reply.nexthop, nexthop);
-       } else {
+           vstring_strcpy(reply.recipient, recipient->address);
+       }
+
+       /*
+        * Resolve the destination to (transport, nexthop, address). The
+        * result address may differ from the one specified by the sender.
+        */
+       else if (var_sender_routing == 0) {
+           if (qmgr_resolve_one(message, recipient,
+                                recipient->address, &reply) < 0)
+               continue;
            if (!STREQ(recipient->address, STR(reply.recipient)))
                UPDATE(recipient->address, STR(reply.recipient));
        }
+
+       /*
+        * XXX Sender-based routing does not work very well, because it has
+        * problems with sending bounces.
+        */
+       else {
+           if (qmgr_resolve_one(message, recipient,
+                                message->sender, &reply) < 0)
+               continue;
+           vstring_strcpy(reply.recipient, recipient->address);
+       }
+
+       /*
+        * Bounce null recipients. This should never happen, but is most
+        * likely the result of a fault in a different program, so aborting
+        * the queue manager process does not help.
+        */
        if (recipient->address[0] == 0) {
            qmgr_bounce_recipient(message, recipient,
                                  "null recipient address");
            continue;
        }
 
-       /*
-        * XXX The nexthop destination is also used as lookup key for the
-        * per-destination queue. Fold the nexthop to lower case so that we
-        * don't have multiple queues for the same site.
-        */
-       lowercase(STR(reply.nexthop));
-
        /*
         * Bounce recipient addresses that start with `-'. External commands
         * may misinterpret such addresses as command-line options.
@@ -716,46 +753,6 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
            continue;
        }
 
-       /*
-        * Queues are identified by the transport name and by the next-hop
-        * hostname. When the delivery agent accepts only one recipient per
-        * delivery, give each recipient its own queue, so that deliveries to
-        * different recipients of the same message can happen in parallel.
-        * This also has the benefit that one bad recipient cannot interfere
-        * with deliveries to other recipients. XXX Should split the address
-        * on the recipient delimiter if one is defined, but doing a proper
-        * job requires knowledge of local aliases. Yuck! I don't want to
-        * duplicate delivery-agent specific knowledge in the queue manager.
-        * 
-        * XXX The nexthop field is overloaded to serve as destination and as
-        * queue name. Should have separate fields for queue name and for
-        * destination, so that we don't have to make a special case for the
-        * error delivery agent (where nexthop is arbitrary text). See also:
-        * qmgr_deliver.c.
-        */
-       at = strrchr(STR(reply.recipient), '@');
-       len = (at ? (at - STR(reply.recipient)) : strlen(STR(reply.recipient)));
-
-       /*
-        * Look up or instantiate the proper transport. We're working a
-        * little ahead, doing queue management stuff that used to be done
-        * way down.
-        */
-       if (transport == 0 || !STREQ(transport->name, STR(reply.transport))) {
-           if ((transport = qmgr_transport_find(STR(reply.transport))) == 0)
-               transport = qmgr_transport_create(STR(reply.transport));
-           queue = 0;
-       }
-       if (strcmp(transport->name, MAIL_SERVICE_ERROR) != 0
-           && transport->recipient_limit == 1) {
-           VSTRING_SPACE(reply.nexthop, len + 2);
-           memmove(STR(reply.nexthop) + len + 1, STR(reply.nexthop),
-                   LEN(reply.nexthop) + 1);
-           memcpy(STR(reply.nexthop), STR(reply.recipient), len);
-           STR(reply.nexthop)[len] = '@';
-           lowercase(STR(reply.nexthop));
-       }
-
        /*
         * Discard mail to the local double bounce address here, so this
         * system can run without a local delivery agent. They'd still have
@@ -766,6 +763,9 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * be directed to a general-purpose null delivery agent.
         */
        if (reply.flags & RESOLVE_CLASS_LOCAL) {
+           at = strrchr(STR(reply.recipient), '@');
+           len = (at ? (at - STR(reply.recipient))
+                  : strlen(STR(reply.recipient)));
            if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
                            len) == 0
                && !var_double_bounce_sender[len]) {
@@ -798,16 +798,6 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
            }
        }
 
-       /*
-        * XXX Gross hack alert. We want to group recipients by transport and
-        * by next-hop hostname, in order to minimize the number of network
-        * transactions. However, it would be wasteful to have an in-memory
-        * resolver reply structure for each in-core recipient. Instead, we
-        * bind each recipient to an in-core queue instance which is needed
-        * anyway. That gives all information needed for recipient grouping.
-        */
-#if 0
-
        /*
         * Look up or instantiate the proper transport.
         */
@@ -816,7 +806,6 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
                transport = qmgr_transport_create(STR(reply.transport));
            queue = 0;
        }
-#endif
 
        /*
         * This transport is dead. Defer delivery to this recipient.
@@ -826,13 +815,50 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
            continue;
        }
 
+       /*
+        * The nexthop destination provides the default name for the
+        * per-destination queue. When the delivery agent accepts only one
+        * recipient per delivery, give each recipient its own queue, so that
+        * deliveries to different recipients of the same message can happen
+        * in parallel, and so that we can enforce per-recipient concurrency
+        * limits and prevent one recipient from tying up all the delivery
+        * agent resources. We use recipient@nexthop as queue name rather
+        * than the actual recipient domain name, so that one recipient in
+        * multiple equivalent domains cannot evade the per-recipient
+        * concurrency limit. XXX Should split the address on the recipient
+        * delimiter if one is defined, but doing a proper job requires
+        * knowledge of local aliases. Yuck! I don't want to duplicate
+        * delivery-agent specific knowledge in the queue manager.
+        * 
+        * Fold the result to lower case so that we don't have multiple queues
+        * for the same name.
+        * 
+        * Important! All recipients in a queue must have the same nexthop
+        * value. It is OK to have multiple queues with the same nexthop
+        * value, but only when those queues are named after recipients.
+        */
+       vstring_strcpy(queue_name, STR(reply.nexthop));
+       if (strcmp(transport->name, MAIL_SERVICE_ERROR) != 0
+           && transport->recipient_limit == 1) {
+           at = strrchr(STR(reply.recipient), '@');
+           len = (at ? (at - STR(reply.recipient))
+                  : strlen(STR(reply.recipient)));
+           VSTRING_SPACE(queue_name, len + 2);
+           memmove(STR(queue_name) + len + 1, STR(queue_name),
+                   LEN(queue_name) + 1);
+           memcpy(STR(queue_name), STR(reply.recipient), len);
+           STR(queue_name)[len] = '@';
+       }
+       lowercase(STR(queue_name));
+
        /*
         * This transport is alive. Find or instantiate a queue for this
         * recipient.
         */
-       if (queue == 0 || !STREQ(queue->name, STR(reply.nexthop))) {
-           if ((queue = qmgr_queue_find(transport, STR(reply.nexthop))) == 0)
-               queue = qmgr_queue_create(transport, STR(reply.nexthop));
+       if (queue == 0 || !STREQ(queue->name, STR(queue_name))) {
+           if ((queue = qmgr_queue_find(transport, STR(queue_name))) == 0)
+               queue = qmgr_queue_create(transport, STR(queue_name),
+                                         STR(reply.nexthop));
        }
 
        /*
@@ -849,6 +875,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
        recipient->queue = queue;
     }
     resolve_clnt_free(&reply);
+    vstring_free(queue_name);
 }
 
 /* qmgr_message_assign - assign recipients to specific delivery requests */
@@ -973,6 +1000,8 @@ void    qmgr_message_free(QMGR_MESSAGE *message)
        myfree(message->filter_xport);
     if (message->inspect_xport)
        myfree(message->inspect_xport);
+    if (message->redirect_addr)
+       myfree(message->redirect_addr);
     qmgr_rcpt_list_free(&message->rcpt_list);
     qmgr_message_count--;
     myfree((char *) message);
index 9854011b6fadbac56941f23d0e971143b1978359..b86034a571d2fdde3a2d29a4587e2a6a2918c187 100644 (file)
@@ -8,16 +8,17 @@
 /*
 /*     int     qmgr_queue_count;
 /*
-/*     QMGR_QUEUE *qmgr_queue_create(transport, site)
+/*     QMGR_QUEUE *qmgr_queue_create(transport, name, nexthop)
 /*     QMGR_TRANSPORT *transport;
-/*     const char *site;
+/*     const char *name;
+/*     const char *nexthop;
 /*
 /*     void    qmgr_queue_done(queue)
 /*     QMGR_QUEUE *queue;
 /*
-/*     QMGR_QUEUE *qmgr_queue_find(transport, site)
+/*     QMGR_QUEUE *qmgr_queue_find(transport, name)
 /*     QMGR_TRANSPORT *transport;
-/*     const char *site;
+/*     const char *name;
 /*
 /*     void    qmgr_queue_throttle(queue, reason)
 /*     QMGR_QUEUE *queue;
@@ -34,7 +35,7 @@
 /*     qmgr_queue_count is a global counter for the total number
 /*     of in-core queue structures.
 /*
-/*     qmgr_queue_create() creates an empty queue for the named
+/*     qmgr_queue_create() creates an empty named queue for the named
 /*     transport and destination. The queue is given an initial
 /*     concurrency limit as specified with the
 /*     \fIinitial_destination_concurrency\fR configuration parameter,
@@ -45,9 +46,8 @@
 /*     its entries have been taken care of. It is an error to dispose
 /*     of a dead queue.
 /*
-/*     qmgr_queue_find() looks up the queue for the named destination
-/*     for the named transport. A null result means that the queue
-/*     was not found.
+/*     qmgr_queue_find() looks up the named queue for the named
+/*     transport. A null result means that the queue was not found.
 /*
 /*     qmgr_queue_throttle() handles a delivery error, and decrements the
 /*     concurrency limit for the destination. When the concurrency limit
@@ -212,13 +212,15 @@ void    qmgr_queue_done(QMGR_QUEUE *queue)
     QMGR_LIST_UNLINK(transport->queue_list, QMGR_QUEUE *, queue, peers);
     htable_delete(transport->queue_byname, queue->name, (void (*) (char *)) 0);
     myfree(queue->name);
+    myfree(queue->nexthop);
     qmgr_queue_count--;
     myfree((char *) queue);
 }
 
 /* qmgr_queue_create - create in-core queue for site */
 
-QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *site)
+QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *name,
+                                     const char *nexthop)
 {
     QMGR_QUEUE *queue;
 
@@ -229,7 +231,8 @@ QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *site)
 
     queue = (QMGR_QUEUE *) mymalloc(sizeof(QMGR_QUEUE));
     qmgr_queue_count++;
-    queue->name = mystrdup(site);
+    queue->name = mystrdup(name);
+    queue->nexthop = mystrdup(nexthop);
     queue->todo_refcount = 0;
     queue->busy_refcount = 0;
     queue->transport = transport;
@@ -240,13 +243,13 @@ QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *site)
     queue->clog_time_to_warn = 0;
     queue->blocker_tag = 0;
     QMGR_LIST_APPEND(transport->queue_list, queue, peers);
-    htable_enter(transport->queue_byname, site, (char *) queue);
+    htable_enter(transport->queue_byname, name, (char *) queue);
     return (queue);
 }
 
-/* qmgr_queue_find - find in-core queue for site */
+/* qmgr_queue_find - find in-core named queue */
 
-QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *transport, const char *site)
+QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *transport, const char *name)
 {
-    return ((QMGR_QUEUE *) htable_find(transport->queue_byname, site));
+    return ((QMGR_QUEUE *) htable_find(transport->queue_byname, name));
 }
index 5ee7d59d8f4961c5999d7d670113f7bbdf14144a..91d20d8384fee5977819fb4a05cd1000e2d7df7d 100644 (file)
@@ -61,6 +61,8 @@ proxymap.o: ../../include/msg.h
 proxymap.o: ../../include/mymalloc.h
 proxymap.o: ../../include/vstring.h
 proxymap.o: ../../include/vbuf.h
+proxymap.o: ../../include/htable.h
+proxymap.o: ../../include/stringops.h
 proxymap.o: ../../include/dict.h
 proxymap.o: ../../include/vstream.h
 proxymap.o: ../../include/argv.h
index e81e5e852b1d67c9b5a8f6ca518a3c9b5ebc3e5d..0caed2983eb6554c55dbe3ad0f6e313f9ae2d68d 100644 (file)
 char   *var_local_rcpt_maps;
 char   *var_virt_alias_maps;
 char   *var_virt_alias_doms;
-char   *var_virt_mbox_maps;
-char   *var_virt_mbox_doms;
+char   *var_virt_mailbox_maps;
+char   *var_virt_mailbox_doms;
 char   *var_relay_rcpt_maps;
 char   *var_relay_domains;
 char   *var_canonical_maps;
 char   *var_send_canon_maps;
 char   *var_rcpt_canon_maps;
-char   *var_relocatedmaps;
+char   *var_relocated_maps;
 char   *var_transport_maps;
 char   *var_proxy_read_maps;
 
@@ -385,14 +385,14 @@ int     main(int argc, char **argv)
        VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps, 0, 0,
        VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps, 0, 0,
        VAR_VIRT_ALIAS_DOMS, DEF_VIRT_ALIAS_DOMS, &var_virt_alias_doms, 0, 0,
-       VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mbox_maps, 0, 0,
-       VAR_VIRT_MAILBOX_DOMS, DEF_VIRT_MAILBOX_DOMS, &var_virt_mbox_doms, 0, 0,
+       VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0,
+       VAR_VIRT_MAILBOX_DOMS, DEF_VIRT_MAILBOX_DOMS, &var_virt_mailbox_doms, 0, 0,
        VAR_RELAY_RCPT_MAPS, DEF_RELAY_RCPT_MAPS, &var_relay_rcpt_maps, 0, 0,
        VAR_RELAY_DOMAINS, DEF_RELAY_DOMAINS, &var_relay_domains, 0, 0,
        VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0,
        VAR_SEND_CANON_MAPS, DEF_SEND_CANON_MAPS, &var_send_canon_maps, 0, 0,
        VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0,
-       VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocatedmaps, 0, 0,
+       VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps, 0, 0,
        VAR_TRANSPORT_MAPS, DEF_TRANSPORT_MAPS, &var_transport_maps, 0, 0,
        VAR_PROXY_READ_MAPS, DEF_PROXY_READ_MAPS, &var_proxy_read_maps, 0, 0,
        0,
index e128810a77f0dccc87d283b01a0d8e2630310f09..f4a1419c8a29c3358bb04271c9591b4e4b2e4932 100644 (file)
@@ -186,6 +186,7 @@ qmgr_message.o: ../../include/verp_sender.h
 qmgr_message.o: ../../include/mail_proto.h
 qmgr_message.o: ../../include/iostuff.h
 qmgr_message.o: ../../include/attr.h
+qmgr_message.o: ../../include/rewrite_clnt.h
 qmgr_message.o: ../../include/resolve_clnt.h
 qmgr_message.o: qmgr.h
 qmgr_message.o: ../../include/scan_dir.h
index e553e1793e0bc3d307b92d56fef41b85771cceac..67a1adfcedc94013a066959094ba8a2a6891f9e8 100644 (file)
@@ -140,7 +140,8 @@ struct QMGR_ENTRY_LIST {
 };
 
 struct QMGR_QUEUE {
-    char   *name;                      /* domain name */
+    char   *name;                      /* domain name or address */
+    char   *nexthop;                   /* domain name */
     int     todo_refcount;             /* queue entries (todo list) */
     int     busy_refcount;             /* queue entries (busy list) */
     int     window;                    /* slow open algorithm */
@@ -157,7 +158,7 @@ struct QMGR_QUEUE {
 
 extern int qmgr_queue_count;
 
-extern QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *, const char *);
+extern QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *, const char *, const char *);
 extern QMGR_QUEUE *qmgr_queue_select(QMGR_TRANSPORT *);
 extern void qmgr_queue_done(QMGR_QUEUE *);
 extern void qmgr_queue_throttle(QMGR_QUEUE *, const char *);
@@ -231,6 +232,7 @@ struct QMGR_MESSAGE {
     char   *return_receipt;            /* confirm receipt address */
     char   *filter_xport;              /* filtering transport */
     char   *inspect_xport;             /* inspecting transport */
+    char   *redirect_addr;             /* info@spammer.tld */
     long    data_size;                 /* message content size */
     long    rcpt_offset;               /* more recipients here */
     QMGR_RCPT_LIST rcpt_list;          /* complete addresses */
index dbdcfd75de6cc267abd997c0df9f2db407104a9f..c1e6ab74904b4051107d5d361b8958507390ace5 100644 (file)
@@ -2,7 +2,7 @@
 /* NAME
 /*     qmgr_deliver 3
 /* SUMMARY
-/*     deliver one pe-site queue entry to that site
+/*     deliver one per-site queue entry to that site
 /* SYNOPSIS
 /*     #include "qmgr.h"
 /*
@@ -124,11 +124,9 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
     QMGR_RCPT_LIST list = entry->rcpt_list;
     QMGR_RCPT *recipient;
     QMGR_MESSAGE *message = entry->message;
-    char   *cp;
     VSTRING *sender_buf = 0;
     char   *sender;
     int     flags;
-    char   *nexthop;
 
     /*
      * If variable envelope return path is requested, change prefix+@origin
@@ -144,28 +142,15 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
        sender = vstring_str(sender_buf);
     }
 
-    /*
-     * With mail transports that accept only one recipient per delivery, the
-     * queue name is user@nexthop, so that we can implement per-recipient
-     * concurrency limits. However, the delivery agent protocol expects
-     * nexthop only, so we must strip off the recipient local part.
-     * 
-     * XXX Should have separate fields for queue name and for destination, so
-     * that we don't have to make a special case for the error delivery agent
-     * (where nexthop is arbitrary text). See also: qmgr_message.c.
-     */
     flags = message->tflags
        | (message->inspect_xport ? DEL_REQ_FLAG_BOUNCE : DEL_REQ_FLAG_DEFLT);
-    nexthop = strcmp(entry->queue->transport->name, MAIL_SERVICE_ERROR) != 0
-       && (cp = strrchr(entry->queue->name, '@')) != 0 && cp[1] ?
-       cp + 1 : entry->queue->name;
     attr_print(stream, ATTR_FLAG_MORE,
               ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
               ATTR_TYPE_STR, MAIL_ATTR_QUEUE, message->queue_name,
               ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, message->queue_id,
               ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, message->data_offset,
               ATTR_TYPE_LONG, MAIL_ATTR_SIZE, message->data_size,
-              ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop,
+              ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, entry->queue->nexthop,
               ATTR_TYPE_STR, MAIL_ATTR_ENCODING, message->encoding,
               ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
               ATTR_TYPE_STR, MAIL_ATTR_ERRTO, message->errors_to,
index 57de8ebd602fae04092322ea4f3223706494590a..3d3f9f461e53f44b24d233d74c36d1de2dc47744 100644 (file)
@@ -239,7 +239,7 @@ QMGR_ENTRY *qmgr_entry_create(QMGR_QUEUE *queue, QMGR_MESSAGE *message)
            && (now = event_time()) >= queue->clog_time_to_warn) {
            active_share = queue_length / (double) qmgr_message_count;
            msg_warn("mail for %s is using up %d of %d active queue entries",
-                    queue->name, queue_length, qmgr_message_count);
+                    queue->nexthop, queue_length, qmgr_message_count);
            if (active_share < 0.9)
                msg_warn("this may slow down other mail deliveries");
            transport = queue->transport;
@@ -253,7 +253,7 @@ QMGR_ENTRY *qmgr_entry_create(QMGR_QUEUE *queue, QMGR_MESSAGE *message)
                         VAR_QMGR_ACT_LIMIT, var_qmgr_active_limit);
            else if (queue->peers.next != queue->peers.prev)
                msg_warn("you may need a separate master.cf transport for %s",
-                        queue->name);
+                        queue->nexthop);
            else {
                msg_warn("you may need to reduce %s connect and helo timeouts",
                         transport->name);
index 15919b3fa2046ea6f8fbc20a977dd233e1f2acea..f3bc58e49a6a3d8e9168c4bebd3fcea4608529f9 100644 (file)
 
 /* Client stubs. */
 
+#include <rewrite_clnt.h>
 #include <resolve_clnt.h>
 
 /* Application-specific. */
@@ -149,6 +150,7 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name,
     message->return_receipt = 0;
     message->filter_xport = 0;
     message->inspect_xport = 0;
+    message->redirect_addr = 0;
     message->data_size = 0;
     message->warn_offset = 0;
     message->warn_time = 0;
@@ -266,6 +268,10 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
            if (message->inspect_xport != 0)
                myfree(message->inspect_xport);
            message->inspect_xport = mystrdup(start);
+       } else if (rec_type == REC_TYPE_RDR) {
+           if (message->redirect_addr != 0)
+               myfree(message->redirect_addr);
+           message->redirect_addr = mystrdup(start);
        } else if (rec_type == REC_TYPE_FROM) {
            if (message->sender == 0) {
                message->sender = mystrdup(start);
@@ -452,14 +458,14 @@ static int qmgr_message_sort_compare(const void *p1, const void *p2)
        /*
         * Compare message transport.
         */
-       if ((result = strcasecmp(queue1->transport->name,
-                                queue2->transport->name)) != 0)
+       if ((result = strcmp(queue1->transport->name,
+                            queue2->transport->name)) != 0)
            return (result);
 
        /*
-        * Compare next-hop hostname.
+        * Compare queue name (nexthop or recipient@nexthop).
         */
-       if ((result = strcasecmp(queue1->name, queue2->name)) != 0)
+       if ((result = strcmp(queue1->name, queue2->name)) != 0)
            return (result);
     }
 
@@ -499,6 +505,24 @@ static void qmgr_message_sort(QMGR_MESSAGE *message)
     }
 }
 
+/* qmgr_resolve_one - resolve or skip one recipient */
+
+static int qmgr_resolve_one(QMGR_MESSAGE *message, QMGR_RCPT *recipient,
+                                   const char *addr, RESOLVE_REPLY *reply)
+{
+    resolve_clnt_query(addr, reply);
+    if (reply->flags & RESOLVE_FLAG_FAIL) {
+       qmgr_defer_recipient(message, recipient, "address resolver failure");
+       return (-1);
+    } else if (reply->flags & RESOLVE_FLAG_ERROR) {
+       qmgr_bounce_recipient(message, recipient,
+                             "bad address syntax: \"%s\"", addr);
+       return (-1);
+    } else {
+       return (0);
+    }
+}
+
 /* qmgr_message_resolve - resolve recipients */
 
 static void qmgr_message_resolve(QMGR_MESSAGE *message)
@@ -509,6 +533,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
     QMGR_TRANSPORT *transport = 0;
     QMGR_QUEUE *queue = 0;
     RESOLVE_REPLY reply;
+    VSTRING *queue_name;
     char   *at;
     char  **cpp;
     char   *nexthop;
@@ -521,63 +546,75 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
 #define UPDATE(ptr,new)        { myfree(ptr); ptr = mystrdup(new); }
 
     resolve_clnt_init(&reply);
+    queue_name = vstring_alloc(1);
     for (recipient = list.info; recipient < list.info + list.len; recipient++) {
 
        /*
-        * Resolve the destination to (transport, nexthop, address). The
-        * result address may differ from the one specified by the sender.
+        * Redirect overrides all else. But only once (per batch of
+        * recipients). For consistency with the remainder of Postfix,
+        * rewrite the address to canonical form before resolving it.
         */
-       if (var_sender_routing == 0) {
-           resolve_clnt_query(recipient->address, &reply);
-           if (reply.flags & RESOLVE_FLAG_FAIL) {
-               qmgr_defer_recipient(message, recipient,
-                                    "address resolver failure");
+       if (message->redirect_addr) {
+           if (recipient > list.info) {
+               recipient->queue = 0;
                continue;
            }
-           if (reply.flags & RESOLVE_FLAG_ERROR) {
-               qmgr_bounce_recipient(message, recipient,
-                                     "bad address syntax: \"%s\"",
-                                     recipient->address);
+           rewrite_clnt_internal(REWRITE_CANON, message->redirect_addr,
+                                 reply.recipient);
+           UPDATE(recipient->address, STR(reply.recipient));
+           if (qmgr_resolve_one(message, recipient,
+                                recipient->address, &reply) < 0)
                continue;
-           }
-       } else {
-           resolve_clnt_query(message->sender, &reply);
-           if (reply.flags & RESOLVE_FLAG_FAIL) {
-               qmgr_defer_recipient(message, recipient,
-                                    "address resolver failure");
-               continue;
-           }
-           if (reply.flags & RESOLVE_FLAG_ERROR) {
-               qmgr_bounce_recipient(message, recipient,
-                                     "bad address syntax: \"%s\"",
-                                     message->sender);
-               continue;
-           }
-           vstring_strcpy(reply.recipient, recipient->address);
+           if (!STREQ(recipient->address, STR(reply.recipient)))
+               UPDATE(recipient->address, STR(reply.recipient));
        }
-       if (message->filter_xport) {
+
+       /*
+        * Content filtering overrides the address resolver.
+        */
+       else if (message->filter_xport) {
            vstring_strcpy(reply.transport, message->filter_xport);
            if ((nexthop = split_at(STR(reply.transport), ':')) == 0
                || *nexthop == 0)
                nexthop = var_myhostname;
            vstring_strcpy(reply.nexthop, nexthop);
-       } else {
+           vstring_strcpy(reply.recipient, recipient->address);
+       }
+
+       /*
+        * Resolve the destination to (transport, nexthop, address). The
+        * result address may differ from the one specified by the sender.
+        */
+       else if (var_sender_routing == 0) {
+           if (qmgr_resolve_one(message, recipient,
+                                recipient->address, &reply) < 0)
+               continue;
            if (!STREQ(recipient->address, STR(reply.recipient)))
                UPDATE(recipient->address, STR(reply.recipient));
        }
+
+       /*
+        * XXX Sender-based routing does not work very well, because it has
+        * problems with sending bounces.
+        */
+       else {
+           if (qmgr_resolve_one(message, recipient,
+                                message->sender, &reply) < 0)
+               continue;
+           vstring_strcpy(reply.recipient, recipient->address);
+       }
+
+       /*
+        * Bounce null recipients. This should never happen, but is most
+        * likely the result of a fault in a different program, so aborting
+        * the queue manager process does not help.
+        */
        if (recipient->address[0] == 0) {
            qmgr_bounce_recipient(message, recipient,
                                  "null recipient address");
            continue;
        }
 
-       /*
-        * XXX The nexthop destination is also used as lookup key for the
-        * per-destination queue. Fold the nexthop to lower case so that we
-        * don't have multiple queues for the same site.
-        */
-       lowercase(STR(reply.nexthop));
-
        /*
         * Bounce recipient addresses that start with `-'. External commands
         * may misinterpret such addresses as command-line options.
@@ -596,46 +633,6 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
            continue;
        }
 
-       /*
-        * Queues are identified by the transport name and by the next-hop
-        * hostname. When the delivery agent accepts only one recipient per
-        * delivery, give each recipient its own queue, so that deliveries to
-        * different recipients of the same message can happen in parallel.
-        * This also has the benefit that one bad recipient cannot interfere
-        * with deliveries to other recipients. XXX Should split the address
-        * on the recipient delimiter if one is defined, but doing a proper
-        * job requires knowledge of local aliases. Yuck! I don't want to
-        * duplicate delivery-agent specific knowledge in the queue manager.
-        * 
-        * XXX The nexthop field is overloaded to serve as destination and as
-        * queue name. Should have separate fields for queue name and for
-        * destination, so that we don't have to make a special case for the
-        * error delivery agent (where nexthop is arbitrary text). See also:
-        * qmgr_deliver.c.
-        */
-       at = strrchr(STR(reply.recipient), '@');
-       len = (at ? (at - STR(reply.recipient)) : strlen(STR(reply.recipient)));
-
-       /*
-        * Look up or instantiate the proper transport. We're working a
-        * little ahead, doing queue management stuff that used to be done
-        * way down.
-        */
-       if (transport == 0 || !STREQ(transport->name, STR(reply.transport))) {
-           if ((transport = qmgr_transport_find(STR(reply.transport))) == 0)
-               transport = qmgr_transport_create(STR(reply.transport));
-           queue = 0;
-       }
-       if (strcmp(transport->name, MAIL_SERVICE_ERROR) != 0
-           && transport->recipient_limit == 1) {
-           VSTRING_SPACE(reply.nexthop, len + 2);
-           memmove(STR(reply.nexthop) + len + 1, STR(reply.nexthop),
-                   LEN(reply.nexthop) + 1);
-           memcpy(STR(reply.nexthop), STR(reply.recipient), len);
-           STR(reply.nexthop)[len] = '@';
-           lowercase(STR(reply.nexthop));
-       }
-
        /*
         * Discard mail to the local double bounce address here, so this
         * system can run without a local delivery agent. They'd still have
@@ -646,6 +643,9 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * be directed to a general-purpose null delivery agent.
         */
        if (reply.flags & RESOLVE_CLASS_LOCAL) {
+           at = strrchr(STR(reply.recipient), '@');
+           len = (at ? (at - STR(reply.recipient))
+                  : strlen(STR(reply.recipient)));
            if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
                            len) == 0
                && !var_double_bounce_sender[len]) {
@@ -678,16 +678,6 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
            }
        }
 
-       /*
-        * XXX Gross hack alert. We want to group recipients by transport and
-        * by next-hop hostname, in order to minimize the number of network
-        * transactions. However, it would be wasteful to have an in-memory
-        * resolver reply structure for each in-core recipient. Instead, we
-        * bind each recipient to an in-core queue instance which is needed
-        * anyway. That gives all information needed for recipient grouping.
-        */
-#if 0
-
        /*
         * Look up or instantiate the proper transport.
         */
@@ -696,7 +686,6 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
                transport = qmgr_transport_create(STR(reply.transport));
            queue = 0;
        }
-#endif
 
        /*
         * This transport is dead. Defer delivery to this recipient.
@@ -706,13 +695,50 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
            continue;
        }
 
+       /*
+        * The nexthop destination provides the default name for the
+        * per-destination queue. When the delivery agent accepts only one
+        * recipient per delivery, give each recipient its own queue, so that
+        * deliveries to different recipients of the same message can happen
+        * in parallel, and so that we can enforce per-recipient concurrency
+        * limits and prevent one recipient from tying up all the delivery
+        * agent resources. We use recipient@nexthop as queue name rather
+        * than the actual recipient domain name, so that one recipient in
+        * multiple equivalent domains cannot evade the per-recipient
+        * concurrency limit. XXX Should split the address on the recipient
+        * delimiter if one is defined, but doing a proper job requires
+        * knowledge of local aliases. Yuck! I don't want to duplicate
+        * delivery-agent specific knowledge in the queue manager.
+        * 
+        * Fold the result to lower case so that we don't have multiple queues
+        * for the same name.
+        * 
+        * Important! All recipients in a queue must have the same nexthop
+        * value. It is OK to have multiple queues with the same nexthop
+        * value, but only when those queues are named after recipients.
+        */
+       vstring_strcpy(queue_name, STR(reply.nexthop));
+       if (strcmp(transport->name, MAIL_SERVICE_ERROR) != 0
+           && transport->recipient_limit == 1) {
+           at = strrchr(STR(reply.recipient), '@');
+           len = (at ? (at - STR(reply.recipient))
+                  : strlen(STR(reply.recipient)));
+           VSTRING_SPACE(queue_name, len + 2);
+           memmove(STR(queue_name) + len + 1, STR(queue_name),
+                   LEN(queue_name) + 1);
+           memcpy(STR(queue_name), STR(reply.recipient), len);
+           STR(queue_name)[len] = '@';
+       }
+       lowercase(STR(queue_name));
+
        /*
         * This transport is alive. Find or instantiate a queue for this
         * recipient.
         */
-       if (queue == 0 || !STREQ(queue->name, STR(reply.nexthop))) {
-           if ((queue = qmgr_queue_find(transport, STR(reply.nexthop))) == 0)
-               queue = qmgr_queue_create(transport, STR(reply.nexthop));
+       if (queue == 0 || !STREQ(queue->name, STR(queue_name))) {
+           if ((queue = qmgr_queue_find(transport, STR(queue_name))) == 0)
+               queue = qmgr_queue_create(transport, STR(queue_name),
+                                         STR(reply.nexthop));
        }
 
        /*
@@ -729,6 +755,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
        recipient->queue = queue;
     }
     resolve_clnt_free(&reply);
+    vstring_free(queue_name);
 }
 
 /* qmgr_message_assign - assign recipients to specific delivery requests */
@@ -791,6 +818,8 @@ void    qmgr_message_free(QMGR_MESSAGE *message)
        myfree(message->filter_xport);
     if (message->inspect_xport)
        myfree(message->inspect_xport);
+    if (message->redirect_addr)
+       myfree(message->redirect_addr);
     qmgr_rcpt_list_free(&message->rcpt_list);
     qmgr_message_count--;
     myfree((char *) message);
index 7d4214f7069d2074d541a3f59d23fe1426b13815..3c88b7eda17636e9d6a291618cb1d852c3d420ab 100644 (file)
@@ -8,16 +8,17 @@
 /*
 /*     int     qmgr_queue_count;
 /*
-/*     QMGR_QUEUE *qmgr_queue_create(transport, site)
+/*     QMGR_QUEUE *qmgr_queue_create(transport, name, nexthop)
 /*     QMGR_TRANSPORT *transport;
-/*     const char *site;
+/*     const char *name;
+/*     const char *nexthop;
 /*
 /*     void    qmgr_queue_done(queue)
 /*     QMGR_QUEUE *queue;
 /*
-/*     QMGR_QUEUE *qmgr_queue_find(transport, site)
+/*     QMGR_QUEUE *qmgr_queue_find(transport, name)
 /*     QMGR_TRANSPORT *transport;
-/*     const char *site;
+/*     const char *name;
 /*
 /*     QMGR_QUEUE *qmgr_queue_select(transport)
 /*     QMGR_TRANSPORT *transport;
@@ -37,7 +38,7 @@
 /*     qmgr_queue_count is a global counter for the total number
 /*     of in-core queue structures.
 /*
-/*     qmgr_queue_create() creates an empty queue for the named
+/*     qmgr_queue_create() creates an empty named queue for the named
 /*     transport and destination. The queue is given an initial
 /*     concurrency limit as specified with the
 /*     \fIinitial_destination_concurrency\fR configuration parameter,
@@ -48,9 +49,8 @@
 /*     its entries have been taken care of. It is an error to dispose
 /*     of a dead queue.
 /*
-/*     qmgr_queue_find() looks up the queue for the named destination
-/*     for the named transport. A null result means that the queue
-/*     was not found.
+/*     qmgr_queue_find() looks up the named queue for the named
+/*     transport. A null result means that the queue was not found.
 /*
 /*     qmgr_queue_select() uses a round-robin strategy to select
 /*     from the named transport one per-destination queue with a
@@ -235,13 +235,15 @@ void    qmgr_queue_done(QMGR_QUEUE *queue)
     QMGR_LIST_UNLINK(transport->queue_list, QMGR_QUEUE *, queue);
     htable_delete(transport->queue_byname, queue->name, (void (*) (char *)) 0);
     myfree(queue->name);
+    myfree(queue->nexthop);
     qmgr_queue_count--;
     myfree((char *) queue);
 }
 
 /* qmgr_queue_create - create in-core queue for site */
 
-QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *site)
+QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *name,
+                                     const char *nexthop)
 {
     QMGR_QUEUE *queue;
 
@@ -252,7 +254,8 @@ QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *site)
 
     queue = (QMGR_QUEUE *) mymalloc(sizeof(QMGR_QUEUE));
     qmgr_queue_count++;
-    queue->name = mystrdup(site);
+    queue->name = mystrdup(name);
+    queue->nexthop = mystrdup(nexthop);
     queue->todo_refcount = 0;
     queue->busy_refcount = 0;
     queue->transport = transport;
@@ -262,13 +265,13 @@ QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *site)
     queue->reason = 0;
     queue->clog_time_to_warn = 0;
     QMGR_LIST_PREPEND(transport->queue_list, queue);
-    htable_enter(transport->queue_byname, site, (char *) queue);
+    htable_enter(transport->queue_byname, name, (char *) queue);
     return (queue);
 }
 
-/* qmgr_queue_find - find in-core queue for site */
+/* qmgr_queue_find - find in-core named queue */
 
-QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *transport, const char *site)
+QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *transport, const char *name)
 {
-    return ((QMGR_QUEUE *) htable_find(transport->queue_byname, site));
+    return ((QMGR_QUEUE *) htable_find(transport->queue_byname, name));
 }
index 7550d3d73df2d43f441820f59830b7f7b2c40fbc..59772ff3a973c7973047864e42e17885c7441566 100644 (file)
@@ -221,6 +221,7 @@ smtpd_check.o: ../../include/rec_type.h
 smtpd_check.o: ../../include/mail_proto.h
 smtpd_check.o: ../../include/iostuff.h
 smtpd_check.o: ../../include/attr.h
+smtpd_check.o: ../../include/mail_addr.h
 smtpd_check.o: ../../include/verify_clnt.h
 smtpd_check.o: ../../include/deliver_request.h
 smtpd_check.o: ../../include/recipient_list.h
index 49b328cf518cd233b949fd99a514bf4a862460e1..1db47f3d965b46f2cb884093eb39d01e2f0d2265 100644 (file)
@@ -1752,11 +1752,11 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
      */
     if (STREQUAL(value, "FILTER", cmd_len)) {
        if (*cmd_text == 0) {
-           msg_warn("access map %s entry %s has FILTER entry without value",
+           msg_warn("access map %s entry \"%s\" has FILTER entry without value",
                     table, datum);
            return (SMTPD_CHECK_DUNNO);
        } else if (strchr(cmd_text, ':') == 0) {
-           msg_warn("access map %s entry %s requires transport:destination",
+           msg_warn("access map %s entry \"%s\" requires transport:destination",
                     table, datum);
            return (SMTPD_CHECK_DUNNO);
        } else {
@@ -1802,6 +1802,26 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
        return (SMTPD_CHECK_OK);
     }
 
+    /*
+     * REDIRECT means deliver to designated recipient. But we may still
+     * change our mind, and reject/discard the message for other reasons.
+     */
+    if (STREQUAL(value, "REDIRECT", cmd_len)) {
+       if (strchr(cmd_text, '@') == 0) {
+           msg_warn("access map %s entry \"%s\" requires user@domain target",
+                    table, datum);
+           return (SMTPD_CHECK_DUNNO);
+       } else {
+           vstring_sprintf(error_text, "<%s>: %s triggers REDIRECT %s",
+                           reply_name, reply_class, cmd_text);
+           log_whatsup(state, "redirect", STR(error_text));
+#ifndef TEST
+           rec_fprintf(state->dest->stream, REC_TYPE_RDR, "%s", cmd_text);
+#endif
+           return (SMTPD_CHECK_DUNNO);
+       }
+    }
+
     /*
      * All-numeric result probably means OK - some out-of-band authentication
      * mechanism uses this as time stamp.
@@ -3309,13 +3329,13 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient)
 
     if ((reply->flags & RESOLVE_CLASS_LOCAL)
        && *var_local_rcpt_maps
-       /* Generated by bounce, absorbed by qmgr. */
+    /* Generated by bounce, absorbed by qmgr. */
        && !MATCH_LEFT(var_double_bounce_sender, CONST_STR(reply->recipient),
                       strlen(var_double_bounce_sender))
-       /* Absorbed by qmgr. */
+    /* Absorbed by qmgr. */
        && !MATCH_LEFT(MAIL_ADDR_POSTMASTER, CONST_STR(reply->recipient),
                       strlen(MAIL_ADDR_POSTMASTER))
-       /* Generated by bounce. */
+    /* 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)))
index bce2c74e673a3039680285d982f867d593c1da0d..4167dfff0186abcdd5724531684c95e1b8bd7a5b 100644 (file)
@@ -624,6 +624,18 @@ dict_open.o: split_at.h
 dict_open.o: htable.h
 dict_pcre.o: dict_pcre.c
 dict_pcre.o: sys_defs.h
+dict_pcre.o: mymalloc.h
+dict_pcre.o: msg.h
+dict_pcre.o: safe.h
+dict_pcre.o: vstream.h
+dict_pcre.o: vbuf.h
+dict_pcre.o: vstring.h
+dict_pcre.o: stringops.h
+dict_pcre.o: readlline.h
+dict_pcre.o: dict.h
+dict_pcre.o: argv.h
+dict_pcre.o: dict_pcre.h
+dict_pcre.o: mac_parse.h
 dict_regexp.o: dict_regexp.c
 dict_regexp.o: sys_defs.h
 dict_regexp.o: mymalloc.h