]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.3-20051010
authorWietse Venema <wietse@porcupine.org>
Mon, 10 Oct 2005 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:31:30 +0000 (06:31 +0000)
37 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/SMTPD_POLICY_README
postfix/RELEASE_NOTES
postfix/html/SMTPD_POLICY_README.html
postfix/html/anvil.8.html
postfix/html/local.8.html
postfix/html/postconf.5.html
postfix/html/smtpd.8.html
postfix/man/man5/postconf.5
postfix/man/man8/anvil.8
postfix/man/man8/local.8
postfix/man/man8/smtpd.8
postfix/mantools/postlink
postfix/proto/SMTPD_POLICY_README.html
postfix/proto/postconf.proto
postfix/src/anvil/anvil.c
postfix/src/global/anvil_clnt.c
postfix/src/global/anvil_clnt.h
postfix/src/global/deliver_request.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/global/smtp_stream.c
postfix/src/global/smtp_stream.h
postfix/src/lmtp/lmtp_chat.c
postfix/src/lmtp/lmtp_trouble.c
postfix/src/local/alias.c
postfix/src/local/local.c
postfix/src/smtp/smtp_chat.c
postfix/src/smtp/smtp_trouble.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd_peer.c
postfix/src/tls/tls.h
postfix/src/tls/tls_client.c
postfix/src/tls/tls_server.c
postfix/src/util/dict_alloc.c

index 2a8c443b764c9e581d8d0eea36befb65c4725559..289be98f6b86b15a0bf51f7e582fe2e278901d72 100644 (file)
@@ -3,7 +3,9 @@
 -TALIAS_TOKEN
 -TANVIL_CLNT
 -TANVIL_LOCAL
+-TANVIL_MAX
 -TANVIL_REMOTE
+-TANVIL_REQ_TABLE
 -TARGV
 -TATTR_CLNT
 -TATTR_TABLE
