-TSMTPD_RBL_STATE
-TSMTPD_STATE
-TSMTPD_TOKEN
+-TSMTPD_XCLIENT_ATTR
-TSMTP_ADDR
-TSMTP_CMD
-TSMTP_RESP
controls periodic logging of maximal connection counts or
rates. The default logging interval is 10 minutes.
+ Feature: "make makefiles WARN=stuff..." overrides the
+ built-in GCC warning options that are used when "make" is
+ invoked from within a source subdirectory. Files: makedefs,
+ */Makefile.in.
+
+20031125
+
+ Feature: qmgr logs "queueid: deleted", just like postsuper,
+ when it removes a message from the mail queue.
+
+ Performance: smtpd connects to the cleanup or proxy server
+ AFTER the first valid RCPT TO command, instead of after
+ the first valid MAIL FROM command. This avoid wasting
+ real-time proxy filter resources when mail is stopped by
+ the SMTP server's access blocks. File: smtpd/smtpd.c.
+
+20031126
+
+ Bugfix: "panic: mymalloc: requested length 0" when master.cf
+ specified an invalid host name or address. Postfix now
+ logs more specific information. File: master/master_ent.c.
+ Reported by several people.
+
+20031125-8
+
+ Feature: XCLIENT support to override the SMTP server's
+ client information for logging and/or access control. This
+ replaces the short-lived XADDR and XLOGINFO extensions.
+ Based on code by Victor Duchovni, with major simplifications.
+ Files: smtpd/{smtpd,smtpd_check,smtpd_proxy,smtpd_xclient}.c
+ smtp/smtp_smtp_proto.c, *qmgr/qmgr_message.c,
+ global/deliver_request.c.
+
Open problems:
High: when virtual aliasing is turned off after content
The same technique may be useful to block mail for undeliverable
recipients, for example on mail relay hosts that do not have a copy
-of all the relayed recipient addresses.
+of all the relayed recipient addresses. This prevents undeliverable
+junk mail from entering the queue, so that Postfix doesn't have to
+waste resources trying to send mailer-daemon messages back.
With address verification turned on, normal mail will suffer only
a short delay of up to 6 seconds while an address is verified for
/etc/postfix/main.cf:
smtpd_sender_restrictions = hash:/etc/postfix/sender_access
unverified_sender_reject_code = 550
+ # Be sure to read the "Caching" section below!
+ address_verify_map = btree:/var/mta/verify
/etc/postfix/sender_access:
aol.com reject_unverified_sender
specify "warn_if_reject reject_unverified_sender" so that you can
see what mail would be blocked:
+/etc/postfix/main.cf:
smtpd_sender_restrictions =
permit_mynetworks
...
reject_unknown_sender_domain
warn_if_reject reject_unverified_sender
...
+ # Be sure to read the "Caching" section below!
+ address_verify_map = btree:/var/mta/verify
This is also a good way to populate your cache with address
verification results before you start to actually reject mail.
NOTE: By default, address verification information is not stored
in a persistent file. You have to specify one in main.cf (see
below). Persistent storage is off by default because it may need
-more disk space than is available in your root file system.
+more disk space than is available in your file system.
Address verification information is cached by the Postfix verify
daemon. Postfix has a bunch of parameters that control the caching
verification information is lost after "postfix reload" or "postfix
stop".
-If your root file system has sufficient space, try:
+If your /var file system has sufficient space, try:
- address_verify_map = btree:/etc/postfix/verify
+ address_verify_map = btree:/var/mta/verify
NOTE: Do not put this file in a file system that fills up. When
the address verification table gets corrupted the world comes to
Right now, no tools are provided to manage the address verification
database. If the file gets too big, or if it gets corrupted, you
-can manually delete the file and run "postfix reload". The new
-verify daemon process will then create a new, empty, database.
+can manually rename or delete the file and run "postfix reload".
+The new verify daemon process will then create a new database.
Controlling the routing of address verification probes
======================================================
/etc/postfix/main.cf:
relayhost = $mydomain
address_verify_relayhost =
+ ...
Sites behind an address translation relay might have to use a
different SMTP client that sends the correct hostname information:
/etc/postfix/master.cf:
scan unix - - n - 10 smtp
+ # -o smtp_send_xclient_command=yes
Instead of a limit of 10 concurrent processes, use whatever process
limit is feasible for your machine. Content inspection software
can gobble up a lot of system resources, so you don't want to have
too much of it running at the same time.
+Uncomment (but keep the leading white-space) the option setting:
+"smtp_send_xclient_command=yes", if you would like the scan transport
+to forward the original client name and IP address to the backend smtpd
+so that mail transaction logs show the real client name IP address.
+See sample-smtp.cf and smtp(8).
+
The content filter can be set up with the Postfix spawn service,
which is the Postfix equivalent of inetd. For example, to instantiate
up to 10 content filtering processes on demand:
The before-filter Postfix SMTP server connects to the content
filter, delivers one message, and disconnects. While sending mail
into the content filter, Postfix speaks ESMTP but uses no command
-pipelining. Postfix generates its own EHLO, XLOGINFO (for logging
+pipelining. Postfix generates its own EHLO, XCLIENT (for logging
the remote client IP address instead of localhost[127.0.0.1]), DATA
and QUIT commands, and forwards unmodified copies of all the MAIL
FROM and RCPT TO commands that the before-filter Postfix SMTP server
completing the SMTP conversation with the after-filter Postfix SMTP
server.
+More details on the postfix-to-proxy interaction is at the end of
+this document, in the section titled "Transparency".
+
Configuration parameters
========================
smtp inet n - n - - smtpd
-o smtpd_proxy_filter=26
:26 inet n - n - - smtpd
- -o smtpd_authorized_xloginfo_clients=127.0.0.0/8
+ -o smtpd_authorized_xclient_hosts=127.0.0.0/8
-o smtpd_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
The ":26" causes Postfix to listen on the localhost address only.
DO NOT expose the secondary SMTP server to the Internet :-)
-The smtpd_authorized_xloginfo_clients parameter allows the before
-filter SMTP server to pass the remote SMTP client name and address
-to the after-filter SMTP server, so that the after-filter Postfix
-daemons log the remote client name and address instead of logging
+The smtpd_authorized_xclient_hosts parameter allows the before
+filter SMTP server to forward remote SMTP client information to
+the after-filter SMTP server, so that the after-filter Postfix
+daemons log the remote SMTP client information instead of logging
localhost[127.0.0.1].
The other parameter settings avoid duplication of effort that is
Other suggestions for test configurations: use the Postfix smtp-sink
command as the proxy, or something as basic as netcat.
+
+Transparency
+============
+
+The before-filter Postfix SMTP server forwards the MAIL FROM, RCPT
+TO and DATA commands that it has approved, but it does not forward
+other commands such as TLS or SASL commands. It can therefore not
+be transparent.
+
+The real-time content filter, on the other hand, has to be transparent.
+In order to support non-transparent real-time content filters,
+Postfix would have to reconcile the before-filter Postfix ESMTP
+feature set with the feature set that Postfix receives from the
+real-time content filter.
+
+- When a future Postfix version supports DSN, but the content filter
+does not announce DSN support in the EHLO reply, then the before-filter
+SMTP server would have to either 1) suppress the DSN feature in
+its EHLO announcement, or 2) duplicate all the work that needs to
+be done when delivering DSN-aware mail to a non-DSN destination.
+
+- When the content filter does not announce 8BITMIME support in
+the EHLO reply, then the before-filter SMTP server would have to
+either 1) suppress the 8BITMIME feature in its EHLO announcement,
+or 2) convert the content to quoted-printable before giving it to
+the content filter.
+
+- Performance: when Postfix has to suppress elements from the
+before-filter EHLO reply because they are incompatible with the
+real-time content filter, then Postfix has to connect to the content
+filter as soon as the client sends a valid EHLO command. This wastes
+a lot of resources when all the MAIL FROM or RCPT TO commands are
+rejected.
--- /dev/null
+Purpose of the XCLIENT extension to SMTP
+========================================
+
+The XCLIENT command targets problems in the following areas:
+
+1 - Access control tests. SMTP server access rules are difficult
+to verify when decisions can be triggered only by remote clients.
+In order to facilitate access rule testing, an SMTP client test
+program needs the ability to override the SMTP server's idea of
+the SMTP client hostname, network address, and other information.
+
+2 - Logging after content filter. With Internet->MTA1->filter->MTA2
+style content filter applications, remote client information is
+lost when MTA1 gives the mail to the content filter. This complicates
+the interpretation of the audit trail. In order maintain audit
+trail continuity, MTA1 needs the ability to forward client information
+through the content filter to MTA2.
+
+3 - Post-filter access control and logging. With Internet->filter->MTA
+style content filter applications, the content filter can be greatly
+simplified if it can delegate all decisions concerning mail relay
+and other access control to the MTA. This requires that the filter
+can forward client information to the MTA for access control
+purposes.
+
+The preceding suggests that there is a need for two modes of
+operation:
+
+ - Operation mode 1: override the SMTP server's idea of SMTP
+ client information for access control and audit trail purposes.
+ Attributes that one may want to override are:
+
+ client network address
+ client hostname
+ hostname lookup failure type (permanent or temporary)
+ client protocol (e.g., SMTP or ESMTP)
+
+ - Operation mode 2: forward remote client information for audit
+ trail purposes only. Examples of attributes are:
+
+ client network address
+ client hostname
+ client protocol (e.g., SMTP or ESMTP)
+ client SMTP HELO parameter
+
+General XCLIENT command syntax
+==============================
+
+The XCLIENT command maintains one set of attributes with surrogate
+client information. The general XCLIENT command syntax is described
+below. Upper case and quoted strings specify terminals, lowercase
+strings specify meta terminals, SP is whitespace, and descriptive
+text is in {}. Although shown below in upper case, command and
+attribute names are in fact case insensitive.
+
+ xclient-command = XCLIENT *( SP argument )
+
+ argument = ( request | attribute )
+
+ request = ( RST | ACL | LOG )
+
+ attribute = name"="value
+
+ name = ( CLIENT_NAME | CLIENT_ADDR | CLIENT_CODE | PROTOCOL | HELO_NAME )
+
+ value = ( { empty } | xtext )
+
+ xtext = { attribute value encoded as per RFC 1891 }
+
+The XCLIENT command is typically sent immediately before or after
+the EHLO command, may be pipelined after the server announces ESMTP
+pipelining support, and must not be used in the middle of an SMTP
+transaction (i.e. between MAIL and DOT).
+
+The server reply codes are as follows:
+
+ Code | Meaning
+ -----|------------
+ 250 | success
+ 501 | command syntax error
+ 502 | unrecognized request name
+ 503 | command out of order (name=value without prior ACL or LOG request)
+ 421 | unable to proceed
+
+The server must report success in case of an unrecognized attribute
+name, although it may log a warning.
+
+Specific XCLIENT usage scenarios
+================================
+
+This section discusses the semantics of XCLIENT requests. Specific
+syntax information is given in the next section.
+
+The RST request resets all XCLIENT attributes and disables any
+override of access control or audit trail attributes. Example:
+
+ XCLIENT RST
+
+The ACL request resets all XCLIENT attributes and must be specified
+before sending surrogate attributes for access control and audit
+trail purposes. Attributes that are not explicitly specified will
+default to their actual value. Example:
+
+ XCLIENT ACL CLIENT_NAME=spike.porcupine.org
+ XCLIENT CLIENT_ADDR=168.100.189.2
+
+This overrides only the client hostname and network address, but
+none of the other client attributes.
+
+The LOG request resets all XCLIENT attributes and must be specified
+before sending surrogate attributes for audit trail purposes.
+Attributes that are not explicitly specified will default to the
+unknown value. Example:
+
+ XCLIENT LOG CLIENT_NAME=spike.porcupine.org CLIENT_ADDR=168.100.189.2
+ XCLIENT HELO_NAME=spike.porcupine.org PROTOCOL=ESMTP
+
+This overrides all client attributes that are defined in this
+document, leaving none at their default unknown value.
+
+Note 1: it is an error to specify name=value pairs without prior
+ACL or LOG request.
+
+Note 2: after an ACL or LOG request, attributes specified with
+successive XCLIENT commands accumulate until the next RST, ACL or
+LOG request.
+
+Note 3: if one XCLIENT command specifies multiple requests (e.g.,
+both ACL and LOG), only the last request takes effect.
+
+XCLIENT attribute value details
+===============================
+
+Attribute values are encoded as RFC 1891 xtext strings. To explicitly
+specify that an attribute value is unknown, the value must be empty;
+the client is not allowed to send its own internal representation
+for unknown information.
+
+CLIENT_CODE 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 set to the unknown
+value.
+
+CLIENT_NAME 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
+set to PERM. When a valid domain name is specified, CLIENT_CODE is
+set to OK. The server may process a syntactically invalid domain
+name as if it were unknown.
+
+CLIENT_ADDR must specify a numerical network address without [].
+
+PROTOCOL is a string of up to 64 printable characters.
+
+HELO_NAME should be a syntactically valid domain name.
+
+Note 4: syntactically valid CLIENT_NAME and HELO_NAME 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 5: attribute values may end up in Received: or other message
+headers. The server may substitute any characters that are special
+according to RFC 822 or RFC 2822.
+
+Security
+========
+
+The XCLIENT command changes audit trails and changes client access
+permissions. For these reasons, use of the XCLIENT command must be
+restricted to authorized clients only.
+
+The examples in this document assume that XCLIENT does not override
+its own access control mechanism.
+
+SMTP connection caching
+=======================
+
+SMTP connection caching makes it possible to deliver multiple
+messages within the same SMTP session. Thus, one persistent SMTP
+session with a content filter can carry messages from unrelated
+clients. Attributes from one remote client should not affect the
+delivery of mail from a different remote client. In order to
+prevent such information leakage, use the XCLIENT RST, ACL or LOG
+requests as appropriate.
#
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.
+# 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
+
# The smtp_line_length_limit parameter controls the length of
# message header and body lines that Postfix will send via SMTP.
# Lines that are longer are broken by inserting <CR> <LF> <SPACE>.
#
#disable_vrfy_command = no
-# The smtpd_authorized_xaddr_clients parameter specifies what clients
-# are allowed to specify the SMTP "XADDR client-address client-name"
-# command. This command changes Postfix's idea of the client hostname
-# and IP address for logging and for access control. Typical use is
-# for SMTPD access testing.
+# The smtpd_authorized_xclient_hosts parameter specifies what clients
+# are allowed to specify the XCLIENT extension to SMTP.
#
-# By default, no clients are allowed to specify XADDR.
+# 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.
#
-# 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_xaddr_clients =
-
-# The smtpd_authorized_xloginfo_clients parameter specifies what
-# clients are allowed to specify the SMTP "XLOGINFO client-address
-# client-name" command. This command changes Postfix's idea of the
-# client hostname and IP address for logging but not for access
-# control. Typical use is for SMTP-based content filters.
-#
-# By default, no clients are allowed to specify XLOGINFO.
+# By default, no clients are allowed to specify XCLIENT.
#
# Specify an explicit list of network/netmask patterns, where the
# mask specifies the number of bits in the network part of a host
# 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_xloginfo_clients =
+smtpd_authorized_xclient_hosts =
# The smtpd_authorized_verp_clients parameter specifies what clients
# are allowed to specify the SMTP XVERP command. This command requests
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
+ receiving the server response.
+
<b>smtp_mail_timeout</b>
- Timeout for sending the <b>MAIL FROM</b> command, and for
+ Timeout for sending the <b>MAIL FROM</b> command, and for
receiving the server response.
<b>smtp_rcpt_timeout</b>
- Timeout for sending the <b>RCPT TO</b> command, and for
+ Timeout for sending the <b>RCPT TO</b> command, and for
receiving the server response.
<b>smtp_data_init_timeout</b>
- Timeout for sending the <b>DATA</b> command, and for
+ Timeout for sending the <b>DATA</b> command, and for
receiving the server response.
<b>smtp_data_xfer_timeout</b>
<b>smtp_data_done_timeout</b>
Timeout for sending the "<b>.</b>" command, and for
- receiving the server response. When no response is
- received, a warning is logged that the mail may be
+ receiving the server response. When no response is
+ received, a warning is logged that the mail may be
delivered multiple times.
<b>smtp_defer_if_no_mx_address_found</b>
- If no, bounce mail when no MX host resolves to an
+ If no, bounce mail when no MX host resolves to an
address (Postfix always ignores MX hosts with equal
- or worse preference than the local MTA). If yes,
- keep trying until a suitable MX host resolves or
+ or worse preference than the local MTA). If yes,
+ 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
+ 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-
+ stream queuing SMTP server.
+
<b>smtp_rset_timeout</b>
Timeout for sending the <b>RSET</b> command.
<b>smtp_quit_timeout</b>
- Timeout for sending the <b>QUIT</b> command, and for
+ Timeout for sending the <b>QUIT</b> command, and for
receiving the server response.
<b>SEE ALSO</b>
syslogd(8) system logging
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
Hostnames, domain names and/or addresses of clients
that are authorized to use the XVERP extension.
- <b>smtpd_authorized_xaddr_clients</b>
+ <b>smtpd_authorized_xclient_hosts</b>
Hostnames, domain names and/or addresses of clients
- that are authorized to use the "XADDR client-
- address client-name" command. This changes Post-
- fix's idea of the SMTP client IP address and host-
- name for access control and for logging purposes.
-
- <b>smtpd_authorized_xloginfo_clients</b>
- Hostnames, domain names and/or addresses of clients
- that are authorized to use the "XLOGINFO client-
- address client-name" command. This changes the
- client name and address that are used for logging,
- without affecting the client IP address and host-
- name that are used for access control. XLOGINFO is
- typically used to propagate remote client informa-
- tion through an SMTP-based content filter to the
- after-filter SMTP server.
+ 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.
<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>
# SUMMARY
# makefile configuration utility
# SYNOPSIS
-# \fBmakedefs\fR
+# \fBmake makefiles \fIname=value...\fR
# DESCRIPTION
# The \fBmakedefs\fR command identifies the program compilation
# environment, and emits macro definitions on the standard output
# .IP \fBOPT=\fIoptimization_level\fR
# Specifies a non-default optimization level. The default is \fB-O\fR.
# Specify \fBOPT=\fR to turn off optimization.
+# .IP \fBWARN=\fIwarning_flags\fR
+# Specifies non-default gcc compiler warning options for use when
+# "make" is invoked in a source subdirectory only.
# LICENSE
# .ad
# .fi
*) : ${OPT='-O'};;
esac
-: ${CC='gcc $(WARN)'} ${OPT='-O'} ${DEBUG='-g'} ${AWK=awk}
+: ${CC='gcc $(WARN)'} ${OPT='-O'} ${DEBUG='-g'} ${AWK=awk} \
+${WARN='-W -Wformat -Wimplicit -Wmissing-prototypes \
+ -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
+ -Wunused'}
export SYSTYPE AR ARFL RANLIB SYSLIBS CC OPT DEBUG AWK OPTS
AWK = $AWK
STRCASE = $STRCASE
EXPORT = AUXLIBS='$AUXLIBS' CCARGS='$CCARGS' OPT='$OPT' DEBUG='$DEBUG'
+WARN = $WARN
EOF
.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
+receiving the server response.
.IP \fBsmtp_mail_timeout\fR
Timeout for sending the \fBMAIL FROM\fR command, and for
receiving the server response.
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,
+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 \fBsmtp_rset_timeout\fR
Timeout for sending the \fBRSET\fR command.
.IP \fBsmtp_quit_timeout\fR
.IP \fBsmtpd_authorized_verp_clients\fR
Hostnames, domain names and/or addresses of clients that are
authorized to use the XVERP extension.
-.IP \fBsmtpd_authorized_xaddr_clients\fR
+.IP \fBsmtpd_authorized_xclient_hosts\fR
Hostnames, domain names and/or addresses of clients that are
-authorized to use the "XADDR client-address client-name" command.
-This changes Postfix's
-idea of the SMTP client IP address and hostname for access
-control and for logging purposes.
-.IP \fBsmtpd_authorized_xloginfo_clients\fR
-Hostnames, domain names and/or addresses of clients that are
-authorized to use the "XLOGINFO client-address client-name" command.
-This changes the client
-name and address that are used for logging, without affecting the
-client IP address and hostname that are used for access control.
-XLOGINFO is typically used to propagate remote client information
-through an SMTP-based content filter to the after-filter SMTP server.
+authorized to use the XCLIENT command. This command changes
+client information for access control and/or logging purposes,
+with the exception of the
+\fBsmtpd_authorized_xclient_hosts\fR access control itself.
.IP \fBdebug_peer_level\fR
Increment in verbose logging level when a remote host matches a
pattern in the \fBdebug_peer_list\fR parameter.
OBJS = anvil.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
bounce_one_service.o bounce_warn_service.o bounce_trace_service.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
cleanup_addr.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG= cleanup_masquerade
OBJS = dns_lookup.o dns_rr.o dns_strerror.o dns_strtype.o
HDRS = dns.h
TESTSRC = test_dns_lookup.c test_alias_token.c
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
INCL =
OBJS = error.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = flush.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = fsstone.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
trace.h log_adhoc.h verify.h dict_proxy.h mail_dict.h qmgr_user.h \
input_transp.h anvil_clnt.h
TESTSRC = rec2stream.c stream2rec.c recdump.c
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
INCL =
/* long arrival_time;
/* RECIPIENT_LIST rcpt_list;
/* char *hop_status;
+/* char *client_name;
+/* char *client_addr;
+/* char *client_proto;
+/* char *client_helo;
/* .in -5
/* } DELIVER_REQUEST;
/*
static VSTRING *address;
static VSTRING *errors_to;
static VSTRING *return_receipt;
+ static VSTRING *client_name;
+ static VSTRING *client_addr;
+ static VSTRING *client_proto;
+ static VSTRING *client_helo;
long offset;
/*
address = vstring_alloc(10);
errors_to = vstring_alloc(10);
return_receipt = vstring_alloc(10);
+ client_name = vstring_alloc(10);
+ client_addr = vstring_alloc(10);
+ client_proto = vstring_alloc(10);
+ client_helo = vstring_alloc(10);
}
/*
ATTR_TYPE_STR, MAIL_ATTR_ERRTO, errors_to,
ATTR_TYPE_STR, MAIL_ATTR_RRCPT, return_receipt,
ATTR_TYPE_LONG, MAIL_ATTR_TIME, &request->arrival_time,
- ATTR_TYPE_END) != 11) {
+ ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, client_name,
+ ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, client_addr,
+ ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, client_proto,
+ ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME, client_helo,
+ ATTR_TYPE_END) != 15) {
msg_warn("%s: error receiving common attributes", myname);
return (-1);
}
request->sender = mystrdup(vstring_str(address));
request->errors_to = mystrdup(vstring_str(errors_to));
request->return_receipt = mystrdup(vstring_str(return_receipt));
+ request->client_name = mystrdup(vstring_str(client_name));
+ request->client_addr = mystrdup(vstring_str(client_addr));
+ request->client_proto = mystrdup(vstring_str(client_proto));
+ request->client_helo = mystrdup(vstring_str(client_helo));
/*
* Extract the recipient offset and address list. Skip over any
request->data_size = 0;
recipient_list_init(&request->rcpt_list);
request->hop_status = 0;
+ request->client_name = 0;
+ request->client_addr = 0;
+ request->client_proto = 0;
+ request->client_helo = 0;
return (request);
}
recipient_list_free(&request->rcpt_list);
if (request->hop_status)
myfree(request->hop_status);
+ if (request->client_name)
+ myfree(request->client_name);
+ if (request->client_addr)
+ myfree(request->client_addr);
+ if (request->client_proto)
+ myfree(request->client_proto);
+ if (request->client_helo)
+ myfree(request->client_helo);
myfree((char *) request);
}
long arrival_time; /* arrival time */
RECIPIENT_LIST rcpt_list; /* envelope recipients */
char *hop_status; /* reason if unavailable */
+ char *client_name; /* client hostname */
+ char *client_addr; /* client address */
+ char *client_proto; /* client protocol */
+ char *client_helo; /* helo parameter */
} DELIVER_REQUEST;
#define DEL_REQ_FLAG_DEFLT (DEL_REQ_FLAG_SUCCESS | DEL_REQ_FLAG_BOUNCE)
#define DEF_SMTP_HELO_TMOUT "300s"
extern int var_smtp_helo_tmout;
+#define VAR_SMTP_XCLNT_TMOUT "smtp_xclient_timeout"
+#define DEF_SMTP_XCLNT_TMOUT "300s"
+extern int var_smtp_xclnt_tmout;
+
#define VAR_SMTP_MAIL_TMOUT "smtp_mail_timeout"
#define DEF_SMTP_MAIL_TMOUT "300s"
extern int var_smtp_mail_tmout;
#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;
+
/*
* SMTP server. The soft error limit determines how many errors an SMTP
* client may make before we start to slow down; the hard error limit
extern char *var_verp_clients;
/*
- * XADDR.
+ * XCLIENT, for rule testing and improved post-filter logging.
*/
-#define VAR_XADDR_CLIENTS "smtpd_authorized_xaddr_clients"
-#define DEF_XADDR_CLIENTS ""
-extern char *var_xaddr_clients;
-
-#define VAR_XLOGINFO_CLIENTS "smtpd_authorized_xloginfo_clients"
-#define DEF_XLOGINFO_CLIENTS ""
-extern char *var_xloginfo_clients;
+#define VAR_XCLIENT_HOSTS "smtpd_authorized_xclient_hosts"
+#define DEF_XCLIENT_HOSTS ""
+extern char *var_xclient_hosts;
/*
* Inbound mail flow control. This allows for a stiffer coupling between
* System library.
*/
#include <stdarg.h>
+#include <string.h>
/*
* Utility library.
#define MAIL_ATTR_ORG_NONE "unknown" /* origin unknown */
#define MAIL_ATTR_ORG_LOCAL "local" /* local submission */
+ /*
+ * Internal forms for unknown XCLIENT information.
+ */
+#define CLIENT_NAME_UNKNOWN "unknown"
+#define CLIENT_ADDR_UNKNOWN "unknown"
+#define CLIENT_NAMADDR_UNKNOWN CLIENT_NAME_UNKNOWN "[" CLIENT_ADDR_UNKNOWN "]"
+#define HELO_NAME_UNKNOWN "" /* or NULL */
+#define PROTOCOL_UNKNOWN "unknown"
+
+ /*
+ * Internal forms: recognizing unknown XCLIENT information.
+ */
+#define IS_UNK_CLNT_NAME(v) (!(v) || !strcmp((v), CLIENT_NAME_UNKNOWN))
+#define IS_UNK_CLNT_ADDR(v) (!(v) || !strcmp((v), CLIENT_ADDR_UNKNOWN))
+#define IS_UNK_CLNT_NAMADDR(v) (!(v) || !strcmp((v), CLIENT_NAMADDR_UNKNOWN))
+#define IS_UNK_HELO_NAME(v) (!(v) || !*(v))
+#define IS_UNK_PROTOCOL(v) (!(v) || !strcmp((v), PROTOCOL_UNKNOWN))
+
/* LICENSE
/* .ad
/* .fi
#include <unistd.h>
#include <errno.h>
#include <utime.h>
+#include <string.h>
/* Utility library. */
* 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 "20031113"
+#define MAIL_RELEASE_DATE "20031128"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "2.0.16-" MAIL_RELEASE_DATE
* Outputs and state changes are interleaved, so we must maintain separate
* offsets for header and body segments.
*/
-#define HEAD_OUT(ptr, info) do { \
+#define HEAD_OUT(ptr, info, len) do { \
(ptr)->head_out((ptr)->app_context, (ptr)->curr_state, \
(info), (ptr)->output_buffer, (ptr)->head_offset); \
(ptr)->head_offset += (len) + 1; \
|| header_info->type != HDR_CONTENT_TRANSFER_ENCODING
|| (state->static_flags & MIME_OPT_DOWNGRADE) == 0
|| state->curr_domain == MIME_ENC_7BIT)
- HEAD_OUT(state, header_info);
+ HEAD_OUT(state, header_info, len);
state->prev_rec_type = 0;
VSTRING_RESET(state->output_buffer);
}
cp = CU_CHAR_PTR("quoted-printable");
vstring_sprintf(state->output_buffer,
"Content-Transfer-Encoding: %s", cp);
- HEAD_OUT(state, (HEADER_OPTS *) 0);
+ HEAD_OUT(state, (HEADER_OPTS *) 0, len);
VSTRING_RESET(state->output_buffer);
}
#include <stdlib.h>
#include <string.h>
#include <msg_vstream.h>
+#include <split_at.h>
#include <vstring_vstream.h>
#include <mail_conf.h>
#include <mail_params.h>
VSTRING *buffer = vstring_alloc(1);
while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
- if ((rule = strtok(STR(buffer), " \t,")) == 0
- || (addr = strtok((char *) 0, " \t,")) == 0)
+ if ((addr = split_at(STR(buffer), ' ')) == 0
+ || *(rule = STR(buffer)) == 0)
usage(argv[0]);
rewrite(rule, addr, reply);
}
/* NAME
/* xtext 3
/* SUMMARY
-/* quote/unquote text, HTTP style.
+/* quote/unquote text, xtext style.
/* SYNOPSIS
/* #include <xtext.h>
/*
/* const char *unquoted;
/* const char *special;
/*
+/* VSTRING *xtext_unquote_append(unquoted, quoted)
+/* VSTRING *unquoted;
+/* const char *quoted;
+/*
/* VSTRING *xtext_unquote(unquoted, quoted)
/* VSTRING *unquoted;
/* const char *quoted;
/* +, <33(10) and >126(10), as well as characters specified with "special"
/* by +XX, XX being the two-digit uppercase hexadecimal equivalent.
/*
+/* xtext_quote_append() is like xtext_quote(), but appends the conversion
+/* result to the result buffer.
+/*
/* xtext_unquote() performs the opposite transformation. This function
/* understands lowercase, uppercase, and mixed case +XX sequences. The
/* result value is the unquoted argument in case of success, a null pointer
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
-/* xtext_quote - unquoted data to quoted */
+/* xtext_quote_append - append unquoted data to quoted data */
-VSTRING *xtext_quote(VSTRING *quoted, const char *unquoted, const char *special)
+VSTRING *xtext_quote_append(VSTRING *quoted, const char *unquoted,
+ const char *special)
{
const char *cp;
int ch;
- VSTRING_RESET(quoted);
for (cp = unquoted; (ch = *(unsigned const char *) cp) != 0; cp++) {
- if (ch != '+' && ch > 32 && ch < 127 && strchr(special, ch) == 0) {
+ if (ch != '+' && ch > 32 && ch < 127
+ && (*special == 0 || strchr(special, ch) == 0)) {
VSTRING_ADDCH(quoted, ch);
} else {
vstring_sprintf_append(quoted, "+%02X", ch);
return (quoted);
}
+/* xtext_quote - unquoted data to quoted */
+
+VSTRING *xtext_quote(VSTRING *quoted, const char *unquoted, const char *special)
+{
+ VSTRING_RESET(quoted);
+ xtext_quote_append(quoted, unquoted, special);
+ return (quoted);
+}
+
/* xtext_unquote - quoted data to unquoted */
VSTRING *xtext_unquote(VSTRING *unquoted, const char *quoted)
/*
* Utility library.
- */
+ */
#include <vstring.h>
/*
* External interface.
*/
extern VSTRING *xtext_quote(VSTRING *, const char *, const char *);
+extern VSTRING *xtext_quote_append(VSTRING *, const char *, const char *);
extern VSTRING *xtext_unquote(VSTRING *, const char *);
/* LICENSE
lmtp_sasl_proto.o
HDRS = lmtp.h
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
local_expand.o
HDRS = local.h
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
PROG = local
mail_flow.o
HDRS = mail_server.h master_proto.h mail_flow.h
INT_HDR = master.h
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
LIB = libmaster.a
MASTER_INET_ADDRLIST(serv) = (INET_ADDR_LIST *)
mymalloc(sizeof(*MASTER_INET_ADDRLIST(serv)));
inet_addr_list_init(MASTER_INET_ADDRLIST(serv));
- inet_addr_host(MASTER_INET_ADDRLIST(serv), host);
+ if (inet_addr_host(MASTER_INET_ADDRLIST(serv), host) == 0)
+ msg_fatal("%s: line %d: bad hostname or network address: %s",
+ VSTREAM_PATH(master_fp), master_line, host);
inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv));
serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used;
} else if (strcasecmp(saved_interfaces, DEF_INET_INTERFACES) == 0) {
qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o
HDRS = qmgr.h
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
char *redirect_addr; /* info@spammer.tld */
long data_size; /* message content size */
long rcpt_offset; /* more recipients here */
+ char *client_name; /* client hostname */
+ char *client_addr; /* client address */
+ char *client_proto; /* client protocol */
+ char *client_helo; /* helo parameter */
QMGR_RCPT_LIST rcpt_list; /* complete addresses */
int rcpt_count; /* used recipient slots */
int rcpt_limit; /* maximum read in-core */
message->queue_id, message->queue_name);
msg_warn("%s: remove %s from %s: %m", myname,
message->queue_id, message->queue_name);
- } else if (msg_verbose) {
- msg_info("%s: remove %s", myname, message->queue_id);
+ } else {
+ /* Same format as logged by postsuper. */
+ msg_info("%s: removed", message->queue_id);
}
}
ATTR_TYPE_STR, MAIL_ATTR_ERRTO, message->errors_to,
ATTR_TYPE_STR, MAIL_ATTR_RRCPT, message->return_receipt,
ATTR_TYPE_LONG, MAIL_ATTR_TIME, message->arrival_time,
+ ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, message->client_name,
+ ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, message->client_addr,
+ ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, message->client_proto,
+ ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME, message->client_helo,
ATTR_TYPE_END);
if (sender_buf != 0)
vstring_free(sender_buf);
message->warn_time = 0;
message->rcpt_offset = 0;
message->verp_delims = 0;
+ message->client_name = 0;
+ message->client_addr = 0;
+ message->client_proto = 0;
+ message->client_helo = 0;
qmgr_rcpt_list_init(&message->rcpt_list);
message->rcpt_count = 0;
message->rcpt_limit = var_qmgr_msg_rcpt_limit;
myfree(message->encoding);
message->encoding = mystrdup(value);
}
+ /* Original client attributes. */
+ if (strcmp(name, MAIL_ATTR_CLIENT_NAME) == 0) {
+ if (message->client_name != 0)
+ myfree(message->client_name);
+ message->client_name = mystrdup(value);
+ }
+ if (strcmp(name, MAIL_ATTR_CLIENT_ADDR) == 0) {
+ if (message->client_addr != 0)
+ myfree(message->client_addr);
+ message->client_addr = mystrdup(value);
+ }
+ if (strcmp(name, MAIL_ATTR_PROTO_NAME) == 0) {
+ if (message->client_proto != 0)
+ myfree(message->client_proto);
+ message->client_proto = mystrdup(value);
+ }
+ if (strcmp(name, MAIL_ATTR_HELO_NAME) == 0) {
+ if (message->client_helo != 0)
+ myfree(message->client_helo);
+ message->client_helo = mystrdup(value);
+ }
/* Optional tracing flags. */
else if (strcmp(name, MAIL_ATTR_TRACE_FLAGS) == 0) {
message->tflags = DEL_REQ_TRACE_FLAGS(atoi(value));
message->return_receipt = mystrdup("");
if (message->encoding == 0)
message->encoding = mystrdup(MAIL_ATTR_ENC_NONE);
+ if (message->client_name == 0)
+ message->client_name = mystrdup(CLIENT_NAME_UNKNOWN);
+ if (message->client_addr == 0)
+ message->client_addr = mystrdup(CLIENT_ADDR_UNKNOWN);
+ if (message->client_proto == 0)
+ message->client_proto = mystrdup(PROTOCOL_UNKNOWN);
+ if (message->client_helo == 0)
+ message->client_helo = mystrdup(HELO_NAME_UNKNOWN);
/*
* Clean up.
OBJS = pickup.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = pipe.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = postalias.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = postcat.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = postconf.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = postdrop.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = postfix.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
FILES = Makefile $(SRCS) $(HDRS)
OBJS = postkick.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = postlock.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = postlog.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
FILES = Makefile $(SRCS) $(HDRS)
OBJS = postmap.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = postqueue.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = postsuper.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = proxymap.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o
HDRS = qmgr.h
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
char *redirect_addr; /* info@spammer.tld */
long data_size; /* message content size */
long rcpt_offset; /* more recipients here */
+ char *client_name; /* client hostname */
+ char *client_addr; /* client address */
+ char *client_proto; /* client protocol */
+ char *client_helo; /* helo parameter */
QMGR_RCPT_LIST rcpt_list; /* complete addresses */
};
message->queue_id, message->queue_name);
msg_warn("%s: remove %s from %s: %m", myname,
message->queue_id, message->queue_name);
- } else if (msg_verbose) {
- msg_info("%s: remove %s", myname, message->queue_id);
+ } else {
+ /* Same format as logged by postsuper. */
+ msg_info("%s: removed", message->queue_id);
}
}
ATTR_TYPE_STR, MAIL_ATTR_ERRTO, message->errors_to,
ATTR_TYPE_STR, MAIL_ATTR_RRCPT, message->return_receipt,
ATTR_TYPE_LONG, MAIL_ATTR_TIME, message->arrival_time,
+ ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, message->client_name,
+ ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, message->client_addr,
+ ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, message->client_proto,
+ ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME, message->client_helo,
ATTR_TYPE_END);
if (sender_buf != 0)
vstring_free(sender_buf);
message->warn_time = 0;
message->rcpt_offset = 0;
message->verp_delims = 0;
+ message->client_name = 0;
+ message->client_addr = 0;
+ message->client_proto = 0;
+ message->client_helo = 0;
qmgr_rcpt_list_init(&message->rcpt_list);
return (message);
}
myfree(message->encoding);
message->encoding = mystrdup(value);
}
+ /* Original client attributes. */
+ if (strcmp(name, MAIL_ATTR_CLIENT_NAME) == 0) {
+ if (message->client_name != 0)
+ myfree(message->client_name);
+ message->client_name = mystrdup(value);
+ }
+ if (strcmp(name, MAIL_ATTR_CLIENT_ADDR) == 0) {
+ if (message->client_addr != 0)
+ myfree(message->client_addr);
+ message->client_addr = mystrdup(value);
+ }
+ if (strcmp(name, MAIL_ATTR_PROTO_NAME) == 0) {
+ if (message->client_proto != 0)
+ myfree(message->client_proto);
+ message->client_proto = mystrdup(value);
+ }
+ if (strcmp(name, MAIL_ATTR_HELO_NAME) == 0) {
+ if (message->client_helo != 0)
+ myfree(message->client_helo);
+ message->client_helo = mystrdup(value);
+ }
/* Optional tracing flags. */
else if (strcmp(name, MAIL_ATTR_TRACE_FLAGS) == 0) {
message->tflags = DEL_REQ_TRACE_FLAGS(atoi(value));
message->return_receipt = mystrdup("");
if (message->encoding == 0)
message->encoding = mystrdup(MAIL_ATTR_ENC_NONE);
+ if (message->client_name == 0)
+ message->client_name = mystrdup(CLIENT_NAME_UNKNOWN);
+ if (message->client_addr == 0)
+ message->client_addr = mystrdup(CLIENT_ADDR_UNKNOWN);
+ if (message->client_proto == 0)
+ message->client_proto = mystrdup(PROTOCOL_UNKNOWN);
+ if (message->client_helo == 0)
+ message->client_helo = mystrdup(HELO_NAME_UNKNOWN);
/*
* Clean up.
OBJS = qmqpd.o qmqpd_state.o qmqpd_peer.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = sendmail.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
for (fd = 0; fd < 3; fd++)
if (fstat(fd, &st) == -1
&& (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
- msg_fatal_status(EX_UNAVAILABLE, "open /dev/null: %m");
+ msg_fatal_status(EX_OSERR, "open /dev/null: %m");
/*
* The CDE desktop calendar manager leaks a parent file descriptor into
OBJS = showq.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
smtp_sasl_proto.o smtp_sasl_glue.o
HDRS = smtp.h smtp_sasl.h
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG= smtp_unalias
smtp_proto.o: ../../include/attr.h
smtp_proto.o: ../../include/mime_state.h
smtp_proto.o: ../../include/header_opts.h
+smtp_proto.o: ../../include/xtext.h
smtp_proto.o: smtp.h
smtp_proto.o: ../../include/argv.h
smtp_proto.o: smtp_sasl.h
/* .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
+/* receiving the server response.
/* .IP \fBsmtp_mail_timeout\fR
/* Timeout for sending the \fBMAIL FROM\fR command, and for
/* receiving the server response.
/* 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,
+/* 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 \fBsmtp_rset_timeout\fR
/* Timeout for sending the \fBRSET\fR command.
/* .IP \fBsmtp_quit_timeout\fR
*/
int var_smtp_conn_tmout;
int var_smtp_helo_tmout;
+int var_smtp_xclnt_tmout;
int var_smtp_mail_tmout;
int var_smtp_rcpt_tmout;
int var_smtp_data0_tmout;
char *var_smtp_host_lookup;
bool var_smtp_quote_821_env;
bool var_smtp_defer_mxaddr;
+bool var_smtp_send_xclient;
/*
* Global variables. smtp_errno is set by the address lookup routines and by
static CONFIG_TIME_TABLE time_table[] = {
VAR_SMTP_CONN_TMOUT, DEF_SMTP_CONN_TMOUT, &var_smtp_conn_tmout, 0, 0,
VAR_SMTP_HELO_TMOUT, DEF_SMTP_HELO_TMOUT, &var_smtp_helo_tmout, 1, 0,
+ VAR_SMTP_XCLNT_TMOUT, DEF_SMTP_XCLNT_TMOUT, &var_smtp_xclnt_tmout, 1, 0,
VAR_SMTP_MAIL_TMOUT, DEF_SMTP_MAIL_TMOUT, &var_smtp_mail_tmout, 1, 0,
VAR_SMTP_RCPT_TMOUT, DEF_SMTP_RCPT_TMOUT, &var_smtp_rcpt_tmout, 1, 0,
VAR_SMTP_DATA0_TMOUT, DEF_SMTP_DATA0_TMOUT, &var_smtp_data0_tmout, 1, 0,
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,
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 */
/*
* smtp.c
#include <quote_821_local.h>
#include <mail_proto.h>
#include <mime_state.h>
+#include <xtext.h>
/* Application-specific. */
* SMTP_STATE_DOT) must have smaller numerical values than the non-sending
* states (SMTP_STATE_ABORT .. SMTP_STATE_LAST).
*/
-#define SMTP_STATE_MAIL 0
-#define SMTP_STATE_RCPT 1
-#define SMTP_STATE_DATA 2
-#define SMTP_STATE_DOT 3
-#define SMTP_STATE_ABORT 4
-#define SMTP_STATE_QUIT 5
-#define SMTP_STATE_LAST 6
+#define SMTP_STATE_XCLIENT_ADDR 0
+#define SMTP_STATE_XCLIENT_HELO 1
+#define SMTP_STATE_MAIL 2
+#define SMTP_STATE_RCPT 3
+#define SMTP_STATE_DATA 4
+#define SMTP_STATE_DOT 5
+#define SMTP_STATE_ABORT 6
+#define SMTP_STATE_QUIT 7
+#define SMTP_STATE_LAST 8
int *xfer_timeouts[SMTP_STATE_LAST] = {
+ &var_smtp_xclnt_tmout,
+ &var_smtp_xclnt_tmout,
&var_smtp_mail_tmout,
&var_smtp_rcpt_tmout,
&var_smtp_data0_tmout,
};
char *xfer_states[SMTP_STATE_LAST] = {
+ "sending XCLIENT address and name",
+ "sending XCLIENT helo_name and protocol",
"sending MAIL FROM",
"sending RCPT TO",
"sending DATA command",
};
char *xfer_request[SMTP_STATE_LAST] = {
+ "XCLIENT command",
+ "XCLIENT command",
"MAIL FROM command",
"RCPT TO command",
"DATA command",
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) {
state->features |= SMTP_FEATURE_SIZE;
if ((word = mystrtok(&words, " \t")) != 0) {
int mail_from_rejected;
int downgrading;
int mime_errs;
+ int send_xclient_addr = 0;
+ int send_xclient_helo = 0;
/*
* Macros for readability.
* receiver detects a serious problem (MAIL FROM rejected, all RCPT TO
* commands rejected, DATA rejected) it forces the sender to abort the
* SMTP dialog with RSET and QUIT.
+ *
+ * Send "XCLIENT LOG" information only if we have a surrogate remote client
+ * name and address, i.e. the mail was actually received from the
+ * network. Since "XCLIENT LOG" overrides all remote client logging
+ * attributes, there is no need to send helo or protocol information that
+ * we do not have.
*/
nrcpt = 0;
- recv_state = send_state = SMTP_STATE_MAIL;
+ send_xclient_addr = (state->features & SMTP_FEATURE_XCLIENT)
+ && !IS_UNK_CLNT_NAME(request->client_name)
+ && !IS_UNK_CLNT_ADDR(request->client_addr);
+ if (send_xclient_addr) {
+ send_xclient_helo = !IS_UNK_HELO_NAME(request->client_helo)
+ || !IS_UNK_PROTOCOL(request->client_proto);
+ recv_state = send_state = SMTP_STATE_XCLIENT_ADDR;
+ } else
+ recv_state = send_state = SMTP_STATE_MAIL;
next_rcpt = send_rcpt = recv_rcpt = 0;
mail_from_rejected = 0;
default:
msg_panic("%s: bad sender state %d", myname, send_state);
+ /*
+ * Build the XCLIENT command. Send what we know, converting
+ * internal form to external form. With properly sanitized
+ * information, this stays within the 512 byte command line
+ * length limit.
+ */
+ case SMTP_STATE_XCLIENT_ADDR:
+ vstring_sprintf(next_command, "XCLIENT LOG");
+ if (!IS_UNK_CLNT_NAME(request->client_name)) {
+ vstring_strcat(next_command, " CLIENT_NAME=");
+ xtext_quote_append(next_command, request->client_name, "");
+ }
+ if (!IS_UNK_CLNT_ADDR(request->client_addr)) {
+ vstring_strcat(next_command, " CLIENT_ADDR=");
+ xtext_quote_append(next_command, request->client_addr, "");
+ }
+ if (send_xclient_helo)
+ next_state = SMTP_STATE_XCLIENT_HELO;
+ else
+ next_state = SMTP_STATE_MAIL;
+ break;
+
+ case SMTP_STATE_XCLIENT_HELO:
+ vstring_sprintf(next_command, "XCLIENT");
+ if (!IS_UNK_HELO_NAME(request->client_helo)) {
+ vstring_strcat(next_command, " HELO_NAME=");
+ xtext_quote_append(next_command, request->client_helo, "");
+ }
+ if (!IS_UNK_PROTOCOL(request->client_proto)) {
+ vstring_strcat(next_command, " PROTOCOL=");
+ xtext_quote_append(next_command, request->client_proto, "");
+ }
+ next_state = SMTP_STATE_MAIL;
+ break;
+
/*
* Build the MAIL FROM command.
*/
/*
* Sanity check.
*/
- if (recv_state < SMTP_STATE_MAIL
+ if (recv_state < SMTP_STATE_XCLIENT_ADDR
|| recv_state > SMTP_STATE_QUIT)
msg_panic("%s: bad receiver state %d (sender state %d)",
myname, recv_state, send_state);
*/
switch (recv_state) {
+ /*
+ * Ignore the XCLIENT response. No Duff device needed.
+ */
+ case SMTP_STATE_XCLIENT_ADDR:
+ if (send_xclient_helo)
+ recv_state = SMTP_STATE_XCLIENT_HELO;
+ else
+ recv_state = SMTP_STATE_MAIL;
+ break;
+
+ case SMTP_STATE_XCLIENT_HELO:
+ recv_state = SMTP_STATE_MAIL;
+ break;
+
/*
* Process the MAIL FROM response. When the server
* rejects the sender, set the mail_from_rejected flag so
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_peer.c smtpd_sasl_proto.c smtpd_sasl_glue.c smtpd_proxy.c \
+ smtpd_xclient.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_peer.o smtpd_sasl_proto.o smtpd_sasl_glue.o smtpd_proxy.o \
+ smtpd_xclient.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
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG= smtpd_token smtpd_check
smtpd.o: ../../include/argv.h
smtpd.o: ../../include/watchdog.h
smtpd.o: ../../include/iostuff.h
+smtpd.o: ../../include/split_at.h
smtpd.o: ../../include/mail_params.h
smtpd.o: ../../include/record.h
smtpd.o: ../../include/rec_type.h
smtpd.o: ../../include/input_transp.h
smtpd.o: ../../include/anvil_clnt.h
smtpd.o: ../../include/attr_clnt.h
+smtpd.o: ../../include/xtext.h
smtpd.o: ../../include/mail_server.h
smtpd.o: smtpd_token.h
smtpd.o: smtpd.h
smtpd_peer.o: ../../include/stringops.h
smtpd_peer.o: ../../include/vstring.h
smtpd_peer.o: ../../include/vbuf.h
-smtpd_peer.o: smtpd.h
+smtpd_peer.o: ../../include/mail_proto.h
smtpd_peer.o: ../../include/vstream.h
+smtpd_peer.o: ../../include/iostuff.h
+smtpd_peer.o: ../../include/attr.h
+smtpd_peer.o: smtpd.h
smtpd_peer.o: ../../include/argv.h
smtpd_peer.o: ../../include/mail_stream.h
smtpd_proxy.o: smtpd_proxy.c
smtpd_proxy.o: ../../include/cleanup_user.h
smtpd_proxy.o: ../../include/mail_params.h
smtpd_proxy.o: ../../include/rec_type.h
+smtpd_proxy.o: ../../include/xtext.h
+smtpd_proxy.o: ../../include/mail_proto.h
+smtpd_proxy.o: ../../include/attr.h
smtpd_proxy.o: smtpd.h
smtpd_proxy.o: ../../include/argv.h
smtpd_proxy.o: ../../include/mail_stream.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
/* .IP \fBsmtpd_authorized_verp_clients\fR
/* Hostnames, domain names and/or addresses of clients that are
/* authorized to use the XVERP extension.
-/* .IP \fBsmtpd_authorized_xaddr_clients\fR
+/* .IP \fBsmtpd_authorized_xclient_hosts\fR
/* Hostnames, domain names and/or addresses of clients that are
-/* authorized to use the "XADDR client-address client-name" command.
-/* This changes Postfix's
-/* idea of the SMTP client IP address and hostname for access
-/* control and for logging purposes.
-/* .IP \fBsmtpd_authorized_xloginfo_clients\fR
-/* Hostnames, domain names and/or addresses of clients that are
-/* authorized to use the "XLOGINFO client-address client-name" command.
-/* This changes the client
-/* name and address that are used for logging, without affecting the
-/* client IP address and hostname that are used for access control.
-/* XLOGINFO is typically used to propagate remote client information
-/* through an SMTP-based content filter to the after-filter SMTP server.
+/* authorized to use the XCLIENT command. This command changes
+/* client information for access control and/or logging purposes,
+/* with the exception of the
+/* \fBsmtpd_authorized_xclient_hosts\fR access control itself.
/* .IP \fBdebug_peer_level\fR
/* Increment in verbose logging level when a remote host matches a
/* pattern in the \fBdebug_peer_list\fR parameter.
#include <dict.h>
#include <watchdog.h>
#include <iostuff.h>
+#include <split_at.h>
/* Global library. */
#include <namadr_list.h>
#include <input_transp.h>
#include <anvil_clnt.h>
+#include <xtext.h>
/* Single-threaded server skeleton. */
int var_smtpd_policy_tmout;
int var_smtpd_policy_idle;
int var_smtpd_policy_ttl;
-char *var_xaddr_clients;
-char *var_xloginfo_clients;
+char *var_xclient_hosts;
int var_smtpd_crate_limit;
int var_smtpd_cconn_limit;
char *var_smtpd_hoggers;
static NAMADR_LIST *verp_clients;
/*
- * XADDR command.
- */
-#define XADDR_CMD "XADDR"
-
-static NAMADR_LIST *xaddr_clients;
-
- /*
- * XLOGINFO command.
+ * XCLIENT command.
*/
-static NAMADR_LIST *xloginfo_clients;
+static NAMADR_LIST *xclient_hosts;
/*
* Client connection and rate limiting.
return (0);
match = namadr_list_match(sasl_exceptions_networks,
- state->name, state->addr);
+ ACL_NAME(state), ACL_ADDR(state));
if (msg_verbose)
- msg_info("sasl_exceptions: %s[%s], match=%d",
- state->name, state->addr, match);
+ msg_info("sasl_exceptions: %s, match=%d", ACL_NAMADDR(state), match);
return (match);
}
mail_reset(state);
rcpt_reset(state);
state->helo_name = mystrdup(printable(argv[1].strval, '?'));
+ neuter(state->helo_name, "<>()\\\";:@", '?');
+ /* Changing the protocol name breaks the unauthorized pipelining check. */
if (strcmp(state->protocol, MAIL_PROTO_ESMTP) != 0)
state->protocol = MAIL_PROTO_SMTP;
smtpd_chat_reply(state, "250 %s", var_myhostname);
mail_reset(state);
rcpt_reset(state);
state->helo_name = mystrdup(printable(argv[1].strval, '?'));
+ neuter(state->helo_name, "<>()\\\";:@", '?');
state->protocol = MAIL_PROTO_ESMTP;
smtpd_chat_reply(state, "250-%s", var_myhostname);
smtpd_chat_reply(state, "250-PIPELINING");
smtpd_chat_reply(state, "250-AUTH=%s", state->sasl_mechanism_list);
}
#endif
- if (namadr_list_match(verp_clients, state->name, state->addr))
+ if (namadr_list_match(verp_clients, ACL_NAME(state), ACL_ADDR(state)))
smtpd_chat_reply(state, "250-%s", VERP_CMD);
- if (namadr_list_match(xaddr_clients, state->name, state->addr))
- smtpd_chat_reply(state, "250-%s", XADDR_CMD);
- if (namadr_list_match(xloginfo_clients, state->name, state->addr))
- smtpd_chat_reply(state, "250-%s", XLOGINFO_CMD);
+ /* XCLIENT must not override its own access control. */
+ if (namadr_list_match(xclient_hosts, state->name, state->addr))
+ smtpd_chat_reply(state, "250-%s", XCLIENT_CMD);
smtpd_chat_reply(state, "250 8BITMIME");
return (0);
}
/* mail_open_stream - open mail queue file or IPC stream */
-static void mail_open_stream(SMTPD_STATE *state, SMTPD_TOKEN *argv,
- const char *encoding, const char *verp_delims)
+static void mail_open_stream(SMTPD_STATE *state, SMTPD_TOKEN *argv)
{
char *postdrop_command;
int cleanup_flags;
smtpd_sasl_mail_log(state);
else
#endif
- msg_info("%s: client=%s", state->queue_id, state->namaddr);
+ msg_info("%s: client=%s", state->queue_id, LOG_NAMADDR(state));
/*
* Record the time of arrival, the sender envelope address, some session
rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", var_filter_xport);
}
rec_fputs(state->cleanup, REC_TYPE_FROM, argv[2].strval);
- if (encoding != 0)
+ if (state->encoding != 0)
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_ENCODING, encoding);
+ MAIL_ATTR_ENCODING, state->encoding);
+
+ /*
+ * Store the client attributes for logging purposes.
+ */
if (SMTPD_STAND_ALONE(state) == 0) {
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_CLIENT_NAME, state->name);
+ MAIL_ATTR_CLIENT_NAME, LOG_NAME(state));
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_CLIENT_ADDR, LOG_ADDR(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_CLIENT_ADDR, state->addr);
+ MAIL_ATTR_ORIGIN, LOG_NAMADDR(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_ORIGIN, state->namaddr);
- if (state->helo_name != 0)
- rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_HELO_NAME, state->helo_name);
+ MAIL_ATTR_HELO_NAME,
+ IS_UNK_HELO_NAME(LOG_HELO_NAME(state)) ?
+ HELO_NAME_UNKNOWN : LOG_HELO_NAME(state));
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_PROTO_NAME, state->protocol);
+ MAIL_ATTR_PROTO_NAME, LOG_PROTOCOL(state));
}
- if (verp_delims)
- rec_fputs(state->cleanup, REC_TYPE_VERP, verp_delims);
+ if (state->verp_delims)
+ rec_fputs(state->cleanup, REC_TYPE_VERP, state->verp_delims);
}
/* extract_addr - extract address from rubble */
int narg;
char *arg;
char *verp_delims = 0;
- char *encoding = 0;
+ state->encoding = 0;
state->msg_size = 0;
/*
smtpd_chat_reply(state, "503 Error: send HELO/EHLO first");
return (-1);
}
-#define IN_MAIL_TRANSACTION(state) ((state)->cleanup || (state)->proxy)
+#define IN_MAIL_TRANSACTION(state) ((state)->sender != 0)
if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
}
if (argv[2].tokval == SMTPD_TOK_ERROR) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Bad address syntax");
+ smtpd_chat_reply(state, "501 Bad sender address syntax");
return (-1);
}
if ((err = extract_addr(state, argv + 2, PERMIT_EMPTY_ADDR, var_strict_rfc821_env)) != 0) {
for (narg = 3; narg < argc; narg++) {
arg = argv[narg].strval;
if (strcasecmp(arg, "BODY=8BITMIME") == 0) { /* RFC 1652 */
- encoding = MAIL_ATTR_ENC_8BIT;
+ state->encoding = MAIL_ATTR_ENC_8BIT;
} else if (strcasecmp(arg, "BODY=7BIT") == 0) { /* RFC 1652 */
- encoding = MAIL_ATTR_ENC_7BIT;
+ state->encoding = MAIL_ATTR_ENC_7BIT;
} else if (strncasecmp(arg, "SIZE=", 5) == 0) { /* RFC 1870 */
/* Reject non-numeric size. */
if (!alldig(arg + 5)) {
return (-1);
}
#endif
- } else if (namadr_list_match(verp_clients, state->name, state->addr)) {
- if (strcasecmp(arg, VERP_CMD) == 0) {
+ } else if (namadr_list_match(verp_clients, ACL_NAME(state),
+ ACL_ADDR(state))
+ && strncasecmp(arg, VERP_CMD, VERP_CMD_LEN) == 0
+ && (arg[VERP_CMD_LEN] == '=' || arg[VERP_CMD_LEN] == 0)) {
+ if (arg[VERP_CMD_LEN] == 0) {
verp_delims = var_verp_delims;
- } else if (strncasecmp(arg, VERP_CMD, VERP_CMD_LEN) == 0
- && arg[VERP_CMD_LEN] == '=') {
+ } else {
verp_delims = arg + VERP_CMD_LEN + 1;
if (verp_delims_verify(verp_delims) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
state->time = time((time_t *) 0);
/*
- * Open connection to SMTP proxy server.
+ * Check the queue file space, if applicable.
*/
- if (SMTPD_STAND_ALONE(state) == 0 && *var_smtpd_proxy_filt) {
- if (smtpd_proxy_open(state, var_smtpd_proxy_filt, var_smtpd_proxy_tmout,
- var_smtpd_proxy_ehlo, STR(state->buffer)) != 0) {
- smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
- return (-1);
- }
- }
+#define USE_SMTPD_PROXY(state) \
+ (SMTPD_STAND_ALONE(state) == 0 && *var_smtpd_proxy_filt)
- /*
- * Open queue file, or open connection to queue file writing process.
- * Check for queue file space first.
- */
- else {
+ if (!USE_SMTPD_PROXY(state)) {
if ((err = smtpd_check_size(state, state->msg_size)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
- mail_open_stream(state, argv, encoding, verp_delims);
}
+
+ /*
+ * No more early returns. The mail transaction is in progress.
+ */
state->sender = mystrdup(argv[2].strval);
+ if (verp_delims)
+ state->verp_delims = mystrdup(verp_delims);
+ if (USE_SMTPD_PROXY(state))
+ state->proxy_mail = mystrdup(STR(state->buffer));
smtpd_chat_reply(state, "250 Ok");
return (0);
}
myfree(state->sender);
state->sender = 0;
}
+ if (state->verp_delims) {
+ myfree(state->verp_delims);
+ state->verp_delims = 0;
+ }
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable)
smtpd_sasl_mail_reset(state);
(void) smtpd_proxy_cmd(state, SMTPD_PROX_WANT_NONE, "QUIT");
smtpd_proxy_close(state);
}
+ if (state->proxy_mail) {
+ myfree(state->proxy_mail);
+ state->proxy_mail = 0;
+ }
}
/* rcpt_cmd - process RCPT TO command */
}
if (argv[2].tokval == SMTPD_TOK_ERROR) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Bad address syntax");
+ smtpd_chat_reply(state, "501 Bad recipient address syntax");
return (-1);
}
if ((err = extract_addr(state, argv + 2, REJECT_EMPTY_ADDR, var_strict_rfc821_env)) != 0) {
return (-1);
}
}
+
+ /*
+ * Don't access the proxy, queue file, or queue file writer process until
+ * we have a valid recipient address.
+ */
+ if (state->proxy == 0 && state->proxy_mail) {
+ if (smtpd_proxy_open(state, var_smtpd_proxy_filt,
+ var_smtpd_proxy_tmout, var_smtpd_proxy_ehlo,
+ state->proxy_mail) != 0) {
+ smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
+ return (-1);
+ }
+ } else if (state->cleanup == 0) {
+ mail_open_stream(state, argv);
+ }
if (state->proxy && smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK,
"%s", STR(state->buffer)) != 0) {
smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
* Terminate the message envelope segment. Start the message content
* segment, and prepend our own Received: header. If there is only one
* recipient, list the recipient address.
+ *
+ * Suppress our own Received: header in the unlikely case that we are an
+ * intermediate proxy.
*/
if (state->cleanup)
rec_fputs(state->cleanup, REC_TYPE_MESG, "");
- out_fprintf(out_stream, REC_TYPE_NORM,
- "Received: from %s (%s [%s])",
- state->helo_name ? state->helo_name : state->name,
- state->name, state->addr);
- if (state->rcpt_count == 1 && state->recipient) {
- out_fprintf(out_stream, REC_TYPE_NORM,
- state->cleanup ? "\tby %s (%s) with %s id %s" :
- "\tby %s (%s) with %s",
- var_myhostname, var_mail_name,
- state->protocol, state->queue_id);
- quote_822_local(state->buffer, state->recipient);
+ if (!state->proxy || state->xclient.addr == 0) {
out_fprintf(out_stream, REC_TYPE_NORM,
+ "Received: from %s (%s [%s])",
+ state->helo_name ? state->helo_name : state->name,
+ state->name, state->addr);
+ if (state->rcpt_count == 1 && state->recipient) {
+ out_fprintf(out_stream, REC_TYPE_NORM,
+ state->cleanup ? "\tby %s (%s) with %s id %s" :
+ "\tby %s (%s) with %s",
+ var_myhostname, var_mail_name,
+ state->protocol, state->queue_id);
+ quote_822_local(state->buffer, state->recipient);
+ out_fprintf(out_stream, REC_TYPE_NORM,
"\tfor <%s>; %s", STR(state->buffer), mail_date(state->time));
- } else {
- out_fprintf(out_stream, REC_TYPE_NORM,
- state->cleanup ? "\tby %s (%s) with %s id %s;" :
- "\tby %s (%s) with %s;",
- var_myhostname, var_mail_name,
- state->protocol, state->queue_id);
- out_fprintf(out_stream, REC_TYPE_NORM,
- "\t%s", mail_date(state->time));
- }
+ } else {
+ out_fprintf(out_stream, REC_TYPE_NORM,
+ state->cleanup ? "\tby %s (%s) with %s id %s;" :
+ "\tby %s (%s) with %s;",
+ var_myhostname, var_mail_name,
+ state->protocol, state->queue_id);
+ out_fprintf(out_stream, REC_TYPE_NORM,
+ "\t%s", mail_date(state->time));
+ }
#ifdef RECEIVED_ENVELOPE_FROM
- quote_822_local(state->buffer, state->sender);
- out_fprintf(out_stream, REC_TYPE_NORM,
- "\t(envelope-from %s)", STR(state->buffer));
+ quote_822_local(state->buffer, state->sender);
+ out_fprintf(out_stream, REC_TYPE_NORM,
+ "\t(envelope-from %s)", STR(state->buffer));
#endif
+ }
smtpd_chat_reply(state, "354 End data with <CR><LF>.<CR><LF>");
/*
return (0);
}
-/* xaddr_cmd - process XADDR */
+/* xclient_cmd - process XCLIENT */
-static int xaddr_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
+static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
+ int arg_no;
+ char *raw_value;
+ char *cooked_value;
+ char *arg_val;
+ int update_namaddr = 0;
/*
- * Sanity checks.
+ * Sanity checks. The XCLIENT command does not override its own access
+ * control.
*/
- if (namadr_list_match(xaddr_clients, state->name, state->addr) == 0) {
+ if (namadr_list_match(xclient_hosts, state->name, state->addr) == 0) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "554 Error: insufficient authorization");
return (-1);
}
- /* Todo: "XADDR address" to let Postfix look up the client name. */
- if (argc != 3
- || !valid_hostaddr(argv[1].strval, DONT_GRIPE)
- || !valid_hostname(argv[2].strval, DONT_GRIPE)) {
- state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Syntax: %s address hostname", XADDR_CMD);
- return (-1);
- }
+#define STREQ(x,y) (strcasecmp((x), (y)) == 0)
+#define UPDATE_STR(s, v) { if (s) myfree(s); s = mystrdup(v); }
/*
- * Change peer information for logging and for access control. Change a
- * numerical hostname into "unknown", to make it easy to extract client
- * information from Received: headers.
+ * Iterate over all XCLIENT arguments.
*/
-#define FIX_NUMERICAL_NAME(s) \
- (valid_hostaddr((s), DONT_GRIPE) ? "unknown" : (s))
-
- smtpd_peer_reset(state);
- state->name = mystrdup(FIX_NUMERICAL_NAME(argv[2].strval));
- state->addr = mystrdup(argv[1].strval);
- state->namaddr =
- concatenate(state->name, "[", state->addr, "]", (char *) 0);
- state->peer_code = strcmp(state->name, "unknown") ? 2 : 5;
- smtpd_chat_reply(state, "250 Ok");
- return (0);
-}
+ for (arg_no = 1; arg_no < argc; arg_no++) {
+ arg_val = argv[arg_no].strval;
+
+ /*
+ * Request name: what should happen with the stored attributes.
+ * Complain about unrecognized request names. The set of requests is
+ * unlikely to change.
+ */
+ if ((raw_value = split_at(arg_val, '=')) == 0) {
+ printable(arg_val, '?');
+ if (STREQ(arg_val, "RST")) { /* blow them away */
+ smtpd_xclient_reset(state, XCLIENT_OVER_NONE);
+ } else if (STREQ(arg_val, "ACL")) { /* access control and logging */
+ smtpd_xclient_reset(state, XCLIENT_OVER_ACL | XCLIENT_OVER_LOG);
+ } else if (STREQ(arg_val, "LOG")) { /* logging only */
+ smtpd_xclient_reset(state, XCLIENT_OVER_LOG);
+ } else { /* error */
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad request: %s", arg_val);
+ return (-1);
+ }
+ }
-/* xloginfo_cmd - process XLOGINFO */
+ /*
+ * NAME=VALUE attribute. Decode the attribute 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.
+ *
+ * The client can send multiple XCLIENT attributes in a single command,
+ * or multiple XCLIENT commands with fewer attributes.
+ *
+ * Note: XCLIENT ACL overrides only specific logging and access control
+ * attributes (desirable for testing), while XCLIENT LOG overrides
+ * all logging attributes (for audit trail consistency).
+ */
+ else {
+ if (state->xclient.mode == 0) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "503 Error: send %s ACL or LOG first",
+ XCLIENT_CMD);
+ 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, '?');
+
+ /*
+ * CLIENT_NAME=hostname. Also updates the client hostname lookup
+ * status code. Treat a numerical hostname as an unavailable
+ * name.
+ */
+ if (STREQ(arg_val, "CLIENT_NAME")) {
+ if (*raw_value && !valid_hostaddr(cooked_value, DONT_GRIPE)) {
+ if (!valid_hostname(cooked_value, DONT_GRIPE)) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad hostname syntax: %s",
+ cooked_value);
+ return (-1);
+ }
+ UPDATE_STR(state->xclient.name, cooked_value);
+ state->xclient.peer_code = SMTPD_PEER_CODE_OK;
+ } else {
+ UPDATE_STR(state->xclient.name, CLIENT_NAME_UNKNOWN);
+ state->xclient.peer_code = SMTPD_PEER_CODE_PERM;
+ }
+ update_namaddr = 1;
+ }
-static int xloginfo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
-{
+ /*
+ * CLIENT_ADDR=client network address.
+ */
+ else if (STREQ(arg_val, "CLIENT_ADDR")) {
+ if (*raw_value) {
+ if (!valid_hostaddr(cooked_value, DONT_GRIPE)) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad address syntax: %s",
+ cooked_value);
+ return (-1);
+ }
+ UPDATE_STR(state->xclient.addr, cooked_value);
+ } else {
+ UPDATE_STR(state->xclient.name, CLIENT_NAME_UNKNOWN);
+ }
+ update_namaddr = 1;
+ }
- /*
- * Sanity checks.
- */
- if (namadr_list_match(xloginfo_clients, state->name, state->addr) == 0) {
- state->error_mask |= MAIL_ERROR_POLICY;
- smtpd_chat_reply(state, "554 Error: insufficient authorization");
- return (-1);
- }
- if (argc != 3
- || !valid_hostaddr(argv[1].strval, DONT_GRIPE)
- || !valid_hostname(argv[2].strval, DONT_GRIPE)) {
- state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Syntax: %s address hostname", XLOGINFO_CMD);
- return (-1);
+ /*
+ * CLIENT_CODE=status. Reset the client hostname if the hostname
+ * lookup status is not OK.
+ */
+ else if (STREQ(arg_val, "CLIENT_CODE")) {
+ if (STREQ(cooked_value, "OK")) {
+ state->xclient.peer_code = SMTPD_PEER_CODE_OK;
+ } else if (STREQ(cooked_value, "TEMP")) {
+ state->xclient.peer_code = SMTPD_PEER_CODE_TEMP;
+ UPDATE_STR(state->xclient.name, CLIENT_NAME_UNKNOWN);
+ update_namaddr = 1;
+ } else if (STREQ(cooked_value, "PERM")) {
+ state->xclient.peer_code = SMTPD_PEER_CODE_PERM;
+ UPDATE_STR(state->xclient.name, CLIENT_NAME_UNKNOWN);
+ update_namaddr = 1;
+ } else {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad hostname status: %s",
+ cooked_value);
+ return (-1);
+ }
+ }
+
+ /*
+ * HELO_NAME=hostname. An empty value means the information was
+ * not provided by the client and that we must not fall back to
+ * the non-XCLIENT value. Disallow characters that could mess up
+ * our own Received: message headers but allow [].
+ */
+ else if (STREQ(arg_val, "HELO_NAME")) {
+ if (*raw_value) {
+ if (strlen(cooked_value) > VALID_HOSTNAME_LEN) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad HELO syntax: %s",
+ cooked_value);
+ return (-1);
+ }
+ neuter(cooked_value, "<>()\\\";:@", '?');
+ UPDATE_STR(state->xclient.helo_name, cooked_value);
+ } else {
+ UPDATE_STR(state->xclient.helo_name, HELO_NAME_UNKNOWN);
+ }
+ }
+
+ /*
+ * PROTOCOL=protocol name. Disallow characters that could mess up
+ * our own Received: message headers.
+ */
+ else if (STREQ(arg_val, "PROTOCOL")) {
+ if (*raw_value) {
+ if (*cooked_value == 0 || strlen(cooked_value) > 64) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad protocol syntax: %s",
+ cooked_value);
+ return (-1);
+ }
+ neuter(cooked_value, "[]<>()\\\";:@", '?');
+ UPDATE_STR(state->xclient.protocol, cooked_value);
+ } else {
+ UPDATE_STR(state->xclient.protocol, PROTOCOL_UNKNOWN);
+ }
+ }
+
+ /*
+ * Unknown attribute name. Don't complain, and log a warning.
+ * Logging is safe because only authorized clients can issue
+ * XCLIENT commands.
+ */
+ else {
+ msg_warn("unknown %s attribute from %s: %s=%s",
+ XCLIENT_CMD, state->namaddr, arg_val, cooked_value);
+ }
+ }
}
/*
- * Change peer information for logging but not for access control. Change
- * a numerical hostname into "unknown", for consistency with XADDR.
+ * Update the combined name and address when either has changed.
*/
- myfree(state->namaddr);
- state->namaddr =
- concatenate(FIX_NUMERICAL_NAME(argv[2].strval),
- "[", argv[1].strval, "]", (char *) 0);
+#define MAYBE_OVERRIDE(state, attr) \
+ ((state)->xclient.attr ? (state)->xclient.attr : (state)->attr)
+
+ if (update_namaddr) {
+ if (state->xclient.namaddr)
+ myfree(state->xclient.namaddr);
+ state->xclient.namaddr =
+ concatenate(MAYBE_OVERRIDE(state, name), "[",
+ MAYBE_OVERRIDE(state, addr), "]",
+ (char *) 0);
+ }
smtpd_chat_reply(state, "250 Ok");
return (0);
}
"VRFY", vrfy_cmd, SMTPD_CMD_FLAG_LIMIT,
"ETRN", etrn_cmd, SMTPD_CMD_FLAG_LIMIT,
"QUIT", quit_cmd, 0,
- "XADDR", xaddr_cmd, SMTPD_CMD_FLAG_LIMIT,
- "XLOGINFO", xloginfo_cmd, SMTPD_CMD_FLAG_LIMIT,
+ "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,
break;
case 0:
+
+ /*
+ * XXX The client connection count/rate control uses the real client
+ * name/address to maintain consistency between connect and
+ * disconnect events.
+ */
if (SMTPD_STAND_ALONE(state) == 0
&& anvil_clnt
&& !namadr_list_match(hogger_list, state->name, state->addr)
break;
}
}
+ /* XXX We use the real client for connect access control. */
if (SMTPD_STAND_ALONE(state) == 0
&& var_smtpd_delay_reject == 0
&& (state->access_denied = smtpd_check_client(state)) != 0) {
smtpd_chat_reply(state, "221 Error: I can break rules, too. Goodbye.");
break;
}
+ /* XXX We use the real client for connect access control. */
if (state->access_denied && cmdp->action != quit_cmd) {
smtpd_chat_reply(state, "503 Error: access denied for %s",
state->namaddr); /* RFC 2821 Sec 3.1 */
}
break;
}
+
+ /*
+ * XXX The client connection count/rate control uses the real client
+ * name/address to maintain consistency between connect and disconnect
+ * events.
+ */
if (SMTPD_STAND_ALONE(state) == 0
&& anvil_clnt
&& !namadr_list_match(hogger_list, state->name, state->addr))
*/
smtpd_noop_cmds = string_list_init(MATCH_FLAG_NONE, var_smtpd_noop_cmds);
verp_clients = namadr_list_init(MATCH_FLAG_NONE, var_verp_clients);
- xaddr_clients = namadr_list_init(MATCH_FLAG_NONE, var_xaddr_clients);
- xloginfo_clients = namadr_list_init(MATCH_FLAG_NONE, var_xloginfo_clients);
+ xclient_hosts = namadr_list_init(MATCH_FLAG_NONE, var_xclient_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_FILT, DEF_SMTPD_PROXY_FILT, &var_smtpd_proxy_filt, 0, 0,
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_XADDR_CLIENTS, DEF_XADDR_CLIENTS, &var_xaddr_clients, 0, 0,
- VAR_XLOGINFO_CLIENTS, DEF_XLOGINFO_CLIENTS, &var_xloginfo_clients, 0, 0,
+ VAR_XCLIENT_HOSTS, DEF_XCLIENT_HOSTS, &var_xclient_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 mode; /* none, log, acl */
+ 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;
+
typedef struct SMTPD_STATE {
int err;
VSTREAM *client;
ARGV *history;
char *reason;
char *sender;
+ char *encoding;
+ char *verp_delims;
char *recipient;
char *etrn_name;
char *protocol;
VSTRING *expand_buf; /* scratch space for $name expansion */
VSTREAM *proxy; /* proxy handle */
VSTRING *proxy_buffer; /* proxy query/reply buffer */
+ char *proxy_mail; /* proxy MAIL FROM command */
+ int proxy_features; /* proxy ESMTP features */
+ SMTPD_XCLIENT_ATTR xclient; /* override access control */
} SMTPD_STATE;
extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *);
(state->client == VSTREAM_IN && getuid() != var_owner_uid)
/*
- * SMPTD peer information lookup.
+ * SMTPD peer information lookup.
*/
extern void smtpd_peer_init(SMTPD_STATE *state);
extern void smtpd_peer_reset(SMTPD_STATE *state);
+#define SMTPD_PEER_CODE_OK 2
+#define SMTPD_PEER_CODE_TEMP 4
+#define SMTPD_PEER_CODE_PERM 5
+
/*
- * Transparency: before mail is queued, do we check for unknown recipients,
- * do we allow address mapping, automatic bcc, header/body checks?
+ * XCLIENT support to override logging and/or access control attributes. It
+ * makes no sense to maintain separate attribute sets for XCLIENT LOG or
+ * XCLIENT ACL, so we set a flag to distinguish purpose.
*/
-extern int smtpd_input_transp_mask;
+#define XCLIENT_CMD "XCLIENT" /* XCLIENT command */
+#define XCLIENT_EHLO "XCLIENT" /* ESMTP advertisement */
+
+#define SMTPD_FEATURE_XCLIENT (1<<0) /* server supports XCLIENT */
+
+#define XCLIENT_OVER_NONE (0) /* override reset */
+#define XCLIENT_OVER_ACL (1<<0) /* override access control */
+#define XCLIENT_OVER_LOG (1<<1) /* override logging */
+
+#define XCLIENT_OVER(s, m, a) \
+ (((s)->xclient.mode & (m)) && (s)->xclient.a ? (s)->xclient.a : (s)->a)
+
+#define ACL_ADDR(s) XCLIENT_OVER((s), XCLIENT_OVER_ACL, addr)
+#define ACL_NAME(s) XCLIENT_OVER((s), XCLIENT_OVER_ACL, name)
+#define ACL_NAMADDR(s) XCLIENT_OVER((s), XCLIENT_OVER_ACL, namaddr)
+#define ACL_PEER_CODE(s) XCLIENT_OVER((s), XCLIENT_OVER_ACL, peer_code)
+#define ACL_PROTOCOL(s) XCLIENT_OVER((s), XCLIENT_OVER_ACL, protocol)
+#define ACL_HELO_NAME(s) XCLIENT_OVER((s), XCLIENT_OVER_ACL, helo_name)
+
+#define LOG_ADDR(s) XCLIENT_OVER((s), XCLIENT_OVER_LOG, addr)
+#define LOG_NAME(s) XCLIENT_OVER((s), XCLIENT_OVER_LOG, name)
+#define LOG_NAMADDR(s) XCLIENT_OVER((s), XCLIENT_OVER_LOG, namaddr)
+#define LOG_PEER_CODE(s) XCLIENT_OVER((s), XCLIENT_OVER_LOG, peer_code)
+#define LOG_PROTOCOL(s) XCLIENT_OVER((s), XCLIENT_OVER_LOG, protocol)
+#define LOG_HELO_NAME(s) XCLIENT_OVER((s), XCLIENT_OVER_LOG, helo_name)
+
+extern void smtpd_xclient_init(SMTPD_STATE *state);
+extern void smtpd_xclient_reset(SMTPD_STATE *state, int);
/*
- * XLOGINFO command.
+ * Transparency: before mail is queued, do we check for unknown recipients,
+ * do we allow address mapping, automatic bcc, header/body checks?
*/
-#define XLOGINFO_CMD "XLOGINFO"
+extern int smtpd_input_transp_mask;
/* LICENSE
/* .ad
vstring_sprintf(buf, "%s: %s: %s from %s: %s;",
state->queue_id ? state->queue_id : "NOQUEUE",
- whatsup, state->where, state->namaddr, text);
+ whatsup, state->where, LOG_NAMADDR(state), text);
if (state->sender)
vstring_sprintf_append(buf, " from=<%s>", state->sender);
if (state->recipient)
vstring_sprintf_append(buf, " to=<%s>", state->recipient);
- if (state->protocol)
- vstring_sprintf_append(buf, " proto=%s", state->protocol);
- if (state->helo_name)
- vstring_sprintf_append(buf, " helo=<%s>", state->helo_name);
+ if (!IS_UNK_PROTOCOL(ACL_PROTOCOL(state)))
+ vstring_sprintf_append(buf, " proto=%s", ACL_PROTOCOL(state));
+ if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state)))
+ vstring_sprintf_append(buf, " helo=<%s>", ACL_HELO_NAME(state));
msg_info("%s", STR(buf));
vstring_free(buf);
}
char *myname = "reject_unknown_client";
if (msg_verbose)
- msg_info("%s: %s %s", myname, state->name, state->addr);
+ msg_info("%s: %s %s", myname, ACL_NAME(state), ACL_ADDR(state));
- if (strcasecmp(state->name, "unknown") == 0)
+ if (IS_UNK_CLNT_NAME(ACL_NAME(state)))
return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
"%d Client host rejected: cannot find your hostname, [%s]",
- state->peer_code == 5 ?
+ ACL_PEER_CODE(state) == SMTPD_PEER_CODE_PERM ?
var_unk_client_code : 450,
- state->addr));
+ ACL_ADDR(state)));
return (SMTPD_CHECK_DUNNO);
}
char *myname = "permit_mynetworks";
if (msg_verbose)
- msg_info("%s: %s %s", myname, state->name, state->addr);
+ msg_info("%s: %s %s", myname, ACL_NAME(state), ACL_ADDR(state));
- if (namadr_list_match(mynetworks, state->name, state->addr))
+ if (namadr_list_match(mynetworks, ACL_NAME(state), ACL_ADDR(state)))
return (SMTPD_CHECK_OK);
return (SMTPD_CHECK_DUNNO);
}
/*
* Permit if the client matches the relay_domains list.
*/
- if (domain_list_match(relay_domains, state->name))
+ if (domain_list_match(relay_domains, ACL_NAME(state)))
return (SMTPD_CHECK_OK);
/*
if (state->client != 0
&& SMTPD_STAND_ALONE(state) == 0
&& vstream_peek(state->client) > 0
- && (strcasecmp(state->protocol, "ESMTP") != 0
+ && (strcasecmp(ACL_PROTOCOL(state), MAIL_PROTO_ESMTP) != 0
|| strcasecmp(state->where, "DATA") == 0)) {
return (smtpd_check_reject(state, MAIL_ERROR_PROTOCOL,
"503 <%s>: %s rejected: Improper use of SMTP command pipelining",
* Return NULL only for non-existent names.
*/
if (STREQ(name, MAIL_ATTR_CLIENT)) {
- return (state->namaddr);
+ return (ACL_NAMADDR(state));
} else if (STREQ(name, MAIL_ATTR_CLIENT_ADDR)) {
- return (state->addr);
+ return (ACL_ADDR(state));
} else if (STREQ(name, MAIL_ATTR_CLIENT_NAME)) {
- return (state->name);
+ return (ACL_NAME(state));
} else if (STREQ(name, MAIL_ATTR_HELO_NAME)) {
- return (state->helo_name ? state->helo_name : "");
+ return (IS_UNK_HELO_NAME(ACL_HELO_NAME(state)) ?
+ "" : ACL_HELO_NAME(state));
} else if (STREQN(name, MAIL_ATTR_SENDER, CONST_LEN(MAIL_ATTR_SENDER))) {
return (smtpd_expand_addr(state->expand_buf, state->sender,
name, CONST_LEN(MAIL_ATTR_SENDER)));
static int warned;
if (msg_verbose)
- msg_info("%s: %s", myname, state->addr);
+ msg_info("%s: %s", myname, ACL_ADDR(state));
if (warned == 0) {
warned++;
REJECT_MAPS_RBL, var_mail_name, REJECT_RBL_CLIENT);
}
while ((rbl_domain = mystrtok(&bp, " \t\r\n,")) != 0) {
- result = reject_rbl_addr(state, rbl_domain, state->addr,
+ result = reject_rbl_addr(state, rbl_domain, ACL_ADDR(state),
SMTPD_NAME_CLIENT);
if (result != SMTPD_CHECK_DUNNO)
break;
ATTR_FLAG_NONE, /* Query attributes. */
ATTR_TYPE_STR, MAIL_ATTR_REQ, "smtpd_access_policy",
ATTR_TYPE_STR, MAIL_ATTR_PROTO_STATE, state->where,
- ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, state->protocol,
- ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, state->addr,
- ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, state->name,
+ ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, ACL_PROTOCOL(state),
+ ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, ACL_ADDR(state),
+ ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, ACL_NAME(state),
ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME,
- state->helo_name ? state->helo_name : "",
+ IS_UNK_HELO_NAME(ACL_HELO_NAME(state)) ?
+ "" : ACL_HELO_NAME(state),
ATTR_TYPE_STR, MAIL_ATTR_SENDER,
state->sender ? state->sender : "",
ATTR_TYPE_STR, MAIL_ATTR_RECIP,
} else if (strcasecmp(name, PERMIT_MYNETWORKS) == 0) {
status = permit_mynetworks(state);
} else if (is_map_command(state, name, CHECK_CLIENT_ACL, &cpp)) {
- status = check_namadr_access(state, *cpp, state->name, state->addr,
- FULL, &found, state->namaddr,
+ status = check_namadr_access(state, *cpp, ACL_NAME(state), ACL_ADDR(state),
+ FULL, &found, ACL_NAMADDR(state),
SMTPD_NAME_CLIENT, def_acl);
} else if (strcasecmp(name, REJECT_MAPS_RBL) == 0) {
status = reject_maps_rbl(state);
if (cpp[1] == 0)
msg_warn("restriction %s requires domain name argument", name);
else
- status = reject_rbl_addr(state, *(cpp += 1), state->addr,
+ status = reject_rbl_addr(state, *(cpp += 1), ACL_ADDR(state),
SMTPD_NAME_CLIENT);
} else if (strcasecmp(name, REJECT_RHSBL_CLIENT) == 0) {
if (cpp[1] == 0)
name);
else {
cpp += 1;
- if (strcasecmp(state->name, "unknown") != 0)
- status = reject_rbl_domain(state, *cpp, state->name,
+ if (!IS_UNK_CLNT_NAME(ACL_NAME(state)))
+ status = reject_rbl_domain(state, *cpp, ACL_NAME(state),
SMTPD_NAME_CLIENT);
}
}
/*
* HELO/EHLO parameter restrictions.
+ *
+ * XXX With XCLIENT overrides, a zero-length name means the client did
+ * not send a HELO/EHLO command. Do not fall back to the non-XCLIENT
+ * HELO/EHLO value.
*/
else if (is_map_command(state, name, CHECK_HELO_ACL, &cpp)) {
- if (state->helo_name)
- status = check_domain_access(state, *cpp, state->helo_name,
- FULL, &found, state->helo_name,
+ if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state)))
+ status = check_domain_access(state, *cpp, ACL_HELO_NAME(state),
+ FULL, &found, ACL_HELO_NAME(state),
SMTPD_NAME_HELO, def_acl);
} else if (strcasecmp(name, REJECT_INVALID_HOSTNAME) == 0) {
- if (state->helo_name) {
- if (*state->helo_name != '[')
- status = reject_invalid_hostname(state, state->helo_name,
- state->helo_name, SMTPD_NAME_HELO);
+ if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
+ if (*ACL_HELO_NAME(state) != '[')
+ status = reject_invalid_hostname(state, ACL_HELO_NAME(state),
+ ACL_HELO_NAME(state), SMTPD_NAME_HELO);
else
- status = reject_invalid_hostaddr(state, state->helo_name,
- state->helo_name, SMTPD_NAME_HELO);
+ status = reject_invalid_hostaddr(state, ACL_HELO_NAME(state),
+ ACL_HELO_NAME(state), SMTPD_NAME_HELO);
}
} else if (strcasecmp(name, REJECT_UNKNOWN_HOSTNAME) == 0) {
- if (state->helo_name) {
- if (*state->helo_name != '[')
- status = reject_unknown_hostname(state, state->helo_name,
- state->helo_name, SMTPD_NAME_HELO);
+ if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
+ if (*ACL_HELO_NAME(state) != '[')
+ status = reject_unknown_hostname(state, ACL_HELO_NAME(state),
+ ACL_HELO_NAME(state), SMTPD_NAME_HELO);
else
- status = reject_invalid_hostaddr(state, state->helo_name,
- state->helo_name, SMTPD_NAME_HELO);
+ status = reject_invalid_hostaddr(state, ACL_HELO_NAME(state),
+ ACL_HELO_NAME(state), SMTPD_NAME_HELO);
}
} else if (strcasecmp(name, PERMIT_NAKED_IP_ADDR) == 0) {
msg_warn("restriction %s is deprecated. Use %s instead",
PERMIT_NAKED_IP_ADDR, PERMIT_MYNETWORKS);
- if (state->helo_name) {
- if (state->helo_name[strspn(state->helo_name, "0123456789.")] == 0
- && (status = reject_invalid_hostaddr(state, state->helo_name,
- state->helo_name, SMTPD_NAME_HELO)) == 0)
+ if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
+ if (ACL_HELO_NAME(state)[strspn(ACL_HELO_NAME(state), "0123456789.")] == 0
+ && (status = reject_invalid_hostaddr(state, ACL_HELO_NAME(state),
+ ACL_HELO_NAME(state), SMTPD_NAME_HELO)) == 0)
status = SMTPD_CHECK_OK;
}
} else if (is_map_command(state, name, CHECK_HELO_NS_ACL, &cpp)) {
- if (state->helo_name) {
- status = check_server_access(state, *cpp, state->helo_name,
- T_NS, state->helo_name,
+ if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
+ status = check_server_access(state, *cpp, ACL_HELO_NAME(state),
+ T_NS, ACL_HELO_NAME(state),
SMTPD_NAME_HELO, def_acl);
- forbid_whitelist(state, name, status, state->helo_name);
+ forbid_whitelist(state, name, status, ACL_HELO_NAME(state));
}
} else if (is_map_command(state, name, CHECK_HELO_MX_ACL, &cpp)) {
- if (state->helo_name) {
- status = check_server_access(state, *cpp, state->helo_name,
- T_MX, state->helo_name,
+ if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
+ status = check_server_access(state, *cpp, ACL_HELO_NAME(state),
+ T_MX, ACL_HELO_NAME(state),
SMTPD_NAME_HELO, def_acl);
- forbid_whitelist(state, name, status, state->helo_name);
+ forbid_whitelist(state, name, status, ACL_HELO_NAME(state));
}
} else if (strcasecmp(name, REJECT_NON_FQDN_HOSTNAME) == 0) {
- if (state->helo_name) {
- if (*state->helo_name != '[')
- status = reject_non_fqdn_hostname(state, state->helo_name,
- state->helo_name, SMTPD_NAME_HELO);
+ if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state))) {
+ if (*ACL_HELO_NAME(state) != '[')
+ status = reject_non_fqdn_hostname(state, ACL_HELO_NAME(state),
+ ACL_HELO_NAME(state), SMTPD_NAME_HELO);
else
- status = reject_invalid_hostaddr(state, state->helo_name,
- state->helo_name, SMTPD_NAME_HELO);
+ status = reject_invalid_hostaddr(state, ACL_HELO_NAME(state),
+ ACL_HELO_NAME(state), SMTPD_NAME_HELO);
}
} else if (strcasecmp(name, REJECT_RHSBL_HELO) == 0) {
if (cpp[1] == 0)
name);
else {
cpp += 1;
- if (state->helo_name)
- status = reject_rbl_domain(state, *cpp, state->helo_name,
+ if (!IS_UNK_HELO_NAME(ACL_HELO_NAME(state)))
+ status = reject_rbl_domain(state, *cpp, ACL_HELO_NAME(state),
SMTPD_NAME_HELO);
}
}
/*
* Initialize.
*/
- if (state->name == 0 || state->addr == 0)
+ if (ACL_NAME(state) == 0 || ACL_ADDR(state) == 0)
return (0);
#define SMTPD_CHECK_RESET() { \
SMTPD_CHECK_RESET();
status = setjmp(smtpd_check_buf);
if (status == 0 && client_restrctions->argc)
- status = generic_checks(state, client_restrctions, state->namaddr,
+ status = generic_checks(state, client_restrctions, ACL_NAMADDR(state),
SMTPD_NAME_CLIENT, CHECK_CLIENT_ACL);
state->defer_if_permit_client = state->defer_if_permit.active;
SMTPD_CHECK_RESET();
status = setjmp(smtpd_check_buf);
if (status == 0 && helo_restrctions->argc)
- status = generic_checks(state, helo_restrctions, state->helo_name,
+ status = generic_checks(state, helo_restrctions, ACL_HELO_NAME(state),
SMTPD_NAME_HELO, CHECK_HELO_ACL);
state->defer_if_permit_helo = state->defer_if_permit.active;
*/
if (var_smtpd_delay_reject)
if ((err = smtpd_check_client(state)) != 0
- || (err = smtpd_check_helo(state, state->helo_name)) != 0
+ || (err = smtpd_check_helo(state, ACL_HELO_NAME(state))) != 0
|| (err = smtpd_check_mail(state, state->sender)) != 0)
SMTPD_CHECK_RCPT_RETURN(err);
*/
if (var_smtpd_delay_reject)
if ((err = smtpd_check_client(state)) != 0
- || (err = smtpd_check_helo(state, state->helo_name)) != 0)
+ || (err = smtpd_check_helo(state, ACL_HELO_NAME(state))) != 0)
SMTPD_CHECK_ETRN_RETURN(err);
/*
if (args->argc == 4)
state.peer_code = atoi(args->argv[3]);
else
- state.peer_code = 2;
+ state.peer_code = SMTPD_PEER_CODE_OK;
if (state.namaddr)
myfree(state.namaddr);
state.namaddr = concatenate(state.name, "[", state.addr,
/* Global library. */
+#include <mail_proto.h>
/* Application-specific. */
* If peer went away, give up.
*/
if (errno == ECONNRESET || errno == ECONNABORTED) {
- state->name = mystrdup("unknown");
- state->addr = mystrdup("unknown");
- state->peer_code = 5;
+ state->name = mystrdup(CLIENT_NAME_UNKNOWN);
+ state->addr = mystrdup(CLIENT_ADDR_UNKNOWN);
+ state->peer_code = SMTPD_PEER_CODE_PERM;
}
/*
hp = gethostbyaddr((char *) &(sin.sin_addr),
sizeof(sin.sin_addr), AF_INET);
if (hp == 0) {
- state->name = mystrdup("unknown");
- state->peer_code = (h_errno == TRY_AGAIN ? 4 : 5);
+ state->name = mystrdup(CLIENT_NAME_UNKNOWN);
+ state->peer_code = (h_errno == TRY_AGAIN ?
+ SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM);
} else if (valid_hostaddr(hp->h_name, DONT_GRIPE)) {
msg_warn("numeric result %s in address->name lookup for %s",
hp->h_name, state->addr);
- state->name = mystrdup("unknown");
- state->peer_code = 5;
+ state->name = mystrdup(CLIENT_NAME_UNKNOWN);
+ state->peer_code = SMTPD_PEER_CODE_PERM;
} else if (!valid_hostname(hp->h_name, DONT_GRIPE)) {
- state->name = mystrdup("unknown");
- state->peer_code = 5;
+ state->name = mystrdup(CLIENT_NAME_UNKNOWN);
+ state->peer_code = SMTPD_PEER_CODE_PERM;
} else {
state->name = mystrdup(hp->h_name); /* hp->name is clobbered!! */
- state->peer_code = 2;
+ state->peer_code = SMTPD_PEER_CODE_OK;
/*
* Reject the hostname if it does not list the peer address.
*/
#define REJECT_PEER_NAME(state, code) { \
myfree(state->name); \
- state->name = mystrdup("unknown"); \
+ state->name = mystrdup(CLIENT_NAME_UNKNOWN); \
state->peer_code = code; \
}
if (hp == 0) {
msg_warn("%s: hostname %s verification failed: %s",
state->addr, state->name, HSTRERROR(h_errno));
- REJECT_PEER_NAME(state, (h_errno == TRY_AGAIN ? 4 : 5));
+ REJECT_PEER_NAME(state, (h_errno == TRY_AGAIN ?
+ SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM));
} else if (hp->h_length != sizeof(sin.sin_addr)) {
msg_warn("%s: hostname %s verification failed: bad address size %d",
state->addr, state->name, hp->h_length);
- REJECT_PEER_NAME(state, 5);
+ REJECT_PEER_NAME(state, SMTPD_PEER_CODE_PERM);
} else {
for (i = 0; /* void */ ; i++) {
if (hp->h_addr_list[i] == 0) {
msg_warn("%s: address not listed for hostname %s",
state->addr, state->name);
- REJECT_PEER_NAME(state, 5);
+ REJECT_PEER_NAME(state, SMTPD_PEER_CODE_PERM);
break;
}
if (memcmp(hp->h_addr_list[i],
else {
state->name = mystrdup("localhost");
state->addr = mystrdup("127.0.0.1"); /* XXX bogus. */
- state->peer_code = 2;
+ state->peer_code = SMTPD_PEER_CODE_OK;
}
/*
/* have the same interface as the routines that are used for non-proxied
/* mail.
/*
-/* smtpd_proxy_open() should be called after receiving the MAIL FROM
-/* command. It connects to the proxy service, sends EHLO, sends the
-/* MAIL FROM command, and receives the reply. A non-zero result means
+/* smtpd_proxy_open() connects to the proxy service, sends EHLO, sends
+/* client information with the XCLIENT 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.
-/* All results are reported via the state->proxy_buffer field in a form
+/* The result is reported via the state->proxy_buffer field in a form
/* that can be sent to the SMTP client. In case of error, the
/* state->error_mask and state->err fields are updated.
/* A state->proxy_buffer field is created automatically; this field
#include <cleanup_user.h>
#include <mail_params.h>
#include <rec_type.h>
+#include <xtext.h>
+#include <mail_proto.h>
/* Application-specific. */
#define LEN(x) VSTRING_LEN(x)
#define SMTPD_PROXY_CONNECT ((char *) 0)
-/* smtpd_proxy_open - open proxy connection after MAIL FROM */
+/* smtpd_proxy_open - open proxy connection */
int smtpd_proxy_open(SMTPD_STATE *state, const char *service,
int timeout, const char *ehlo_name,
int fd;
char *lines;
char *line;
+ VSTRING *buf;
+ int bad;
/*
* This buffer persists beyond the end of a proxy session so we can
*
* If this fails then we have a problem because the proxy should always
* accept our connection. Make up our own response instead of passing
- * back the greeting banner: the client expects a MAIL FROM reply.
+ * back the greeting banner: the proxy open might be delayed to the point
+ * that the client expects a MAIL FROM or RCPT TO reply.
*/
if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, SMTPD_PROXY_CONNECT) != 0) {
vstring_sprintf(state->proxy_buffer,
/*
* Send our own EHLO command. If this fails then we have a problem
* because the proxy should always accept our EHLO command. Make up our
- * own response instead of passing back the EHLO reply: the client
- * expects a MAIL FROM reply.
+ * own response instead of passing back the EHLO reply: the proxy open
+ * might be delayed to the point that the client expects a MAIL FROM or
+ * RCPT TO reply.
*/
if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "EHLO %s", ehlo_name) != 0) {
vstring_sprintf(state->proxy_buffer,
}
/*
- * Parse the EHLO reply and see if we can forward the client hostname and
- * address info for logging purposes. If the command fails, then proceed.
- * It is not the end of the world.
+ * Parse the EHLO reply and see if we can forward logging information.
*/
+ state->proxy_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, XLOGINFO_CMD) == 0)
- (void) smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, "%s %s %s",
- XLOGINFO_CMD, state->addr, state->name);
+ && strcmp(line + 4, XCLIENT_EHLO) == 0)
+ state->proxy_features |= SMTPD_FEATURE_XCLIENT;
+
+ /*
+ * Send our XCLIENT attributes. Transform internal forms to external
+ * forms and encode the result as xtext.
+ */
+ if (state->proxy_features & SMTPD_FEATURE_XCLIENT) {
+ buf = vstring_alloc(100);
+ vstring_sprintf(buf, "%s LOG CLIENT_NAME=", XCLIENT_CMD);
+ if (!IS_UNK_CLNT_NAME(LOG_NAME(state)))
+ xtext_quote_append(buf, LOG_NAME(state), "");
+ vstring_strcat(buf, " CLIENT_ADDR=");
+ if (!IS_UNK_CLNT_ADDR(LOG_ADDR(state)))
+ xtext_quote_append(buf, LOG_ADDR(state), "");
+ bad = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, "%s", STR(buf));
+ if (bad == 0) {
+ vstring_sprintf(buf, "%s HELO_NAME=", XCLIENT_CMD);
+ if (!IS_UNK_HELO_NAME(LOG_HELO_NAME(state)))
+ xtext_quote_append(buf, LOG_HELO_NAME(state), "");
+ vstring_strcat(buf, " PROTOCOL=");
+ if (!IS_UNK_PROTOCOL(LOG_PROTOCOL(state)))
+ xtext_quote_append(buf, LOG_PROTOCOL(state), "");
+ bad = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, "%s", STR(buf));
+ }
+ vstring_free(buf);
+ if (bad) {
+ vstring_sprintf(state->proxy_buffer,
+ "451 Error: queue file write error");
+ smtpd_proxy_close(state);
+ return (-1);
+ }
+ }
/*
* Pass-through the client's MAIL FROM command. If this fails, then we
#define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3))
msg_info("%s: client=%s%s%s%s%s%s%s",
- state->queue_id, state->namaddr,
+ state->queue_id, LOG_NAMADDR(state),
IFELSE(state->sasl_method, ", sasl_method=", ""),
IFELSE(state->sasl_method, state->sasl_method, ""),
IFELSE(state->sasl_username, ", sasl_username=", ""),
state->history = 0;
state->reason = 0;
state->sender = 0;
+ state->verp_delims = 0;
state->recipient = 0;
state->etrn_name = 0;
state->protocol = MAIL_PROTO_SMTP;
state->expand_buf = 0;
state->proxy = 0;
state->proxy_buffer = 0;
+ state->proxy_mail = 0;
+ state->proxy_features = 0;
#ifdef USE_SASL_AUTH
if (SMTPD_STAND_ALONE(state))
*/
smtpd_peer_init(state);
+ /*
+ * Initialize xclient information.
+ */
+ smtpd_xclient_init(state);
+
/*
* Initialize the conversation history.
*/
if (state->buffer)
vstring_free(state->buffer);
smtpd_peer_reset(state);
+ smtpd_xclient_reset(state, XCLIENT_OVER_NONE);
if (state->defer_if_permit.reason)
vstring_free(state->defer_if_permit.reason);
if (state->defer_if_reject.reason)
--- /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, mode)
+/* SMTPD_STATE *state;
+/* int mode;
+/* DESCRIPTION
+/* smtpd_xclient_init() initializes state variables that are
+/* used for storage of XCLIENT command parameters.
+/* These variables override specific members of the global state
+/* structure for access control or logging purposes.
+/*
+/* smtpd_xclient_reset() releases memory allocated after the return
+/* from smtpd_xclient_init() and optionally presets the state variables
+/* to defaults that are suitable for the specified mode:
+/* .IP XCLIENT_OVER_NONE
+/* This should be used after the XCLIENT RST request.
+/* .IP XCLIENT_OVER_ACL|XCLIENT_OVER_LOG
+/* This should be used after the XCLIENT ACL request.
+/* .IP XCLIENT_OVER_LOG
+/* This should be used after the XCLIENT LOG request.
+/* 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.mode = 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_reset - reset XCLIENT attributes */
+
+void smtpd_xclient_reset(SMTPD_STATE *state, int mode)
+{
+ switch (mode) {
+
+ /*
+ * Can't happen.
+ */
+ default:
+ msg_panic("smtpd_xclient_reset: unknown mode 0x%x", mode);
+
+ /*
+ * Restore smtpd_xclient_init() result. Allow selective override.
+ * This is desirable for access rule testing.
+ */
+#define FREE_AND_WIPE(s) { if (s) { myfree(s); s = 0; } }
+
+ case XCLIENT_OVER_NONE:
+ case XCLIENT_OVER_ACL | XCLIENT_OVER_LOG:
+ 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);
+ break;
+
+ /*
+ * Non-selective override. Set all attributes to "unknown". This is
+ * desirable to avoid polluting the audit trail with data from mixed
+ * origins.
+ */
+#define FREE_AND_DUP(s,v) { if (s) { myfree(s); s = mystrdup(v); } }
+
+ case XCLIENT_OVER_LOG:
+ FREE_AND_DUP(state->xclient.name, CLIENT_NAME_UNKNOWN);
+ FREE_AND_DUP(state->xclient.addr, CLIENT_ADDR_UNKNOWN);
+ FREE_AND_DUP(state->xclient.namaddr, CLIENT_NAMADDR_UNKNOWN);
+ state->xclient.peer_code = 0;
+ FREE_AND_DUP(state->xclient.protocol, PROTOCOL_UNKNOWN);
+ FREE_AND_DUP(state->xclient.helo_name, HELO_NAME_UNKNOWN);
+ break;
+ }
+ state->xclient.mode = mode;
+}
OBJS = smtp-source.o smtp-sink.o qmqp-source.o qmqp-sink.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = spawn.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = trivial-rewrite.o rewrite.o resolve.o transport.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
LIB =
username.c valid_hostname.c vbuf.c vbuf_print.c vstream.c \
vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \
write_buf.c write_wait.c auto_clnt.c attr_clnt.c attr_scan_plain.c \
- attr_print_plain.c sane_connect.c
+ attr_print_plain.c sane_connect.c neuter.c
OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \
attr_scan0.o attr_scan64.o base64_code.o basename.o binhash.o \
chroot_uid.o clean_env.o close_on_exec.o concatenate.o ctable.o \
username.o valid_hostname.o vbuf.o vbuf_print.o vstream.o \
vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \
write_buf.o write_wait.o auto_clnt.o attr_clnt.o attr_scan_plain.o \
- attr_print_plain.o sane_connect.o $(STRCASE)
+ attr_print_plain.o sane_connect.o $(STRCASE) neuter.o
HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \
connect.h ctable.h dict.h dict_db.h dict_dbm.h dict_env.h \
dict_cidr.h dict_ht.h dict_ldap.h dict_mysql.h dict_ni.h dict_nis.h \
auto_clnt.h attr_clnt.h sane_connect.h
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
stream_test.c dup2_pass_on_exec.c
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
FILES = Makefile $(SRCS) $(HDRS)
netstring.o: vbuf.h
netstring.o: vstring.h
netstring.o: netstring.h
+neuter.o: neuter.c
+neuter.o: sys_defs.h
+neuter.o: stringops.h
+neuter.o: vstring.h
+neuter.o: vbuf.h
non_blocking.o: non_blocking.c
non_blocking.o: sys_defs.h
non_blocking.o: msg.h
--- /dev/null
+/*++
+/* NAME
+/* neuter 3
+/* SUMMARY
+/* mask non-neuter characters
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* char *neuter(buffer, bad, replacement)
+/* char *buffer;
+/* const char *bad;
+/* int replacement;
+/* DESCRIPTION
+/* neuter() replaces bad characters in its input
+/* by the given replacement.
+/*
+/* Arguments:
+/* .IP buffer
+/* The null-terminated input string.
+/* .IP bad
+/* The null-terminated bad character string.
+/* .IP replacement
+/* Replacement value for characters in \fIbuffer\fR that do not
+/* pass the bad character test.
+/* 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 <string.h>
+
+/* Utility library. */
+
+#include <stringops.h>
+
+/* neuter - neutralize bad characters */
+
+char *neuter(char *string, const char *bad, int replacement)
+{
+ char *cp;
+ int ch;
+
+ for (cp = string; (ch = *(unsigned char *) cp) != 0; cp++)
+ if (strchr(bad, ch) != 0)
+ *cp = replacement;
+ return (string);
+}
* External interface.
*/
extern char *printable(char *, int);
+extern char *neuter(char *, const char *, int);
extern char *lowercase(char *);
extern char *skipblanks(const char *);
extern char *trimblanks(char *, int);
#define NATIVE_DAEMON_DIR "/usr/libexec/postfix"
#endif
-#if defined(FREEBSD2) || defined(FREEBSD3) || defined(FREEBSD4)
+/* __FreeBSD_version version is major+minor */
+
+#if __FreeBSD_version >= 200000
#define HAS_DUPLEX_PIPE
+#define HAS_ISSETUGID
#endif
-#if defined(OPENBSD2) || defined(OPENBSD3) \
- || defined(FREEBSD3) || defined(FREEBSD4)
+#if __FreeBSD_version >= 400000
+#define SOCKADDR_SIZE socklen_t
+#define SOCKOPT_SIZE socklen_t
+#endif
+
+/* OpenBSD version is year+month */
+
+#if OpenBSD >= 200000 /* XXX */
#define HAS_ISSETUGID
#endif
-#if defined(NETBSD1)
+#if OpenBSD >= 200200 /* XXX */
+#define SOCKADDR_SIZE socklen_t
+#define SOCKOPT_SIZE socklen_t
+#endif
+
+/* __NetBSD_Version__ is major+minor */
+
+#if __NetBSD_Version__ >= 103000000 /* XXX */
#undef DEF_MAILBOX_LOCK
#define DEF_MAILBOX_LOCK "flock, dotlock"
+#endif
+
+#if __NetBSD_Version__ >= 106000000 /* XXX */
+#define SOCKADDR_SIZE socklen_t
+#define SOCKOPT_SIZE socklen_t
#endif
/*
#ifdef LINUX2
#define SUPPORTED
#include <sys/types.h>
+#include <features.h>
#define USE_PATHS_H
#define HAS_FLOCK_LOCK
#define HAS_FCNTL_LOCK
#define NATIVE_NEWALIAS_PATH "/usr/bin/newaliases"
#define NATIVE_COMMAND_DIR "/usr/sbin"
#define NATIVE_DAEMON_DIR "/usr/libexec/postfix"
+#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+#define SOCKADDR_SIZE socklen_t
+#define SOCKOPT_SIZE socklen_t
+#endif
#endif
#ifdef LINUX1
#endif
#define OPTIND (optind > 0 ? optind : 1)
+ /*
+ * Check for required but missing definitions.
+ */
#if !defined(HAS_FCNTL_LOCK) && !defined(HAS_FLOCK_LOCK)
#error "define HAS_FCNTL_LOCK and/or HAS_FLOCK_LOCK"
#endif
#endif
/*
- * Defaults for normal systems.
+ * Defaults for systems that pre-date POSIX socklen_t.
*/
#ifndef SOCKADDR_SIZE
#define SOCKADDR_SIZE int
#define SOCKOPT_SIZE int
#endif
+ /*
+ * Defaults for normal systems.
+ */
#ifndef LOCAL_LISTEN
#define LOCAL_LISTEN unix_listen
#define LOCAL_ACCEPT unix_accept
OBJS = verify.o
HDRS =
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG=
OBJS = virtual.o mailbox.o recipient.o deliver_attr.o maildir.o unknown.o
HDRS = virtual.h
TESTSRC =
-WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
- -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
- -Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
PROG = virtual