diff --git a/postfix/HISTORY b/postfix/HISTORY
index 67ed9c452..af0d3efe2 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -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
diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES
index e4e8cf33b..342194db4 100644
--- a/postfix/RELEASE_NOTES
+++ b/postfix/RELEASE_NOTES
@@ -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
=========================================================
diff --git a/postfix/conf/access b/postfix/conf/access
index 0b4a8ad63..1f4b38ba7 100644
--- a/postfix/conf/access
+++ b/postfix/conf/access
@@ -164,8 +164,17 @@
# 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,
diff --git a/postfix/conf/sample-filter.cf b/postfix/conf/sample-filter.cf
index 3ab0d02e4..115fdcb7f 100644
--- a/postfix/conf/sample-filter.cf
+++ b/postfix/conf/sample-filter.cf
@@ -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.
#
diff --git a/postfix/conf/sample-pcre-body.cf b/postfix/conf/sample-pcre-body.cf
index 14e151225..0ece30ca0 100644
--- a/postfix/conf/sample-pcre-body.cf
+++ b/postfix/conf/sample-pcre-body.cf
@@ -51,7 +51,12 @@
# 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
diff --git a/postfix/conf/sample-pcre-header.cf b/postfix/conf/sample-pcre-header.cf
index 9dc72813a..58fde1226 100644
--- a/postfix/conf/sample-pcre-header.cf
+++ b/postfix/conf/sample-pcre-header.cf
@@ -52,7 +52,12 @@
# 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
diff --git a/postfix/conf/sample-regexp-body.cf b/postfix/conf/sample-regexp-body.cf
index e86c306fb..89357f397 100644
--- a/postfix/conf/sample-regexp-body.cf
+++ b/postfix/conf/sample-regexp-body.cf
@@ -43,7 +43,11 @@
# 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.
diff --git a/postfix/conf/sample-regexp-header.cf b/postfix/conf/sample-regexp-header.cf
index bc29fdf6d..10e529009 100644
--- a/postfix/conf/sample-regexp-header.cf
+++ b/postfix/conf/sample-regexp-header.cf
@@ -43,7 +43,11 @@
# 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
diff --git a/postfix/conf/sample-smtpd.cf b/postfix/conf/sample-smtpd.cf
index 73c3cd797..b6a6643d2 100644
--- a/postfix/conf/sample-smtpd.cf
+++ b/postfix/conf/sample-smtpd.cf
@@ -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.
diff --git a/postfix/html/access.5.html b/postfix/html/access.5.html
index 5158b390c..b841dfeab 100644
--- a/postfix/html/access.5.html
+++ b/postfix/html/access.5.html
@@ -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 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,
diff --git a/postfix/html/uce.html b/postfix/html/uce.html
index eee2d501d..9ca51eb41 100644
--- a/postfix/html/uce.html
+++ b/postfix/html/uce.html
@@ -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 content_filter setting.
+
+After the message is queued, send the message to the
+specified address instead of the intended recipients.
+overrides the FILTER action.
+
diff --git a/postfix/man/man5/access.5 b/postfix/man/man5/access.5
index 12528f659..fcdca96b4 100644
--- a/postfix/man/man5/access.5
+++ b/postfix/man/man5/access.5
@@ -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).
diff --git a/postfix/proto/access b/postfix/proto/access
index eef4a4bf1..f1bbc99f1 100644
--- a/postfix/proto/access
+++ b/postfix/proto/access
@@ -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
@@ -100,8 +100,8 @@
# 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.
@@ -136,7 +136,14 @@
# 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).
diff --git a/postfix/src/cleanup/cleanup.h b/postfix/src/cleanup/cleanup.h
index 8a4083d3c..88636ef12 100644
--- a/postfix/src/cleanup/cleanup.h
+++ b/postfix/src/cleanup/cleanup.h
@@ -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;
/*
diff --git a/postfix/src/cleanup/cleanup_extracted.c b/postfix/src/cleanup/cleanup_extracted.c
index 19131642d..50b3d12ed 100644
--- a/postfix/src/cleanup/cleanup_extracted.c
+++ b/postfix/src/cleanup/cleanup_extracted.c
@@ -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
diff --git a/postfix/src/cleanup/cleanup_message.c b/postfix/src/cleanup/cleanup_message.c
index b7283fde0..28ac89b10 100644
--- a/postfix/src/cleanup/cleanup_message.c
+++ b/postfix/src/cleanup/cleanup_message.c
@@ -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);
diff --git a/postfix/src/cleanup/cleanup_state.c b/postfix/src/cleanup/cleanup_state.c
index 522db49c2..2f17fd050 100644
--- a/postfix/src/cleanup/cleanup_state.c
+++ b/postfix/src/cleanup/cleanup_state.c
@@ -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);
}
diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in
index c88e30bdf..267ee712b 100644
--- a/postfix/src/global/Makefile.in
+++ b/postfix/src/global/Makefile.in
@@ -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
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 9eb5d9876..718590173 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -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
diff --git a/postfix/src/global/rec_type.h b/postfix/src/global/rec_type.h
index a14656915..0e54d7ee9 100644
--- a/postfix/src/global/rec_type.h
+++ b/postfix/src/global/rec_type.h
@@ -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
diff --git a/postfix/src/nqmgr/Makefile.in b/postfix/src/nqmgr/Makefile.in
index 1b1b139b8..c9a01a6f3 100644
--- a/postfix/src/nqmgr/Makefile.in
+++ b/postfix/src/nqmgr/Makefile.in
@@ -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
diff --git a/postfix/src/nqmgr/qmgr.h b/postfix/src/nqmgr/qmgr.h
index 54d91e588..65037f657 100644
--- a/postfix/src/nqmgr/qmgr.h
+++ b/postfix/src/nqmgr/qmgr.h
@@ -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 */
diff --git a/postfix/src/nqmgr/qmgr_deliver.c b/postfix/src/nqmgr/qmgr_deliver.c
index c26efff61..3f7e78bb5 100644
--- a/postfix/src/nqmgr/qmgr_deliver.c
+++ b/postfix/src/nqmgr/qmgr_deliver.c
@@ -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,
diff --git a/postfix/src/nqmgr/qmgr_entry.c b/postfix/src/nqmgr/qmgr_entry.c
index 5032a4556..c26d29325 100644
--- a/postfix/src/nqmgr/qmgr_entry.c
+++ b/postfix/src/nqmgr/qmgr_entry.c
@@ -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);
diff --git a/postfix/src/nqmgr/qmgr_message.c b/postfix/src/nqmgr/qmgr_message.c
index 60d051c9f..3be89bfcf 100644
--- a/postfix/src/nqmgr/qmgr_message.c
+++ b/postfix/src/nqmgr/qmgr_message.c
@@ -124,6 +124,7 @@
/* Client stubs. */
+#include
#include
/* 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);
diff --git a/postfix/src/nqmgr/qmgr_queue.c b/postfix/src/nqmgr/qmgr_queue.c
index 9854011b6..b86034a57 100644
--- a/postfix/src/nqmgr/qmgr_queue.c
+++ b/postfix/src/nqmgr/qmgr_queue.c
@@ -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));
}
diff --git a/postfix/src/proxymap/Makefile.in b/postfix/src/proxymap/Makefile.in
index 5ee7d59d8..91d20d838 100644
--- a/postfix/src/proxymap/Makefile.in
+++ b/postfix/src/proxymap/Makefile.in
@@ -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
diff --git a/postfix/src/proxymap/proxymap.c b/postfix/src/proxymap/proxymap.c
index e81e5e852..0caed2983 100644
--- a/postfix/src/proxymap/proxymap.c
+++ b/postfix/src/proxymap/proxymap.c
@@ -149,14 +149,14 @@
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,
diff --git a/postfix/src/qmgr/Makefile.in b/postfix/src/qmgr/Makefile.in
index e128810a7..f4a1419c8 100644
--- a/postfix/src/qmgr/Makefile.in
+++ b/postfix/src/qmgr/Makefile.in
@@ -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
diff --git a/postfix/src/qmgr/qmgr.h b/postfix/src/qmgr/qmgr.h
index e553e1793..67a1adfce 100644
--- a/postfix/src/qmgr/qmgr.h
+++ b/postfix/src/qmgr/qmgr.h
@@ -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 */
diff --git a/postfix/src/qmgr/qmgr_deliver.c b/postfix/src/qmgr/qmgr_deliver.c
index dbdcfd75d..c1e6ab749 100644
--- a/postfix/src/qmgr/qmgr_deliver.c
+++ b/postfix/src/qmgr/qmgr_deliver.c
@@ -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,
diff --git a/postfix/src/qmgr/qmgr_entry.c b/postfix/src/qmgr/qmgr_entry.c
index 57de8ebd6..3d3f9f461 100644
--- a/postfix/src/qmgr/qmgr_entry.c
+++ b/postfix/src/qmgr/qmgr_entry.c
@@ -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);
diff --git a/postfix/src/qmgr/qmgr_message.c b/postfix/src/qmgr/qmgr_message.c
index 15919b3fa..f3bc58e49 100644
--- a/postfix/src/qmgr/qmgr_message.c
+++ b/postfix/src/qmgr/qmgr_message.c
@@ -115,6 +115,7 @@
/* Client stubs. */
+#include
#include
/* 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);
diff --git a/postfix/src/qmgr/qmgr_queue.c b/postfix/src/qmgr/qmgr_queue.c
index 7d4214f70..3c88b7eda 100644
--- a/postfix/src/qmgr/qmgr_queue.c
+++ b/postfix/src/qmgr/qmgr_queue.c
@@ -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));
}
diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in
index 7550d3d73..59772ff3a 100644
--- a/postfix/src/smtpd/Makefile.in
+++ b/postfix/src/smtpd/Makefile.in
@@ -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
diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c
index 49b328cf5..1db47f3d9 100644
--- a/postfix/src/smtpd/smtpd_check.c
+++ b/postfix/src/smtpd/smtpd_check.c
@@ -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)))
diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in
index bce2c74e6..4167dfff0 100644
--- a/postfix/src/util/Makefile.in
+++ b/postfix/src/util/Makefile.in
@@ -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