unparsable canonical name caused the SMTPD policy client
to allocate zero-length memory, triggering an assertion
that it shouldn't do such things. File: smtpd/smtpd_check.c.
+
+20070912
+
+ Bugfix (introduced Postfix 2.4) missing initialization of
+ event mask in the event_mask_drain() routine (used by the
+ obsolete postkick(1) command). Found by Coverity. File:
+ util/events.c.
+
+20070917
+
+ Workaround: the flush daemon forces an access time update
+ for the per-destination logfile, to prevent an excessive
+ rate of delivery attempts when the queue file system is
+ mounted with "noatime". File: flush/flush.c.
+
+20070923
+
+ Cleanup: don't complain when a "corrupt" queue file is
+ deleted before it can be saved to the "corrupt" queue.
+ Files: *qmgr/qmgr_active.c.
+
+20071003
+
+ Logging: the Postfix SMTP server now logs the number of
+ bytes received after the DATA command when a connection
+ breaks before mail delivery completes. This may help finding
+ the cause of the problem: packet loss, MTU, or other. File:
+ smtpd/smtpd.c.
+
+20071004
+
+ Logging: all daemons now log the TCP port number of remote
+ SMTP or QMQP clients. The information is overruled with
+ the SMTP XCLIENT command, is propagated through SMTP-based
+ content filters with XFORWARD, and is sent to Milter
+ applications. Files: smtpd/smtpd_peer.c, smtpd/smtpd.c,
+ smtpd/smtpd_proxy.c, smtpd/smtpd_milter.c, qmqpd/qmqpd_peer.c,
+ cleanup/cleanup_milter.c, *qmgr/qmgr_message.c,
+ *qmgr/qmgr_deliver.c, smtp/smtp_proto.c, pipe/pipe.c,
+ global/deliver_request.c, global/deliver_pass.c,
+ proto/XFORWARD_README, proto/XCLIENT_README.
+
+ Feature: per-command delays in smtp-sink. File:
+ smtpstone/smtp-sink.c. Victor Duchovni.
Postfix version 2.3 introduces support for the Sendmail version 8 Milter (mail
filter) protocol. This protocol is used by applications that run outside the
MTA to inspect SMTP events (CONNECT, DISCONNECT), SMTP commands (HELO, MAIL
-FROM, etc.) as well as mail content. All this happens before mail is queued.
+FROM, etc.) as well as mail content (headers and body). All this happens before
+mail is queued.
The reason for adding Milter support to Postfix is that there exists a large
collection of applications, not only to block unwanted mail, but also to verify
-authenticity (examples: Domain keys identified mail, SenderID+SPF and Domain
-keys) or to digitally sign mail (examples: Domain keys identified mail, Domain
-keys). Having yet another Postfix-specific version of all that software is a
-poor use of human and system resources.
+authenticity (examples: DomainKeys Identified Mail (DKIM), SenderID+SPF and
+DomainKeys) or to digitally sign mail (examples: DomainKeys Identified Mail
+(DKIM), DomainKeys). Having yet another Postfix-specific version of all that
+software is a poor use of human and system resources.
Postfix version 2.4 implements all the requests of Sendmail version 8 Milter
protocols up to version 4, including message body replacement (body replacement
|{client_name} |Always |when lookup or |
| | |verification fails |
|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+ |{client_port} |Always |Client TCP port |
+ | |(Postfix >=2.5) | |
+ |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
| | |Client name from reverse |
|{client_ptr} |CONNECT, HELO, MAIL, DATA|lookup, "unknown" when |
| | |lookup fails |
xclient-command = XCLIENT 1*( SP attribute-name"="attribute-value )
- attribute-name = ( NAME | ADDR | PROTO | HELO )
+ attribute-name = ( NAME | ADDR | PORT | PROTO | HELO )
attribute-value = xtext
an IPv6 address prefixed with IPV6:, or [UNAVAILABLE] when the address
information is unavailable. Address information is not enclosed with [].
+ * The PORT attribute specifies the SMTP client TCP port number as a decimal
+ number, or [UNAVAILABLE] when the information is unavailable.
+
* The PROTO attribute specifies either SMTP or ESMTP.
* The HELO attribute specifies an SMTP HELO parameter value, or the value
attribute values. Servers that wish to interoperate with these older
implementations should be prepared to receive unencoded information.
+Note 4: Postfix implementations prior to version 2.5 do not implement the PORT
+attribute.
+
X\bXC\bCL\bLI\bIE\bEN\bNT\bT S\bSe\ber\brv\bve\ber\br r\bre\bes\bsp\bpo\bon\bns\bse\be
Upon receipt of a correctly formatted XCLIENT command, the server resets state
xforward-command = XFORWARD 1*( SP attribute-name"="attribute-value )
- attribute-name = ( NAME | ADDR | PROTO | HELO | SOURCE )
+ attribute-name = ( NAME | ADDR | PORT | PROTO | HELO | SOURCE )
attribute-value = xtext
[UNAVAILABLE] when the information is unavailable. Address information is
not enclosed with []. The address may be a non-IP address.
+ * The PORT attribute specifies an up-stream client TCP port number in
+ decimal, or [UNAVAILABLE] when the information is unavailable.
+
* The PROTO attribute specifies the mail protocol for receiving mail from the
up-stream host. This may be an SMTP or non-SMTP protocol name of up to 64
characters, or [UNAVAILABLE] when the information is unavailable.
Wish list:
+ Combine smtpd_peer.c and qmqpd_peer.c into a single function
+ that produces a client context object, and provide attribute
+ print/scan routines that pass these client context objects
+ around. With this, we no longer have to update a multiple
+ pieces of code when a client attribute is added. Ditto for
+ SASL and TLS context.
+
Make TLS_BIO_BUFSIZE run-time adjustable, to future-proof
Postfix for remote connections with MSS > 8 kbytes.
<p> Postfix version 2.3 introduces support for the Sendmail version
8 Milter (mail filter) protocol. This protocol is used by applications
that run outside the MTA to inspect SMTP events (CONNECT, DISCONNECT),
-SMTP commands (HELO, MAIL FROM, etc.) as well as mail content. All
-this happens before mail is queued. </p>
+SMTP commands (HELO, MAIL FROM, etc.) as well as mail content (headers
+and body). All this happens before mail is queued. </p>
<p> The reason for adding Milter support to Postfix is that there
exists a large collection of applications, not only to block unwanted
mail, but also to verify authenticity (examples: <a
-href="http://sourceforge.net/projects/dkim-milter/">Domain keys
-identified mail</a>, <a
+href="http://sourceforge.net/projects/dkim-milter/">DomainKeys
+Identified Mail (DKIM)</a>, <a
href="http://sourceforge.net/projects/sid-milter/">SenderID+SPF</a> and
-<a href="http://sourceforge.net/projects/dk-milter/">Domain keys</a>)
+<a href="http://sourceforge.net/projects/dk-milter/">DomainKeys</a>)
or to digitally sign mail (examples: <a
-href="http://sourceforge.net/projects/dkim-milter/">Domain keys
-identified mail</a>, <a
-href="http://sourceforge.net/projects/dk-milter/">Domain keys</a>).
+href="http://sourceforge.net/projects/dkim-milter/">DomainKeys
+Identified Mail (DKIM)</a>, <a
+href="http://sourceforge.net/projects/dk-milter/">DomainKeys</a>).
Having yet another Postfix-specific version of all that software
is a poor use of human and system resources. </p>
<tr> <td> {client_name} </td> <td> Always </td> <td> Client hostname,
"unknown" when lookup or verification fails </td> </tr>
+<tr> <td> {client_port} </td> <td> Always <br> (Postfix ≥2.5) </td>
+<td> Client TCP port </td> </tr>
+
<tr> <td> {client_ptr} </td> <td> CONNECT, HELO, MAIL, DATA </td>
<td> Client name from reverse lookup, "unknown" when lookup fails
</td> </tr>
xclient-command = XCLIENT 1*( SP attribute-name"="attribute-value )
</p>
<p>
- attribute-name = ( NAME | ADDR | PROTO | HELO )
+ attribute-name = ( NAME | ADDR | PORT | PROTO | HELO )
</p>
<p>
attribute-value = xtext
<ul>
- <li> <p> Attribute values are xtext encoded as per <a href="http://www.faqs.org/rfcs/rfc1891.html">RFC 1891</a>.
+ <li> <p> Attribute values are xtext encoded as per <a href="http://tools.ietf.org/html/rfc1891">RFC 1891</a>.
</p>
<li> <p> The NAME attribute specifies an SMTP client hostname
[UNAVAILABLE] when the address information is unavailable.
Address information is not enclosed with []. </p>
+ <li> <p> The PORT attribute specifies the SMTP client TCP port
+ number as a decimal number, or [UNAVAILABLE] when the information
+ is unavailable. </p>
+
<li> <p> The PROTO attribute specifies either SMTP or ESMTP.
</p>
with these older implementations should be prepared to receive
unencoded information. </p>
+<p> Note 4: Postfix implementations prior to version 2.5 do not
+implement the PORT attribute. </p>
+
<h2>XCLIENT Server response</h2>
<p> Upon receipt of a correctly formatted XCLIENT command, the
<h2> References </h2>
<p> Moore, K, "SMTP Service Extension for Delivery Status Notifications",
-<a href="http://www.faqs.org/rfcs/rfc1891.html">RFC 1891</a>, January 1996. </p>
+<a href="http://tools.ietf.org/html/rfc1891">RFC 1891</a>, January 1996. </p>
</body>
xforward-command = XFORWARD 1*( SP attribute-name"="attribute-value )
</p>
<p>
- attribute-name = ( NAME | ADDR | PROTO | HELO | SOURCE )
+ attribute-name = ( NAME | ADDR | PORT | PROTO | HELO | SOURCE )
</p>
<p>
attribute-value = xtext
<ul>
- <li> <p> Attribute values are xtext encoded as per <a href="http://www.faqs.org/rfcs/rfc1891.html">RFC 1891</a>.
+ <li> <p> Attribute values are xtext encoded as per <a href="http://tools.ietf.org/html/rfc1891">RFC 1891</a>.
</p>
<li> <p> The NAME attribute specifies the up-stream hostname,
Address information is not enclosed with []. The address may
be a non-IP address. </p>
+ <li> <p> The PORT attribute specifies an up-stream client TCP
+ port number in decimal, or [UNAVAILABLE] when the information
+ is unavailable. </p>
+
<li> <p> The PROTO attribute specifies the mail protocol for
receiving mail from the up-stream host. This may be an SMTP or
non-SMTP protocol name of up to 64 characters, or [UNAVAILABLE]
<h2> References </h2>
<p> Moore, K, "SMTP Service Extension for Delivery Status Notifications",
-<a href="http://www.faqs.org/rfcs/rfc1891.html">RFC 1891</a>, January 1996. </p>
+<a href="http://tools.ietf.org/html/rfc1891">RFC 1891</a>, January 1996. </p>
</body>
This is available in Postfix 2.2 and later.
+ <b>${client_port</b>}
+ This macro expands to the remote client TCP
+ port number.
+
+ This is available in Postfix 2.5 and later.
+
<b>${client_protocol</b>}
This macro expands to the remote client pro-
tocol.
Toggles the case sensitivity flag. By default,
matching is case insensitive.
- <b>x</b> (default: on)
- Toggles the extended expression syntax flag. By
- default, support for extended expression syntax is
- enabled.
-
<b>m</b> (default: off)
Toggle the multi-line mode flag. When this flag is
on, the <b>^</b> and <b>$</b> metacharacters match immediately
respectively, in addition to matching at the start
and end of the input string.
+ <b>x</b> (default: on)
+ Toggles the extended expression syntax flag. By
+ default, support for extended expression syntax is
+ enabled.
+
<b>TABLE SEARCH ORDER</b>
Patterns are applied in the order as specified in the ta-
ble, until a pattern is found that matches the input
Wait <i>delay</i> seconds before responding to a DATA com-
mand.
+ <b>-W</b> <i>command:delay[:odds]</i>
+ Wait <i>delay</i> seconds before responding to <i>command</i>.
+ If <i>odds</i> is also specified (a number between 1-99
+ inclusive), wait for a random multiple of <i>delay</i>.
+ The random multiplier is equal to the number of
+ times the program needs to roll a dice with a range
+ of 0..99 inclusive, before the dice produces a
+ result greater than or equal to <i>odds</i>.
+
[<b>inet:</b>][<i>host</i>]:<i>port</i>
Listen on network interface <i>host</i> (default: any
interface) TCP port <i>port</i>. Both <i>host</i> and <i>port</i> may be
Show the SMTP conversations.
.IP "\fB-w \fIdelay\fR"
Wait \fIdelay\fR seconds before responding to a DATA command.
+.IP "\fB-W \fIcommand:delay[:odds]\fR"
+Wait \fIdelay\fR seconds before responding to \fIcommand\fR.
+If \fIodds\fR is also specified (a number between 1-99
+inclusive), wait for a random multiple of \fIdelay\fR. The
+random multiplier is equal to the number of times the program
+needs to roll a dice with a range of 0..99 inclusive, before
+the dice produces a result greater than or equal to \fIodds\fR.
.IP [\fBinet:\fR][\fIhost\fR]:\fIport\fR
Listen on network interface \fIhost\fR (default: any interface)
TCP port \fIport\fR. Both \fIhost\fR and \fIport\fR may be
.IP "\fBi\fR (default: on)"
Toggles the case sensitivity flag. By default, matching is case
insensitive.
-.IP "\fBx\fR (default: on)"
-Toggles the extended expression syntax flag. By default, support
-for extended expression syntax is enabled.
.IP "\fBm\fR (default: off)"
Toggle the multi-line mode flag. When this flag is on, the \fB^\fR
and \fB$\fR metacharacters match immediately after and immediately
before a newline character, respectively, in addition to
matching at the start and end of the input string.
+.IP "\fBx\fR (default: on)"
+Toggles the extended expression syntax flag. By default, support
+for extended expression syntax is enabled.
.SH "TABLE SEARCH ORDER"
.na
.nf
This macro expands to the remote client hostname.
.sp
This is available in Postfix 2.2 and later.
+.IP \fB${\fBclient_port\fR}\fR
+This macro expands to the remote client TCP port number.
+.sp
+This is available in Postfix 2.5 and later.
.IP \fB${\fBclient_protocol\fR}\fR
This macro expands to the remote client protocol.
.sp
<p> Postfix version 2.3 introduces support for the Sendmail version
8 Milter (mail filter) protocol. This protocol is used by applications
that run outside the MTA to inspect SMTP events (CONNECT, DISCONNECT),
-SMTP commands (HELO, MAIL FROM, etc.) as well as mail content. All
-this happens before mail is queued. </p>
+SMTP commands (HELO, MAIL FROM, etc.) as well as mail content (headers
+and body). All this happens before mail is queued. </p>
<p> The reason for adding Milter support to Postfix is that there
exists a large collection of applications, not only to block unwanted
mail, but also to verify authenticity (examples: <a
-href="http://sourceforge.net/projects/dkim-milter/">Domain keys
-identified mail</a>, <a
+href="http://sourceforge.net/projects/dkim-milter/">DomainKeys
+Identified Mail (DKIM)</a>, <a
href="http://sourceforge.net/projects/sid-milter/">SenderID+SPF</a> and
-<a href="http://sourceforge.net/projects/dk-milter/">Domain keys</a>)
+<a href="http://sourceforge.net/projects/dk-milter/">DomainKeys</a>)
or to digitally sign mail (examples: <a
-href="http://sourceforge.net/projects/dkim-milter/">Domain keys
-identified mail</a>, <a
-href="http://sourceforge.net/projects/dk-milter/">Domain keys</a>).
+href="http://sourceforge.net/projects/dkim-milter/">DomainKeys
+Identified Mail (DKIM)</a>, <a
+href="http://sourceforge.net/projects/dk-milter/">DomainKeys</a>).
Having yet another Postfix-specific version of all that software
is a poor use of human and system resources. </p>
<tr> <td> {client_name} </td> <td> Always </td> <td> Client hostname,
"unknown" when lookup or verification fails </td> </tr>
+<tr> <td> {client_port} </td> <td> Always <br> (Postfix ≥2.5) </td>
+<td> Client TCP port </td> </tr>
+
<tr> <td> {client_ptr} </td> <td> CONNECT, HELO, MAIL, DATA </td>
<td> Client name from reverse lookup, "unknown" when lookup fails
</td> </tr>
xclient-command = XCLIENT 1*( SP attribute-name"="attribute-value )
</p>
<p>
- attribute-name = ( NAME | ADDR | PROTO | HELO )
+ attribute-name = ( NAME | ADDR | PORT | PROTO | HELO )
</p>
<p>
attribute-value = xtext
[UNAVAILABLE] when the address information is unavailable.
Address information is not enclosed with []. </p>
+ <li> <p> The PORT attribute specifies the SMTP client TCP port
+ number as a decimal number, or [UNAVAILABLE] when the information
+ is unavailable. </p>
+
<li> <p> The PROTO attribute specifies either SMTP or ESMTP.
</p>
with these older implementations should be prepared to receive
unencoded information. </p>
+<p> Note 4: Postfix implementations prior to version 2.5 do not
+implement the PORT attribute. </p>
+
<h2>XCLIENT Server response</h2>
<p> Upon receipt of a correctly formatted XCLIENT command, the
xforward-command = XFORWARD 1*( SP attribute-name"="attribute-value )
</p>
<p>
- attribute-name = ( NAME | ADDR | PROTO | HELO | SOURCE )
+ attribute-name = ( NAME | ADDR | PORT | PROTO | HELO | SOURCE )
</p>
<p>
attribute-value = xtext
Address information is not enclosed with []. The address may
be a non-IP address. </p>
+ <li> <p> The PORT attribute specifies an up-stream client TCP
+ port number in decimal, or [UNAVAILABLE] when the information
+ is unavailable. </p>
+
<li> <p> The PROTO attribute specifies the mail protocol for
receiving mail from the up-stream host. This may be an SMTP or
non-SMTP protocol name of up to 64 characters, or [UNAVAILABLE]
# .IP "\fBi\fR (default: on)"
# Toggles the case sensitivity flag. By default, matching is case
# insensitive.
-# .IP "\fBx\fR (default: on)"
-# Toggles the extended expression syntax flag. By default, support
-# for extended expression syntax is enabled.
# .IP "\fBm\fR (default: off)"
# Toggle the multi-line mode flag. When this flag is on, the \fB^\fR
# and \fB$\fR metacharacters match immediately after and immediately
# before a newline character, respectively, in addition to
# matching at the start and end of the input string.
+# .IP "\fBx\fR (default: on)"
+# Toggles the extended expression syntax flag. By default, support
+# for extended expression syntax is enabled.
# TABLE SEARCH ORDER
# .ad
# .fi
return (state->client_addr);
if (strcmp(name, S8_MAC_CLIENT_NAME) == 0)
return (state->client_name);
+ if (strcmp(name, S8_MAC_CLIENT_PORT) == 0)
+ return (state->client_port);
if (strcmp(name, S8_MAC_CLIENT_PTR) == 0)
return (state->reverse_name);
state->client_af = atoi(proto_attr);
if (state->reverse_name == 0)
state->reverse_name = state->client_name;
+ /* Compatibility with pre-2.5 queue files. */
if (state->client_port == 0)
state->client_port = NO_CLIENT_PORT;
}
#include <sys_defs.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <utime.h>
if (count > 0 && ftruncate(vstream_fileno(log), (off_t) 0) < 0)
msg_fatal("%s: truncate fast flush logfile %s: %m", myname, path);
+ /*
+ * Workaround for noatime mounts. Use futimes() if available.
+ */
+ (void) utimes(VSTREAM_PATH(log), (struct timeval *) 0);
+
/*
* Request delivery and clean up.
*/
ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, request->dsn_envid,
ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, request->dsn_ret,
ATTR_TYPE_FUNC, msg_stats_print, (void *) &request->msg_stats,
+ /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, request->client_name,
ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, request->client_addr,
+ ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_PORT, request->client_port,
ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, request->client_proto,
ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, request->client_helo,
+ /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, request->sasl_method,
ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, request->sasl_username,
ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, request->sasl_sender,
+ /* XXX Ditto if we want to pass TLS certificate info. */
ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context,
ATTR_TYPE_INT, MAIL_ATTR_RCPT_COUNT, 1,
ATTR_TYPE_END);
/* DSN *hop_status;
/* char *client_name;
/* char *client_addr;
+/* char *client_port;
/* char *client_proto;
/* char *client_helo;
/* char *sasl_method;
static VSTRING *address;
static VSTRING *client_name;
static VSTRING *client_addr;
+ static VSTRING *client_port;
static VSTRING *client_proto;
static VSTRING *client_helo;
static VSTRING *sasl_method;
address = vstring_alloc(10);
client_name = vstring_alloc(10);
client_addr = vstring_alloc(10);
+ client_port = vstring_alloc(10);
client_proto = vstring_alloc(10);
client_helo = vstring_alloc(10);
sasl_method = vstring_alloc(10);
ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, &dsn_ret,
ATTR_TYPE_FUNC, msg_stats_scan, (void *) &request->msg_stats,
+ /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, client_name,
ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, client_addr,
+ ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_PORT, client_port,
ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, client_proto,
ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, client_helo,
+ /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, sasl_method,
ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, sasl_username,
ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, sasl_sender,
+ /* XXX Ditto if we want to pass TLS certificate info. */
ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, rewrite_context,
ATTR_TYPE_INT, MAIL_ATTR_RCPT_COUNT, &rcpt_count,
- ATTR_TYPE_END) != 20) {
+ ATTR_TYPE_END) != 21) {
msg_warn("%s: error receiving common attributes", myname);
return (-1);
}
request->sender = mystrdup(vstring_str(address));
request->client_name = mystrdup(vstring_str(client_name));
request->client_addr = mystrdup(vstring_str(client_addr));
+ request->client_port = mystrdup(vstring_str(client_port));
request->client_proto = mystrdup(vstring_str(client_proto));
request->client_helo = mystrdup(vstring_str(client_helo));
request->sasl_method = mystrdup(vstring_str(sasl_method));
request->hop_status = 0;
request->client_name = 0;
request->client_addr = 0;
+ request->client_port = 0;
request->client_proto = 0;
request->client_helo = 0;
request->sasl_method = 0;
myfree(request->client_name);
if (request->client_addr)
myfree(request->client_addr);
+ if (request->client_port)
+ myfree(request->client_port);
if (request->client_proto)
myfree(request->client_proto);
if (request->client_helo)
DSN *hop_status; /* DSN status */
char *client_name; /* client hostname */
char *client_addr; /* client address */
+ char *client_port; /* client port */
char *client_proto; /* client protocol */
char *client_helo; /* helo parameter */
char *sasl_method; /* SASL method */
/* NAME
/* mail_proto 3h
/* SUMMARY
-/* mail internal IPC support
+/* mail internal and external protocol support
/* SYNOPSIS
/* #include <mail_proto.h>
/* DESCRIPTION
#define MAIL_ATTR_LOG_CLIENT_NAME "log_client_name" /* client hostname */
#define MAIL_ATTR_LOG_CLIENT_ADDR "log_client_address" /* client address */
+#define MAIL_ATTR_LOG_CLIENT_PORT "log_client_port" /* client port */
#define MAIL_ATTR_LOG_HELO_NAME "log_helo_name" /* SMTP helo name */
#define MAIL_ATTR_LOG_PROTO_NAME "log_protocol_name" /* SMTP/ESMTP/QMQP */
-#define MAIL_ATTR_LOG_ORIGIN "log_message_origin" /* hostname[address] */
+#define MAIL_ATTR_LOG_ORIGIN "log_message_origin" /* name[addr]:port */
#define MAIL_ATTR_ACT_CLIENT "client"/* client name addr */
#define MAIL_ATTR_ACT_CLIENT_NAME "client_name" /* client name */
* XCLIENT/XFORWARD in SMTP.
*/
#define XCLIENT_CMD "XCLIENT" /* XCLIENT command */
-#define XCLIENT_NAME "NAME" /* client name */
+#define XCLIENT_NAME "NAME" /* client name */
#define XCLIENT_REVERSE_NAME "REVERSE_NAME" /* reverse client name */
#ifdef FORWARD_CLIENT_NAME
#define XCLIENT_FORWARD_NAME "FORWARD_NAME" /* forward client name */
#endif
-#define XCLIENT_ADDR "ADDR" /* client address */
-#define XCLIENT_PROTO "PROTO" /* client protocol */
-#define XCLIENT_HELO "HELO" /* client helo */
+#define XCLIENT_ADDR "ADDR" /* client address */
+#define XCLIENT_PORT "PORT" /* client port */
+#define XCLIENT_PROTO "PROTO" /* client protocol */
+#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_PROTO "PROTO" /* client protocol */
-#define XFORWARD_HELO "HELO" /* client helo */
-#define XFORWARD_IDENT "IDENT" /* message identifier */
-#define XFORWARD_DOMAIN "SOURCE" /* origin type */
-#define XFORWARD_DOM_LOCAL "LOCAL" /* local origin */
-#define XFORWARD_DOM_REMOTE "REMOTE" /* remote origin */
+#define XFORWARD_NAME "NAME" /* client name */
+#define XFORWARD_ADDR "ADDR" /* client address */
+#define XFORWARD_PORT "PORT" /* client port */
+#define XFORWARD_PROTO "PROTO" /* client protocol */
+#define XFORWARD_HELO "HELO" /* client helo */
+#define XFORWARD_IDENT "IDENT" /* message identifier */
+#define XFORWARD_DOMAIN "SOURCE"/* origin type */
+#define XFORWARD_DOM_LOCAL "LOCAL" /* local origin */
+#define XFORWARD_DOM_REMOTE "REMOTE"/* remote origin */
#define XFORWARD_UNAVAILABLE "[UNAVAILABLE]" /* attribute unavailable */
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20070911"
+#define MAIL_RELEASE_DATE "20071004"
#define MAIL_VERSION_NUMBER "2.5"
#ifdef SNAPSHOT
#define S8_MAC_CLIENT_ADDR "{client_addr}"
#define S8_MAC_CLIENT_CONN "{client_connections}"
#define S8_MAC_CLIENT_NAME "{client_name}"
+#define S8_MAC_CLIENT_PORT "{client_port}"
#define S8_MAC_CLIENT_PTR "{client_ptr}"
#define S8_MAC_CLIENT_RES "{client_resolve}"
print_addr = inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf));
if (print_addr == 0)
print_addr = strerror(errno);
- printf("AF_INET (%s)\n", print_addr);
+ printf("AF_INET (%s:%d)\n", print_addr, ntohs(sin->sin_port));
}
break;
#ifdef HAS_IPV6
print_addr = inet_ntop(AF_INET, &sin6->sin6_addr, buf, sizeof(buf));
if (print_addr == 0)
print_addr = strerror(errno);
- printf("AF_INET6 (%s)\n", print_addr);
+ printf("AF_INET6 (%s:%d)\n", print_addr, ntohs(sin6->sin6_port));
}
break;
#endif
long rcpt_offset; /* more recipients here */
char *client_name; /* client hostname */
char *client_addr; /* client address */
+ char *client_port; /* client port */
char *client_proto; /* client protocol */
char *client_helo; /* helo parameter */
char *sasl_method; /* SASL method */
if (errno != ENOENT)
msg_fatal("%s: save corrupt file queue %s id %s: %m",
myname, MAIL_QUEUE_ACTIVE, queue_id);
- msg_warn("%s: save corrupt file queue %s id %s: %m",
- myname, MAIL_QUEUE_ACTIVE, queue_id);
} else {
msg_warn("saving corrupt file \"%s\" from queue \"%s\" to queue \"%s\"",
queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT);
ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, message->dsn_envid,
ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, message->dsn_ret,
ATTR_TYPE_FUNC, msg_stats_print, (void *) &stats,
+ /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, message->client_name,
ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, message->client_addr,
+ ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_PORT, message->client_port,
ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, message->client_proto,
ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, message->client_helo,
+ /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, message->sasl_method,
ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, message->sasl_username,
ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, message->sasl_sender,
+ /* XXX Ditto if we want to pass TLS certificate info. */
ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, message->rewrite_context,
ATTR_TYPE_INT, MAIL_ATTR_RCPT_COUNT, list.len,
ATTR_TYPE_END);
message->verp_delims = 0;
message->client_name = 0;
message->client_addr = 0;
+ message->client_port = 0;
message->client_proto = 0;
message->client_helo = 0;
message->sasl_method = 0;
if (message->client_addr != 0)
myfree(message->client_addr);
message->client_addr = mystrdup(value);
+ } else if (strcmp(name, MAIL_ATTR_LOG_CLIENT_PORT) == 0) {
+ if (message->client_port != 0)
+ myfree(message->client_port);
+ message->client_port = mystrdup(value);
} else if (strcmp(name, MAIL_ATTR_LOG_PROTO_NAME) == 0) {
if (message->client_proto != 0)
myfree(message->client_proto);
message->client_name = mystrdup("");
if (message->client_addr == 0)
message->client_addr = mystrdup("");
+ if (message->client_port == 0)
+ message->client_port = mystrdup("");
if (message->client_proto == 0)
message->client_proto = mystrdup("");
if (message->client_helo == 0)
myfree(message->client_name);
if (message->client_addr)
myfree(message->client_addr);
+ if (message->client_port)
+ myfree(message->client_port);
if (message->client_proto)
myfree(message->client_proto);
if (message->client_helo)
/* This macro expands to the remote client hostname.
/* .sp
/* This is available in Postfix 2.2 and later.
+/* .IP \fB${\fBclient_port\fR}\fR
+/* This macro expands to the remote client TCP port number.
+/* .sp
+/* This is available in Postfix 2.5 and later.
/* .IP \fB${\fBclient_protocol\fR}\fR
/* This macro expands to the remote client protocol.
/* .sp
#define PIPE_DICT_SIZE "size" /* key */
#define PIPE_DICT_CLIENT_ADDR "client_address" /* key */
#define PIPE_DICT_CLIENT_NAME "client_hostname" /* key */
+#define PIPE_DICT_CLIENT_PORT "client_port" /* key */
#define PIPE_DICT_CLIENT_PROTO "client_protocol" /* key */
#define PIPE_DICT_CLIENT_HELO "client_helo" /* key */
#define PIPE_DICT_SASL_METHOD "sasl_method" /* key */
PIPE_DICT_SIZE, 0,
PIPE_DICT_CLIENT_ADDR, 0,
PIPE_DICT_CLIENT_NAME, 0,
+ PIPE_DICT_CLIENT_PORT, 0,
PIPE_DICT_CLIENT_PROTO, 0,
PIPE_DICT_CLIENT_HELO, 0,
PIPE_DICT_SASL_METHOD, 0,
request->client_helo);
dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_NAME,
request->client_name);
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_PORT,
+ request->client_port);
dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_PROTO,
request->client_proto);
dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_METHOD,
long rcpt_offset; /* more recipients here */
char *client_name; /* client hostname */
char *client_addr; /* client address */
+ char *client_port; /* client port */
char *client_proto; /* client protocol */
char *client_helo; /* helo parameter */
char *sasl_method; /* SASL method */
if (errno != ENOENT)
msg_fatal("%s: save corrupt file queue %s id %s: %m",
myname, MAIL_QUEUE_ACTIVE, queue_id);
- msg_warn("%s: save corrupt file queue %s id %s: %m",
- myname, MAIL_QUEUE_ACTIVE, queue_id);
} else {
msg_warn("saving corrupt file \"%s\" from queue \"%s\" to queue \"%s\"",
queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT);
ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, message->dsn_envid,
ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, message->dsn_ret,
ATTR_TYPE_FUNC, msg_stats_print, (void *) &stats,
+ /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, message->client_name,
ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, message->client_addr,
+ ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_PORT, message->client_port,
ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, message->client_proto,
ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, message->client_helo,
+ /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, message->sasl_method,
ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, message->sasl_username,
ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, message->sasl_sender,
+ /* XXX Ditto if we want to pass TLS certificate info. */
ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, message->rewrite_context,
ATTR_TYPE_INT, MAIL_ATTR_RCPT_COUNT, list.len,
ATTR_TYPE_END);
message->verp_delims = 0;
message->client_name = 0;
message->client_addr = 0;
+ message->client_port = 0;
message->client_proto = 0;
message->client_helo = 0;
message->sasl_method = 0;
if (message->client_addr != 0)
myfree(message->client_addr);
message->client_addr = mystrdup(value);
+ } else if (strcmp(name, MAIL_ATTR_LOG_CLIENT_PORT) == 0) {
+ if (message->client_port != 0)
+ myfree(message->client_port);
+ message->client_port = mystrdup(value);
} else if (strcmp(name, MAIL_ATTR_LOG_PROTO_NAME) == 0) {
if (message->client_proto != 0)
myfree(message->client_proto);
message->client_name = mystrdup("");
if (message->client_addr == 0)
message->client_addr = mystrdup("");
+ if (message->client_port == 0)
+ message->client_port = mystrdup("");
if (message->client_proto == 0)
message->client_proto = mystrdup("");
if (message->client_helo == 0)
myfree(message->client_name);
if (message->client_addr)
myfree(message->client_addr);
+ if (message->client_port)
+ myfree(message->client_port);
if (message->client_proto)
myfree(message->client_proto);
if (message->client_helo)
if (IS_AVAIL_CLIENT_ADDR(state->addr))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_LOG_CLIENT_ADDR, state->rfc_addr);
+ if (IS_AVAIL_CLIENT_PORT(state->port))
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_LOG_CLIENT_PORT, state->port);
if (IS_AVAIL_CLIENT_NAMADDR(state->namaddr))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_LOG_ORIGIN, state->namaddr);
MAIL_ATTR_ACT_CLIENT_NAME, state->name);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ACT_CLIENT_ADDR, state->rfc_addr);
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_ACT_CLIENT_PORT, state->port);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%u",
MAIL_ATTR_ACT_CLIENT_AF, state->addr_family);
}
struct timeval arrival_time; /* start of session */
char *name; /* client name */
char *addr; /* client IP address */
- char *namaddr; /* name[addr] */
+ char *port; /* client TCP port */
+ char *namaddr; /* name[addr]:port */
char *rfc_addr; /* RFC 2821 client IP address */
int addr_family; /* address family */
char *queue_id; /* queue file ID */
#define CLIENT_NAME_UNKNOWN CLIENT_ATTR_UNKNOWN
#define CLIENT_ADDR_UNKNOWN CLIENT_ATTR_UNKNOWN
+#define CLIENT_PORT_UNKNOWN CLIENT_ATTR_UNKNOWN
#define CLIENT_NAMADDR_UNKNOWN CLIENT_ATTR_UNKNOWN
#define IS_AVAIL_CLIENT_ATTR(v) ((v) && strcmp((v), CLIENT_ATTR_UNKNOWN))
#define IS_AVAIL_CLIENT_NAME(v) IS_AVAIL_CLIENT_ATTR(v)
#define IS_AVAIL_CLIENT_ADDR(v) IS_AVAIL_CLIENT_ATTR(v)
+#define IS_AVAIL_CLIENT_PORT(v) IS_AVAIL_CLIENT_ATTR(v)
#define IS_AVAIL_CLIENT_NAMADDR(v) IS_AVAIL_CLIENT_ATTR(v)
/*
/* .IP addr
/* Printable representation of the client address.
/* .IP namaddr
-/* String of the form: "name[addr]".
+/* String of the form: "name[addr]:port".
/* .PP
/* qmqpd_peer_reset() releases memory allocated by qmqpd_peer_init().
/* LICENSE
state->addr = mystrdup(CLIENT_ADDR_UNKNOWN);
state->rfc_addr = mystrdup(CLIENT_ADDR_UNKNOWN);
state->addr_family = AF_UNSPEC;
+ state->port = mystrdup(CLIENT_PORT_UNKNOWN);
}
/*
)) {
MAI_HOSTNAME_STR client_name;
MAI_HOSTADDR_STR client_addr;
+ MAI_SERVPORT_STR client_port;
int aierr;
char *colonp;
* Convert the client address to printable form.
*/
if ((aierr = sockaddr_to_hostaddr(sa, sa_length, &client_addr,
- (MAI_SERVPORT_STR *) 0, 0)) != 0)
- msg_fatal("%s: cannot convert client address to string: %s",
+ &client_port, 0)) != 0)
+ msg_fatal("%s: cannot convert client address/port to string: %s",
myname, MAI_STRERROR(aierr));
+ state->port = mystrdup(client_port.buf);
/*
* We convert IPv4-in-IPv6 address to 'true' IPv4 address early on,
state->addr = mystrdup("127.0.0.1"); /* XXX bogus. */
state->rfc_addr = mystrdup("127.0.0.1");/* XXX bogus. */
state->addr_family = AF_UNSPEC;
+ state->port = mystrdup("0"); /* XXX bogus. */
}
/*
- * Do the name[addr] formatting for pretty reports.
+ * Do the name[addr]:port formatting for pretty reports.
*/
state->namaddr =
- concatenate(state->name, "[", state->addr, "]", (char *) 0);
+ concatenate(state->name, "[", state->addr,
+ "]:", state->port, (char *) 0);
}
/* qmqpd_peer_reset - destroy peer information */
myfree(state->addr);
myfree(state->namaddr);
myfree(state->rfc_addr);
+ myfree(state->port);
}
#define SMTP_FEATURE_DSN (1<<15) /* DSN supported */
#define SMTP_FEATURE_PIX_NO_ESMTP (1<<16) /* PIX smtp fixup mode */
#define SMTP_FEATURE_PIX_DELAY_DOTCRLF (1<<17) /* PIX smtp fixup mode */
+#define SMTP_FEATURE_XFORWARD_PORT (1<<18)
/*
* Features that passivate under the endpoint.
static NAME_CODE xforward_features[] = {
XFORWARD_NAME, SMTP_FEATURE_XFORWARD_NAME,
XFORWARD_ADDR, SMTP_FEATURE_XFORWARD_ADDR,
+ XFORWARD_PORT, SMTP_FEATURE_XFORWARD_PORT,
XFORWARD_PROTO, SMTP_FEATURE_XFORWARD_PROTO,
XFORWARD_HELO, SMTP_FEATURE_XFORWARD_HELO,
XFORWARD_DOMAIN, SMTP_FEATURE_XFORWARD_DOMAIN,
DEL_REQ_ATTR_AVAIL(request->client_addr) ?
request->client_addr : XFORWARD_UNAVAILABLE, "");
}
+ if (session->features & SMTP_FEATURE_XFORWARD_PORT) {
+ vstring_strcat(next_command, " " XFORWARD_PORT "=");
+ xtext_quote_append(next_command,
+ DEL_REQ_ATTR_AVAIL(request->client_port) ?
+ request->client_port : XFORWARD_UNAVAILABLE, "");
+ }
if (session->send_proto_helo)
next_state = SMTP_STATE_XFORWARD_PROTO_HELO;
else
ENQUEUE_FIX_REPLY(state, reply_buf, XCLIENT_CMD
" " XCLIENT_NAME " " XCLIENT_ADDR
" " XCLIENT_PROTO " " XCLIENT_HELO
- " " XCLIENT_REVERSE_NAME);
+ " " XCLIENT_REVERSE_NAME " " XCLIENT_PORT);
if ((discard_mask & EHLO_MASK_XFORWARD) == 0)
if (xforward_allowed)
ENQUEUE_FIX_REPLY(state, reply_buf, XFORWARD_CMD
" " XFORWARD_NAME " " XFORWARD_ADDR
" " XFORWARD_PROTO " " XFORWARD_HELO
- " " XFORWARD_DOMAIN);
+ " " XFORWARD_DOMAIN " " XFORWARD_PORT);
if ((discard_mask & EHLO_MASK_ENHANCEDSTATUSCODES) == 0)
ENQUEUE_FIX_REPLY(state, reply_buf, "ENHANCEDSTATUSCODES");
if ((discard_mask & EHLO_MASK_8BITMIME) == 0)
if (IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_LOG_CLIENT_ADDR, FORWARD_ADDR(state));
+ if (IS_AVAIL_CLIENT_PORT(FORWARD_PORT(state)))
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_LOG_CLIENT_PORT, FORWARD_PORT(state));
if (IS_AVAIL_CLIENT_NAMADDR(FORWARD_NAMADDR(state)))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_LOG_ORIGIN, FORWARD_NAMADDR(state));
MAIL_ATTR_ACT_REVERSE_CLIENT_NAME, state->reverse_name);
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ACT_CLIENT_ADDR, state->addr);
+ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_ACT_CLIENT_PORT, state->port);
if (state->helo_name)
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ACT_HELO_NAME, state->helo_name);
update_namaddr = 1;
}
+ /*
+ * PORT=substitute SMTP client port number.
+ */
+ else if (STREQ(attr_name, XCLIENT_PORT)) {
+ if (STREQ(attr_value, XCLIENT_UNAVAILABLE)) {
+ attr_value = CLIENT_PORT_UNKNOWN;
+ } else {
+ if (!alldig(attr_value)
+ || strlen(attr_value) > sizeof("65535") - 1) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
+ XCLIENT_PORT, attr_value);
+ return (-1);
+ }
+ }
+ UPDATE_STR(state->port, attr_value);
+ update_namaddr = 1;
+ }
+
/*
* HELO=substitute SMTP client HELO parameter. Censor special
* characters that could mess up message headers.
if (state->namaddr)
myfree(state->namaddr);
state->namaddr =
- concatenate(state->name, "[", state->addr, "]", (char *) 0);
+ concatenate(state->name, "[", state->addr, "]:",
+ state->port, (char *) 0);
}
/*
static NAME_CODE xforward_flags[] = {
XFORWARD_NAME, SMTPD_STATE_XFORWARD_NAME,
XFORWARD_ADDR, SMTPD_STATE_XFORWARD_ADDR,
+ XFORWARD_PORT, SMTPD_STATE_XFORWARD_PORT,
XFORWARD_PROTO, SMTPD_STATE_XFORWARD_PROTO,
XFORWARD_HELO, SMTPD_STATE_XFORWARD_HELO,
XFORWARD_DOMAIN, SMTPD_STATE_XFORWARD_DOMAIN,
UPDATE_STR(state->xforward.rfc_addr, attr_value);
break;
+ /*
+ * PORT=up-stream port number.
+ */
+ case SMTPD_STATE_XFORWARD_PORT:
+ if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
+ attr_value = CLIENT_PORT_UNKNOWN;
+ } else {
+ if (!alldig(attr_value)
+ || strlen(attr_value) > sizeof("65535") - 1) {
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
+ smtpd_chat_reply(state, "501 5.5.4 Bad %s syntax: %s",
+ XFORWARD_PORT, attr_value);
+ return (-1);
+ }
+ }
+ UPDATE_STR(state->xforward.port, attr_value);
+ break;
+
/*
* HELO=hostname that the up-stream MTA introduced itself with
* (not necessarily SMTP HELO). Censor special characters that
state->xforward.namaddr =
IS_AVAIL_CLIENT_ADDR(state->xforward.addr) ?
concatenate(state->xforward.name, "[",
- state->xforward.addr, "]",
+ state->xforward.addr, "]:",
+ state->xforward.port,
(char *) 0) : mystrdup(state->xforward.name);
}
smtpd_chat_reply(state, "250 2.0.0 Ok");
* HELO or EHLO, but we do change the feature list that is announced
* in the EHLO response.
*/
-#define XXX_NO_PORT "0"
else {
err = 0;
if (smtpd_milters != 0 && SMTPD_STAND_ALONE(state) == 0) {
milter_macro_callback(smtpd_milters, smtpd_milter_eval,
(void *) state);
if ((err = milter_conn_event(smtpd_milters, state->name,
- state->addr, XXX_NO_PORT,
+ state->addr, state->port,
state->addr_family)) != 0)
err = check_milter_reply(state, err);
}
* Log abnormal session termination, in case postmaster notification has
* been turned off. In the log, indicate the last recognized state before
* things went wrong. Don't complain about clients that go away without
- * sending QUIT.
+ * sending QUIT. Log the byte count after DATA to help diagnose MTU
+ * troubles.
*/
- if (state->reason && state->where
- && (strcmp(state->where, SMTPD_AFTER_DOT)
- || strcmp(state->reason, REASON_LOST_CONNECTION)))
- msg_info("%s after %s from %s[%s]",
- state->reason, state->where, state->name, state->addr);
+ if (state->reason && state->where) {
+ if (strcmp(state->where, SMTPD_CMD_DATA) == 0) {
+ msg_info("%s after %s (%lu bytes) from %s[%s]",
+ state->reason, state->where, (long) state->act_size,
+ state->name, state->addr);
+ } else if (strcmp(state->where, SMTPD_AFTER_DOT)
+ || strcmp(state->reason, REASON_LOST_CONNECTION)) {
+ msg_info("%s after %s from %s[%s]",
+ state->reason, state->where, state->name, state->addr);
+ }
+ }
/*
* Cleanup whatever information the client gave us during the SMTP
* machines.
*/
smtpd_state_init(&state, stream, service);
- msg_info("connect from %s[%s]", state.name, state.addr);
+ msg_info("connect from %s", state.namaddr);
/*
* With TLS wrapper mode, we run on a dedicated port and turn on TLS
* After the client has gone away, clean up whatever we have set up at
* connection time.
*/
- msg_info("disconnect from %s[%s]", state.name, state.addr);
+ msg_info("disconnect from %s", state.namaddr);
smtpd_state_reset(&state);
debug_peer_restore();
}
int flags; /* XFORWARD server state */
char *name; /* name for access control */
char *addr; /* address for access control */
- char *namaddr; /* name[address] */
+ char *port; /* port for logging */
+ char *namaddr; /* name[address]:port */
char *rfc_addr; /* address for RFC 2821 */
char *protocol; /* email protocol */
char *helo_name; /* helo/ehlo parameter */
char *name; /* verified client hostname */
char *reverse_name; /* unverified client hostname */
char *addr; /* client host address string */
- char *namaddr; /* combined name and address */
+ char *port; /* port for logging */
+ char *namaddr; /* name[address]:port */
char *rfc_addr; /* address for RFC 2821 */
int addr_family; /* address family */
struct sockaddr_storage sockaddr; /* binary client endpoint */
#define SMTPD_STATE_XFORWARD_HELO (1<<4) /* client helo received */
#define SMTPD_STATE_XFORWARD_IDENT (1<<5) /* message identifier */
#define SMTPD_STATE_XFORWARD_DOMAIN (1<<6) /* message identifier */
+#define SMTPD_STATE_XFORWARD_PORT (1<<7) /* client port received */
#define SMTPD_STATE_XFORWARD_CLIENT_MASK \
(SMTPD_STATE_XFORWARD_NAME | SMTPD_STATE_XFORWARD_ADDR \
- | SMTPD_STATE_XFORWARD_PROTO | SMTPD_STATE_XFORWARD_HELO)
+ | SMTPD_STATE_XFORWARD_PROTO | SMTPD_STATE_XFORWARD_HELO \
+ | SMTPD_STATE_XFORWARD_PORT)
extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *, const char *);
extern void smtpd_state_reset(SMTPD_STATE *);
#define CLIENT_NAME_UNKNOWN CLIENT_ATTR_UNKNOWN
#define CLIENT_ADDR_UNKNOWN CLIENT_ATTR_UNKNOWN
+#define CLIENT_PORT_UNKNOWN CLIENT_ATTR_UNKNOWN
#define CLIENT_NAMADDR_UNKNOWN CLIENT_ATTR_UNKNOWN
#define CLIENT_HELO_UNKNOWN 0
#define CLIENT_PROTO_UNKNOWN CLIENT_ATTR_UNKNOWN
#define IS_AVAIL_CLIENT_NAME(v) IS_AVAIL_CLIENT_ATTR(v)
#define IS_AVAIL_CLIENT_ADDR(v) IS_AVAIL_CLIENT_ATTR(v)
+#define IS_AVAIL_CLIENT_PORT(v) IS_AVAIL_CLIENT_ATTR(v)
#define IS_AVAIL_CLIENT_NAMADDR(v) IS_AVAIL_CLIENT_ATTR(v)
#define IS_AVAIL_CLIENT_HELO(v) ((v) != 0)
#define IS_AVAIL_CLIENT_PROTO(v) IS_AVAIL_CLIENT_ATTR(v)
#define FORWARD_NAMADDR(s) FORWARD_CLIENT_ATTR((s), namaddr)
#define FORWARD_PROTO(s) FORWARD_CLIENT_ATTR((s), protocol)
#define FORWARD_HELO(s) FORWARD_CLIENT_ATTR((s), helo_name)
+#define FORWARD_PORT(s) FORWARD_CLIENT_ATTR((s), port)
#define FORWARD_IDENT(s) \
(((s)->xforward.flags & SMTPD_STATE_XFORWARD_IDENT) ? \
return (var_myhostname);
if (strcmp(name, S8_MAC_CLIENT_ADDR) == 0)
return (state->rfc_addr);
+ if (strcmp(name, S8_MAC_CLIENT_PORT) == 0)
+ return (state->port);
if (strcmp(name, S8_MAC_CLIENT_CONN) == 0) {
if (state->expand_buf == 0)
state->expand_buf = vstring_alloc(10);
/* .IP addr
/* Printable representation of the client address.
/* .IP namaddr
-/* String of the form: "name[addr]".
+/* String of the form: "name[addr]:port".
/* .IP rfc_addr
/* String of the form "ipv4addr" or "ipv6:ipv6addr" for use
/* in Received: message headers.
state->addr_family = AF_UNSPEC;
state->name_status = SMTPD_PEER_CODE_PERM;
state->reverse_name_status = SMTPD_PEER_CODE_PERM;
+ state->port = mystrdup(CLIENT_PORT_UNKNOWN);
}
/*
)) {
MAI_HOSTNAME_STR client_name;
MAI_HOSTADDR_STR client_addr;
+ MAI_SERVPORT_STR client_port;
int aierr;
char *colonp;
* Convert the client address to printable form.
*/
if ((aierr = sockaddr_to_hostaddr(sa, sa_length, &client_addr,
- (MAI_SERVPORT_STR *) 0, 0)) != 0)
- msg_fatal("%s: cannot convert client address to string: %s",
+ &client_port, 0)) != 0)
+ msg_fatal("%s: cannot convert client address/port to string: %s",
myname, MAI_STRERROR(aierr));
+ state->port = mystrdup(client_port.buf);
/*
* We convert IPv4-in-IPv6 address to 'true' IPv4 address early on,
state->addr_family = AF_UNSPEC;
state->name_status = SMTPD_PEER_CODE_OK;
state->reverse_name_status = SMTPD_PEER_CODE_OK;
+ state->port = mystrdup("0"); /* XXX bogus. */
}
/*
- * Do the name[addr] formatting for pretty reports.
+ * Do the name[addr]:port formatting for pretty reports.
*/
state->namaddr =
- concatenate(state->name, "[", state->addr, "]", (char *) 0);
+ concatenate(state->name, "[", state->addr,
+ "]:", state->port, (char *) 0);
}
/* smtpd_peer_reset - destroy peer information */
myfree(state->addr);
myfree(state->namaddr);
myfree(state->rfc_addr);
+ myfree(state->port);
}
#define SMTPD_PROXY_XFORWARD_HELO (1<<3) /* client helo */
#define SMTPD_PROXY_XFORWARD_IDENT (1<<4) /* message identifier */
#define SMTPD_PROXY_XFORWARD_DOMAIN (1<<5) /* origin type */
+#define SMTPD_PROXY_XFORWARD_PORT (1<<6) /* client port */
/*
* SLMs.
static NAME_CODE xforward_features[] = {
XFORWARD_NAME, SMTPD_PROXY_XFORWARD_NAME,
XFORWARD_ADDR, SMTPD_PROXY_XFORWARD_ADDR,
+ XFORWARD_PORT, SMTPD_PROXY_XFORWARD_PORT,
XFORWARD_PROTO, SMTPD_PROXY_XFORWARD_PROTO,
XFORWARD_HELO, SMTPD_PROXY_XFORWARD_HELO,
XFORWARD_DOMAIN, SMTPD_PROXY_XFORWARD_DOMAIN,
bad = smtpd_xforward(state, buf, XFORWARD_ADDR,
IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)),
FORWARD_ADDR(state));
+ if (bad == 0
+ && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_PORT))
+ bad = smtpd_xforward(state, buf, XFORWARD_PORT,
+ IS_AVAIL_CLIENT_PORT(FORWARD_PORT(state)),
+ FORWARD_PORT(state));
if (bad == 0
&& (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_HELO))
bad = smtpd_xforward(state, buf, XFORWARD_HELO,
state->xforward.flags = 0;
state->xforward.name = 0;
state->xforward.addr = 0;
+ state->xforward.port = 0;
state->xforward.namaddr = 0;
state->xforward.protocol = 0;
state->xforward.helo_name = 0;
state->xforward.flags = SMTPD_STATE_XFORWARD_INIT;
state->xforward.name = mystrdup(CLIENT_NAME_UNKNOWN);
state->xforward.addr = mystrdup(CLIENT_ADDR_UNKNOWN);
+ state->xforward.port = mystrdup(CLIENT_PORT_UNKNOWN);
state->xforward.namaddr = mystrdup(CLIENT_NAMADDR_UNKNOWN);
state->xforward.rfc_addr = mystrdup(CLIENT_ADDR_UNKNOWN);
/* Leave helo at zero. */
state->xforward.flags = 0;
FREE_AND_WIPE(state->xforward.name);
FREE_AND_WIPE(state->xforward.addr);
+ FREE_AND_WIPE(state->xforward.port);
FREE_AND_WIPE(state->xforward.namaddr);
FREE_AND_WIPE(state->xforward.rfc_addr);
FREE_AND_WIPE(state->xforward.protocol);
/* Show the SMTP conversations.
/* .IP "\fB-w \fIdelay\fR"
/* Wait \fIdelay\fR seconds before responding to a DATA command.
+/* .IP "\fB-W \fIcommand:delay[:odds]\fR"
+/* Wait \fIdelay\fR seconds before responding to \fIcommand\fR.
+/* If \fIodds\fR is also specified (a number between 1-99
+/* inclusive), wait for a random multiple of \fIdelay\fR. The
+/* random multiplier is equal to the number of times the program
+/* needs to roll a dice with a range of 0..99 inclusive, before
+/* the dice produces a result greater than or equal to \fIodds\fR.
/* .IP [\fBinet:\fR][\fIhost\fR]:\fIport\fR
/* Listen on network interface \fIhost\fR (default: any interface)
/* TCP port \fIport\fR. Both \fIhost\fR and \fIport\fR may be
time_t start_time; /* MAIL command time */
int id; /* pseudo-random */
VSTREAM *dump_file; /* dump file or null */
+ void (*delayed_response) (struct SINK_STATE *state, const char *);
+ char *delayed_args;
} SINK_STATE;
#define ST_ANY 0
static int max_quit_count;
static int disable_pipelining;
static int disable_8bitmime;
-static int fixed_delay;
static int disable_esmtp;
static int enable_lmtp;
static int pretend_pix;
mail_file_finish_header(state);
}
-/* data_event - delayed response to DATA command */
-
-static void data_event(int unused_event, char *context)
-{
- SINK_STATE *state = (SINK_STATE *) context;
-
- data_response(state, "");
- /* Resume input event handling after the delayed DATA response. */
- event_enable_read(vstream_fileno(state->stream), read_event, (char *) state);
- event_request_timer(read_timeout, (char *) state, var_tmout);
-}
-
/* dot_resp_hard - hard error response to . command */
static void dot_resp_hard(SINK_STATE *state)
smtp_flush(state->stream);
}
+/* delay_event - delayed command response */
+
+static void delay_event(int unused_event, char *context)
+{
+ SINK_STATE *state = (SINK_STATE *) context;
+
+ state->delayed_response(state, state->delayed_args);
+ myfree(state->delayed_args);
+ state->delayed_args = 0;
+
+ if (state->delayed_response == quit_response) {
+ disconnect(state);
+ return;
+ }
+ /* Resume input event handling after the delayed response. */
+ event_enable_read(vstream_fileno(state->stream), read_event, (char *) state);
+ event_request_timer(read_timeout, (char *) state, var_tmout);
+}
+
/* data_read - read data from socket */
static int data_read(SINK_STATE *state)
void (*hard_response) (SINK_STATE *);
void (*soft_response) (SINK_STATE *);
int flags;
+ int delay;
+ int delay_odds;
} SINK_COMMAND;
#define FLAG_ENABLE (1<<0) /* command is enabled */
#define FLAG_DISCONNECT (1<<4) /* disconnect */
static SINK_COMMAND command_table[] = {
- "connect", conn_response, hard_err_resp, soft_err_resp, 0,
- "helo", helo_response, hard_err_resp, soft_err_resp, 0,
- "ehlo", ehlo_response, hard_err_resp, soft_err_resp, 0,
- "lhlo", ehlo_response, hard_err_resp, soft_err_resp, 0,
- "xclient", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
- "xforward", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
- "auth", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
- "mail", mail_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
- "rcpt", rcpt_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
- "data", data_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
- ".", dot_response, dot_resp_hard, dot_resp_soft, FLAG_ENABLE,
- "rset", rset_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
- "noop", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
- "vrfy", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
- "quit", quit_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
+ "connect", conn_response, hard_err_resp, soft_err_resp, 0, 0, 0,
+ "helo", helo_response, hard_err_resp, soft_err_resp, 0, 0, 0,
+ "ehlo", ehlo_response, hard_err_resp, soft_err_resp, 0, 0, 0,
+ "lhlo", ehlo_response, hard_err_resp, soft_err_resp, 0, 0, 0,
+ "xclient", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
+ "xforward", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
+ "auth", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
+ "mail", mail_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
+ "rcpt", rcpt_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
+ "data", data_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
+ ".", dot_response, dot_resp_hard, dot_resp_soft, FLAG_ENABLE, 0, 0,
+ "rset", rset_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
+ "noop", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
+ "vrfy", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
+ "quit", quit_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, 0, 0,
0,
};
myfree(saved_cmds);
}
+/* set_cmd_delay - set per-command delay */
+
+static void set_cmd_delay(const char *cmd, int delay, int odds)
+{
+ SINK_COMMAND *cmdp;
+
+ for (cmdp = command_table; cmdp->name != 0; cmdp++)
+ if (strcasecmp(cmd, cmdp->name) == 0)
+ break;
+ if (cmdp->name == 0)
+ msg_fatal("unknown command: %s", cmd);
+
+ if (delay <= 0)
+ msg_fatal("non-positive '%s' delay", cmd);
+ if (odds < 0 || odds > 99)
+ msg_fatal("delay odds for '%s' out of range", cmd);
+
+ cmdp->delay = delay;
+ cmdp->delay_odds = odds;
+}
+
+/* set_cmd_delay_arg - set per-command delay from option argument */
+
+static void set_cmd_delay_arg(char *arg)
+{
+ char *cp;
+ char *saved_arg;
+ char *cmd;
+ char *delay;
+ char *odds;
+
+ saved_arg = cp = mystrdup(arg);
+ cmd = mystrtok(&cp, ":");
+ delay = mystrtok(&cp, ":");
+ if (cmd == 0 || delay == 0)
+ msg_fatal("invalid command delay argument: %s", arg);
+ odds = mystrtok(&cp, "");
+ set_cmd_delay(cmd, atoi(delay), odds ? atoi(odds) : 0);
+ myfree(saved_arg);
+}
+
/* command_resp - respond to command */
static int command_resp(SINK_STATE *state, SINK_COMMAND *cmdp,
cmdp->soft_response(state);
return (0);
}
- if (cmdp->response == data_response && fixed_delay > 0) {
- /* Suspend input event handling while delaying the DATA response. */
+ if (cmdp->delay > 0) {
+ int delay = cmdp->delay;
+
+ if (cmdp->delay_odds > 0)
+ for (delay = 0;
+ ((int) (100.0 * rand() / (RAND_MAX + 1.0))) < cmdp->delay_odds;
+ delay += cmdp->delay)
+ /* NOP */ ;
+ /* Suspend input event handling while delaying the command response. */
event_disable_readwrite(vstream_fileno(state->stream));
event_cancel_timer(read_timeout, (char *) state);
- event_request_timer(data_event, (char *) state, fixed_delay);
+ event_request_timer(delay_event, (char *) state, delay);
+ state->delayed_response = cmdp->response;
+ state->delayed_args = mystrdup(args);
} else {
cmdp->response(state, args);
if (cmdp->response == quit_response)
myfree(state->helo_args);
/* Delete incomplete mail transaction. */
mail_cmd_reset(state);
+ if (state->delayed_args)
+ myfree(state->delayed_args);
myfree((char *) state);
if (max_quit_count > 0 && quit_count >= max_quit_count)
exit(0);
smtp_timeout_setup(state->stream, var_tmout);
state->in_mail = 0;
state->rcpts = 0;
+ state->delayed_response = 0;
+ state->delayed_args = 0;
/* Initialize file capture attributes. */
#ifdef AF_INET6
if (sa.sa_family == AF_INET6)
{
int backlog;
int ch;
+ int delay;
const char *protocols = INET_PROTO_NAME_ALL;
const char *root_dir = 0;
const char *user_privs = 0;
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "468aA:cCd:D:eEf:Fh:Ln:m:pPq:r:R:s:S:t:u:vw:")) > 0) {
+ while ((ch = GETOPT(argc, argv, "468aA:cCd:D:eEf:Fh:Ln:m:pPq:r:R:s:S:t:u:vw:W:")) > 0) {
switch (ch) {
case '4':
protocols = INET_PROTO_NAME_IPV4;
msg_verbose++;
break;
case 'w':
- if ((fixed_delay = atoi(optarg)) <= 0)
+ if ((delay = atoi(optarg)) <= 0)
usage(argv[0]);
+ set_cmd_delay("data", delay, 0);
+ break;
+ case 'W':
+ set_cmd_delay_arg(optarg);
break;
default:
usage(argv[0]);
&& strcmp(wanted_name, STR(name_buf)) == 0))
break;
if ((flags & ATTR_FLAG_EXTRA) != 0) {
- msg_warn("unexpected attribute %s in input from %s",
- STR(name_buf), VSTREAM_PATH(fp));
+ msg_warn("unexpected attribute %s from %s (expecting: %s)",
+ STR(name_buf), VSTREAM_PATH(fp), wanted_name);
return (conversions);
}
&& strcmp(wanted_name, STR(name_buf)) == 0))
break;
if ((flags & ATTR_FLAG_EXTRA) != 0) {
- msg_warn("unexpected attribute %s in input from %s",
- STR(name_buf), VSTREAM_PATH(fp));
+ msg_warn("unexpected attribute %s from %s (expecting: %s)",
+ STR(name_buf), VSTREAM_PATH(fp), wanted_name);
return (conversions);
}
&& strcmp(wanted_name, STR(name_buf)) == 0))
break;
if ((flags & ATTR_FLAG_EXTRA) != 0) {
- msg_warn("unexpected attribute %s in input from %s",
- STR(name_buf), VSTREAM_PATH(fp));
+ msg_warn("unexpected attribute %s from %s (expecting: %s)",
+ STR(name_buf), VSTREAM_PATH(fp), wanted_name);
return (conversions);
}
if (EVENT_INIT_NEEDED())
return;
+#if (EVENTS_STYLE == EVENTS_STYLE_SELECT)
EVENT_MASK_ZERO(&zero_mask);
+#else
+ EVENT_MASK_ALLOC(&zero_mask, event_fdslots);
+#endif
(void) time(&event_present);
max_time = event_present + time_limit;
while (event_present < max_time
|| memcmp(&zero_mask, &event_xmask,
EVENT_MASK_BYTE_COUNT(&zero_mask)) != 0))
event_loop(1);
+#if (EVENTS_STYLE != EVENTS_STYLE_SELECT)
+ EVENT_MASK_FREE(&zero_mask);
+#endif
}
/* event_enable_read - enable read events */