index 7378cf24f68f366103116bea9cdd8719528d7a23..36ddd3356850370c0f6b8c192305c602e06e6e0f 100644 (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
index d690a47ff8ca1bdf304c74d39ca27806ac1648ec..b8a1436fb7dc556a36fa455c4a2b46b7d42da35b 100644 (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:
 
index ce56ca140d23ee21c7f2c43d8c217eae6373a74e..1f24d92df836584b9267b329d4ffcc784c06a288 100644 (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
 ======================================
index 325e7e8f7bc552f7a7b316acb1ed6d227c6ba40c..c6c6fe8a6caa7aedd6337e60efabfe5960f16328 100644 (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>
 
index e55868714284bd9a285db20b626e643006a4e959..b10dd570586f0afcec16bca2125937373c854a01 100644 (file)
@@ -13,151 +13,167 @@ 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 <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>
@@ -165,28 +181,28 @@ ANVIL(8)                                                              ANVIL(8)
               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>
@@ -198,7 +214,7 @@ ANVIL(8)                                                              ANVIL(8)
        <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>
index 5bb9b37a7c3138c958d9ded82c9a73a3f712254c..344e4613b8b9631719d5b2041965ac03ea66c2c8 100644 (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>-
index bccd8ed6586c2f353dd8a0411742cab76b02aaca..7b018576f56c1ec2634f189d1bc921b0e82d7bee 100644 (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>
index 142aa88841f6bb1876b7130e954c533993b3120c..edd1fca3db7142ad8d1c1d6b24ced595e3634be3 100644 (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
index ef44cd526d74c3556dadba0aaaeeb0c13b934a47..f4aa2e9fe091e6cbae9bddb1b9ffdb6006283504 100644 (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.
index c44b8d8b81fdcae18dae5d9c5d4e59b2f163765d..bcb2d76c5d2593a4e18b4bb63d7f464ed19fcbcb 100644 (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,20 +101,60 @@ 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
-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
@@ -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.
index 409f7f5d3cea97c7000fdb9c3604a8fb3d7cc687..c5fb993bd076c9f60fea6b4d4e9455840041a869 100644 (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
index f4ea870576879d065771c98c040d072299a8c01c..7ecb1c2e4e67fac8142304dc4d3037c71b84a6ed 100644 (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
index 06a3a1f3acda91cab843c719926bf6497acc1dfb..65704bbbee9682a471d5a106f53d4ba88374b977 100755 (executable)
@@ -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.
 
index 884d0de521557c15633bac6c8a4287d5143fd9c6..9e8c329174a91ed48833ebde8304530885f170bd 100644 (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>
 
index b37eadef67dcf8e0c15f7c928b2e22893e547c81..adcf4124d939fbb2b8af97da12a15b9e3970c2ea 100644 (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>
index 43f7e54d6d4b1800ef9c18e5ab0986808fcbdb65..9f8a084392fccf415bfbed8e25cb568470ef376d 100644 (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
 /*     \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.
@@ -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,67 +386,42 @@ 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)->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) \
@@ -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 =
-             (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);
     }
 }
@@ -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);
-       }
-       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 */
@@ -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 {
-           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 {
@@ -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;
     }
 }
 
index c54b9500b9caaa8111752a089d28ac1a535437cf..a0117cdc771aea51fd2e38faa90fdf024e45ea56 100644 (file)
 /*     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).
@@ -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,17 +279,41 @@ 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)
+{
+    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. */
@@ -291,13 +329,13 @@ int       anvil_clnt_newtls(ANVIL_CLNT *anvil_clnt, const char *service,
 
 /* 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,
@@ -326,13 +364,17 @@ 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)
+int     main(int unused_argc, char **argv)
 {
     VSTRING *inbuf = vstring_alloc(1);
     char   *bufp;
@@ -340,10 +382,11 @@ int       main(int unused_argc, char **argv)
     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);
@@ -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();
index dbdce6313e7634827ef03a1fe2ac00bf4bd9c3b3..0e48a42dd789885c15aaa187958d133c31a1f4ea 100644 (file)
 #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 *);
 
index 26b4cb013169c085acdc48ccaf0e190264c84ec8..14ddda5131829205704f195db8dfda9907dabc77 100644 (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);
 }
 
index 8a22a1c6d5a54700aa3eca729dc0e7743122fcac..0ca0f9a1bc889d12fcda0747892fb796ff4adae5 100644 (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
index 9e1b0d5af36e30297a1df907a1d7b07a282e8334..ac5579892c9712a3c0560350e90be8edf53839db 100644 (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
index d836e67361ca0858aee8ca2965ecdabeb3f42f1f..fc83695f5033b56115564e9ecabc7c5fea37ff42 100644 (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.
index 2fc2f5f66c981afd8b0548f0cfadbcc7d52693bc..3ca7a045228b3aef9f4ab4437ae0733886c2dee3 100644 (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 *,...);
index 608b8a8f3ebc24cac062acd6d3c83c92ccd2e93d..621d17d89f0d642884608a3894324fc04322b49e 100644 (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);
+       }
     }
 
     /*
index 15d5fc948db7274148101c7b97135ae069b8b4e1..9aafe927fd19a897db4c3a6478beaab010a7a93b 100644 (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));
 }
index 5149f3ef61e1b1d23c5e610bbe485f83fd47e232..5ff6f4e47409364efdb9664f7dfa075b564f33a0 100644 (file)
@@ -263,8 +263,7 @@ 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);
+               RESET_OWNER_ATTR(state.msg_attr, state.level);
            }
 
            /*
index f849c1da57f431c94a13eb2b6a948dfe1f95c91d..d01207be71dd5a86f2af00d16f9639f0c99a7557 100644 (file)
 /*     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,
     };
 
index 7f27fc20f60251bedc25db5312f4d3b46c4a6f98..00f4ccbb5cd2d646cf61954ee5de018c17376677 100644 (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);
+       }
     }
 
     /*
index e3a40e6e7342a7a2955a72aed958ffe46cad22df..2896f96ea3593321d45bd43481105c73529f4b3c 100644 (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));
 }
index faf956ee04aaf7e4b6d2e53571dfaf0e7bbf2f8a..5ce6bf6061c5d0194c626337029617e06ece088e 100644 (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
index b9a4d1aab72b3d1a6878eb9e965b4e15c36552b4..107e8034322d48cc971f5a7552337df3fda7ad38 100644 (file)
 /*     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
@@ -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.
@@ -3037,9 +3070,30 @@ static void smtpd_start_tls(SMTPD_STATE *state)
      * 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.
@@ -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[] = {
index 0eba3b07f0ab677c2a822eeefa1ab87239b5e0fb..b49eff3ead41666210924631f67b561b73b36665 100644 (file)
 
 #include <mail_proto.h>
 #include <valid_mailhost_addr.h>
+#include <mail_params.h>
 
 /* Application-specific. */
 
@@ -251,8 +252,19 @@ void    smtpd_peer_init(SMTPD_STATE *state)
        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
index 2fed1254b776b37aad6084a1f88fdd0012e9dee3..134037071efcbf8f82f6d3e3798844fd8ddee71b 100644 (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
index 979f4e8e2261c298d01dcf82a47a551840b18972..3e4a0dc3e87439677a46c3c954d497dda7986e58 100644 (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.
index 6bfb6cd8497171ebf78572367523ee6bf5023411..91122a0a354795d408340d48d7e5b88f8e6e803c 100644 (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);
index e2c812e3fb7ad2d3cb8431d79f7b8b688237595e..3ac5be42ff28a6b3c5ce5b12bc09af3a5f9ed419 100644 (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.