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.
+ SMTP reply codes, and thus, repeated delivery attempts)
+ when we already know that the recipient does not exist.
+ Files: smtpd/smtpd.c, smtpd/smtpd_check.c.
+
+20021215
+
+ Cleanup: further simplification of transport map handling
+ after some really fine hair splitting with Victor Duchovni.
+ Files: trivial-rewrite/resolve.c, trivial-rewrite/transport.c.
+
+20021216
+
+ Workaround: transform the address local-part into unquoted
+ form only when the address domain is local and the local-part
+ contains routing operators. Otherwise, we may damage the
+ address local-part by inserting space between non-operator
+ tokens. Some people use weird addresses and expect them to
+ be handled without damage. File: trivial-rewrite/resolve.c.
+
+ Robustness: scan the resolved recipient address for routing
+ operators in the address local-part, even when the local
+ MTA does not recognize ! and % as valid operators. File:
+ trivial-rewrite/resolve.c.
+
+ Cleanup: the address rewriting code no longer tries to
+ rewrite broken user@ or user@. address forms into even more
+ broken forms. bother. File: trivial-rewrite/rewrite.c.
+
+ Cleanup: the address resolver code now treates forms ending
+ in @ in a more rational manner (because the address rewriting
+ code no longer messes up by appending .my.domain).
+
+ Bugfix: a null address local-part before @domain now is
+ properly quoted just like the null address. File:
+ global/quote_82[12]_local.c.
+
+
+20021217
+
+ Cleanup: more work on the trivial-rewrite address rewriting
+ and address resolving code. New regression tests for address
+ rewriting and resolving that make some assumptions about
+ main.cf settings. Files: global/Makefile.in (assumptions),
+ global/rewrite_clnt.in, global/rewrite_clnt.ref,
+ global/resolve_clnt.in, global/resolve_clnt.ref.
+
+ Safety: configurable SMTPD reject codes for recipients not
+ in {local,relay}_recipient,virtual_{alias,mailbox}}_maps,
+ aptly named unknown_mumble_reject_code. Postfix installs
+ with unknown_local_recipient_reject_code=450, unless the
+ site already ran Postfix with local_recipient_maps enabled.
+ Files: smtpd/smtpd.c, smtpd/smtpd_check.c, conf/post-install.
Open problems:
# machine considers itself the final destination for.
#
# These domains are routed to the delivery agent specified with the
-# local_transport parameter setting.
+# local_transport parameter setting. By default, that is the UNIX
+# compatible delivery agent that lookups all recipients in /etc/passwd
+# and /etc/aliases or their equivalent.
#
# The default is $myhostname + localhost.$mydomain. On a mail domain
# gateway, you should also include $mydomain.
# a name matches a lookup key (the right-hand side is ignored).
# Continue long lines by starting the next line with whitespace.
#
+# See also below, section "REJECTING MAIL FOR UNKNOWN LOCAL USERS".
+#
#mydestination = $myhostname, localhost.$mydomain
#mydestination = $myhostname, localhost.$mydomain $mydomain
#mydestination = $myhostname, localhost.$mydomain, $mydomain,
# mail.$mydomain, www.$mydomain, ftp.$mydomain
-# REJECTING UNKNOWN LOCAL USERS
+# REJECTING MAIL FOR UNKNOWN LOCAL USERS
#
# The local_recipient_maps parameter specifies optional lookup tables
# with all names or addresses of users that are local with respect
# If this parameter is defined, then the SMTP server will reject
# mail for unknown local users. This parameter is defined by default.
#
+# To turn off local recipient checking in the SMTP server, specify
+# local_recipient_maps = (i.e. empty).
+#
# The default setting assumes that you use the default Postfix local
# delivery agent for local delivery. You need to update the
# local_recipient_maps setting if:
# - 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
-# system dependent.
+# Beware: if the Postfix SMTP server runs chrooted, you probably have
+# to copy the passwd (not shadow) database into the jail, and perhaps
+# other files. This is system dependent.
#
-local_recipient_maps = unix:passwd.byname $alias_maps
+#local_recipient_maps = unix:passwd.byname $alias_maps
+#local_recipient_maps =
+
+# The unknown_local_recipient_reject_code specifies the SMTP server
+# response code when a recipient domain matches $mydestination or
+# $inet_interfaces, while $local_recipient_maps is non-empty and the
+# recipient address or address local-part is not found.
+#
+# The default setting is 550 (reject mail) but it is safer to start
+# with 450 (try again later) until you are certain that your
+# local_recipient_maps settings are OK.
+#
+#unknown_local_recipient_reject_code = 550
+unknown_local_recipient_reject_code = 450
# TRUST AND RELAY CONTROL
# with all addresses in the domains that match $relay_domains.
#
# If this parameter is defined, then the SMTP server will reject
-# mail for unknown relay users.
+# mail for unknown relay users. This feature is off by default.
#
#relay_recipient_maps = hash:/etc/postfix/relay_recipients
}
done
- # With 10000 active queue files, the active queue directory should
+ # With 20000 active queue files, the active queue directory should
# be hashed, and so should the other directories, because they
# can contain even more mail.
#
$POSTCONF -c $config_directory -e hash_queue_names="$found$missing" ||
exit 1
}
+
+ # Turn on safety nets for new features that could bounce mail that
+ # would be accepted by a previous Postfix version.
+
+ unknown_local=unknown_local_recipient_reject_code
+ has_lrm=`$POSTCONF -n local_recipient_maps`
+ has_lrjc=`$POSTCONF -n $unknown_local`
+
+ if [ -z "$has_lrm" -a -z "$has_lrjc" ]
+ then
+ echo SAFETY: editing main.cf, setting $unknown_local=450.
+ echo See the RELEASE_NOTES and $config_directory/main.cf for details.
+ $POSTCONF -e "$unknown_local = 450" || exit 1
+ fi
+
}
# A reminder if this is the first time Postfix is being installed.
# machine considers itself the final destination for.
#
# These domains are routed to the delivery agent specified with the
-# local_transport parameter setting.
+# local_transport parameter setting. By default, that is the UNIX
+# compatible delivery agent that lookups all recipients in /etc/passwd
+# and /etc/aliases or their equivalent.
#
# The default is $myhostname + localhost.$mydomain. On a mail domain
# gateway, you should also include $mydomain.
# a name matches a lookup key. Continue long lines by starting the
# next line with whitespace.
#
+# See sample-local.cf for a description of the local_recipient_maps
+# and unknown_local_recipient_reject_code parameters. By default,
+# the SMTP server rejects mail for recipients not listed with the
+# local_recipient_maps parameter.
+#
#mydestination = $myhostname, localhost.$mydomain $mydomain
#mydestination = $myhostname, localhost.$mydomain www.$mydomain, ftp.$mydomain
mydestination = $myhostname, localhost.$mydomain
#
relay_domains = $mydestination
+# The relay_recipient_maps parameter specifies optional lookup tables
+# with all addresses in the domains that match $relay_domains.
+#
+# If this parameter is defined, then the SMTP server will reject
+# mail to unknown relay users. This feature is off by default.
+#
+#relay_recipient_maps = hash:/etc/postfix/relay_recipients
+
#
# RESPONSE CODES
#
# Alternatively, the table can be provided as a regular-
# expression map where patterns are given as regular expres-
# sions. In that case, the lookups are done in a slightly
-# different way as described in the section titled "REGULAR
-# EXPRESSION TABLES".
+# different way as described in section "REGULAR EXPRESSION
+# TABLES".
#
# TABLE FORMAT
# The format of the transport table is as follows:
#
# pattern result
-# When pattern matches the domain, use the corre-
-# sponding result.
+# When pattern matches the recipient address or
+# domain, use the corresponding result.
#
# blank lines and comments
# Empty lines and whitespace-only lines are ignored,
# line that starts with whitespace continues a logi-
# cal line.
#
-# In an indexed file, a pattern of `*' matches everything.
+# The pattern specifies an email address, a domain name, or
+# a domain name hierarchy, as described in section "TABLE
+# LOOKUP".
#
# The result is of the form transport:nexthop. The trans-
# port field specifies a mail delivery transport such as
# smtp or local. The nexthop field specifies where and how
-# to deliver mail. A null transport or nexthop field means
-# "do not change": use the delivery transport and nexthop
-# information that would be used if no match were found.
+# to deliver mail. More details are given in section "RESULT
+# FORMAT".
#
-# TRANSPORT FIELD
-# The transport field specifies the name of a mail delivery
-# transport (the first name of a mail delivery service entry
-# in the Postfix master.cf file).
-#
-# When a null transport field is specified, Postfix uses one
-# of the following transports:
-#
-# $local_transport
-# The domain matches $mydestination or $inet_inter-
-# faces.
-#
-# $virtual_transport
-# The domain matches $virtual_mailbox_domains.
-#
-# $relay_transport
-# The domain matches $relay_transport.
-#
-# $default_transport
-# All other non-local, non-virtual destinations.
-#
-# NEXTHOP FIELD
-# The interpretation of the nexthop field is transport
-# dependent. In the case of SMTP, specify host:service for a
-# non-default server port, and use [host] or [host]:port in
-# order to disable MX (mail exchanger) DNS lookups. The []
-# form can also be used with IP addresses instead of host-
-# names.
-#
-# LOOKUP ORDER
+# TABLE LOOKUP
# With lookups from indexed files such as DB or DBM, or from
# networked tables such as NIS, LDAP or SQL, patterns are
# tried in the order as listed below:
# ting. Otherwise, a domain name matches itself and
# its subdomains.
#
-# NOTE
-# The special pattern <> represents the null address, and
-# the special pattern * represents any address (i.e. it
-# functions as the wild-card pattern).
+# Note 1: the special pattern * represents any address (i.e.
+# it functions as the wild-card pattern).
+#
+# Note 2: the null recipient address is looked up as the
+# local mailer-daemon address (mailer-daemon@fully-quali-
+# fied-domain-name).
+#
+# RESULT FORMAT
+# A null transport and null nexthop result means "do not
+# change": use the delivery transport and nexthop informa-
+# tion that would be used when the entire transport table
+# did not exist.
+#
+# A non-null transport field with a null nexthop field
+# resets the nexthop information to the recipient domain.
+#
+# A null transport field with non-null nexthop field does
+# not modify the transport information.
+#
+# TRANSPORT FIELD
+# The transport field specifies the name of a mail delivery
+# transport (the first name of a mail delivery service entry
+# in the Postfix master.cf file).
+#
+# When a null transport field is specified, Postfix uses one
+# of the following transports:
+#
+# $local_transport
+# The domain matches $mydestination or $inet_inter-
+# faces.
+#
+# $virtual_transport
+# The domain matches $virtual_mailbox_domains.
+#
+# $relay_transport
+# The domain matches $relay_transport.
+#
+# $default_transport
+# All other non-local, non-virtual destinations.
+#
+# NEXTHOP FIELD
+# The interpretation of the nexthop field is transport
+# dependent. In the case of SMTP, specify host:service for a
+# non-default server port, and use [host] or [host]:port in
+# order to disable MX (mail exchanger) DNS lookups. The []
+# form can also be used with IP addresses instead of host-
+# names.
#
# EXAMPLES
-# In order to deliver internal mail directly, while using a
-# mail relay for all other mail, specify a null entry for
-# internal destinations (do not change the delivery trans-
-# port or the nexthop information) and specify a wildcard
-# for all other destinations. Note that for this trick to
-# work you should not specify a relayhost in the main.cf
+# In order to deliver internal mail directly, while using a
+# mail relay for all other mail, specify a null entry for
+# internal destinations (do not change the delivery trans-
+# port or the nexthop information) and specify a wildcard
+# for all other destinations. Note that for this trick to
+# work you should not specify a relayhost in the main.cf
# file.
#
# my.domain :
# .my.domain :
# * smtp:outbound-relay.my.domain
#
-# In order to send mail for foo.org and its subdomains via
+# In order to send mail for foo.org and its subdomains via
# the uucp transport to the UUCP host named foo:
#
# foo.org uucp:foo
# .foo.org uucp:foo
#
-# When no nexthop host name is specified, the destination
-# domain name is used instead. For example, the following
-# directs mail for user@foo.org via the slow transport to a
-# mail exchanger for foo.org. The slow transport could be
-# something that runs at most one delivery process at a
+# When no nexthop host name is specified, the destination
+# domain name is used instead. For example, the following
+# directs mail for user@foo.org via the slow transport to a
+# mail exchanger for foo.org. The slow transport could be
+# something that runs at most one delivery process at a
# time:
#
# foo.org slow:
#
# When no transport is specified, Postfix uses the transport
# that matches the address domain class (see TRANSPORT FIELD
-# discussion above). The following sends all mail for
+# discussion above). The following sends all mail for
# foo.org and its subdomains to host gateway.foo.org:
#
# foo.org :[gateway.foo.org]
# .foo.org :[gateway.foo.org]
#
-# In the above example, the [] are used to suppress MX
-# lookups. The result would likely point to your local
+# In the above example, the [] are used to suppress MX
+# lookups. The result would likely point to your local
# machine.
#
-# In the case of delivery via SMTP, one may specify host-
+# In the case of delivery via SMTP, one may specify host-
# name:service instead of just a host:
#
# foo.org smtp:bar.org:2025
#
-# This directs mail for user@foo.org to host bar.org port
-# 2025. Instead of a numerical port a symbolic name may be
-# used. Specify [] around the hostname in order to disable
+# This directs mail for user@foo.org to host bar.org port
+# 2025. Instead of a numerical port a symbolic name may be
+# used. Specify [] around the hostname in order to disable
# MX lookups.
#
# The error mailer can be used to bounce mail:
#
-# .foo.org error:mail for *.foo.org is not deliv-
+# .foo.org error:mail for *.foo.org is not deliv-
# erable
#
-# This causes all mail for user@anything.foo.org to be
+# This causes all mail for user@anything.foo.org to be
# bounced.
#
# REGULAR EXPRESSION TABLES
-# This section describes how the table lookups change when
+# This section describes how the table lookups change when
# the table is given in the form of regular expressions. For
-# a description of regular expression lookup table syntax,
+# a description of regular expression lookup table syntax,
# see regexp_table(5) or pcre_table(5).
#
-# Each pattern is a regular expression that is applied to
+# Each pattern is a regular expression that is applied to
# the entire domain being looked up. Thus, some.domain.hier-
# archy is not broken up into parent domains.
#
-# Patterns are applied in the order as specified in the
-# table, until a pattern is found that matches the search
+# Patterns are applied in the order as specified in the
+# table, until a pattern is found that matches the search
# string.
#
-# Results are the same as with indexed file lookups, with
-# the additional feature that parenthesized substrings from
+# Results are the same as with indexed file lookups, with
+# the additional feature that parenthesized substrings from
# the pattern can be interpolated as $1, $2 and so on.
#
# CONFIGURATION PARAMETERS
-# The following main.cf parameters are especially relevant
-# to this topic. See the Postfix main.cf file for syntax
-# details and for default values. Use the postfix reload
+# The following main.cf parameters are especially relevant
+# to this topic. See the Postfix main.cf file for syntax
+# details and for default values. Use the postfix reload
# command after a configuration change.
#
# parent_domain_matches_subdomains
-# List of Postfix features that use domain.tld pat-
-# terns to match sub.domain.tld (as opposed to
+# List of Postfix features that use domain.tld pat-
+# terns to match sub.domain.tld (as opposed to
# requiring .domain.tld patterns).
#
# transport_maps
#
# local_transport
# The default mail delivery transport when the desti-
-# nation matches $mydestination or $inet_interfaces.
+# nation matches $mydestination or $inet_interfaces.
#
# virtual_transport
# The default mail delivery transport when the desti-
#
# default_transport
# The default mail delivery transport when the desti-
-# nation does not match a local, virtual or relay
+# nation does not match a local, virtual or relay
# destination.
#
# mydestination
# regexp_table(5) format of POSIX regular expression tables
#
# LICENSE
-# The Secure Mailer license must be distributed with this
+# The Secure Mailer license must be distributed with this
# software.
#
# AUTHOR(S)
<b>hopcount</b><i>_</i><b>limit</b>
Limit the number of <b>Received:</b> message headers.
- <b>local</b><i>_</i><b>recipient</b><i>_</i><b>maps</b>
- List of maps with user names that are local to
- <b>$myorigin</b> or <b>$inet</b><i>_</i><b>interfaces</b>. If this parameter is
- defined, then the SMTP server rejects mail for
- unknown local users.
-
- <b>relay</b><i>_</i><b>recipient</b><i>_</i><b>maps</b>
- List of maps that define all the email addresses in
- the domains that match <b>$relay</b><i>_</i><b>domains</b>. If this
- parameter is defined, then the SMTP server rejects
- mail for unknown relay recipients.
-
<b>notify</b><i>_</i><b>classes</b>
List of error classes. Of special interest are:
The characters that Postfix accepts as VERP delim-
iter characters.
+<b>Known</b> <b>versus</b> <b>unknown</b> <b>recipients</b>
+ <b>unknown</b><i>_</i><b>local</b><i>_</i><b>recipient</b><i>_</i><b>reject</b><i>_</i><b>code</b>
+ The response code when a client specifies a recipi-
+ ent whose domain matches <b>$mydestination</b> or
+ <b>$inet</b><i>_</i><b>interfaces</b>, while <b>$local</b><i>_</i><b>recipient</b><i>_</i><b>maps</b> is
+ non-empty and does not list the recipient address
+ or address local-part.
+
+ <b>unknown</b><i>_</i><b>relay</b><i>_</i><b>recipient</b><i>_</i><b>reject</b><i>_</i><b>code</b>
+ The response code when a client specifies a recipi-
+ ent whose domain matches <b>$relay</b><i>_</i><b>domains</b>, while
+ <b>$relay</b><i>_</i><b>recipient</b><i>_</i><b>maps</b> is non-empty and does not
+ list the recipient address.
+
+ <b>unknown</b><i>_</i><b>virtual</b><i>_</i><b>alias</b><i>_</i><b>reject</b><i>_</i><b>code</b>
+ The response code when a client specifies a recipi-
+ ent whose domain matches <b>$virtual</b><i>_</i><b>alias</b><i>_</i><b>domains</b>,
+ while the recipient is not listed in <b>$vir-</b>
+ <b>tual</b><i>_</i><b>alias</b><i>_</i><b>maps</b>.
+
+ <b>unknown</b><i>_</i><b>virtual</b><i>_</i><b>mailbox</b><i>_</i><b>reject</b><i>_</i><b>code</b>
+ The response code when a client specifies a recipi-
+ ent whose domain matches <b>$virtual</b><i>_</i><b>mailbox</b><i>_</i><b>domains</b>,
+ while the recipient is not listed in <b>$virtual</b><i>_</i><b>mail-</b>
+ <b>box</b><i>_</i><b>maps</b>.
+
<b>Resource</b> <b>controls</b>
<b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>
- Limit the amount of memory in bytes used for the
+ Limit the amount of memory in bytes used for the
handling of partial input lines.
<b>message</b><i>_</i><b>size</b><i>_</i><b>limit</b>
ing on-disk storage for envelope information.
<b>queue</b><i>_</i><b>minfree</b>
- Minimal amount of free space in bytes in the queue
- file system for the SMTP server to accept any mail
+ Minimal amount of free space in bytes in the queue
+ file system for the SMTP server to accept any mail
at all.
<b>smtpd</b><i>_</i><b>history</b><i>_</i><b>flush</b><i>_</i><b>threshold</b>
<b>smtpd</b><i>_</i><b>soft</b><i>_</i><b>error</b><i>_</i><b>limit</b>
When an SMTP client has made this number of errors,
- wait <i>error_count</i> seconds before responding to any
+ wait <i>error_count</i> seconds before responding to any
client request.
<b>smtpd</b><i>_</i><b>hard</b><i>_</i><b>error</b><i>_</i><b>limit</b>
- Disconnect after a client has made this number of
+ Disconnect after a client has made this number of
errors.
<b>smtpd</b><i>_</i><b>junk</b><i>_</i><b>command</b><i>_</i><b>limit</b>
Limit the number of times a client can issue a junk
- command such as NOOP, VRFY, ETRN or RSET in one
- SMTP session before it is penalized with tarpit
+ command such as NOOP, VRFY, ETRN or RSET in one
+ SMTP session before it is penalized with tarpit
delays.
<b>UCE</b> <b>control</b> <b>restrictions</b>
<b>parent</b><i>_</i><b>domain</b><i>_</i><b>matches</b><i>_</i><b>subdomains</b>
- List of Postfix features that use <i>domain.tld</i> pat-
- terns to match <i>sub.domain.tld</i> (as opposed to
+ List of Postfix features that use <i>domain.tld</i> pat-
+ terns to match <i>sub.domain.tld</i> (as opposed to
requiring <i>.domain.tld</i> patterns).
<b>smtpd</b><i>_</i><b>client</b><i>_</i><b>restrictions</b>
tem.
<b>smtpd</b><i>_</i><b>helo</b><i>_</i><b>required</b>
- Require that clients introduce themselves at the
+ Require that clients introduce themselves at the
beginning of an SMTP session.
<b>smtpd</b><i>_</i><b>helo</b><i>_</i><b>restrictions</b>
- Restrict what client hostnames are allowed in <b>HELO</b>
+ Restrict what client hostnames are allowed in <b>HELO</b>
and <b>EHLO</b> commands.
<b>smtpd</b><i>_</i><b>sender</b><i>_</i><b>restrictions</b>
- Restrict what sender addresses are allowed in <b>MAIL</b>
+ Restrict what sender addresses are allowed in <b>MAIL</b>
<b>FROM</b> commands.
<b>smtpd</b><i>_</i><b>recipient</b><i>_</i><b>restrictions</b>
- Restrict what recipient addresses are allowed in
+ Restrict what recipient addresses are allowed in
<b>RCPT</b> <b>TO</b> commands.
<b>smtpd</b><i>_</i><b>etrn</b><i>_</i><b>restrictions</b>
mands, and what clients may issue <b>ETRN</b> commands.
<b>smtpd</b><i>_</i><b>data</b><i>_</i><b>restrictions</b>
- Restrictions on the <b>DATA</b> command. Currently, the
- only restriction that makes sense here is
+ Restrictions on the <b>DATA</b> command. Currently, the
+ only restriction that makes sense here is
<b>reject</b><i>_</i><b>unauth</b><i>_</i><b>pipelining</b>.
<b>allow</b><i>_</i><b>untrusted</b><i>_</i><b>routing</b>
- Allow untrusted clients to specify addresses with
- sender-specified routing. Enabling this opens up
- nasty relay loopholes involving trusted backup MX
+ Allow untrusted clients to specify addresses with
+ sender-specified routing. Enabling this opens up
+ nasty relay loopholes involving trusted backup MX
hosts.
<b>smtpd</b><i>_</i><b>restriction</b><i>_</i><b>classes</b>
- Declares the name of zero or more parameters that
- contain a list of UCE restrictions. The names of
- these parameters can then be used instead of the
+ Declares the name of zero or more parameters that
+ contain a list of UCE restrictions. The names of
+ these parameters can then be used instead of the
restriction lists that they represent.
<b>smtpd</b><i>_</i><b>null</b><i>_</i><b>access</b><i>_</i><b>lookup</b><i>_</i><b>key</b>
- The lookup key to be used in SMTPD access tables
- instead of the null sender address. A null sender
+ The lookup key to be used in SMTPD access tables
+ instead of the null sender address. A null sender
address cannot be looked up.
<b>maps</b><i>_</i><b>rbl</b><i>_</i><b>domains</b> (deprecated)
- List of DNS domains that publish the addresses of
+ List of DNS domains that publish the addresses of
blacklisted hosts. This is used with the deprecated
<b>reject</b><i>_</i><b>maps</b><i>_</i><b>rbl</b> restriction.
<b>permit</b><i>_</i><b>mx</b><i>_</i><b>backup</b><i>_</i><b>networks</b>
- Only domains whose primary MX hosts match the
- listed networks are eligible for the <b>per-</b>
+ Only domains whose primary MX hosts match the
+ listed networks are eligible for the <b>per-</b>
<b>mit</b><i>_</i><b>mx</b><i>_</i><b>backup</b> feature.
<b>relay</b><i>_</i><b>domains</b>
- Restrict what domains this mail system will relay
- mail to. The domains are routed to the delivery
+ Restrict what domains this mail system will relay
+ mail to. The domains are routed to the delivery
agent specified with the <b>relay</b><i>_</i><b>transport</b> setting.
<b>UCE</b> <b>control</b> <b>responses</b>
<b>access</b><i>_</i><b>map</b><i>_</i><b>reject</b><i>_</i><b>code</b>
- Response code when a client violates an access
+ Response code when a client violates an access
database restriction.
<b>default</b><i>_</i><b>rbl</b><i>_</i><b>reply</b>
Default template reply when a request is RBL black-
- listed. This template is used by the <b>reject</b><i>_</i><b>rbl</b><i>_</i><b>*</b>
- and <b>reject</b><i>_</i><b>rhsbl</b><i>_</i><b>*</b> restrictions. See also:
+ listed. This template is used by the <b>reject</b><i>_</i><b>rbl</b><i>_</i><b>*</b>
+ and <b>reject</b><i>_</i><b>rhsbl</b><i>_</i><b>*</b> restrictions. See also:
<b>rbl</b><i>_</i><b>reply</b><i>_</i><b>maps</b> and <b>smtpd</b><i>_</i><b>expansion</b><i>_</i><b>filter</b>.
<b>defer</b><i>_</i><b>code</b>
- Response code when a client request is rejected by
+ Response code when a client request is rejected by
the <b>defer</b> restriction.
<b>invalid</b><i>_</i><b>hostname</b><i>_</i><b>reject</b><i>_</i><b>code</b>
- Response code when a client violates the
+ Response code when a client violates the
<b>reject</b><i>_</i><b>invalid</b><i>_</i><b>hostname</b> restriction.
<b>maps</b><i>_</i><b>rbl</b><i>_</i><b>reject</b><i>_</i><b>code</b>
Response code when a request is RBL blacklisted.
<b>rbl</b><i>_</i><b>reply</b><i>_</i><b>maps</b>
- Table with template responses for RBL blacklisted
- requests, indexed by RBL domain name. These tem-
+ Table with template responses for RBL blacklisted
+ requests, indexed by RBL domain name. These tem-
plates are used by the <b>reject</b><i>_</i><b>rbl</b><i>_</i><b>*</b> and
- <b>reject</b><i>_</i><b>rhsbl</b><i>_</i><b>*</b> restrictions. See also:
+ <b>reject</b><i>_</i><b>rhsbl</b><i>_</i><b>*</b> restrictions. See also:
<b>default</b><i>_</i><b>rbl</b><i>_</i><b>reply</b> and <b>smtpd</b><i>_</i><b>expansion</b><i>_</i><b>filter</b>.
<b>reject</b><i>_</i><b>code</b>
- Response code when the client matches a <b>reject</b>
+ Response code when the client matches a <b>reject</b>
restriction.
<b>relay</b><i>_</i><b>domains</b><i>_</i><b>reject</b><i>_</i><b>code</b>
mail relay policy.
<b>unknown</b><i>_</i><b>address</b><i>_</i><b>reject</b><i>_</i><b>code</b>
- Response code when a client violates the
+ Response code when a client violates the
<b>reject</b><i>_</i><b>unknown</b><i>_</i><b>address</b> restriction.
<b>unknown</b><i>_</i><b>client</b><i>_</i><b>reject</b><i>_</i><b>code</b>
tion.
<b>unknown</b><i>_</i><b>hostname</b><i>_</i><b>reject</b><i>_</i><b>code</b>
- Response code when a client violates the
+ Response code when a client violates the
<b>reject</b><i>_</i><b>unknown</b><i>_</i><b>hostname</b> restriction.
<b>SEE</b> <b>ALSO</b>
syslogd(8) system logging
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
Alternatively, the table can be provided as a regular-
expression map where patterns are given as regular expres-
sions. In that case, the lookups are done in a slightly
- different way as described in the section titled "REGULAR
- EXPRESSION TABLES".
+ different way as described in section "REGULAR EXPRESSION
+ TABLES".
<b>TABLE</b> <b>FORMAT</b>
The format of the transport table is as follows:
<i>pattern</i> <i>result</i>
- When <i>pattern</i> matches the domain, use the corre-
- sponding <i>result</i>.
+ When <i>pattern</i> matches the recipient address or
+ domain, use the corresponding <i>result</i>.
blank lines and comments
Empty lines and whitespace-only lines are ignored,
line that starts with whitespace continues a logi-
cal line.
- In an indexed file, a pattern of `<b>*</b>' matches everything.
+ The <i>pattern</i> specifies an email address, a domain name, or
+ a domain name hierarchy, as described in section "TABLE
+ LOOKUP".
The <i>result</i> is of the form <i>transport</i><b>:</b><i>nexthop</i>. The <i>trans-</i>
<i>port</i> field specifies a mail delivery transport such as
<b>smtp</b> or <b>local</b>. The <i>nexthop</i> field specifies where and how
- to deliver mail. A null <i>transport</i> or <i>nexthop</i> field means
- "do not change": use the delivery transport and nexthop
- information that would be used if no match were found.
+ to deliver mail. More details are given in section "RESULT
+ FORMAT".
-<b>TRANSPORT</b> <b>FIELD</b>
- The transport field specifies the name of a mail delivery
- transport (the first name of a mail delivery service entry
- in the Postfix <b>master.cf</b> file).
-
- When a null transport field is specified, Postfix uses one
- of the following transports:
-
- <b>$local</b><i>_</i><b>transport</b>
- The domain matches <b>$mydestination</b> or <b>$inet</b><i>_</i><b>inter-</b>
- <b>faces</b>.
-
- <b>$virtual</b><i>_</i><b>transport</b>
- The domain matches <b>$virtual</b><i>_</i><b>mailbox</b><i>_</i><b>domains</b>.
-
- <b>$relay</b><i>_</i><b>transport</b>
- The domain matches <b>$relay</b><i>_</i><b>transport</b>.
-
- <b>$default</b><i>_</i><b>transport</b>
- All other non-local, non-virtual destinations.
-
-<b>NEXTHOP</b> <b>FIELD</b>
- The interpretation of the nexthop field is transport
- dependent. In the case of SMTP, specify <i>host</i>:<i>service</i> for a
- non-default server port, and use [<i>host</i>] or [<i>host</i>]:<i>port</i> in
- order to disable MX (mail exchanger) DNS lookups. The []
- form can also be used with IP addresses instead of host-
- names.
-
-<b>LOOKUP</b> <b>ORDER</b>
+<b>TABLE</b> <b>LOOKUP</b>
With lookups from indexed files such as DB or DBM, or from
networked tables such as NIS, LDAP or SQL, patterns are
tried in the order as listed below:
ting. Otherwise, a domain name matches itself and
its subdomains.
-<b>NOTE</b>
- The special pattern <> represents the null address, and
- the special pattern <b>*</b> represents any address (i.e. it
- functions as the wild-card pattern).
+ Note 1: the special pattern <b>*</b> represents any address (i.e.
+ it functions as the wild-card pattern).
+
+ Note 2: the null recipient address is looked up as the
+ local mailer-daemon address (mailer-daemon@fully-quali-
+ fied-domain-name).
+
+<b>RESULT</b> <b>FORMAT</b>
+ A null <i>transport</i> and null <i>nexthop</i> result means "do not
+ change": use the delivery transport and nexthop informa-
+ tion that would be used when the entire transport table
+ did not exist.
+
+ A non-null <i>transport</i> field with a null <i>nexthop</i> field
+ resets the nexthop information to the recipient domain.
+
+ A null <i>transport</i> field with non-null <i>nexthop</i> field does
+ not modify the transport information.
+
+<b>TRANSPORT</b> <b>FIELD</b>
+ The transport field specifies the name of a mail delivery
+ transport (the first name of a mail delivery service entry
+ in the Postfix <b>master.cf</b> file).
+
+ When a null transport field is specified, Postfix uses one
+ of the following transports:
+
+ <b>$local</b><i>_</i><b>transport</b>
+ The domain matches <b>$mydestination</b> or <b>$inet</b><i>_</i><b>inter-</b>
+ <b>faces</b>.
+
+ <b>$virtual</b><i>_</i><b>transport</b>
+ The domain matches <b>$virtual</b><i>_</i><b>mailbox</b><i>_</i><b>domains</b>.
+
+ <b>$relay</b><i>_</i><b>transport</b>
+ The domain matches <b>$relay</b><i>_</i><b>transport</b>.
+
+ <b>$default</b><i>_</i><b>transport</b>
+ All other non-local, non-virtual destinations.
+
+<b>NEXTHOP</b> <b>FIELD</b>
+ The interpretation of the nexthop field is transport
+ dependent. In the case of SMTP, specify <i>host</i>:<i>service</i> for a
+ non-default server port, and use [<i>host</i>] or [<i>host</i>]:<i>port</i> in
+ order to disable MX (mail exchanger) DNS lookups. The []
+ form can also be used with IP addresses instead of host-
+ names.
<b>EXAMPLES</b>
- In order to deliver internal mail directly, while using a
- mail relay for all other mail, specify a null entry for
- internal destinations (do not change the delivery trans-
- port or the nexthop information) and specify a wildcard
- for all other destinations. Note that for this trick to
- work you should not specify a <b>relayhost</b> in the <b>main.cf</b>
+ In order to deliver internal mail directly, while using a
+ mail relay for all other mail, specify a null entry for
+ internal destinations (do not change the delivery trans-
+ port or the nexthop information) and specify a wildcard
+ for all other destinations. Note that for this trick to
+ work you should not specify a <b>relayhost</b> in the <b>main.cf</b>
file.
<b>my.domain</b> <b>:</b>
<b>.my.domain</b> <b>:</b>
<b>*</b> <b>smtp:outbound-relay.my.domain</b>
- In order to send mail for <b>foo.org</b> and its subdomains via
+ In order to send mail for <b>foo.org</b> and its subdomains via
the <b>uucp</b> transport to the UUCP host named <b>foo</b>:
<b>foo.org</b> <b>uucp:foo</b>
<b>.foo.org</b> <b>uucp:foo</b>
- When no nexthop host name is specified, the destination
- domain name is used instead. For example, the following
- directs mail for <i>user</i>@<b>foo.org</b> via the <b>slow</b> transport to a
- mail exchanger for <b>foo.org</b>. The <b>slow</b> transport could be
- something that runs at most one delivery process at a
+ When no nexthop host name is specified, the destination
+ domain name is used instead. For example, the following
+ directs mail for <i>user</i>@<b>foo.org</b> via the <b>slow</b> transport to a
+ mail exchanger for <b>foo.org</b>. The <b>slow</b> transport could be
+ something that runs at most one delivery process at a
time:
<b>foo.org</b> <b>slow:</b>
When no transport is specified, Postfix uses the transport
that matches the address domain class (see TRANSPORT FIELD
- discussion above). The following sends all mail for
+ discussion above). The following sends all mail for
<b>foo.org</b> and its subdomains to host <b>gateway.foo.org</b>:
<b>foo.org</b> <b>:[gateway.foo.org]</b>
<b>.foo.org</b> <b>:[gateway.foo.org]</b>
- In the above example, the [] are used to suppress MX
- lookups. The result would likely point to your local
+ In the above example, the [] are used to suppress MX
+ lookups. The result would likely point to your local
machine.
- In the case of delivery via SMTP, one may specify <i>host-</i>
+ In the case of delivery via SMTP, one may specify <i>host-</i>
<i>name</i>:<i>service</i> instead of just a host:
<b>foo.org</b> <b>smtp:bar.org:2025</b>
- This directs mail for <i>user</i>@<b>foo.org</b> to host <b>bar.org</b> port
- <b>2025</b>. Instead of a numerical port a symbolic name may be
- used. Specify [] around the hostname in order to disable
+ This directs mail for <i>user</i>@<b>foo.org</b> to host <b>bar.org</b> port
+ <b>2025</b>. Instead of a numerical port a symbolic name may be
+ used. Specify [] around the hostname in order to disable
MX lookups.
The error mailer can be used to bounce mail:
- <b>.foo.org</b> <b>error:mail</b> <b>for</b> <b>*.foo.org</b> <b>is</b> <b>not</b> <b>deliv-</b>
+ <b>.foo.org</b> <b>error:mail</b> <b>for</b> <b>*.foo.org</b> <b>is</b> <b>not</b> <b>deliv-</b>
<b>erable</b>
- This causes all mail for <i>user</i>@<i>anything</i><b>.foo.org</b> to be
+ This causes all mail for <i>user</i>@<i>anything</i><b>.foo.org</b> to be
bounced.
<b>REGULAR</b> <b>EXPRESSION</b> <b>TABLES</b>
- This section describes how the table lookups change when
+ This section describes how the table lookups change when
the table is given in the form of regular expressions. For
- a description of regular expression lookup table syntax,
+ a description of regular expression lookup table syntax,
see <a href="regexp_table.5.html"><b>regexp</b><i>_</i><b>table</b>(5)</a> or <a href="pcre_table.5.html"><b>pcre</b><i>_</i><b>table</b>(5)</a>.
- Each pattern is a regular expression that is applied to
+ Each pattern is a regular expression that is applied to
the entire domain being looked up. Thus, <i>some.domain.hier-</i>
<i>archy</i> is not broken up into parent domains.
- Patterns are applied in the order as specified in the
- table, until a pattern is found that matches the search
+ Patterns are applied in the order as specified in the
+ table, until a pattern is found that matches the search
string.
- Results are the same as with indexed file lookups, with
- the additional feature that parenthesized substrings from
+ Results are the same as with indexed file lookups, with
+ the additional feature that parenthesized substrings from
the pattern can be interpolated as <b>$1</b>, <b>$2</b> and so on.
<b>CONFIGURATION</b> <b>PARAMETERS</b>
- The following <b>main.cf</b> parameters are especially relevant
- to this topic. See the Postfix <b>main.cf</b> file for syntax
- details and for default values. Use the <b>postfix</b> <b>reload</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this topic. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
command after a configuration change.
<b>parent</b><i>_</i><b>domain</b><i>_</i><b>matches</b><i>_</i><b>subdomains</b>
- List of Postfix features that use <i>domain.tld</i> pat-
- terns to match <i>sub.domain.tld</i> (as opposed to
+ List of Postfix features that use <i>domain.tld</i> pat-
+ terns to match <i>sub.domain.tld</i> (as opposed to
requiring <i>.domain.tld</i> patterns).
<b>transport</b><i>_</i><b>maps</b>
<b>local</b><i>_</i><b>transport</b>
The default mail delivery transport when the desti-
- nation matches <b>$mydestination</b> or <b>$inet</b><i>_</i><b>interfaces</b>.
+ nation matches <b>$mydestination</b> or <b>$inet</b><i>_</i><b>interfaces</b>.
<b>virtual</b><i>_</i><b>transport</b>
The default mail delivery transport when the desti-
<b>default</b><i>_</i><b>transport</b>
The default mail delivery transport when the desti-
- nation does not match a local, virtual or relay
+ nation does not match a local, virtual or relay
destination.
<b>mydestination</b>
<a href="regexp_table.5.html">regexp_table(5)</a> format of POSIX regular expression tables
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
Syntax is <i>transport</i>:<i>nexthop</i>; see <a href="transport.5.html"><b>transport</b>(5)</a> for
details. The :<i>nexthop</i> part is optional.
- <b>error</b><i>_</i><b>transport</b>
- Where to deliver mail for non-existent recipients
- in domains that match <b>virtual</b><i>_</i><b>alias</b><i>_</i><b>domains</b> (all
- recipients in simulated virtual domains must be
- aliased to some other local or remote domain), or
- for recipients that have moved. The default trans-
- port is <b>error</b>.
-
- Syntax is <i>transport</i>:<i>nexthop</i>; see <a href="transport.5.html"><b>transport</b>(5)</a> for
- details. The :<i>nexthop</i> part is optional.
-
<b>virtual</b><i>_</i><b>transport</b>
Where to deliver mail for non-local domains that
match <b>$virtual</b><i>_</i><b>mailbox</b><i>_</i><b>domains</b>. The default trans-
List of tables with <i>domain</i> to (<i>transport,</i> <i>nexthop</i>)
mappings.
- <b>transport</b><i>_</i><b>null</b><i>_</i><b>address</b><i>_</i><b>lookup</b><i>_</i><b>key</b>
- Lookup key to be used for the null address.
-
<b>SEE</b> <b>ALSO</b>
<a href="master.8.html">master(8)</a> process manager
syslogd(8) system logging
Alternatively, the table can be provided as a regular-expression
map where patterns are given as regular expressions. In that case,
the lookups are done in a slightly different way as described
-in the section titled "REGULAR EXPRESSION TABLES".
+in section "REGULAR EXPRESSION TABLES".
.SH TABLE FORMAT
.na
.nf
.fi
The format of the transport table is as follows:
.IP "\fIpattern result\fR"
-When \fIpattern\fR matches the domain, use the corresponding
-\fIresult\fR.
+When \fIpattern\fR matches the recipient address or domain, use the
+corresponding \fIresult\fR.
.IP "blank lines and comments"
Empty lines and whitespace-only lines are ignored, as
are lines whose first non-whitespace character is a `#'.
A logical line starts with non-whitespace text. A line that
starts with whitespace continues a logical line.
.PP
-In an indexed file, a pattern of `\fB*\fR' matches everything.
-.PP
+The \fIpattern\fR specifies an email address, a domain name, or
+a domain name hierarchy, as described in section "TABLE LOOKUP".
+
The \fIresult\fR is of the form \fItransport\fB:\fInexthop\fR.
The \fItransport\fR field specifies a mail delivery transport
such as \fBsmtp\fR or \fBlocal\fR. The \fInexthop\fR field
-specifies where and how to deliver mail. A null \fItransport\fR
-or \fInexthop\fR field means "do not change": use the delivery
-transport and nexthop information that would be used if no
-match were found.
+specifies where and how to deliver mail. More details are given
+in section "RESULT FORMAT".
+.SH TABLE LOOKUP
+.ad
+.fi
+With lookups from indexed files such as DB or DBM, or from networked
+tables such as NIS, LDAP or SQL, patterns are tried in the order as
+listed below:
+.IP "\fIuser+extension@domain transport\fR:\fInexthop\fR"
+Mail for \fIuser+extension@domain\fR is delivered through
+\fItransport\fR to
+\fInexthop\fR.
+.IP "\fIuser@domain transport\fR:\fInexthop\fR"
+Mail for \fIuser@domain\fR is delivered through \fItransport\fR to
+\fInexthop\fR.
+.IP "\fIdomain transport\fR:\fInexthop\fR"
+Mail for \fIdomain\fR is delivered through \fItransport\fR to
+\fInexthop\fR.
+.IP "\fI.domain transport\fR:\fInexthop\fR"
+Mail for any subdomain of \fIdomain\fR is delivered through
+\fItransport\fR to \fInexthop\fR. This applies only when the
+string \fBtransport_maps\fR is not listed in the
+\fBparent_domain_matches_subdomains\fR configuration setting.
+Otherwise, a domain name matches itself and its subdomains.
+.PP
+Note 1: the special pattern \fB*\fR represents any address (i.e. it
+functions as the wild-card pattern).
+
+Note 2: the null recipient address is looked up as the local
+mailer-daemon address (mailer-daemon@fully-qualified-domain-name).
+.SH RESULT FORMAT
+.ad
+.fi
+
+A null \fItransport\fR and null \fInexthop\fR result means "do
+not change": use the delivery transport and nexthop information
+that would be used when the entire transport table did not exist.
+
+A non-null \fItransport\fR field with a null \fInexthop\fR field
+resets the nexthop information to the recipient domain.
+
+A null \fItransport\fR field with non-null \fInexthop\fR field
+does not modify the transport information.
.SH TRANSPORT FIELD
.ad
.fi
non-default server port, and use [\fIhost\fR] or [\fIhost\fR]:\fIport\fR
in order to disable MX (mail exchanger) DNS lookups. The [] form
can also be used with IP addresses instead of hostnames.
-.SH LOOKUP ORDER
-.ad
-.fi
-With lookups from indexed files such as DB or DBM, or from networked
-tables such as NIS, LDAP or SQL, patterns are tried in the order as
-listed below:
-.IP "\fIuser+extension@domain transport\fR:\fInexthop\fR"
-Mail for \fIuser+extension@domain\fR is delivered through
-\fItransport\fR to
-\fInexthop\fR.
-.IP "\fIuser@domain transport\fR:\fInexthop\fR"
-Mail for \fIuser@domain\fR is delivered through \fItransport\fR to
-\fInexthop\fR.
-.IP "\fIdomain transport\fR:\fInexthop\fR"
-Mail for \fIdomain\fR is delivered through \fItransport\fR to
-\fInexthop\fR.
-.IP "\fI.domain transport\fR:\fInexthop\fR"
-Mail for any subdomain of \fIdomain\fR is delivered through
-\fItransport\fR to \fInexthop\fR. This applies only when the
-string \fBtransport_maps\fR is not listed in the
-\fBparent_domain_matches_subdomains\fR configuration setting.
-Otherwise, a domain name matches itself and its subdomains.
-.PP
-.SH NOTE
-.na
-.nf
-.ad
-.fi
-The special pattern \fB<>\fR represents the null address, and the
-special pattern \fB*\fR represents any address (i.e. it functions
-as the wild-card pattern).
.SH EXAMPLES
.na
.nf
Recipient of protocol/policy/resource/software error notices.
.IP \fBhopcount_limit\fR
Limit the number of \fBReceived:\fR message headers.
-.IP \fBlocal_recipient_maps\fR
-List of maps with user names that are local to \fB$myorigin\fR
-or \fB$inet_interfaces\fR. If this parameter is defined,
-then the SMTP server rejects mail for unknown local users.
-.IP \fBrelay_recipient_maps\fR
-List of maps that define all the email addresses in the domains
-that match \fB$relay_domains\fR. If this parameter is defined,
-then the SMTP server rejects mail for unknown relay recipients.
.IP \fBnotify_classes\fR
List of error classes. Of special interest are:
.RS
This can be useful for testing purposes.
.IP \fBverp_delimiter_filter\fR
The characters that Postfix accepts as VERP delimiter characters.
+.SH "Known versus unknown recipients"
+.ad
+.fi
+.IP \fBunknown_local_recipient_reject_code\fR
+The response code when a client specifies a recipient whose domain
+matches \fB$mydestination\fR or \fB$inet_interfaces\fR, while
+\fB$local_recipient_maps\fR is non-empty and does not list
+the recipient address or address local-part.
+.IP \fBunknown_relay_recipient_reject_code\fR
+The response code when a client specifies a recipient whose domain
+matches \fB$relay_domains\fR, while \fB$relay_recipient_maps\fR
+is non-empty and does not list the recipient address.
+.IP \fBunknown_virtual_alias_reject_code\fR
+The response code when a client specifies a recipient whose domain
+matches \fB$virtual_alias_domains\fR, while the recipient is not
+listed in \fB$virtual_alias_maps\fR.
+.IP \fBunknown_virtual_mailbox_reject_code\fR
+The response code when a client specifies a recipient whose domain
+matches \fB$virtual_mailbox_domains\fR, while the recipient is not
+listed in \fB$virtual_mailbox_maps\fR.
.SH "Resource controls"
.ad
.fi
.sp
Syntax is \fItransport\fR:\fInexthop\fR; see \fBtransport\fR(5)
for details. The :\fInexthop\fR part is optional.
-.IP \fBerror_transport\fR
-Where to deliver mail for non-existent recipients in domains
-that match \fBvirtual_alias_domains\fR (all recipients
-in simulated virtual domains must be aliased to some other
-local or remote domain), or for recipients that have moved.
-The default transport is \fBerror\fR.
-.sp
-Syntax is \fItransport\fR:\fInexthop\fR; see \fBtransport\fR(5)
-for details. The :\fInexthop\fR part is optional.
.IP \fBvirtual_transport\fR
Where to deliver mail for non-local domains that match
\fB$virtual_mailbox_domains\fR.
.IP \fBtransport_maps\fR
List of tables with \fIdomain\fR to (\fItransport, nexthop\fR)
mappings.
-.IP \fBtransport_null_address_lookup_key\fR
-Lookup key to be used for the null address.
.SH SEE ALSO
.na
.nf
# Alternatively, the table can be provided as a regular-expression
# map where patterns are given as regular expressions. In that case,
# the lookups are done in a slightly different way as described
-# in the section titled "REGULAR EXPRESSION TABLES".
+# in section "REGULAR EXPRESSION TABLES".
# TABLE FORMAT
# .ad
# .fi
# The format of the transport table is as follows:
# .IP "\fIpattern result\fR"
-# When \fIpattern\fR matches the domain, use the corresponding
-# \fIresult\fR.
+# When \fIpattern\fR matches the recipient address or domain, use the
+# corresponding \fIresult\fR.
# .IP "blank lines and comments"
# Empty lines and whitespace-only lines are ignored, as
# are lines whose first non-whitespace character is a `#'.
# A logical line starts with non-whitespace text. A line that
# starts with whitespace continues a logical line.
# .PP
-# In an indexed file, a pattern of `\fB*\fR' matches everything.
-# .PP
+# The \fIpattern\fR specifies an email address, a domain name, or
+# a domain name hierarchy, as described in section "TABLE LOOKUP".
+#
# The \fIresult\fR is of the form \fItransport\fB:\fInexthop\fR.
# The \fItransport\fR field specifies a mail delivery transport
# such as \fBsmtp\fR or \fBlocal\fR. The \fInexthop\fR field
-# specifies where and how to deliver mail. A null \fItransport\fR
-# or \fInexthop\fR field means "do not change": use the delivery
-# transport and nexthop information that would be used if no
-# match were found.
+# specifies where and how to deliver mail. More details are given
+# in section "RESULT FORMAT".
+# .SH TABLE LOOKUP
+# .ad
+# .fi
+# With lookups from indexed files such as DB or DBM, or from networked
+# tables such as NIS, LDAP or SQL, patterns are tried in the order as
+# listed below:
+# .IP "\fIuser+extension@domain transport\fR:\fInexthop\fR"
+# Mail for \fIuser+extension@domain\fR is delivered through
+# \fItransport\fR to
+# \fInexthop\fR.
+# .IP "\fIuser@domain transport\fR:\fInexthop\fR"
+# Mail for \fIuser@domain\fR is delivered through \fItransport\fR to
+# \fInexthop\fR.
+# .IP "\fIdomain transport\fR:\fInexthop\fR"
+# Mail for \fIdomain\fR is delivered through \fItransport\fR to
+# \fInexthop\fR.
+# .IP "\fI.domain transport\fR:\fInexthop\fR"
+# Mail for any subdomain of \fIdomain\fR is delivered through
+# \fItransport\fR to \fInexthop\fR. This applies only when the
+# string \fBtransport_maps\fR is not listed in the
+# \fBparent_domain_matches_subdomains\fR configuration setting.
+# Otherwise, a domain name matches itself and its subdomains.
+# .PP
+# Note 1: the special pattern \fB*\fR represents any address (i.e. it
+# functions as the wild-card pattern).
+#
+# Note 2: the null recipient address is looked up as the local
+# mailer-daemon address (mailer-daemon@fully-qualified-domain-name).
+# .SH RESULT FORMAT
+# .ad
+# .fi
+#
+# A null \fItransport\fR and null \fInexthop\fR result means "do
+# not change": use the delivery transport and nexthop information
+# that would be used when the entire transport table did not exist.
+#
+# A non-null \fItransport\fR field with a null \fInexthop\fR field
+# resets the nexthop information to the recipient domain.
+#
+# A null \fItransport\fR field with non-null \fInexthop\fR field
+# does not modify the transport information.
# .SH TRANSPORT FIELD
# .ad
# .fi
# non-default server port, and use [\fIhost\fR] or [\fIhost\fR]:\fIport\fR
# in order to disable MX (mail exchanger) DNS lookups. The [] form
# can also be used with IP addresses instead of hostnames.
-# .SH LOOKUP ORDER
-# .ad
-# .fi
-# With lookups from indexed files such as DB or DBM, or from networked
-# tables such as NIS, LDAP or SQL, patterns are tried in the order as
-# listed below:
-# .IP "\fIuser+extension@domain transport\fR:\fInexthop\fR"
-# Mail for \fIuser+extension@domain\fR is delivered through
-# \fItransport\fR to
-# \fInexthop\fR.
-# .IP "\fIuser@domain transport\fR:\fInexthop\fR"
-# Mail for \fIuser@domain\fR is delivered through \fItransport\fR to
-# \fInexthop\fR.
-# .IP "\fIdomain transport\fR:\fInexthop\fR"
-# Mail for \fIdomain\fR is delivered through \fItransport\fR to
-# \fInexthop\fR.
-# .IP "\fI.domain transport\fR:\fInexthop\fR"
-# Mail for any subdomain of \fIdomain\fR is delivered through
-# \fItransport\fR to \fInexthop\fR. This applies only when the
-# string \fBtransport_maps\fR is not listed in the
-# \fBparent_domain_matches_subdomains\fR configuration setting.
-# Otherwise, a domain name matches itself and its subdomains.
-# .PP
-# NOTE
-# .ad
-# .fi
-# The special pattern \fB<>\fR represents the null address, and the
-# special pattern \fB*\fR represents any address (i.e. it functions
-# as the wild-card pattern).
# EXAMPLES
# .ad
# .fi
diff virtual8.ref virtual8.tmp
rm -f virtual8.tmp virtual8_map.db
+# Requires: Postfix running, root privileges
+
+rewrite_clnt_test: rewrite_clnt rewrite_clnt.in rewrite_clnt.ref
+ ./rewrite_clnt <rewrite_clnt.in >rewrite_clnt.tmp
+ sed "s/MYDOMAIN/`postconf -h mydomain`/" rewrite_clnt.ref | \
+ diff - rewrite_clnt.tmp
+ rm -f rewrite_clnt.tmp
+
+# Requires: Postfix, root, myorigin=$myhostname, relayhost=$mydomain,
+# no transport map
+
+resolve_clnt_test: resolve_clnt resolve_clnt.in resolve_clnt.ref
+ sed -e "s/MYDOMAIN/`postconf -h mydomain`/g" \
+ -e "s/MYHOSTNAME/`postconf -h myhostname`/g" \
+ resolve_clnt.in | ./resolve_clnt >resolve_clnt.tmp
+ sed -e "s/MYDOMAIN/`postconf -h mydomain`/g" \
+ -e "s/MYHOSTNAME/`postconf -h myhostname`/g" \
+ -e "s/RELAYHOST/`postconf -h mydomain`/g" \
+ resolve_clnt.ref | diff - resolve_clnt.tmp
+ rm -f resolve_clnt.tmp
+
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
* The default local delivery transport.
*/
#define VAR_LOCAL_TRANSPORT "local_transport"
-#define DEF_LOCAL_TRANSPORT "local"
+#define DEF_LOCAL_TRANSPORT MAIL_SERVICE_LOCAL ":$myhostname"
extern char *var_local_transport;
/*
/*
* trivial rewrite/resolve service: mapping tables.
*/
-#define VAR_ERROR_TRANSPORT "error_transport"
-#define DEF_ERROR_TRANSPORT MAIL_SERVICE_ERROR
-extern char *var_error_transport;
-
#define VAR_VIRT_ALIAS_MAPS "virtual_alias_maps"
#define DEF_VIRT_ALIAS_MAPS "$virtual_maps" /* Compatibility! */
extern char *var_virt_alias_maps;
#define DEF_VIRT_ALIAS_DOMS "$virtual_alias_maps"
extern char *var_virt_alias_doms;
+#define VAR_VIRT_ALIAS_CODE "unknown_virtual_alias_reject_code"
+#define DEF_VIRT_ALIAS_CODE 550
+extern int var_virt_alias_code;
+
#define VAR_CANONICAL_MAPS "canonical_maps"
#define DEF_CANONICAL_MAPS ""
extern char *var_canonical_maps;
#define DEF_RELAY_RCPT_MAPS ""
extern char *var_relay_rcpt_maps;
+#define VAR_RELAY_RCPT_CODE "unknown_relay_recipient_reject_code"
+#define DEF_RELAY_RCPT_CODE 550
+extern int var_relay_rcpt_code;
+
#define VAR_CLIENT_CHECKS "smtpd_client_restrictions"
#define DEF_CLIENT_CHECKS ""
extern char *var_client_checks;
extern char *var_smtpd_exp_filter;
/*
- * Heuristic to reject most unknown recipients at the SMTP port.
+ * Heuristic to reject unknown local recipients at the SMTP port.
*/
#define VAR_LOCAL_RCPT_MAPS "local_recipient_maps"
#define DEF_LOCAL_RCPT_MAPS "unix:passwd.byname $alias_maps"
extern char *var_local_rcpt_maps;
+#define VAR_LOCAL_RCPT_CODE "unknown_local_recipient_reject_code"
+#define DEF_LOCAL_RCPT_CODE 550
+extern int var_local_rcpt_code;
+
/*
* Other.
*/
#define DEF_VIRT_MAILBOX_DOMS "$virtual_mailbox_maps"
extern char *var_virt_mailbox_doms;
+#define VAR_VIRT_MAILBOX_CODE "unknown_virtual_mailbox_reject_code"
+#define DEF_VIRT_MAILBOX_CODE 550
+extern int var_virt_mailbox_code;
+
#define VAR_VIRT_UID_MAPS "virtual_uid_maps"
#define DEF_VIRT_UID_MAPS ""
extern char *var_virt_uid_maps;
* 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 "20021214"
+#define MAIL_RELEASE_DATE "20021217"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "1.1.12-" MAIL_RELEASE_DATE
* lookup tables to speed up some of the work, but hey, how large can a
* local-part be anyway?
*/
- if (local_part[0] == 0 || local_part[0] == '.')
+ if (local_part == end || local_part[0] == 0 || local_part[0] == '.')
return (NO);
for (cp = local_part; cp < end && (ch = *(unsigned char *) cp) != 0; cp++) {
if (ch == '.' && cp[1] == '.')
* RFC 822 expects 7-bit data. Rather than quoting every 8-bit character
* (and still passing it on as 8-bit data) we leave 8-bit data alone.
*/
- if (local_part[0] == 0 || local_part[0] == '.')
+ if (local_part == end || local_part[0] == 0 || local_part[0] == '.')
return (NO);
for (cp = local_part; cp < end && (ch = *(unsigned char *) cp) != 0; cp++) {
if (ch == '.' && (cp + 1) < end && cp[1] == '.')
/* authorized mail relay destination.
/* .IP RESOLVE_CLASS_DEFAULT
/* The address matches none of the above. Access to this domain
-/* should be limited to authorized senders only.
+/* should be limited to authorized senders only.
/* .PP
/* For convenience, the constant RESOLVE_CLASS_FINAL includes all
/* cases where the local machine is the final destination.
*/
if (rewrite_clnt_stream == 0)
rewrite_clnt_stream = clnt_stream_create(MAIL_CLASS_PRIVATE,
- var_rewrite_service, var_ipc_idle_limit);
+ var_rewrite_service, var_ipc_idle_limit);
for (;;) {
stream = clnt_stream_access(rewrite_clnt_stream);
static void resolve(char *addr, RESOLVE_REPLY *reply)
{
+ struct RESOLVE_FLAG_TABLE {
+ int flag;
+ const char *name;
+ };
+ struct RESOLVE_FLAG_TABLE resolve_flag_table[] = {
+ RESOLVE_FLAG_FINAL, "FLAG_FINAL",
+ RESOLVE_FLAG_ROUTED, "FLAG_ROUTED",
+ RESOLVE_FLAG_ERROR, "FLAG_ERROR",
+ RESOLVE_FLAG_FAIL, "FLAG_FAIL",
+ RESOLVE_CLASS_LOCAL, "CLASS_LOCAL",
+ RESOLVE_CLASS_ALIAS, "CLASS_ALIAS",
+ RESOLVE_CLASS_VIRTUAL, "CLASS_VIRTUAL",
+ RESOLVE_CLASS_RELAY, "CLASS_RELAY",
+ RESOLVE_CLASS_DEFAULT, "CLASS_DEFAULT",
+ 0,
+ };
+ struct RESOLVE_FLAG_TABLE *fp;
+
resolve_clnt_query(addr, reply);
if (reply->flags & RESOLVE_FLAG_FAIL) {
vstream_printf("request failed\n");
vstream_printf("%-10s %s\n", "nexthop", *STR(reply->nexthop) ?
STR(reply->nexthop) : "[none]");
vstream_printf("%-10s %s\n", "recipient", STR(reply->recipient));
+ vstream_printf("%-10s ", "flags");
+ for (fp = resolve_flag_table; fp->name; fp++) {
+ if (reply->flags & fp->flag) {
+ vstream_printf("%s ", fp->name);
+ reply->flags &= ~fp->flag;
+ }
+ }
+ if (reply->flags != 0)
+ vstream_printf("Unknown flag 0x%x", reply->flags);
+ vstream_printf("\n");
vstream_fflush(VSTREAM_OUT);
}
}
vstring_free(buffer);
}
resolve_clnt_free(&reply);
+ exit(0);
}
#endif
--- /dev/null
+
+@
+@@
+@a.
+@..
+@.@.
+!
+a!
+!b
+a!b
+!@
+a!@
+!b@
+a!b@
+%
+a%
+%b
+a%b
+%@
+a%@
+%b@
+@@
+a@@
+@b@
+a@b@
+a%b@
+a%b@MYHOSTNAME
+a!b@MYHOSTNAME
+a@b@MYHOSTNAME
+a[b]@MYHOSTNAME@MYHOSTNAME
+a[b]%MYHOSTNAME@MYHOSTNAME
+a[b]%MYHOSTNAME%MYHOSTNAME
+MYHOSTNAME!a[b]@MYHOSTNAME
+MYHOSTNAME!a[b]%MYHOSTNAME
+MYHOSTNAME!MYHOSTNAME!a[b]
+user@dom.ain1@dom.ain2
+user%dom.ain1@dom.ain2
+dom.ain1!user@dom.ain2
+user@[1.2.3.4]@dom.ain2
+user%[1.2.3.4]@dom.ain2
+[1.2.3.4]!user@dom.ain2
+user@localhost.MYDOMAIN
+user@[321.1.2.3]
+user@1.2.3
+user@host:port
--- /dev/null
+address
+transport local
+nexthop MYHOSTNAME
+recipient MAILER-DAEMON@MYHOSTNAME
+flags CLASS_LOCAL
+address @
+transport local
+nexthop MYHOSTNAME
+recipient MAILER-DAEMON@MYHOSTNAME
+flags CLASS_LOCAL
+address @@
+transport local
+nexthop MYHOSTNAME
+recipient MAILER-DAEMON@MYHOSTNAME
+flags CLASS_LOCAL
+address @a.
+transport smtp
+nexthop RELAYHOST
+recipient @a
+flags CLASS_DEFAULT
+address @..
+transport smtp
+nexthop RELAYHOST
+recipient @..
+flags FLAG_ERROR CLASS_DEFAULT
+address @.@.
+transport local
+nexthop MYHOSTNAME
+recipient MAILER-DAEMON@MYHOSTNAME
+flags CLASS_LOCAL
+address !
+transport local
+nexthop MYHOSTNAME
+recipient MAILER-DAEMON@MYHOSTNAME
+flags CLASS_LOCAL
+address a!
+transport smtp
+nexthop RELAYHOST
+recipient @a.MYDOMAIN
+flags CLASS_DEFAULT
+address !b
+transport local
+nexthop MYHOSTNAME
+recipient b@MYHOSTNAME
+flags CLASS_LOCAL
+address a!b
+transport smtp
+nexthop RELAYHOST
+recipient b@a.MYDOMAIN
+flags CLASS_DEFAULT
+address !@
+transport local
+nexthop MYHOSTNAME
+recipient MAILER-DAEMON@MYHOSTNAME
+flags CLASS_LOCAL
+address a!@
+transport smtp
+nexthop RELAYHOST
+recipient @a.MYDOMAIN
+flags CLASS_DEFAULT
+address !b@
+transport local
+nexthop MYHOSTNAME
+recipient b@MYHOSTNAME
+flags CLASS_LOCAL
+address a!b@
+transport smtp
+nexthop RELAYHOST
+recipient b@a.MYDOMAIN
+flags CLASS_DEFAULT
+address %
+transport local
+nexthop MYHOSTNAME
+recipient MAILER-DAEMON@MYHOSTNAME
+flags CLASS_LOCAL
+address a%
+transport local
+nexthop MYHOSTNAME
+recipient a@MYHOSTNAME
+flags CLASS_LOCAL
+address %b
+transport smtp
+nexthop RELAYHOST
+recipient @b.MYDOMAIN
+flags CLASS_DEFAULT
+address a%b
+transport smtp
+nexthop RELAYHOST
+recipient a@b.MYDOMAIN
+flags CLASS_DEFAULT
+address %@
+transport local
+nexthop MYHOSTNAME
+recipient MAILER-DAEMON@MYHOSTNAME
+flags CLASS_LOCAL
+address a%@
+transport local
+nexthop MYHOSTNAME
+recipient a@MYHOSTNAME
+flags CLASS_LOCAL
+address %b@
+transport smtp
+nexthop RELAYHOST
+recipient @b.MYDOMAIN
+flags CLASS_DEFAULT
+address @@
+transport local
+nexthop MYHOSTNAME
+recipient MAILER-DAEMON@MYHOSTNAME
+flags CLASS_LOCAL
+address a@@
+transport local
+nexthop MYHOSTNAME
+recipient a@MYHOSTNAME
+flags CLASS_LOCAL
+address @b@
+transport smtp
+nexthop RELAYHOST
+recipient @b.MYDOMAIN
+flags CLASS_DEFAULT
+address a@b@
+transport smtp
+nexthop RELAYHOST
+recipient a@b.MYDOMAIN
+flags CLASS_DEFAULT
+address a%b@
+transport smtp
+nexthop RELAYHOST
+recipient a@b.MYDOMAIN
+flags CLASS_DEFAULT
+address a%b@MYHOSTNAME
+transport smtp
+nexthop RELAYHOST
+recipient a@b.MYDOMAIN
+flags CLASS_DEFAULT
+address a!b@MYHOSTNAME
+transport smtp
+nexthop RELAYHOST
+recipient b@a.MYDOMAIN
+flags CLASS_DEFAULT
+address a@b@MYHOSTNAME
+transport smtp
+nexthop RELAYHOST
+recipient a@b.MYDOMAIN
+flags CLASS_DEFAULT
+address a[b]@MYHOSTNAME@MYHOSTNAME
+transport local
+nexthop MYHOSTNAME
+recipient a[b]@MYHOSTNAME
+flags CLASS_LOCAL
+address a[b]%MYHOSTNAME@MYHOSTNAME
+transport local
+nexthop MYHOSTNAME
+recipient a[b]@MYHOSTNAME
+flags CLASS_LOCAL
+address a[b]%MYHOSTNAME%MYHOSTNAME
+transport local
+nexthop MYHOSTNAME
+recipient a[b]@MYHOSTNAME
+flags CLASS_LOCAL
+address MYHOSTNAME!a[b]@MYHOSTNAME
+transport local
+nexthop MYHOSTNAME
+recipient a [b]@MYHOSTNAME
+flags CLASS_LOCAL
+address MYHOSTNAME!a[b]%MYHOSTNAME
+transport local
+nexthop MYHOSTNAME
+recipient a [b]@MYHOSTNAME
+flags CLASS_LOCAL
+address MYHOSTNAME!MYHOSTNAME!a[b]
+transport local
+nexthop MYHOSTNAME
+recipient a [b]@MYHOSTNAME
+flags CLASS_LOCAL
+address user@dom.ain1@dom.ain2
+transport smtp
+nexthop RELAYHOST
+recipient user@dom.ain1@dom.ain2
+flags FLAG_ROUTED CLASS_DEFAULT
+address user%dom.ain1@dom.ain2
+transport smtp
+nexthop RELAYHOST
+recipient user%dom.ain1@dom.ain2
+flags FLAG_ROUTED CLASS_DEFAULT
+address dom.ain1!user@dom.ain2
+transport smtp
+nexthop RELAYHOST
+recipient dom.ain1!user@dom.ain2
+flags FLAG_ROUTED CLASS_DEFAULT
+address user@[1.2.3.4]@dom.ain2
+transport smtp
+nexthop RELAYHOST
+recipient user@[1.2.3.4]@dom.ain2
+flags FLAG_ROUTED CLASS_DEFAULT
+address user%[1.2.3.4]@dom.ain2
+transport smtp
+nexthop RELAYHOST
+recipient user%[1.2.3.4]@dom.ain2
+flags FLAG_ROUTED CLASS_DEFAULT
+address [1.2.3.4]!user@dom.ain2
+transport smtp
+nexthop RELAYHOST
+recipient [1.2.3.4]!user@dom.ain2
+flags FLAG_ROUTED CLASS_DEFAULT
+address user@localhost.MYDOMAIN
+transport local
+nexthop MYHOSTNAME
+recipient user@localhost.MYDOMAIN
+flags CLASS_LOCAL
+address user@[321.1.2.3]
+transport smtp
+nexthop RELAYHOST
+recipient user@[321.1.2.3]
+flags FLAG_ERROR CLASS_DEFAULT
+address user@1.2.3
+transport smtp
+nexthop RELAYHOST
+recipient user@1.2.3
+flags CLASS_DEFAULT
+address user@host:port
+transport smtp
+nexthop RELAYHOST
+recipient user@host:port
+flags FLAG_ERROR CLASS_DEFAULT
vstring_free(buffer);
}
vstring_free(reply);
+ exit(0);
}
#endif
--- /dev/null
+x !
+x a!
+x !b
+x a!b
+x %
+x a%
+x %b
+x a%b
+x @
+x a@
+x a@.
+x a@b
+x a@b.
--- /dev/null
+rule x
+address !
+result ""@
+rule x
+address a!
+result ""@a.MYDOMAIN
+rule x
+address !b
+result b@
+rule x
+address a!b
+result b@a.MYDOMAIN
+rule x
+address %
+result ""@
+rule x
+address a%
+result a@
+rule x
+address %b
+result ""@b.MYDOMAIN
+rule x
+address a%b
+result a@b.MYDOMAIN
+rule x
+address @
+result ""
+rule x
+address a@
+result a@
+rule x
+address a@.
+result a@.
+rule x
+address a@b
+result a@b.MYDOMAIN
+rule x
+address a@b.
+result a@b.
qmgr_message.o: ../../include/sent.h
qmgr_message.o: ../../include/deliver_completed.h
qmgr_message.o: ../../include/opened.h
-qmgr_message.o: ../../include/resolve_local.h
qmgr_message.o: ../../include/verp_sender.h
qmgr_message.o: ../../include/mail_proto.h
qmgr_message.o: ../../include/iostuff.h
#include <sent.h>
#include <deliver_completed.h>
#include <opened.h>
-#include <resolve_local.h>
#include <verp_sender.h>
#include <mail_proto.h>
* system can run without a local delivery agent. They'd still have
* to configure something for mail directed to the local postmaster,
* though, but that is an RFC requirement anyway.
+ *
+ * XXX This lookup should be done in the resolver, and the mail should
+ * be directed to a general-purpose null delivery agent.
*/
- if (at == 0 || resolve_local(at + 1)) {
+ if (reply.flags & RESOLVE_CLASS_LOCAL) {
if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
len) == 0
&& !var_double_bounce_sender[len]) {
qmgr_message.o: ../../include/sent.h
qmgr_message.o: ../../include/deliver_completed.h
qmgr_message.o: ../../include/opened.h
-qmgr_message.o: ../../include/resolve_local.h
qmgr_message.o: ../../include/verp_sender.h
qmgr_message.o: ../../include/mail_proto.h
qmgr_message.o: ../../include/iostuff.h
#include <sent.h>
#include <deliver_completed.h>
#include <opened.h>
-#include <resolve_local.h>
#include <verp_sender.h>
#include <mail_proto.h>
* system can run without a local delivery agent. They'd still have
* to configure something for mail directed to the local postmaster,
* though, but that is an RFC requirement anyway.
+ *
+ * XXX This lookup should be done in the resolver, and the mail should
+ * be directed to a general-purpose null delivery agent.
*/
- if (at == 0 || resolve_local(at + 1)) {
+ if (reply.flags & RESOLVE_CLASS_LOCAL) {
if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
len) == 0
&& !var_double_bounce_sender[len]) {
/* Recipient of protocol/policy/resource/software error notices.
/* .IP \fBhopcount_limit\fR
/* Limit the number of \fBReceived:\fR message headers.
-/* .IP \fBlocal_recipient_maps\fR
-/* List of maps with user names that are local to \fB$myorigin\fR
-/* or \fB$inet_interfaces\fR. If this parameter is defined,
-/* then the SMTP server rejects mail for unknown local users.
-/* .IP \fBrelay_recipient_maps\fR
-/* List of maps that define all the email addresses in the domains
-/* that match \fB$relay_domains\fR. If this parameter is defined,
-/* then the SMTP server rejects mail for unknown relay recipients.
/* .IP \fBnotify_classes\fR
/* List of error classes. Of special interest are:
/* .RS
/* This can be useful for testing purposes.
/* .IP \fBverp_delimiter_filter\fR
/* The characters that Postfix accepts as VERP delimiter characters.
+/* .SH "Known versus unknown recipients"
+/* .ad
+/* .fi
+/* .IP \fBunknown_local_recipient_reject_code\fR
+/* The response code when a client specifies a recipient whose domain
+/* matches \fB$mydestination\fR or \fB$inet_interfaces\fR, while
+/* \fB$local_recipient_maps\fR is non-empty and does not list
+/* the recipient address or address local-part.
+/* .IP \fBunknown_relay_recipient_reject_code\fR
+/* The response code when a client specifies a recipient whose domain
+/* matches \fB$relay_domains\fR, while \fB$relay_recipient_maps\fR
+/* is non-empty and does not list the recipient address.
+/* .IP \fBunknown_virtual_alias_reject_code\fR
+/* The response code when a client specifies a recipient whose domain
+/* matches \fB$virtual_alias_domains\fR, while the recipient is not
+/* listed in \fB$virtual_alias_maps\fR.
+/* .IP \fBunknown_virtual_mailbox_reject_code\fR
+/* The response code when a client specifies a recipient whose domain
+/* matches \fB$virtual_mailbox_domains\fR, while the recipient is not
+/* listed in \fB$virtual_mailbox_maps\fR.
/* .SH "Resource controls"
/* .ad
/* .fi
int var_smtpd_hist_thrsh;
char *var_smtpd_exp_filter;
char *var_def_rbl_reply;
-char *var_def_transport;
-char *var_error_transport;
-char *var_local_transport;
-char *var_relay_transport;
-char *var_virt_transport;
char *var_relay_rcpt_maps;
+int var_local_rcpt_code;
+int var_virt_alias_code;
+int var_virt_mailbox_code;
+int var_relay_rcpt_code;
/*
* Silly little macros.
VAR_NON_FQDN_CODE, DEF_NON_FQDN_CODE, &var_non_fqdn_code, 0, 0,
VAR_SMTPD_JUNK_CMD, DEF_SMTPD_JUNK_CMD, &var_smtpd_junk_cmd_limit, 1, 0,
VAR_SMTPD_HIST_THRSH, DEF_SMTPD_HIST_THRSH, &var_smtpd_hist_thrsh, 1, 0,
+ VAR_LOCAL_RCPT_CODE, DEF_LOCAL_RCPT_CODE, &var_local_rcpt_code, 0, 0,
+ VAR_VIRT_ALIAS_CODE, DEF_VIRT_ALIAS_CODE, &var_virt_alias_code, 0, 0,
+ VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code, 0, 0,
+ VAR_RELAY_RCPT_CODE, DEF_RELAY_RCPT_CODE, &var_relay_rcpt_code, 0, 0,
0,
};
static CONFIG_TIME_TABLE time_table[] = {
VAR_SMTPD_SND_AUTH_MAPS, DEF_SMTPD_SND_AUTH_MAPS, &var_smtpd_snd_auth_maps, 0, 0,
VAR_SMTPD_NOOP_CMDS, DEF_SMTPD_NOOP_CMDS, &var_smtpd_noop_cmds, 0, 0,
VAR_SMTPD_NULL_KEY, DEF_SMTPD_NULL_KEY, &var_smtpd_null_key, 0, 0,
- VAR_DEF_TRANSPORT, DEF_DEF_TRANSPORT, &var_def_transport, 1, 0,
- VAR_ERROR_TRANSPORT, DEF_ERROR_TRANSPORT, &var_error_transport, 1, 0,
- VAR_LOCAL_TRANSPORT, DEF_LOCAL_TRANSPORT, &var_local_transport, 1, 0,
- VAR_RELAY_TRANSPORT, DEF_RELAY_TRANSPORT, &var_relay_transport, 1, 0,
- VAR_VIRT_TRANSPORT, DEF_VIRT_TRANSPORT, &var_virt_transport, 1, 0,
VAR_RELAY_RCPT_MAPS, DEF_RELAY_RCPT_MAPS, &var_relay_rcpt_maps, 0, 0,
0,
};
* unknown recipients in simulated virtual domains will both resolve to
* "error:user unknown".
*/
- if (strcmp(STR(reply->transport), var_error_transport) == 0) {
+ if (strcmp(STR(reply->transport), MAIL_SERVICE_ERROR) == 0) {
(void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
- "%d <%s>: %s", 550,
+ "%d <%s>: %s",
+ (reply->flags & RESOLVE_CLASS_ALIAS) ?
+ var_virt_alias_code : 550,
recipient, STR(reply->nexthop));
SMTPD_CHECK_RCPT_RETURN(STR(error_text));
}
*/
if ((reply->flags & RESOLVE_CLASS_LOCAL)
&& *var_local_rcpt_maps
-#if 0
- && strcmp(STR(reply->transport), var_local_transport) == 0
-#endif
&& NOMATCH(local_rcpt_maps, CONST_STR(reply->recipient))) {
(void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
"%d <%s>: User unknown in local recipient table",
- 550, recipient);
+ var_local_rcpt_code, recipient);
SMTPD_CHECK_RCPT_RETURN(STR(error_text));
}
* Reject mail to unknown addresses in virtual mailbox domains.
*/
if ((reply->flags & RESOLVE_CLASS_VIRTUAL)
-#if 0
- && strcmp(STR(reply->transport), var_virt_transport) == 0
-#endif
&& NOMATCHV8(virt_mailbox_maps, CONST_STR(reply->recipient))) {
(void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
"%d <%s>: User unknown in virtual mailbox table",
- 550, recipient);
+ var_virt_mailbox_code, recipient);
SMTPD_CHECK_RCPT_RETURN(STR(error_text));
}
*/
if ((reply->flags & RESOLVE_CLASS_RELAY)
&& *var_relay_rcpt_maps
-#if 0
- && strcmp(STR(reply->transport), var_relay_transport) == 0
-#endif
&& NOMATCH(relay_rcpt_maps, CONST_STR(reply->recipient))) {
(void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
"%d <%s>: User unknown in relay recipient table",
- 550, recipient);
+ var_relay_rcpt_code, recipient);
SMTPD_CHECK_RCPT_RETURN(STR(error_text));
}
char *var_rbl_reply_maps;
char *var_smtpd_exp_filter;
char *var_def_rbl_reply;
-char *var_local_transport;
-char *var_error_transport;
-char *var_virt_transport;
-char *var_relay_transport;
-char *var_def_transport;
char *var_relay_rcpt_maps;
typedef struct {
VAR_RBL_REPLY_MAPS, DEF_RBL_REPLY_MAPS, &var_rbl_reply_maps,
VAR_SMTPD_EXP_FILTER, DEF_SMTPD_EXP_FILTER, &var_smtpd_exp_filter,
VAR_DEF_RBL_REPLY, DEF_DEF_RBL_REPLY, &var_def_rbl_reply,
- VAR_LOCAL_TRANSPORT, DEF_LOCAL_TRANSPORT, &var_local_transport,
- VAR_ERROR_TRANSPORT, DEF_ERROR_TRANSPORT, &var_error_transport,
- VAR_VIRT_TRANSPORT, DEF_VIRT_TRANSPORT, &var_virt_transport,
- VAR_RELAY_TRANSPORT, DEF_RELAY_TRANSPORT, &var_relay_transport,
- VAR_DEF_TRANSPORT, DEF_DEF_TRANSPORT, &var_def_transport,
VAR_RELAY_RCPT_MAPS, DEF_RELAY_RCPT_MAPS, &var_relay_rcpt_maps,
0,
};
int var_non_fqdn_code;
int var_smtpd_delay_reject;
int var_allow_untrust_route;
+int var_local_rcpt_code;
+int var_relay_rcpt_code;
+int var_virt_mailbox_code;
+int var_virt_alias_code;
static INT_TABLE int_table[] = {
"msg_verbose", 0, &msg_verbose,
VAR_NON_FQDN_CODE, DEF_NON_FQDN_CODE, &var_non_fqdn_code,
VAR_SMTPD_DELAY_REJECT, DEF_SMTPD_DELAY_REJECT, &var_smtpd_delay_reject,
VAR_ALLOW_UNTRUST_ROUTE, DEF_ALLOW_UNTRUST_ROUTE, &var_allow_untrust_route,
+ VAR_LOCAL_RCPT_CODE, DEF_LOCAL_RCPT_CODE, &var_local_rcpt_code,
+ VAR_RELAY_RCPT_CODE, DEF_RELAY_RCPT_CODE, &var_relay_rcpt_code,
+ VAR_VIRT_ALIAS_CODE, DEF_VIRT_ALIAS_CODE, &var_virt_alias_code,
+ VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code,
0,
};
domain += 1;
if (resolve_local(domain)) {
reply->flags = RESOLVE_CLASS_LOCAL;
- vstring_strcpy(reply->transport, var_local_transport);
+ vstring_strcpy(reply->transport, MAIL_SERVICE_LOCAL);
vstring_strcpy(reply->nexthop, domain);
} else if (string_list_match(virt_alias_doms, domain)) {
reply->flags = RESOLVE_CLASS_ALIAS;
- vstring_strcpy(reply->transport, var_error_transport);
+ vstring_strcpy(reply->transport, MAIL_SERVICE_ERROR);
vstring_strcpy(reply->nexthop, "user unknown");
} else if (string_list_match(virt_mailbox_doms, domain)) {
reply->flags = RESOLVE_CLASS_VIRTUAL;
- vstring_strcpy(reply->transport, var_virt_transport);
+ vstring_strcpy(reply->transport, MAIL_SERVICE_VIRTUAL);
vstring_strcpy(reply->nexthop, domain);
} else if (domain_list_match(relay_domains, domain)) {
reply->flags = RESOLVE_CLASS_RELAY;
- vstring_strcpy(reply->transport, var_relay_transport);
+ vstring_strcpy(reply->transport, MAIL_SERVICE_RELAY);
vstring_strcpy(reply->nexthop, domain);
} else {
reply->flags = RESOLVE_CLASS_DEFAULT;
- vstring_strcpy(reply->transport, var_def_transport);
+ vstring_strcpy(reply->transport, MAIL_SERVICE_SMTP);
vstring_strcpy(reply->nexthop, domain);
}
vstring_strcpy(reply->recipient, addr);
resolve.o: ../../include/split_at.h
resolve.o: ../../include/valid_hostname.h
resolve.o: ../../include/stringops.h
+resolve.o: ../../include/mymalloc.h
resolve.o: ../../include/mail_params.h
resolve.o: ../../include/mail_proto.h
resolve.o: ../../include/iostuff.h
transport.o: ../../include/maps.h
transport.o: ../../include/match_parent_style.h
transport.o: ../../include/match_ops.h
+transport.o: ../../include/mail_proto.h
+transport.o: ../../include/iostuff.h
+transport.o: ../../include/attr.h
transport.o: transport.h
trivial-rewrite.o: trivial-rewrite.c
trivial-rewrite.o: ../../include/sys_defs.h
#include <split_at.h>
#include <valid_hostname.h>
#include <stringops.h>
+#include <mymalloc.h>
/* Global library. */
* 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.
+ * socket pathname is not an adequate text as 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.
+ * In the code below, rcpt_domain specifies the domain name that we will use
+ * when the transport table specifies a non-default channel but no nexthop
+ * information (we use a generic text when that non-default channel is the
+ * error transport).
*/
#define STR vstring_str
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 */
{
char *myname = "resolve_addr";
VSTRING *addr_buf = vstring_alloc(100);
- TOK822 *tree;
+ TOK822 *tree = 0;
TOK822 *saved_domain = 0;
TOK822 *domain = 0;
char *destination;
const char *blame = 0;
const char *rcpt_domain;
+ int addr_len;
+ int loop_count;
+ int loop_max;
+ char *local;
+ char *oper;
+ char *junk;
- vstring_strcpy(saved_class_domain, "UNINITIALIZED SAVED_CLASS_DOMAIN");
*flags = 0;
+ vstring_strcpy(channel, "CHANNEL NOT UPDATED");
+ vstring_strcpy(nexthop, "NEXTHOP NOT UPDATED");
+ vstring_strcpy(nextrcpt, "NEXTRCPT NOT UPDATED");
/*
- * The address is in internalized (unquoted) form, so we must externalize
- * it first before we can parse it.
+ * The address is in internalized (unquoted) form.
+ *
+ * In an ideal world we would parse the externalized address form as given
+ * to us by the sender.
+ *
+ * However, in the real world we have to look for routing characters like
+ * %@! in the address local-part, even when that information is quoted
+ * due to the presence of special characters or whitespace. Although
+ * technically incorrect, this is needed to stop user@domain@domain relay
+ * attempts when forwarding mail to a Sendmail MX host.
*
- * While quoting the address local part, do not treat @ as a special
- * character. This allows us to detect extra @ characters and block
- * source routed relay attempts.
+ * This suggests that we parse the address in internalized (unquoted) form.
+ * Unfortunately, if we do that, the unparser generates incorrect white
+ * space between adjacent non-operator tokens. Example: ``first last''
+ * needs white space, but ``stuff[stuff]'' does not. This is is not a
+ * problem when unparsing the result from parsing externalized forms,
+ * because the parser/unparser were designed for valid externalized forms
+ * where ``stuff[stuff]'' does not happen.
*
- * But practically, we have to look at the unquoted form so that routing
- * characters like @ remain visible, in order to stop user@domain@domain
- * relay attempts when forwarding mail to a primary Sendmail MX host.
+ * As a workaround we start with the quoted form and then dequote the
+ * local-part only where needed. This will do the right thing in most
+ * (but not all) cases.
*/
- if (var_resolve_dequoted) {
- tree = tok822_scan_addr(addr);
- } else {
- quote_822_local(addr_buf, addr);
- tree = tok822_scan_addr(vstring_str(addr_buf));
+ addr_len = strlen(addr);
+ quote_822_local(addr_buf, addr);
+ tree = tok822_scan_addr(vstring_str(addr_buf));
+
+ /*
+ * Let the optimizer replace multiple expansions of this macro by a GOTO
+ * to a single instance.
+ */
+#define FREE_MEMORY_AND_RETURN { \
+ if (saved_domain) \
+ tok822_free_tree(saved_domain); \
+ if(tree) \
+ tok822_free_tree(tree); \
+ if (addr_buf) \
+ vstring_free(addr_buf); \
}
/*
* Preliminary resolver: strip off all instances of the local domain.
* Terminate when no destination domain is left over, or when the
* destination domain is remote.
+ *
+ * XXX To whom it may concern. If you change the resolver loop below, or
+ * quote_822_local.c, or tok822_parse.c, be sure to re-run the tests
+ * under "make resolve_clnt_test" in the global directory.
*/
#define RESOLVE_LOCAL(domain) \
resolve_local(STR(tok822_internalize(addr_buf, domain, TOK822_STR_DEFL)))
- while (tree->head) {
+ dict_errno = 0;
+
+ for (loop_count = 0, loop_max = addr_len + 100; /* void */ ; loop_count++) {
+
+ /*
+ * Grr. resolve_local() table lookups may fail. It may be OK for
+ * local file lookup code to abort upon failure, but with
+ * network-based tables it is preferable to return an error
+ * indication to the requestor.
+ */
+ if (dict_errno) {
+ *flags |= RESOLVE_FLAG_FAIL;
+ FREE_MEMORY_AND_RETURN;
+ }
+
+ /*
+ * XXX Should never happen, but if this happens with some
+ * pathological address, then that is not sufficient reason to
+ * disrupt the operation of an MTA.
+ */
+ if (loop_count > loop_max) {
+ msg_warn("resolve_addr: <%s>: giving up after %d iterations",
+ addr, loop_count);
+ break;
+ }
/*
* Strip trailing dot at end of domain, but not dot-dot. This merely
* makes diagnostics more accurate by leaving bogus addresses alone.
*/
- if (tree->tail->type == '.'
+ if (tree->tail
+ && tree->tail->type == '.'
&& tok822_rfind_type(tree->tail, '@') != 0
&& tree->tail->prev->type != '.')
tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));
/*
* Strip trailing @.
*/
- if (tree->tail->type == '@') {
+ if (tree->tail
+ && tree->tail->type == '@')
tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));
- continue;
- }
-
- /*
- * A lone empty string becomes the postmaster.
- */
- if (tree->head == tree->tail && tree->head->type == TOK822_QSTRING
- && VSTRING_LEN(tree->head->vstr) == 0) {
- tok822_free(tree->head);
- tree->head = tok822_scan(MAIL_ADDR_POSTMASTER, &tree->tail);
- rewrite_tree(REWRITE_CANON, tree);
- }
/*
* Strip (and save) @domain if local.
*/
if ((domain = tok822_rfind_type(tree->tail, '@')) != 0) {
- if (RESOLVE_LOCAL(domain->next) == 0)
+ if (domain->next && RESOLVE_LOCAL(domain->next) == 0)
break;
tok822_sub_keep_before(tree, domain);
if (saved_domain)
* After stripping the local domain, if any, replace foo%bar by
* foo@bar, site!user by user@site, rewrite to canonical form, and
* retry.
- *
- * Otherwise we're done.
*/
if (tok822_rfind_type(tree->tail, '@')
|| (var_swap_bangpath && tok822_rfind_type(tree->tail, '!'))
|| (var_percent_hack && tok822_rfind_type(tree->tail, '%'))) {
rewrite_tree(REWRITE_CANON, tree);
- } else {
- domain = 0;
- break;
+ continue;
+ }
+
+ /*
+ * If the local-part is a quoted string, crack it open when we're
+ * permitted to do so and look for routing operators. This is
+ * technically incorrect, but is needed to stop relaying problems.
+ *
+ * XXX Do another feeble attempt to keep local-part info quoted.
+ */
+ if (var_resolve_dequoted
+ && tree->head && tree->head == tree->tail
+ && tree->head->type == TOK822_QSTRING
+ && ((oper = strrchr(local = STR(tree->head->vstr), '@')) != 0
+ || (var_percent_hack && (oper = strrchr(local, '%')) != 0)
+ || (var_swap_bangpath && (oper = strrchr(local, '!')) != 0))) {
+ if (*oper == '%')
+ *oper = '@';
+ tok822_internalize(addr_buf, tree->head, TOK822_STR_DEFL);
+ if (*oper == '@') {
+ junk = mystrdup(STR(addr_buf));
+ quote_822_local(addr_buf, junk);
+ myfree(junk);
+ }
+ tok822_free(tree->head);
+ tree->head = tok822_scan(STR(addr_buf), &tree->tail);
+ rewrite_tree(REWRITE_CANON, tree);
+ continue;
+ }
+
+ /*
+ * An empty local-part or an empty quoted string local-part becomes
+ * the local MAILER-DAEMON, for consistency with our own From:
+ * message headers.
+ */
+ if (tree->head && tree->head == tree->tail
+ && tree->head->type == TOK822_QSTRING
+ && VSTRING_LEN(tree->head->vstr) == 0) {
+ tok822_free(tree->head);
+ tree->head = 0;
}
+ if (tree->head == 0)
+ tree->head = tok822_scan(MAIL_ADDR_MAIL_DAEMON, &tree->tail);
+
+ /*
+ * We're done. There are no domains left to strip off the address,
+ * and all null local-part information is sanitized.
+ */
+ domain = 0;
+ break;
}
- /*
- * If the destination is non-local, recognize routing operators in the
- * address localpart. This is needed to prevent backup MX hosts from
- * relaying third-party destinations through primary MX hosts, otherwise
- * the backup host could end up on black lists. Ignore local
- * swap_bangpath and percent_hack settings because we can't know how the
- * primary MX host is set up.
- */
- if (domain && domain->prev)
- if (tok822_rfind_type(domain->prev, '@') != 0
- || tok822_rfind_type(domain->prev, '!') != 0
- || tok822_rfind_type(domain->prev, '%') != 0)
- *flags |= RESOLVE_FLAG_ROUTED;
+ vstring_free(addr_buf);
+ addr_buf = 0;
/*
* Make sure the resolved envelope recipient has the user@domain form. If
if (saved_domain) {
tok822_sub_append(tree, saved_domain);
saved_domain = 0;
- } else { /* Aargh! Always! */
+ } else {
tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
tok822_sub_append(tree, tok822_scan(var_myhostname, (TOK822 **) 0));
}
}
+
+ /*
+ * Transform the recipient address back to internal form.
+ *
+ * XXX This may produce incorrect results if we cracked open a quoted
+ * local-part with routing operators; see discussion above at the top of
+ * the big loop.
+ */
tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL);
+ rcpt_domain = strrchr(STR(nextrcpt), '@') + 1;
+ if (*rcpt_domain == '[' ? !valid_hostliteral(rcpt_domain, DONT_GRIPE) :
+ !valid_hostname(rcpt_domain, DONT_GRIPE))
+ *flags |= RESOLVE_FLAG_ERROR;
+ tok822_free_tree(tree);
+ tree = 0;
+
+ /*
+ * Recognize routing operators in the local-part, even when we do not
+ * recognize ! or % as valid routing operators locally. This is needed to
+ * prevent backup MX hosts from relaying third-party destinations through
+ * primary MX hosts, otherwise the backup host could end up on black
+ * lists. Ignore local swap_bangpath and percent_hack settings because we
+ * can't know how the next MX host is set up.
+ */
+ if (strcmp(STR(nextrcpt) + strcspn(STR(nextrcpt), "@!%") + 1, rcpt_domain))
+ *flags |= RESOLVE_FLAG_ROUTED;
/*
- * With relay or other non-local destinations, the relayhost setting
- * overrides the destination domain name.
+ * With local, virtual, relay, or other non-local destinations, give the
+ * highest precedence to transport associated nexthop information.
*
- * With virtual, relay, or other non-local destinations, give the highest
- * precedence to delivery transport associated next-hop information.
+ * Otherwise, with relay or other non-local destinations, the relayhost
+ * setting overrides the destination domain name.
*
- * XXX With the virtual mailbox transport, set the nexthop information to
- * $myhostname, so that in default configurations the virtual delivery
- * agent will not use separate queues for every $virtual_mailbox_domains
- * domain name. That prevents anomalies where many low-traffic domains
- * starve a high-traffic domain.
+ * XXX Nag if the recipient domain is listed in multiple domain lists. The
+ * result is implementation defined, and may break when internals change.
*
- * XXX Nag if the domain is listed in multiple domain lists. The effect is
- * implementation defined, and may break when internals change.
+ * For now, we distinguish only a fixed number of address classes.
+ * Eventually this may become extensible, so that new classes can be
+ * configured with their own domain list, delivery transport, and
+ * recipient table.
*/
#define STREQ(x,y) (strcmp((x), (y)) == 0)
dict_errno = 0;
if (domain != 0) {
- tok822_internalize(nexthop, domain->next, TOK822_STR_DEFL);
- 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;
+
+ /*
+ * Virtual alias domain.
+ */
if (virt_alias_doms
- && string_list_match(virt_alias_doms, STR(nexthop))) {
+ && string_list_match(virt_alias_doms, rcpt_domain)) {
if (var_helpful_warnings
&& virt_mailbox_doms
- && string_list_match(virt_mailbox_doms, STR(nexthop)))
+ && string_list_match(virt_mailbox_doms, rcpt_domain))
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, var_error_transport);
+ rcpt_domain, VAR_VIRT_ALIAS_DOMS,
+ VAR_VIRT_MAILBOX_DOMS);
+ vstring_strcpy(channel, MAIL_SERVICE_ERROR);
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) {
msg_warn("%s lookup failure", VAR_VIRT_ALIAS_DOMS);
*flags |= RESOLVE_FLAG_FAIL;
- } else if (virt_mailbox_doms
- && string_list_match(virt_mailbox_doms, STR(nexthop))) {
+ FREE_MEMORY_AND_RETURN;
+ }
+
+ /*
+ * Virtual mailbox domain.
+ */
+ else if (virt_mailbox_doms
+ && string_list_match(virt_mailbox_doms, rcpt_domain)) {
vstring_strcpy(channel, var_virt_transport);
- vstring_strcpy(nexthop, var_myhostname);
- vstring_strcpy(saved_class_domain, var_myhostname);
+ vstring_strcpy(nexthop, rcpt_domain);
blame = VAR_VIRT_TRANSPORT;
*flags |= RESOLVE_CLASS_VIRTUAL;
} else if (dict_errno != 0) {
msg_warn("%s lookup failure", VAR_VIRT_MAILBOX_DOMS);
*flags |= RESOLVE_FLAG_FAIL;
+ FREE_MEMORY_AND_RETURN;
} else {
+
+ /*
+ * Off-host relay destination.
+ */
if (relay_domains
- && domain_list_match(relay_domains, STR(nexthop))) {
+ && domain_list_match(relay_domains, rcpt_domain)) {
vstring_strcpy(channel, var_relay_transport);
blame = VAR_RELAY_TRANSPORT;
*flags |= RESOLVE_CLASS_RELAY;
} else if (dict_errno != 0) {
msg_warn("%s lookup failure", VAR_RELAY_DOMAINS);
*flags |= RESOLVE_FLAG_FAIL;
- } else {
+ FREE_MEMORY_AND_RETURN;
+ }
+
+ /*
+ * Other off-host destination.
+ */
+ else {
vstring_strcpy(channel, var_def_transport);
blame = VAR_DEF_TRANSPORT;
*flags |= RESOLVE_CLASS_DEFAULT;
}
- if (*var_relayhost) {
+
+ /*
+ * With off-host delivery, relayhost overrides recipient domain.
+ */
+ if (*var_relayhost)
vstring_strcpy(nexthop, var_relayhost);
- 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);
- if (!STREQ(STR(channel), var_error_transport))
- vstring_strcpy(saved_class_domain, STR(nexthop));
+ else
+ vstring_strcpy(nexthop, rcpt_domain);
}
}
/*
- * Local delivery. Set up the default local transport and the default
- * next-hop hostname (myself).
- *
- * XXX Set the nexthop information to myhostname, so that the local delivery
- * agent does not get a queue for every domain name in $mydestination or
- * for every network address in $inet_interfaces.
+ * Local delivery.
*
* XXX Nag if the domain is listed in multiple domain lists. The effect is
* implementation defined, and may break when internals change.
*/
else {
- if (var_helpful_warnings
- && (rcpt_domain = strrchr(STR(nextrcpt), '@')) != 0) {
- rcpt_domain++;
+ if (var_helpful_warnings) {
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_MAILBOX_DOMS);
}
vstring_strcpy(channel, var_local_transport);
+ vstring_strcpy(nexthop, rcpt_domain);
blame = VAR_LOCAL_TRANSPORT;
- if ((destination = split_at(STR(channel), ':')) == 0
- || *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;
}
+ /*
+ * An explicit main.cf transport:nexthop setting overrides the nexthop.
+ *
+ * XXX We depend on this mechanism to enforce per-recipient concurrencies
+ * for local recipients. With "local_transport = local:$myhostname" we
+ * force mail for any domain in $mydestination/$inet_interfaces to share
+ * the same queue.
+ */
+ if ((destination = split_at(STR(channel), ':')) != 0 && *destination)
+ vstring_strcpy(nexthop, destination);
+
/*
* Sanity checks.
*/
- if ((*flags & RESOLVE_FLAG_FAIL) == 0) {
- if (*STR(channel) == 0) {
- if (blame == 0)
- msg_panic("%s: null blame", myname);
- msg_warn("file %s/%s: parameter %s: null transport is not allowed",
- var_config_dir, MAIN_CONF_FILE, blame);
- *flags |= RESOLVE_FLAG_FAIL;
- }
- if (*STR(nexthop) == 0)
- msg_panic("%s: null nexthop", myname);
+ if (*STR(channel) == 0) {
+ if (blame == 0)
+ msg_panic("%s: null blame", myname);
+ msg_warn("file %s/%s: parameter %s: null transport is not allowed",
+ var_config_dir, MAIN_CONF_FILE, blame);
+ *flags |= RESOLVE_FLAG_FAIL;
+ FREE_MEMORY_AND_RETURN;
}
+ if (*STR(nexthop) == 0)
+ msg_panic("%s: null nexthop", myname);
/*
- * The transport map overrides any transport and next-hop host info that
- * is set up above.
+ * The transport map can selectively override any transport and/or
+ * nexthop host info that is set up above. Unfortunately, the syntax for
+ * nexthop information is transport specific. We therefore need sane and
+ * intuitive semantics for transport map entries that specify a channel
+ * but no nexthop.
+ *
+ * With non-error transports, the initial nexthop information is the
+ * recipient domain. However, specific main.cf transport definitions may
+ * specify a transport-specific destination, such as a host + TCP socket,
+ * or the pathname of a UNIX-domain socket. With less precedence than
+ * main.cf transport definitions, a main.cf relayhost definition may also
+ * override nexthop information for off-host deliveries.
*
- * 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.
+ * With the error transport, the nexthop information is free text that
+ * specifies the reason for non-delivery.
+ *
+ * Because nexthop syntax is transport specific we reset the nexthop
+ * information to the recipient domain when the transport table specifies
+ * a transport without also specifying the nexthop information.
+ *
+ * Subtle note: reset nexthop even when the transport table does not change
+ * the transport. Otherwise it is hard to get rid of main.cf specified
+ * nexthop information.
*/
- if ((*flags & RESOLVE_FLAG_FAIL) == 0 && *var_transport_maps) {
- 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) {
+ if (*var_transport_maps) {
+ if (transport_lookup(STR(nextrcpt), rcpt_domain, channel, nexthop) == 0
+ && dict_errno != 0) {
msg_warn("%s lookup failure", VAR_TRANSPORT_MAPS);
*flags |= RESOLVE_FLAG_FAIL;
+ FREE_MEMORY_AND_RETURN;
}
}
*/
#define IGNORE_ADDR_EXTENSION ((char **) 0)
- if ((*flags & RESOLVE_FLAG_FAIL) == 0 && relocated_maps != 0) {
+ if (relocated_maps != 0) {
const char *newloc;
if ((newloc = mail_addr_find(relocated_maps, STR(nextrcpt),
IGNORE_ADDR_EXTENSION)) != 0) {
- vstring_strcpy(channel, var_error_transport);
+ vstring_strcpy(channel, MAIL_SERVICE_ERROR);
vstring_sprintf(nexthop, "User has moved to %s", newloc);
} else if (dict_errno != 0) {
msg_warn("%s lookup failure", VAR_RELOCATED_MAPS);
*flags |= RESOLVE_FLAG_FAIL;
+ FREE_MEMORY_AND_RETURN;
}
}
/*
* Clean up.
*/
- if (saved_domain)
- tok822_free_tree(saved_domain);
- tok822_free_tree(tree);
- vstring_free(addr_buf);
+ FREE_MEMORY_AND_RETURN;
}
/* Static, so they can be used by the network protocol interface only. */
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 =
TOK822 *bang;
TOK822 *local;
+ /*
+ * XXX If you change this module, quote_822_local.c, or tok822_parse.c,
+ * be sure to re-run the tests under "make rewrite_clnt_test" and "make
+ * resolve_clnt_test" in the global directory.
+ */
+
/*
* Sanity check.
*/
}
/*
- * Append missing .domain
+ * Append missing .domain, but leave broken forms ending in @ alone. This
+ * merely makes diagnostics more accurate by leaving bogus addresses
+ * alone.
*/
if (var_append_dot_mydomain != 0
&& (domain = tok822_rfind_type(tree->tail, '@')) != 0
+ && domain != tree->tail
&& tok822_find_type(domain, TOK822_DOMLIT) == 0
&& tok822_find_type(domain, '.') == 0) {
tok822_sub_append(tree, tok822_alloc('.', (char *) 0));
}
/*
- * Strip trailing dot at end of domain, but not dot-dot. This merely
- * makes diagnostics more accurate by leaving bogus addresses alone.
+ * Strip trailing dot at end of domain, but not dot-dot or @-dot. This
+ * merely makes diagnostics more accurate by leaving bogus addresses
+ * alone.
*/
+#if 0
if (tree->tail->type == '.'
- && tok822_rfind_type(tree->tail, '@') != 0
- && tree->tail->prev->type != '.')
+ && tree->tail->prev
+ && tree->tail->prev->type != '.'
+ && tree->tail->prev->type != '@')
tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));
+#endif
}
/* rewrite_addr - rewrite address according to rule set */
/*
/* void transport_init()
/*
-/* int transport_lookup(address, channel, nexthop)
+/* int transport_lookup(address, rcpt_domain, channel, nexthop)
/* const char *address;
+/* const char *rcpt_domain;
/* VSTRING *channel;
/* VSTRING *nexthop;
/* DESCRIPTION
#include <mail_params.h>
#include <maps.h>
#include <match_parent_style.h>
+#include <mail_proto.h>
/* Application-specific. */
#define STR(x) vstring_str(x)
- /*
- * Macro for consistent updates of the transport and nexthop results.
- */
-#define UPDATE_IF_SPECIFIED(dst, src) do { \
- if ((src) && *(src)) vstring_strcpy((dst), (src)); \
- } while (0)
-
/* transport_init - pre-jail initialization */
void transport_init(void)
transport_match_parent_style = match_parent_style(VAR_TRANSPORT_MAPS);
}
+/* update_entry - update from transport table entry */
+
+static void update_entry(const char *new_channel, const char *new_nexthop,
+ const char *rcpt_domain, VSTRING *channel,
+ VSTRING *nexthop)
+{
+
+ /*
+ * :[nexthop] means don't change the channel, and don't change the
+ * nexthop unless a non-default nexthop is specified. Thus, a right-hand
+ * side of ":" is the transport table equivalent of a NOOP.
+ */
+ if (*new_channel == 0) { /* :[nexthop] */
+ if (*new_nexthop != 0)
+ vstring_strcpy(nexthop, new_nexthop);
+ }
+
+ /*
+ * transport[:[nexthop]] means change the channel, and reset the nexthop
+ * to the default unless a non-default nexthop is specified.
+ */
+ else {
+ vstring_strcpy(channel, new_channel);
+ if (*new_nexthop != 0)
+ vstring_strcpy(nexthop, new_nexthop);
+ else if (strcmp(STR(channel), MAIL_SERVICE_ERROR) != 0)
+ vstring_strcpy(nexthop, rcpt_domain);
+ else
+ vstring_strcpy(nexthop, "Address is undeliverable");
+ }
+}
+
/* find_transport_entry - look up and parse transport table entry */
-static int find_transport_entry(const char *key, int flags,
- VSTRING *channel, VSTRING *nexthop)
+static int find_transport_entry(const char *key, const char *rcpt_domain,
+ int flags, VSTRING *channel, VSTRING *nexthop)
{
char *saved_value;
const char *host;
const char *value;
+ /*
+ * Reset previous error history.
+ */
+ dict_errno = 0;
+
#define FOUND 1
#define NOTFOUND 0
*
* XXX Should report lookup failure status to caller instead of aborting.
*/
- if ((value = maps_find(transport_path, key, flags)) == 0) {
- if (dict_errno != 0)
- msg_fatal("transport table lookup problem.");
+ if ((value = maps_find(transport_path, key, flags)) == 0)
return (NOTFOUND);
- }
/*
* It would be great if we could specify a recipient address in the
else {
saved_value = mystrdup(value);
host = split_at(saved_value, ':');
- UPDATE_IF_SPECIFIED(nexthop, host);
- UPDATE_IF_SPECIFIED(channel, saved_value);
+ update_entry(saved_value, host ? host : "", rcpt_domain,
+ channel, nexthop);
myfree(saved_value);
return (FOUND);
}
#define FULL 0
#define PARTIAL DICT_FLAG_FIXED
- if (find_transport_entry(WILDCARD, FULL, channel, nexthop)) {
+ if (find_transport_entry(WILDCARD, "", FULL, channel, nexthop)) {
wildcard_channel = channel;
wildcard_nexthop = nexthop;
if (msg_verbose)
msg_info("wildcard_{chan:hop}={%s:%s}",
vstring_str(wildcard_channel), vstring_str(wildcard_nexthop));
} else {
+ if (dict_errno != 0)
+ msg_fatal("transport table initialization problem.");
vstring_free(channel);
vstring_free(nexthop);
}
/* transport_lookup - map a transport domain */
-int transport_lookup(const char *addr, VSTRING *channel, VSTRING *nexthop)
+int transport_lookup(const char *addr, const char *rcpt_domain,
+ VSTRING *channel, VSTRING *nexthop)
{
- char *full_addr = lowercase(mystrdup(*addr ? addr : var_xport_null_key));
+ char *full_addr;
char *stripped_addr;
char *ratsign = 0;
const char *name;
#define STREQ(x,y) (strcmp((x), (y)) == 0)
#define DISCARD_EXTENSION ((char **) 0)
+ /*
+ * The null recipient is rewritten to the local mailer daemon address.
+ */
+ if (*addr == 0) {
+ msg_warn("transport_lookup: null address - skipping table lookup");
+ return (NOTFOUND);
+ }
+ full_addr = lowercase(mystrdup(addr));
+
/*
* The optimizer will replace multiple instances of this macro expansion
* by gotos to a single instance that does the same thing.
return (x); \
}
- /*
- * If this is a special address such as <> do only one lookup of the full
- * string. Specify the FULL flag to include regexp maps in the query.
- */
- if (STREQ(full_addr, var_xport_null_key)) {
- if (find_transport_entry(full_addr, FULL, channel, nexthop))
- RETURN_FREE(FOUND);
- RETURN_FREE(NOTFOUND);
- }
-
/*
* Look up the full address with the FULL flag to include regexp maps in
* the query.
if ((ratsign = strrchr(full_addr, '@')) == 0 || ratsign[1] == 0)
msg_panic("transport_lookup: bad address: \"%s\"", full_addr);
- if (find_transport_entry(full_addr, FULL, channel, nexthop))
+ if (find_transport_entry(full_addr, rcpt_domain, FULL, channel, nexthop))
RETURN_FREE(FOUND);
+ if (dict_errno != 0)
+ RETURN_FREE(NOTFOUND);
/*
* If the full address did not match, and there is an address extension,
*/
if ((stripped_addr = strip_addr(full_addr, DISCARD_EXTENSION,
*var_rcpt_delim)) != 0) {
- if (find_transport_entry(stripped_addr, PARTIAL,
- channel, nexthop)) {
- myfree(stripped_addr);
+ found = find_transport_entry(stripped_addr, rcpt_domain, PARTIAL,
+ channel, nexthop);
+
+ myfree(stripped_addr);
+ if (found)
RETURN_FREE(FOUND);
- } else {
- myfree(stripped_addr);
- }
+ if (dict_errno != 0)
+ RETURN_FREE(NOTFOUND);
}
/*
* Specify that the lookup key is partial, to avoid matching partial keys
* with regular expressions.
*/
- for (found = 0, name = ratsign + 1; /* void */ ; name = next) {
- if (find_transport_entry(name, PARTIAL, channel, nexthop))
+ for (name = ratsign + 1; /* void */ ; name = next) {
+ if (find_transport_entry(name, rcpt_domain, PARTIAL, channel, nexthop))
RETURN_FREE(FOUND);
+ if (dict_errno != 0)
+ RETURN_FREE(NOTFOUND);
if ((next = strchr(name + 1, '.')) == 0)
break;
if (transport_match_parent_style == MATCH_FLAG_PARENT)
* Fall back to the wild-card entry.
*/
if (wildcard_channel) {
- UPDATE_IF_SPECIFIED(channel, STR(wildcard_channel));
- UPDATE_IF_SPECIFIED(nexthop, STR(wildcard_nexthop));
+ update_entry(STR(wildcard_channel), STR(wildcard_nexthop),
+ rcpt_domain, channel, nexthop);
RETURN_FREE(FOUND);
}
*/
extern void transport_init(void);
extern void transport_wildcard_init(void);
-extern int transport_lookup(const char *, VSTRING *, VSTRING *);
+extern int transport_lookup(const char *, const char *, VSTRING *, VSTRING *);
/* LICENSE
/* .ad
/* .sp
/* Syntax is \fItransport\fR:\fInexthop\fR; see \fBtransport\fR(5)
/* for details. The :\fInexthop\fR part is optional.
-/* .IP \fBerror_transport\fR
-/* Where to deliver mail for non-existent recipients in domains
-/* that match \fBvirtual_alias_domains\fR (all recipients
-/* in simulated virtual domains must be aliased to some other
-/* local or remote domain), or for recipients that have moved.
-/* The default transport is \fBerror\fR.
-/* .sp
-/* Syntax is \fItransport\fR:\fInexthop\fR; see \fBtransport\fR(5)
-/* for details. The :\fInexthop\fR part is optional.
/* .IP \fBvirtual_transport\fR
/* Where to deliver mail for non-local domains that match
/* \fB$virtual_mailbox_domains\fR.
/* .IP \fBtransport_maps\fR
/* List of tables with \fIdomain\fR to (\fItransport, nexthop\fR)
/* mappings.
-/* .IP \fBtransport_null_address_lookup_key\fR
-/* Lookup key to be used for the null address.
/* SEE ALSO
/* master(8) process manager
/* syslogd(8) system logging
bool var_append_at_myorigin;
bool var_percent_hack;
char *var_local_transport;
-char *var_error_transport;
char *var_virt_transport;
char *var_relay_transport;
int var_resolve_dequoted;
-char *var_xport_null_key;
char *var_virt_alias_maps; /* XXX virtual_alias_domains */
char *var_virt_mailbox_maps; /* XXX virtual_mailbox_domains */
char *var_virt_alias_doms;
static CONFIG_STR_TABLE str_table[] = {
VAR_TRANSPORT_MAPS, DEF_TRANSPORT_MAPS, &var_transport_maps, 0, 0,
VAR_LOCAL_TRANSPORT, DEF_LOCAL_TRANSPORT, &var_local_transport, 1, 0,
- VAR_ERROR_TRANSPORT, DEF_ERROR_TRANSPORT, &var_error_transport, 1, 0,
VAR_VIRT_TRANSPORT, DEF_VIRT_TRANSPORT, &var_virt_transport, 1, 0,
VAR_RELAY_TRANSPORT, DEF_RELAY_TRANSPORT, &var_relay_transport, 1, 0,
- VAR_XPORT_NULL_KEY, DEF_XPORT_NULL_KEY, &var_xport_null_key, 1, 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_mailbox_maps, 0, 0,
/* int valid_hostaddr(addr, gripe)
/* const char *addr;
/* int gripe;
+/*
+/* int valid_hostliteral(addr, gripe)
+/* const char *addr;
+/* int gripe;
/* DESCRIPTION
/* valid_hostname() scrutinizes a hostname: the name should be no
/* longer than VALID_HOSTNAME_LEN characters, should contain only
/* no leading or trailing dots or hyphens, no labels longer than
/* VALID_LABEL_LEN characters, and no numeric top-level domain.
/*
-/* valid_hostaddr() requirs that the input is a valid string
+/* valid_hostaddr() requires that the input is a valid string
/* representation of an internet network address.
/*
+/* valid_hostliteral() requires an address enclosed in [].
+/*
/* These routines operate silently unless the gripe parameter
/* specifies a non-zero value. The macros DO_GRIPE and DONT_GRIPE
/* provide suitable constants.
return (1);
}
+/* valid_hostliteral - validate address literal */
+
+int valid_hostliteral(const char *addr, int gripe)
+{
+ const char *myname = "valid_hostliteral";
+ char buf[100];
+ const char *last;
+
+ if (*addr != '[') {
+ if (gripe)
+ msg_warn("%s: '[' expected at start: %.100s", myname, addr);
+ return (0);
+ }
+ if ((last = strchr(addr, ']')) == 0) {
+ if (gripe)
+ msg_warn("%s: ']' expected at end: %.100s", myname, addr);
+ return (0);
+ }
+ if (last[1]) {
+ if (gripe)
+ msg_warn("%s: unexpected text after ']': %.100s", myname, addr);
+ return (0);
+ }
+ if (last - addr >= sizeof(buf)) {
+ if (gripe)
+ msg_warn("%s: too much text: %.100s", myname, addr);
+ return (0);
+ }
+ strncpy(buf, addr + 1, last - addr - 1);
+ buf[last - addr - 1] = 0;
+ return (valid_hostaddr(buf, gripe));
+}
+
#ifdef TEST
/*
msg_info("testing: \"%s\"", vstring_str(buffer));
valid_hostname(vstring_str(buffer), DO_GRIPE);
valid_hostaddr(vstring_str(buffer), DO_GRIPE);
+ valid_hostliteral(vstring_str(buffer), DO_GRIPE);
}
exit(0);
}
extern int valid_hostname(const char *, int);
extern int valid_hostaddr(const char *, int);
+extern int valid_hostliteral(const char *, int);
/* LICENSE
/* .ad
a.b.-bb
a.b.bb-
a-a.b-b
+[1.2.3.4]
+[321.255.255.255]
+[1.2.3.4
+[1.2.3.4]foo
+[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
./valid_hostname: testing: "123456789012345678901234567890123456789012345678901234567890123"
./valid_hostname: warning: valid_hostname: numeric hostname: 123456789012345678901234567890123456789012345678901234567890123
./valid_hostname: warning: valid_hostaddr: invalid octet value: 123456789012345678901234567890123456789012345678901234567890123
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 123456789012345678901234567890123456789012345678901234567890123
./valid_hostname: testing: "1234567890123456789012345678901234567890123456789012345678901234"
./valid_hostname: warning: valid_hostname: hostname label too long: 1234567890123456789012345678901234567890123456789012345678901234
./valid_hostname: warning: valid_hostaddr: invalid octet value: 1234567890123456789012345678901234567890123456789012345678901234
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1234567890123456789012345678901234567890123456789012345678901234
./valid_hostname: testing: "a.123456789012345678901234567890123456789012345678901234567890123.b"
./valid_hostname: warning: valid_hostaddr: invalid character 97(decimal): a.123456789012345678901234567890123456789012345678901234567890123.b
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: a.123456789012345678901234567890123456789012345678901234567890123.b
./valid_hostname: testing: "a.1234567890123456789012345678901234567890123456789012345678901234.b"
./valid_hostname: warning: valid_hostname: hostname label too long: a.1234567890123456789012345678901234567890123456789012345678901234.b
./valid_hostname: warning: valid_hostaddr: invalid character 97(decimal): a.1234567890123456789012345678901234567890123456789012345678901234.b
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: a.1234567890123456789012345678901234567890123456789012345678901234.b
./valid_hostname: testing: "1.2.3.4"
./valid_hostname: warning: valid_hostname: numeric hostname: 1.2.3.4
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1.2.3.4
./valid_hostname: testing: "321.255.255.255"
./valid_hostname: warning: valid_hostname: numeric hostname: 321.255.255.255
./valid_hostname: warning: valid_hostaddr: invalid octet value: 321.255.255.255
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 321.255.255.255
./valid_hostname: testing: "0.0.0.0"
./valid_hostname: warning: valid_hostname: numeric hostname: 0.0.0.0
./valid_hostname: warning: valid_hostaddr: bad initial octet value: 0.0.0.0
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 0.0.0.0
./valid_hostname: testing: "255.255.255.255"
./valid_hostname: warning: valid_hostname: numeric hostname: 255.255.255.255
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 255.255.255.255
./valid_hostname: testing: "0.255.255.255"
./valid_hostname: warning: valid_hostname: numeric hostname: 0.255.255.255
./valid_hostname: warning: valid_hostaddr: bad initial octet value: 0.255.255.255
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 0.255.255.255
./valid_hostname: testing: "1.2.3.321"
./valid_hostname: warning: valid_hostname: numeric hostname: 1.2.3.321
./valid_hostname: warning: valid_hostaddr: invalid octet value: 1.2.3.321
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1.2.3.321
./valid_hostname: testing: "1.2.3"
./valid_hostname: warning: valid_hostname: numeric hostname: 1.2.3
./valid_hostname: warning: valid_hostaddr: invalid octet count: 1.2.3
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1.2.3
./valid_hostname: testing: "1.2.3.4.5"
./valid_hostname: warning: valid_hostname: numeric hostname: 1.2.3.4.5
./valid_hostname: warning: valid_hostaddr: invalid octet count: 1.2.3.4.5
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1.2.3.4.5
./valid_hostname: testing: "1..2.3.4"
./valid_hostname: warning: valid_hostname: misplaced delimiter: 1..2.3.4
./valid_hostname: warning: valid_hostaddr: misplaced dot: 1..2.3.4
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1..2.3.4
./valid_hostname: testing: ".1.2.3.4"
./valid_hostname: warning: valid_hostname: misplaced delimiter: .1.2.3.4
./valid_hostname: warning: valid_hostaddr: misplaced dot: .1.2.3.4
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: .1.2.3.4
./valid_hostname: testing: "1.2.3.4.5."
./valid_hostname: warning: valid_hostname: misplaced delimiter: 1.2.3.4.5.
./valid_hostname: warning: valid_hostaddr: misplaced dot: 1.2.3.4.5.
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1.2.3.4.5.
./valid_hostname: testing: "1"
./valid_hostname: warning: valid_hostname: numeric hostname: 1
./valid_hostname: warning: valid_hostaddr: invalid octet count: 1
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1
./valid_hostname: testing: "."
./valid_hostname: warning: valid_hostname: misplaced delimiter: .
./valid_hostname: warning: valid_hostaddr: misplaced dot: .
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: .
./valid_hostname: testing: ""
./valid_hostname: warning: valid_hostname: empty hostname
./valid_hostname: warning: valid_hostaddr: empty address
+./valid_hostname: warning: valid_hostliteral: '[' expected at start:
./valid_hostname: testing: "321"
./valid_hostname: warning: valid_hostname: numeric hostname: 321
./valid_hostname: warning: valid_hostaddr: invalid octet value: 321
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 321
./valid_hostname: testing: "f"
./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): f
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: f
./valid_hostname: testing: "f.2.3.4"
./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): f.2.3.4
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: f.2.3.4
./valid_hostname: testing: "1f.2.3.4"
./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): 1f.2.3.4
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1f.2.3.4
./valid_hostname: testing: "f1.2.3.4"
./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): f1.2.3.4
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: f1.2.3.4
./valid_hostname: testing: "1.2f.3.4"
./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): 1.2f.3.4
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1.2f.3.4
./valid_hostname: testing: "1.f2.3.4"
./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): 1.f2.3.4
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1.f2.3.4
./valid_hostname: testing: "1.2.3.4f"
./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): 1.2.3.4f
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1.2.3.4f
./valid_hostname: testing: "1.2.3.f4"
./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): 1.2.3.f4
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1.2.3.f4
./valid_hostname: testing: "1.2.3.f"
./valid_hostname: warning: valid_hostaddr: invalid character 102(decimal): 1.2.3.f
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: 1.2.3.f
./valid_hostname: testing: "-.a.b"
./valid_hostname: warning: valid_hostname: misplaced hyphen: -.a.b
./valid_hostname: warning: valid_hostaddr: invalid character 45(decimal): -.a.b
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: -.a.b
./valid_hostname: testing: "a.-.b"
./valid_hostname: warning: valid_hostname: misplaced hyphen: a.-.b
./valid_hostname: warning: valid_hostaddr: invalid character 97(decimal): a.-.b
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: a.-.b
./valid_hostname: testing: "a.b.-"
./valid_hostname: warning: valid_hostname: misplaced hyphen: a.b.-
./valid_hostname: warning: valid_hostaddr: invalid character 97(decimal): a.b.-
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: a.b.-
./valid_hostname: testing: "-aa.b.b"
./valid_hostname: warning: valid_hostname: misplaced hyphen: -aa.b.b
./valid_hostname: warning: valid_hostaddr: invalid character 45(decimal): -aa.b.b
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: -aa.b.b
./valid_hostname: testing: "aa-.b.b"
./valid_hostname: warning: valid_hostname: misplaced hyphen: aa-.b.b
./valid_hostname: warning: valid_hostaddr: invalid character 97(decimal): aa-.b.b
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: aa-.b.b
./valid_hostname: testing: "a.-bb.b"
./valid_hostname: warning: valid_hostname: misplaced hyphen: a.-bb.b
./valid_hostname: warning: valid_hostaddr: invalid character 97(decimal): a.-bb.b
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: a.-bb.b
./valid_hostname: testing: "a.bb-.b"
./valid_hostname: warning: valid_hostname: misplaced hyphen: a.bb-.b
./valid_hostname: warning: valid_hostaddr: invalid character 97(decimal): a.bb-.b
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: a.bb-.b
./valid_hostname: testing: "a.b.-bb"
./valid_hostname: warning: valid_hostname: misplaced hyphen: a.b.-bb
./valid_hostname: warning: valid_hostaddr: invalid character 97(decimal): a.b.-bb
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: a.b.-bb
./valid_hostname: testing: "a.b.bb-"
./valid_hostname: warning: valid_hostname: misplaced hyphen: a.b.bb-
./valid_hostname: warning: valid_hostaddr: invalid character 97(decimal): a.b.bb-
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: a.b.bb-
./valid_hostname: testing: "a-a.b-b"
./valid_hostname: warning: valid_hostaddr: invalid character 97(decimal): a-a.b-b
+./valid_hostname: warning: valid_hostliteral: '[' expected at start: a-a.b-b
+./valid_hostname: testing: "[1.2.3.4]"
+./valid_hostname: warning: valid_hostname: invalid character 91(decimal): [1.2.3.4]
+./valid_hostname: warning: valid_hostaddr: invalid character 91(decimal): [1.2.3.4]
+./valid_hostname: testing: "[321.255.255.255]"
+./valid_hostname: warning: valid_hostname: invalid character 91(decimal): [321.255.255.255]
+./valid_hostname: warning: valid_hostaddr: invalid character 91(decimal): [321.255.255.255]
+./valid_hostname: warning: valid_hostaddr: invalid octet value: 321.255.255.255
+./valid_hostname: testing: "[1.2.3.4"
+./valid_hostname: warning: valid_hostname: invalid character 91(decimal): [1.2.3.4
+./valid_hostname: warning: valid_hostaddr: invalid character 91(decimal): [1.2.3.4
+./valid_hostname: warning: valid_hostliteral: ']' expected at end: [1.2.3.4
+./valid_hostname: testing: "[1.2.3.4]foo"
+./valid_hostname: warning: valid_hostname: invalid character 91(decimal): [1.2.3.4]foo
+./valid_hostname: warning: valid_hostaddr: invalid character 91(decimal): [1.2.3.4]foo
+./valid_hostname: warning: valid_hostliteral: unexpected text after ']': [1.2.3.4]foo
+./valid_hostname: testing: "[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]"
+./valid_hostname: warning: valid_hostname: invalid character 91(decimal): [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+./valid_hostname: warning: valid_hostaddr: invalid character 91(decimal): [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+./valid_hostname: warning: valid_hostliteral: too much text: [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx