2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 05:38:06 +00:00

postfix-2.3-20051010

This commit is contained in:
Wietse Venema 2005-10-10 00:00:00 -05:00 committed by Viktor Dukhovni
parent 14313fe35c
commit 88a2e39fc7
37 changed files with 932 additions and 543 deletions

2
postfix/.indent.pro vendored
View File

@ -3,7 +3,9 @@
-TALIAS_TOKEN
-TANVIL_CLNT
-TANVIL_LOCAL
-TANVIL_MAX
-TANVIL_REMOTE
-TANVIL_REQ_TABLE
-TARGV
-TATTR_CLNT
-TATTR_TABLE

View File

@ -11148,19 +11148,47 @@ Apologies for any names omitted.
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

View File

@ -17,7 +17,8 @@ Postfix source code, in the directory examples/smtpd-policy.
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:

View File

@ -26,12 +26,9 @@ files. With deeply nested aliases or .forward files, this can greatly
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
======================================

View File

@ -36,7 +36,8 @@ the Postfix source code, in the directory examples/smtpd-policy.
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>

View File

@ -13,107 +13,115 @@ ANVIL(8) ANVIL(8)
<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 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.
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.
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 LIMITING</b>
When a remote client connects, a connection count (or
rate) limited server should send the following request to
<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>
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.
<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>
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).
@ -135,10 +143,18 @@ ANVIL(8) ANVIL(8)
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
<a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.

View File

@ -386,13 +386,6 @@ LOCAL(8) LOCAL(8)
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>-

View File

@ -7205,6 +7205,47 @@ Example:
</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>
@ -7924,6 +7965,18 @@ null sender address.
</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>
@ -9149,22 +9202,6 @@ The default time unit is s (seconds).
</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>

View File

@ -548,6 +548,12 @@ SMTPD(8) SMTPD(8)
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.
@ -577,6 +583,13 @@ SMTPD(8) SMTPD(8)
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

View File

@ -4070,6 +4070,33 @@ smtpd_client_message_rate_limit = 1000
.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
@ -4540,6 +4567,12 @@ This list overrides any commands built into the Postfix SMTP server.
.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.
@ -5349,16 +5382,6 @@ This is used for delivery to file or mailbox.
.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.

View File

@ -12,23 +12,25 @@ Postfix session count and request rate control
.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
@ -36,11 +38,10 @@ server should send the following request to the \fBanvil\fR(8) server:
\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
@ -50,13 +51,8 @@ combination:
\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
@ -64,19 +60,16 @@ server should send the following request to the \fBanvil\fR(8) server:
\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
@ -85,28 +78,22 @@ request to the \fBanvil\fR(8) server:
\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
@ -114,10 +101,35 @@ the \fBanvil\fR(8) server:
\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
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
@ -125,9 +137,24 @@ for that (service, client) combination:
\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
.SH "SECURITY"
.na
.nf
@ -136,12 +163,15 @@ service should also register connect/disconnect events.
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
@ -165,14 +195,22 @@ In this preliminary implementation, a count (or rate) limited 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 \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.

View File

@ -399,11 +399,6 @@ Update the \fBlocal\fR(8) delivery agent's idea of the Delivered-To:
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

View File

@ -453,6 +453,11 @@ receiving a remote SMTP client request.
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.
@ -473,6 +478,11 @@ Postfix actually accepts those recipients.
.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

View File

