explicit process count limit. If an incorrect process limit
is specified in master.cf the service aborts.
+20021214
+
+ Cleanup: it looks like we finally get it right with transport
+ lookup table entries that either override or specify an
+ error transport without updating the nexthop information.
+ File: trivial-rewrite/resolve.c.
+
+ Performance: don't do UCE checks (which may result in 4xx
+ SMTP reply codes) when we already know that the recipient
+ is undeliverable. Files: smtpd/smtpd.c, smtpd/smtpd_check.c.
+
Open problems:
Low: after successful delivery, per-queue window += 1/window,
- You run the Postfix SMTP server chrooted (see master.cf).
- You redefined the local delivery agent in master.cf.
- You redefined the "local_transport" setting in main.cf.
-- You use the mailbox_transport or fallback_transport feature
- of the Postfix local delivery agent.
+- You use the mailbox_transport feature of the Postfix local delivery agent.
+- You use the fallback_transport feature of the Postfix local delivery agent.
+- You use the luser_relay feature of the Postfix local delivery agent.
Specify "local_recipient_maps =" (i.e. empty) to make the SMTP
-server accept mail for all known and unknown local recipients. You
-will be considered a bad network citizen, though.
+server accept mail for all known and unknown local recipients.
+That was the default setting prior to Postfix version 1.2.
Postfix no longer defaults to the "smtp" transport for all non-local
destinations. This may affect your defer_transports settings. In
#
# - You redefined the "local_transport" setting in main.cf.
#
-# - You use the mailbox_transport or fallback_transport feature
-# of the Postfix local delivery agent (see sample-local.cf).
+# - You use the "luser_relay", "mailbox_transport", or "fallback_transport"
+# feature of the Postfix local delivery agent (see sample-local.cf).
#
# Beware: if the Postfix SMTP server runs chrooted, you may have to
# copy the passwd (not shadow) database into the jail. This is
# :nexthop part is optional. For more details see the sample transport
# configuration file.
#
+# NOTE: if you use this feature for accounts not in the UNIX password
+# file, then you must update the "local_recipient_maps" setting in
+# the main.cf file, otherwise the SMTP server will reject mail for
+# non-UNIX accounts with "User unknown in local recipient table".
+#
#mailbox_transport = lmtp:unix:/file/name
#mailbox_transport = cyrus
# :nexthop part is optional. For more details see the sample transport
# configuration file.
#
+# NOTE: if you use this feature for accounts not in the UNIX password
+# file, then you must update the "local_recipient_maps" setting in
+# the main.cf file, otherwise the SMTP server will reject mail for
+# non-UNIX accounts with "User unknown in local recipient table".
+#
#fallback_transport = lmtp:unix:/file/name
#fallback_transport = cyrus
#fallback_transport =
#
# luser_relay works only for the default Postfix local delivery agent.
#
+# NOTE: if you use this feature for accounts not in the UNIX password
+# file, then you must specify "local_recipient_maps =" (i.e. empty) in
+# the main.cf file, otherwise the SMTP server will reject mail for
+# non-UNIX accounts with "User unknown in local recipient table".
+#
#luser_relay = $user@other.host
#luser_relay = $local@other.host
#luser_relay = admin+$local
#
# luser_relay works only for the default Postfix local delivery agent.
#
+# NOTE: if you use this feature for accounts not in the UNIX password
+# file, then you must specify "local_recipient_maps =" (i.e. empty) in
+# the main.cf file, otherwise the SMTP server will reject mail for
+# non-UNIX accounts with "User unknown in local recipient table".
+#
#luser_relay = $user@other.host
#luser_relay = $local@other.host
#luser_relay = admin+$local
# :nexthop part is optional. For more details see the sample transport
# configuration file.
#
-# Beware: if you use the mailbox_transport feature for users not in
-# /etc/passwd and /etc/aliases then you also need to review the
-# section "REJECTING UNKNOWN LOCAL USERS" in the main.cf file,
-# otherwise the SMTP server may reject mail incorrectly.
+# NOTE: if you use this feature for accounts not in the UNIX password
+# file, then you must update the "local_recipient_maps" setting in
+# the main.cf file, otherwise the SMTP server will reject mail for
+# non-UNIX accounts with "User unknown in local recipient table".
#
#mailbox_transport = lmtp:unix:/file/name
#mailbox_transport = cyrus
# :nexthop part is optional. For more details see the sample transport
# configuration file.
#
-# Beware: if you use the fallback_transport feature for users not in
-# /etc/passwd and /etc/aliases then you also need to review the
-# section "REJECTING UNKNOWN LOCAL USERS" in the main.cf file,
-# otherwise the SMTP server may reject mail incorrectly.
+# NOTE: if you use this feature for accounts not in the UNIX password
+# file, then you must update the "local_recipient_maps" setting in
+# the main.cf file, otherwise the SMTP server will reject mail for
+# non-UNIX accounts with "User unknown in local recipient table".
#
#fallback_transport = lmtp:unix:/file/name
#fallback_transport = cyrus
in the UNIX passwd database. This parameter over-
rides <b>luser</b><i>_</i><b>relay</b>.
+ Note: you must update the <b>local</b><i>_</i><b>recipient</b><i>_</i><b>maps</b> set-
+ ting in the <b>main.cf</b> file, otherwise the Postfix
+ SMTP server will reject mail for non-UNIX accounts
+ with "<b>User</b> <b>unknown</b> <b>in</b> <b>local</b> <b>recipient</b> <b>table</b>".
+
<b>home</b><i>_</i><b>mailbox</b>
- Pathname of a mailbox relative to a user's home
+ Pathname of a mailbox relative to a user's home
directory. Specify a path ending in <b>/</b> for maildir-
style delivery.
<b>luser</b><i>_</i><b>relay</b>
- Destination (<i>@domain</i> or <i>address</i>) for non-existent
- users. The <i>address</i> is subjected to <i>$name</i> expan-
+ Destination (<i>@domain</i> or <i>address</i>) for non-existent
+ users. The <i>address</i> is subjected to <i>$name</i> expan-
sion.
+ Note: you must specify "<b>local</b><i>_</i><b>recipient</b><i>_</i><b>maps</b> <b>=</b>"
+ (i.e. empty) in the <b>main.cf</b> file, otherwise the
+ Postfix SMTP server will reject mail for non-UNIX
+ accounts with "<b>User</b> <b>unknown</b> <b>in</b> <b>local</b> <b>recipient</b>
+ <b>table</b>".
+
<b>mail</b><i>_</i><b>spool</b><i>_</i><b>directory</b>
- Directory with UNIX-style mailboxes. The default
- pathname is system dependent. Specify a path end-
+ Directory with UNIX-style mailboxes. The default
+ pathname is system dependent. Specify a path end-
ing in <b>/</b> for maildir-style delivery.
<b>mailbox</b><i>_</i><b>command</b>
- External command to use for mailbox delivery. The
+ External command to use for mailbox delivery. The
command executes with the recipient privileges
- (exception: root). The string is subject to $name
+ (exception: root). The string is subject to $name
expansions.
<b>mailbox</b><i>_</i><b>command</b><i>_</i><b>maps</b>
- Lookup tables with per-recipient external commands
- to use for mailbox delivery. Behavior is as with
+ Lookup tables with per-recipient external commands
+ to use for mailbox delivery. Behavior is as with
<b>mailbox</b><i>_</i><b>command</b>.
<b>mailbox</b><i>_</i><b>transport</b>
- Message transport to use for mailbox delivery to
+ Message transport to use for mailbox delivery to
all local recipients, whether or not they are found
- in the UNIX passwd database. This parameter over-
- rides all other configuration parameters that con-
+ in the UNIX passwd database. This parameter over-
+ rides all other configuration parameters that con-
trol mailbox delivery, including <b>luser</b><i>_</i><b>relay</b>.
+ Note: if you use this feature to receive mail for
+ non-UNIX accounts then you must update the
+ <b>local</b><i>_</i><b>recipient</b><i>_</i><b>maps</b> setting in the <b>main.cf</b> file,
+ otherwise the Postfix SMTP server will reject mail
+ for non-UNIX accounts with "<b>User</b> <b>unknown</b> <b>in</b> <b>local</b>
+ <b>recipient</b> <b>table</b>".
+
<b>Locking</b> <b>controls</b>
<b>deliver</b><i>_</i><b>lock</b><i>_</i><b>attempts</b>
Limit the number of attempts to acquire an exclu-
<p>
+Note: if you use the <b>luser_relay</b> feature in order to receive
+mail for non-UNIX accounts, then you must specify:
+
+<blockquote>
+<pre>
+<b>local_recipient_maps =</b>
+</pre>
+</blockquote>
+
+(i.e. empty) in the <b>main.cf</b> file, otherwise the Postfix SMTP
+server will reject mail for non-UNIX accounts with "User unknown
+in local recipient table".
+
+<p>
+
<b>luser_relay</b> can specify one address. It is subjected to
<i>$name</i> expansions. The most useful examples are:
Message transport for recipients that are not found in the UNIX
passwd database.
This parameter overrides \fBluser_relay\fR.
+.sp
+Note: you must update the \fBlocal_recipient_maps\fR
+setting in the \fBmain.cf\fR file, otherwise the Postfix SMTP
+server will reject mail for non-UNIX accounts with "\fBUser
+unknown in local recipient table\fR".
.IP \fBhome_mailbox\fR
Pathname of a mailbox relative to a user's home directory.
Specify a path ending in \fB/\fR for maildir-style delivery.
.IP \fBluser_relay\fR
Destination (\fI@domain\fR or \fIaddress\fR) for non-existent users.
The \fIaddress\fR is subjected to \fI$name\fR expansion.
+.sp
+Note: you must specify "\fBlocal_recipient_maps =\fR"
+(i.e. empty) in the \fBmain.cf\fR file, otherwise the Postfix SMTP
+server will reject mail for non-UNIX accounts with "\fBUser
+unknown in local recipient table\fR".
.IP \fBmail_spool_directory\fR
Directory with UNIX-style mailboxes. The default pathname is system
dependent.
recipients, whether or not they are found in the UNIX passwd database.
This parameter overrides all other configuration parameters that
control mailbox delivery, including \fBluser_relay\fR.
+.sp
+Note: if you use this feature to receive mail for non-UNIX
+accounts then you must update the \fBlocal_recipient_maps\fR
+setting in the \fBmain.cf\fR file, otherwise the Postfix SMTP
+server will reject mail for non-UNIX accounts with "\fBUser
+unknown in local recipient table\fR".
.SH "Locking controls"
.ad
.fi
* 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 "20021213"
+#define MAIL_RELEASE_DATE "20021214"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "1.1.12-" MAIL_RELEASE_DATE
/* Message transport for recipients that are not found in the UNIX
/* passwd database.
/* This parameter overrides \fBluser_relay\fR.
+/* .sp
+/* Note: you must update the \fBlocal_recipient_maps\fR
+/* setting in the \fBmain.cf\fR file, otherwise the Postfix SMTP
+/* server will reject mail for non-UNIX accounts with "\fBUser
+/* unknown in local recipient table\fR".
/* .IP \fBhome_mailbox\fR
/* Pathname of a mailbox relative to a user's home directory.
/* Specify a path ending in \fB/\fR for maildir-style delivery.
/* .IP \fBluser_relay\fR
/* Destination (\fI@domain\fR or \fIaddress\fR) for non-existent users.
/* The \fIaddress\fR is subjected to \fI$name\fR expansion.
+/* .sp
+/* Note: you must specify "\fBlocal_recipient_maps =\fR"
+/* (i.e. empty) in the \fBmain.cf\fR file, otherwise the Postfix SMTP
+/* server will reject mail for non-UNIX accounts with "\fBUser
+/* unknown in local recipient table\fR".
/* .IP \fBmail_spool_directory\fR
/* Directory with UNIX-style mailboxes. The default pathname is system
/* dependent.
/* recipients, whether or not they are found in the UNIX passwd database.
/* This parameter overrides all other configuration parameters that
/* control mailbox delivery, including \fBluser_relay\fR.
+/* .sp
+/* Note: if you use this feature to receive mail for non-UNIX
+/* accounts then you must update the \fBlocal_recipient_maps\fR
+/* setting in the \fBmain.cf\fR file, otherwise the Postfix SMTP
+/* server will reject mail for non-UNIX accounts with "\fBUser
+/* unknown in local recipient table\fR".
/* .SH "Locking controls"
/* .ad
/* .fi
return (-1);
}
if (SMTPD_STAND_ALONE(state) == 0) {
- if ((err = smtpd_check_rcpt(state, argv[2].strval)) != 0) {
+ if ((err = smtpd_check_rcptmap(state, argv[2].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
- if ((err = smtpd_check_rcptmap(state, argv[2].strval)) != 0) {
+ if ((err = smtpd_check_rcpt(state, argv[2].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
#include "trivial-rewrite.h"
#include "transport.h"
+ /*
+ * The job of the address resolver is to map one recipient address to a
+ * triple of (channel, nexthop, recipient). The channel is the name of the
+ * delivery service specified in master.cf, the nexthop is (usually) a
+ * description of the next host to deliver to, and recipient is the final
+ * recipient address. The latter may differ from the input address as the
+ * result of stripping multiple layers of sender-specified routing.
+ *
+ * Addresses are resolved by their domain name. Known domain names are
+ * categorized into classes: local, virtual alias, virtual mailbox, relay,
+ * and everything else. Finding the address domain class is a matter of
+ * table lookups.
+ *
+ * Different address domain classes generally use different delivery channels,
+ * and may use class dependent ways to arrive at the corresponding nexthop
+ * information. With classes that do final delivery, the nexthop is
+ * typically the local machine hostname.
+ *
+ * The transport lookup table provides a means to override the domain class
+ * channel and/or nexhop information for specific recipients or for entire
+ * domain hierarchies.
+ *
+ * This works well in the general case. The only bug in this approach is that
+ * the structure of the nexthop information is transport dependent.
+ * Typically, the nexthop specifies a hostname, hostname + TCP Port, or the
+ * pathname of a UNIX-domain socket. However, with the error transport the
+ * nexthop field contains free text with the reason for non-delivery.
+ *
+ * Therefore, a transport map entry that overrides the channel but not the
+ * nexthop information (or vice versa) may produce surprising results. In
+ * particular, the free text nexthop information for the error transport is
+ * likely to confuse regular delivery agents; and conversely, a hostname or
+ * socket pathname is not a useful reason for non-delivery.
+ *
+ * In the code below, the class_domain variable specifies the domain name that
+ * we will use when (the class transport is the error transport and the
+ * class transport is replaced by a transport map lookup result) but the
+ * nexthop information is not updated.
+ *
+ * For the sake of completeness, we also take action when the reverse happens:
+ * replacing the class transport by the error transport without updating the
+ * nexthop information.
+ *
+ * The ability to specify "error:reason for non-delivery" in main.cf and in
+ * transport maps is just too convenient to take it away.
+ */
+
#define STR vstring_str
+ /*
+ * Some of the lists that define the address domain classes.
+ */
static DOMAIN_LIST *relay_domains;
static STRING_LIST *virt_alias_doms;
static STRING_LIST *virt_mailbox_doms;
+
+ /*
+ * Saved address domain class information.
+ */
+static VSTRING *saved_class_channel;
+static VSTRING *saved_class_nexthop;
+static VSTRING *saved_class_domain;
+
static MAPS *relocated_maps;
/* resolve_addr - resolve address according to rule set */
const char *blame = 0;
const char *rcpt_domain;
+ vstring_strcpy(saved_class_domain, "UNINITIALIZED SAVED_CLASS_DOMAIN");
*flags = 0;
/*
* XXX Nag if the domain is listed in multiple domain lists. The effect is
* implementation defined, and may break when internals change.
*/
-#define VIRT_ALIAS_TRANSPORT var_error_transport
-#define VIRT_ALIAS_NEXTHOP "User unknown in virtual alias table"
+#define STREQ(x,y) (strcmp((x), (y)) == 0)
dict_errno = 0;
if (domain != 0) {
tok822_internalize(nexthop, domain->next, TOK822_STR_DEFL);
- lowercase(STR(nexthop));
+ vstring_strcpy(saved_class_domain, STR(nexthop));
if (STR(nexthop)[strspn(STR(nexthop), "[]0123456789.")] != 0
&& valid_hostname(STR(nexthop), DONT_GRIPE) == 0)
*flags |= RESOLVE_FLAG_ERROR;
&& string_list_match(virt_mailbox_doms, STR(nexthop)))
msg_warn("do not list domain %s in BOTH %s and %s",
STR(nexthop), VAR_VIRT_ALIAS_DOMS, VAR_VIRT_MAILBOX_DOMS);
- vstring_strcpy(channel, VIRT_ALIAS_TRANSPORT);
- vstring_strcpy(nexthop, VIRT_ALIAS_NEXTHOP);
+ vstring_strcpy(channel, var_error_transport);
+ vstring_strcpy(nexthop, "User unknown in virtual alias table");
+ vstring_strcpy(saved_class_domain, var_myhostname);
blame = VAR_ERROR_TRANSPORT;
*flags |= RESOLVE_CLASS_ALIAS;
} else if (dict_errno != 0) {
&& string_list_match(virt_mailbox_doms, STR(nexthop))) {
vstring_strcpy(channel, var_virt_transport);
vstring_strcpy(nexthop, var_myhostname);
+ vstring_strcpy(saved_class_domain, var_myhostname);
blame = VAR_VIRT_TRANSPORT;
*flags |= RESOLVE_CLASS_VIRTUAL;
} else if (dict_errno != 0) {
}
if (*var_relayhost) {
vstring_strcpy(nexthop, var_relayhost);
- lowercase(STR(nexthop));
+ if (!STREQ(STR(channel), var_error_transport))
+ vstring_strcpy(saved_class_domain, STR(nexthop));
}
}
if ((destination = split_at(STR(channel), ':')) != 0 && *destination) {
vstring_strcpy(nexthop, destination);
- lowercase(STR(nexthop));
+ if (!STREQ(STR(channel), var_error_transport))
+ vstring_strcpy(saved_class_domain, STR(nexthop));
}
}
* implementation defined, and may break when internals change.
*/
else {
- if ((rcpt_domain = strrchr(STR(nextrcpt), '@')) != 0) {
+ if (var_helpful_warnings
+ && (rcpt_domain = strrchr(STR(nextrcpt), '@')) != 0) {
rcpt_domain++;
- if (var_helpful_warnings
- && virt_alias_doms
+ if (virt_alias_doms
&& string_list_match(virt_alias_doms, rcpt_domain))
msg_warn("do not list domain %s in BOTH %s and %s",
rcpt_domain, VAR_MYDEST, VAR_VIRT_ALIAS_DOMS);
- if (var_helpful_warnings
- && virt_mailbox_doms
+ if (virt_mailbox_doms
&& string_list_match(virt_mailbox_doms, rcpt_domain))
msg_warn("do not list domain %s in BOTH %s and %s",
rcpt_domain, VAR_MYDEST, VAR_VIRT_MAILBOX_DOMS);
|| *destination == 0)
destination = var_myhostname;
vstring_strcpy(nexthop, destination);
+ if (!STREQ(STR(channel), var_error_transport))
+ vstring_strcpy(saved_class_domain, STR(nexthop));
+ else
+ vstring_strcpy(saved_class_domain, var_myhostname);
*flags |= RESOLVE_CLASS_LOCAL;
}
/*
* The transport map overrides any transport and next-hop host info that
* is set up above.
+ *
+ * With error transports, the nexthop info is arbitrary text that must not
+ * be passed on to regular delivery agents. When the transport map
+ * overrides an error transport without overriding the nexthop
+ * information, or vice versa, update the nexthop information
+ * appropriately.
*/
if ((*flags & RESOLVE_FLAG_FAIL) == 0 && *var_transport_maps) {
- if (transport_lookup(STR(nextrcpt), channel, nexthop) == 0
- && dict_errno != 0) {
+ vstring_strcpy(saved_class_channel, STR(channel));
+ vstring_strcpy(saved_class_nexthop, STR(nexthop));
+
+ if (transport_lookup(STR(nextrcpt), channel, nexthop) != 0) {
+ if (!STREQ(STR(saved_class_channel), STR(channel))
+ && STREQ(STR(saved_class_nexthop), STR(nexthop))) {
+ vstring_strcpy(nexthop, STREQ(STR(channel), var_error_transport) ?
+ "Address is not deliverable" : STR(saved_class_domain));
+ }
+ } else if (dict_errno != 0) {
msg_warn("%s lookup failure", VAR_TRANSPORT_MAPS);
*flags |= RESOLVE_FLAG_FAIL;
}
}
- /*
- * Kludge for virtual alias domains. Their next-hop info is arbitrary
- * text that must not be passed on to regular delivery agents. So, if the
- * transport was changed, but the nexthop was not, copy over the local
- * hostname instead.
- */
-#define STREQ(x, y) (strcmp((x), (y)) == 0)
-
- if ((*flags & RESOLVE_FLAG_FAIL) == 0
- && (*flags & RESOLVE_CLASS_ALIAS) != 0
- && !STREQ(STR(channel), VIRT_ALIAS_TRANSPORT)
- && STREQ(STR(nexthop), VIRT_ALIAS_NEXTHOP))
- vstring_strcpy(nexthop, var_myhostname);
-
/*
* Bounce recipients that have moved, regardless of domain address class.
- * The downside of doing this here is that this table has no effect on
- * local alias expansion results. Such mail will have to make almost an
- * entire iteration through the mail system.
+ * We do this last, in anticipation of transport maps that can override
+ * the recipient address.
+ *
+ * The downside of not doing this in delivery agents is that this table has
+ * no effect on local alias expansion results. Such mail will have to
+ * make almost an entire iteration through the mail system.
*/
#define IGNORE_ADDR_EXTENSION ((char **) 0)
channel = vstring_alloc(100);
nexthop = vstring_alloc(100);
nextrcpt = vstring_alloc(100);
+ saved_class_channel = vstring_alloc(100);
+ saved_class_nexthop = vstring_alloc(100);
+ saved_class_domain = vstring_alloc(100);
if (*var_virt_alias_doms)
virt_alias_doms =
msg_panic("find_transport_entry: missing initialization");
/*
- * Look up an entry with extreme prejedice.
+ * Look up an entry with extreme prejudice.
*
* XXX Should report lookup failure status to caller instead of aborting.
*/
}
/*
- * Can't do transport:user@domain because the right-hand side can have
- * arbitrary content (especially in the case of the error mailer).
+ * It would be great if we could specify a recipient address in the
+ * lookup result. Unfortunately, we cannot simply run the result through
+ * a parser that recognizes "transport:user@domain" because the lookup
+ * result can have arbitrary content (especially in the case of the error
+ * mailer).
*/
else {
saved_value = mystrdup(value);