because the deliver_pass.c module was not updated for the
changed message delivery protocol.
+20031211
+
+ Safety: in dynamically growing data structures, update the
+ length info after (instead of before) updating the data
+ size. Files: util/argv.c, util/inet_addrlist.c, util/intv.c,
+ util/mvect.c, util/vstring.c, global/recipient_list.c,
+ *qmgr/qmgr_rcpt_list.c.
+
+20031212
+
+ Cleanup: separate extensions XCLIENT (impersonate SMTP
+ client) and XFORWARD (down-stream logging of up-stream
+ session information, not necessarily SMTP related). The
+ protocol is extensible: the server advertises what attributes
+ XCLIENT or XFORWARD will accept. The requirement for xtext
+ encoding is dropped as no attribute currently needs it.
+ All errors are reported by the server and are logged by
+ the client. See also: XCLIENT_README and XFORWARD_README.
+
+20031214
+
+ Feature: XCLIENT and XFORWARD support in the LMTP client.
+
Open problems:
High: when virtual aliasing is turned off after content
Purpose of the XCLIENT extension to SMTP
========================================
-The XCLIENT command targets problems in the following areas:
+The XCLIENT command targets the following problems:
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 authorized SMTP
client test program needs the ability to override the SMTP server's
-idea of the SMTP client hostname, network address, and other
+idea of the SMTP client hostname, network address, and other client
information, for the entire duration of an SMTP session.
-2 - Logging after SMTP-based content filter. With the deployment
-of Internet->MTA1->filter->MTA2 style content filter applications,
-remote client information is lost when MTA1 gives the mail to the
-content filter. To simplify the interpretation of MTA2 logging,
-it would help if MTA1 could forward client information through the
-content filter to MTA2.
+2 - Client software that downloads mail from an up-stream mail
+server and injects it into a local MTA via SMTP. In order to take
+advantage of the local MTA's access rules, the client software
+needs the ability to override the SMTP server's idea of the remote
+client name, client address and other information. Such information
+can typically be extracted from the up-stream mail server's Received:
+message header.
3 - Post-filter access control and logging. With Internet->filter->MTA
style content filter applications, the filter can be simplified if
it can delegate decisions concerning mail relay and other access
-control to the MTA. As in the first example, this requires that
-the filter can override the MTA's idea of the SMTP client hostname,
-network address, and other information, for the entire duration of
-an SMTP session.
+control to the MTA. This is especially useful when the filter acts
+as a transparent proxy for SMTP commands. As in the first example,
+this requires that the filter can override the MTA's idea of the
+SMTP client hostname, network address, and other information.
-4 - Fetchmail.
+Command syntax
+==============
-Command overview
-================
-
-The EHLO keyword associated with this extension is XCLIENT. In EHLO
-replies, XCLIENT is followed by the names of the supported XCLIENT
-functions.
-
-The XCLIENT OVERRIDE function updates remote client attributes that
-the MTA normally uses for access control, message headers, logging
-and so on, for the duration of an entire SMTP session.
-
-The XCLIENT FORWARD function updates temporary remote client
-attributes that the MTA uses for transaction logging. These attributes
-are valid for only one message delivery attempt. In the absence
-of forwarded attributes the MTA must use the normal remote client
-attribute values.
+In SMTP server EHLO replies, the keyword associated with this
+extension is XCLIENT. It is followed by the names of the attributes
+that the XCLIENT implementation supports.
The general command syntax is described below. Upper case and
quoted strings specify terminals, lowercase strings specify meta
-terminals, SP is whitespace, and descriptive text is enclosed in
-{}. Although command and attribute names are shown below in upper
-case, they are in fact case insensitive.
+terminals, and SP is whitespace. Although command and attribute
+names are shown in upper case, they are in fact case insensitive.
- xclient-command = XCLIENT SP function SP 1*( attribute )
-
- function = ( OVERRIDE | FORWARD )
+ xclient-command = XCLIENT 1*( SP attribute )
attribute = name"="value
- name = ( NAME|ADDR|CODE|PROTO|HELO )
-
- value = ( { empty } | xtext )
-
- xtext = { attribute value encoded as per RFC 1891 }
+ name = ( NAME | ADDR | PROTO | HELO )
The XCLIENT command can be sent at any time except in the middle
of a mail delivery transaction (i.e. between MAIL and DOT). The
-command may be pipelined after the server EHLO reply announces
-ESMTP pipelining support.
+command may be pipelined if the server EHLO reply announces ESMTP
+pipelining support.
-The server reply codes are as follows:
+The XCLIENT reply codes are as follows:
Code | Meaning
-----|------------
250 | success
- 501 | bad command parameter
+ 501 | bad command parameter syntax
503 | mail transaction in progress
421 | unable to proceed
-The server must report success in case of an unrecognized attribute
-name, although it may log a warning.
-
-Specific usage scenarios
-========================
-
-This section discusses the semantics of XCLIENT requests. Specific
-syntax details are given in the next section.
-
-The XCLIENT OVERRIDE request modifies remote client attributes that
-the MTA normally uses for access control, message headers, logging,
-and for other purposes, for the duraction of the entire SMTP session.
-Attributes that are not specified in XCLIENT OVERRIDE requests are
-not modified.
-
-The following example overrides only the client hostname and network
-address, leaving unchanged all other client attributes such as the
-mail protocol or the hostname given in the HELO command:
-
- XCLIENT OVERRIDE NAME=spike.porcupine.org
- XCLIENT OVERRIDE ADDR=168.100.189.2
-
-The XCLIENT FORWARD request specifies remote client attributes that
-are logged with one message delivery attempt. The attributes are
-discarded after the MAIL FROM transaction finishes. In the absence
-of any XCLIENT FORWARD attributes, the MTA must use the normal
-client attributes.
-
-If only a subset of all possible XCLIENT FORWARD attributes is
-specified, the unspecified attributes must be treated as if they
-are unknown. The implementation must not replace missing XCLIENT
-FORWARD attributes by normal attributes.
-
-The following example updates all forwarded client attributes that
-are defined in this document, leaving none at their default unknown
-value:
-
- XCLIENT FORWARD NAME=spike.porcupine.org ADDR=168.100.189.2
- XCLIENT FORWARD HELO=spike.porcupine.org PROTO=ESMTP
-
-Note 1: attributes specified with successive XCLIENT commands
-accumulate.
-
-Note 2: XCLIENT FORWARD attributes take precedence over XCLIENT
-OVERRIDE attributes.
-
-Attribute value details
-=======================
-
-Attribute values are encoded as RFC 1891 xtext strings. To explicitly
-specify that an attribute value is unavailable, the value must be
-empty; the client must not send its own internal representation of
-unavailable information.
-
-The NAME_CODE attribute specifies NAME hostname lookup status
-information. Values are OK (success), TEMP (temporary lookup
-failure) or PERM (permanent lookup failure). When CODE is set to
-any value other than OK, the NAME attribute is automatically set
-to the unknown value.
-
-The NAME attribute specifies a name space (typically, DNS) and
-an MTA name within that name space.
-and not a numerical address. When a null client name is specified
-(i.e. the client name is unknown), the CODE attribute is implicitly
-set to PERM. When a valid domain name is specified, CODE is implicitly
-set to OK. The server may process a syntactically invalid domain
-name as if it were unknown.
+The NAME attribute specifies an SMTP client hostname (not an SMTP
+client address), [unavailable] when client hostname lookup failed
+due to a permanent error, or [temporary] when the lookup error
+condition was transient.
-The ADDR attribute must specify a numerical network address without
-[].
+The ADDR attribute specifies an SMTP client numerical IPv4 network
+address, an IPv6 address prefixed with IPV6:, or [unavailable]
+when the address information is unavailable. Address information
+is not enclosed with [].
-The PROTO attribute should be a string of up to 64 printable
-characters, where printable is defined by the ANSI C isascii() and
-isprint() predicates.
+The PROTO attribute specifies either SMTP or ESMTP.
-The HELO attribute should be a syntactically valid HELO
-parameter value.
+The HELO attribute specifies a HELO parameter value, or the value
+[unavailable] when the information is unavailable.
-Note 3: syntactically valid NAME and HELO attributes can be up to
+Note 1: syntactically valid NAME and HELO attributes can be up to
255 characters long. The client must not send XCLIENT commands that
-exceed the 512 character limit of SMTP commands.
+exceed the 512 character limit for SMTP commands.
-Note 4: attribute values may end up in Received: or other message
-headers. The receiving MTA may substitute characters in order to
-not violate RFC 822 or RFC 2822.
+Note 2: [UNAVAILABLE], [TEMPORARY] and IPV6: may be specified in
+upper case, lower case or mixed case.
Security
========
The XCLIENT command changes audit trails and/or client access
-permissions. For these reasons, use of these commands must be
-restricted to authorized clients only.
+permissions. Use of this command must be restricted to authorized
+clients.
-The examples in this document assume that XCLIENT does not override
-its own access control mechanism.
+The XCLIENT should 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. The XCLIENT FORWARD attributes are reset after the MAIL
-FROM command completes, so there is no risk of information leakage.
+messages within the same SMTP session. The XCLIENT attributes are
+persistent across deliveries, and need to be reset as appropriate
+in order to avoid information leakage.
--- /dev/null
+Purpose of the XFORWARD extension to SMTP
+========================================
+
+The XFORWARD command targets the following problem:
+
+- Logging after SMTP-based content filter. With the deployment of
+Internet->MTA1->filter->MTA2 style content filter applications,
+the logging of client and message identifying information changes
+when MTA1 gives the mail to the content filter. To simplify the
+interpretation of MTA2 logging, it would help if MTA1 could forward
+remote client and/or message identifying information through the
+content filter to MTA2, so that the information could be logged as
+part of mail handling transactions.
+
+Command syntax
+==============
+
+In SMTP server EHLO replies, the keyword associated with this
+extension is XFORWARD. The keyword is followed by the names of the
+attributes that the XFORWARD implementation supports.
+
+The general command syntax is described below. Upper case and
+quoted strings specify terminals, lowercase strings specify meta
+terminals, and SP is whitespace. Although command and attribute
+names are shown in upper case, they are in fact case insensitive.
+
+ xclient-command = XFORWARD 1*( SP attribute )
+
+ attribute = name"="value
+
+ name = ( NAME | ADDR | PROTO | HELO | IDENT )
+
+The XFORWARD command can be sent at any time except in the middle
+of a mail delivery transaction (i.e. between MAIL and DOT). The
+command may be pipelined if the server EHLO reply announces ESMTP
+pipelining support.
+
+The XFORWARD reply codes are as follows:
+
+ Code | Meaning
+ -----|------------
+ 250 | success
+ 501 | bad command parameter syntax
+ 503 | mail transaction in progress
+ 421 | unable to proceed
+
+The information specified with XFORWARD attribues is not limited
+to DNS hostnames, IP addresses or SMTP protocol names. Attribute
+values may contain arbitrary text, but must not contain control
+characters, non-ASCII characters, whitespace, or other characters
+that are special in message headers.
+
+The NAME attribute specifies an up-stream client hostname, or
+[UNAVAILABLE] when the information is unavailable. The hostname
+may be a non-DNS hostname.
+
+The ADDR attribute specifies an up-stream client network address,
+or [UNAVAILABLE] when the information is unavailable. Address
+information is not enclosed with []. The address may be a non-IP
+address.
+
+The PROTO attribute specifies the mail protocol that was used by
+the up-stream client. This may be an SMTP non-SMTP protocol name
+of up to 64 characters, or [UNAVAILABLE] when the information is
+unavailable.
+
+The HELO attribute specifies the hostname that the up-stream client
+host introduced itself with (for example, via the SMTP HELO command),
+or [UNAVAILABLE] when the information is unavailable. The hostname
+may be a non-DNS hostname.
+
+Note 1: DNS hostnames can be up to 255 characters long. The XFORWARD
+client implementation must not send XFORWARD commands that exceed
+the 512 character limit for SMTP commands.
+
+Note 2: [UNAVAILABLE] may be specified in upper case, lower case
+or mixed case.
+
+Note 3: the XFORWARD server implementation must not mix information
+from the current SMTP session with forwarded information from an
+up-stream session.
+
+Security
+========
+
+The XFORWARD command changes audit trails. Use of this command
+must be restricted to authorized clients.
+
+SMTP connection caching
+=======================
+
+SMTP connection caching makes it possible to deliver multiple
+messages within the same SMTP session. The XFORWARD attributes are
+reset after the MAIL FROM command completes, so there is no risk
+of information leakage.
$readme_directory/UUCP_README:f:root:-:644
$readme_directory/VERP_README:f:root:-:644
$readme_directory/VIRTUAL_README:f:root:-:644
+$readme_directory/XCLIENT_README:f:root:-:644
+$readme_directory/XFORWARD_README:f:root:-:644
#
lmtp_tcp_port = 24
+# The lmtp_send_xforward_command parameter controls whether the Postfix
+# LMTP client will send an XFORWARD command to the SMTP server, when
+# the ELMTP HELO response of the remote host indicates XFORWARD support.
+# This allows an "smtp" delivery agent, used for content filter
+# message injection, to forward the name, address, protocol and HELO
+# name of the original client to the content filter and downstream
+# queuing LMTP server. Before you change the value to yes, it is best
+# to make sure your content filter supports this command.
+#
+lmtp_send_xforward_command = no
+
#
# RESOURCE AND RATE CONTROLS
#
#
lmtp_lhlo_timeout = 300s
+# The lmtp_xforward_timeout parameter specifies the LMTP client timeout
+# for sending the LMTP XFORWARD command, and for receiving the server
+# response.
+#
+# In case of problems the client does NOT try the next address on
+# the mail exchanger list.
+#
+# Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
+# The default time unit is s (seconds).
+#
+lmtp_xforward_timeout = 300s
+
# The lmtp_mail_timeout parameter specifies the LMTP client timeout
# for sending the LMTP MAIL FROM command, and for receiving the server
# response.
server. Used as backup if the <b>lmtp</b> service is not
found in <b>services</b>(4).
+ <b>lmtp_send_xforward_command</b>
+ If the LMTP server announces XFORWARD support, send
+ the name, address, protocol and HELO name of the
+ original client. This can be used to forward client
+ information through a content filter to a down-
+ stream queuing LMTP server.
+
<b>Authentication controls</b>
<b>lmtp_sasl_auth_enable</b>
Enable per-session authentication as per <a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a>
Timeout for sending the <b>LHLO</b> command, and for
receiving the server response.
+ <b>lmtp_xforward_timeout</b>
+ Timeout for sending the <b>XFORWARD</b> command, and for
+ receiving the server response.
+
<b>lmtp_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>lmtp_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>lmtp_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>lmtp_data_xfer_timeout</b>
<b>lmtp_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>lmtp_rset_timeout</b>
- Timeout for sending the <b>RSET</b> command, and for
+ Timeout for sending the <b>RSET</b> command, and for
receiving the server response.
<b>lmtp_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>
Numerical source network address to bind to when
making a connection.
+ <b>smtp_defer_if_no_mx_address_found</b>
+ 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
+ until the mail is too old.
+
<b>smtp_line_length_limit</b>
Length limit for SMTP message content lines. Zero
means no limit. Some SMTP servers misbehave on
nested deeper, when converting from 8BITMIME format
to 7BIT format.
+ <b>smtp_send_xforward_command</b>
+ If the SMTP server announces XFORWARD support, send
+ the name, address, protocol and HELO name of the
+ original client. This can be used to forward client
+ information through a content filter to a down-
+ stream queuing SMTP server.
+
<b>Authentication controls</b>
<b>smtp_sasl_auth_enable</b>
Enable per-session authentication as per <a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a>
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
- 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
- until the mail is too old.
-
- <b>smtp_send_xforward_command</b>
- If the SMTP server announces XFORWARD support, send
- the name, address, protocol and HELO name of the
- original client. This can be used to forward client
- information through a content filter to a down-
- stream queuing SMTP server.
-
<b>smtp_rset_timeout</b>
Timeout for sending the <b>RSET</b> command.
.IP \fBlmtp_tcp_port\fR
The TCP port to be used when connecting to a LMTP server. Used as
backup if the \fBlmtp\fR service is not found in \fBservices\fR(4).
+.IP \fBlmtp_send_xforward_command\fR
+If the LMTP server announces XFORWARD support, send the name,
+address, protocol and HELO name of the original client. This
+can be used to forward client information through a content
+filter to a downstream queuing LMTP server.
.SH "Authentication controls"
.IP \fBlmtp_sasl_auth_enable\fR
Enable per-session authentication as per RFC 2554 (SASL).
.IP \fBlmtp_lhlo_timeout\fR
Timeout for sending the \fBLHLO\fR command, and for
receiving the server response.
+.IP \fBlmtp_xforward_timeout\fR
+Timeout for sending the \fBXFORWARD\fR command, and for
+receiving the server response.
.IP \fBlmtp_mail_timeout\fR
Timeout for sending the \fBMAIL FROM\fR command, and for
receiving the server response.
Never send EHLO at the start of a connection.
.IP \fBsmtp_bind_address\fR
Numerical source network address to bind to when making a connection.
+.IP \fBsmtp_defer_if_no_mx_address_found\fR
+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 until
+the mail is too old.
.IP \fBsmtp_line_length_limit\fR
Length limit for SMTP message content lines. Zero means no limit.
Some SMTP servers misbehave on long lines.
The maximal nesting level of multipart mail that the MIME
processor can handle. Refuse mail that is nested deeper,
when converting from 8BITMIME format to 7BIT format.
+.IP \fBsmtp_send_xforward_command\fR
+If the SMTP server announces XFORWARD support, send the name,
+address, protocol and HELO name of the original client. This
+can be used to forward client information through a content
+filter to a downstream queuing SMTP server.
.SH "Authentication controls"
.IP \fBsmtp_sasl_auth_enable\fR
Enable per-session authentication as per RFC 2554 (SASL).
Timeout for sending the "\fB.\fR" command, and for
receiving the server response. When no response is received, a
warning is logged that the mail may be delivered multiple times.
-.IP \fBsmtp_defer_if_no_mx_address_found\fR
-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 until
-the mail is too old.
-.IP \fBsmtp_send_xforward_command\fR
-If the SMTP server announces XFORWARD support, send the name,
-address, protocol and HELO name of the original client. This
-can be used to forward client information through a content
-filter to a downstream queuing SMTP server.
.IP \fBsmtp_rset_timeout\fR
Timeout for sending the \fBRSET\fR command.
.IP \fBsmtp_quit_timeout\fR
#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_XFWD_TMOUT "smtp_xforward_timeout"
+#define DEF_SMTP_XFWD_TMOUT "300s"
+extern int var_smtp_xfwd_tmout;
#define VAR_SMTP_MAIL_TMOUT "smtp_mail_timeout"
#define DEF_SMTP_MAIL_TMOUT "300s"
#define DEF_LMTP_LHLO_TMOUT "300s"
extern int var_lmtp_lhlo_tmout;
+#define VAR_LMTP_XFWD_TMOUT "lmtp_xforward_timeout"
+#define DEF_LMTP_XFWD_TMOUT "300s"
+extern int var_lmtp_xfwd_tmout;
+
#define VAR_LMTP_MAIL_TMOUT "lmtp_mail_timeout"
#define DEF_LMTP_MAIL_TMOUT "300s"
extern int var_lmtp_mail_tmout;
#define DEF_LMTP_QUIT_TMOUT "300s"
extern int var_lmtp_quit_tmout;
+#define VAR_LMTP_SEND_XFORWARD "lmtp_send_xforward_command"
+#define DEF_LMTP_SEND_XFORWARD 0
+extern bool var_lmtp_send_xforward;
+
/*
* Cleanup service. Header info that exceeds $header_size_limit bytes forces
* the start of the message body.
#define MAIL_ATTR_ORG_LOCAL "local" /* local submission */
/*
- * XCLIENT in SMTP.
+ * XCLIENT/XFORWARD in SMTP.
*/
#define XCLIENT_CMD "XCLIENT" /* XCLIENT command */
#define XCLIENT_NAME "NAME" /* client name */
#define XCLIENT_ADDR "ADDR" /* client address */
#define XCLIENT_PROTO "PROTO" /* client protocol */
-#define XCLIENT_CODE "NAME_CODE" /* client name status */
#define XCLIENT_HELO "HELO" /* client helo */
+#define XCLIENT_UNAVAILABLE "[UNAVAILABLE]" /* permanently unavailable */
+#define XCLIENT_TEMPORARY "[TEMPUNAVAIL]" /* temporarily unavailable */
+
#define XFORWARD_CMD "XFORWARD" /* XFORWARD command */
#define XFORWARD_NAME "NAME" /* client name */
#define XFORWARD_ADDR "ADDR" /* client address */
#define XFORWARD_HELO "HELO" /* client helo */
#define XFORWARD_IDENT "IDENT" /* message identifier */
+#define XFORWARD_UNAVAILABLE "[UNAVAILABLE]" /* attribute unavailable */
+
/* LICENSE
/* .ad
/* .fi
* Patches change the patchlevel and the release date. Snapshots change the
* release date only, unless they include the same bugfix as a patch release.
*/
-#define MAIL_RELEASE_DATE "20031213"
+#define MAIL_RELEASE_DATE "20031214"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "2.0.16-" MAIL_RELEASE_DATE
/* .IP \fBlmtp_tcp_port\fR
/* The TCP port to be used when connecting to a LMTP server. Used as
/* backup if the \fBlmtp\fR service is not found in \fBservices\fR(4).
+/* .IP \fBlmtp_send_xforward_command\fR
+/* If the LMTP server announces XFORWARD support, send the name,
+/* address, protocol and HELO name of the original client. This
+/* can be used to forward client information through a content
+/* filter to a downstream queuing LMTP server.
/* .SH "Authentication controls"
/* .IP \fBlmtp_sasl_auth_enable\fR
/* Enable per-session authentication as per RFC 2554 (SASL).
/* .IP \fBlmtp_lhlo_timeout\fR
/* Timeout for sending the \fBLHLO\fR command, and for
/* receiving the server response.
+/* .IP \fBlmtp_xforward_timeout\fR
+/* Timeout for sending the \fBXFORWARD\fR command, and for
+/* receiving the server response.
/* .IP \fBlmtp_mail_timeout\fR
/* Timeout for sending the \fBMAIL FROM\fR command, and for
/* receiving the server response.
int var_lmtp_conn_tmout;
int var_lmtp_rset_tmout;
int var_lmtp_lhlo_tmout;
+int var_lmtp_xfwd_tmout;
int var_lmtp_mail_tmout;
int var_lmtp_rcpt_tmout;
int var_lmtp_data0_tmout;
char *var_lmtp_sasl_opts;
char *var_lmtp_sasl_passwd;
bool var_lmtp_sasl_enable;
+bool var_lmtp_send_xforward;
/*
* Global variables.
VAR_LMTP_CONN_TMOUT, DEF_LMTP_CONN_TMOUT, &var_lmtp_conn_tmout, 0, 0,
VAR_LMTP_RSET_TMOUT, DEF_LMTP_RSET_TMOUT, &var_lmtp_rset_tmout, 1, 0,
VAR_LMTP_LHLO_TMOUT, DEF_LMTP_LHLO_TMOUT, &var_lmtp_lhlo_tmout, 1, 0,
+ VAR_LMTP_XFWD_TMOUT, DEF_LMTP_XFWD_TMOUT, &var_lmtp_xfwd_tmout, 1, 0,
VAR_LMTP_MAIL_TMOUT, DEF_LMTP_MAIL_TMOUT, &var_lmtp_mail_tmout, 1, 0,
VAR_LMTP_RCPT_TMOUT, DEF_LMTP_RCPT_TMOUT, &var_lmtp_rcpt_tmout, 1, 0,
VAR_LMTP_DATA0_TMOUT, DEF_LMTP_DATA0_TMOUT, &var_lmtp_data0_tmout, 1, 0,
#define LMTP_FEATURE_PIPELINING (1<<2)
#define LMTP_FEATURE_SIZE (1<<3)
#define LMTP_FEATURE_AUTH (1<<5)
+#define LMTP_FEATURE_XFORWARD_NAME (1<<6)
+#define LMTP_FEATURE_XFORWARD_ADDR (1<<7)
+#define LMTP_FEATURE_XFORWARD_PROTO (1<<8)
+#define LMTP_FEATURE_XFORWARD_HELO (1<<9)
+
+#define LMTP_FEATURE_XFORWARD_NAME_ADDR \
+ (LMTP_FEATURE_XFORWARD_NAME | LMTP_FEATURE_XFORWARD_ADDR)
+
+#define LMTP_FEATURE_XFORWARD_PROTO_HELO \
+ (LMTP_FEATURE_XFORWARD_PROTO | LMTP_FEATURE_XFORWARD_HELO)
/*
* lmtp.c
#include <vstring_vstream.h>
#include <stringops.h>
#include <mymalloc.h>
+#include <name_code.h>
/* Global library. */
* the existing code for exception handling and error reporting.
*
* Client states that are associated with sending mail (up to and including
- * SMTP_STATE_DOT) must have smaller numerical values than the non-sending
- * states (SMTP_STATE_ABORT .. SMTP_STATE_LAST).
+ * LMTP_STATE_DOT) must have smaller numerical values than the non-sending
+ * states (LMTP_STATE_ABORT .. LMTP_STATE_LAST).
*/
-#define LMTP_STATE_MAIL 0
-#define LMTP_STATE_RCPT 1
-#define LMTP_STATE_DATA 2
-#define LMTP_STATE_DOT 3
-#define LMTP_STATE_ABORT 4
-#define LMTP_STATE_RSET 5
-#define LMTP_STATE_QUIT 6
-#define LMTP_STATE_LAST 7
+#define LMTP_STATE_XFORWARD_NAME_ADDR 0
+#define LMTP_STATE_XFORWARD_PROTO_HELO 1
+#define LMTP_STATE_MAIL 2
+#define LMTP_STATE_RCPT 3
+#define LMTP_STATE_DATA 4
+#define LMTP_STATE_DOT 5
+#define LMTP_STATE_ABORT 6
+#define LMTP_STATE_RSET 7
+#define LMTP_STATE_QUIT 8
+#define LMTP_STATE_LAST 9
int *xfer_timeouts[LMTP_STATE_LAST] = {
+ &var_lmtp_xfwd_tmout, /* name/addr */
+ &var_lmtp_xfwd_tmout, /* helo/proto */
&var_lmtp_mail_tmout,
&var_lmtp_rcpt_tmout,
&var_lmtp_data0_tmout,
&var_lmtp_data2_tmout,
- &var_lmtp_rset_tmout,
- &var_lmtp_rset_tmout,
+ &var_lmtp_rset_tmout, /* abort */
+ &var_lmtp_rset_tmout, /* rset */
&var_lmtp_quit_tmout,
};
char *xfer_states[LMTP_STATE_LAST] = {
+ "sending XFORWARD name/address",
+ "sending XFORWARD protocol/helo_name",
"sending MAIL FROM",
"sending RCPT TO",
"sending DATA command",
"sending end of data -- message may be sent more than once",
- "sending RSET",
- "sending RSET",
+ "sending RSET", /* abort */
+ "sending RSET", /* rset */
"sending QUIT",
};
char *xfer_request[LMTP_STATE_LAST] = {
+ "XFORWARD name/address command",
+ "XFORWARD helo/protocol command",
"MAIL FROM command",
"RCPT TO command",
"DATA command",
"end of DATA command",
- "RSET command",
- "RSET command",
+ "RSET command", /* abort */
+ "RSET command", /* rset */
"QUIT command",
};
+static int lmtp_send_proto_helo;
+
/* lmtp_lhlo - perform initial handshake with LMTP server */
int lmtp_lhlo(LMTP_STATE *state)
char *lines;
char *words;
char *word;
+ static NAME_CODE xforward_features[] = {
+ XFORWARD_NAME, LMTP_FEATURE_XFORWARD_NAME,
+ XFORWARD_ADDR, LMTP_FEATURE_XFORWARD_ADDR,
+ XFORWARD_PROTO, LMTP_FEATURE_XFORWARD_PROTO,
+ XFORWARD_HELO, LMTP_FEATURE_XFORWARD_HELO,
+ 0, 0,
+ };
/*
* Prepare for disaster.
state->features |= LMTP_FEATURE_8BITMIME;
else if (strcasecmp(word, "PIPELINING") == 0)
state->features |= LMTP_FEATURE_PIPELINING;
+ else if (strcasecmp(word, "XFORWARD") == 0)
+ while ((word = mystrtok(&words, " \t")) != 0)
+ state->features |= name_code(xforward_features,
+ NAME_CODE_FLAG_NONE, word);
else if (strcasecmp(word, "SIZE") == 0)
state->features |= LMTP_FEATURE_SIZE;
#ifdef USE_SASL_AUTH
default:
msg_panic("%s: bad sender state %d", myname, send_state);
+ /*
+ * Build the XFORWARD command. With properly sanitized
+ * information, the command length stays within the 512 byte
+ * command line length limit.
+ */
+ case LMTP_STATE_XFORWARD_NAME_ADDR:
+ vstring_strcpy(next_command, XFORWARD_CMD);
+ if (state->features & LMTP_FEATURE_XFORWARD_NAME)
+ vstring_sprintf_append(next_command, " %s=%s",
+ XFORWARD_NAME, DEL_REQ_ATTR_AVAIL(request->client_name) ?
+ request->client_name : XFORWARD_UNAVAILABLE);
+ if (state->features & LMTP_FEATURE_XFORWARD_ADDR)
+ vstring_sprintf_append(next_command, " %s=%s",
+ XFORWARD_ADDR, DEL_REQ_ATTR_AVAIL(request->client_addr) ?
+ request->client_addr : XFORWARD_UNAVAILABLE);
+ if (lmtp_send_proto_helo)
+ next_state = LMTP_STATE_XFORWARD_PROTO_HELO;
+ else
+ next_state = LMTP_STATE_MAIL;
+ break;
+
+ case LMTP_STATE_XFORWARD_PROTO_HELO:
+ vstring_strcpy(next_command, XFORWARD_CMD);
+ if (state->features & LMTP_FEATURE_XFORWARD_PROTO)
+ vstring_sprintf_append(next_command, " %s=%s",
+ XFORWARD_PROTO, DEL_REQ_ATTR_AVAIL(request->client_proto) ?
+ request->client_proto : XFORWARD_UNAVAILABLE);
+ if (state->features & LMTP_FEATURE_XFORWARD_HELO)
+ vstring_sprintf_append(next_command, " %s=%s",
+ XFORWARD_HELO, DEL_REQ_ATTR_AVAIL(request->client_helo) ?
+ request->client_helo : XFORWARD_UNAVAILABLE);
+ next_state = LMTP_STATE_MAIL;
+ break;
+
/*
* Build the MAIL FROM command.
*/
/*
* Sanity check.
*/
- if (recv_state < LMTP_STATE_MAIL
+ if (recv_state < LMTP_STATE_XFORWARD_NAME_ADDR
|| recv_state > LMTP_STATE_QUIT)
msg_panic("%s: bad receiver state %d (sender state %d)",
myname, recv_state, send_state);
*/
switch (recv_state) {
+ /*
+ * Process the XFORWARD response.
+ */
+ case LMTP_STATE_XFORWARD_NAME_ADDR:
+ if (resp->code / 100 != 2)
+ msg_warn("host %s said: %s (in reply to %s)",
+ session->namaddr,
+ translit(resp->str, "\n", " "),
+ xfer_request[LMTP_STATE_XFORWARD_NAME_ADDR]);
+ if (lmtp_send_proto_helo)
+ recv_state = LMTP_STATE_XFORWARD_PROTO_HELO;
+ else
+ recv_state = LMTP_STATE_MAIL;
+ break;
+
+ case LMTP_STATE_XFORWARD_PROTO_HELO:
+ if (resp->code / 100 != 2)
+ msg_warn("host %s said: %s (in reply to %s)",
+ session->namaddr,
+ translit(resp->str, "\n", " "),
+ xfer_request[LMTP_STATE_XFORWARD_PROTO_HELO]);
+ recv_state = LMTP_STATE_MAIL;
+ break;
+
/*
* Process the MAIL FROM response. When the server
* rejects the sender, set the mail_from_rejected flag so
&& sent(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, rcpt->orig_addr,
rcpt->address, rcpt->offset,
- session->namaddr, request->arrival_time,
+ session->namaddr, request->arrival_time,
"%s",
translit(resp->str, "\n", " ")) == 0) {
if (request->flags & DEL_REQ_FLAG_SUCCESS)
int lmtp_xfer(LMTP_STATE *state)
{
- return (lmtp_loop(state, LMTP_STATE_MAIL, LMTP_STATE_MAIL));
+ DELIVER_REQUEST *request = state->request;
+ int start;
+ int send_name_addr;
+
+ /*
+ * Use the XFORWARD command to forward client attributes only when a
+ * minimal amount of information is available.
+ */
+ send_name_addr =
+ (var_lmtp_send_xforward
+ && (state->features & LMTP_FEATURE_XFORWARD_NAME_ADDR)
+ && (DEL_REQ_ATTR_AVAIL(request->client_name)
+ || DEL_REQ_ATTR_AVAIL(request->client_addr)));
+ lmtp_send_proto_helo =
+ (var_lmtp_send_xforward
+ && (state->features & LMTP_FEATURE_XFORWARD_PROTO_HELO)
+ && (DEL_REQ_ATTR_AVAIL(request->client_proto)
+ || DEL_REQ_ATTR_AVAIL(request->client_helo)));
+ if (send_name_addr)
+ start = LMTP_STATE_XFORWARD_NAME_ADDR;
+ else if (lmtp_send_proto_helo)
+ start = LMTP_STATE_XFORWARD_PROTO_HELO;
+ else
+ start = LMTP_STATE_MAIL;
+ return (lmtp_loop(state, start, start));
}
/* lmtp_rset - send a lone RSET command and wait for response */
* Sanity checks. Verify that every member has an acceptable value.
*/
if (user == 0)
- msg_fatal("missing user= attribute");
+ msg_fatal("missing user= command-line attribute");
if (attr->command == 0)
- msg_fatal("missing argv= attribute");
+ msg_fatal("missing argv= command-line attribute");
if (attr->uid == 0)
- msg_fatal("request to deliver as root");
+ msg_fatal("user= command-line attribute specifies root privileges");
if (attr->uid == var_owner_uid)
- msg_fatal("request to deliver as mail system owner");
+ msg_fatal("user= command-line attribute specifies mail system owner %s",
+ var_mail_owner);
if (attr->gid == 0)
- msg_fatal("request to use privileged group id %ld", (long) attr->gid);
+ msg_fatal("user= command-line attribute specifies privileged group id 0");
if (attr->gid == var_owner_gid)
- msg_fatal("request to use mail system owner group id %ld", (long) attr->gid);
+ msg_fatal("user= command-line attribute specifies mail system owner %s group id %ld",
+ var_mail_owner, (long) attr->gid);
/*
* Give the poor tester a clue of what is going on.
smtp_proto.o: ../../include/mymalloc.h
smtp_proto.o: ../../include/iostuff.h
smtp_proto.o: ../../include/split_at.h
+smtp_proto.o: ../../include/name_code.h
smtp_proto.o: ../../include/mail_params.h
smtp_proto.o: ../../include/smtp_stream.h
smtp_proto.o: ../../include/mail_queue.h
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
/* Never send EHLO at the start of a connection.
/* .IP \fBsmtp_bind_address\fR
/* Numerical source network address to bind to when making a connection.
+/* .IP \fBsmtp_defer_if_no_mx_address_found\fR
+/* 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 until
+/* the mail is too old.
/* .IP \fBsmtp_line_length_limit\fR
/* Length limit for SMTP message content lines. Zero means no limit.
/* Some SMTP servers misbehave on long lines.
/* The maximal nesting level of multipart mail that the MIME
/* processor can handle. Refuse mail that is nested deeper,
/* when converting from 8BITMIME format to 7BIT format.
+/* .IP \fBsmtp_send_xforward_command\fR
+/* If the SMTP server announces XFORWARD support, send the name,
+/* address, protocol and HELO name of the original client. This
+/* can be used to forward client information through a content
+/* filter to a downstream queuing SMTP server.
/* .SH "Authentication controls"
/* .IP \fBsmtp_sasl_auth_enable\fR
/* Enable per-session authentication as per RFC 2554 (SASL).
/* Timeout for sending the "\fB.\fR" command, and for
/* receiving the server response. When no response is received, a
/* warning is logged that the mail may be delivered multiple times.
-/* .IP \fBsmtp_defer_if_no_mx_address_found\fR
-/* 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 until
-/* the mail is too old.
-/* .IP \fBsmtp_send_xforward_command\fR
-/* If the SMTP server announces XFORWARD support, send the name,
-/* address, protocol and HELO name of the original client. This
-/* can be used to forward client information through a content
-/* filter to a downstream queuing SMTP server.
/* .IP \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_xfwd_tmout;
int var_smtp_mail_tmout;
int var_smtp_rcpt_tmout;
int var_smtp_data0_tmout;
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_XFWD_TMOUT, DEF_SMTP_XFWD_TMOUT, &var_smtp_xfwd_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,
#define SMTP_FEATURE_XFORWARD_PROTO (1<<9)
#define SMTP_FEATURE_XFORWARD_HELO (1<<10)
-#define SMTP_FEATURE_XFORWARD_MASK \
- (SMTP_FEATURE_XFORWARD_NAME | SMTP_FEATURE_XFORWARD_ADDR \
- | SMTP_FEATURE_XFORWARD_PROTO | SMTP_FEATURE_XFORWARD_HELO)
+#define SMTP_FEATURE_XFORWARD_NAME_ADDR \
+ (SMTP_FEATURE_XFORWARD_NAME | SMTP_FEATURE_XFORWARD_ADDR)
+
+#define SMTP_FEATURE_XFORWARD_PROTO_HELO \
+ (SMTP_FEATURE_XFORWARD_PROTO | SMTP_FEATURE_XFORWARD_HELO)
/*
* 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_XFORWARD_ADDR 0
-#define SMTP_STATE_XFORWARD_HELO 1
+#define SMTP_STATE_XFORWARD_NAME_ADDR 0
+#define SMTP_STATE_XFORWARD_PROTO_HELO 1
#define SMTP_STATE_MAIL 2
#define SMTP_STATE_RCPT 3
#define SMTP_STATE_DATA 4
#define SMTP_STATE_LAST 8
int *xfer_timeouts[SMTP_STATE_LAST] = {
- &var_smtp_xclnt_tmout,
- &var_smtp_xclnt_tmout,
+ &var_smtp_xfwd_tmout, /* name/addr */
+ &var_smtp_xfwd_tmout, /* helo/proto */
&var_smtp_mail_tmout,
&var_smtp_rcpt_tmout,
&var_smtp_data0_tmout,
};
char *xfer_states[SMTP_STATE_LAST] = {
- "sending XFORWARD address and name",
- "sending XFORWARD helo_name and protocol",
+ "sending XFORWARD name/address",
+ "sending XFORWARD protocol/helo_name",
"sending MAIL FROM",
"sending RCPT TO",
"sending DATA command",
state->features |= SMTP_FEATURE_8BITMIME;
else if (strcasecmp(word, "PIPELINING") == 0)
state->features |= SMTP_FEATURE_PIPELINING;
- else if (strcasecmp(word, "XFORWARD") == 0) {
+ else if (strcasecmp(word, "XFORWARD") == 0)
while ((word = mystrtok(&words, " \t")) != 0)
state->features |= name_code(xforward_features,
NAME_CODE_FLAG_NONE, word);
- } else if (strcasecmp(word, "SIZE") == 0) {
+ else if (strcasecmp(word, "SIZE") == 0) {
state->features |= SMTP_FEATURE_SIZE;
if ((word = mystrtok(&words, " \t")) != 0) {
if (!alldig(word))
int mail_from_rejected;
int downgrading;
int mime_errs;
+ int send_name_addr;
+ int send_proto_helo;
/*
* Macros for readability.
* amount of information is available.
*/
nrcpt = 0;
- if (var_smtp_send_xforward
- && (state->features & SMTP_FEATURE_XFORWARD_MASK)
- && (DEL_REQ_ATTR_AVAIL(request->client_name)
- || DEL_REQ_ATTR_AVAIL(request->client_addr)))
- recv_state = send_state = SMTP_STATE_XFORWARD_ADDR;
+ send_name_addr =
+ (var_smtp_send_xforward
+ && (state->features & SMTP_FEATURE_XFORWARD_NAME_ADDR)
+ && (DEL_REQ_ATTR_AVAIL(request->client_name)
+ || DEL_REQ_ATTR_AVAIL(request->client_addr)));
+ send_proto_helo =
+ (var_smtp_send_xforward
+ && (state->features & SMTP_FEATURE_XFORWARD_PROTO_HELO)
+ && (DEL_REQ_ATTR_AVAIL(request->client_proto)
+ || DEL_REQ_ATTR_AVAIL(request->client_helo)));
+ if (send_name_addr)
+ recv_state = send_state = SMTP_STATE_XFORWARD_NAME_ADDR;
+ else if (send_proto_helo)
+ recv_state = send_state = SMTP_STATE_XFORWARD_PROTO_HELO;
else
recv_state = send_state = SMTP_STATE_MAIL;
next_rcpt = send_rcpt = recv_rcpt = 0;
* information, the command length stays within the 512 byte
* command line length limit.
*/
- case SMTP_STATE_XFORWARD_ADDR:
+ case SMTP_STATE_XFORWARD_NAME_ADDR:
vstring_strcpy(next_command, XFORWARD_CMD);
- if (state->features & SMTP_FEATURE_XFORWARD_NAME) {
- vstring_strcat(next_command, " " XCLIENT_NAME "=");
- if (DEL_REQ_ATTR_AVAIL(request->client_name))
- xtext_quote_append(next_command, request->client_name, "");
- }
- if (state->features & SMTP_FEATURE_XFORWARD_ADDR) {
- vstring_strcat(next_command, " " XFORWARD_ADDR "=");
- if (DEL_REQ_ATTR_AVAIL(request->client_addr))
- xtext_quote_append(next_command, request->client_addr, "");
- }
- next_state = SMTP_STATE_XFORWARD_HELO;
+ if (state->features & SMTP_FEATURE_XFORWARD_NAME)
+ vstring_sprintf_append(next_command, " %s=%s",
+ XFORWARD_NAME, DEL_REQ_ATTR_AVAIL(request->client_name) ?
+ request->client_name : XFORWARD_UNAVAILABLE);
+ if (state->features & SMTP_FEATURE_XFORWARD_ADDR)
+ vstring_sprintf_append(next_command, " %s=%s",
+ XFORWARD_ADDR, DEL_REQ_ATTR_AVAIL(request->client_addr) ?
+ request->client_addr : XFORWARD_UNAVAILABLE);
+ if (send_proto_helo)
+ next_state = SMTP_STATE_XFORWARD_PROTO_HELO;
+ else
+ next_state = SMTP_STATE_MAIL;
break;
- case SMTP_STATE_XFORWARD_HELO:
+ case SMTP_STATE_XFORWARD_PROTO_HELO:
vstring_strcpy(next_command, XFORWARD_CMD);
- if (state->features & SMTP_FEATURE_XFORWARD_HELO) {
- vstring_strcat(next_command, " " XCLIENT_HELO "=");
- if (DEL_REQ_ATTR_AVAIL(request->client_helo))
- xtext_quote_append(next_command, request->client_helo, "");
- }
- if (state->features & SMTP_FEATURE_XFORWARD_ADDR) {
- vstring_strcat(next_command, " " XFORWARD_PROTO "=");
- if (DEL_REQ_ATTR_AVAIL(request->client_proto))
- xtext_quote_append(next_command, request->client_proto, "");
- }
+ if (state->features & SMTP_FEATURE_XFORWARD_PROTO)
+ vstring_sprintf_append(next_command, " %s=%s",
+ XFORWARD_PROTO, DEL_REQ_ATTR_AVAIL(request->client_proto) ?
+ request->client_proto : XFORWARD_UNAVAILABLE);
+ if (state->features & SMTP_FEATURE_XFORWARD_HELO)
+ vstring_sprintf_append(next_command, " %s=%s",
+ XFORWARD_HELO, DEL_REQ_ATTR_AVAIL(request->client_helo) ?
+ request->client_helo : XFORWARD_UNAVAILABLE);
next_state = SMTP_STATE_MAIL;
break;
/*
* Sanity check.
*/
- if (recv_state < SMTP_STATE_XFORWARD_ADDR
+ if (recv_state < SMTP_STATE_XFORWARD_NAME_ADDR
|| recv_state > SMTP_STATE_QUIT)
msg_panic("%s: bad receiver state %d (sender state %d)",
myname, recv_state, send_state);
/*
* Process the XFORWARD response.
*/
- case SMTP_STATE_XFORWARD_ADDR:
+ case SMTP_STATE_XFORWARD_NAME_ADDR:
if (resp->code / 100 != 2)
msg_warn("host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
- xfer_request[SMTP_STATE_MAIL]);
- recv_state = SMTP_STATE_XFORWARD_HELO;
+ xfer_request[SMTP_STATE_XFORWARD_NAME_ADDR]);
+ if (send_proto_helo)
+ recv_state = SMTP_STATE_XFORWARD_PROTO_HELO;
+ else
+ recv_state = SMTP_STATE_MAIL;
break;
- case SMTP_STATE_XFORWARD_HELO:
+ case SMTP_STATE_XFORWARD_PROTO_HELO:
if (resp->code / 100 != 2)
msg_warn("host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
- xfer_request[SMTP_STATE_MAIL]);
+ xfer_request[SMTP_STATE_XFORWARD_PROTO_HELO]);
recv_state = SMTP_STATE_MAIL;
break;
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_proxy.o: ../../include/stringops.h
smtpd_proxy.o: ../../include/connect.h
smtpd_proxy.o: ../../include/iostuff.h
+smtpd_proxy.o: ../../include/name_code.h
smtpd_proxy.o: ../../include/mail_error.h
smtpd_proxy.o: ../../include/name_mask.h
smtpd_proxy.o: ../../include/smtp_stream.h
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
#include <namadr_list.h>
#include <input_transp.h>
#include <anvil_clnt.h>
-#include <xtext.h>
/* Single-threaded server skeleton. */
if (xclient_allowed)
smtpd_chat_reply(state, "250-" XCLIENT_CMD
" " XCLIENT_NAME " " XCLIENT_ADDR
- " " XCLIENT_CODE " " XCLIENT_PROTO
- " " XCLIENT_HELO);
+ " " XCLIENT_PROTO " " XCLIENT_HELO);
if (xforward_allowed)
smtpd_chat_reply(state, "250-" XFORWARD_CMD
" " XFORWARD_NAME " " XFORWARD_ADDR
return (0);
}
-/* xclient_cmd - override client attributes */
+/* xclient_cmd - override SMTP client attributes */
static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
- int arg_no;
+ SMTPD_TOKEN *argp;
char *attr_value;
char *attr_name;
int update_namaddr = 0;
- int code;
- static NAME_CODE xclient_codes[] = {
- "OK", SMTPD_PEER_CODE_OK,
- "PERM", SMTPD_PEER_CODE_PERM,
- "TEMP", SMTPD_PEER_CODE_TEMP,
- 0, -1,
+ int peer_code;
+ static NAME_CODE peer_codes[] = {
+ XCLIENT_UNAVAILABLE, SMTPD_PEER_CODE_PERM,
+ XCLIENT_TEMPORARY, SMTPD_PEER_CODE_TEMP,
+ 0, SMTPD_PEER_CODE_OK,
};
- static NAME_CODE xclient_proto[] = {
+ static NAME_CODE proto_names[] = {
MAIL_PROTO_SMTP, 1,
MAIL_PROTO_ESMTP, 2,
0, -1,
}
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Syntax: %s name=value...",
+ smtpd_chat_reply(state, "501 Syntax: %s attribute=value...",
XCLIENT_CMD);
return (-1);
}
return (-1);
}
#define STREQ(x,y) (strcasecmp((x), (y)) == 0)
-#define UPDATE_STR(s, v) do { if (s) myfree(s); s = (v) ? mystrdup(v) : 0; } while(0)
+#define UPDATE_STR(s, v) do { \
+ if (s) myfree(s); \
+ s = (v) ? mystrdup(v) : 0; \
+ } while(0)
+#define NEUTER_CHARACTERS "<>()\\\";:@"
/*
- * Iterate over all NAME=VALUE attributes.
+ * Iterate over all attribute=value elements.
*/
- for (arg_no = 1; arg_no < argc; arg_no++) {
- attr_name = argv[arg_no].strval;
+ for (argp = argv + 1; argp < argv + argc; argp++) {
+ attr_name = argp->strval;
/*
- * For safety's sake mask non-printable characters in the raw and
- * decoded values; we don't want to handle unexploded munitions.
- * Complain when they send an attribute that we didn't announce.
- *
- * An implementation must allow clients to send XCLIENT before the
- * HELO/EHLO greeting.
- *
- * The client can send multiple XCLIENT attributes in a single command,
- * or multiple XCLIENT commands with fewer attributes.
+ * For safety's sake mask non-printable characters. We'll do more
+ * specific censoring later.
*/
- if ((attr_value = split_at(attr_name, '=')) == 0) {
+ if ((attr_value = split_at(attr_name, '=')) == 0 || *attr_value == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Error: name=value expected");
+ smtpd_chat_reply(state, "501 Error: attribute=value expected");
return (-1);
}
printable(attr_value, '?');
/*
- * NAME=hostname. Also updates the client hostname lookup status
- * code. Treat a numerical hostname as an unavailable name.
+ * NAME=substitute SMTP client hostname. Also updates the client
+ * hostname lookup status code.
*/
if (STREQ(attr_name, XCLIENT_NAME)) {
- if (*attr_value && !valid_hostaddr(attr_value, DONT_GRIPE)) {
- if (!valid_hostname(attr_value, DONT_GRIPE)) {
+ peer_code = name_code(peer_codes, NAME_CODE_FLAG_NONE, attr_value);
+ if (peer_code != SMTPD_PEER_CODE_OK) {
+ attr_value = CLIENT_NAME_UNKNOWN;
+ } else {
+ if (!valid_hostname(attr_value, DONT_GRIPE)
+ || valid_hostaddr(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XCLIENT_NAME, attr_value);
return (-1);
}
- UPDATE_STR(state->name, attr_value);
- state->peer_code = SMTPD_PEER_CODE_OK;
- } else {
- UPDATE_STR(state->name, CLIENT_NAME_UNKNOWN);
- state->peer_code = SMTPD_PEER_CODE_PERM;
}
+ state->peer_code = peer_code;
+ UPDATE_STR(state->name, attr_value);
update_namaddr = 1;
}
/*
- * ADDR=client network address.
+ * ADDR=substitute SMTP client network address.
*/
else if (STREQ(attr_name, XCLIENT_ADDR)) {
- if (*attr_value) {
+ if (STREQ(attr_value, XCLIENT_UNAVAILABLE)) {
+ attr_value = CLIENT_ADDR_UNKNOWN;
+ } else {
if (!valid_hostaddr(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XCLIENT_ADDR, attr_value);
return (-1);
}
- UPDATE_STR(state->addr, attr_value);
- } else {
- UPDATE_STR(state->addr, CLIENT_ADDR_UNKNOWN);
}
+ UPDATE_STR(state->addr, attr_value);
update_namaddr = 1;
}
/*
- * CODE=hostname lookup status. Reset the client hostname if the
- * hostname lookup status is not OK.
- */
- else if (STREQ(attr_name, XCLIENT_CODE)) {
- if (*attr_value) {
- if ((code = name_code(xclient_codes, NAME_CODE_FLAG_NONE, attr_value)) < 0) {
- state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Bad %s value: %s",
- XCLIENT_CODE, attr_value);
- return (-1);
- }
- state->peer_code = code;
- if (code != SMTPD_PEER_CODE_OK) {
- UPDATE_STR(state->name, CLIENT_NAME_UNKNOWN);
- update_namaddr = 1;
- }
- }
- }
-
- /*
- * HELO=hostname. Disallow characters that could mess up our own
- * Received: message headers but allow [].
+ * HELO=substitute SMTP client HELO parameter. Censor special
+ * characters that could mess up message headers.
*/
else if (STREQ(attr_name, XCLIENT_HELO)) {
- if (*attr_value) {
+ if (STREQ(attr_value, XCLIENT_UNAVAILABLE)) {
+ attr_value = CLIENT_HELO_UNKNOWN;
+ } else {
if (strlen(attr_value) > VALID_HOSTNAME_LEN) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XCLIENT_HELO, attr_value);
return (-1);
}
- neuter(attr_value, "<>()\\\";:@", '?');
- UPDATE_STR(state->helo_name, attr_value);
- } else {
- UPDATE_STR(state->helo_name, CLIENT_HELO_UNKNOWN);
+ neuter(attr_value, NEUTER_CHARACTERS, '?');
}
+ UPDATE_STR(state->helo_name, attr_value);
}
/*
- * PROTO=protocol name. Disallow characters that could mess up our
- * own Received: message headers.
+ * PROTO=SMTP protocol name.
*/
else if (STREQ(attr_name, XCLIENT_PROTO)) {
- if (name_code(xclient_proto, NAME_CODE_FLAG_NONE, attr_value) < 0) {
+ if (name_code(proto_names, NAME_CODE_FLAG_NONE, attr_value) < 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XCLIENT_PROTO, attr_value);
return (-1);
}
- UPDATE_STR(state->protocol, attr_value);
+ UPDATE_STR(state->protocol, uppercase(attr_value));
}
/*
static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
- int arg_no;
+ SMTPD_TOKEN *argp;
char *attr_value;
char *attr_name;
int updated = 0;
}
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Syntax: %s name=value...",
+ smtpd_chat_reply(state, "501 Syntax: %s attribute=value...",
XFORWARD_CMD);
return (-1);
}
smtpd_xforward_preset(state);
/*
- * Iterate over all NAME=VALUE attributes.
+ * Iterate over all attribute=value elements.
*/
- for (arg_no = 1; arg_no < argc; arg_no++) {
- attr_name = argv[arg_no].strval;
+ for (argp = argv + 1; argp < argv + argc; argp++) {
+ attr_name = argp->strval;
/*
- * For safety's sake mask non-printable characters in the raw and
- * decoded values; we don't want to handle unexploded munitions.
- * Complain when they send an attribute that we didn't announce.
- *
- * The client can send multiple XFORWARD attributes in a single command,
- * or multiple XFORWARD commands with fewer attributes.
+ * For safety's sake mask non-printable characters. We'll do more
+ * specific censoring later.
*/
- if ((attr_value = split_at(attr_name, '=')) == 0) {
+ if ((attr_value = split_at(attr_name, '=')) == 0 || *attr_value == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Error: name=value expected");
+ smtpd_chat_reply(state, "501 Error: attribute=value expected");
return (-1);
}
printable(attr_value, '?');
switch (flag) {
/*
- * NAME=hostname. Also updates the client hostname lookup status
- * code. Treat a numerical hostname as an unavailable name.
+ * NAME=host name, not necessarily in the DNS. Censor special
+ * characters that could mess up message headers.
*/
case SMTPD_XFORWARD_FLAG_NAME:
- if (*attr_value && !valid_hostaddr(attr_value, DONT_GRIPE)) {
- if (!valid_hostname(attr_value, DONT_GRIPE)) {
- state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Bad %s syntax: %s",
- XFORWARD_NAME, attr_value);
- return (-1);
- }
- } else {
+ if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_NAME_UNKNOWN;
+ } else {
+ neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.name, attr_value);
break;
/*
- * ADDR=client network address.
+ * ADDR=host network address, not necessarily on the Internet.
+ * Censor special characters that could mess up message headers.
*/
case SMTPD_XFORWARD_FLAG_ADDR:
- if (*attr_value) {
- if (!valid_hostaddr(attr_value, DONT_GRIPE)) {
- state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Bad %s syntax: %s",
- XFORWARD_ADDR, attr_value);
- return (-1);
- }
- } else {
+ if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_ADDR_UNKNOWN;
+ } else {
+ neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.addr, attr_value);
break;
/*
- * HELO=hostname. Disallow characters that could mess up our own
- * Received: message headers but allow [].
+ * HELO=hostname that the host introduced itself with (not
+ * necessarily SMTP HELO). Censor special characters that could
+ * mess up message headers.
*/
case SMTPD_XFORWARD_FLAG_HELO:
- if (*attr_value) {
- if (strlen(attr_value) > VALID_HOSTNAME_LEN) {
- state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Bad %s syntax: %s",
- XFORWARD_HELO, attr_value);
- return (-1);
- }
- neuter(attr_value, "<>()\\\";:@", '?');
- } else {
+ if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_HELO_UNKNOWN;
+ } else {
+ neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.helo_name, attr_value);
break;
/*
- * PROTO=protocol name. Neutralize characters that could mess up
- * our own Received: message headers.
+ * PROTO=protocol name, not necessarily SMTP or ESMTP. Censor
+ * special characters that could mess up message headers.
*/
case SMTPD_XFORWARD_FLAG_PROTO:
- if (strlen(attr_value) > 64) {
- state->error_mask |= MAIL_ERROR_PROTOCOL;
- smtpd_chat_reply(state, "501 Bad %s syntax: %s",
- XFORWARD_PROTO, attr_value);
- return (-1);
+ if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
+ attr_value = CLIENT_PROTO_UNKNOWN;
+ } else {
+ if (strlen(attr_value) > 64) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 Bad %s syntax: %s",
+ XFORWARD_PROTO, attr_value);
+ return (-1);
+ }
+ neuter(attr_value, NEUTER_CHARACTERS, '?');
}
- neuter(attr_value, "<>()\\\";:@", '?');
UPDATE_STR(state->xforward.protocol, attr_value);
break;
if (state->xforward.namaddr)
myfree(state->xforward.namaddr);
state->xforward.namaddr =
+ IS_AVAIL_CLIENT_ADDR(state->xforward.addr) ?
concatenate(state->xforward.name, "[",
state->xforward.addr, "]",
- (char *) 0);
+ (char *) 0) : mystrdup(state->xforward.name);
}
smtpd_chat_reply(state, "250 Ok");
return (0);
#define SMTPD_PROXY_XFORWARD_IDENT (1<<4) /* message identifier */
/*
- * If forwarding client information, don't mix direct client information
- * from the current SMTP session with forwarded client information from an
- * up-stream session.
+ * If forwarding client information, don't mix information from the current
+ * SMTP session with forwarded information from an up-stream session.
*/
#define FORWARD_CLIENT_ATTR(s, a) \
(((s)->xforward.flags & SMTPD_XFORWARD_FLAG_CLIENT_MASK) ? \
#include <cleanup_user.h>
#include <mail_params.h>
#include <rec_type.h>
-#include <xtext.h>
#include <mail_proto.h>
/* Application-specific. */
* How much space does this attribute need?
*/
if (!value_available)
- value = "";
+ value = XFORWARD_UNAVAILABLE;
new_len = strlen(name) + strlen(value) + 2; /* SPACE name = value */
if (new_len > PAYLOAD_LIMIT)
- msg_warn("%s payload %s=%.10s... exceeds SMTP protocol limit",
+ msg_warn("%s command payload %s=%.10s... exceeds SMTP protocol limit",
XFORWARD_CMD, name, value);
/*
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 neuter.c name_code.c
+ attr_print_plain.c sane_connect.c neuter.c name_code.c \
+ uppercase.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) neuter.o name_code.o
+ attr_print_plain.o sane_connect.o $(STRCASE) neuter.o name_code.o \
+ uppercase.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 \
unsafe.o: unsafe.c
unsafe.o: sys_defs.h
unsafe.o: safe.h
+uppercase.o: uppercase.c
+uppercase.o: sys_defs.h
+uppercase.o: stringops.h
+uppercase.o: vstring.h
+uppercase.o: vbuf.h
username.o: username.c
username.o: sys_defs.h
username.o: username.h
extern char *printable(char *, int);
extern char *neuter(char *, const char *, int);
extern char *lowercase(char *);
+extern char *uppercase(char *);
extern char *skipblanks(const char *);
extern char *trimblanks(char *, int);
extern char *concatenate(const char *,...);
--- /dev/null
+/*++
+/* NAME
+/* uppercase 3
+/* SUMMARY
+/* map lowercase characters to uppercase
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* char *uppercase(buf)
+/* char *buf;
+/* DESCRIPTION
+/* uppercase() replaces lowercase characters in its null-terminated
+/* input by their uppercase equivalent.
+/* 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 <ctype.h>
+
+/* Utility library. */
+
+#include "stringops.h"
+
+char *uppercase(char *string)
+{
+ char *cp;
+ int ch;
+
+ for (cp = string; (ch = *cp) != 0; cp++)
+ if (ISLOWER(ch))
+ *cp = TOUPPER(ch);
+ return (string);
+}