@ -372,6 +372,7 @@ while (<>) {
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;
@ -390,6 +391,7 @@ while (<>) {
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;
@ -502,7 +504,6 @@ while (<>) {
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.

View File

@ -36,7 +36,8 @@ the Postfix source code, in the directory examples/smtpd-policy.
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>

View File

@ -4399,6 +4399,43 @@ Example:
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>
@ -8542,18 +8579,6 @@ message_strip_characters = \0
<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:
@ -8567,3 +8592,11 @@ Postfix releases, the behavior is as if this parameter is set 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>

View File

@ -6,21 +6,23 @@
/* 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
@ -28,11 +30,10 @@
/* \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
@ -42,13 +43,8 @@
/* \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
@ -56,17 +52,14 @@
/* \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
@ -75,26 +68,20 @@
/* \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
@ -102,10 +89,33 @@
/* \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
@ -113,21 +123,39 @@
/* \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).
/*
@ -147,12 +175,20 @@
/* 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.
@ -252,28 +288,53 @@ static HTABLE *anvil_remote_map; /* indexed by service+ remote client */
* 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.
@ -284,7 +345,7 @@ typedef struct {
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;
@ -296,12 +357,6 @@ typedef struct {
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.
@ -312,14 +367,14 @@ typedef struct {
/* 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)
@ -331,66 +386,41 @@ typedef struct {
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)->rate = 0; \
(remote)->mail = 0; \
(remote)->rcpt = 0; \
(remote)->newtls = 0; \
(remote)->start = _now; \
} else if ((remote)->rate < INT_MAX) { \
(remote)->rate += 1; \
} \
(remote)->ntls = 0; \
(remote)->start = _start; \
} while(0)
/* Add connection to (service, client) state. */
#define ANVIL_REMOTE_INCR_RATE(remote, _what) \
do { \
time_t _now = event_time(); \
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_REMOTE_NEXT_CONN(remote) \
do { \
ANVIL_REMOTE_INCR_RATE((remote), rate); \
if ((remote)->count == 0) \
event_cancel_timer(anvil_remote_expire, (char *) remote); \
(remote)->count++; \
} 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)
#define ANVIL_REMOTE_INCR_MAIL(remote) ANVIL_REMOTE_INCR_RATE((remote), mail)
#define ANVIL_ADD_RCPT(remote) \
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; \
} \
} while(0)
#define ANVIL_REMOTE_INCR_RCPT(remote) ANVIL_REMOTE_INCR_RATE((remote), rcpt)
#define ANVIL_ADD_STARTTLS(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; \
} \
} while(0)
#define ANVIL_REMOTE_INCR_NTLS(remote) ANVIL_REMOTE_INCR_RATE((remote), ntls)
/* Drop connection from (service, client) state. */
@ -443,6 +473,20 @@ typedef struct {
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)
@ -472,8 +516,6 @@ static void anvil_remote_lookup(VSTREAM *client_stream, const char *ident)
{
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",
@ -483,39 +525,31 @@ static void anvil_remote_lookup(VSTREAM *client_stream, const char *ident)
/*
* 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 =
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);
}
}
@ -542,14 +576,14 @@ static ANVIL_REMOTE *anvil_remote_conn_update(VSTREAM *client_stream, const char
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);
}
/*
@ -592,28 +626,12 @@ static void anvil_remote_connect(VSTREAM *client_stream, const char *ident)
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 */
@ -632,25 +650,17 @@ static void anvil_remote_mail(VSTREAM *client_stream, const char *ident)
/*
* 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 */
@ -669,25 +679,17 @@ static void anvil_remote_rcpt(VSTREAM *client_stream, const char *ident)
/*
* 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 */
@ -706,25 +708,51 @@ static void anvil_remote_newtls(VSTREAM *client_stream, const char *ident)
/*
* 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);
if (anvil_remote->ntls > max_ntls_rate.value)
ANVIL_MAX_UPDATE(max_ntls_rate, anvil_remote->ntls, anvil_remote->ident);
}
max_newtls_time = event_time();
/* 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 */
@ -796,8 +824,19 @@ static void anvil_service_done(VSTREAM *client_stream, char *unused_service,
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.
@ -805,6 +844,14 @@ static void anvil_service(VSTREAM *client_stream, char *unused_service, char **a
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
@ -818,23 +865,18 @@ static void anvil_service(VSTREAM *client_stream, char *unused_service, char **a
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 {
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);
break;
}
if (STREQ(rp->name, STR(request))) {
rp->action(client_stream, STR(ident));
break;
}
}
vstream_fflush(client_stream);
} else {
@ -843,8 +885,6 @@ static void anvil_service(VSTREAM *client_stream, char *unused_service, char **a
}
if (msg_verbose)
msg_info("--- end request ---");
vstring_free(ident);
vstring_free(request);
}
/* post_jail_init - post-jail initialization */
@ -874,39 +914,16 @@ static void post_jail_init(char *unused_name, char **unused_argv)
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;
}
}

View File

@ -37,6 +37,12 @@
/* 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;
@ -52,29 +58,36 @@
/* 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
@ -99,7 +112,7 @@
/* 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).
@ -168,7 +181,7 @@ void anvil_clnt_free(ANVIL_CLNT *anvil_clnt)
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;
@ -184,7 +197,8 @@ int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service,
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;
@ -192,7 +206,7 @@ int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service,
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)
@ -217,7 +231,7 @@ int anvil_clnt_connect(ANVIL_CLNT *anvil_clnt, const char *service,
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)
@ -241,7 +255,7 @@ int anvil_clnt_mail(ANVIL_CLNT *anvil_clnt, const char *service,
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)
@ -265,7 +279,7 @@ int anvil_clnt_rcpt(ANVIL_CLNT *anvil_clnt, const char *service,
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)
@ -275,7 +289,31 @@ int anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service,
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,
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_stat(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_STAT,
ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
ATTR_TYPE_END,
ATTR_FLAG_MISSING, /* Reply attributes. */
@ -326,10 +364,14 @@ int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service,
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)
@ -344,6 +386,7 @@ int main(int unused_argc, char **argv)
int rate;
int msgs;
int rcpts;
int newtls;
ANVIL_CLNT *anvil;
msg_vstream_init(argv[0], VSTREAM_ERR);
@ -384,6 +427,16 @@ int main(int unused_argc, char **argv)
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!");
@ -391,11 +444,11 @@ int main(int unused_argc, char **argv)
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();

