2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 13:48:06 +00:00

postfix-2.0.3-20030125

This commit is contained in:
Wietse Venema
2003-01-25 00:00:00 -05:00
committed by Viktor Dukhovni
parent 0bf8270bb1
commit 17e07be3f4
37 changed files with 498 additions and 280 deletions

View 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

View 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
=========================================================

View File

@@ -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,

View 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.
#

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View 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.

View 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>,

View 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>

View 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).

View 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
@@ -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).

View 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;
/*

View 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

View 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);

View 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);
}

View 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

View 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

View 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

View 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

View 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 */

View 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,

View 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);

View File

@@ -124,6 +124,7 @@
/* 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);

View 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));
}

View 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

View File

@@ -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,

View 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

View 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 */

View 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,

View 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);

View File

@@ -115,6 +115,7 @@
/* 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);

View 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));
}

View 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

View 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)))

View 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