diff --git a/postfix/HISTORY b/postfix/HISTORY index 61c98c0b8..d85530405 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -7426,8 +7426,58 @@ Apologies for any names omitted. 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: diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index b26e358fa..adfb58fbe 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -122,7 +122,9 @@ mail_owner = postfix # 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. @@ -145,12 +147,14 @@ mail_owner = postfix # 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 @@ -159,6 +163,9 @@ mail_owner = postfix # 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: @@ -175,11 +182,24 @@ mail_owner = postfix # - 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 @@ -282,7 +302,7 @@ local_recipient_maps = unix:passwd.byname $alias_maps # 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 diff --git a/postfix/conf/post-install b/postfix/conf/post-install index ccdfc0fb5..88fae3a76 100644 --- a/postfix/conf/post-install +++ b/postfix/conf/post-install @@ -498,7 +498,7 @@ EOF } 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. # @@ -520,6 +520,21 @@ EOF $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. diff --git a/postfix/conf/sample-misc.cf b/postfix/conf/sample-misc.cf index 44042f6bf..6d671287c 100644 --- a/postfix/conf/sample-misc.cf +++ b/postfix/conf/sample-misc.cf @@ -200,7 +200,9 @@ max_use = 100 # 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. @@ -223,6 +225,11 @@ max_use = 100 # 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 diff --git a/postfix/conf/sample-smtpd.cf b/postfix/conf/sample-smtpd.cf index 9a82c179e..da140faf9 100644 --- a/postfix/conf/sample-smtpd.cf +++ b/postfix/conf/sample-smtpd.cf @@ -434,6 +434,14 @@ allow_untrusted_routing = no # 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 # diff --git a/postfix/conf/transport b/postfix/conf/transport index 2b36e7c4d..65be20410 100644 --- a/postfix/conf/transport +++ b/postfix/conf/transport @@ -30,15 +30,15 @@ # 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, @@ -50,45 +50,17 @@ # 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: @@ -113,96 +85,139 @@ # 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 @@ -212,7 +227,7 @@ # # 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- @@ -224,7 +239,7 @@ # # 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 @@ -243,7 +258,7 @@ # 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) diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index 7d1221af6..cc416b07f 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -145,18 +145,6 @@ SMTPD(8) SMTPD(8) hopcount_limit Limit the number of Received: message headers. - local_recipient_maps - List of maps with user names that are local to - $myorigin or $inet_interfaces. If this parameter is - defined, then the SMTP server rejects mail for - unknown local users. - - relay_recipient_maps - List of maps that define all the email addresses in - the domains that match $relay_domains. If this - parameter is defined, then the SMTP server rejects - mail for unknown relay recipients. - notify_classes List of error classes. Of special interest are: @@ -195,9 +183,35 @@ SMTPD(8) SMTPD(8) The characters that Postfix accepts as VERP delim- iter characters. +Known versus unknown recipients + unknown_local_recipient_reject_code + The response code when a client specifies a recipi- + ent whose domain matches $mydestination or + $inet_interfaces, while $local_recipient_maps is + non-empty and does not list the recipient address + or address local-part. + + unknown_relay_recipient_reject_code + The response code when a client specifies a recipi- + ent whose domain matches $relay_domains, while + $relay_recipient_maps is non-empty and does not + list the recipient address. + + unknown_virtual_alias_reject_code + The response code when a client specifies a recipi- + ent whose domain matches $virtual_alias_domains, + while the recipient is not listed in $vir- + tual_alias_maps. + + unknown_virtual_mailbox_reject_code + The response code when a client specifies a recipi- + ent whose domain matches $virtual_mailbox_domains, + while the recipient is not listed in $virtual_mail- + box_maps. + Resource controls line_length_limit - 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. message_size_limit @@ -205,8 +219,8 @@ SMTPD(8) SMTPD(8) ing on-disk storage for envelope information. queue_minfree - 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. smtpd_history_flush_threshold @@ -221,23 +235,23 @@ SMTPD(8) SMTPD(8) smtpd_soft_error_limit When an SMTP client has made this number of errors, - wait error_count seconds before responding to any + wait error_count seconds before responding to any client request. smtpd_hard_error_limit - Disconnect after a client has made this number of + Disconnect after a client has made this number of errors. smtpd_junk_command_limit 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. UCE control restrictions 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). smtpd_client_restrictions @@ -245,19 +259,19 @@ SMTPD(8) SMTPD(8) tem. smtpd_helo_required - Require that clients introduce themselves at the + Require that clients introduce themselves at the beginning of an SMTP session. smtpd_helo_restrictions - Restrict what client hostnames are allowed in HELO + Restrict what client hostnames are allowed in HELO and EHLO commands. smtpd_sender_restrictions - Restrict what sender addresses are allowed in MAIL + Restrict what sender addresses are allowed in MAIL FROM commands. smtpd_recipient_restrictions - Restrict what recipient addresses are allowed in + Restrict what recipient addresses are allowed in RCPT TO commands. smtpd_etrn_restrictions @@ -265,73 +279,73 @@ SMTPD(8) SMTPD(8) mands, and what clients may issue ETRN commands. smtpd_data_restrictions - Restrictions on the DATA command. Currently, the - only restriction that makes sense here is + Restrictions on the DATA command. Currently, the + only restriction that makes sense here is reject_unauth_pipelining. allow_untrusted_routing - 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. smtpd_restriction_classes - 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. smtpd_null_access_lookup_key - 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. maps_rbl_domains (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 reject_maps_rbl restriction. permit_mx_backup_networks - Only domains whose primary MX hosts match the - listed networks are eligible for the per- + Only domains whose primary MX hosts match the + listed networks are eligible for the per- mit_mx_backup feature. relay_domains - 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 relay_transport setting. UCE control responses access_map_reject_code - Response code when a client violates an access + Response code when a client violates an access database restriction. default_rbl_reply Default template reply when a request is RBL black- - listed. This template is used by the reject_rbl_* - and reject_rhsbl_* restrictions. See also: + listed. This template is used by the reject_rbl_* + and reject_rhsbl_* restrictions. See also: rbl_reply_maps and smtpd_expansion_filter. defer_code - Response code when a client request is rejected by + Response code when a client request is rejected by the defer restriction. invalid_hostname_reject_code - Response code when a client violates the + Response code when a client violates the reject_invalid_hostname restriction. maps_rbl_reject_code Response code when a request is RBL blacklisted. rbl_reply_maps - 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 reject_rbl_* and - reject_rhsbl_* restrictions. See also: + reject_rhsbl_* restrictions. See also: default_rbl_reply and smtpd_expansion_filter. reject_code - Response code when the client matches a reject + Response code when the client matches a reject restriction. relay_domains_reject_code @@ -339,7 +353,7 @@ SMTPD(8) SMTPD(8) mail relay policy. unknown_address_reject_code - Response code when a client violates the + Response code when a client violates the reject_unknown_address restriction. unknown_client_reject_code @@ -348,7 +362,7 @@ SMTPD(8) SMTPD(8) tion. unknown_hostname_reject_code - Response code when a client violates the + Response code when a client violates the reject_unknown_hostname restriction. SEE ALSO @@ -358,7 +372,7 @@ SMTPD(8) SMTPD(8) syslogd(8) system logging LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/transport.5.html b/postfix/html/transport.5.html index 613240673..58543b893 100644 --- a/postfix/html/transport.5.html +++ b/postfix/html/transport.5.html @@ -31,15 +31,15 @@ TRANSPORT(5) TRANSPORT(5) 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, @@ -51,45 +51,17 @@ TRANSPORT(5) TRANSPORT(5) 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: @@ -114,96 +86,139 @@ TRANSPORT(5) TRANSPORT(5) 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 @@ -213,7 +228,7 @@ TRANSPORT(5) TRANSPORT(5) 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- @@ -225,7 +240,7 @@ TRANSPORT(5) TRANSPORT(5) 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 @@ -244,7 +259,7 @@ TRANSPORT(5) TRANSPORT(5) 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) diff --git a/postfix/html/trivial-rewrite.8.html b/postfix/html/trivial-rewrite.8.html index b1c3d4836..508bfca2b 100644 --- a/postfix/html/trivial-rewrite.8.html +++ b/postfix/html/trivial-rewrite.8.html @@ -127,17 +127,6 @@ TRIVIAL-REWRITE(8) TRIVIAL-REWRITE(8) Syntax is transport:nexthop; see transport(5) for details. The :nexthop part is optional. - error_transport - Where to deliver mail for non-existent recipients - in domains that match virtual_alias_domains (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 error. - - Syntax is transport:nexthop; see transport(5) for - details. The :nexthop part is optional. - virtual_transport Where to deliver mail for non-local domains that match $virtual_mailbox_domains. The default trans- @@ -178,9 +167,6 @@ TRIVIAL-REWRITE(8) TRIVIAL-REWRITE(8) List of tables with domain to (transport, nexthop) mappings. - transport_null_address_lookup_key - Lookup key to be used for the null address. - SEE ALSO master(8) process manager syslogd(8) system logging diff --git a/postfix/man/man5/transport.5 b/postfix/man/man5/transport.5 index 24e9487c3..c35b35420 100644 --- a/postfix/man/man5/transport.5 +++ b/postfix/man/man5/transport.5 @@ -33,7 +33,7 @@ or SQL, the same lookups are done as for ordinary indexed files. 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 @@ -41,8 +41,8 @@ in the section titled "REGULAR EXPRESSION TABLES". .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 `#'. @@ -50,15 +50,55 @@ 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 @@ -84,37 +124,6 @@ dependent. In the case of SMTP, specify \fIhost\fR:\fIservice\fR for a 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 diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index 8914156c0..713674266 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -132,14 +132,6 @@ XVERP command is specified without explicit delimiters. 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 @@ -167,6 +159,26 @@ Change hard (5xx) reject responses into soft (4xx) reject responses. 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 diff --git a/postfix/man/man8/trivial-rewrite.8 b/postfix/man/man8/trivial-rewrite.8 index 2bf08c72d..b299a9c96 100644 --- a/postfix/man/man8/trivial-rewrite.8 +++ b/postfix/man/man8/trivial-rewrite.8 @@ -117,15 +117,6 @@ The default transport is \fBlocal\fR. .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. @@ -160,8 +151,6 @@ to the destination's mail exchanger. .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 diff --git a/postfix/proto/transport b/postfix/proto/transport index 0ebd73bbf..c8634c45b 100644 --- a/postfix/proto/transport +++ b/postfix/proto/transport @@ -27,14 +27,14 @@ # 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 `#'. @@ -42,15 +42,55 @@ # 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 @@ -76,35 +116,6 @@ # 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 --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index 2be94cdcd..5a5a430eb 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -298,6 +298,27 @@ virtual8_test: virtual8_maps virtual8_map virtual8.in virtual8.ref \ 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.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 diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 937224a27..db32eae47 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -96,7 +96,7 @@ extern char *var_mydomain; * 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; /* @@ -293,10 +293,6 @@ extern bool var_disable_vrfy_cmd; /* * 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; @@ -305,6 +301,10 @@ 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; @@ -1126,6 +1126,10 @@ extern char *var_relay_transport; #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; @@ -1279,12 +1283,16 @@ abcdefghijklmnopqrstuvwxyz{|}~" 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. */ @@ -1375,6 +1383,10 @@ extern char *var_virt_mailbox_maps; #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; diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 156eb251e..3dc150f1e 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change the patchlevel and the release date. Snapshots change the * release date only, unless they include the same bugfix as a patch release. */ -#define MAIL_RELEASE_DATE "20021214" +#define MAIL_RELEASE_DATE "20021217" #define VAR_MAIL_VERSION "mail_version" #define DEF_MAIL_VERSION "1.1.12-" MAIL_RELEASE_DATE diff --git a/postfix/src/global/quote_821_local.c b/postfix/src/global/quote_821_local.c index b87cf7eb1..b13605294 100644 --- a/postfix/src/global/quote_821_local.c +++ b/postfix/src/global/quote_821_local.c @@ -82,7 +82,7 @@ static int is_821_dot_string(char *local_part, char *end, int flags) * 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] == '.') diff --git a/postfix/src/global/quote_822_local.c b/postfix/src/global/quote_822_local.c index 5f4672f74..bf18059d3 100644 --- a/postfix/src/global/quote_822_local.c +++ b/postfix/src/global/quote_822_local.c @@ -95,7 +95,7 @@ static int is_822_dot_string(const char *local_part, const char *end, int flags) * 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] == '.') diff --git a/postfix/src/global/resolve_clnt.c b/postfix/src/global/resolve_clnt.c index 219f678bd..4d739609f 100644 --- a/postfix/src/global/resolve_clnt.c +++ b/postfix/src/global/resolve_clnt.c @@ -70,7 +70,7 @@ /* 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. @@ -178,7 +178,7 @@ void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply) */ 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); @@ -247,6 +247,24 @@ static NORETURN usage(char *myname) 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"); @@ -256,6 +274,16 @@ static void resolve(char *addr, RESOLVE_REPLY *reply) 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); } } @@ -297,6 +325,7 @@ int main(int argc, char **argv) vstring_free(buffer); } resolve_clnt_free(&reply); + exit(0); } #endif diff --git a/postfix/src/global/resolve_clnt.in b/postfix/src/global/resolve_clnt.in new file mode 100644 index 000000000..d44aa3d6b --- /dev/null +++ b/postfix/src/global/resolve_clnt.in @@ -0,0 +1,45 @@ + +@ +@@ +@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 diff --git a/postfix/src/global/resolve_clnt.ref b/postfix/src/global/resolve_clnt.ref new file mode 100644 index 000000000..c7b55a1a1 --- /dev/null +++ b/postfix/src/global/resolve_clnt.ref @@ -0,0 +1,225 @@ +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 diff --git a/postfix/src/global/rewrite_clnt.c b/postfix/src/global/rewrite_clnt.c index c02375b44..8e07dba78 100644 --- a/postfix/src/global/rewrite_clnt.c +++ b/postfix/src/global/rewrite_clnt.c @@ -242,6 +242,7 @@ int main(int argc, char **argv) vstring_free(buffer); } vstring_free(reply); + exit(0); } #endif diff --git a/postfix/src/global/rewrite_clnt.in b/postfix/src/global/rewrite_clnt.in new file mode 100644 index 000000000..4f79ebc27 --- /dev/null +++ b/postfix/src/global/rewrite_clnt.in @@ -0,0 +1,13 @@ +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. diff --git a/postfix/src/global/rewrite_clnt.ref b/postfix/src/global/rewrite_clnt.ref new file mode 100644 index 000000000..40d2faf44 --- /dev/null +++ b/postfix/src/global/rewrite_clnt.ref @@ -0,0 +1,39 @@ +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. diff --git a/postfix/src/nqmgr/Makefile.in b/postfix/src/nqmgr/Makefile.in index f2b846ea4..771cac507 100644 --- a/postfix/src/nqmgr/Makefile.in +++ b/postfix/src/nqmgr/Makefile.in @@ -185,7 +185,6 @@ qmgr_message.o: ../../include/rec_type.h 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 diff --git a/postfix/src/nqmgr/qmgr_message.c b/postfix/src/nqmgr/qmgr_message.c index 31420c70d..83b7d5c9e 100644 --- a/postfix/src/nqmgr/qmgr_message.c +++ b/postfix/src/nqmgr/qmgr_message.c @@ -119,7 +119,6 @@ #include #include #include -#include #include #include @@ -768,8 +767,11 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message) * 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]) { diff --git a/postfix/src/qmgr/Makefile.in b/postfix/src/qmgr/Makefile.in index f3cd4c2c6..35212640b 100644 --- a/postfix/src/qmgr/Makefile.in +++ b/postfix/src/qmgr/Makefile.in @@ -172,7 +172,6 @@ qmgr_message.o: ../../include/rec_type.h 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 diff --git a/postfix/src/qmgr/qmgr_message.c b/postfix/src/qmgr/qmgr_message.c index 534f2bc56..011f6020a 100644 --- a/postfix/src/qmgr/qmgr_message.c +++ b/postfix/src/qmgr/qmgr_message.c @@ -110,7 +110,6 @@ #include #include #include -#include #include #include @@ -648,8 +647,11 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message) * 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]) { diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 5d757afd4..fa9657599 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -118,14 +118,6 @@ /* 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 @@ -153,6 +145,26 @@ /* 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 @@ -413,12 +425,11 @@ char *var_smtpd_null_key; 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. @@ -1619,6 +1630,10 @@ int main(int argc, char **argv) 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[] = { @@ -1663,11 +1678,6 @@ int main(int argc, char **argv) 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, }; diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 346eb70e4..7d9949c96 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -3149,9 +3149,11 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient) * 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)); } @@ -3170,13 +3172,10 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient) */ 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)); } @@ -3184,13 +3183,10 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient) * 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)); } @@ -3199,13 +3195,10 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient) */ 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)); } @@ -3365,11 +3358,6 @@ char *var_double_bounce_sender; 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 { @@ -3408,11 +3396,6 @@ static STRING_TABLE string_table[] = { 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, }; @@ -3465,6 +3448,10 @@ int var_defer_code; 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, @@ -3480,6 +3467,10 @@ static INT_TABLE int_table[] = { 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, }; @@ -3633,23 +3624,23 @@ void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply) 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); diff --git a/postfix/src/trivial-rewrite/Makefile.in b/postfix/src/trivial-rewrite/Makefile.in index 3ff0eb8e6..36d30660e 100644 --- a/postfix/src/trivial-rewrite/Makefile.in +++ b/postfix/src/trivial-rewrite/Makefile.in @@ -71,6 +71,7 @@ resolve.o: ../../include/vstring_vstream.h 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 @@ -127,6 +128,9 @@ transport.o: ../../include/mail_params.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 diff --git a/postfix/src/trivial-rewrite/resolve.c b/postfix/src/trivial-rewrite/resolve.c index 8e55123c8..53fdd5b30 100644 --- a/postfix/src/trivial-rewrite/resolve.c +++ b/postfix/src/trivial-rewrite/resolve.c @@ -63,6 +63,7 @@ #include #include #include +#include /* Global library. */ @@ -117,19 +118,12 @@ * 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 @@ -141,13 +135,6 @@ static DOMAIN_LIST *relay_domains; static STRING_LIST *virt_alias_doms; static STRING_LIST *virt_mailbox_doms; - /* - * Saved address domain class information. - */ -static VSTRING *saved_class_channel; -static VSTRING *saved_class_nexthop; -static VSTRING *saved_class_domain; - static MAPS *relocated_maps; /* resolve_addr - resolve address according to rule set */ @@ -157,50 +144,109 @@ void resolve_addr(char *addr, VSTRING *channel, VSTRING *nexthop, { 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. * - * 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. + * In an ideal world we would parse the externalized address form as given + * to us by the sender. * - * 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. + * 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. + * + * 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. + * + * 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)); @@ -208,26 +254,15 @@ void resolve_addr(char *addr, VSTRING *channel, VSTRING *nexthop, /* * 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) @@ -239,32 +274,65 @@ void resolve_addr(char *addr, VSTRING *channel, VSTRING *nexthop, * 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 @@ -275,105 +343,134 @@ void resolve_addr(char *addr, VSTRING *channel, VSTRING *nexthop, 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)); } } - tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL); /* - * With relay or other non-local destinations, the relayhost setting - * overrides the destination domain name. + * Transform the recipient address back to internal form. * - * With virtual, relay, or other non-local destinations, give the highest - * precedence to delivery transport associated next-hop information. + * 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 local, virtual, relay, or other non-local destinations, give the + * highest precedence to transport associated nexthop information. * - * 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. + * Otherwise, with relay or other non-local destinations, the relayhost + * setting overrides the destination domain name. * - * XXX Nag if the domain is listed in multiple domain lists. The effect is - * implementation defined, and may break when internals change. + * XXX Nag if the recipient domain is listed in multiple domain lists. The + * result 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", @@ -384,56 +481,67 @@ void resolve_addr(char *addr, VSTRING *channel, VSTRING *nexthop, 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; } /* - * Sanity checks. + * 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 ((*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 ((destination = split_at(STR(channel), ':')) != 0 && *destination) + vstring_strcpy(nexthop, destination); /* - * The transport map overrides any transport and next-hop host info that - * is set up above. - * - * With error transports, the nexthop info is arbitrary text that must not - * be passed on to regular delivery agents. When the transport map - * overrides an error transport without overriding the nexthop - * information, or vice versa, update the nexthop information - * appropriately. + * Sanity checks. */ - if ((*flags & RESOLVE_FLAG_FAIL) == 0 && *var_transport_maps) { - vstring_strcpy(saved_class_channel, STR(channel)); - vstring_strcpy(saved_class_nexthop, STR(nexthop)); + 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); - 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) { + /* + * 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 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 (*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; } } @@ -448,26 +556,24 @@ void resolve_addr(char *addr, VSTRING *channel, VSTRING *nexthop, */ #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. */ @@ -516,9 +622,6 @@ void resolve_init(void) 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 = diff --git a/postfix/src/trivial-rewrite/rewrite.c b/postfix/src/trivial-rewrite/rewrite.c index d34a46482..f551ecd1e 100644 --- a/postfix/src/trivial-rewrite/rewrite.c +++ b/postfix/src/trivial-rewrite/rewrite.c @@ -90,6 +90,12 @@ void rewrite_tree(char *unused_ruleset, TOK822 *tree) 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. */ @@ -158,10 +164,13 @@ void rewrite_tree(char *unused_ruleset, TOK822 *tree) } /* - * 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)); @@ -169,13 +178,17 @@ void rewrite_tree(char *unused_ruleset, TOK822 *tree) } /* - * 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 */ diff --git a/postfix/src/trivial-rewrite/transport.c b/postfix/src/trivial-rewrite/transport.c index 0ac8a4fe3..fa6080258 100644 --- a/postfix/src/trivial-rewrite/transport.c +++ b/postfix/src/trivial-rewrite/transport.c @@ -8,8 +8,9 @@ /* /* 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 @@ -65,6 +66,7 @@ #include #include #include +#include /* Application-specific. */ @@ -77,13 +79,6 @@ static VSTRING *wildcard_nexthop; #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) @@ -95,15 +90,52 @@ 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 @@ -115,11 +147,8 @@ static int find_transport_entry(const char *key, int flags, * * 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 @@ -131,8 +160,8 @@ static int find_transport_entry(const char *key, int flags, 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); } @@ -159,13 +188,15 @@ void transport_wildcard_init(void) #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); } @@ -173,9 +204,10 @@ void transport_wildcard_init(void) /* 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; @@ -185,6 +217,15 @@ int transport_lookup(const char *addr, VSTRING *channel, VSTRING *nexthop) #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. @@ -194,16 +235,6 @@ int transport_lookup(const char *addr, VSTRING *channel, VSTRING *nexthop) 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. @@ -211,8 +242,10 @@ int transport_lookup(const char *addr, VSTRING *channel, VSTRING *nexthop) 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, @@ -221,13 +254,14 @@ int transport_lookup(const char *addr, VSTRING *channel, VSTRING *nexthop) */ 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); } /* @@ -246,9 +280,11 @@ int transport_lookup(const char *addr, VSTRING *channel, VSTRING *nexthop) * 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) @@ -259,8 +295,8 @@ int transport_lookup(const char *addr, VSTRING *channel, VSTRING *nexthop) * 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); } diff --git a/postfix/src/trivial-rewrite/transport.h b/postfix/src/trivial-rewrite/transport.h index 2ebe3def4..0884eb291 100644 --- a/postfix/src/trivial-rewrite/transport.h +++ b/postfix/src/trivial-rewrite/transport.h @@ -18,7 +18,7 @@ */ 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 diff --git a/postfix/src/trivial-rewrite/trivial-rewrite.c b/postfix/src/trivial-rewrite/trivial-rewrite.c index d5ca7a177..2a7f0233f 100644 --- a/postfix/src/trivial-rewrite/trivial-rewrite.c +++ b/postfix/src/trivial-rewrite/trivial-rewrite.c @@ -101,15 +101,6 @@ /* .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. @@ -144,8 +135,6 @@ /* .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 @@ -208,11 +197,9 @@ bool var_append_dot_mydomain; 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; @@ -284,10 +271,8 @@ int main(int argc, char **argv) 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, diff --git a/postfix/src/util/valid_hostname.c b/postfix/src/util/valid_hostname.c index fa628c488..9a63fe53c 100644 --- a/postfix/src/util/valid_hostname.c +++ b/postfix/src/util/valid_hostname.c @@ -13,6 +13,10 @@ /* 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 @@ -20,9 +24,11 @@ /* 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. @@ -192,6 +198,39 @@ int valid_hostaddr(const char *addr, int gripe) 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 /* @@ -216,6 +255,7 @@ int main(int unused_argc, char **argv) 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); } diff --git a/postfix/src/util/valid_hostname.h b/postfix/src/util/valid_hostname.h index 8de21619e..e66bbc53d 100644 --- a/postfix/src/util/valid_hostname.h +++ b/postfix/src/util/valid_hostname.h @@ -21,6 +21,7 @@ extern int valid_hostname(const char *, int); extern int valid_hostaddr(const char *, int); +extern int valid_hostliteral(const char *, int); /* LICENSE /* .ad diff --git a/postfix/src/util/valid_hostname.in b/postfix/src/util/valid_hostname.in index 9d48f5db0..bb50ec026 100644 --- a/postfix/src/util/valid_hostname.in +++ b/postfix/src/util/valid_hostname.in @@ -36,3 +36,8 @@ a.bb-.b 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] diff --git a/postfix/src/util/valid_hostname.ref b/postfix/src/util/valid_hostname.ref index 28e2e737c..7b0938053 100644 --- a/postfix/src/util/valid_hostname.ref +++ b/postfix/src/util/valid_hostname.ref @@ -1,101 +1,158 @@ ./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