View File

@ -32,13 +32,15 @@
#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
@ -54,7 +56,8 @@ extern int anvil_clnt_connect(ANVIL_CLNT *, const char *, const char *, int *, i
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 *);

View File

@ -355,6 +355,7 @@ static DELIVER_REQUEST *deliver_request_alloc(void)
request->sasl_username = 0;
request->sasl_sender = 0;
request->rewrite_context = 0;
request->dsn_envid = 0;
return (request);
}

View File

@ -1728,6 +1728,10 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`\
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.
*/
@ -2301,6 +2305,10 @@ extern int var_smtpd_cmail_limit;
#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;
@ -2380,10 +2388,6 @@ extern char *var_msg_strip_chars;
#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

View File

@ -20,7 +20,7 @@
* 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

View File

@ -95,6 +95,9 @@
/* 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.

View File

@ -28,6 +28,7 @@
*/
#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 *,...);

View File

@ -231,7 +231,21 @@ LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state)
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);
}
}
/*

View File

@ -378,6 +378,12 @@ int lmtp_stream_except(LMTP_STATE *state, int code, const char *description)
"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));
}

View File

@ -263,7 +263,6 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
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);
}

View File

@ -367,11 +367,6 @@
/* 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
@ -620,7 +615,6 @@ int var_mailtool_compat;
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;
@ -861,7 +855,6 @@ int main(int argc, char **argv)
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,
};

View File

@ -251,7 +251,21 @@ SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
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);
}
}
/*

View File

@ -432,6 +432,12 @@ int smtp_stream_except(SMTP_STATE *state, int code, const char *description)
"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));
}

View File

@ -275,6 +275,7 @@ smtpd_peer.o: ../../include/argv.h
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

View File

@ -415,6 +415,11 @@
/* 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.
@ -435,6 +440,11 @@
/* .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
@ -912,6 +922,7 @@ int var_smtpd_crate_limit;
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;
@ -933,6 +944,8 @@ char *var_smtpd_sasl_tls_opts;
#endif
bool var_smtpd_peername_lookup;
/*
* Silly little macros.
*/
@ -982,6 +995,7 @@ int smtpd_input_transp_mask;
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);
/*
@ -3025,6 +3039,25 @@ static void chat_reset(SMTPD_STATE *state, int threshold)
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.
@ -3041,6 +3074,27 @@ static void smtpd_start_tls(SMTPD_STATE *state)
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.
* There is nothing we can do except to disconnect from the client.
@ -3587,7 +3641,8 @@ static void post_jail_init(char *unused_name, char **unused_argv)
* 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();
}
@ -3625,6 +3680,7 @@ int main(int argc, char **argv)
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
@ -3664,6 +3720,7 @@ int main(int argc, char **argv)
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[] = {

View File

@ -118,6 +118,7 @@
#include <mail_proto.h>
#include <valid_mailhost_addr.h>
#include <mail_params.h>
/* Application-specific. */
@ -251,7 +252,18 @@ void smtpd_peer_init(SMTPD_STATE *state)
state->name_status = code; \
}
if ((aierr = sockaddr_to_hostname(sa, sa_len, &client_name,
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);

View File

@ -56,7 +56,8 @@ typedef struct {
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

View File

@ -693,6 +693,12 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
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.

View File

@ -564,6 +564,12 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
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.
@ -618,6 +624,8 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
*/
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);

View File

@ -15,7 +15,8 @@
/* 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.