-TALIAS_TOKEN
-TANVIL_CLNT
-TANVIL_LOCAL
+-TANVIL_MAX
-TANVIL_REMOTE
+-TANVIL_REQ_TABLE
-TARGV
-TATTR_CLNT
-TATTR_TABLE
others. Files: local/local.c, local/aliases.c, local/dotforward.c,
local/mailbox.c, local/maildir.c.
- Cleanup: while expanding a local(8) alias that has an owner
- alias, don't reset the owner-alias information when recursing
- into an alias that has no owner alias. This produces better
- error reports when delivering mail to nested aliases that
- have an owner alias only at the top level. To get the old
- behavior, specify "sticky_owner_alias = no". Problem
- reported by Victor Duchovni. File: local/alias.c.
-
Logging: additional SASL debug logging by Andreas Winkelmann.
Files: */*sasl_glue.c.
+20050929
+
+ Paranoia: don't ignore garbage in SMTP or LMTP server replies
+ when ESMTP command pipelining is turned on. For example,
+ after sending ".<CR><LF>QUIT<CR><LF>", Postfix could recognize
+ the server's 2XX QUIT reply as a 2XX END-OF-DATA reply after
+ garbage, causing mail to be lost. The SMTP and LMTP clients
+ now report a remote protocol error and defer delivery.
+ Files: smtp/smtp_chat.c, smtp/smtp_trouble.c, lmtp/lmtp_chat.c,
+ lmtp/lmtp_trouble.c.
+
+ Performance: specify "smtpd_peername_lookup = no" to disable
+ client hostname lookups in the SMTP server. All clients are
+ treated as "unknown". This should be used only under extreme
+ conditions where DNS lookup latencies are critical. File:
+ smtpd/smtpd_peer.c.
+
+20051010
+
+ Feature: smtpd_client_new_tls_session_rate_limit parameter
+ to limit the number of new (i.e. uncached) TLS sessions
+ that a remote SMTP client may negotiate per unit time. This
+ feature, which is off by default, can limit the CPU load
+ due to expensive crypto operations. Files: global/anvil_clnt.c,
+ anvil/anvil.c, smtpd/smtpd.c.
+
+ Cleanup: eliminated code duplicatiom in the anvil server
+ that resulted from adding similar features one at a time.
+ File: anvil/anvil.c.
+
Open problems:
+ Try to recognize that Resent- headers appear in blocks,
+ newest block first. But don't break on incorrect header
+ block organization.
+
+ Hard limits on cache sizes (anvil, specifically).
+
Look for systems with XPG basename() declared in <libgen.h>,
and prepare for phasing out the Postfix-supplied one.
Beware, however, that XPG basename() takes (char *), and
Policy delegation is now the preferred method for adding policies to Postfix.
It's much easier to develop a new feature in few lines of Perl, than trying to
do the same in C code. The difference in performance will be unnoticeable
-except in the most demanding environments.
+except in the most demanding environments. On active systems a policy daemon
+process is used multiple times, for up to 100 incoming SMTP connections.
This document covers the following topics:
reduce the number of queue files and cleanup process instances. To
get the earlier behavior, specify "frozen_delivered_to = no".
-While expanding an alias that has an owner alias, the Postfix
-local(8) delivery agent no longer resets the owner information when
-it expands a subordinate alias that has no owner alias. This
-produces better error reporting with nested aliases that have an
-owner alias only at the top level. To get the earlier behavior,
-specify "sticky_owner_alias = no".
+The frozen_delivered_to feature also fixes an old problem with
+duplicate deliveries to recipients that are listed in multiple
+nested aliases.
Incompatibility with snapshot 20050828
======================================
to Postfix. It's much easier to develop a new feature in few lines
of Perl, than trying to do the same in C code. The difference in
performance will be unnoticeable except in the most demanding
-environments. </p>
+environments. On active systems a policy daemon process is used
+multiple times, for up to 100 incoming SMTP connections. </p>
<p> This document covers the following topics: </p>
<b>anvil</b> [generic Postfix daemon options]
<b>DESCRIPTION</b>
- The Postfix <a href="anvil.8.html"><b>anvil</b>(8)</a> server maintains short-term statis-
- tics to defend against clients that hammer a server with
- either too many simultaneous sessions, or with too many
- successive requests within a configurable time interval.
- This server is designed to run under control by the Post-
- fix <a href="master.8.html"><b>master</b>(8)</a> server.
-
- The <a href="anvil.8.html"><b>anvil</b>(8)</a> server maintains no persistent database.
- Standard library utilities do not meet Postfix performance
- and robustness requirements.
-
-<b>CONNECTION COUNT/RATE LIMITING</b>
- When a remote client connects, a connection count (or
- rate) limited server should send the following request to
+ The Postfix <a href="anvil.8.html"><b>anvil</b>(8)</a> server maintains statistics about
+ client connection counts or client request rates. This
+ information can be used to defend against clients that
+ hammer a server with either too many simultaneous ses-
+ sions, or with too many successive requests within a con-
+ figurable time interval. This server is designed to run
+ under control by the Postfix <a href="master.8.html"><b>master</b>(8)</a> server.
+
+ In the following text, <b>ident</b> specifies a (service, client)
+ combination. The exact syntax of that information is
+ application-dependent; the <a href="anvil.8.html"><b>anvil</b>(8)</a> server does not care.
+
+<b>CONNECTION COUNT/RATE CONTROL</b>
+ To register a new connection send the following request to
the <a href="anvil.8.html"><b>anvil</b>(8)</a> server:
<b>request=connect</b>
<b>ident=</b><i>string</i>
- This registers a new connection for the (service, client)
- combination specified with <b>ident</b>. The <a href="anvil.8.html"><b>anvil</b>(8)</a> server
- answers with the number of simultaneous connections and
- the number of connections per unit time for that (service,
- client) combination:
+ The <a href="anvil.8.html"><b>anvil</b>(8)</a> server answers with the number of simultane-
+ ous connections and the number of connections per unit
+ time for the (service, client) combination specified with
+ <b>ident</b>:
<b>status=0</b>
<b>count=</b><i>number</i>
<b>rate=</b><i>number</i>
- The <b>rate</b> is computed as the number of connections that
- were registered in the current "time unit" interval. It
- is left up to the server to decide if the remote client
- exceeds the connection count (or rate) limit.
-
- When a remote client disconnects, a connection count (or
- rate) limited server should send the following request to
- the <a href="anvil.8.html"><b>anvil</b>(8)</a> server:
+ To register a disconnect event send the following request
+ to the <a href="anvil.8.html"><b>anvil</b>(8)</a> server:
<b>request=disconnect</b>
<b>ident=</b><i>string</i>
- This registers a disconnect event for the (service,
- client) combination specified with <b>ident</b>. The <a href="anvil.8.html"><b>anvil</b>(8)</a>
- server replies with:
+ The <a href="anvil.8.html"><b>anvil</b>(8)</a> server replies with:
<b>status=0</b>
-<b>MESSAGE RATE LIMITING</b>
- When a remote client sends a message delivery request, a
- message rate limited server should send the following
+<b>MESSAGE RATE CONTROL</b>
+ To register a message delivery request send the following
request to the <a href="anvil.8.html"><b>anvil</b>(8)</a> server:
<b>request=message</b>
<b>ident=</b><i>string</i>
- This registers a message delivery request for the (ser-
- vice, client) combination specified with <b>ident</b>. The
- <a href="anvil.8.html"><b>anvil</b>(8)</a> server answers with the number of message deliv-
- ery requests per unit time for that (service, client) com-
- bination:
+ The <a href="anvil.8.html"><b>anvil</b>(8)</a> server answers with the number of message
+ delivery requests per unit time for the (service, client)
+ combination specified with <b>ident</b>:
<b>status=0</b>
<b>rate=</b><i>number</i>
- In order to prevent the <a href="anvil.8.html"><b>anvil</b>(8)</a> server from discarding
- client request rates too early or too late, a message rate
- limited service should also register connect/disconnect
- events.
-
-<b>RECIPIENT RATE LIMITING</b>
- When a remote client sends a recipient address, a recipi-
- ent rate limited server should send the following request
+<b>RECIPIENT RATE CONTROL</b>
+ To register a recipient request send the following request
to the <a href="anvil.8.html"><b>anvil</b>(8)</a> server:
<b>request=recipient</b>
<b>ident=</b><i>string</i>
- This registers a recipient request for the (service,
- client) combination specified with <b>ident</b>. The <a href="anvil.8.html"><b>anvil</b>(8)</a>
- server answers with the number of recipient addresses per
- unit time for that (service, client) combination:
+ The <a href="anvil.8.html"><b>anvil</b>(8)</a> server answers with the number of recipient
+ addresses per unit time for the (service, client) combina-
+ tion specified with <b>ident</b>:
+
+ <b>status=0</b>
+ <b>rate=</b><i>number</i>
+
+<b>TLS SESSION NEGOTIATION RATE CONTROL</b>
+ The features described in this section are available with
+ Postfix 2.3 and later.
+
+ To register a request for a new (i.e. not cached) TLS ses-
+ sion send the following request to the <a href="anvil.8.html"><b>anvil</b>(8)</a> server:
+
+ <b>request=newtls</b>
+ <b>ident=</b><i>string</i>
+
+ The <a href="anvil.8.html"><b>anvil</b>(8)</a> server answers with the number of new TLS
+ session requests per unit time for the (service, client)
+ combination specified with <b>ident</b>:
<b>status=0</b>
<b>rate=</b><i>number</i>
- In order to prevent the <a href="anvil.8.html"><b>anvil</b>(8)</a> server from discarding
- client request rates too early or too late, a recipient
- rate limited service should also register connect/discon-
- nect events.
+ To retrieve new TLS session request rate information with-
+ out updating the counter information, use:
+
+ <b>request=newtls_report</b>
+ <b>ident=</b><i>string</i>
+
+ The <a href="anvil.8.html"><b>anvil</b>(8)</a> server answers with the number of new TLS
+ session requests per unit time for the (service, client)
+ combination specified with <b>ident</b>.
+
+ <b>status=0</b>
+ <b>rate=</b><i>number</i>
<b>SECURITY</b>
The <a href="anvil.8.html"><b>anvil</b>(8)</a> server does not talk to the network or to
local users, and can run chrooted at fixed low privilege.
The <a href="anvil.8.html"><b>anvil</b>(8)</a> server maintains an in-memory table with
- information about recent clients of a connection count (or
- rate) limited service. Although state is kept only tempo-
- rarily, this may require a lot of memory on systems that
- handle connections from many remote clients. To reduce
- memory usage, reduce the time unit over which state is
- kept.
+ information about recent clients requests. No persistent
+ state is kept because standard system library routines are
+ not sufficiently robust for update-intensive applications.
+
+ Although the in-memory state is kept only temporarily,
+ this may require a lot of memory on systems that handle
+ connections from many remote clients. To reduce memory
+ usage, reduce the time unit over which state is kept.
<b>DIAGNOSTICS</b>
Problems and transactions are logged to <b>syslogd</b>(8).
Upon exit, and every <b><a href="postconf.5.html#anvil_status_update_time">anvil_status_update_time</a></b> seconds, the
- server logs the maximal count and rate values measured,
- together with (service, client) information and the time
- of day associated with those events. In order to avoid
- unnecessary overhead, no measurements are done for activ-
+ server logs the maximal count and rate values measured,
+ together with (service, client) information and the time
+ of day associated with those events. In order to avoid
+ unnecessary overhead, no measurements are done for activ-
ity that isn't concurrency limited or rate limited.
<b>BUGS</b>
- Systems behind network address translating routers or
+ Systems behind network address translating routers or
proxies appear to have the same client address and can run
into connection count and/or rate limits falsely.
In this preliminary implementation, a count (or rate) lim-
- ited server can have only one remote client at a time. If
- a server reports multiple simultaneous clients, all but
+ ited server can have only one remote client at a time. If
+ a server reports multiple simultaneous clients, all but
the last reported client are ignored.
+ The <a href="anvil.8.html"><b>anvil</b>(8)</a> server automatically discards client request
+ information after it expires. To prevent the <a href="anvil.8.html"><b>anvil</b>(8)</a>
+ server from discarding client request rate information too
+ early or too late, a rate limited service should always
+ register connect/disconnect events even when it does not
+ explicitly limit them.
+
<b>CONFIGURATION PARAMETERS</b>
- Changes to <b>main.cf</b> are picked up automatically as <a href="anvil.8.html"><b>anvil</b>(8)</a>
- processes run for only a limited amount of time. Use the
- command "<b>postfix reload</b>" to speed up a change.
+ On low-traffic mail systems, changes to <b>main.cf</b> are picked
+ up automatically as <a href="anvil.8.html"><b>anvil</b>(8)</a> processes run for only a lim-
+ ited amount of time. On other mail systems, use the com-
+ mand "<b>postfix reload</b>" to speed up a change.
- The text below provides only a parameter summary. See
+ The text below provides only a parameter summary. See
<a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
<b><a href="postconf.5.html#anvil_rate_time_unit">anvil_rate_time_unit</a> (60s)</b>
- The time unit over which client connection rates
+ The time unit over which client connection rates
and other rates are calculated.
<b><a href="postconf.5.html#anvil_status_update_time">anvil_status_update_time</a> (600s)</b>
- How frequently the <a href="anvil.8.html"><b>anvil</b>(8)</a> connection and rate
+ How frequently the <a href="anvil.8.html"><b>anvil</b>(8)</a> connection and rate
limiting server logs peak usage information.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix main.cf and
+ The default location of the Postfix main.cf and
master.cf configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
- How much time a Postfix daemon process may take to
- handle a request before it is terminated by a
+ How much time a Postfix daemon process may take to
+ handle a request before it is terminated by a
built-in watchdog timer.
<b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
over an internal communication channel.
<b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
- The maximum amount of time that an idle Postfix
- daemon process waits for the next service request
+ The maximum amount of time that an idle Postfix
+ daemon process waits for the next service request
before exiting.
<b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
- The maximal number of connection requests before a
+ The maximal number of connection requests before a
Postfix daemon process terminates.
<b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
- The process ID of a Postfix command or daemon
+ The process ID of a Postfix command or daemon
process.
<b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
- The process name of a Postfix command or daemon
+ The process name of a Postfix command or daemon
process.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
- The mail system name that is prepended to the
- process name in syslog records, so that "smtpd"
+ The mail system name that is prepended to the
+ process name in syslog records, so that "smtpd"
becomes, for example, "postfix/smtpd".
<b>SEE ALSO</b>
<a href="TUNING_README.html">TUNING_README</a>, performance tuning
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>HISTORY</b>
attempt; do not update the Delivered-To: address
while expanding aliases or .forward files.
- <b><a href="postconf.5.html#sticky_owner_alias">sticky_owner_alias</a> (yes)</b>
- When expanding a <a href="local.8.html"><b>local</b>(8)</a> alias that has an owner
- alias (see owner-<i>name</i> discussion in <a href="aliases.5.html"><b>aliases</b>(5)</a>),
- use the owner information even when the expansion
- invokes a subordinate alias that has no owner
- alias.
-
<b>DELIVERY METHOD CONTROLS</b>
The precedence of <a href="local.8.html"><b>local</b>(8)</a> delivery methods from high to
low is: aliases, .forward files, <a href="postconf.5.html#mailbox_transport">mailbox_transport</a>, <a href="postconf.5.html#mailbox_command_maps">mail</a>-
</pre>
+</DD>
+
+<DT><b><a name="smtpd_client_new_tls_session_rate_limit">smtpd_client_new_tls_session_rate_limit</a>
+(default: 0)</b></DT><DD>
+
+<p>
+The maximal number of new (i.e., uncached) TLS sessions that a
+remote SMTP client is allowed to negotiate with this service per
+time unit. The time unit is specified with the <a href="postconf.5.html#anvil_rate_time_unit">anvil_rate_time_unit</a>
+configuration parameter.
+</p>
+
+<p>
+By default, a remote SMTP client can negotiate as many new TLS
+sessions per time unit as Postfix can accept.
+</p>
+
+<p>
+To disable this feature, specify a limit of 0. Otherwise, specify
+a limit that is at least the per-client concurrent session limit,
+or else legitimate client sessions may be rejected.
+</p>
+
+<p>
+WARNING: The purpose of this feature is to limit abuse. It must
+not be used to regulate legitimate mail traffic.
+</p>
+
+<p>
+This feature is available in Postfix 2.3 and later.
+</p>
+
+<p>
+Example:
+</p>
+
+<pre>
+<a href="postconf.5.html#smtpd_client_new_tls_session_rate_limit">smtpd_client_new_tls_session_rate_limit</a> = 100
+</pre>
+
+
</DD>
<DT><b><a name="smtpd_client_recipient_rate_limit">smtpd_client_recipient_rate_limit</a>
</p>
+</DD>
+
+<DT><b><a name="smtpd_peername_lookup">smtpd_peername_lookup</a>
+(default: yes)</b></DT><DD>
+
+<p> Attempt to look up the SMTP client hostname, and verify that
+the name matches the client IP address. A client name is set to
+"unknown" when it cannot be looked up or verified, or when name
+lookup is disabled. Turning off name lookup reduces delays due to
+DNS lookup and increases the maximal inbound delivery rate. </p>
+
+
</DD>
<DT><b><a name="smtpd_policy_service_max_idle">smtpd_policy_service_max_idle</a>
</p>
-</DD>
-
-<DT><b><a name="sticky_owner_alias">sticky_owner_alias</a>
-(default: yes)</b></DT><DD>
-
-<p> When expanding a <a href="local.8.html">local(8)</a> alias that has an owner alias (see
-owner-<i>name</i> discussion in <a href="aliases.5.html">aliases(5)</a>), use the owner information
-even when the expansion invokes a subordinate alias that has no
-owner alias. </p>
-
-<p> This feature is available in Postfix 2.3 and later. With older
-Postfix releases, the behavior is as if this parameter is set to
-"no". The old setting provides poorer error reporting with nested
-aliases that only have an owner alias at the top level. </p>
-
-
</DD>
<DT><b><a name="strict_7bit_headers">strict_7bit_headers</a>
server command history before it is flushed upon
receipt of EHLO, RSET, or end of DATA.
+ Available in Postfix version 2.3 and later:
+
+ <b><a href="postconf.5.html#smtpd_peername_lookup">smtpd_peername_lookup</a> (yes)</b>
+ Attempt to look up the SMTP client hostname, and
+ verify that the name matches the client IP address.
+
The per SMTP client connection count and request rate lim-
its are implemented in co-operation with the <a href="anvil.8.html"><b>anvil</b>(8)</a> ser-
vice, and are available in Postfix version 2.2 and later.
Clients that are excluded from connection count,
connection rate, or SMTP request rate restrictions.
+ Available in Postfix version 2.3 and later:
+
+ <b><a href="postconf.5.html#smtpd_client_new_tls_session_rate_limit">smtpd_client_new_tls_session_rate_limit</a> (0)</b>
+ The maximal number of new (i.e., uncached) TLS ses-
+ sions that any client is allowed to negotiate with
+ this service per time unit.
+
<b>TARPIT CONTROLS</b>
When a remote SMTP client makes errors, the Postfix SMTP
server can insert delays before responding. This can help
.fi
.ad
.ft R
+.SH smtpd_client_new_tls_session_rate_limit (default: 0)
+The maximal number of new (i.e., uncached) TLS sessions that a
+remote SMTP client is allowed to negotiate with this service per
+time unit. The time unit is specified with the anvil_rate_time_unit
+configuration parameter.
+.PP
+By default, a remote SMTP client can negotiate as many new TLS
+sessions per time unit as Postfix can accept.
+.PP
+To disable this feature, specify a limit of 0. Otherwise, specify
+a limit that is at least the per-client concurrent session limit,
+or else legitimate client sessions may be rejected.
+.PP
+WARNING: The purpose of this feature is to limit abuse. It must
+not be used to regulate legitimate mail traffic.
+.PP
+This feature is available in Postfix 2.3 and later.
+.PP
+Example:
+.PP
+.nf
+.na
+.ft C
+smtpd_client_new_tls_session_rate_limit = 100
+.fi
+.ad
+.ft R
.SH smtpd_client_recipient_rate_limit (default: 0)
The maximal number of recipient addresses that any client is allowed
to send to this service per time unit, regardless of whether or not
.SH smtpd_null_access_lookup_key (default: <>)
The lookup key to be used in SMTP \fBaccess\fR(5) tables instead of the
null sender address.
+.SH smtpd_peername_lookup (default: yes)
+Attempt to look up the SMTP client hostname, and verify that
+the name matches the client IP address. A client name is set to
+"unknown" when it cannot be looked up or verified, or when name
+lookup is disabled. Turning off name lookup reduces delays due to
+DNS lookup and increases the maximal inbound delivery rate.
.SH smtpd_policy_service_max_idle (default: 300s)
The time after which an idle SMTPD policy service connection is
closed.
.PP
Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
The default time unit is s (seconds).
-.SH sticky_owner_alias (default: yes)
-When expanding a \fBlocal\fR(8) alias that has an owner alias (see
-owner-\fIname\fR discussion in \fBaliases\fR(5)), use the owner information
-even when the expansion invokes a subordinate alias that has no
-owner alias.
-.PP
-This feature is available in Postfix 2.3 and later. With older
-Postfix releases, the behavior is as if this parameter is set to
-"no". The old setting provides poorer error reporting with nested
-aliases that only have an owner alias at the top level.
.SH strict_7bit_headers (default: no)
Reject mail with 8-bit text in message headers. This blocks mail
from poorly written applications.
.SH DESCRIPTION
.ad
.fi
-The Postfix \fBanvil\fR(8) server maintains short-term statistics
-to defend against clients that hammer a server with either too
-many simultaneous sessions, or with too many successive requests
-within a configurable time interval.
-This server is designed to run under control by the Postfix
-\fBmaster\fR(8) server.
+The Postfix \fBanvil\fR(8) server maintains statistics about
+client connection counts or client request rates. This
+information can be used to defend against clients that
+hammer a server with either too many simultaneous sessions,
+or with too many successive requests within a configurable
+time interval. This server is designed to run under control
+by the Postfix \fBmaster\fR(8) server.
-The \fBanvil\fR(8) server maintains no persistent database. Standard
-library utilities do not meet Postfix performance and robustness
-requirements.
-.SH "CONNECTION COUNT/RATE LIMITING"
+In the following text, \fBident\fR specifies a (service,
+client) combination. The exact syntax of that information
+is application-dependent; the \fBanvil\fR(8) server does
+not care.
+.SH "CONNECTION COUNT/RATE CONTROL"
.na
.nf
.ad
.fi
-When a remote client connects, a connection count (or rate) limited
-server should send the following request to the \fBanvil\fR(8) server:
+To register a new connection send the following request to
+the \fBanvil\fR(8) server:
.PP
.in +4
\fBrequest=connect\fR
\fBident=\fIstring\fR
.in
.PP
-This registers a new connection for the (service, client)
-combination specified with \fBident\fR. The \fBanvil\fR(8) server
-answers with the number of simultaneous connections and the
-number of connections per unit time for that (service, client)
-combination:
+The \fBanvil\fR(8) server answers with the number of
+simultaneous connections and the number of connections per
+unit time for the (service, client) combination specified
+with \fBident\fR:
.PP
.in +4
\fBstatus=0\fR
\fBrate=\fInumber\fR
.in
.PP
-The \fBrate\fR is computed as the number of connections
-that were registered in the current "time unit" interval.
-It is left up to the server to decide if the remote client
-exceeds the connection count (or rate) limit.
-.PP
-When a remote client disconnects, a connection count (or rate) limited
-server should send the following request to the \fBanvil\fR(8) server:
+To register a disconnect event send the following request
+to the \fBanvil\fR(8) server:
.PP
.in +4
\fBrequest=disconnect\fR
\fBident=\fIstring\fR
.in
.PP
-This registers a disconnect event for the (service, client)
-combination specified with \fBident\fR. The \fBanvil\fR(8)
-server replies with:
+The \fBanvil\fR(8) server replies with:
.PP
.ti +4
\fBstatus=0\fR
-.SH "MESSAGE RATE LIMITING"
+.SH "MESSAGE RATE CONTROL"
.na
.nf
.ad
.fi
-When a remote client sends a message delivery request, a
-message rate limited server should send the following
+To register a message delivery request send the following
request to the \fBanvil\fR(8) server:
.PP
.in +4
\fBident=\fIstring\fR
.in
.PP
-This registers a message delivery request for the (service, client)
-combination specified with \fBident\fR. The \fBanvil\fR(8) server
-answers with the number of message delivery requests per unit time
-for that (service, client) combination:
+The \fBanvil\fR(8) server answers with the number of message
+delivery requests per unit time for the (service, client)
+combination specified with \fBident\fR:
.PP
.in +4
\fBstatus=0\fR
.br
\fBrate=\fInumber\fR
.in
-.PP
-In order to prevent the \fBanvil\fR(8) server from discarding client
-request rates too early or too late, a message rate limited
-service should also register connect/disconnect events.
-.SH "RECIPIENT RATE LIMITING"
+.SH "RECIPIENT RATE CONTROL"
.na
.nf
.ad
.fi
-When a remote client sends a recipient address, a recipient
-rate limited server should send the following request to
-the \fBanvil\fR(8) server:
+To register a recipient request send the following request
+to the \fBanvil\fR(8) server:
.PP
.in +4
\fBrequest=recipient\fR
\fBident=\fIstring\fR
.in
.PP
-This registers a recipient request for the (service, client)
-combination specified with \fBident\fR. The \fBanvil\fR(8) server
-answers with the number of recipient addresses per unit time
-for that (service, client) combination:
+The \fBanvil\fR(8) server answers with the number of recipient
+addresses per unit time for the (service, client) combination
+specified with \fBident\fR:
.PP
.in +4
\fBstatus=0\fR
.br
\fBrate=\fInumber\fR
.in
+.SH "TLS SESSION NEGOTIATION RATE CONTROL"
+.na
+.nf
+.ad
+.fi
+The features described in this section are available with
+Postfix 2.3 and later.
+
+To register a request for a new (i.e. not cached) TLS session
+send the following request to the \fBanvil\fR(8) server:
+.PP
+.in +4
+\fBrequest=newtls\fR
+.br
+\fBident=\fIstring\fR
+.in
.PP
-In order to prevent the \fBanvil\fR(8) server from discarding client
-request rates too early or too late, a recipient rate limited
-service should also register connect/disconnect events.
+The \fBanvil\fR(8) server answers with the number of new
+TLS session requests per unit time for the (service, client)
+combination specified with \fBident\fR:
+.PP
+.in +4
+\fBstatus=0\fR
+.br
+\fBrate=\fInumber\fR
+.in
+.PP
+To retrieve new TLS session request rate information without
+updating the counter information, use:
+.PP
+.in +4
+\fBrequest=newtls_report\fR
+.br
+\fBident=\fIstring\fR
+.in
+.PP
+The \fBanvil\fR(8) server answers with the number of new
+TLS session requests per unit time for the (service, client)
+combination specified with \fBident\fR.
+.PP
+.in +4
+\fBstatus=0\fR
+.br
+\fBrate=\fInumber\fR
+.in
.SH "SECURITY"
.na
.nf
The \fBanvil\fR(8) server does not talk to the network or to local
users, and can run chrooted at fixed low privilege.
-The \fBanvil\fR(8) server maintains an in-memory table with information
-about recent clients of a connection count (or rate) limited service.
-Although state is kept only temporarily, this may require a lot of
-memory on systems that handle connections from many remote clients.
-To reduce memory usage, reduce the time unit over which state
-is kept.
+The \fBanvil\fR(8) server maintains an in-memory table with
+information about recent clients requests. No persistent
+state is kept because standard system library routines are
+not sufficiently robust for update-intensive applications.
+
+Although the in-memory state is kept only temporarily, this
+may require a lot of memory on systems that handle connections
+from many remote clients. To reduce memory usage, reduce
+the time unit over which state is kept.
.SH DIAGNOSTICS
.ad
.fi
can have only one remote client at a time. If a server reports
multiple simultaneous clients, all but the last reported client
are ignored.
+
+The \fBanvil\fR(8) server automatically discards client
+request information after it expires. To prevent the
+\fBanvil\fR(8) server from discarding client request rate
+information too early or too late, a rate limited service
+should always register connect/disconnect events even when
+it does not explicitly limit them.
.SH "CONFIGURATION PARAMETERS"
.na
.nf
.ad
.fi
-Changes to \fBmain.cf\fR are picked up automatically as \fBanvil\fR(8)
-processes run for only a limited amount of time. Use the command
-"\fBpostfix reload\fR" to speed up a change.
+On low-traffic mail systems, changes to \fBmain.cf\fR are
+picked up automatically as \fBanvil\fR(8) processes run for
+only a limited amount of time. On other mail systems, use
+the command "\fBpostfix reload\fR" to speed up a change.
The text below provides only a parameter summary. See
\fBpostconf\fR(5) for more details including examples.
address (see prepend_delivered_header) only once, at the start of
a delivery attempt; do not update the Delivered-To: address while
expanding aliases or .forward files.
-.IP "\fBsticky_owner_alias (yes)\fR"
-When expanding a \fBlocal\fR(8) alias that has an owner alias (see
-owner-\fIname\fR discussion in \fBaliases\fR(5)), use the owner information
-even when the expansion invokes a subordinate alias that has no
-owner alias.
.SH "DELIVERY METHOD CONTROLS"
.na
.nf
The maximal number of lines in the Postfix SMTP server command history
before it is flushed upon receipt of EHLO, RSET, or end of DATA.
.PP
+Available in Postfix version 2.3 and later:
+.IP "\fBsmtpd_peername_lookup (yes)\fR"
+Attempt to look up the SMTP client hostname, and verify that
+the name matches the client IP address.
+.PP
The per SMTP client connection count and request rate limits are
implemented in co-operation with the \fBanvil\fR(8) service, and
are available in Postfix version 2.2 and later.
.IP "\fBsmtpd_client_event_limit_exceptions ($mynetworks)\fR"
Clients that are excluded from connection count, connection rate,
or SMTP request rate restrictions.
+.PP
+Available in Postfix version 2.3 and later:
+.IP "\fBsmtpd_client_new_tls_session_rate_limit (0)\fR"
+The maximal number of new (i.e., uncached) TLS sessions that any
+client is allowed to negotiate with this service per time unit.
.SH "TARPIT CONTROLS"
.na
.nf
s;\bsmtpd_client_connection_rate_limit\b;<a href="postconf.5.html#smtpd_client_connection_rate_limit">$&</a>;g;
s;\bsmtpd_client_message_rate_limit\b;<a href="postconf.5.html#smtpd_client_message_rate_limit">$&</a>;g;
s;\bsmtpd_client_recipient_rate_limit\b;<a href="postconf.5.html#smtpd_client_recipient_rate_limit">$&</a>;g;
+ s;\bsmtpd_client_new_tls_session_rate_limit\b;<a href="postconf.5.html#smtpd_client_new_tls_session_rate_limit">$&</a>;g;
s;\bsmtpd_client_restrictions\b;<a href="postconf.5.html#smtpd_client_restrictions">$&</a>;g;
s;\bsmtpd_data_restrictions\b;<a href="postconf.5.html#smtpd_data_restrictions">$&</a>;g;
s;\bsmtpd_delay_reject\b;<a href="postconf.5.html#smtpd_delay_reject">$&</a>;g;
s;\bsmtpd_noop_commands\b;<a href="postconf.5.html#smtpd_noop_commands">$&</a>;g;
s;\bsmtpd_null_access_lookup_key\b;<a href="postconf.5.html#smtpd_null_access_lookup_key">$&</a>;g;
s;\bsmtpd_recipient_overshoot_limit\b;<a href="postconf.5.html#smtpd_recipient_overshoot_limit">$&</a>;g;
+ s;\bsmtpd_peername_lookup\b;<a href="postconf.5.html#smtpd_peername_lookup">$&</a>;g;
s;\bsmtpd_policy_service_max_idle\b;<a href="postconf.5.html#smtpd_policy_service_max_idle">$&</a>;g;
s;\bsmtpd_policy_service_max_ttl\b;<a href="postconf.5.html#smtpd_policy_service_max_ttl">$&</a>;g;
s;\bsmtpd_policy_service_timeout\b;<a href="postconf.5.html#smtpd_policy_service_timeout">$&</a>;g;
s;\btls_ran[-</Bb>]*\n* *[<Bb>]*dom_source\b;<a href="postconf.5.html#tls_random_source">$&</a>;g;
s;\bfrozen_delivered_to\b;<a href="postconf.5.html#frozen_delivered_to">$&</a>;g;
- s;\bsticky_owner_alias\b;<a href="postconf.5.html#sticky_owner_alias">$&</a>;g;
# Undo hyperlinks of manual pages with the same name as parameters.
to Postfix. It's much easier to develop a new feature in few lines
of Perl, than trying to do the same in C code. The difference in
performance will be unnoticeable except in the most demanding
-environments. </p>
+environments. On active systems a policy daemon process is used
+multiple times, for up to 100 incoming SMTP connections. </p>
<p> This document covers the following topics: </p>
smtpd_client_recipient_rate_limit = 1000
</pre>
+%PARAM smtpd_client_new_tls_session_rate_limit 0
+
+<p>
+The maximal number of new (i.e., uncached) TLS sessions that a
+remote SMTP client is allowed to negotiate with this service per
+time unit. The time unit is specified with the anvil_rate_time_unit
+configuration parameter.
+</p>
+
+<p>
+By default, a remote SMTP client can negotiate as many new TLS
+sessions per time unit as Postfix can accept.
+</p>
+
+<p>
+To disable this feature, specify a limit of 0. Otherwise, specify
+a limit that is at least the per-client concurrent session limit,
+or else legitimate client sessions may be rejected.
+</p>
+
+<p>
+WARNING: The purpose of this feature is to limit abuse. It must
+not be used to regulate legitimate mail traffic.
+</p>
+
+<p>
+This feature is available in Postfix 2.3 and later.
+</p>
+
+<p>
+Example:
+</p>
+
+<pre>
+smtpd_client_new_tls_session_rate_limit = 100
+</pre>
+
%PARAM smtpd_client_restrictions
<p>
<p> This feature is available in Postfix 2.3 and later. </p>
-%PARAM sticky_owner_alias yes
-
-<p> When expanding a local(8) alias that has an owner alias (see
-owner-<i>name</i> discussion in aliases(5)), use the owner information
-even when the expansion invokes a subordinate alias that has no
-owner alias. </p>
-
-<p> This feature is available in Postfix 2.3 and later. With older
-Postfix releases, the behavior is as if this parameter is set to
-"no". The old setting provides poorer error reporting with nested
-aliases that only have an owner alias at the top level. </p>
-
%PARAM frozen_delivered_to yes
<p> Update the local(8) delivery agent's idea of the Delivered-To:
or .forward files. When an alias or .forward file changes the
Delivered-To: address, it ties up one queue file and one cleanup
process instance while mail is being forwarded. </p>
+
+%PARAM smtpd_peername_lookup yes
+
+<p> Attempt to look up the SMTP client hostname, and verify that
+the name matches the client IP address. A client name is set to
+"unknown" when it cannot be looked up or verified, or when name
+lookup is disabled. Turning off name lookup reduces delays due to
+DNS lookup and increases the maximal inbound delivery rate. </p>
/* SYNOPSIS
/* \fBanvil\fR [generic Postfix daemon options]
/* DESCRIPTION
-/* The Postfix \fBanvil\fR(8) server maintains short-term statistics
-/* to defend against clients that hammer a server with either too
-/* many simultaneous sessions, or with too many successive requests
-/* within a configurable time interval.
-/* This server is designed to run under control by the Postfix
-/* \fBmaster\fR(8) server.
+/* The Postfix \fBanvil\fR(8) server maintains statistics about
+/* client connection counts or client request rates. This
+/* information can be used to defend against clients that
+/* hammer a server with either too many simultaneous sessions,
+/* or with too many successive requests within a configurable
+/* time interval. This server is designed to run under control
+/* by the Postfix \fBmaster\fR(8) server.
/*
-/* The \fBanvil\fR(8) server maintains no persistent database. Standard
-/* library utilities do not meet Postfix performance and robustness
-/* requirements.
-/* CONNECTION COUNT/RATE LIMITING
+/* In the following text, \fBident\fR specifies a (service,
+/* client) combination. The exact syntax of that information
+/* is application-dependent; the \fBanvil\fR(8) server does
+/* not care.
+/* CONNECTION COUNT/RATE CONTROL
/* .ad
/* .fi
-/* When a remote client connects, a connection count (or rate) limited
-/* server should send the following request to the \fBanvil\fR(8) server:
+/* To register a new connection send the following request to
+/* the \fBanvil\fR(8) server:
/* .PP
/* .in +4
/* \fBrequest=connect\fR
/* \fBident=\fIstring\fR
/* .in
/* .PP
-/* This registers a new connection for the (service, client)
-/* combination specified with \fBident\fR. The \fBanvil\fR(8) server
-/* answers with the number of simultaneous connections and the
-/* number of connections per unit time for that (service, client)
-/* combination:
+/* The \fBanvil\fR(8) server answers with the number of
+/* simultaneous connections and the number of connections per
+/* unit time for the (service, client) combination specified
+/* with \fBident\fR:
/* .PP
/* .in +4
/* \fBstatus=0\fR
/* \fBrate=\fInumber\fR
/* .in
/* .PP
-/* The \fBrate\fR is computed as the number of connections
-/* that were registered in the current "time unit" interval.
-/* It is left up to the server to decide if the remote client
-/* exceeds the connection count (or rate) limit.
-/* .PP
-/* When a remote client disconnects, a connection count (or rate) limited
-/* server should send the following request to the \fBanvil\fR(8) server:
+/* To register a disconnect event send the following request
+/* to the \fBanvil\fR(8) server:
/* .PP
/* .in +4
/* \fBrequest=disconnect\fR
/* \fBident=\fIstring\fR
/* .in
/* .PP
-/* This registers a disconnect event for the (service, client)
-/* combination specified with \fBident\fR. The \fBanvil\fR(8)
-/* server replies with:
+/* The \fBanvil\fR(8) server replies with:
/* .PP
/* .ti +4
/* \fBstatus=0\fR
-/* MESSAGE RATE LIMITING
+/* MESSAGE RATE CONTROL
/* .ad
/* .fi
-/* When a remote client sends a message delivery request, a
-/* message rate limited server should send the following
+/* To register a message delivery request send the following
/* request to the \fBanvil\fR(8) server:
/* .PP
/* .in +4
/* \fBident=\fIstring\fR
/* .in
/* .PP
-/* This registers a message delivery request for the (service, client)
-/* combination specified with \fBident\fR. The \fBanvil\fR(8) server
-/* answers with the number of message delivery requests per unit time
-/* for that (service, client) combination:
+/* The \fBanvil\fR(8) server answers with the number of message
+/* delivery requests per unit time for the (service, client)
+/* combination specified with \fBident\fR:
/* .PP
/* .in +4
/* \fBstatus=0\fR
/* .br
/* \fBrate=\fInumber\fR
/* .in
-/* .PP
-/* In order to prevent the \fBanvil\fR(8) server from discarding client
-/* request rates too early or too late, a message rate limited
-/* service should also register connect/disconnect events.
-/* RECIPIENT RATE LIMITING
+/* RECIPIENT RATE CONTROL
/* .ad
/* .fi
-/* When a remote client sends a recipient address, a recipient
-/* rate limited server should send the following request to
-/* the \fBanvil\fR(8) server:
+/* To register a recipient request send the following request
+/* to the \fBanvil\fR(8) server:
/* .PP
/* .in +4
/* \fBrequest=recipient\fR
/* \fBident=\fIstring\fR
/* .in
/* .PP
-/* This registers a recipient request for the (service, client)
-/* combination specified with \fBident\fR. The \fBanvil\fR(8) server
-/* answers with the number of recipient addresses per unit time
-/* for that (service, client) combination:
+/* The \fBanvil\fR(8) server answers with the number of recipient
+/* addresses per unit time for the (service, client) combination
+/* specified with \fBident\fR:
+/* .PP
+/* .in +4
+/* \fBstatus=0\fR
+/* .br
+/* \fBrate=\fInumber\fR
+/* .in
+/* TLS SESSION NEGOTIATION RATE CONTROL
+/* .ad
+/* .fi
+/* The features described in this section are available with
+/* Postfix 2.3 and later.
+/*
+/* To register a request for a new (i.e. not cached) TLS session
+/* send the following request to the \fBanvil\fR(8) server:
+/* .PP
+/* .in +4
+/* \fBrequest=newtls\fR
+/* .br
+/* \fBident=\fIstring\fR
+/* .in
+/* .PP
+/* The \fBanvil\fR(8) server answers with the number of new
+/* TLS session requests per unit time for the (service, client)
+/* combination specified with \fBident\fR:
/* .PP
/* .in +4
/* \fBstatus=0\fR
/* \fBrate=\fInumber\fR
/* .in
/* .PP
-/* In order to prevent the \fBanvil\fR(8) server from discarding client
-/* request rates too early or too late, a recipient rate limited
-/* service should also register connect/disconnect events.
+/* To retrieve new TLS session request rate information without
+/* updating the counter information, use:
+/* .PP
+/* .in +4
+/* \fBrequest=newtls_report\fR
+/* .br
+/* \fBident=\fIstring\fR
+/* .in
+/* .PP
+/* The \fBanvil\fR(8) server answers with the number of new
+/* TLS session requests per unit time for the (service, client)
+/* combination specified with \fBident\fR.
+/* .PP
+/* .in +4
+/* \fBstatus=0\fR
+/* .br
+/* \fBrate=\fInumber\fR
+/* .in
/* SECURITY
/* .ad
/* .fi
/* The \fBanvil\fR(8) server does not talk to the network or to local
/* users, and can run chrooted at fixed low privilege.
/*
-/* The \fBanvil\fR(8) server maintains an in-memory table with information
-/* about recent clients of a connection count (or rate) limited service.
-/* Although state is kept only temporarily, this may require a lot of
-/* memory on systems that handle connections from many remote clients.
-/* To reduce memory usage, reduce the time unit over which state
-/* is kept.
+/* The \fBanvil\fR(8) server maintains an in-memory table with
+/* information about recent clients requests. No persistent
+/* state is kept because standard system library routines are
+/* not sufficiently robust for update-intensive applications.
+/*
+/* Although the in-memory state is kept only temporarily, this
+/* may require a lot of memory on systems that handle connections
+/* from many remote clients. To reduce memory usage, reduce
+/* the time unit over which state is kept.
/* DIAGNOSTICS
/* Problems and transactions are logged to \fBsyslogd\fR(8).
/*
/* can have only one remote client at a time. If a server reports
/* multiple simultaneous clients, all but the last reported client
/* are ignored.
+/*
+/* The \fBanvil\fR(8) server automatically discards client
+/* request information after it expires. To prevent the
+/* \fBanvil\fR(8) server from discarding client request rate
+/* information too early or too late, a rate limited service
+/* should always register connect/disconnect events even when
+/* it does not explicitly limit them.
/* CONFIGURATION PARAMETERS
/* .ad
/* .fi
-/* Changes to \fBmain.cf\fR are picked up automatically as \fBanvil\fR(8)
-/* processes run for only a limited amount of time. Use the command
-/* "\fBpostfix reload\fR" to speed up a change.
+/* On low-traffic mail systems, changes to \fBmain.cf\fR are
+/* picked up automatically as \fBanvil\fR(8) processes run for
+/* only a limited amount of time. On other mail systems, use
+/* the command "\fBpostfix reload\fR" to speed up a change.
/*
/* The text below provides only a parameter summary. See
/* \fBpostconf\fR(5) for more details including examples.
* Absent a real-time query interface, these are logged at process exit time
* and at regular intervals.
*/
-static int max_count;
-static char *max_count_user;
-static time_t max_count_time;
+typedef struct {
+ int value; /* peak value */
+ char *ident; /* lookup key */
+ time_t when; /* time of peak value */
+} ANVIL_MAX;
-static int max_rate;
-static char *max_rate_user;
-static time_t max_rate_time;
+static ANVIL_MAX max_conn_count; /* peak connection count */
+static ANVIL_MAX max_conn_rate; /* peak connection rate */
+static ANVIL_MAX max_mail_rate; /* peak message rate */
+static ANVIL_MAX max_rcpt_rate; /* peak recipient rate */
+static ANVIL_MAX max_ntls_rate; /* peak new TLS session rate */
-static int max_mail;
-static char *max_mail_user;
-static time_t max_mail_time;
+static int max_cache_size; /* peak cache size */
+static time_t max_cache_time; /* time of peak size */
-static int max_rcpt;
-static char *max_rcpt_user;
-static time_t max_rcpt_time;
+/* Update/report peak usage. */
-static int max_newtls;
-static char *max_newtls_user;
-static time_t max_newtls_time;
+#define ANVIL_MAX_UPDATE(_max, _value, _ident) \
+ do { \
+ _max.value = _value; \
+ if (_max.ident == 0) { \
+ _max.ident = mystrdup(_ident); \
+ } else if (!STREQ(_max.ident, _ident)) { \
+ myfree(_max.ident); \
+ _max.ident = mystrdup(_ident); \
+ } \
+ _max.when = event_time(); \
+ } while (0)
-static int max_cache;
-static time_t max_cache_time;
+#define ANVIL_MAX_RATE_REPORT(_max, _name) \
+ do { \
+ if (_max.value > 0) { \
+ msg_info("statistics: max " _name " rate %d/%ds for (%s) at %.15s", \
+ _max.value, var_anvil_time_unit, \
+ _max.ident, ctime(&_max.when) + 4); \
+ _max.value = 0; \
+ } \
+ } while (0);
+
+#define ANVIL_MAX_COUNT_REPORT(_max, _name) \
+ do { \
+ if (_max.value > 0) { \
+ msg_info("statistics: max " _name " count %d for (%s) at %.15s", \
+ _max.value, _max.ident, ctime(&_max.when) + 4); \
+ _max.value = 0; \
+ } \
+ } while (0);
/*
* Remote connection state, one instance for each (service, client) pair.
int rate; /* connection rate */
int mail; /* message rate */
int rcpt; /* recipient rate */
- int newtls; /* newtls rate */
+ int ntls; /* new TLS session rate */
time_t start; /* time of first rate sample */
} ANVIL_REMOTE;
ANVIL_REMOTE *anvil_remote; /* XXX should be list */
} ANVIL_LOCAL;
- /*
- * Silly little macros.
- */
-#define STR(x) vstring_str(x)
-#define STREQ(x,y) (strcmp((x), (y)) == 0)
-
/*
* The following operations are implemented as macros with recognizable
* names so that we don't lose sight of what the code is trying to do.
/* Create new (service, client) state. */
-#define ANVIL_REMOTE_FIRST(remote, id) \
+#define ANVIL_REMOTE_FIRST_CONN(remote, id) \
do { \
(remote)->ident = mystrdup(id); \
(remote)->count = 1; \
(remote)->rate = 1; \
(remote)->mail = 0; \
(remote)->rcpt = 0; \
- (remote)->newtls = 0; \
+ (remote)->ntls = 0; \
(remote)->start = event_time(); \
} while(0)
myfree((char *) (remote)); \
} while(0)
-/* Add connection to (service, client) state. */
+/* Reset event rate counters and start of data collection interval. */
-#define ANVIL_REMOTE_NEXT(remote) \
+#define ANVIL_REMOTE_RSET_RATE(remote, _start) \
do { \
- time_t _now = event_time(); \
- if ((remote)->start + var_anvil_time_unit < _now) { \
- (remote)->rate = 1; \
- (remote)->mail = 0; \
- (remote)->rcpt = 0; \
- (remote)->newtls = 0; \
- (remote)->start = _now; \
- } else if ((remote)->rate < INT_MAX) { \
- (remote)->rate += 1; \
- } \
- if ((remote)->count == 0) \
- event_cancel_timer(anvil_remote_expire, (char *) remote); \
- (remote)->count++; \
+ (remote)->rate = 0; \
+ (remote)->mail = 0; \
+ (remote)->rcpt = 0; \
+ (remote)->ntls = 0; \
+ (remote)->start = _start; \
} while(0)
-#define ANVIL_ADD_MAIL(remote) \
- do { \
- time_t _now = event_time(); \
- if ((remote)->start + var_anvil_time_unit < _now) { \
- (remote)->rate = 0; \
- (remote)->mail = 1; \
- (remote)->rcpt = 0; \
- (remote)->newtls = 0; \
- (remote)->start = _now; \
- } else if ((remote)->mail < INT_MAX) { \
- (remote)->mail += 1; \
- } \
- } while(0)
+/* Add connection to (service, client) state. */
-#define ANVIL_ADD_RCPT(remote) \
+#define ANVIL_REMOTE_INCR_RATE(remote, _what) \
do { \
time_t _now = event_time(); \
- if ((remote)->start + var_anvil_time_unit < _now) { \
- (remote)->rate = 0; \
- (remote)->mail = 0; \
- (remote)->rcpt = 1; \
- (remote)->newtls = 0; \
- (remote)->start = _now; \
- } else if ((remote)->rcpt < INT_MAX) { \
- (remote)->rcpt += 1; \
- } \
+ if ((remote)->start + var_anvil_time_unit < _now) \
+ ANVIL_REMOTE_RSET_RATE((remote), _now); \
+ if ((remote)->_what < INT_MAX) \
+ (remote)->_what += 1; \
} while(0)
-#define ANVIL_ADD_STARTTLS(remote) \
+#define ANVIL_REMOTE_NEXT_CONN(remote) \
do { \
- time_t _now = event_time(); \
- if ((remote)->start + var_anvil_time_unit < _now) { \
- (remote)->rate = 0; \
- (remote)->mail = 0; \
- (remote)->rcpt = 0; \
- (remote)->newtls = 1; \
- (remote)->start = _now; \
- } else if ((remote)->newtls < INT_MAX) { \
- (remote)->newtls += 1; \
- } \
+ ANVIL_REMOTE_INCR_RATE((remote), rate); \
+ if ((remote)->count == 0) \
+ event_cancel_timer(anvil_remote_expire, (char *) remote); \
+ (remote)->count++; \
} while(0)
+#define ANVIL_REMOTE_INCR_MAIL(remote) ANVIL_REMOTE_INCR_RATE((remote), mail)
+
+#define ANVIL_REMOTE_INCR_RCPT(remote) ANVIL_REMOTE_INCR_RATE((remote), rcpt)
+
+#define ANVIL_REMOTE_INCR_NTLS(remote) ANVIL_REMOTE_INCR_RATE((remote), ntls)
+
/* Drop connection from (service, client) state. */
#define ANVIL_REMOTE_DROP_ONE(remote) \
anvil_remote_disconnect((stream), (local)->anvil_remote->ident); \
} while (0)
+ /*
+ * Lookup table to map request names to action routines.
+ */
+typedef struct {
+ const char *name;
+ void (*action) (VSTREAM *, const char *);
+} ANVIL_REQ_TABLE;
+
+ /*
+ * Silly little macros.
+ */
+#define STR(x) vstring_str(x)
+#define STREQ(x,y) (strcmp((x), (y)) == 0)
+
/* anvil_remote_expire - purge expired connection state */
static void anvil_remote_expire(int unused_event, char *context)
{
ANVIL_REMOTE *anvil_remote;
char *myname = "anvil_remote_lookup";
- HTABLE_INFO **ht_info;
- HTABLE_INFO **ht;
if (msg_verbose)
msg_info("%s fd=%d stream=0x%lx ident=%s",
/*
* Look up remote client information.
*/
- if (STREQ(ident, "*")) {
- attr_print_plain(client_stream, ATTR_FLAG_MORE,
- ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
- ATTR_TYPE_END);
- ht_info = htable_list(anvil_remote_map);
- for (ht = ht_info; *ht; ht++) {
- anvil_remote = (ANVIL_REMOTE *) ht[0]->value;
- attr_print_plain(client_stream, ATTR_FLAG_MORE,
- ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ht[0]->key,
- ATTR_TYPE_NUM, ANVIL_ATTR_COUNT, anvil_remote->count,
- ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->rate,
- ATTR_TYPE_NUM, ANVIL_ATTR_MAIL, anvil_remote->mail,
- ATTR_TYPE_NUM, ANVIL_ATTR_RCPT, anvil_remote->rcpt,
- ATTR_TYPE_END);
- }
- attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_END);
- myfree((char *) ht_info);
- } else if ((anvil_remote =
- (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) {
+ if ((anvil_remote =
+ (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) {
attr_print_plain(client_stream, ATTR_FLAG_NONE,
- ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_FAIL,
+ ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
ATTR_TYPE_NUM, ANVIL_ATTR_COUNT, 0,
ATTR_TYPE_NUM, ANVIL_ATTR_RATE, 0,
ATTR_TYPE_NUM, ANVIL_ATTR_MAIL, 0,
ATTR_TYPE_NUM, ANVIL_ATTR_RCPT, 0,
+ ATTR_TYPE_NUM, ANVIL_ATTR_NTLS, 0,
ATTR_TYPE_END);
} else {
+
+ /*
+ * Do not report stale information.
+ */
+ if (anvil_remote->start != 0
+ && anvil_remote->start + var_anvil_time_unit < event_time())
+ ANVIL_REMOTE_RSET_RATE(anvil_remote, 0);
attr_print_plain(client_stream, ATTR_FLAG_NONE,
ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
ATTR_TYPE_NUM, ANVIL_ATTR_COUNT, anvil_remote->count,
ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->rate,
ATTR_TYPE_NUM, ANVIL_ATTR_MAIL, anvil_remote->mail,
ATTR_TYPE_NUM, ANVIL_ATTR_RCPT, anvil_remote->rcpt,
+ ATTR_TYPE_NUM, ANVIL_ATTR_NTLS, anvil_remote->ntls,
ATTR_TYPE_END);
}
}
if ((anvil_remote =
(ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) {
anvil_remote = (ANVIL_REMOTE *) mymalloc(sizeof(*anvil_remote));
- ANVIL_REMOTE_FIRST(anvil_remote, ident);
+ ANVIL_REMOTE_FIRST_CONN(anvil_remote, ident);
htable_enter(anvil_remote_map, ident, (char *) anvil_remote);
- if (max_cache < anvil_remote_map->used) {
- max_cache = anvil_remote_map->used;
+ if (max_cache_size < anvil_remote_map->used) {
+ max_cache_size = anvil_remote_map->used;
max_cache_time = event_time();
}
} else {
- ANVIL_REMOTE_NEXT(anvil_remote);
+ ANVIL_REMOTE_NEXT_CONN(anvil_remote);
}
/*
ATTR_TYPE_END);
/*
- * Update local statistics.
+ * Update peak statistics.
*/
- if (anvil_remote->rate > max_rate) {
- max_rate = anvil_remote->rate;
- if (max_rate_user == 0) {
- max_rate_user = mystrdup(anvil_remote->ident);
- } else if (!STREQ(max_rate_user, anvil_remote->ident)) {
- myfree(max_rate_user);
- max_rate_user = mystrdup(anvil_remote->ident);
- }
- max_rate_time = event_time();
- }
- if (anvil_remote->count > max_count) {
- max_count = anvil_remote->count;
- if (max_count_user == 0) {
- max_count_user = mystrdup(anvil_remote->ident);
- } else if (!STREQ(max_count_user, anvil_remote->ident)) {
- myfree(max_count_user);
- max_count_user = mystrdup(anvil_remote->ident);
- }
- max_count_time = event_time();
- }
+ if (anvil_remote->rate > max_conn_rate.value)
+ ANVIL_MAX_UPDATE(max_conn_rate, anvil_remote->rate, anvil_remote->ident);
+ if (anvil_remote->count > max_conn_count.value)
+ ANVIL_MAX_UPDATE(max_conn_count, anvil_remote->count, anvil_remote->ident);
}
/* anvil_remote_mail - register message delivery request */
/*
* Update message delivery request rate and respond to local client.
*/
- ANVIL_ADD_MAIL(anvil_remote);
+ ANVIL_REMOTE_INCR_MAIL(anvil_remote);
attr_print_plain(client_stream, ATTR_FLAG_NONE,
ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->mail,
ATTR_TYPE_END);
/*
- * Update local statistics.
+ * Update peak statistics.
*/
- if (anvil_remote->mail > max_mail) {
- max_mail = anvil_remote->mail;
- if (max_mail_user == 0) {
- max_mail_user = mystrdup(anvil_remote->ident);
- } else if (!STREQ(max_mail_user, anvil_remote->ident)) {
- myfree(max_mail_user);
- max_mail_user = mystrdup(anvil_remote->ident);
- }
- max_mail_time = event_time();
- }
+ if (anvil_remote->mail > max_mail_rate.value)
+ ANVIL_MAX_UPDATE(max_mail_rate, anvil_remote->mail, anvil_remote->ident);
}
/* anvil_remote_rcpt - register recipient address event */
/*
* Update recipient address rate and respond to local client.
*/
- ANVIL_ADD_RCPT(anvil_remote);
+ ANVIL_REMOTE_INCR_RCPT(anvil_remote);
attr_print_plain(client_stream, ATTR_FLAG_NONE,
ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->rcpt,
ATTR_TYPE_END);
/*
- * Update local statistics.
+ * Update peak statistics.
*/
- if (anvil_remote->rcpt > max_rcpt) {
- max_rcpt = anvil_remote->rcpt;
- if (max_rcpt_user == 0) {
- max_rcpt_user = mystrdup(anvil_remote->ident);
- } else if (!STREQ(max_rcpt_user, anvil_remote->ident)) {
- myfree(max_rcpt_user);
- max_rcpt_user = mystrdup(anvil_remote->ident);
- }
- max_rcpt_time = event_time();
- }
+ if (anvil_remote->rcpt > max_rcpt_rate.value)
+ ANVIL_MAX_UPDATE(max_rcpt_rate, anvil_remote->rcpt, anvil_remote->ident);
}
/* anvil_remote_newtls - register newtls event */
/*
* Update newtls rate and respond to local client.
*/
- ANVIL_ADD_STARTTLS(anvil_remote);
+ ANVIL_REMOTE_INCR_NTLS(anvil_remote);
attr_print_plain(client_stream, ATTR_FLAG_NONE,
ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
- ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->newtls,
+ ATTR_TYPE_NUM, ANVIL_ATTR_RATE, anvil_remote->ntls,
ATTR_TYPE_END);
/*
- * Update local statistics.
+ * Update peak statistics.
*/
- if (anvil_remote->newtls > max_newtls) {
- max_newtls = anvil_remote->newtls;
- if (max_newtls_user == 0) {
- max_newtls_user = mystrdup(anvil_remote->ident);
- } else if (!STREQ(max_newtls_user, anvil_remote->ident)) {
- myfree(max_newtls_user);
- max_newtls_user = mystrdup(anvil_remote->ident);
- }
- max_newtls_time = event_time();
+ if (anvil_remote->ntls > max_ntls_rate.value)
+ ANVIL_MAX_UPDATE(max_ntls_rate, anvil_remote->ntls, anvil_remote->ident);
+}
+
+/* anvil_remote_newtls_stat - report newtls stats */
+
+static void anvil_remote_newtls_stat(VSTREAM *client_stream, const char *ident)
+{
+ ANVIL_REMOTE *anvil_remote;
+ int rate;
+
+ /*
+ * Be prepared for "postfix reload" after "connect".
+ */
+ if ((anvil_remote =
+ (ANVIL_REMOTE *) htable_find(anvil_remote_map, ident)) == 0) {
+ rate = 0;
+ }
+
+ /*
+ * Do not report stale information.
+ */
+ else {
+ if (anvil_remote->start != 0
+ && anvil_remote->start + var_anvil_time_unit < event_time())
+ ANVIL_REMOTE_RSET_RATE(anvil_remote, 0);
+ rate = anvil_remote->ntls;
}
+
+ /*
+ * Respond to local client.
+ */
+ attr_print_plain(client_stream, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK,
+ ATTR_TYPE_NUM, ANVIL_ATTR_RATE, rate,
+ ATTR_TYPE_END);
}
/* anvil_remote_disconnect - report disconnect event */
static void anvil_service(VSTREAM *client_stream, char *unused_service, char **argv)
{
- VSTRING *request = vstring_alloc(10);
- VSTRING *ident = vstring_alloc(10);
+ static VSTRING *request;
+ static VSTRING *ident;
+ static ANVIL_REQ_TABLE request_table[] = {
+ ANVIL_REQ_CONN, anvil_remote_connect,
+ ANVIL_REQ_MAIL, anvil_remote_mail,
+ ANVIL_REQ_RCPT, anvil_remote_rcpt,
+ ANVIL_REQ_NTLS, anvil_remote_newtls,
+ ANVIL_REQ_DISC, anvil_remote_disconnect,
+ ANVIL_REQ_NTLS_STAT, anvil_remote_newtls_stat,
+ ANVIL_REQ_LOOKUP, anvil_remote_lookup,
+ 0, 0,
+ };
+ ANVIL_REQ_TABLE *rp;
/*
* Sanity check. This service takes no command-line arguments.
if (argv[0])
msg_fatal("unexpected command-line argument: %s", argv[0]);
+ /*
+ * Initialize.
+ */
+ if (request == 0) {
+ request = vstring_alloc(10);
+ ident = vstring_alloc(10);
+ }
+
/*
* This routine runs whenever a client connects to the socket dedicated
* to the client connection rate management service. All
ATTR_TYPE_STR, ANVIL_ATTR_REQ, request,
ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
ATTR_TYPE_END) == 2) {
- if (STREQ(STR(request), ANVIL_REQ_CONN)) {
- anvil_remote_connect(client_stream, STR(ident));
- } else if (STREQ(STR(request), ANVIL_REQ_MAIL)) {
- anvil_remote_mail(client_stream, STR(ident));
- } else if (STREQ(STR(request), ANVIL_REQ_RCPT)) {
- anvil_remote_rcpt(client_stream, STR(ident));
- } else if (STREQ(STR(request), ANVIL_REQ_NEWTLS)) {
- anvil_remote_newtls(client_stream, STR(ident));
- } else if (STREQ(STR(request), ANVIL_REQ_DISC)) {
- anvil_remote_disconnect(client_stream, STR(ident));
- } else if (STREQ(STR(request), ANVIL_REQ_LOOKUP)) {
- anvil_remote_lookup(client_stream, STR(ident));
- } else {
- msg_warn("unrecognized request: \"%s\", ignored", STR(request));
- attr_print_plain(client_stream, ATTR_FLAG_NONE,
+ for (rp = request_table; /* see below */ ; rp++) {
+ if (rp->name == 0) {
+ msg_warn("unrecognized request: \"%s\", ignored", STR(request));
+ attr_print_plain(client_stream, ATTR_FLAG_NONE,
ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_FAIL,
- ATTR_TYPE_END);
+ ATTR_TYPE_END);
+ break;
+ }
+ if (STREQ(rp->name, STR(request))) {
+ rp->action(client_stream, STR(ident));
+ break;
+ }
}
vstream_fflush(client_stream);
} else {
}
if (msg_verbose)
msg_info("--- end request ---");
- vstring_free(ident);
- vstring_free(request);
}
/* post_jail_init - post-jail initialization */
static void anvil_status_dump(char *unused_name, char **unused_argv)
{
- if (max_rate > 0) {
- msg_info("statistics: max connection rate %d/%ds for (%s) at %.15s",
- max_rate, var_anvil_time_unit,
- max_rate_user, ctime(&max_rate_time) + 4);
- max_rate = 0;
- }
- if (max_count > 0) {
- msg_info("statistics: max connection count %d for (%s) at %.15s",
- max_count, max_count_user, ctime(&max_count_time) + 4);
- max_count = 0;
- }
- if (max_mail > 0) {
- msg_info("statistics: max message rate %d/%ds for (%s) at %.15s",
- max_mail, var_anvil_time_unit,
- max_mail_user, ctime(&max_mail_time) + 4);
- max_mail = 0;
- }
- if (max_rcpt > 0) {
- msg_info("statistics: max recipient rate %d/%ds for (%s) at %.15s",
- max_rcpt, var_anvil_time_unit,
- max_rcpt_user, ctime(&max_rcpt_time) + 4);
- max_rcpt = 0;
- }
- if (max_newtls > 0) {
- msg_info("statistics: max newtls rate %d/%ds for (%s) at %.15s",
- max_newtls, var_anvil_time_unit,
- max_newtls_user, ctime(&max_newtls_time) + 4);
- max_newtls = 0;
- }
- if (max_cache > 0) {
+ ANVIL_MAX_RATE_REPORT(max_conn_rate, "connection");
+ ANVIL_MAX_COUNT_REPORT(max_conn_count, "connection");
+ ANVIL_MAX_RATE_REPORT(max_mail_rate, "message");
+ ANVIL_MAX_RATE_REPORT(max_rcpt_rate, "recipient");
+ ANVIL_MAX_RATE_REPORT(max_ntls_rate, "newtls");
+
+ if (max_cache_size > 0) {
msg_info("statistics: max cache size %d at %.15s",
- max_cache, ctime(&max_cache_time) + 4);
- max_cache = 0;
+ max_cache_size, ctime(&max_cache_time) + 4);
+ max_cache_size = 0;
}
}
/* const char *addr;
/* int *newtls;
/*
+/* int anvil_clnt_newtls_stat(anvil_clnt, service, addr, newtls)
+/* ANVIL_CLNT *anvil_clnt;
+/* const char *service;
+/* const char *addr;
+/* int *newtls;
+/*
/* int anvil_clnt_disconnect(anvil_clnt, service, addr)
/* ANVIL_CLNT *anvil_clnt;
/* const char *service;
/* int *msgs;
/* int *rcpts;
/* DESCRIPTION
-/* anvil_clnt_create() instantiates an anvil service client endpoint.
+/* anvil_clnt_create() instantiates a local anvil service
+/* client endpoint.
/*
/* anvil_clnt_connect() informs the anvil server that a
-/* client has connected, and returns the current connection
-/* count and connection rate for that client.
+/* remote client has connected, and returns the current
+/* connection count and connection rate for that remote client.
/*
-/* anvil_clnt_mail() registers a MAIL FROM event and returns
-/* the current MAIL FROM rate for the specified client.
+/* anvil_clnt_mail() registers a MAIL FROM event and
+/* returns the current MAIL FROM rate for the specified remote
+/* client.
/*
-/* anvil_clnt_rcpt() registers a RCPT TO event and returns
-/* the current RCPT TO rate for the specified client.
+/* anvil_clnt_rcpt() registers a RCPT TO event and
+/* returns the current RCPT TO rate for the specified remote
+/* client.
/*
-/* anvil_clnt_newtls() registers a request to negotiate a new
-/* (uncached) TLS session and returns the current request rate
-/* for the specified client.
+/* anvil_clnt_newtls() registers a remote client request
+/* to negotiate a new (uncached) TLS session and returns the
+/* current newtls request rate for the specified remote client.
/*
-/* anvil_clnt_disconnect() informs the anvil server that a
+/* anvil_clnt_newtls_stat() returns the current newtls request
+/* rate for the specified remote client.
+/*
+/* anvil_clnt_disconnect() informs the anvil server that a remote
/* client has disconnected.
/*
-/* anvil_clnt_lookup() looks up the current connection
-/* count and connection rate for that client.
+/* anvil_clnt_lookup() returns the current count and rate
+/* information for the specified client.
/*
-/* anvil_clnt_free() destroys an anvil service client endpoint.
+/* anvil_clnt_free() destroys a local anvil service client
+/* endpoint.
/*
/* Arguments:
/* .IP anvil_clnt
/* Pointer to storage for the current "new TLS session" rate
/* for this remote client.
/* DIAGNOSTICS
-/* anvil_clnt_connect() and anvil_clnt_disconnect() return
+/* The update and status query routines return
/* ANVIL_STAT_OK in case of success, ANVIL_STAT_FAIL otherwise
/* (either the communication with the server is broken or the
/* server experienced a problem).
int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service,
const char *addr, int *count, int *rate,
- int *msgs, int *rcpts)
+ int *msgs, int *rcpts, int *newtls)
{
char *ident = ANVIL_IDENT(service, addr);
int status;
ATTR_TYPE_NUM, ANVIL_ATTR_RATE, rate,
ATTR_TYPE_NUM, ANVIL_ATTR_MAIL, msgs,
ATTR_TYPE_NUM, ANVIL_ATTR_RCPT, rcpts,
- ATTR_TYPE_END) != 5)
+ ATTR_TYPE_NUM, ANVIL_ATTR_NTLS, newtls,
+ ATTR_TYPE_END) != 6)
status = ANVIL_STAT_FAIL;
else if (status != ANVIL_STAT_OK)
status = ANVIL_STAT_FAIL;
return (status);
}
-/* anvil_clnt_connect - heads-up and policy query */
+/* anvil_clnt_connect - heads-up and status query */
int anvil_clnt_connect(ANVIL_CLNT *anvil_clnt, const char *service,
const char *addr, int *count, int *rate)
return (status);
}
-/* anvil_clnt_mail - heads-up and policy query */
+/* anvil_clnt_mail - heads-up and status query */
int anvil_clnt_mail(ANVIL_CLNT *anvil_clnt, const char *service,
const char *addr, int *msgs)
return (status);
}
-/* anvil_clnt_rcpt - heads-up and policy query */
+/* anvil_clnt_rcpt - heads-up and status query */
int anvil_clnt_rcpt(ANVIL_CLNT *anvil_clnt, const char *service,
const char *addr, int *rcpts)
return (status);
}
-/* anvil_clnt_newtls - heads-up and policy query */
+/* anvil_clnt_newtls - heads-up and status query */
+
+int anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service,
+ const char *addr, int *newtls)
+{
+ char *ident = ANVIL_IDENT(service, addr);
+ int status;
+
+ if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
+ ATTR_FLAG_NONE, /* Query attributes. */
+ ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_NTLS,
+ ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
+ ATTR_TYPE_END,
+ ATTR_FLAG_MISSING, /* Reply attributes. */
+ ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, &status,
+ ATTR_TYPE_NUM, ANVIL_ATTR_RATE, newtls,
+ ATTR_TYPE_END) != 2)
+ status = ANVIL_STAT_FAIL;
+ else if (status != ANVIL_STAT_OK)
+ status = ANVIL_STAT_FAIL;
+ myfree(ident);
+ return (status);
+}
+
+/* anvil_clnt_newtls_stat - status query */
-int anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service,
- const char *addr, int *newtls)
+int anvil_clnt_newtls_stat(ANVIL_CLNT *anvil_clnt, const char *service,
+ const char *addr, int *newtls)
{
char *ident = ANVIL_IDENT(service, addr);
- int status;
+ int status;
- if (attr_clnt_request((ATTR_CLNT *)anvil_clnt,
+ if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
ATTR_FLAG_NONE, /* Query attributes. */
- ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_NEWTLS,
+ ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_NTLS_STAT,
ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
ATTR_TYPE_END,
ATTR_FLAG_MISSING, /* Reply attributes. */
/* anvil_clnt_disconnect - heads-up only */
-int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service,
- const char *addr)
+int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service,
+ const char *addr)
{
char *ident = ANVIL_IDENT(service, addr);
- int status;
+ int status;
- if (attr_clnt_request((ATTR_CLNT *)anvil_clnt,
+ if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
ATTR_FLAG_NONE, /* Query attributes. */
ATTR_TYPE_STR, ANVIL_ATTR_REQ, ANVIL_REQ_DISC,
ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
static void usage(void)
{
- vstream_printf("usage: %s service addr | %s service addr |"
- " %s service addr | %s service addr\n",
- ANVIL_REQ_CONN, ANVIL_REQ_DISC,
- ANVIL_REQ_MAIL, ANVIL_REQ_RCPT);
+ vstream_printf("usage: "
+ ANVIL_REQ_CONN " service addr | "
+ ANVIL_REQ_DISC " service addr | "
+ ANVIL_REQ_MAIL " service addr | "
+ ANVIL_REQ_RCPT " service addr | "
+ ANVIL_REQ_NTLS " service addr | "
+ ANVIL_REQ_NTLS_STAT " service addr | "
+ ANVIL_REQ_LOOKUP " service addr\n");
}
-int main(int unused_argc, char **argv)
+int main(int unused_argc, char **argv)
{
VSTRING *inbuf = vstring_alloc(1);
char *bufp;
ssize_t cmd_len;
char *service;
char *addr;
- int count;
- int rate;
- int msgs;
- int rcpts;
+ int count;
+ int rate;
+ int msgs;
+ int rcpts;
+ int newtls;
ANVIL_CLNT *anvil;
msg_vstream_init(argv[0], VSTREAM_ERR);
msg_warn("error!");
else
vstream_printf("rate=%d\n", rcpts);
+ } else if (strncmp(cmd, ANVIL_REQ_NTLS, cmd_len) == 0) {
+ if (anvil_clnt_newtls(anvil, service, addr, &newtls) != ANVIL_STAT_OK)
+ msg_warn("error!");
+ else
+ vstream_printf("rate=%d\n", newtls);
+ } else if (strncmp(cmd, ANVIL_REQ_NTLS_STAT, cmd_len) == 0) {
+ if (anvil_clnt_newtls_stat(anvil, service, addr, &newtls) != ANVIL_STAT_OK)
+ msg_warn("error!");
+ else
+ vstream_printf("rate=%d\n", newtls);
} else if (strncmp(cmd, ANVIL_REQ_DISC, cmd_len) == 0) {
if (anvil_clnt_disconnect(anvil, service, addr) != ANVIL_STAT_OK)
msg_warn("error!");
vstream_printf("OK\n");
} else if (strncmp(cmd, ANVIL_REQ_LOOKUP, cmd_len) == 0) {
if (anvil_clnt_lookup(anvil, service, addr, &count, &rate,
- &msgs, &rcpts) != ANVIL_STAT_OK)
+ &msgs, &rcpts, &newtls) != ANVIL_STAT_OK)
msg_warn("error!");
else
- vstream_printf("count=%d, rate=%d msgs=%d rcpts=%d\n",
- count, rate, msgs, rcpts);
+ vstream_printf("count=%d, rate=%d msgs=%d rcpts=%d newtls=%d\n",
+ count, rate, msgs, rcpts, newtls);
} else {
vstream_printf("bad command: \"%s\"\n", cmd);
usage();
#define ANVIL_REQ_DISC "disconnect"
#define ANVIL_REQ_MAIL "message"
#define ANVIL_REQ_RCPT "recipient"
-#define ANVIL_REQ_NEWTLS "newtls"
+#define ANVIL_REQ_NTLS "newtls"
+#define ANVIL_REQ_NTLS_STAT "newtls_status"
#define ANVIL_REQ_LOOKUP "lookup"
#define ANVIL_ATTR_IDENT "ident"
#define ANVIL_ATTR_COUNT "count"
#define ANVIL_ATTR_RATE "rate"
#define ANVIL_ATTR_MAIL "mail"
#define ANVIL_ATTR_RCPT "rcpt"
+#define ANVIL_ATTR_NTLS "newtls"
#define ANVIL_ATTR_STATUS "status"
#define ANVIL_STAT_OK 0
extern int anvil_clnt_mail(ANVIL_CLNT *, const char *, const char *, int *);
extern int anvil_clnt_rcpt(ANVIL_CLNT *, const char *, const char *, int *);
extern int anvil_clnt_newtls(ANVIL_CLNT *, const char *, const char *, int *);
-extern int anvil_clnt_lookup(ANVIL_CLNT *, const char *, const char *, int *, int *, int *, int *);
+extern int anvil_clnt_newtls_stat(ANVIL_CLNT *, const char *, const char *, int *);
+extern int anvil_clnt_lookup(ANVIL_CLNT *, const char *, const char *, int *, int *, int *, int *, int *);
extern int anvil_clnt_disconnect(ANVIL_CLNT *, const char *, const char *);
extern void anvil_clnt_free(ANVIL_CLNT *);
request->sasl_username = 0;
request->sasl_sender = 0;
request->rewrite_context = 0;
+ request->dsn_envid = 0;
return (request);
}
abcdefghijklmnopqrstuvwxyz{|}~"
extern char *var_smtpd_exp_filter;
+#define VAR_SMTPD_PEERNAME_LOOKUP "smtpd_peername_lookup"
+#define DEF_SMTPD_PEERNAME_LOOKUP 1
+extern bool var_smtpd_peername_lookup;
+
/*
* Heuristic to reject unknown local recipients at the SMTP port.
*/
#define DEF_SMTPD_CRCPT_LIMIT 0
extern int var_smtpd_crcpt_limit;
+#define VAR_SMTPD_CNTLS_LIMIT "smtpd_client_new_tls_session_rate_limit"
+#define DEF_SMTPD_CNTLS_LIMIT 0
+extern int var_smtpd_cntls_limit;
+
#define VAR_SMTPD_HOGGERS "smtpd_client_event_limit_exceptions"
#define DEF_SMTPD_HOGGERS "${smtpd_client_connection_limit_exceptions:$" VAR_MYNETWORKS "}"
extern char *var_smtpd_hoggers;
#define DEF_FROZEN_DELIVERED 1
extern bool var_frozen_delivered;
-#define VAR_STICKY_OWNER_ALIAS "sticky_owner_alias"
-#define DEF_STICKY_OWNER_ALIAS 1
-extern bool var_sticky_owner_alias;
-
/* LICENSE
/* .ad
/* .fi
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20050923"
+#define MAIL_RELEASE_DATE "20051010"
#define MAIL_VERSION_NUMBER "2.3"
#ifdef SNAPSHOT
/* An I/O error happened, or the peer has disconnected unexpectedly.
/* .IP SMTP_ERR_TIME
/* The time limit specified to smtp_timeout_setup() was exceeded.
+/* .IP SMTP_ERR_PROTO
+/* This error is never generated by the smtp_stream(3) module, but
+/* is defined for application-specific use.
/* BUGS
/* The timeout deadline affects all I/O on the named stream, not
/* just the I/O done on behalf of this module.
*/
#define SMTP_ERR_EOF 1 /* unexpected client disconnect */
#define SMTP_ERR_TIME 2 /* time out */
+#define SMTP_ERR_PROTO 3 /* protocol (application) */
extern void smtp_timeout_setup(VSTREAM *, int);
extern void PRINTFLIKE(2, 3) smtp_printf(VSTREAM *, const char *,...);
if (*cp == ' ' || *cp == 0)
break;
}
+
+ /*
+ * XXX Do not ignore garbage when ESMTP command pipelining is turned
+ * on. After sending ".<CR><LF>QUIT<CR><LF>", Postfix might recognize
+ * the server's 2XX QUIT reply as a 2XX END-OF-DATA reply after
+ * garbage, causing mail to be lost. Instead, make a long jump so
+ * that all recipients of multi-recipient mail get consistent
+ * treatment.
+ */
state->error_mask |= MAIL_ERROR_PROTOCOL;
+ if (state->features & LMTP_FEATURE_PIPELINING) {
+ msg_warn("non-SMTP response from %s: %s",
+ session->namaddr, STR(state->buffer));
+ vstream_longjmp(session->stream, SMTP_ERR_PROTO);
+ }
}
/*
"conversation with %s timed out while %s",
session->namaddr, description);
break;
+ case SMTP_ERR_PROTO:
+ lmtp_fill_dsn(state, &dsn, DSN_BY_LOCAL_MTA,
+ "4.5.0", "403 remote protocol error",
+ "protocol error in reply from %s while %s",
+ session->namaddr, description);
+ break;
}
return (lmtp_bulk_fail(state, &dsn, LMTP_THROTTLE));
}
SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level);
} else {
canon_owner = 0;
- if (var_sticky_owner_alias == 0)
- RESET_OWNER_ATTR(state.msg_attr, state.level);
+ RESET_OWNER_ATTR(state.msg_attr, state.level);
}
/*
/* address (see prepend_delivered_header) only once, at the start of
/* a delivery attempt; do not update the Delivered-To: address while
/* expanding aliases or .forward files.
-/* .IP "\fBsticky_owner_alias (yes)\fR"
-/* When expanding a \fBlocal\fR(8) alias that has an owner alias (see
-/* owner-\fIname\fR discussion in \fBaliases\fR(5)), use the owner information
-/* even when the expansion invokes a subordinate alias that has no
-/* owner alias.
/* DELIVERY METHOD CONTROLS
/* .ad
/* .fi
char *var_mailbox_lock;
int var_mailbox_limit;
bool var_frozen_delivered;
-bool var_sticky_owner_alias;
int local_cmd_deliver_mask;
int local_file_deliver_mask;
VAR_STAT_HOME_DIR, DEF_STAT_HOME_DIR, &var_stat_home_dir,
VAR_MAILTOOL_COMPAT, DEF_MAILTOOL_COMPAT, &var_mailtool_compat,
VAR_FROZEN_DELIVERED, DEF_FROZEN_DELIVERED, &var_frozen_delivered,
- VAR_STICKY_OWNER_ALIAS, DEF_STICKY_OWNER_ALIAS, &var_sticky_owner_alias,
0,
};
if (*cp == ' ' || *cp == 0)
break;
}
+
+ /*
+ * XXX Do not ignore garbage when ESMTP command pipelining is turned
+ * on. After sending ".<CR><LF>QUIT<CR><LF>", Postfix might recognize
+ * the server's 2XX QUIT reply as a 2XX END-OF-DATA reply after
+ * garbage, causing mail to be lost. Instead, make a long jump so
+ * that all recipients of multi-recipient mail get consistent
+ * treatment.
+ */
session->error_mask |= MAIL_ERROR_PROTOCOL;
+ if (session->features & SMTP_FEATURE_PIPELINING) {
+ msg_warn("non-SMTP response from %s: %s",
+ session->namaddr, STR(session->buffer));
+ vstream_longjmp(session->stream, SMTP_ERR_PROTO);
+ }
}
/*
"conversation with %s timed out while %s",
session->namaddr, description);
break;
+ case SMTP_ERR_PROTO:
+ smtp_fill_dsn(state, &dsn, DSN_BY_LOCAL_MTA,
+ "4.5.0", "403 remote protocol error",
+ "protocol error in reply from %s while %s",
+ session->namaddr, description);
+ break;
}
return (smtp_bulk_fail(state, &dsn, SMTP_THROTTLE));
}
smtpd_peer.o: ../../include/attr.h
smtpd_peer.o: ../../include/inet_proto.h
smtpd_peer.o: ../../include/iostuff.h
+smtpd_peer.o: ../../include/mail_params.h
smtpd_peer.o: ../../include/mail_proto.h
smtpd_peer.o: ../../include/mail_stream.h
smtpd_peer.o: ../../include/msg.h
/* The maximal number of lines in the Postfix SMTP server command history
/* before it is flushed upon receipt of EHLO, RSET, or end of DATA.
/* .PP
+/* Available in Postfix version 2.3 and later:
+/* .IP "\fBsmtpd_peername_lookup (yes)\fR"
+/* Attempt to look up the SMTP client hostname, and verify that
+/* the name matches the client IP address.
+/* .PP
/* The per SMTP client connection count and request rate limits are
/* implemented in co-operation with the \fBanvil\fR(8) service, and
/* are available in Postfix version 2.2 and later.
/* .IP "\fBsmtpd_client_event_limit_exceptions ($mynetworks)\fR"
/* Clients that are excluded from connection count, connection rate,
/* or SMTP request rate restrictions.
+/* .PP
+/* Available in Postfix version 2.3 and later:
+/* .IP "\fBsmtpd_client_new_tls_session_rate_limit (0)\fR"
+/* The maximal number of new (i.e., uncached) TLS sessions that any
+/* client is allowed to negotiate with this service per time unit.
/* TARPIT CONTROLS
/* .ad
/* .fi
int var_smtpd_cconn_limit;
int var_smtpd_cmail_limit;
int var_smtpd_crcpt_limit;
+int var_smtpd_cntls_limit;
char *var_smtpd_hoggers;
char *var_local_rwr_clients;
char *var_smtpd_ehlo_dis_words;
#endif
+bool var_smtpd_peername_lookup;
+
/*
* Silly little macros.
*/
static void helo_reset(SMTPD_STATE *);
static void mail_reset(SMTPD_STATE *);
static void rcpt_reset(SMTPD_STATE *);
+static void tls_reset(SMTPD_STATE *);
static void chat_reset(SMTPD_STATE *, int);
/*
static void smtpd_start_tls(SMTPD_STATE *state)
{
+ int rate;
+
+ /*
+ * XXX The client event count/rate control must be consistent in its use
+ * of client address information in connect and disconnect events. For
+ * now we exclude xclient authorized hosts from event count/rate control.
+ */
+ if (SMTPD_STAND_ALONE(state) == 0
+ && !xclient_allowed
+ && anvil_clnt
+ && var_smtpd_cntls_limit > 0
+ && !namadr_list_match(hogger_list, state->name, state->addr)
+ && anvil_clnt_newtls_stat(anvil_clnt, state->service, state->addr,
+ &rate) == ANVIL_STAT_OK
+ && rate > var_smtpd_cntls_limit) {
+ msg_warn("Refusing STARTTLS request from %s for service %s",
+ state->namaddr, state->service);
+ vstream_longjmp(state->client, SMTP_ERR_EOF);
+ }
/*
* Wrapper mode uses a dedicated port and always requires TLS.
* verification unless TLS is required.
*/
state->tls_context =
- tls_server_start(smtpd_tls_ctx, state->client,
- var_smtpd_starttls_tmout, state->name, state->addr,
- (var_smtpd_tls_req_ccert && state->tls_enforce_tls));
+ tls_server_start(smtpd_tls_ctx, state->client,
+ var_smtpd_starttls_tmout, state->name, state->addr,
+ (var_smtpd_tls_req_ccert && state->tls_enforce_tls));
+
+ /*
+ * XXX The client event count/rate control must be consistent in its use
+ * of client address information in connect and disconnect events. For
+ * now we exclude xclient authorized hosts from event count/rate control.
+ */
+ if (state->tls_context
+ && state->tls_context->session_reused == 0
+ && SMTPD_STAND_ALONE(state) == 0
+ && !xclient_allowed
+ && anvil_clnt
+ && var_smtpd_cntls_limit > 0
+ && !namadr_list_match(hogger_list, state->name, state->addr)
+ && anvil_clnt_newtls(anvil_clnt, state->service, state->addr,
+ &rate) == ANVIL_STAT_OK
+ && rate > var_smtpd_cntls_limit) {
+ msg_warn("Too many uncached TLS sessions: "
+ "%d from %s for service %s",
+ rate, state->namaddr, state->service);
+ tls_reset(state);
+ }
/*
* When the TLS handshake fails, the conversation is in an unknown state.
* Connection rate management.
*/
if (var_smtpd_crate_limit || var_smtpd_cconn_limit
- || var_smtpd_cmail_limit || var_smtpd_crcpt_limit)
+ || var_smtpd_cmail_limit || var_smtpd_crcpt_limit
+ || var_smtpd_cntls_limit)
anvil_clnt = anvil_clnt_create();
}
VAR_SMTPD_CCONN_LIMIT, DEF_SMTPD_CCONN_LIMIT, &var_smtpd_cconn_limit, 0, 0,
VAR_SMTPD_CMAIL_LIMIT, DEF_SMTPD_CMAIL_LIMIT, &var_smtpd_cmail_limit, 0, 0,
VAR_SMTPD_CRCPT_LIMIT, DEF_SMTPD_CRCPT_LIMIT, &var_smtpd_crcpt_limit, 0, 0,
+ VAR_SMTPD_CNTLS_LIMIT, DEF_SMTPD_CNTLS_LIMIT, &var_smtpd_cntls_limit, 0, 0,
#ifdef USE_TLS
VAR_SMTPD_TLS_CCERT_VD, DEF_SMTPD_TLS_CCERT_VD, &var_smtpd_tls_ccert_vd, 0, 0,
#endif
VAR_SMTPD_TLS_RCERT, DEF_SMTPD_TLS_RCERT, &var_smtpd_tls_req_ccert,
VAR_SMTPD_TLS_RECHEAD, DEF_SMTPD_TLS_RECHEAD, &var_smtpd_tls_received_header,
#endif
+ VAR_SMTPD_PEERNAME_LOOKUP, DEF_SMTPD_PEERNAME_LOOKUP, &var_smtpd_peername_lookup,
0,
};
static CONFIG_STR_TABLE str_table[] = {
#include <mail_proto.h>
#include <valid_mailhost_addr.h>
+#include <mail_params.h>
/* Application-specific. */
state->name_status = code; \
}
- if ((aierr = sockaddr_to_hostname(sa, sa_len, &client_name,
- (MAI_SERVNAME_STR *) 0, 0)) != 0) {
+ if (var_smtpd_peername_lookup == 0) {
+ state->name = mystrdup(CLIENT_NAME_UNKNOWN);
+ state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
+#ifdef FORWARD_CLIENT_NAME
+ state->forward_name = mystrdup(CLIENT_NAME_UNKNOWN);
+#endif
+ state->name_status = SMTPD_PEER_CODE_PERM;
+ state->reverse_name_status = SMTPD_PEER_CODE_PERM;
+#ifdef FORWARD_CLIENT_NAME
+ state->forward_name_status = SMTPD_PEER_CODE_PERM;
+#endif
+ } else if ((aierr = sockaddr_to_hostname(sa, sa_len, &client_name,
+ (MAI_SERVNAME_STR *) 0, 0)) != 0) {
state->name = mystrdup(CLIENT_NAME_UNKNOWN);
state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
#ifdef FORWARD_CLIENT_NAME
const char *cipher_name;
int cipher_usebits;
int cipher_algbits;
- int log_level;
+ int log_level; /* TLS library logging level */
+ int session_reused; /* this session was reused */
} TLScontext_t;
#define TLS_BIO_BUFSIZE 8192
if (var_smtp_tls_loglevel < 4)
BIO_set_callback(SSL_get_rbio(TLScontext->con), 0);
+ /*
+ * The caller may want to know if this session was reused or if a new
+ * session was negotiated.
+ */
+ TLScontext->session_reused = SSL_session_reused(TLScontext->con);
+
/*
* Do peername verification if requested and extract useful information
* from the certificate for later use.
if (var_smtpd_tls_loglevel < 4)
BIO_set_callback(SSL_get_rbio(TLScontext->con), 0);
+ /*
+ * The caller may want to know if this session was reused or if a new
+ * session was negotiated.
+ */
+ TLScontext->session_reused = SSL_session_reused(TLScontext->con);
+
/*
* Let's see whether a peer certificate is available and what is the
* actual information. We want to save it for later use.
*/
if (requirecert) {
if (!TLScontext->peer_verified || !TLScontext->peer_CN) {
+ if (TLScontext->session_reused == 0)
+ msg_panic("tls_server_start: peer was not verified");
msg_info("Re-used session without peer certificate removed");
uncache_session(server_ctx, TLScontext);
tls_free_context(TLScontext);
/* DICT *ptr;
/* DESCRIPTION
/* dict_alloc() allocates memory for a dictionary structure of
-/* \fIsize\fR bytes, initializes all properties to default settings,
+/* \fIsize\fR bytes, initializes all generic dictionary
+/* properties to default settings,
/* and installs default methods that do not support any operation.
/* The caller is supposed to override the default methods with
/* ones that it supports.