Minor cleanups of the xclient error messages; xclient
command lookup tables. File: smtpd/smtpd.c.
+20031206
+
+ Feature: reject_sender_login_mismatch allows multiple owners
+ of a sender address. Code by Liviu Daia. Files:
+ smtpd/smtpd_check.c and documentation.
+
+ The reject_sender_login_mismatch feature is now implemented
+ by elementary features reject_unauth_sender_login_mismatch
+ (reject if the client is not SASL logged in but the sender
+ address has an owner in smtpd_sender_login_maps) and
+ reject_auth_sender_login_mismatch (reject if the client is
+ SASL logged in but does not own the sender address). Code
+ by Liviu Daia. Files: smtpd/smtpd_check.c and documentation.
+
20031207
Bugfix: fallback_transport and mailbox_transport were broken
because the deliver_pass.c module was not updated for the
changed message delivery protocol.
+
Open problems:
High: when virtual aliasing is turned off after content
Low: with quoted-printable, perhaps use =46rom instead of
>From.
+
+ virtual_mailbox_path expression like forward_path, so that
+ people can specify prefix and suffix.
network address, and other information, for the entire duration of
an SMTP session.
+4 - Fetchmail.
+
Command overview
================
-The EHLO keyword associated with this extension is XCLIENT.
+The EHLO keyword associated with this extension is XCLIENT. In EHLO
+replies, XCLIENT is followed by the names of the supported XCLIENT
+functions.
-The XCLIENT OVERRIDE command updates remote client attributes that
+The XCLIENT OVERRIDE function updates remote client attributes that
the MTA normally uses for access control, message headers, logging
and so on, for the duration of an entire SMTP session.
-The XCLIENT FORWARD command updates temporary remote client attributes
-that the MTA uses for transaction logging. These attributes are
-valid for only one message delivery attempt. In the absence of
-forwarded attributes the MTA must use the normal remote client
+The XCLIENT FORWARD function updates temporary remote client
+attributes that the MTA uses for transaction logging. These attributes
+are valid for only one message delivery attempt. In the absence
+of forwarded attributes the MTA must use the normal remote client
attribute values.
The general command syntax is described below. Upper case and
attribute = name"="value
- name = ( CLIENT_NAME|CLIENT_ADDR|CLIENT_CODE|CLIENT_PROTO|CLIENT_HELO )
+ name = ( NAME|ADDR|CODE|PROTO|HELO )
value = ( { empty } | xtext )
address, leaving unchanged all other client attributes such as the
mail protocol or the hostname given in the HELO command:
- XCLIENT OVERRIDE CLIENT_NAME=spike.porcupine.org
- XCLIENT OVERRIDE CLIENT_ADDR=168.100.189.2
+ XCLIENT OVERRIDE NAME=spike.porcupine.org
+ XCLIENT OVERRIDE ADDR=168.100.189.2
The XCLIENT FORWARD request specifies remote client attributes that
are logged with one message delivery attempt. The attributes are
are defined in this document, leaving none at their default unknown
value:
- XCLIENT FORWARD CLIENT_NAME=spike.porcupine.org CLIENT_ADDR=168.100.189.2
- XCLIENT FORWARD CLIENT_HELO=spike.porcupine.org CLIENT_PROTO=ESMTP
+ XCLIENT FORWARD NAME=spike.porcupine.org ADDR=168.100.189.2
+ XCLIENT FORWARD HELO=spike.porcupine.org PROTO=ESMTP
Note 1: attributes specified with successive XCLIENT commands
accumulate.
empty; the client must not send its own internal representation of
unavailable information.
-The CLIENT_CODE attribute specifies CLIENT_NAME hostname lookup
-status information. Values are OK (success), TEMP (temporary lookup
-failure) or PERM (permanent lookup failure). When CLIENT_CODE is
-set to any value other than OK, the CLIENT_NAME attribute is
-automatically set to the unknown value.
+The NAME_CODE attribute specifies NAME hostname lookup status
+information. Values are OK (success), TEMP (temporary lookup
+failure) or PERM (permanent lookup failure). When CODE is set to
+any value other than OK, the NAME attribute is automatically set
+to the unknown value.
-The CLIENT_NAME attribute should specify a syntactically valid
-domain name and not a numerical address. When a null client name
-is specified (i.e. the client name is unknown), the CLIENT_CODE
-attribute is implicitly set to PERM. When a valid domain name is
-specified, CLIENT_CODE is implicitly set to OK. The server may
-process a syntactically invalid domain name as if it were unknown.
+The NAME attribute specifies a name space (typically, DNS) and
+an MTA name within that name space.
+and not a numerical address. When a null client name is specified
+(i.e. the client name is unknown), the CODE attribute is implicitly
+set to PERM. When a valid domain name is specified, CODE is implicitly
+set to OK. The server may process a syntactically invalid domain
+name as if it were unknown.
-The CLIENT_ADDR attribute must specify a numerical network address
-without [].
+The ADDR attribute must specify a numerical network address without
+[].
-The CLIENT_PROTO attribute should be a string of up to 64 printable
+The PROTO attribute should be a string of up to 64 printable
characters, where printable is defined by the ANSI C isascii() and
isprint() predicates.
-The CLIENT_HELO attribute should be a syntactically valid HELO
+The HELO attribute should be a syntactically valid HELO
parameter value.
-Note 3: syntactically valid CLIENT_NAME and CLIENT_HELO attributes
-can be up to 255 characters long. The client must not send XCLIENT
-commands that exceed the 512 character limit of SMTP commands.
+Note 3: syntactically valid NAME and HELO attributes can be up to
+255 characters long. The client must not send XCLIENT commands that
+exceed the 512 character limit of SMTP commands.
Note 4: attribute values may end up in Received: or other message
headers. The receiving MTA may substitute characters in order to
# To reject all SMTP connections from unauthenticated clients,
# specify smtpd_delay_reject=yes (which is the default) and use:
#
-# smtpd_client_restrictions = permit_sasl_authenticated
+# smtpd_client_restrictions = permit_sasl_authenticated, reject
#
# In order to enable server-side authentication, build Postfix with
# SASL support, and install a configuration file /usr/lib/sasl/smtpd.conf
#
smtp_defer_if_no_mx_address_found = no
-# The smtp_send_xclient_command parameter controls whether the Postfix
-# SMTP client will send an XCLIENT command to the SMTP server, when
-# the ESMTP HELO response of the remote host indicates XCLIENT support.
+# The smtp_send_xforward_command parameter controls whether the Postfix
+# SMTP client will send an XFORWARD command to the SMTP server, when
+# the ESMTP HELO response of the remote host indicates XFORWARD support.
# This allows an "smtp" delivery agent, used for content filter
# message injection, to forward the name, address, protocol and HELO
# name of the original client to the content filter and downstream
# queuing SMTP server. Before you change the value to yes, it is best
# to make sure your content filter supports this command.
#
-smtp_send_xclient_command = no
+smtp_send_xforward_command = no
# The smtp_line_length_limit parameter controls the length of
# message header and body lines that Postfix will send via SMTP.
#
smtp_helo_timeout = 300s
-# The smtp_xclient_timeout parameter specifies the SMTP client timeout
-# for sending the SMTP XCLIENT command, and for receiving the server
+# The smtp_xforward_timeout parameter specifies the SMTP client timeout
+# for sending the SMTP XFORWARD command, and for receiving the server
# response.
-# for receiving the SMTP greeting banner.
#
# In case of problems the client does NOT try the next address on
# the mail exchanger list.
# Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
# The default time unit is s (seconds).
#
-smtp_xclient_timeout = 300s
+smtp_xforward_timeout = 300s
# The smtp_mail_timeout parameter specifies the SMTP client timeout
# for sending the SMTP MAIL FROM command, and for receiving the server
#
# The smtpd_sender_login_maps parameter specifies the (SASL) login
-# name that owns a sender (MAIL FROM) address.
+# names that own sender (MAIL FROM) addresses.
#
# Specify zero or more maptype:mapname entries. Maps are created with
# postmap(1) or with equivalent means. The maps are searched in the
# specified order. Regexp tables are allowed.
#
-# Each map entry specifies a sender address and the login name that
-# owns the address. The search order is:
+# Each map entry specifies a sender address and a list of login names
+# that owns the address. The search order is:
#
-# 1) user@domain owner
+# 1) user@domain owner, owner, ...
#
# This form has the highest precedence.
#
-# 2) user owner
+# 2) user owner, owner, ...
#
# This matches user@site when site is equal to $myorigin, when site
# is listed in $mydestination, or when it is listed in $inet_interfaces.
#
-# 3) @domain owner
+# 3) @domain owner, owner, ...
#
# This matches every address in the specified domain, and has the
# lowest precedence.
# are allowed to specify the XCLIENT extension to SMTP.
#
# This command overrides SMTP client information that is used for
-# logging or access control. Typical use is for SMTP-based content
-# filters or for SMTP server access rule testing.
+# logging and access control. Typical use is for SMTP-based content
+# filters, fetchmail-like programs, or SMTP server access rule testing.
#
# By default, no clients are allowed to specify XCLIENT.
#
#
smtpd_authorized_xclient_hosts =
+# The smtpd_authorized_xforward_hosts parameter specifies what clients
+# are allowed to specify the XFORWARD extension to SMTP.
+#
+# This command forwards information that is used to improve logging
+# after SMTP-based content filters.
+#
+# By default, no clients are allowed to specify XFORWARD.
+#
+# Specify an explicit list of network/netmask patterns, where the
+# mask specifies the number of bits in the network part of a host
+# address. You can also specify hostnames or .domain names (the
+# initial dot causes the domain to match any name below it).
+#
+# You can also specify the absolute pathname of a pattern file instead
+# of listing the patterns here. Specify type:table for table-based lookups
+# (the value on the table right-hand side is not used).
+#
+smtpd_authorized_xforward_hosts =
+
# The smtpd_authorized_verp_clients parameter specifies what clients
# are allowed to specify the SMTP XVERP command. This command requests
# that mail be delivered one recipient at a time with a per recipient
# look up sender address MX hosts (or name servers) and apply the
# specified access table to those hosts.
# Note: the OK result is not allowed here for security reasons.
-# reject_sender_login_mismatch: reject if $smtpd_sender_login_maps specifies
-# a MAIL FROM address owner, but the client is not (SASL) logged in as
-# that MAIL FROM address owner; or if the client is (SASL) logged in, but
-# the client login name doesn't own the MAIL FROM address according to
-# $smtpd_sender_login_maps (see above).
+# reject_sender_login_mismatch:
+# reject if $smtpd_sender_login_maps specifies a MAIL FROM
+# address owner, but the client is not (SASL) logged in as
+# that MAIL FROM address owner; or if the client is (SASL)
+# logged in, but the client login name doesn't own the MAIL
+# FROM address.
+# reject_authenticated_sender_login_mismatch:
+# reject if the client is (SASL) logged in, but the client
+# login name doesn't own the MAIL FROM address according to
+# $smtpd_sender_login_maps (see above). This still allows
+# unauthenticated clients to impersonate the sender.
+# reject_unauthenticated_sender_login_mismatch:
+# reject if $smtpd_sender_login_maps specifies a MAIL FROM
+# address owner, but the client is not (SASL) logged in.
+# This prevents unauthenticated clients from impersonating
+# the sender.
# reject_non_fqdn_sender: reject sender address that is not in FQDN form
# check_policy_service transport:endpoint: delegate the decision to
# an external policy server. See SMTPD_POLICY_README for details.
# $virtual_alias_domains, or $virtual_mailbox_domains.
# - to destinations matching $relay_domains or subdomain thereof,
# except for addresses with sender-specified routing.
-# reject_unauth_destination: reject mail unless it is sent
+# *reject_unauth_destination: reject mail unless it is sent
# - to destinations matching $inet_interfaces, $mydestination,
# $virtual_alias_domains, or $virtual_mailbox_domains.
# - to destinations matching $relay_domains or subdomain thereof,
Timeout for sending the <b>HELO</b> command, and for
receiving the server response.
- <b>smtp_xclient_timeout</b>
- Timeout for sending the <b>XCLIENT</b> command, and for
+ <b>smtp_xforward_timeout</b>
+ Timeout for sending the <b>XFORWARD</b> command, and for
receiving the server response.
<b>smtp_mail_timeout</b>
keep trying until a suitable MX host resolves or
until the mail is too old.
- <b>smtp_send_xclient_command</b>
- If the SMTP server announces XCLIENT support, send
+ <b>smtp_send_xforward_command</b>
+ If the SMTP server announces XFORWARD support, send
the name, address, protocol and HELO name of the
original client. This can be used to forward client
information through a content filter to a down-
Maps that specify the SASL login name that owns a
MAIL FROM sender address. Used by the
<b>reject_sender_login_mismatch</b> sender anti-spoofing
- restriction.
+ restriction, as well as by its component restric-
+ tions <b>reject_authenticated_sender_login_mismatch</b>
+ (an authenticated client can't use a MAIL FROM
+ sender address that is owned by someone else) and
+ <b>reject_unauthenticated_sender_login_mismatch</b> (a
+ client must be authenticated in order to use the
+ MAIL FROM sender address).
<b>Miscellaneous</b>
<b>smtpd_authorized_verp_clients</b>
<b>smtpd_authorized_xclient_hosts</b>
Hostnames, domain names and/or addresses of clients
that are authorized to use the XCLIENT command.
- This command changes client information for access
- control and/or logging purposes, with the exception
- of the <b>smtpd_authorized_xclient_hosts</b> access con-
- trol itself.
+ This command overrides client information for
+ access control and logging purposes, with the
+ exception of the <b>smtpd_authorized_xclient_hosts</b>
+ access control itself.
+
+ <b>smtpd_authorized_xforward_hosts</b>
+ Hostnames, domain names and/or addresses of clients
+ that are authorized to use the XFORWARD command.
+ This command accepts client and message identofying
+ information for logging purposes.
<b>debug_peer_level</b>
- Increment in verbose logging level when a remote
+ Increment in verbose logging level when a remote
host matches a pattern in the <b>debug_peer_list</b>
parameter.
<b>debug_peer_list</b>
- List of domain or network patterns. When a remote
- host matches a pattern, increase the verbose log-
- ging level by the amount specified in the
+ List of domain or network patterns. When a remote
+ host matches a pattern, increase the verbose log-
+ ging level by the amount specified in the
<b>debug_peer_level</b> parameter.
<b>default_verp_delimiters</b>
The default VERP delimiter characters that are used
- when the XVERP command is specified without
+ when the XVERP command is specified without
explicit delimiters.
<b>error_notice_recipient</b>
- Recipient of protocol/policy/resource/software
+ Recipient of protocol/policy/resource/software
error notices.
<b>hopcount_limit</b>
<b>notify_classes</b>
List of error classes. Of special interest are:
- <b>policy</b> When a client violates any policy, mail a
+ <b>policy</b> When a client violates any policy, mail a
transcript of the entire SMTP session to the
postmaster.
<b>protocol</b>
- When a client violates the SMTP protocol or
+ When a client violates the SMTP protocol or
issues an unimplemented command, mail a
transcript of the entire SMTP session to the
postmaster.
<b>smtpd_banner</b>
- Text that follows the <b>220</b> status code in the SMTP
+ Text that follows the <b>220</b> status code in the SMTP
greeting banner.
<b>smtpd_expansion_filter</b>
expansion of rbl template responses and other text.
<b>smtpd_recipient_limit</b>
- Restrict the number of recipients that the SMTP
+ Restrict the number of recipients that the SMTP
server accepts per message delivery.
<b>smtpd_timeout</b>
- Limit the time to send a server response and to
+ Limit the time to send a server response and to
receive a client request.
<b>soft_bounce</b>
- Change hard (5xx) reject responses into soft (4xx)
- reject responses. This can be useful for testing
+ Change hard (5xx) reject responses into soft (4xx)
+ reject responses. This can be useful for testing
purposes.
<b>verp_delimiter_filter</b>
- The characters that Postfix accepts as VERP delim-
+ The characters that Postfix accepts as VERP delim-
iter characters.
<b>Known versus unknown recipients</b>
<b>show_user_unknown_table_name</b>
- Whether or not to reveal the table name in the
- "User unknown" responses. The extra detail makes
- trouble shooting easier but also reveals informa-
+ Whether or not to reveal the table name in the
+ "User unknown" responses. The extra detail makes
+ trouble shooting easier but also reveals informa-
tion that is nobody elses business.
<b>unknown_local_recipient_reject_code</b>
The response code when a client specifies a recipi-
- ent whose domain matches <b>$mydestination</b> or
+ ent whose domain matches <b>$mydestination</b> or
<b>$inet_interfaces</b>, while <b>$local_recipient_maps</b> is
- non-empty and does not list the recipient address
+ non-empty and does not list the recipient address
or address local-part.
<b>unknown_relay_recipient_reject_code</b>
The response code when a client specifies a recipi-
ent whose domain matches <b>$relay_domains</b>, while
- <b>$relay_recipient_maps</b> is non-empty and does not
+ <b>$relay_recipient_maps</b> is non-empty and does not
list the recipient address.
<b>unknown_virtual_alias_reject_code</b>
The response code when a client specifies a recipi-
- ent whose domain matches <b>$virtual_alias_domains</b>,
- while the recipient is not listed in <b>$vir-</b>
+ ent whose domain matches <b>$virtual_alias_domains</b>,
+ while the recipient is not listed in <b>$vir-</b>
<b>tual_alias_maps</b>.
<b>unknown_virtual_mailbox_reject_code</b>
The response code when a client specifies a recipi-
- ent whose domain matches <b>$virtual_mailbox_domains</b>,
+ ent whose domain matches <b>$virtual_mailbox_domains</b>,
while the recipient is not listed in <b>$virtual_mail-</b>
<b>box_maps</b>.
<b>Resource controls</b>
<b>line_length_limit</b>
- Limit the amount of memory in bytes used for the
+ Limit the amount of memory in bytes used for the
handling of partial input lines.
<b>message_size_limit</b>
ing on-disk storage for envelope information.
<b>queue_minfree</b>
- Minimal amount of free space in bytes in the queue
- file system for the SMTP server to accept any mail
- at all (default: twice the <b>message_size_limit</b>
+ Minimal amount of free space in bytes in the queue
+ file system for the SMTP server to accept any mail
+ at all (default: twice the <b>message_size_limit</b>
value).
<b>smtpd_history_flush_threshold</b>
<b>smtpd_client_connection_count_limit</b>
The maximal number of simultaneous connections that
- any client is allowed to make to this service.
- When a client exceeds the limit, the SMTP server
+ any client is allowed to make to this service.
+ When a client exceeds the limit, the SMTP server
logs a warning with the client name/address and the
service name as configured in master.cf.
<b>smtpd_client_connection_rate_limit</b>
- The maximal number of connections per unit time
+ The maximal number of connections per unit time
(specified with <b>connection_rate_time_unit</b>) that any
- client is allowed to make to this service. When a
- client exceeds the limit, the SMTP server logs a
- warning with the client name/address and the ser-
+ client is allowed to make to this service. When a
+ client exceeds the limit, the SMTP server logs a
+ warning with the client name/address and the ser-
vice name as configured in master.cf.
<b>smtpd_client_connection_limit_exceptions</b>
- Hostnames, .domain names and/or network address
+ Hostnames, .domain names and/or network address
blocks of clients that are excluded from connection
count or rate limits.
<b>smtpd_soft_error_limit</b>
When an SMTP client has made this number of errors,
- wait <i>error</i><b>_</b><i>count</i> seconds before responding to any
+ wait <i>error</i><b>_</b><i>count</i> seconds before responding to any
client request.
<b>smtpd_hard_error_limit</b>
- Disconnect after a client has made this number of
+ Disconnect after a client has made this number of
errors.
<b>smtpd_junk_command_limit</b>
Limit the number of times a client can issue a junk
- command such as NOOP, VRFY, ETRN or RSET in one
- SMTP session before it is penalized with tarpit
+ command such as NOOP, VRFY, ETRN or RSET in one
+ SMTP session before it is penalized with tarpit
delays.
<b>Delegated policy</b>
receiving from a delegated SMTPD policy server.
<b>smtpd_policy_service_max_idle</b>
- Time after which an unused SMTPD policy service
+ Time after which an unused SMTPD policy service
connection is closed.
<b>smtpd_policy_service_timeout</b>
- Time after which an active SMTPD policy service
+ Time after which an active SMTPD policy service
connection is closed.
<b>UCE control restrictions</b>
<b>parent_domain_matches_subdomains</b>
- List of Postfix features that use <i>domain.tld</i> pat-
- terns to match <i>sub.domain.tld</i> (as opposed to
+ List of Postfix features that use <i>domain.tld</i> pat-
+ terns to match <i>sub.domain.tld</i> (as opposed to
requiring <i>.domain.tld</i> patterns).
<b>smtpd_client_restrictions</b>
tem.
<b>smtpd_helo_required</b>
- Require that clients introduce themselves at the
+ Require that clients introduce themselves at the
beginning of an SMTP session.
<b>smtpd_helo_restrictions</b>
- Restrict what client hostnames are allowed in <b>HELO</b>
+ Restrict what client hostnames are allowed in <b>HELO</b>
and <b>EHLO</b> commands.
<b>smtpd_sender_restrictions</b>
- Restrict what sender addresses are allowed in <b>MAIL</b>
+ Restrict what sender addresses are allowed in <b>MAIL</b>
<b>FROM</b> commands.
<b>smtpd_recipient_restrictions</b>
- Restrict what recipient addresses are allowed in
+ Restrict what recipient addresses are allowed in
<b>RCPT TO</b> commands.
<b>smtpd_etrn_restrictions</b>
mands, and what clients may issue <b>ETRN</b> commands.
<b>smtpd_data_restrictions</b>
- Restrictions on the <b>DATA</b> command. Currently, the
- only restriction that makes sense here is
+ Restrictions on the <b>DATA</b> command. Currently, the
+ only restriction that makes sense here is
<b>reject_unauth_pipelining</b>.
<b>allow_untrusted_routing</b>
- Allow untrusted clients to specify addresses with
- sender-specified routing. Enabling this opens up
- nasty relay loopholes involving trusted backup MX
+ Allow untrusted clients to specify addresses with
+ sender-specified routing. Enabling this opens up
+ nasty relay loopholes involving trusted backup MX
hosts.
<b>smtpd_restriction_classes</b>
- Declares the name of zero or more parameters that
- contain a list of UCE restrictions. The names of
- these parameters can then be used instead of the
+ Declares the name of zero or more parameters that
+ contain a list of UCE restrictions. The names of
+ these parameters can then be used instead of the
restriction lists that they represent.
<b>smtpd_null_access_lookup_key</b>
- The lookup key to be used in SMTPD access tables
- instead of the null sender address. A null sender
+ The lookup key to be used in SMTPD access tables
+ instead of the null sender address. A null sender
address cannot be looked up.
<b>maps_rbl_domains</b> (deprecated)
- List of DNS domains that publish the addresses of
+ List of DNS domains that publish the addresses of
blacklisted hosts. This is used with the deprecated
<b>reject_maps_rbl</b> restriction.
<b>permit_mx_backup_networks</b>
- Only domains whose primary MX hosts match the
- listed networks are eligible for the <b>per-</b>
+ Only domains whose primary MX hosts match the
+ listed networks are eligible for the <b>per-</b>
<b>mit_mx_backup</b> feature.
<b>relay_domains</b>
- Restrict what domains this mail system will relay
- mail to. The domains are routed to the delivery
+ Restrict what domains this mail system will relay
+ mail to. The domains are routed to the delivery
agent specified with the <b>relay_transport</b> setting.
<b>Sender/recipient address verification</b>
Address verification is implemented by sending probe email
- messages that are not actually delivered, and is enabled
- via the reject_unverified_{sender,recipient} access
- restriction. The status of verification probes is main-
+ messages that are not actually delivered, and is enabled
+ via the reject_unverified_{sender,recipient} access
+ restriction. The status of verification probes is main-
tained by the address verification service.
<b>address_verify_poll_count</b>
- How many times to query the address verification
- service for completion of an address verification
- request. Specify 1 to implement a simple form of
- greylisting, that is, always defer the request for
+ How many times to query the address verification
+ service for completion of an address verification
+ request. Specify 1 to implement a simple form of
+ greylisting, that is, always defer the request for
a new sender or recipient address.
<b>address_verify_poll_delay</b>
- Time to wait after querying the address verifica-
+ Time to wait after querying the address verifica-
tion service for completion of an address verifica-
tion request.
<b>UCE control responses</b>
<b>access_map_reject_code</b>
- Response code when a client violates an access
+ Response code when a client violates an access
database restriction.
<b>default_rbl_reply</b>
Default template reply when a request is RBL black-
- listed. This template is used by the <b>reject_rbl_*</b>
- and <b>reject_rhsbl_*</b> restrictions. See also:
+ listed. This template is used by the <b>reject_rbl_*</b>
+ and <b>reject_rhsbl_*</b> restrictions. See also:
<b>rbl_reply_maps</b> and <b>smtpd_expansion_filter</b>.
<b>defer_code</b>
- Response code when a client request is rejected by
+ Response code when a client request is rejected by
the <b>defer</b> restriction.
<b>invalid_hostname_reject_code</b>
- Response code when a client violates the
+ Response code when a client violates the
<b>reject_invalid_hostname</b> restriction.
<b>maps_rbl_reject_code</b>
Response code when a request is RBL blacklisted.
<b>multi_recipient_bounce_reject_code</b>
- Response code when a multi-recipient bounce is
+ Response code when a multi-recipient bounce is
blocked.
<b>rbl_reply_maps</b>
- Table with template responses for RBL blacklisted
- requests, indexed by RBL domain name. These tem-
+ Table with template responses for RBL blacklisted
+ requests, indexed by RBL domain name. These tem-
plates are used by the <b>reject_rbl_*</b> and
- <b>reject_rhsbl_*</b> restrictions. See also:
+ <b>reject_rhsbl_*</b> restrictions. See also:
<b>default_rbl_reply</b> and <b>smtpd_expansion_filter</b>.
<b>reject_code</b>
- Response code when the client matches a <b>reject</b>
+ Response code when the client matches a <b>reject</b>
restriction.
<b>relay_domains_reject_code</b>
mail relay policy.
<b>unknown_address_reject_code</b>
- Response code when a client violates the
+ Response code when a client violates the
<b>reject_unknown_address</b> restriction.
<b>unknown_client_reject_code</b>
tion.
<b>unknown_hostname_reject_code</b>
- Response code when a client violates the
+ Response code when a client violates the
<b>reject_unknown_hostname</b> restriction.
<b>unverified_sender_reject_code</b>
- Response code when a sender address is known to be
+ Response code when a sender address is known to be
undeliverable.
<b>unverified_recipient_reject_code</b>
- Response code when a recipient address is known to
+ Response code when a recipient address is known to
be undeliverable.
<b>SEE ALSO</b>
<a href="verify.8.html">verify(8)</a> address verification service
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
<dt> <b>reject_sender_login_mismatch</b>
<dd> Reject the request when <a href="#smtpd_sender_login_maps">
-$smtpd_sender_owner_maps</a> specifies an owner for the MAIL FROM
+$smtpd_sender_login_maps</a> specifies an owner for the MAIL FROM
address, but the client is not (SASL) logged in as that MAIL FROM
address owner; or when the client is (SASL) logged in, but the
-client login name doesn't own the MAIL FROM address according to
-<a href="#smtpd_sender_login_maps"> $smtpd_sender_login_maps</a>.
+client login name doesn't own the MAIL FROM address.
+
+<p>
+
+<a name="reject_authenticated_sender_login_mismatch">
+
+<dt> <b>reject_authenticated_sender_login_mismatch</b>
+
+<dd> Reject the request when the client is (SASL) logged in but
+the client login name doesn't own the MAIL FROM address according
+to <a href="#smtpd_sender_login_maps">$smtpd_sender_login_maps</a>.
+
+<p>
+
+<a name="reject_unauthenticated_sender_login_mismatch">
+
+<dt> <b>reject_unauthenticated_sender_login_mismatch</b>
+
+<dd> Reject the request when <a href="#smtpd_sender_login_maps">
+$smtpd_sender_login_maps</a> specifies an owner for the address,
+but the client is not (SASL) logged in.
<p>
<dt> <b>smtpd_sender_login_maps</b>
<dd>This parameter specifies ownership of MAIL FROM addresses, as
-used by the <a href="#reject_sender_login_mismatch">
-reject_sender_login_mismatch</a> sender address restriction.
+used by the <a
+href="#reject_sender_login_mismatch">reject_sender_login_mismatch</a>
+anti-spoofing restriction and by its component sender address
+restrictions: <a href="#reject_authenticated_sender_login_mismatch">
+reject_authenticated_sender_login_mismatch</a> and <a
+href="#reject_unauthenticated_sender_login_mismatch">
+reject_unauthenticated_sender_login_mismatch</a>.
<p>
<p>
-Each map entry specifies a sender address and the login name that
-owns the address. The search order is:
+Each map entry specifies a sender address and a list of login names
+(separated by whitespace and/or commas) that owns the address. The
+search order is:
<p>
<dl>
-<dt><i>user@domain owner</i>
+<dt><i>user@domain</i> <i>owner, owner, ...</i>
<dd>This form has the highest precedence.
<p>
-<dt><i>user owner</i>
+<dt><i>user</i> <i>owner, owner, ...</i>
-<dd>This matches <i>user@site</i> when <i>site</i> is equal to <a
-href="basic.html#myorigin"> $myorigin</a>, when <i>site</i> is
+<dd>This matches <i>user@site</i> when <i>site</i> is equal to
+<a href="basic.html#myorigin"> $myorigin</a>, when <i>site</i> is
listed in <a href="basic.html#mydestination"> $mydestination</a>,
or when it is listed in <a href="basic.html#inet_interfaces">
$inet_interfaces</a>.
<p>
-<dt><i>@domain owner</i>
+<dt><i>@domain</i> <i>owner, owner, ...</i>
<dd>This matches every address in the specified domain, and has
the lowest precedence.
.IP \fBsmtp_helo_timeout\fR
Timeout for sending the \fBHELO\fR command, and for
receiving the server response.
-.IP \fBsmtp_xclient_timeout\fR
-Timeout for sending the \fBXCLIENT\fR command, and for
+.IP \fBsmtp_xforward_timeout\fR
+Timeout for sending the \fBXFORWARD\fR command, and for
receiving the server response.
.IP \fBsmtp_mail_timeout\fR
Timeout for sending the \fBMAIL FROM\fR command, and for
than the local MTA).
If yes, keep trying until a suitable MX host resolves or until
the mail is too old.
-.IP \fBsmtp_send_xclient_command\fR
-If the SMTP server announces XCLIENT support, send the name,
+.IP \fBsmtp_send_xforward_command\fR
+If the SMTP server announces XFORWARD support, send the name,
address, protocol and HELO name of the original client. This
can be used to forward client information through a content
filter to a downstream queuing SMTP server.
.IP \fBsmtpd_sender_login_maps\fR
Maps that specify the SASL login name that owns a MAIL FROM sender
address. Used by the \fBreject_sender_login_mismatch\fR sender
-anti-spoofing restriction.
+anti-spoofing restriction, as well as by its component restrictions
+\fBreject_authenticated_sender_login_mismatch\fR (an authenticated
+client can't use a MAIL FROM sender address that is owned by someone
+else) and \fBreject_unauthenticated_sender_login_mismatch\fR (a client
+must be authenticated in order to use the MAIL FROM sender address).
.SH Miscellaneous
.ad
.fi
authorized to use the XVERP extension.
.IP \fBsmtpd_authorized_xclient_hosts\fR
Hostnames, domain names and/or addresses of clients that are
-authorized to use the XCLIENT command. This command changes
-client information for access control and/or logging purposes,
+authorized to use the XCLIENT command. This command overrides
+client information for access control and logging purposes,
with the exception of the
\fBsmtpd_authorized_xclient_hosts\fR access control itself.
+.IP \fBsmtpd_authorized_xforward_hosts\fR
+Hostnames, domain names and/or addresses of clients that are
+authorized to use the XFORWARD command. This command accepts
+client and message identofying information for logging purposes.
.IP \fBdebug_peer_level\fR
Increment in verbose logging level when a remote host matches a
pattern in the \fBdebug_peer_list\fR parameter.
quote_821_local: quote_821_local.c $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS)
-peer_name: $(LIB)
- mv $@.o junk
- $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
- mv junk $@.o
-
mail_conf_time: $(LIB) $(LIBS)
mv $@.o junk
$(CC) -DTEST $(CFLAGS) -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
own_inet_addr.o: ../../include/vbuf.h
own_inet_addr.o: mail_params.h
own_inet_addr.o: own_inet_addr.h
-peer_name.o: peer_name.c
-peer_name.o: ../../include/sys_defs.h
-peer_name.o: ../../include/msg.h
-peer_name.o: ../../include/valid_hostname.h
-peer_name.o: peer_name.h
pipe_command.o: pipe_command.c
pipe_command.o: ../../include/sys_defs.h
pipe_command.o: ../../include/msg.h
* I have seen this happen just too often.
*/
if (strcasecmp(var_myhostname, var_relayhost) == 0)
- msg_fatal("myhostname == relayhost");
+ msg_fatal("%s and %s parameter settings must not be identical: %s",
+ VAR_MYHOSTNAME, VAR_RELAYHOST, var_myhostname);
/*
* XXX These should be caught by a proper parameter parsing algorithm.
*/
if (var_myorigin[strcspn(var_myorigin, ", \t\r\n")])
- msg_fatal("myorigin parameter setting contains multiple values: %s",
- var_myorigin);
+ msg_fatal("%s parameter setting must not contain multiple values: %s",
+ VAR_MYORIGIN, var_myorigin);
if (var_relayhost[strcspn(var_relayhost, ", \t\r\n")])
- msg_fatal("relayhost parameter setting contains multiple values: %s",
- var_relayhost);
+ msg_fatal("%s parameter setting must not contain multiple values: %s",
+ VAR_RELAYHOST, var_relayhost);
/*
* One more sanity check.
#define DEF_SMTP_DEFER_MXADDR 0
extern bool var_smtp_defer_mxaddr;
-#define VAR_SMTP_SEND_XCLIENT "smtp_send_xclient_command"
-#define DEF_SMTP_SEND_XCLIENT 0
-extern bool var_smtp_send_xclient;
+#define VAR_SMTP_SEND_XFORWARD "smtp_send_xforward_command"
+#define DEF_SMTP_SEND_XFORWARD 0
+extern bool var_smtp_send_xforward;
/*
* SMTP server. The soft error limit determines how many errors an SMTP
extern char *var_smtpd_snd_auth_maps;
#define REJECT_SENDER_LOGIN_MISMATCH "reject_sender_login_mismatch"
+#define REJECT_AUTH_SENDER_LOGIN_MISMATCH \
+ "reject_authenticated_sender_login_mismatch"
+#define REJECT_UNAUTH_SENDER_LOGIN_MISMATCH \
+ "reject_unauthenticated_sender_login_mismatch"
/*
* SASL authentication support, SMTP client side.
extern char *var_verp_clients;
/*
- * XCLIENT, for rule testing and improved post-filter logging.
+ * XCLIENT, for rule testing and fetchmail like apps.
*/
#define VAR_XCLIENT_HOSTS "smtpd_authorized_xclient_hosts"
#define DEF_XCLIENT_HOSTS ""
extern char *var_xclient_hosts;
+ /*
+ * XFORWARD, for improved post-filter logging.
+ */
+#define VAR_XFORWARD_HOSTS "smtpd_authorized_xforward_hosts"
+#define DEF_XFORWARD_HOSTS ""
+extern char *var_xforward_hosts;
+
/*
* Inbound mail flow control. This allows for a stiffer coupling between
* receiving mail and sending mail. A sending process produces one token for
* XCLIENT in SMTP.
*/
#define XCLIENT_CMD "XCLIENT" /* XCLIENT command */
-#define XCLIENT_OVERRIDE "OVERRIDE" /* override function */
-#define XCLIENT_FORWARD "FORWARD" /* forward function */
-#define XCLIENT_NAME "CLIENT_NAME" /* client name */
-#define XCLIENT_ADDR "CLIENT_ADDR" /* client address */
-#define XCLIENT_PROTO "CLIENT_PROTO" /* client protocol */
-#define XCLIENT_CODE "CLIENT_CODE" /* client name status */
-#define XCLIENT_HELO "CLIENT_HELO" /* client helo */
-
- /*
- * This is how Postfix represents unknown client information within smtpd or
- * qmqpd processes.
- *
- * This is not the representation that Postfix uses in queue files, in queue
- * manager delivery requests, nor is it the representation of information in
- * XCLIENT commands!
- */
-#define CLIENT_ATTR_UNKNOWN "unknown"
-
-#define CLIENT_NAME_UNKNOWN CLIENT_ATTR_UNKNOWN
-#define CLIENT_ADDR_UNKNOWN CLIENT_ATTR_UNKNOWN
-#define CLIENT_NAMADDR_UNKNOWN CLIENT_ATTR_UNKNOWN
-#define CLIENT_HELO_UNKNOWN 0
-#define CLIENT_PROTO_UNKNOWN CLIENT_ATTR_UNKNOWN
-
-#define IS_AVAIL_CLIENT_ATTR(v) ((v) && strcmp((v), CLIENT_ATTR_UNKNOWN))
-
-#define IS_AVAIL_CLIENT_NAME(v) IS_AVAIL_CLIENT_ATTR(v)
-#define IS_AVAIL_CLIENT_ADDR(v) IS_AVAIL_CLIENT_ATTR(v)
-#define IS_AVAIL_CLIENT_NAMADDR(v) IS_AVAIL_CLIENT_ATTR(v)
-#define IS_AVAIL_CLIENT_HELO(v) (v)
-#define IS_AVAIL_CLIENT_PROTO(v) IS_AVAIL_CLIENT_ATTR(v)
+#define XCLIENT_NAME "NAME" /* client name */
+#define XCLIENT_ADDR "ADDR" /* client address */
+#define XCLIENT_PROTO "PROTO" /* client protocol */
+#define XCLIENT_CODE "NAME_CODE" /* client name status */
+#define XCLIENT_HELO "HELO" /* client helo */
+
+#define XFORWARD_CMD "XFORWARD" /* XFORWARD command */
+#define XFORWARD_NAME "NAME" /* client name */
+#define XFORWARD_ADDR "ADDR" /* client address */
+#define XFORWARD_PROTO "PROTO" /* client protocol */
+#define XFORWARD_HELO "HELO" /* client helo */
+#define XFORWARD_IDENT "IDENT" /* message identifier */
/* LICENSE
/* .ad
{
const char *cp;
- if (*queue_id == 0 || strlen(queue_id) > 100)
+ if (*queue_id == 0 || strlen(queue_id) > VALID_HOSTNAME_LEN)
return (0);
/*
- * OK if in in time+inum form.
+ * OK if in in time+inum form or in host-name_domain_tld form.
*/
- for (cp = queue_id; /* void */ ; cp++) {
- if (*cp == 0)
- return (1);
- if (!ISALNUM(*cp))
- break;
- }
-
- /*
- * BAD if in time.pid form.
- */
- for (cp = queue_id; /* void */ ; cp++) {
- if (*cp == 0)
+ for (cp = queue_id; *cp; cp++)
+ if (!ISALNUM(*cp) && *cp != '_' && *cp != '-')
return (0);
- if (!ISDIGIT(*cp) && *cp != '.')
- break;
- }
-
- /*
- * OK if in valid hostname form.
- */
- return (valid_hostname(queue_id, DONT_GRIPE));
+ return (1);
}
/* mail_queue_enter - make mail queue entry with locally-unique name */
* 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 "20031207"
+#define MAIL_RELEASE_DATE "20031213"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "2.0.16-" MAIL_RELEASE_DATE
+++ /dev/null
-/*++
-/* NAME
-/* peer_name 3
-/* SUMMARY
-/* produce printable peer name and address
-/* SYNOPSIS
-/* #include <peer_name.h>
-/*
-/* typedef struct {
-/* .in +4
-/* int type;
-/* char name;
-/* char addr;
-/* .in -4
-/* } PEER_NAME;
-/*
-/* PEER_NAME *peer_name(sock)
-/* int sock;
-/* DESCRIPTION
-/* The \fIpeer_name\fR() routine attempts to produce a printable
-/* version of the peer name and address of the specified socket.
-/* The result is in static memory that will be overwritten.
-/* Make a copy if the result is to be used for an appreciable
-/* amount of time.
-/*
-/* Where information is unavailable, the name and/or address
-/* are set to "unknown".
-/* The \fItype\fR result field specifies how the name and address
-/* should be interpreted:
-/* .IP PEER_TYPE_INET
-/* The socket specifies a TCP/IP endpoint.
-/* The result is a hostname (from the DNS, a local hosts file or
-/* other); the address a dotted quad.
-/* .IP PEER_TYPE_LOCAL
-/* The socket argument specifies a local transport.
-/* The result name is "localhost"; the result address is "127.0.0.1".
-/* .IP PEER_TYPE_UNKNOWN
-/* The socket argument does not specify a socket.
-/* The result name is "localhost"; the result address is "127.0.0.1".
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <errno.h>
-#include <netdb.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <valid_hostname.h>
-#include <peer_name.h>
-
-/* peer_name - produce printable peer name and address */
-
-PEER_NAME *peer_name(int sock)
-{
- static PEER_NAME peer;
- struct sockaddr_in sin;
- SOCKADDR_SIZE len = sizeof(sin);
- struct hostent *hp;
-
- if (getpeername(sock, (struct sockaddr *) & sin, &len) == 0) {
- switch (sin.sin_family) {
- case AF_INET:
- peer.type = PEER_TYPE_INET;
- hp = gethostbyaddr((char *) &(sin.sin_addr),
- sizeof(sin.sin_addr), AF_INET);
- peer.name = (hp && valid_hostname(hp->h_name, DO_GRIPE) ?
- hp->h_name : "unknown");
- peer.addr = inet_ntoa(sin.sin_addr);
- return (&peer);
- case AF_UNSPEC:
- case AF_UNIX:
- peer.type = PEER_TYPE_LOCAL;
- peer.name = "localhost";
- peer.addr = "127.0.0.1";
- return (&peer);
- }
- }
- peer.type = PEER_TYPE_UNKNOWN;
- peer.name = "localhost";
- peer.addr = "127.0.0.1";
- return (&peer);
-
-}
-
-#ifdef TEST
-
-#include <unistd.h>
-
-int main(int unused_argc, char **unused_argv)
-{
- PEER_NAME *peer;
-
- peer = peer_name(STDIN_FILENO);
- msg_info("name %s addr %s", peer->name, peer->addr);
-}
-
-#endif
+++ /dev/null
-#ifndef _PEER_NAME_H_INCLUDED_
-#define _PEER_NAME_H_INCLUDED_
-
-/*++
-/* NAME
-/* peer_name 3h
-/* SUMMARY
-/* produce printable peer name and address
-/* SYNOPSIS
-/* #include <peer_name.h>
-/* DESCRIPTION
-
- /*
- * External interface.
- */
-typedef struct {
- int type; /* IPC type, see below */
- char *name; /* peer official name */
- char *addr; /* peer address */
-} PEER_NAME;
-
-#define PEER_TYPE_UNKNOWN 0
-#define PEER_TYPE_INET 1
-#define PEER_TYPE_LOCAL 2
-
-extern PEER_NAME *peer_name(int);
-
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-#endif
void recipient_list_add(RECIPIENT_LIST *list, long offset,
const char *orig_rcpt, const char *rcpt)
{
+ int new_avail;
+
if (list->len >= list->avail) {
- list->avail *= 2;
+ new_avail = list->avail * 2;
list->info = (RECIPIENT *)
- myrealloc((char *) list->info, list->avail * sizeof(RECIPIENT));
+ myrealloc((char *) list->info, new_avail * sizeof(RECIPIENT));
+ list->avail = new_avail;
}
list->info[list->len].orig_addr = mystrdup(orig_rcpt);
list->info[list->len].address = mystrdup(rcpt);
void qmgr_rcpt_list_add(QMGR_RCPT_LIST *list, long offset,
const char *orcpt, const char *rcpt)
{
+ int new_avail;
+
if (list->len >= list->avail) {
- list->avail *= 2;
+ new_avail = list->avail * 2;
list->info = (QMGR_RCPT *)
- myrealloc((char *) list->info, list->avail * sizeof(QMGR_RCPT));
+ myrealloc((char *) list->info, new_avail * sizeof(QMGR_RCPT));
+ list->avail = new_avail;
}
list->info[list->len].orig_rcpt = mystrdup(orcpt);
list->info[list->len].address = mystrdup(rcpt);
void qmgr_rcpt_list_add(QMGR_RCPT_LIST *list, long offset,
const char *orcpt, const char *rcpt)
{
+ int new_avail;
+
if (list->len >= list->avail) {
- list->avail *= 2;
+ new_avail = list->avail * 2;
list->info = (QMGR_RCPT *)
- myrealloc((char *) list->info, list->avail * sizeof(QMGR_RCPT));
+ myrealloc((char *) list->info, new_avail * sizeof(QMGR_RCPT));
+ list->avail = new_avail;
}
list->info[list->len].orig_rcpt = mystrdup(orcpt);
list->info[list->len].address = mystrdup(rcpt);
if (IS_AVAIL_CLIENT_ADDR(state->addr))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_CLIENT_ADDR, state->addr);
- rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_ORIGIN, state->namaddr);
+ if (IS_AVAIL_CLIENT_NAMADDR(state->namaddr))
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_ORIGIN, state->namaddr);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_PROTO_NAME, state->protocol);
}
VSTRING *why_rejected; /* REJECT reason */
} QMQPD_STATE;
+ /*
+ * Postfix representation unknown client information within qmqpd processes.
+ * This is not the representation that Postfix uses in queue files, in queue
+ * manager delivery requests, nor is it the representation of information in
+ * XCLIENT/XFORWARD commands!
+ */
+#define CLIENT_ATTR_UNKNOWN "unknown"
+
+#define CLIENT_NAME_UNKNOWN CLIENT_ATTR_UNKNOWN
+#define CLIENT_ADDR_UNKNOWN CLIENT_ATTR_UNKNOWN
+#define CLIENT_NAMADDR_UNKNOWN CLIENT_ATTR_UNKNOWN
+
+#define IS_AVAIL_CLIENT_ATTR(v) ((v) && strcmp((v), CLIENT_ATTR_UNKNOWN))
+
+#define IS_AVAIL_CLIENT_NAME(v) IS_AVAIL_CLIENT_ATTR(v)
+#define IS_AVAIL_CLIENT_ADDR(v) IS_AVAIL_CLIENT_ATTR(v)
+#define IS_AVAIL_CLIENT_NAMADDR(v) IS_AVAIL_CLIENT_ATTR(v)
+
/*
* QMQP protocol status codes.
*/
* If peer went away, give up.
*/
if (errno == ECONNRESET || errno == ECONNABORTED) {
- state->name = mystrdup("unknown");
- state->addr = mystrdup("unknown");
+ state->name = mystrdup(CLIENT_ATTR_UNKNOWN);
+ state->addr = mystrdup(CLIENT_ATTR_UNKNOWN);
}
/*
hp = gethostbyaddr((char *) &(sin.sin_addr),
sizeof(sin.sin_addr), AF_INET);
if (hp == 0) {
- state->name = mystrdup("unknown");
+ state->name = mystrdup(CLIENT_ATTR_UNKNOWN);
} else if (!valid_hostname(hp->h_name, DONT_GRIPE)) {
- state->name = mystrdup("unknown");
+ state->name = mystrdup(CLIENT_ATTR_UNKNOWN);
} else {
state->name = mystrdup(hp->h_name); /* hp->name is clobbered!! */
*/
#define REJECT_PEER_NAME(state) { \
myfree(state->name); \
- state->name = mystrdup("unknown"); \
+ state->name = mystrdup(CLIENT_ATTR_UNKNOWN); \
}
hp = gethostbyname(state->name); /* clobbers hp->name!! */
/* .IP \fBsmtp_helo_timeout\fR
/* Timeout for sending the \fBHELO\fR command, and for
/* receiving the server response.
-/* .IP \fBsmtp_xclient_timeout\fR
-/* Timeout for sending the \fBXCLIENT\fR command, and for
+/* .IP \fBsmtp_xforward_timeout\fR
+/* Timeout for sending the \fBXFORWARD\fR command, and for
/* receiving the server response.
/* .IP \fBsmtp_mail_timeout\fR
/* Timeout for sending the \fBMAIL FROM\fR command, and for
/* than the local MTA).
/* If yes, keep trying until a suitable MX host resolves or until
/* the mail is too old.
-/* .IP \fBsmtp_send_xclient_command\fR
-/* If the SMTP server announces XCLIENT support, send the name,
+/* .IP \fBsmtp_send_xforward_command\fR
+/* If the SMTP server announces XFORWARD support, send the name,
/* address, protocol and HELO name of the original client. This
/* can be used to forward client information through a content
/* filter to a downstream queuing SMTP server.
char *var_smtp_host_lookup;
bool var_smtp_quote_821_env;
bool var_smtp_defer_mxaddr;
-bool var_smtp_send_xclient;
+bool var_smtp_send_xforward;
/*
* Global variables. smtp_errno is set by the address lookup routines and by
VAR_SMTP_RAND_ADDR, DEF_SMTP_RAND_ADDR, &var_smtp_rand_addr,
VAR_SMTP_QUOTE_821_ENV, DEF_SMTP_QUOTE_821_ENV, &var_smtp_quote_821_env,
VAR_SMTP_DEFER_MXADDR, DEF_SMTP_DEFER_MXADDR, &var_smtp_defer_mxaddr,
- VAR_SMTP_SEND_XCLIENT, DEF_SMTP_SEND_XCLIENT, &var_smtp_send_xclient,
+ VAR_SMTP_SEND_XFORWARD, DEF_SMTP_SEND_XFORWARD, &var_smtp_send_xforward,
0,
};
#define SMTP_FEATURE_STARTTLS (1<<4)
#define SMTP_FEATURE_AUTH (1<<5)
#define SMTP_FEATURE_MAYBEPIX (1<<6) /* PIX smtp fixup mode */
-#define SMTP_FEATURE_XCLIENT (1<<7) /* server supports XCLIENT */
+#define SMTP_FEATURE_XFORWARD_NAME (1<<7)
+#define SMTP_FEATURE_XFORWARD_ADDR (1<<8)
+#define SMTP_FEATURE_XFORWARD_PROTO (1<<9)
+#define SMTP_FEATURE_XFORWARD_HELO (1<<10)
+
+#define SMTP_FEATURE_XFORWARD_MASK \
+ (SMTP_FEATURE_XFORWARD_NAME | SMTP_FEATURE_XFORWARD_ADDR \
+ | SMTP_FEATURE_XFORWARD_PROTO | SMTP_FEATURE_XFORWARD_HELO)
/*
* smtp.c
#include <mymalloc.h>
#include <iostuff.h>
#include <split_at.h>
+#include <name_code.h>
/* Global library. */
* SMTP_STATE_DOT) must have smaller numerical values than the non-sending
* states (SMTP_STATE_ABORT .. SMTP_STATE_LAST).
*/
-#define SMTP_STATE_XCLIENT_ADDR 0
-#define SMTP_STATE_XCLIENT_HELO 1
+#define SMTP_STATE_XFORWARD_ADDR 0
+#define SMTP_STATE_XFORWARD_HELO 1
#define SMTP_STATE_MAIL 2
#define SMTP_STATE_RCPT 3
#define SMTP_STATE_DATA 4
};
char *xfer_states[SMTP_STATE_LAST] = {
- "sending XCLIENT address and name",
- "sending XCLIENT helo_name and protocol",
+ "sending XFORWARD address and name",
+ "sending XFORWARD helo_name and protocol",
"sending MAIL FROM",
"sending RCPT TO",
"sending DATA command",
};
char *xfer_request[SMTP_STATE_LAST] = {
- "XCLIENT command",
- "XCLIENT command",
+ "XFORWARD name/address command",
+ "XFORWARD helo/protocol command",
"MAIL FROM command",
"RCPT TO command",
"DATA command",
char *words;
char *word;
int n;
+ static NAME_CODE xforward_features[] = {
+ XFORWARD_NAME, SMTP_FEATURE_XFORWARD_NAME,
+ XFORWARD_ADDR, SMTP_FEATURE_XFORWARD_ADDR,
+ XFORWARD_PROTO, SMTP_FEATURE_XFORWARD_PROTO,
+ XFORWARD_HELO, SMTP_FEATURE_XFORWARD_HELO,
+ 0, 0,
+ };
/*
* Prepare for disaster.
state->features |= SMTP_FEATURE_8BITMIME;
else if (strcasecmp(word, "PIPELINING") == 0)
state->features |= SMTP_FEATURE_PIPELINING;
- else if (strcasecmp(word, "XCLIENT") == 0)
- state->features |= SMTP_FEATURE_XCLIENT;
- else if (strcasecmp(word, "SIZE") == 0) {
+ else if (strcasecmp(word, "XFORWARD") == 0) {
+ while ((word = mystrtok(&words, " \t")) != 0)
+ state->features |= name_code(xforward_features,
+ NAME_CODE_FLAG_NONE, word);
+ } else if (strcasecmp(word, "SIZE") == 0) {
state->features |= SMTP_FEATURE_SIZE;
if ((word = mystrtok(&words, " \t")) != 0) {
if (!alldig(word))
* commands rejected, DATA rejected) it forces the sender to abort the
* SMTP dialog with RSET and QUIT.
*
- * Use the XCLIENT command to forward client attributes only when a minimal
+ * Use the XFORWARD command to forward client attributes only when a minimal
* amount of information is available.
*/
nrcpt = 0;
- if (var_smtp_send_xclient
- && (state->features & SMTP_FEATURE_XCLIENT)
+ if (var_smtp_send_xforward
+ && (state->features & SMTP_FEATURE_XFORWARD_MASK)
&& (DEL_REQ_ATTR_AVAIL(request->client_name)
|| DEL_REQ_ATTR_AVAIL(request->client_addr)))
- recv_state = send_state = SMTP_STATE_XCLIENT_ADDR;
+ recv_state = send_state = SMTP_STATE_XFORWARD_ADDR;
else
recv_state = send_state = SMTP_STATE_MAIL;
next_rcpt = send_rcpt = recv_rcpt = 0;
msg_panic("%s: bad sender state %d", myname, send_state);
/*
- * Build the XCLIENT command. With properly sanitized
+ * Build the XFORWARD command. With properly sanitized
* information, the command length stays within the 512 byte
* command line length limit.
*/
- case SMTP_STATE_XCLIENT_ADDR:
- vstring_strcpy(next_command,
- XCLIENT_CMD " " XCLIENT_FORWARD " " XCLIENT_NAME "=");
- if (DEL_REQ_ATTR_AVAIL(request->client_name))
- xtext_quote_append(next_command, request->client_name, "");
- vstring_strcat(next_command, " " XCLIENT_ADDR "=");
- if (DEL_REQ_ATTR_AVAIL(request->client_addr))
- xtext_quote_append(next_command, request->client_addr, "");
- next_state = SMTP_STATE_XCLIENT_HELO;
+ case SMTP_STATE_XFORWARD_ADDR:
+ vstring_strcpy(next_command, XFORWARD_CMD);
+ if (state->features & SMTP_FEATURE_XFORWARD_NAME) {
+ vstring_strcat(next_command, " " XCLIENT_NAME "=");
+ if (DEL_REQ_ATTR_AVAIL(request->client_name))
+ xtext_quote_append(next_command, request->client_name, "");
+ }
+ if (state->features & SMTP_FEATURE_XFORWARD_ADDR) {
+ vstring_strcat(next_command, " " XFORWARD_ADDR "=");
+ if (DEL_REQ_ATTR_AVAIL(request->client_addr))
+ xtext_quote_append(next_command, request->client_addr, "");
+ }
+ next_state = SMTP_STATE_XFORWARD_HELO;
break;
- case SMTP_STATE_XCLIENT_HELO:
- vstring_strcpy(next_command,
- XCLIENT_CMD " " XCLIENT_FORWARD " " XCLIENT_HELO "=");
- if (DEL_REQ_ATTR_AVAIL(request->client_helo))
- xtext_quote_append(next_command, request->client_helo, "");
- vstring_strcat(next_command, " " XCLIENT_PROTO "=");
- if (DEL_REQ_ATTR_AVAIL(request->client_proto))
- xtext_quote_append(next_command, request->client_proto, "");
+ case SMTP_STATE_XFORWARD_HELO:
+ vstring_strcpy(next_command, XFORWARD_CMD);
+ if (state->features & SMTP_FEATURE_XFORWARD_HELO) {
+ vstring_strcat(next_command, " " XCLIENT_HELO "=");
+ if (DEL_REQ_ATTR_AVAIL(request->client_helo))
+ xtext_quote_append(next_command, request->client_helo, "");
+ }
+ if (state->features & SMTP_FEATURE_XFORWARD_ADDR) {
+ vstring_strcat(next_command, " " XFORWARD_PROTO "=");
+ if (DEL_REQ_ATTR_AVAIL(request->client_proto))
+ xtext_quote_append(next_command, request->client_proto, "");
+ }
next_state = SMTP_STATE_MAIL;
break;
/*
* Sanity check.
*/
- if (recv_state < SMTP_STATE_XCLIENT_ADDR
+ if (recv_state < SMTP_STATE_XFORWARD_ADDR
|| recv_state > SMTP_STATE_QUIT)
msg_panic("%s: bad receiver state %d (sender state %d)",
myname, recv_state, send_state);
switch (recv_state) {
/*
- * Process the XCLIENT response.
+ * Process the XFORWARD response.
*/
- case SMTP_STATE_XCLIENT_ADDR:
- recv_state = SMTP_STATE_XCLIENT_HELO;
+ case SMTP_STATE_XFORWARD_ADDR:
+ if (resp->code / 100 != 2)
+ msg_warn("host %s said: %s (in reply to %s)",
+ session->namaddr,
+ translit(resp->str, "\n", " "),
+ xfer_request[SMTP_STATE_MAIL]);
+ recv_state = SMTP_STATE_XFORWARD_HELO;
break;
- case SMTP_STATE_XCLIENT_HELO:
+ case SMTP_STATE_XFORWARD_HELO:
+ if (resp->code / 100 != 2)
+ msg_warn("host %s said: %s (in reply to %s)",
+ session->namaddr,
+ translit(resp->str, "\n", " "),
+ xfer_request[SMTP_STATE_MAIL]);
recv_state = SMTP_STATE_MAIL;
break;
SHELL = /bin/sh
SRCS = smtpd.c smtpd_token.c smtpd_check.c smtpd_chat.c smtpd_state.c \
smtpd_peer.c smtpd_sasl_proto.c smtpd_sasl_glue.c smtpd_proxy.c \
- smtpd_xclient.c
+ smtpd_xforward.c
OBJS = smtpd.o smtpd_token.o smtpd_check.o smtpd_chat.o smtpd_state.o \
smtpd_peer.o smtpd_sasl_proto.o smtpd_sasl_glue.o smtpd_proxy.o \
- smtpd_xclient.o
+ smtpd_xforward.o
HDRS = smtpd_token.h smtpd_check.h smtpd_chat.h smtpd_sasl_proto.h \
smtpd_sasl_glue.h smtpd_proxy.h
TESTSRC = smtpd_token_test.c
../../libexec/$(PROG): $(PROG)
cp $(PROG) ../../libexec
-SMTPD_CHECK_OBJ = smtpd_state.o smtpd_peer.o smtpd_xclient.o
+SMTPD_CHECK_OBJ = smtpd_state.o smtpd_peer.o smtpd_xforward.o
smtpd_token: smtpd_token.c $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS)
smtpd.o: ../../include/watchdog.h
smtpd.o: ../../include/iostuff.h
smtpd.o: ../../include/split_at.h
+smtpd.o: ../../include/name_code.h
smtpd.o: ../../include/mail_params.h
smtpd.o: ../../include/record.h
smtpd.o: ../../include/rec_type.h
smtpd_token.o: smtpd_token.h
smtpd_token.o: ../../include/vstring.h
smtpd_token.o: ../../include/vbuf.h
-smtpd_xclient.o: smtpd_xclient.c
-smtpd_xclient.o: ../../include/sys_defs.h
-smtpd_xclient.o: ../../include/mymalloc.h
-smtpd_xclient.o: ../../include/msg.h
-smtpd_xclient.o: ../../include/mail_proto.h
-smtpd_xclient.o: ../../include/vstream.h
-smtpd_xclient.o: ../../include/vbuf.h
-smtpd_xclient.o: ../../include/iostuff.h
-smtpd_xclient.o: ../../include/attr.h
-smtpd_xclient.o: smtpd.h
-smtpd_xclient.o: ../../include/vstring.h
-smtpd_xclient.o: ../../include/argv.h
-smtpd_xclient.o: ../../include/mail_stream.h
+smtpd_xforward.o: smtpd_xforward.c
+smtpd_xforward.o: ../../include/sys_defs.h
+smtpd_xforward.o: ../../include/mymalloc.h
+smtpd_xforward.o: ../../include/msg.h
+smtpd_xforward.o: ../../include/mail_proto.h
+smtpd_xforward.o: ../../include/vstream.h
+smtpd_xforward.o: ../../include/vbuf.h
+smtpd_xforward.o: ../../include/iostuff.h
+smtpd_xforward.o: ../../include/attr.h
+smtpd_xforward.o: smtpd.h
+smtpd_xforward.o: ../../include/vstring.h
+smtpd_xforward.o: ../../include/argv.h
+smtpd_xforward.o: ../../include/mail_stream.h
/* .IP \fBsmtpd_sender_login_maps\fR
/* Maps that specify the SASL login name that owns a MAIL FROM sender
/* address. Used by the \fBreject_sender_login_mismatch\fR sender
-/* anti-spoofing restriction.
+/* anti-spoofing restriction, as well as by its component restrictions
+/* \fBreject_authenticated_sender_login_mismatch\fR (an authenticated
+/* client can't use a MAIL FROM sender address that is owned by someone
+/* else) and \fBreject_unauthenticated_sender_login_mismatch\fR (a client
+/* must be authenticated in order to use the MAIL FROM sender address).
/* .SH Miscellaneous
/* .ad
/* .fi
/* authorized to use the XVERP extension.
/* .IP \fBsmtpd_authorized_xclient_hosts\fR
/* Hostnames, domain names and/or addresses of clients that are
-/* authorized to use the XCLIENT command. This command changes
-/* client information for access control and/or logging purposes,
+/* authorized to use the XCLIENT command. This command overrides
+/* client information for access control and logging purposes,
/* with the exception of the
/* \fBsmtpd_authorized_xclient_hosts\fR access control itself.
+/* .IP \fBsmtpd_authorized_xforward_hosts\fR
+/* Hostnames, domain names and/or addresses of clients that are
+/* authorized to use the XFORWARD command. This command accepts
+/* client and message identofying information for logging purposes.
/* .IP \fBdebug_peer_level\fR
/* Increment in verbose logging level when a remote host matches a
/* pattern in the \fBdebug_peer_list\fR parameter.
int var_smtpd_policy_idle;
int var_smtpd_policy_ttl;
char *var_xclient_hosts;
+char *var_xforward_hosts;
int var_smtpd_crate_limit;
int var_smtpd_cconn_limit;
char *var_smtpd_hoggers;
static NAMADR_LIST *xclient_hosts;
static int xclient_allowed;
+ /*
+ * XFORWARD command. Access control is cached.
+ */
+static NAMADR_LIST *xforward_hosts;
+static int xforward_allowed;
+
/*
* Client connection and rate limiting.
*/
smtpd_chat_reply(state, "250-%s", VERP_CMD);
/* XCLIENT must not override its own access control. */
if (xclient_allowed)
- smtpd_chat_reply(state, "250-%s", XCLIENT_CMD);
+ smtpd_chat_reply(state, "250-" XCLIENT_CMD
+ " " XCLIENT_NAME " " XCLIENT_ADDR
+ " " XCLIENT_CODE " " XCLIENT_PROTO
+ " " XCLIENT_HELO);
+ if (xforward_allowed)
+ smtpd_chat_reply(state, "250-" XFORWARD_CMD
+ " " XFORWARD_NAME " " XFORWARD_ADDR
+ " " XFORWARD_PROTO " " XFORWARD_HELO);
smtpd_chat_reply(state, "250 8BITMIME");
return (0);
}
(void) smtpd_proxy_cmd(state, SMTPD_PROX_WANT_NONE, "QUIT");
smtpd_proxy_close(state);
}
- if (state->xclient.used)
- smtpd_xclient_reset(state);
+ if (state->xforward.flags)
+ smtpd_xforward_reset(state);
}
/* rcpt_cmd - process RCPT TO command */
rec_fprintf(state->cleanup, REC_TYPE_FLGS, "%d", state->saved_flags);
rec_fputs(state->cleanup, REC_TYPE_MESG, "");
}
- if (!state->proxy || state->xclient.used == 0) {
+ if (!state->proxy || state->xforward.flags == 0) {
out_fprintf(out_stream, REC_TYPE_NORM,
"Received: from %s (%s [%s])",
state->helo_name ? state->helo_name : state->name,
return (0);
}
- /*
- * Lookup tables with xclient/normal attribute offsets. Maybe we should not
- * try so hard to make XOVERRIDE and XFORWARD attribute lists identical.
- */
-#define FUNC_OVERRIDE 0
-#define FUNC_FORWARD 1
-
-struct attr_offset {
- int name[2];
- int addr[2];
- int namaddr[2];
- int peer_code[2];
- int protocol[2];
- int helo_name[2];
-};
-
-#define ATTR_OFFSETS(member) \
- offsetof(SMTPD_STATE, member), offsetof(SMTPD_STATE, xclient.member)
-
-static const struct attr_offset attr_offset = {
- ATTR_OFFSETS(name),
- ATTR_OFFSETS(addr),
- ATTR_OFFSETS(namaddr),
- ATTR_OFFSETS(peer_code),
- ATTR_OFFSETS(protocol),
- ATTR_OFFSETS(helo_name)
-};
-
-#define PTR_ATTR(state, func, attr) (((char *) state) + attr_offset.attr[func])
-#define STR_ATTR(state, func, attr) *((char **) PTR_ATTR(state, func, attr))
-#define INT_ATTR(state, func, attr) *((int *) PTR_ATTR(state, func, attr))
-
-#define RST_STR_ATTR(state, func, attr) { \
- if (STR_ATTR(state, func, attr)) \
- myfree(STR_ATTR(state, func, attr)); \
- STR_ATTR(state, func, attr) = 0; \
- }
-
-#define UPD_STR_ATTR(state, func, attr, value) { \
- if (STR_ATTR(state, func, attr)) \
- myfree(STR_ATTR(state, func, attr)); \
- STR_ATTR(state, func, attr) = mystrdup(value); \
- }
-
-#define UPD_INT_ATTR(state, func, attr, value) { \
- INT_ATTR(state, func, attr) = (value); \
- }
-
-/* xclient_cmd - process XCLIENT */
+/* xclient_cmd - override client attributes */
static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
int arg_no;
- char *raw_value;
- char *cooked_value;
- char *arg_val;
+ char *attr_value;
+ char *attr_name;
int update_namaddr = 0;
- int function;
int code;
- static NAME_CODE xclient_functions[] = {
- XCLIENT_OVERRIDE, FUNC_OVERRIDE,
- XCLIENT_FORWARD, FUNC_FORWARD,
- 0, -1,
- };
static NAME_CODE xclient_codes[] = {
"OK", SMTPD_PEER_CODE_OK,
"PERM", SMTPD_PEER_CODE_PERM,
"TEMP", SMTPD_PEER_CODE_TEMP,
0, -1,
};
+ static NAME_CODE xclient_proto[] = {
+ MAIL_PROTO_SMTP, 1,
+ MAIL_PROTO_ESMTP, 2,
+ 0, -1,
+ };
/*
* Sanity checks. The XCLIENT command does not override its own access
smtpd_chat_reply(state, "503 Error: MAIL transaction in progress");
return (-1);
}
- if (argc < 3) {
+ if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Syntax: %s function name=value...",
+ smtpd_chat_reply(state, "501 Syntax: %s name=value...",
XCLIENT_CMD);
return (-1);
}
smtpd_chat_reply(state, "554 Error: insufficient authorization");
return (-1);
}
-#define STREQ(x,y) (strcasecmp((x), (y)) == 0)
+#define STREQ(x,y) (strcasecmp((x), (y)) == 0)
+#define UPDATE_STR(s, v) do { if (s) myfree(s); s = (v) ? mystrdup(v) : 0; } while(0)
/*
- * Function name: what attributes to update. Complain about unrecognized
- * request names. The set of requests is unlikely to change.
+ * Iterate over all NAME=VALUE attributes.
*/
- arg_val = argv[1].strval;
- printable(arg_val, '?');
- if ((function = name_code(xclient_functions, arg_val)) < 0) {
- state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Bad %s function: %s",
- XCLIENT_CMD, arg_val);
- return (-1);
- }
- if (function == FUNC_FORWARD && state->xclient.used == 0)
- smtpd_xclient_preset(state);
-
- /*
- * Iterate over all NAME=VALUE attributes. An empty value means the
- * information was not provided by the client and that we must not fall
- * back to the non-XCLIENT value.
- */
- for (arg_no = 2; arg_no < argc; arg_no++) {
- arg_val = argv[arg_no].strval;
+ for (arg_no = 1; arg_no < argc; arg_no++) {
+ attr_name = argv[arg_no].strval;
/*
- * Decode the attribute value and for safety's sake mask
- * non-printable characters in the raw and decoded values; we don't
- * want to handle unexploded munitions. Do not complain about
- * unrecognized attribute names. The set of attributes may change
- * over time.
+ * For safety's sake mask non-printable characters in the raw and
+ * decoded values; we don't want to handle unexploded munitions.
+ * Complain when they send an attribute that we didn't announce.
+ *
+ * An implementation must allow clients to send XCLIENT before the
+ * HELO/EHLO greeting.
*
* The client can send multiple XCLIENT attributes in a single command,
* or multiple XCLIENT commands with fewer attributes.
- *
- * Note: XCLIENT OVERRIDE overrides only the specified remote client
- * attributes (for testing), while XCLIENT FORWARD overrides all
- * remote client attributes (for consistency).
*/
- if ((raw_value = split_at(arg_val, '=')) == 0) {
+ if ((attr_value = split_at(attr_name, '=')) == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Error: name=value expected");
return (-1);
}
- if (xtext_unquote(state->buffer, raw_value) == 0) {
- state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Bad attribute value syntax: %s",
- printable(raw_value, '?'));
- return (-1);
- }
- cooked_value = printable(STR(state->buffer), '?');
- (void) printable(raw_value, '?');
+ printable(attr_value, '?');
/*
- * CLIENT_NAME=hostname. Also updates the client hostname lookup
- * status code. Treat a numerical hostname as an unavailable name.
+ * NAME=hostname. Also updates the client hostname lookup status
+ * code. Treat a numerical hostname as an unavailable name.
*/
- if (STREQ(arg_val, XCLIENT_NAME)) {
- if (*raw_value && !valid_hostaddr(cooked_value, DONT_GRIPE)) {
- if (!valid_hostname(cooked_value, DONT_GRIPE)) {
+ if (STREQ(attr_name, XCLIENT_NAME)) {
+ if (*attr_value && !valid_hostaddr(attr_value, DONT_GRIPE)) {
+ if (!valid_hostname(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
- XCLIENT_NAME, raw_value);
+ XCLIENT_NAME, attr_value);
return (-1);
}
- UPD_STR_ATTR(state, function, name, cooked_value);
- UPD_INT_ATTR(state, function, peer_code, SMTPD_PEER_CODE_OK);
+ UPDATE_STR(state->name, attr_value);
+ state->peer_code = SMTPD_PEER_CODE_OK;
} else {
- UPD_STR_ATTR(state, function, name, CLIENT_NAME_UNKNOWN);
- UPD_INT_ATTR(state, function, peer_code, SMTPD_PEER_CODE_PERM);
+ UPDATE_STR(state->name, CLIENT_NAME_UNKNOWN);
+ state->peer_code = SMTPD_PEER_CODE_PERM;
}
update_namaddr = 1;
}
/*
- * CLIENT_ADDR=client network address.
+ * ADDR=client network address.
*/
- else if (STREQ(arg_val, XCLIENT_ADDR)) {
- if (*raw_value) {
- if (!valid_hostaddr(cooked_value, DONT_GRIPE)) {
+ else if (STREQ(attr_name, XCLIENT_ADDR)) {
+ if (*attr_value) {
+ if (!valid_hostaddr(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
- XCLIENT_ADDR, raw_value);
+ XCLIENT_ADDR, attr_value);
return (-1);
}
- UPD_STR_ATTR(state, function, addr, cooked_value);
+ UPDATE_STR(state->addr, attr_value);
} else {
- UPD_STR_ATTR(state, function, addr, CLIENT_ADDR_UNKNOWN);
+ UPDATE_STR(state->addr, CLIENT_ADDR_UNKNOWN);
}
update_namaddr = 1;
}
/*
- * CLIENT_CODE=hostname lookup status. Reset the client hostname if
- * the hostname lookup status is not OK.
+ * CODE=hostname lookup status. Reset the client hostname if the
+ * hostname lookup status is not OK.
*/
- else if (STREQ(arg_val, XCLIENT_CODE)) {
- if (*raw_value) {
- if ((code = name_code(xclient_codes, cooked_value)) < 0) {
+ else if (STREQ(attr_name, XCLIENT_CODE)) {
+ if (*attr_value) {
+ if ((code = name_code(xclient_codes, NAME_CODE_FLAG_NONE, attr_value)) < 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s value: %s",
- XCLIENT_CODE, raw_value);
+ XCLIENT_CODE, attr_value);
return (-1);
}
- UPD_INT_ATTR(state, function, peer_code, code);
+ state->peer_code = code;
if (code != SMTPD_PEER_CODE_OK) {
- UPD_STR_ATTR(state, function, name, CLIENT_NAME_UNKNOWN);
+ UPDATE_STR(state->name, CLIENT_NAME_UNKNOWN);
update_namaddr = 1;
}
}
}
/*
- * CLIENT_HELO=hostname. Disallow characters that could mess up our
- * own Received: message headers but allow [].
+ * HELO=hostname. Disallow characters that could mess up our own
+ * Received: message headers but allow [].
*/
- else if (STREQ(arg_val, XCLIENT_HELO)) {
- if (*raw_value) {
- if (strlen(cooked_value) > VALID_HOSTNAME_LEN) {
+ else if (STREQ(attr_name, XCLIENT_HELO)) {
+ if (*attr_value) {
+ if (strlen(attr_value) > VALID_HOSTNAME_LEN) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
- XCLIENT_HELO, raw_value);
+ XCLIENT_HELO, attr_value);
return (-1);
}
- neuter(cooked_value, "<>()\\\";:@", '?');
- UPD_STR_ATTR(state, function, helo_name, cooked_value);
+ neuter(attr_value, "<>()\\\";:@", '?');
+ UPDATE_STR(state->helo_name, attr_value);
} else {
- RST_STR_ATTR(state, function, helo_name);
+ UPDATE_STR(state->helo_name, CLIENT_HELO_UNKNOWN);
}
}
/*
- * CLIENT_PROTO=protocol name. Disallow characters that could mess up
- * our own Received: message headers.
+ * PROTO=protocol name. Disallow characters that could mess up our
+ * own Received: message headers.
*/
- else if (STREQ(arg_val, XCLIENT_PROTO)) {
- if (*raw_value) {
- if (*cooked_value == 0 || strlen(cooked_value) > 64) {
- state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Bad %s syntax: %s",
- XCLIENT_PROTO, raw_value);
- return (-1);
- }
- neuter(cooked_value, "[]<>()\\\";:@", '?');
- UPD_STR_ATTR(state, function, protocol, cooked_value);
- } else {
- UPD_STR_ATTR(state, function, protocol, CLIENT_PROTO_UNKNOWN);
+ else if (STREQ(attr_name, XCLIENT_PROTO)) {
+ if (name_code(xclient_proto, NAME_CODE_FLAG_NONE, attr_value) < 0) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad %s syntax: %s",
+ XCLIENT_PROTO, attr_value);
+ return (-1);
}
+ UPDATE_STR(state->protocol, attr_value);
}
/*
- * Unknown attribute name. Don't complain, and log a warning. Logging
- * is safe because only authorized clients can issue XCLIENT
- * commands.
+ * Unknown attribute name. Complain.
*/
else {
- msg_warn("unknown %s attribute from %s: %s=%s",
- XCLIENT_CMD, state->namaddr, arg_val, cooked_value);
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad %s attribute name: %s",
+ XCLIENT_CMD, attr_name);
+ return (-1);
}
}
* Update the combined name and address when either has changed.
*/
if (update_namaddr) {
- if (STR_ATTR(state, function, namaddr))
- myfree(STR_ATTR(state, function, namaddr));
- STR_ATTR(state, function, namaddr) =
- concatenate(STR_ATTR(state, function, name), "[",
- STR_ATTR(state, function, addr), "]",
+ if (state->namaddr)
+ myfree(state->namaddr);
+ state->namaddr =
+ concatenate(state->name, "[", state->addr, "]", (char *) 0);
+ }
+ smtpd_chat_reply(state, "250 Ok");
+ return (0);
+}
+
+/* xforward_cmd - forward logging attributes */
+
+static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
+{
+ int arg_no;
+ char *attr_value;
+ char *attr_name;
+ int updated = 0;
+ static NAME_CODE xforward_flags[] = {
+ XFORWARD_NAME, SMTPD_XFORWARD_FLAG_NAME,
+ XFORWARD_ADDR, SMTPD_XFORWARD_FLAG_ADDR,
+ XFORWARD_PROTO, SMTPD_XFORWARD_FLAG_PROTO,
+ XFORWARD_HELO, SMTPD_XFORWARD_FLAG_HELO,
+ 0, 0,
+ };
+ int flag;
+
+ /*
+ * Sanity checks.
+ */
+ if (IN_MAIL_TRANSACTION(state)) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "503 Error: MAIL transaction in progress");
+ return (-1);
+ }
+ if (argc < 2) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Syntax: %s name=value...",
+ XFORWARD_CMD);
+ return (-1);
+ }
+ if (!xforward_allowed) {
+ state->error_mask |= MAIL_ERROR_POLICY;
+ smtpd_chat_reply(state, "554 Error: insufficient authorization");
+ return (-1);
+ }
+
+ /*
+ * Initialize.
+ */
+ if (state->xforward.flags == 0)
+ smtpd_xforward_preset(state);
+
+ /*
+ * Iterate over all NAME=VALUE attributes.
+ */
+ for (arg_no = 1; arg_no < argc; arg_no++) {
+ attr_name = argv[arg_no].strval;
+
+ /*
+ * For safety's sake mask non-printable characters in the raw and
+ * decoded values; we don't want to handle unexploded munitions.
+ * Complain when they send an attribute that we didn't announce.
+ *
+ * The client can send multiple XFORWARD attributes in a single command,
+ * or multiple XFORWARD commands with fewer attributes.
+ */
+ if ((attr_value = split_at(attr_name, '=')) == 0) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Error: name=value expected");
+ return (-1);
+ }
+ printable(attr_value, '?');
+
+ flag = name_code(xforward_flags, NAME_CODE_FLAG_NONE, attr_name);
+ switch (flag) {
+
+ /*
+ * NAME=hostname. Also updates the client hostname lookup status
+ * code. Treat a numerical hostname as an unavailable name.
+ */
+ case SMTPD_XFORWARD_FLAG_NAME:
+ if (*attr_value && !valid_hostaddr(attr_value, DONT_GRIPE)) {
+ if (!valid_hostname(attr_value, DONT_GRIPE)) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad %s syntax: %s",
+ XFORWARD_NAME, attr_value);
+ return (-1);
+ }
+ } else {
+ attr_value = CLIENT_NAME_UNKNOWN;
+ }
+ UPDATE_STR(state->xforward.name, attr_value);
+ break;
+
+ /*
+ * ADDR=client network address.
+ */
+ case SMTPD_XFORWARD_FLAG_ADDR:
+ if (*attr_value) {
+ if (!valid_hostaddr(attr_value, DONT_GRIPE)) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad %s syntax: %s",
+ XFORWARD_ADDR, attr_value);
+ return (-1);
+ }
+ } else {
+ attr_value = CLIENT_ADDR_UNKNOWN;
+ }
+ UPDATE_STR(state->xforward.addr, attr_value);
+ break;
+
+ /*
+ * HELO=hostname. Disallow characters that could mess up our own
+ * Received: message headers but allow [].
+ */
+ case SMTPD_XFORWARD_FLAG_HELO:
+ if (*attr_value) {
+ if (strlen(attr_value) > VALID_HOSTNAME_LEN) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad %s syntax: %s",
+ XFORWARD_HELO, attr_value);
+ return (-1);
+ }
+ neuter(attr_value, "<>()\\\";:@", '?');
+ } else {
+ attr_value = CLIENT_HELO_UNKNOWN;
+ }
+ UPDATE_STR(state->xforward.helo_name, attr_value);
+ break;
+
+ /*
+ * PROTO=protocol name. Neutralize characters that could mess up
+ * our own Received: message headers.
+ */
+ case SMTPD_XFORWARD_FLAG_PROTO:
+ if (strlen(attr_value) > 64) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad %s syntax: %s",
+ XFORWARD_PROTO, attr_value);
+ return (-1);
+ }
+ neuter(attr_value, "<>()\\\";:@", '?');
+ UPDATE_STR(state->xforward.protocol, attr_value);
+ break;
+
+ /*
+ * Unknown attribute name. Complain.
+ */
+ default:
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad %s attribute name: %s",
+ XFORWARD_CMD, attr_name);
+ return (-1);
+ }
+ updated |= flag;
+ }
+ state->xforward.flags |= updated;
+
+ /*
+ * Update the combined name and address when either has changed.
+ */
+ if (updated & (SMTPD_XFORWARD_FLAG_NAME | SMTPD_XFORWARD_FLAG_ADDR)) {
+ if (state->xforward.namaddr)
+ myfree(state->xforward.namaddr);
+ state->xforward.namaddr =
+ concatenate(state->xforward.name, "[",
+ state->xforward.addr, "]",
(char *) 0);
}
smtpd_chat_reply(state, "250 Ok");
} SMTPD_CMD;
#define SMTPD_CMD_FLAG_LIMIT (1<<0) /* limit usage */
-#define SMTPD_CMD_FLAG_FORBIDDEN (1<<1) /* RFC 2822 mail header */
+#define SMTPD_CMD_FLAG_FORBID (1<<1) /* RFC 2822 mail header */
static SMTPD_CMD smtpd_cmd_table[] = {
"HELO", helo_cmd, SMTPD_CMD_FLAG_LIMIT,
"ETRN", etrn_cmd, SMTPD_CMD_FLAG_LIMIT,
"QUIT", quit_cmd, 0,
"XCLIENT", xclient_cmd, SMTPD_CMD_FLAG_LIMIT,
- "Received:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
- "Reply-To:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
- "Message-ID:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
- "Subject:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
- "From:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
- "CONNECT", 0, SMTPD_CMD_FLAG_FORBIDDEN,
- "User-Agent:", 0, SMTPD_CMD_FLAG_FORBIDDEN,
+ "XFORWARD", xforward_cmd, SMTPD_CMD_FLAG_LIMIT,
+ "Received:", 0, SMTPD_CMD_FLAG_FORBID,
+ "Reply-To:", 0, SMTPD_CMD_FLAG_FORBID,
+ "Message-ID:", 0, SMTPD_CMD_FLAG_FORBID,
+ "Subject:", 0, SMTPD_CMD_FLAG_FORBID,
+ "From:", 0, SMTPD_CMD_FLAG_FORBID,
+ "CONNECT", 0, SMTPD_CMD_FLAG_FORBID,
+ "User-Agent:", 0, SMTPD_CMD_FLAG_FORBID,
0,
};
state->error_count++;
continue;
}
- if (cmdp->flags & SMTPD_CMD_FLAG_FORBIDDEN) {
+ if (cmdp->flags & SMTPD_CMD_FLAG_FORBID) {
msg_warn("%s sent non-SMTP command: %.100s",
state->namaddr, vstring_str(state->buffer));
smtpd_chat_reply(state, "221 Error: I can break rules, too. Goodbye.");
xclient_allowed =
namadr_list_match(xclient_hosts, state.name, state.addr);
+ /*
+ * Overriding XFORWARD access control makes no sense, either.
+ */
+ xforward_allowed =
+ namadr_list_match(xforward_hosts, state.name, state.addr);
+
/*
* See if we need to turn on verbose logging for this client.
*/
smtpd_noop_cmds = string_list_init(MATCH_FLAG_NONE, var_smtpd_noop_cmds);
verp_clients = namadr_list_init(MATCH_FLAG_NONE, var_verp_clients);
xclient_hosts = namadr_list_init(MATCH_FLAG_NONE, var_xclient_hosts);
+ xforward_hosts = namadr_list_init(MATCH_FLAG_NONE, var_xforward_hosts);
hogger_list = namadr_list_init(MATCH_FLAG_NONE, var_smtpd_hoggers);
if (getuid() == 0 || getuid() == var_owner_uid)
smtpd_check_init();
VAR_SMTPD_PROXY_EHLO, DEF_SMTPD_PROXY_EHLO, &var_smtpd_proxy_ehlo, 0, 0,
VAR_INPUT_TRANSP, DEF_INPUT_TRANSP, &var_input_transp, 0, 0,
VAR_XCLIENT_HOSTS, DEF_XCLIENT_HOSTS, &var_xclient_hosts, 0, 0,
+ VAR_XFORWARD_HOSTS, DEF_XFORWARD_HOSTS, &var_xforward_hosts, 0, 0,
VAR_SMTPD_HOGGERS, DEF_SMTPD_HOGGERS, &var_smtpd_hoggers, 0, 0,
0,
};
int class; /* error notification class */
} SMTPD_DEFER;
-typedef struct SMTPD_XCLIENT_ATTR {
- int used; /* status */
+typedef struct SMTPD_XFORWARD_ATTR {
+ int flags; /* see below */
char *name; /* name for access control */
char *addr; /* address for access control */
char *namaddr; /* name[address] */
- int peer_code; /* name status */
char *protocol; /* email protocol */
char *helo_name; /* helo/ehlo parameter */
-} SMTPD_XCLIENT_ATTR;
+ char *ident; /* message identifier */
+} SMTPD_XFORWARD_ATTR;
+
+#define SMTPD_XFORWARD_FLAG_INIT (1<<0) /* preset done */
+#define SMTPD_XFORWARD_FLAG_NAME (1<<1) /* client name received */
+#define SMTPD_XFORWARD_FLAG_ADDR (1<<2) /* client address received */
+#define SMTPD_XFORWARD_FLAG_PROTO (1<<3)/* protocol received */
+#define SMTPD_XFORWARD_FLAG_HELO (1<<4) /* client helo received */
+#define SMTPD_XFORWARD_FLAG_IDENT (1<<5)/* message identifier received */
+
+#define SMTPD_XFORWARD_FLAG_CLIENT_MASK \
+ (SMTPD_XFORWARD_FLAG_NAME | SMTPD_XFORWARD_FLAG_ADDR \
+ | SMTPD_XFORWARD_FLAG_PROTO | SMTPD_XFORWARD_FLAG_HELO)
typedef struct SMTPD_STATE {
int err;
VSTREAM *proxy; /* proxy handle */
VSTRING *proxy_buffer; /* proxy query/reply buffer */
char *proxy_mail; /* owned by mail_cmd() */
- int proxy_features; /* proxy ESMTP features */
- SMTPD_XCLIENT_ATTR xclient; /* override access control */
+ int proxy_xforward_features; /* proxy XFORWARD features */
+ SMTPD_XFORWARD_ATTR xforward; /* override access control */
} SMTPD_STATE;
extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *);
#define SMTPD_AFTER_CONNECT "CONNECT"
#define SMTPD_AFTER_DOT "END-OF-MESSAGE"
+ /*
+ * Postfix representation of unknown client information within smtpd
+ * processes. This is not the representation that Postfix uses in queue
+ * files, in queue manager delivery requests, nor is it the representation
+ * of information in XCLIENT/XFORWARD commands!
+ */
+#define CLIENT_ATTR_UNKNOWN "unknown"
+
+#define CLIENT_NAME_UNKNOWN CLIENT_ATTR_UNKNOWN
+#define CLIENT_ADDR_UNKNOWN CLIENT_ATTR_UNKNOWN
+#define CLIENT_NAMADDR_UNKNOWN CLIENT_ATTR_UNKNOWN
+#define CLIENT_HELO_UNKNOWN 0
+#define CLIENT_PROTO_UNKNOWN CLIENT_ATTR_UNKNOWN
+#define CLIENT_IDENT_UNKNOWN 0
+
+#define IS_AVAIL_CLIENT_ATTR(v) ((v) && strcmp((v), CLIENT_ATTR_UNKNOWN))
+
+#define IS_AVAIL_CLIENT_NAME(v) IS_AVAIL_CLIENT_ATTR(v)
+#define IS_AVAIL_CLIENT_ADDR(v) IS_AVAIL_CLIENT_ATTR(v)
+#define IS_AVAIL_CLIENT_NAMADDR(v) IS_AVAIL_CLIENT_ATTR(v)
+#define IS_AVAIL_CLIENT_HELO(v) ((v) != 0)
+#define IS_AVAIL_CLIENT_PROTO(v) IS_AVAIL_CLIENT_ATTR(v)
+#define IS_AVAIL_CLIENT_IDENT(v) ((v) != 0)
+
/*
* If running in stand-alone mode, do not try to talk to Postfix daemons but
* write to queue file instead.
*
* Note 2: outside the SMTP server, the representation of unknown/known
* attribute values is different in queue files, in queue manager delivery
- * requests, and in over-the-network XCLIENT commands.
+ * requests, and in over-the-network XFORWARD commands.
*/
-#define SMTPD_FEATURE_XCLIENT (1<<0) /* proxy announces XCLIENT */
-
-#define MAYBE_FORWARD(s, a) \
- ((s)->xclient.used ? (s)->xclient.a : (s)->a)
+#define SMTPD_PROXY_XFORWARD_NAME (1<<0) /* client name */
+#define SMTPD_PROXY_XFORWARD_ADDR (1<<1) /* client address */
+#define SMTPD_PROXY_XFORWARD_PROTO (1<<2) /* protocol */
+#define SMTPD_PROXY_XFORWARD_HELO (1<<3) /* client helo */
+#define SMTPD_PROXY_XFORWARD_IDENT (1<<4) /* message identifier */
-#define FORWARD_ADDR(s) MAYBE_FORWARD((s), addr)
-#define FORWARD_NAME(s) MAYBE_FORWARD((s), name)
-#define FORWARD_NAMADDR(s) MAYBE_FORWARD((s), namaddr)
-#define FORWARD_CODE(s) MAYBE_FORWARD((s), peer_code)
-#define FORWARD_PROTO(s) MAYBE_FORWARD((s), protocol)
-#define FORWARD_HELO(s) MAYBE_FORWARD((s), helo_name)
-
-extern void smtpd_xclient_init(SMTPD_STATE *state);
-extern void smtpd_xclient_preset(SMTPD_STATE *state);
-extern void smtpd_xclient_reset(SMTPD_STATE *state);
+ /*
+ * If forwarding client information, don't mix direct client information
+ * from the current SMTP session with forwarded client information from an
+ * up-stream session.
+ */
+#define FORWARD_CLIENT_ATTR(s, a) \
+ (((s)->xforward.flags & SMTPD_XFORWARD_FLAG_CLIENT_MASK) ? \
+ (s)->xforward.a : (s)->a)
+
+#define FORWARD_IDENT_ATTR(s) \
+ (((s)->xforward.flags & SMTPD_XFORWARD_FLAG_IDENT) ? \
+ (s)->queue_id : (s)->ident)
+
+#define FORWARD_ADDR(s) FORWARD_CLIENT_ATTR((s), addr)
+#define FORWARD_NAME(s) FORWARD_CLIENT_ATTR((s), name)
+#define FORWARD_NAMADDR(s) FORWARD_CLIENT_ATTR((s), namaddr)
+#define FORWARD_PROTO(s) FORWARD_CLIENT_ATTR((s), protocol)
+#define FORWARD_HELO(s) FORWARD_CLIENT_ATTR((s), helo_name)
+#define FORWARD_IDENT(s) FORWARD_IDENT_ATTR(s)
+
+extern void smtpd_xforward_init(SMTPD_STATE *state);
+extern void smtpd_xforward_preset(SMTPD_STATE *state);
+extern void smtpd_xforward_reset(SMTPD_STATE *state);
/*
* Transparency: before mail is queued, do we check for unknown recipients,
htable_enter(smtpd_rest_classes, "check_relay_domains",
smtpd_check_parse("permit_mydomain reject_unauth_destination"));
#endif
+ htable_enter(smtpd_rest_classes, REJECT_SENDER_LOGIN_MISMATCH,
+ (char *) smtpd_check_parse(REJECT_AUTH_SENDER_LOGIN_MISMATCH
+ " " REJECT_UNAUTH_SENDER_LOGIN_MISMATCH));
/*
* People screw up the relay restrictions too often. Require that they
return (result);
}
-/* reject_sender_login_mismatch - reject login/sender ownership mismatch */
+#ifdef USE_SASL
-static int reject_sender_login_mismatch(SMTPD_STATE *state, const char *sender)
+/* reject_auth_sender_login_mismatch - logged in client must own sender address */
+
+static int reject_auth_sender_login_mismatch(SMTPD_STATE *state, const char *sender)
{
const RESOLVE_REPLY *reply;
- const char *login = 0;
- const char *owner = 0;
+ const char *owners;
+ char *saved_owners;
+ char *cp;
+ char *name;
+ int found = 0;
/*
- * If the sender address is owned by a login name, or if the client has
- * logged in, then require that the client is logged in as the owner of
- * the sender address.
+ * Reject if the client is logged in and does not own the sender address.
*/
- reply = (const RESOLVE_REPLY *) ctable_locate(smtpd_resolve_cache, sender);
- if (reply->flags & RESOLVE_FLAG_FAIL)
- reject_dict_retry(state, sender);
- owner = check_mail_addr_find(state, sender, smtpd_sender_login_maps,
- STR(reply->recipient), (char **) 0);
-#ifdef USE_SASL_AUTH
- if (var_smtpd_sasl_enable && state->sasl_username != 0)
- login = state->sasl_username;
-#endif
- if (login) {
- if (owner == 0 || strcasecmp(login, owner) != 0)
+ if (var_smtpd_sasl_enable && state->sasl_username != 0) {
+ reply = (const RESOLVE_REPLY *) ctable_locate(smtpd_resolve_cache, sender);
+ if (reply->flags & RESOLVE_FLAG_FAIL)
+ reject_dict_retry(state, sender);
+ if ((owners = check_mail_addr_find(state, sender, smtpd_sender_login_maps,
+ STR(reply->recipient), (char **) 0)) != 0) {
+ cp = saved_owners = mystrdup(owners);
+ while ((name = mystrtok(&cp, ", \t\r\n")) != 0) {
+ if (strcasecmp(state->sasl_username, name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ myfree(saved_owners);
+ }
+ if (!found)
return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
"553 <%s>: Sender address rejected: not owned by user %s",
- sender, login));
- } else {
- if (owner)
+ sender, state->sasl_username));
+ }
+ return (SMTPD_CHECK_DUNNO);
+}
+
+/* reject_unauth_sender_login_mismatch - sender requires client is logged in */
+
+static int reject_unauth_sender_login_mismatch(SMTPD_STATE *state, const char *sender)
+{
+ const RESOLVE_REPLY *reply;
+ const char *login = 0;
+
+ /*
+ * Reject if the client is not logged in and the sender address has an
+ * owner.
+ */
+ if (var_smtpd_sasl_enable && state->sasl_username == 0) {
+ reply = (const RESOLVE_REPLY *) ctable_locate(smtpd_resolve_cache, sender);
+ if (reply->flags & RESOLVE_FLAG_FAIL)
+ reject_dict_retry(state, sender);
+ if (check_mail_addr_find(state, sender, smtpd_sender_login_maps,
+ STR(reply->recipient), (char **) 0) != 0)
return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
- "553 <%s>: Sender address rejected: not logged in as owner",
- sender));
+ "553 <%s>: Sender address rejected: not logged in", sender));
}
return (SMTPD_CHECK_DUNNO);
}
+#endif
+
/* check_policy_service - check delegated policy service */
static int check_policy_service(SMTPD_STATE *state, const char *server,
if (state->sender && *state->sender)
status = reject_non_fqdn_address(state, state->sender,
state->sender, SMTPD_NAME_SENDER);
- } else if (strcasecmp(name, REJECT_SENDER_LOGIN_MISMATCH) == 0) {
- if (state->sender && *state->sender)
- status = reject_sender_login_mismatch(state, state->sender);
+ } else if (strcasecmp(name, REJECT_AUTH_SENDER_LOGIN_MISMATCH) == 0) {
+#ifdef USE_SASL
+ if (var_smtpd_sasl_enable) {
+ if (state->sender && *state->sender)
+ status = reject_auth_sender_login_mismatch(state, state->sender);
+ } else
+#endif
+ msg_warn("restriction `%s' ignored: no SASL support", name);
+ } else if (strcasecmp(name, REJECT_UNAUTH_SENDER_LOGIN_MISMATCH) == 0) {
+#ifdef USE_SASL
+ if (var_smtpd_sasl_enable) {
+ if (state->sender && *state->sender)
+ status = reject_unauth_sender_login_mismatch(state, state->sender);
+ } else
+#endif
+ msg_warn("restriction `%s' ignored: no SASL support", name);
} else if (is_map_command(state, name, CHECK_SENDER_NS_ACL, &cpp)) {
if (state->sender && *state->sender) {
status = check_server_access(state, *cpp, state->sender,
/* mail.
/*
/* smtpd_proxy_open() connects to the proxy service, sends EHLO, sends
-/* client information with the XCLIENT command if possible, sends
+/* client information with the XFORWARD command if possible, sends
/* the MAIL FROM command, and receives the reply. A non-zero result means
/* trouble: either the proxy is unavailable, or it did not send the
/* expected reply.
#include <vstring.h>
#include <stringops.h>
#include <connect.h>
+#include <name_code.h>
/* Global library. */
#define LEN(x) VSTRING_LEN(x)
#define SMTPD_PROXY_CONNECT ((char *) 0)
+/* smtpd_xforward_flush - flush forwarding information */
+
+static int smtpd_xforward_flush(SMTPD_STATE *state, VSTRING *buf)
+{
+ int ret;
+
+ if (VSTRING_LEN(buf) > 0) {
+ ret = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK,
+ XFORWARD_CMD "%s", STR(buf));
+ VSTRING_RESET(buf);
+ return (ret);
+ }
+ return (0);
+}
+
+/* smtpd_xforward - send forwarding information */
+
+static int smtpd_xforward(SMTPD_STATE *state, VSTRING *buf, const char *name,
+ int value_available, const char *value)
+{
+ size_t new_len;
+ int ret;
+
+#define CONSTR_LEN(s) (sizeof(s) - 1)
+#define PAYLOAD_LIMIT (512 - CONSTR_LEN("250 " XFORWARD_CMD "\r\n"))
+
+ /*
+ * How much space does this attribute need?
+ */
+ if (!value_available)
+ value = "";
+ new_len = strlen(name) + strlen(value) + 2; /* SPACE name = value */
+ if (new_len > PAYLOAD_LIMIT)
+ msg_warn("%s payload %s=%.10s... exceeds SMTP protocol limit",
+ XFORWARD_CMD, name, value);
+
+ /*
+ * Flush the buffer if we need to, and store the attribute.
+ */
+ if (VSTRING_LEN(buf) > 0 && VSTRING_LEN(buf) + new_len > PAYLOAD_LIMIT)
+ if ((ret = smtpd_xforward_flush(state, buf)) < 0)
+ return (ret);
+ vstring_sprintf_append(buf, " %s=%s", name, value);
+
+ return (0);
+}
+
/* smtpd_proxy_open - open proxy connection */
int smtpd_proxy_open(SMTPD_STATE *state, const char *service,
{
int fd;
char *lines;
- char *line;
+ char *words;
VSTRING *buf;
int bad;
+ char *word;
+ static NAME_CODE xforward_features[] = {
+ XFORWARD_NAME, SMTPD_PROXY_XFORWARD_NAME,
+ XFORWARD_ADDR, SMTPD_PROXY_XFORWARD_ADDR,
+ XFORWARD_PROTO, SMTPD_PROXY_XFORWARD_PROTO,
+ XFORWARD_HELO, SMTPD_PROXY_XFORWARD_HELO,
+ 0, 0,
+ };
/*
* This buffer persists beyond the end of a proxy session so we can
/*
* Parse the EHLO reply and see if we can forward logging information.
*/
- state->proxy_features = 0;
+ state->proxy_xforward_features = 0;
lines = STR(state->proxy_buffer);
- while ((line = mystrtok(&lines, "\n")) != 0)
- if (ISDIGIT(line[0]) && ISDIGIT(line[1]) && ISDIGIT(line[2])
- && (line[3] == ' ' || line[3] == '-')
- && strcmp(line + 4, XCLIENT_CMD) == 0)
- state->proxy_features |= SMTPD_FEATURE_XCLIENT;
+ while ((words = mystrtok(&lines, "\n")) != 0) {
+ if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) {
+ if (strcasecmp(word, XFORWARD_CMD) == 0)
+ while ((word = mystrtok(&words, " \t")) != 0)
+ state->proxy_xforward_features |=
+ name_code(xforward_features, NAME_CODE_FLAG_NONE, word);
+ }
+ }
/*
- * Send all XCLIENT attributes, but only if we have some minimal amount
- * of remote client information. Transform internal forms to external
- * forms and encode the result as xtext.
+ * Send XFORWARD attributes. For robustness, explicitly specify what SMTP
+ * session attributes are known and unknown.
*/
- if ((state->proxy_features & SMTPD_FEATURE_XCLIENT)
- && (IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state))
- || IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)))) {
+ if (state->proxy_xforward_features) {
buf = vstring_alloc(100);
- vstring_strcpy(buf, XCLIENT_CMD " " XCLIENT_FORWARD
- " " XCLIENT_NAME "=");
- if (IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state)))
- xtext_quote_append(buf, FORWARD_NAME(state), "");
- vstring_strcat(buf, " " XCLIENT_ADDR "=");
- if (IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)))
- xtext_quote_append(buf, FORWARD_ADDR(state), "");
- bad = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, "%s", STR(buf));
- if (bad == 0) {
- vstring_strcpy(buf, XCLIENT_CMD " " XCLIENT_FORWARD
- " " XCLIENT_HELO "=");
- if (IS_AVAIL_CLIENT_HELO(FORWARD_HELO(state)))
- xtext_quote_append(buf, FORWARD_HELO(state), "");
- vstring_strcat(buf, " " XCLIENT_PROTO "=");
- if (IS_AVAIL_CLIENT_PROTO(FORWARD_PROTO(state)))
- xtext_quote_append(buf, FORWARD_PROTO(state), "");
- bad = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, "%s", STR(buf));
- }
+ bad = 0;
+ if ((state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_NAME)
+ && !(bad = smtpd_xforward(state, buf, XFORWARD_NAME,
+ IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state)),
+ FORWARD_NAME(state)))
+ && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_ADDR)
+ && !(bad = smtpd_xforward(state, buf, XFORWARD_ADDR,
+ IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)),
+ FORWARD_ADDR(state)))
+ && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_HELO)
+ && !(bad = smtpd_xforward(state, buf, XFORWARD_HELO,
+ IS_AVAIL_CLIENT_HELO(FORWARD_HELO(state)),
+ FORWARD_HELO(state)))
+ && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_PROTO)
+ && !(bad = smtpd_xforward(state, buf, XFORWARD_PROTO,
+ IS_AVAIL_CLIENT_PROTO(FORWARD_PROTO(state)),
+ FORWARD_PROTO(state))))
+ bad = smtpd_xforward_flush(state, buf);
vstring_free(buf);
if (bad) {
vstring_sprintf(state->proxy_buffer,
state->proxy = 0;
state->proxy_buffer = 0;
state->proxy_mail = 0;
- state->proxy_features = 0;
+ state->proxy_xforward_features = 0;
state->saved_filter = 0;
state->saved_redirect = 0;
state->saved_flags = 0;
smtpd_peer_init(state);
/*
- * Initialize xclient information.
+ * Initialize xforward information.
*/
- smtpd_xclient_init(state);
+ smtpd_xforward_init(state);
/*
* Initialize the conversation history.
+++ /dev/null
-/*++
-/* NAME
-/* smtpd_xclient 3
-/* SUMMARY
-/* maintain XCLIENT information
-/* SYNOPSIS
-/* #include "smtpd.h"
-/*
-/* void smtpd_xclient_init(state)
-/* SMTPD_STATE *state;
-/*
-/* void smtpd_xclient_reset(state)
-/* SMTPD_STATE *state;
-/* DESCRIPTION
-/* smtpd_xclient_init() zeroes the attributes for storage of XCLIENT
-/* FORWARD command parameters.
-/*
-/* smtpd_xclient_preset() takes the result from smtpd_xclient_init()
-/* and sets all fields to the same "unknown" value that regular
-/* client attributes would have.
-/*
-/* smtpd_xclient_reset() restores the state from smtpd_xclient_init().
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-
-/* Utility library. */
-
-#include <mymalloc.h>
-#include <msg.h>
-
-/* Global library. */
-
-#include <mail_proto.h>
-
-/* Application-specific. */
-
-#include <smtpd.h>
-
-/* smtpd_xclient_init - initialize XCLIENT attributes */
-
-void smtpd_xclient_init(SMTPD_STATE *state)
-{
- state->xclient.used = 0;
- state->xclient.name = 0;
- state->xclient.addr = 0;
- state->xclient.namaddr = 0;
- state->xclient.peer_code = 0;
- state->xclient.protocol = 0;
- state->xclient.helo_name = 0;
-}
-
-/* smtpd_xclient_preset - set xclient attributes to "unknown" */
-
-void smtpd_xclient_preset(SMTPD_STATE *state)
-{
-
- /*
- * This is a temporary solution. Unknown forwarded attributes get the
- * same values as unknown normal attributes, so that we don't break
- * assumptions in pre-existing code.
- */
- state->xclient.used = 1;
- state->xclient.name = mystrdup(CLIENT_NAME_UNKNOWN);
- state->xclient.addr = mystrdup(CLIENT_ADDR_UNKNOWN);
- state->xclient.namaddr = mystrdup(CLIENT_NAMADDR_UNKNOWN);
- state->xclient.protocol = mystrdup(CLIENT_PROTO_UNKNOWN);
-}
-
-/* smtpd_xclient_reset - reset XCLIENT attributes */
-
-void smtpd_xclient_reset(SMTPD_STATE *state)
-{
-#define FREE_AND_WIPE(s) { if (s) myfree(s); s = 0; }
-
- state->xclient.used = 0;
- FREE_AND_WIPE(state->xclient.name);
- FREE_AND_WIPE(state->xclient.addr);
- FREE_AND_WIPE(state->xclient.namaddr);
- state->xclient.peer_code = 0;
- FREE_AND_WIPE(state->xclient.protocol);
- FREE_AND_WIPE(state->xclient.helo_name);
-}
--- /dev/null
+/*++
+/* NAME
+/* smtpd_xforward 3
+/* SUMMARY
+/* maintain XCLIENT information
+/* SYNOPSIS
+/* #include "smtpd.h"
+/*
+/* void smtpd_xforward_init(state)
+/* SMTPD_STATE *state;
+/*
+/* void smtpd_xforward_reset(state)
+/* SMTPD_STATE *state;
+/* DESCRIPTION
+/* smtpd_xforward_init() zeroes the attributes for storage of
+/* XFORWARD command parameters.
+/*
+/* smtpd_xforward_preset() takes the result from smtpd_xforward_init()
+/* and sets all fields to the same "unknown" value that regular
+/* client attributes would have.
+/*
+/* smtpd_xforward_reset() restores the state from smtpd_xforward_init().
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <msg.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+
+/* Application-specific. */
+
+#include <smtpd.h>
+
+/* smtpd_xforward_init - initialize XCLIENT attributes */
+
+void smtpd_xforward_init(SMTPD_STATE *state)
+{
+ state->xforward.flags = 0;
+ state->xforward.name = 0;
+ state->xforward.addr = 0;
+ state->xforward.namaddr = 0;
+ state->xforward.protocol = 0;
+ state->xforward.helo_name = 0;
+ state->xforward.ident = 0;
+}
+
+/* smtpd_xforward_preset - set xforward attributes to "unknown" */
+
+void smtpd_xforward_preset(SMTPD_STATE *state)
+{
+
+ /*
+ * Sanity checks.
+ */
+ if (state->xforward.flags)
+ msg_panic("smtpd_xforward_preset: bad flags: 0x%x",
+ state->xforward.flags);
+
+ /*
+ * This is a temporary solution. Unknown forwarded attributes get the
+ * same values as unknown normal attributes, so that we don't break
+ * assumptions in pre-existing code.
+ */
+ state->xforward.flags = SMTPD_XFORWARD_FLAG_INIT;
+ state->xforward.name = mystrdup(CLIENT_NAME_UNKNOWN);
+ state->xforward.addr = mystrdup(CLIENT_ADDR_UNKNOWN);
+ state->xforward.namaddr = mystrdup(CLIENT_NAMADDR_UNKNOWN);
+ /* Leave helo at zero. */
+ state->xforward.protocol = mystrdup(CLIENT_PROTO_UNKNOWN);
+ /* Leave ident at zero. */
+}
+
+/* smtpd_xforward_reset - reset XCLIENT attributes */
+
+void smtpd_xforward_reset(SMTPD_STATE *state)
+{
+#define FREE_AND_WIPE(s) { if (s) myfree(s); s = 0; }
+
+ state->xforward.flags = 0;
+ FREE_AND_WIPE(state->xforward.name);
+ FREE_AND_WIPE(state->xforward.addr);
+ FREE_AND_WIPE(state->xforward.namaddr);
+ FREE_AND_WIPE(state->xforward.protocol);
+ FREE_AND_WIPE(state->xforward.helo_name);
+ FREE_AND_WIPE(state->xforward.ident);
+}
count++;
break;
case 'C':
- if ((connect_count = atoi(optarg)) <= 0)
+ if ((connect_count = atoi(optarg)) <= 0) {
+ msg_error("bad connection count: %s", optarg);
usage(argv[0]);
+ }
break;
case 'd':
disconnect = 0;
sender = optarg;
break;
case 'l':
- if ((message_length = atoi(optarg)) <= 0)
+ if ((message_length = atoi(optarg)) <= 0) {
+ msg_error("bad message length: %s", optarg);
usage(argv[0]);
+ }
message_data = mymalloc(message_length);
memset(message_data, 'X', message_length);
for (i = 80; i < message_length; i += 80) {
talk_lmtp = 1;
break;
case 'm':
- if ((message_count = atoi(optarg)) <= 0)
+ if ((message_count = atoi(optarg)) <= 0) {
+ msg_error("bad message count: %s", optarg);
usage(argv[0]);
+ }
break;
case 'o':
send_helo_first = 0;
send_headers = 0;
break;
case 'r':
- if ((recipients = atoi(optarg)) <= 0)
+ if ((recipients = atoi(optarg)) <= 0) {
+ msg_error("bad recipient count: %s", optarg);
usage(argv[0]);
+ }
break;
case 'R':
- if (fixed_delay > 0 || (random_delay = atoi(optarg)) <= 0)
+ if (fixed_delay > 0) {
+ msg_error("do not use -w and -R options at the same time");
+ usage(argv[0]);
+ }
+ if ((random_delay = atoi(optarg)) <= 0) {
+ msg_error("bad random delay: %s", optarg);
usage(argv[0]);
+ }
break;
case 's':
- if ((sessions = atoi(optarg)) <= 0)
+ if ((sessions = atoi(optarg)) <= 0) {
+ msg_error("bad session count: %s", optarg);
usage(argv[0]);
+ }
break;
case 'S':
subject = optarg;
msg_verbose++;
break;
case 'w':
- if (random_delay > 0 || (fixed_delay = atoi(optarg)) <= 0)
+ if (random_delay > 0) {
+ msg_error("do not use -w and -R options at the same time");
usage(argv[0]);
+ }
+ if ((fixed_delay = atoi(optarg)) <= 0) {
+ msg_error("bad fixed delay: %s", optarg);
+ usage(argv[0]);
+ }
break;
default:
usage(argv[0]);
ARGV *argv_alloc(int len)
{
ARGV *argvp;
+ int sane_len;
/*
* Make sure that always argvp->argc < argvp->len.
*/
argvp = (ARGV *) mymalloc(sizeof(*argvp));
- argvp->len = (len < 2 ? 2 : len);
- argvp->argv = (char **) mymalloc((argvp->len + 1) * sizeof(char *));
+ argvp->len = 0;
+ sane_len = (len < 2 ? 2 : len);
+ argvp->argv = (char **) mymalloc((sane_len + 1) * sizeof(char *));
+ argvp->len = sane_len;
argvp->argc = 0;
argvp->argv[0] = 0;
return (argvp);
static void argv_extend(ARGV *argvp)
{
- argvp->len *= 2;
+ int new_len;
+
+ new_len = argvp->len * 2;
argvp->argv = (char **)
- myrealloc((char *) argvp->argv,
- (argvp->len + 1) * sizeof(char *));
+ myrealloc((char *) argvp->argv, (new_len + 1) * sizeof(char *));
+ argvp->len = new_len;
}
/* argv_add - add string to vector */
void inet_addr_list_init(INET_ADDR_LIST *list)
{
+ int init_size;
+
list->used = 0;
- list->size = 2;
- list->addrs = (struct in_addr *)
- mymalloc(sizeof(*list->addrs) * list->size);
+ list->size = 0;
+ init_size = 2;
+ list->addrs = (struct in_addr *) mymalloc(sizeof(*list->addrs) * init_size);
+ list->size = init_size;
}
/* inet_addr_list_append - append address to internet address list */
void inet_addr_list_append(INET_ADDR_LIST *list, struct in_addr * addr)
{
char *myname = "inet_addr_list_append";
+ int new_size;
if (msg_verbose > 1)
msg_info("%s: %s", myname, inet_ntoa(*addr));
- if (list->used >= list->size)
- list->size *= 2;
- list->addrs = (struct in_addr *)
- myrealloc((char *) list->addrs,
- sizeof(*list->addrs) * list->size);
+ if (list->used >= list->size) {
+ new_size = list->size * 2;
+ list->addrs = (struct in_addr *)
+ myrealloc((char *) list->addrs, sizeof(*list->addrs) * new_size);
+ list->size = new_size;
+ }
list->addrs[list->used++] = *addr;
}
* Initialize.
*/
intvp = (INTV *) mymalloc(sizeof(*intvp));
+ intvp->len = 0;
+ intvp->intv = (int *) mymalloc(len * sizeof(intvp->intv[0]));
intvp->len = len;
- intvp->intv = (int *) mymalloc(intvp->len * sizeof(intvp->intv[0]));
intvp->intc = 0;
return (intvp);
}
void intv_add(INTV *intvp, int count,...)
{
va_list ap;
+ int new_len;
/*
* Make sure that always intvp->intc < intvp->len.
va_start(ap, count);
while (count-- > 0) {
if (intvp->intc >= intvp->len) {
- intvp->len *= 2;
+ new_len = intvp->len * 2;
intvp->intv = (int *) myrealloc((char *) intvp->intv,
- intvp->len * sizeof(int));
+ new_len * sizeof(int));
+ intvp->len = new_len;
}
intvp->intv[intvp->intc++] = va_arg(ap, int);
}
char *mvect_alloc(MVECT *vect, int elsize, int nelm,
void (*init_fn) (char *, int), void (*wipe_fn) (char *, int))
{
- vect->nelm = nelm;
- vect->elsize = elsize;
vect->init_fn = init_fn;
vect->wipe_fn = wipe_fn;
- vect->ptr = mymalloc(vect->elsize * vect->nelm);
+ vect->nelm = 0;
+ vect->ptr = mymalloc(elsize * nelm);
+ vect->nelm = nelm;
+ vect->elsize = elsize;
if (vect->init_fn)
vect->init_fn(vect->ptr, vect->nelm);
return (vect->ptr);
{
int old_len = vect->nelm;
int incr = nelm - old_len;
+ int new_nelm;
if (incr > 0) {
if (incr < old_len)
incr = old_len;
- vect->nelm += incr;
- vect->ptr = myrealloc(vect->ptr, vect->elsize * vect->nelm);
+ new_nelm = vect->nelm + incr;
+ vect->ptr = myrealloc(vect->ptr, vect->elsize * new_nelm);
+ vect->nelm = new_nelm;
if (vect->init_fn)
vect->init_fn(vect->ptr + old_len * vect->elsize, incr);
}
/* .in -4
/* } NAME_CODE;
/*
-/* int name_code(table, name)
+/* int name_code(table, flags, name)
/* NAME_CODE *table;
+/* int flags;
/* const char *name;
/*
/* const char *str_name_code(table, code)
/* corresponds to "name not found".
/*
/* name_code() looks up the code that corresponds with the name.
-/* The lookup is case insensitive.
+/* The lookup is case insensitive. The flags argument specifies
+/* zero or more of the following:
+/* .IP NAME_CODE_FLAG_STRICT_CASE
+/* String lookups are case sensitive.
+/* .PP
+/* For convenience the constant NAME_CODE_FLAG_NONE requests
+/* no special processing.
/*
/* str_name_code() translates a number to its equivalend string.
/* DIAGNOSTICS
/* name_code - look up code by name */
-int name_code(NAME_CODE *table, const char *name)
+int name_code(NAME_CODE *table, int flags, const char *name)
{
NAME_CODE *np;
+ int (*lookup) (const char *, const char *);
+
+ if (flags & NAME_CODE_FLAG_STRICT_CASE)
+ lookup = strcmp;
+ else
+ lookup = strcasecmp;
for (np = table; np->name; np++)
- if (strcasecmp(name, np->name) == 0)
+ if (lookup(name, np->name) == 0)
break;
return (np->code);
}
int code;
} NAME_CODE;
-extern int name_code(NAME_CODE *, const char *);
+#define NAME_CODE_FLAG_NONE 0
+#define NAME_CODE_FLAG_STRICT_CASE (1<<0)
+
+extern int name_code(NAME_CODE *, int, const char *);
extern const char *str_name_code(NAME_CODE *, int);
/* LICENSE
static void vstring_extend(VBUF *bp, int incr)
{
unsigned used = bp->ptr - bp->data;
+ int new_len;
/*
* Note: vp->vbuf.len is the current buffer size (both on entry and on
* strings we might want to abandon the length doubling strategy, and go
* to fixed increments.
*/
- bp->len += (bp->len > incr ? bp->len : incr);
- bp->data = (unsigned char *) myrealloc((char *) bp->data, bp->len);
+ new_len = bp->len + (bp->len > incr ? bp->len : incr);
+ bp->data = (unsigned char *) myrealloc((char *) bp->data, new_len);
+ bp->len = new_len;
bp->ptr = bp->data + used;
bp->cnt = bp->len - used;
}
msg_panic("vstring_alloc: bad length %d", len);
vp = (VSTRING *) mymalloc(sizeof(*vp));
vp->vbuf.flags = 0;
+ vp->vbuf.len = 0;
vp->vbuf.data = (unsigned char *) mymalloc(len);
vp->vbuf.len = len;
VSTRING_RESET(vp);