]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.7-20091109
authorWietse Venema <wietse@porcupine.org>
Mon, 9 Nov 2009 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:35:45 +0000 (06:35 +0000)
30 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/SMTPD_PROXY_README
postfix/RELEASE_NOTES
postfix/html/SMTPD_PROXY_README.html
postfix/html/postconf.5.html
postfix/html/smtpd.8.html
postfix/man/man5/postconf.5
postfix/man/man8/smtpd.8
postfix/mantools/postlink
postfix/proto/SMTPD_PROXY_README.html
postfix/proto/postconf.proto
postfix/src/global/mail_copy.c
postfix/src/global/mail_params.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/master/event_server.c
postfix/src/master/multi_server.c
postfix/src/master/single_server.c
postfix/src/master/trigger_server.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd.h
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_proxy.c
postfix/src/smtpd/smtpd_proxy.h
postfix/src/smtpd/smtpd_state.c
postfix/src/smtpd/smtpd_xforward.c
postfix/src/tls/tls_bio_ops.c
postfix/src/util/sys_defs.h

index 32ea76168930a1165c4dd2fbc8193e324f2633fb..c229c2ce65128717e5dbcc31e65a29c88373a38f 100644 (file)
 -TSMFICTX
 -TSMTPD_CMD
 -TSMTPD_DEFER
+-TSMTPD_PROXY
 -TSMTPD_RBL_EXPAND_CONTEXT
 -TSMTPD_RBL_STATE
 -TSMTPD_RCPTMAP_ST
index b37c9c902401391d7882ca9a8c6eaca7a6b50b8b..e49e852ae662db1a21a3c0df9343b94ebc26f74e 100644 (file)
@@ -15458,3 +15458,32 @@ Apologies for any names omitted.
        the Postfix SMTP server. This a last-resort tool to fix bad
        command syntax that Postfix would otherwise reject.  See
        examples in the postconf(5) manual page.  File: smtpd/smtpd.c.
+
+20091026
+
+       Cleanup: changed parameter evaluation order so that the
+       multi_instance_wrapper parameter value is evaluated after
+       the command and daemon directory parameters. File:
+       global/mail_params.h.
+
+20091101
+
+       Performance: specify "smtpd_proxy_options = speed_adjust"
+       to receive an entire message before sending it through a
+       before-queue content filter. This reduces the number of
+       simultaneous content filtering processes, and thus, the
+       system memory requirements.  Files: smtpd/smtpd.[hc],
+       smtpd/smtpd_proxy.[hc].
+
+20091103-4
+
+       Cleaned up the speed-adjust code, streamlined the error
+       handling, and updated documentation. Files: smtpd/smtpd.[hc],
+       smtpd/smtpd_proxy.[hc], proto/SMTPD_PROXY_README.html.
+
+20091105
+
+       Cleaning up after speed_adjust introduction: smtpd segfault
+       caused by an incomplete API change; refined the queue space
+       check; release scratch space immediately after delivering
+       mail to the before-queue filter. Files: smtpd.c, smtpd_proxy.c.
index 407fa0de85903d8ddfa13a7e136e8a38432fa922..27e3eb762093fdf4ea3ca581507f72441969979b 100644 (file)
@@ -4,9 +4,9 @@ P\bPo\bos\bst\btf\bfi\bix\bx B\bBe\bef\bfo\bor\bre\be-\b-Q\bQu\bue\beu\bue\be C\bCo\bon\bnt\bte\ben\bnt\bt
 
 W\bWA\bAR\bRN\bNI\bIN\bNG\bG
 
-The before-queue content filtering feature described in this document is
-suitable only for low-traffic sites. See the "Pros and Cons" section below for
-details.
+The before-queue content filtering feature described in this document limits
+the amount of mail that a site can handle. See the "Pros and Cons" section
+below for details.
 
 T\bTh\bhe\be P\bPo\bos\bst\btf\bfi\bix\bx b\bbe\bef\bfo\bor\bre\be-\b-q\bqu\bue\beu\bue\be c\bco\bon\bnt\bte\ben\bnt\bt f\bfi\bil\blt\bte\ber\br f\bfe\bea\bat\btu\bur\bre\be
 
@@ -70,14 +70,23 @@ P\bPr\bro\bos\bs a\ban\bnd\bd c\bco\bon\bns\bs o\bof\bf b\bbe\bef\bfo\bor\bre\be-\b-q\bqu\bue\beu\bue\be
     system load increases, fewer and fewer CPU cycles remain available to
     answer within the deadline, and eventually you either have to stop
     accepting mail or you have to stop filtering mail. It is for this reason
-    that the before-queue content filter can be used only on low-traffic sites.
+    that the before-queue content filter limits the amount of mail that a site
+    can handle.
 
-  * Con: Content filtering software can use lots of memory resources. In order
-    to not run out of memory you have to reduce the number of before-filter
-    SMTP server processes so that a burst of mail will not drive your system
-    into the ground with too many content filter processes. This, in turn,
-    means that SMTP clients have to wait for a long time before they receive
-    service.
+  * Con: Content filtering software can use lots of memory resources. You have
+    to reduce the number of simultaneous content filter processes so that a
+    burst of mail will not drive your system into the ground.
+
+      o With Postfix versions 2.7 and later, SMTP clients will experience an
+        increase in the delay between the time the client sends "end-of-
+        message" and the time the Postfix SMTP server replies (here, the number
+        of before-filter SMTP server processes can be larger than the number of
+        filter processes).
+
+      o With Postfix versions before 2.7, SMTP clients will experience an
+        increase in the delay before they can receive service (here, the number
+        of before-filter SMTP server processes is always equal to the number of
+        filter processes).
 
 C\bCo\bon\bnf\bfi\big\bgu\bur\bri\bin\bng\bg t\bth\bhe\be P\bPo\bos\bst\btf\bfi\bix\bx S\bSM\bMT\bTP\bP p\bpa\bas\bss\bs-\b-t\bth\bhr\bro\bou\bug\bgh\bh p\bpr\bro\box\bxy\by f\bfe\bea\bat\btu\bur\bre\be
 
@@ -111,6 +120,8 @@ This is configured by editing the master.cf file:
         smtp      inet  n       -       n       -       20      smtpd
             -o smtpd_proxy_filter=127.0.0.1:10025
             -o smtpd_client_connection_count_limit=10
+       # Postfix 2.7 and later performance feature.
+       # -o smtpd_proxy_options=speed_adjust
         #
         # After-filter SMTP server. Receive mail from the content filter
         # on localhost port 10026.
@@ -142,11 +153,23 @@ master.cf file:
     Note: this setting is available in Postfix version 2.2 and later. Earlier
     Postfix versions will ignore it.
 
-  * The "-o smtpd_proxy_filter=127.0.0.1:10025" tells the before filter SMTP
+  * The "-o smtpd_proxy_filter=127.0.0.1:10025" tells the before-filter SMTP
     server that it should give incoming mail to the content filter that listens
     on localhost TCP port 10025.
 
-  * Postfix 2.3 supports both TCP and UNIX-domain filters. The above filter
+  * The "-o smtpd_proxy_options=speed_adjust" tells the before-filter SMTP
+    server that it should receive an entire email message before it connects to
+    a content filter. This reduces the number of simultaneous filter processes.
+
+    NOTE 1: When this option is turned on, a content filter must not
+    selectively reject recipients of a multi-recipient message. Rejecting all
+    recipients is OK, as is accepting all recipients.
+
+    NOTE 2: This feature increases the minimum amount of free queue space by
+    $message_size_limit. The extra space is needed to save the message to a
+    temporary file.
+
+  * Postfix >= 2.3 supports both TCP and UNIX-domain filters. The above filter
     could be specified as "inet:127.0.0.1:10025". To specify a UNIX-domain
     filter, specify "unix:pathname". A relative pathname is interpreted
     relative to the Postfix queue directory.
@@ -159,7 +182,7 @@ The after-filter SMTP server is a new master.cf entry:
 
   * The "-o smtpd_authorized_xforward_hosts=127.0.0.0/8" allows the after-
     filter SMTP server to receive remote SMTP client information from the
-    before filter SMTP server, so that the after-filter Postfix daemons log the
+    before-filter SMTP server, so that the after-filter Postfix daemons log the
     remote SMTP client information instead of logging localhost[127.0.0.1].
 
   * The other after-filter SMTP server settings avoid duplication of work that
index e85a4b0fcd6449082d9184a0f30b6e3fe5c37762..6f2324dc18b5a37a2917457855aaed4f6a1926f0 100644 (file)
@@ -14,6 +14,34 @@ specifies the release date of a stable release or snapshot release.
 If you upgrade from Postfix 2.5 or earlier, read RELEASE_NOTES-2.6
 before proceeding.
 
+Major changes with snapshot 20091109
+====================================
+
+Improved before-queue filter performance. With "smtpd_proxy_options
+= speed_adjust", the Postfix SMTP server receives the entire message
+before it connects to a before-queue content filter. This means you
+can run more SMTP server processes with the same number of running
+content filter processes, and thus, handle more mail. This feature
+is off by default until it is proven to create no new problems.
+
+This addresses a concern of people in Europe who want to reject all
+bad mail with a before-queue filter. The alternative, an after-queue
+filter, means they would have to discard bad mail (which is illegal)
+or bounce bad mail (which violates good network citizenship).
+
+NOTE 1: When this feature is turned on, a filter cannot selectively
+reject recipients of a multi-recipient message.  Rejecting all
+recipients is OK, as is deferring all recipients, or accepting all
+recipients of the same multi-recipient message.
+
+NOTE 2: This feature increases the minimum amount of free queue
+space by $message_size_limit. The extra space is needed to save the
+message to a temporary file.
+
+To keep the performance overhead low, the same temporary file is
+reused with successive mail transactions (the file is of course
+truncated before reuse, so there is no information leakage).
+
 Incompatibility with snapshot 20091008
 ======================================
 
index 09f342797cc2a8b180341cf5e845c0d5548d1003..6fc463c224676ffda5d746fe14eb9bbdbaac824f 100644 (file)
@@ -19,9 +19,9 @@
 
 <h2>WARNING </h2>
 
-<p> The before-queue content filtering feature described in
-this document is suitable only for low-traffic sites. See the "<a
-href="#pros_cons">Pros and Cons</a>" section below for details.
+<p> The before-queue content filtering feature described in this
+document limits the amount of mail that a site can handle. See the
+"<a href="#pros_cons">Pros and Cons</a>" section below for details.
 </p>
 
 <h2>The Postfix before-queue content filter feature</h2>
@@ -154,14 +154,27 @@ a deadline.  As the system load increases, fewer and fewer CPU
 cycles remain available to answer within the deadline, and eventually
 you either have to stop accepting mail or you have to stop filtering
 mail. It is for this reason that the before-queue content filter
-can be used only on low-traffic sites. </p>
+limits the amount of mail that a site can handle. </p>
 
 <li> <p> Con: Content filtering software can use lots of memory
-resources. In order to not run out of memory you have to reduce
-the number of before-filter SMTP server processes so that a burst
-of mail will not drive your system into the ground with too many
-content filter processes. This, in turn, means that SMTP clients
-have to wait for a long time before they receive service.  </p>
+resources. You have to reduce the number of simultaneous content
+filter processes so that a burst of mail will not drive your system
+into the ground. </p>
+<ul>
+
+<li> <p> With Postfix versions 2.7 and later, SMTP clients will
+experience an increase in the delay between the time the client
+sends "end-of-message" and the time the Postfix SMTP server replies
+(here, the number of before-filter SMTP server processes can be
+larger than the number of filter processes).  </p>
+
+<li> <p> With Postfix versions before 2.7, SMTP clients will
+experience an increase in the delay before they can receive service
+(here, the number of before-filter SMTP server processes is always
+equal to the number of filter processes).  </p>
+
+</ul>
 
 </ul>
 
@@ -239,6 +252,8 @@ PERL/SMTP content filtering framework. See:
     smtp      inet  n       -       n       -       20      smtpd
         -o <a href="postconf.5.html#smtpd_proxy_filter">smtpd_proxy_filter</a>=127.0.0.1:10025
         -o <a href="postconf.5.html#smtpd_client_connection_count_limit">smtpd_client_connection_count_limit</a>=10
+       # Postfix 2.7 and later performance feature.
+       # -o <a href="postconf.5.html#smtpd_proxy_options">smtpd_proxy_options</a>=speed_adjust
     #
     # After-filter SMTP server. Receive mail from the content filter
     # on localhost port 10026.
@@ -276,10 +291,24 @@ the top of the <a href="master.5.html">master.cf</a> file: </p>
     later.  Earlier Postfix versions will ignore it.  </p>
 
     <li> <p> The "-o <a href="postconf.5.html#smtpd_proxy_filter">smtpd_proxy_filter</a>=127.0.0.1:10025" tells the
-    before filter SMTP server that it should give incoming mail to
+    before-filter SMTP server that it should give incoming mail to
     the content filter that listens on localhost TCP port 10025.
 
-    <li> <p> Postfix 2.3 supports both TCP and UNIX-domain filters.
+    <li> <p> The "-o <a href="postconf.5.html#smtpd_proxy_options">smtpd_proxy_options</a>=speed_adjust" tells the
+    before-filter SMTP server that it should receive an entire email
+    message before it connects to a content filter. This reduces
+    the number of simultaneous filter processes. </p>
+
+    <p> NOTE 1: When this option is turned on, a content filter must
+    not <i>selectively</i> reject recipients of a multi-recipient
+    message.  Rejecting all recipients is OK, as is accepting all
+    recipients.  </p>
+
+    <p> NOTE 2: This feature increases the minimum amount of free
+    queue space by $<a href="postconf.5.html#message_size_limit">message_size_limit</a>. The extra space is needed
+    to save the message to a temporary file. </p>
+
+    <li> <p> Postfix &ge; 2.3 supports both TCP and UNIX-domain filters.
     The above filter could be specified as "inet:127.0.0.1:10025".
     To specify a UNIX-domain filter, specify "unix:<i>pathname</i>".
     A relative pathname is interpreted relative to the Postfix queue
@@ -299,7 +328,7 @@ the top of the <a href="master.5.html">master.cf</a> file: </p>
 
     <li> <p> The "-o <a href="postconf.5.html#smtpd_authorized_xforward_hosts">smtpd_authorized_xforward_hosts</a>=127.0.0.0/8"
     allows the after-filter SMTP server to receive remote SMTP
-    client information from the before filter SMTP server, so that
+    client information from the before-filter SMTP server, so that
     the after-filter Postfix daemons log the remote SMTP client
     information instead of logging localhost[127.0.0.1]. </p>
 
index 03621e7af988a4c8be5fc6bc21977691eb4e4c51..779f6ddf031e55b4b917c3e5288ca23ebe1c22a9 100644 (file)
@@ -11082,10 +11082,9 @@ invalid command syntax that would otherwise be rejected by Postfix.
 </p>
 
 <p> Specify the name of a "<a href="DATABASE_README.html">type:table</a>" lookup table. The search
-string is the SMTP command as received from the SMTP client. The
-only change made is to remove initial whitespace and the trailing
-<CR><LF> characters. The result value is executed by the Postfix
-SMTP server.  </p>
+string is the SMTP command as received from the SMTP client, except
+that initial whitespace and the trailing <CR><LF> are removed. The
+result value is executed by the Postfix SMTP server.  </p>
 
 <p> Examples: </p>
 
@@ -11106,7 +11105,7 @@ SMTP server.  </p>
 </pre>
 
 <pre>
-    # Work around clients that enclose addresses in single quotes.
+    # Work around clients that send RCPT TO:<'user@domain'>.
     # WARNING: do not lose the parameters that follow the address.
     /^RCPT\s+TO:\s*<'([^[:space:]]+)'>(.*)/     RCPT TO:<$1>$2
 </pre>
@@ -11708,6 +11707,39 @@ directory.  </p>
 and later.  </p>
 
 
+</DD>
+
+<DT><b><a name="smtpd_proxy_options">smtpd_proxy_options</a>
+(default: empty)</b></DT><DD>
+
+<p>
+List of options that control how the Postfix SMTP server
+communicates with a before-queue content filter. Specify zero or
+more of the following, separated by comma or whitespace.  </p>
+
+<dl>
+
+<dt><b>speed_adjust</b></dt>
+
+<dd> Do not connect to a before-queue content filter until an entire
+message has been received. This reduces the number of simultaneous
+before-queue content filter processes. </p>
+
+<p> NOTE 1: A filter must not <i>selectively</i> reject recipients
+of a multi-recipient message.  Rejecting all recipients is OK, as
+is accepting all recipients. </p>
+
+<p> NOTE 2: This feature increases the minimum amount of free queue
+space by $<a href="postconf.5.html#message_size_limit">message_size_limit</a>. The extra space is needed to save the
+message to a temporary file. </p> </dd>
+
+</dl>
+
+<p>
+This feature is available in Postfix 2.7 and later.
+</p>
+
+
 </DD>
 
 <DT><b><a name="smtpd_proxy_timeout">smtpd_proxy_timeout</a>
index 8002204007973898b73217bb532e369277038bf9..ef6b45033a30c4d2efe6ee0d8669f53f1f19c6e5 100644 (file)
@@ -106,121 +106,125 @@ SMTPD(8)                                                              SMTPD(8)
               rejecting the address as invalid.
 
        <b><a href="postconf.5.html#smtpd_command_filter">smtpd_command_filter</a> (empty)</b>
-              A mechanism to substitute incoming SMTP commands by
-              other commands.
+              A mechanism to substitute incoming SMTP commands.
 
        <b><a href="postconf.5.html#smtpd_reject_unlisted_sender">smtpd_reject_unlisted_sender</a> (no)</b>
-              Request that the Postfix SMTP server  rejects  mail
-              from   unknown   sender  addresses,  even  when  no
-              explicit <a href="postconf.5.html#reject_unlisted_sender">reject_unlisted_sender</a> access  restriction
+              Request  that  the Postfix SMTP server rejects mail
+              from  unknown  sender  addresses,  even   when   no
+              explicit  <a href="postconf.5.html#reject_unlisted_sender">reject_unlisted_sender</a> access restriction
               is specified.
 
        <b><a href="postconf.5.html#smtpd_sasl_exceptions_networks">smtpd_sasl_exceptions_networks</a> (empty)</b>
-              What  remote  SMTP  clients the Postfix SMTP server
+              What remote SMTP clients the  Postfix  SMTP  server
               will not offer AUTH support to.
 
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#smtpd_discard_ehlo_keyword_address_maps">smtpd_discard_ehlo_keyword_address_maps</a> (empty)</b>
-              Lookup tables, indexed by the  remote  SMTP  client
-              address,  with  case insensitive lists of EHLO key-
-              words (pipelining, starttls, auth, etc.)  that  the
+              Lookup  tables,  indexed  by the remote SMTP client
+              address, with case insensitive lists of  EHLO  key-
+              words  (pipelining,  starttls, auth, etc.) that the
               SMTP server will not send in the EHLO response to a
               remote SMTP client.
 
        <b><a href="postconf.5.html#smtpd_discard_ehlo_keywords">smtpd_discard_ehlo_keywords</a> (empty)</b>
-              A case insensitive list of EHLO keywords  (pipelin-
-              ing,  starttls,  auth,  etc.)  that the SMTP server
+              A  case insensitive list of EHLO keywords (pipelin-
+              ing, starttls, auth, etc.)  that  the  SMTP  server
               will not send in the EHLO response to a remote SMTP
               client.
 
        <b><a href="postconf.5.html#smtpd_delay_open_until_valid_rcpt">smtpd_delay_open_until_valid_rcpt</a> (yes)</b>
-              Postpone  the  start  of  an  SMTP mail transaction
+              Postpone the start  of  an  SMTP  mail  transaction
               until a valid RCPT TO command is received.
 
        Available in Postfix version 2.3 and later:
 
        <b><a href="postconf.5.html#smtpd_tls_always_issue_session_ids">smtpd_tls_always_issue_session_ids</a> (yes)</b>
-              Force the Postfix SMTP server to issue a  TLS  ses-
-              sion  id,  even  when TLS session caching is turned
+              Force  the  Postfix SMTP server to issue a TLS ses-
+              sion id, even when TLS session  caching  is  turned
               off (<a href="postconf.5.html#smtpd_tls_session_cache_database">smtpd_tls_session_cache_database</a> is empty).
 
        Available in Postfix version 2.6 and later:
 
        <b><a href="postconf.5.html#tcp_windowsize">tcp_windowsize</a> (0)</b>
-              An optional workaround for routers that  break  TCP
+              An  optional  workaround for routers that break TCP
               window scaling.
 
 <b>ADDRESS REWRITING CONTROLS</b>
-       See  the  <a href="ADDRESS_REWRITING_README.html">ADDRESS_REWRITING_README</a> document for a detailed
+       See the <a href="ADDRESS_REWRITING_README.html">ADDRESS_REWRITING_README</a> document for  a  detailed
        discussion of Postfix address rewriting.
 
        <b><a href="postconf.5.html#receive_override_options">receive_override_options</a> (empty)</b>
-              Enable or disable  recipient  validation,  built-in
+              Enable  or  disable  recipient validation, built-in
               content filtering, or address mapping.
 
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> (<a href="postconf.5.html#permit_inet_interfaces">permit_inet_interfaces</a>)</b>
               Rewrite message header addresses in mail from these
-              clients and update incomplete  addresses  with  the
+              clients  and  update  incomplete addresses with the
               domain name in $<a href="postconf.5.html#myorigin">myorigin</a> or $<a href="postconf.5.html#mydomain">mydomain</a>; either don't
-              rewrite message headers from other clients at  all,
-              or  rewrite  message  headers and update incomplete
-              addresses  with  the  domain   specified   in   the
+              rewrite  message headers from other clients at all,
+              or rewrite message headers  and  update  incomplete
+              addresses   with   the   domain  specified  in  the
               <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter.
 
 <b>AFTER QUEUE EXTERNAL CONTENT INSPECTION CONTROLS</b>
-       As  of  version 1.0, Postfix can be configured to send new
-       mail to an external  content  filter  AFTER  the  mail  is
-       queued.  This  content  filter  is expected to inject mail
-       back into a (Postfix or other) MTA for  further  delivery.
+       As of version 1.0, Postfix can be configured to  send  new
+       mail  to  an  external  content  filter  AFTER the mail is
+       queued. This content filter is  expected  to  inject  mail
+       back  into  a (Postfix or other) MTA for further delivery.
        See the <a href="FILTER_README.html">FILTER_README</a> document for details.
 
        <b><a href="postconf.5.html#content_filter">content_filter</a> (empty)</b>
-              The  name of a mail delivery transport that filters
+              The name of a mail delivery transport that  filters
               mail after it is queued.
 
 <b>BEFORE QUEUE EXTERNAL CONTENT INSPECTION CONTROLS</b>
-       As of version 2.1, the Postfix SMTP server can be  config-
-       ured  to send incoming mail to a real-time SMTP-based con-
+       As  of version 2.1, the Postfix SMTP server can be config-
+       ured to send incoming mail to a real-time SMTP-based  con-
        tent filter BEFORE mail is queued.  This content filter is
-       expected  to  inject  mail  back  into  Postfix.   See the
-       <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a> document for details on how to  config-
+       expected to  inject  mail  back  into  Postfix.   See  the
+       <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a>  document for details on how to config-
        ure and operate this feature.
 
        <b><a href="postconf.5.html#smtpd_proxy_filter">smtpd_proxy_filter</a> (empty)</b>
-              The  hostname  and  TCP  port of the mail filtering
+              The hostname and TCP port  of  the  mail  filtering
               proxy server.
 
        <b><a href="postconf.5.html#smtpd_proxy_ehlo">smtpd_proxy_ehlo</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
               How the Postfix SMTP server announces itself to the
               proxy filter.
 
+       <b><a href="postconf.5.html#smtpd_proxy_options">smtpd_proxy_options</a> (empty)</b>
+              List of options that control how the  Postfix  SMTP
+              server  communicates  with  a  before-queue content
+              filter.
+
        <b><a href="postconf.5.html#smtpd_proxy_timeout">smtpd_proxy_timeout</a> (100s)</b>
               The time limit for connecting to a proxy filter and
               for sending or receiving information.
 
 <b>BEFORE QUEUE MILTER CONTROLS</b>
        As of version 2.3, Postfix supports the Sendmail version 8
-       Milter  (mail  filter) protocol. These content filters run
-       outside Postfix. They can inspect the SMTP command  stream
-       and  the  message  content,  and can request modifications
-       before mail is queued. For details see  the  <a href="MILTER_README.html">MILTER_README</a>
+       Milter (mail filter) protocol. These content  filters  run
+       outside  Postfix. They can inspect the SMTP command stream
+       and the message content,  and  can  request  modifications
+       before  mail  is queued. For details see the <a href="MILTER_README.html">MILTER_README</a>
        document.
 
        <b><a href="postconf.5.html#smtpd_milters">smtpd_milters</a> (empty)</b>
               A list of Milter (mail filter) applications for new
-              mail that arrives via the Postfix <a href="smtpd.8.html"><b>smtpd</b>(8)</a>  server.
+              mail  that arrives via the Postfix <a href="smtpd.8.html"><b>smtpd</b>(8)</a> server.
 
        <b><a href="postconf.5.html#milter_protocol">milter_protocol</a> (6)</b>
-              The  mail filter protocol version and optional pro-
-              tocol extensions for communication  with  a  Milter
-              application;  prior to Postfix 2.6 the default pro-
+              The mail filter protocol version and optional  pro-
+              tocol  extensions  for  communication with a Milter
+              application; prior to Postfix 2.6 the default  pro-
               tocol is 2.
 
        <b><a href="postconf.5.html#milter_default_action">milter_default_action</a> (tempfail)</b>
-              The default action  when  a  Milter  (mail  filter)
+              The  default  action  when  a  Milter (mail filter)
               application is unavailable or mis-configured.
 
        <b><a href="postconf.5.html#milter_macro_daemon_name">milter_macro_daemon_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
@@ -232,190 +236,190 @@ SMTPD(8)                                                              SMTPD(8)
               cations.
 
        <b><a href="postconf.5.html#milter_connect_timeout">milter_connect_timeout</a> (30s)</b>
-              The  time  limit  for  connecting to a Milter (mail
-              filter) application, and for  negotiating  protocol
+              The time limit for connecting  to  a  Milter  (mail
+              filter)  application,  and for negotiating protocol
               options.
 
        <b><a href="postconf.5.html#milter_command_timeout">milter_command_timeout</a> (30s)</b>
-              The  time  limit  for  sending an SMTP command to a
+              The time limit for sending an  SMTP  command  to  a
               Milter (mail filter) application, and for receiving
               the response.
 
        <b><a href="postconf.5.html#milter_content_timeout">milter_content_timeout</a> (300s)</b>
-              The  time  limit  for  sending message content to a
+              The time limit for sending  message  content  to  a
               Milter (mail filter) application, and for receiving
               the response.
 
        <b><a href="postconf.5.html#milter_connect_macros">milter_connect_macros</a> (see 'postconf -d' output)</b>
-              The  macros  that  are sent to Milter (mail filter)
-              applications after completion of  an  SMTP  connec-
+              The macros that are sent to  Milter  (mail  filter)
+              applications  after  completion  of an SMTP connec-
               tion.
 
        <b><a href="postconf.5.html#milter_helo_macros">milter_helo_macros</a> (see 'postconf -d' output)</b>
-              The  macros  that  are sent to Milter (mail filter)
+              The macros that are sent to  Milter  (mail  filter)
               applications after the SMTP HELO or EHLO command.
 
        <b><a href="postconf.5.html#milter_mail_macros">milter_mail_macros</a> (see 'postconf -d' output)</b>
-              The macros that are sent to  Milter  (mail  filter)
+              The  macros  that  are sent to Milter (mail filter)
               applications after the SMTP MAIL FROM command.
 
        <b><a href="postconf.5.html#milter_rcpt_macros">milter_rcpt_macros</a> (see 'postconf -d' output)</b>
-              The  macros  that  are sent to Milter (mail filter)
+              The macros that are sent to  Milter  (mail  filter)
               applications after the SMTP RCPT TO command.
 
        <b><a href="postconf.5.html#milter_data_macros">milter_data_macros</a> (see 'postconf -d' output)</b>
-              The macros that are sent to  version  4  or  higher
-              Milter  (mail  filter)  applications after the SMTP
+              The  macros  that  are  sent to version 4 or higher
+              Milter (mail filter) applications  after  the  SMTP
               DATA command.
 
        <b><a href="postconf.5.html#milter_unknown_command_macros">milter_unknown_command_macros</a> (see 'postconf -d' output)</b>
-              The macros that are sent to  version  3  or  higher
-              Milter  (mail filter) applications after an unknown
+              The  macros  that  are  sent to version 3 or higher
+              Milter (mail filter) applications after an  unknown
               SMTP command.
 
        <b><a href="postconf.5.html#milter_end_of_header_macros">milter_end_of_header_macros</a> (see 'postconf -d' output)</b>
-              The macros that are sent to  Milter  (mail  filter)
+              The  macros  that  are sent to Milter (mail filter)
               applications after the end of the message header.
 
        <b><a href="postconf.5.html#milter_end_of_data_macros">milter_end_of_data_macros</a> (see 'postconf -d' output)</b>
-              The  macros  that  are sent to Milter (mail filter)
+              The macros that are sent to  Milter  (mail  filter)
               applications after the message end-of-data.
 
 <b>GENERAL CONTENT INSPECTION CONTROLS</b>
-       The following parameters are applicable for both  built-in
+       The  following parameters are applicable for both built-in
        and external content filters.
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#receive_override_options">receive_override_options</a> (empty)</b>
-              Enable  or  disable  recipient validation, built-in
+              Enable or disable  recipient  validation,  built-in
               content filtering, or address mapping.
 
 <b>EXTERNAL CONTENT INSPECTION CONTROLS</b>
-       The following parameters are applicable for  both  before-
+       The  following  parameters are applicable for both before-
        queue and after-queue content filtering.
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtpd_authorized_xforward_hosts">smtpd_authorized_xforward_hosts</a> (empty)</b>
-              What  SMTP  clients are allowed to use the XFORWARD
+              What SMTP clients are allowed to use  the  XFORWARD
               feature.
 
 <b>SASL AUTHENTICATION CONTROLS</b>
-       Postfix SASL support (<a href="http://tools.ietf.org/html/rfc4954">RFC 4954</a>) can be used  to  authenti-
-       cate  remote  SMTP clients to the Postfix SMTP server, and
-       to authenticate the Postfix SMTP client to a  remote  SMTP
+       Postfix  SASL  support (<a href="http://tools.ietf.org/html/rfc4954">RFC 4954</a>) can be used to authenti-
+       cate remote SMTP clients to the Postfix SMTP  server,  and
+       to  authenticate  the Postfix SMTP client to a remote SMTP
        server.  See the <a href="SASL_README.html">SASL_README</a> document for details.
 
        <b><a href="postconf.5.html#broken_sasl_auth_clients">broken_sasl_auth_clients</a> (no)</b>
-              Enable  inter-operability  with  SMTP  clients that
-              implement an obsolete version of the  AUTH  command
+              Enable inter-operability  with  SMTP  clients  that
+              implement  an  obsolete version of the AUTH command
               (<a href="http://tools.ietf.org/html/rfc4954">RFC 4954</a>).
 
        <b><a href="postconf.5.html#smtpd_sasl_auth_enable">smtpd_sasl_auth_enable</a> (no)</b>
-              Enable  SASL  authentication  in  the  Postfix SMTP
+              Enable SASL  authentication  in  the  Postfix  SMTP
               server.
 
        <b><a href="postconf.5.html#smtpd_sasl_local_domain">smtpd_sasl_local_domain</a> (empty)</b>
-              The name of the Postfix SMTP  server's  local  SASL
+              The  name  of  the Postfix SMTP server's local SASL
               authentication realm.
 
        <b><a href="postconf.5.html#smtpd_sasl_security_options">smtpd_sasl_security_options</a> (noanonymous)</b>
-              Postfix  SMTP  server  SASL security options; as of
-              Postfix 2.3 the list of available features  depends
-              on  the SASL server implementation that is selected
+              Postfix SMTP server SASL security  options;  as  of
+              Postfix  2.3 the list of available features depends
+              on the SASL server implementation that is  selected
               with <b><a href="postconf.5.html#smtpd_sasl_type">smtpd_sasl_type</a></b>.
 
        <b><a href="postconf.5.html#smtpd_sender_login_maps">smtpd_sender_login_maps</a> (empty)</b>
-              Optional lookup table with  the  SASL  login  names
+              Optional  lookup  table  with  the SASL login names
               that own sender (MAIL FROM) addresses.
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtpd_sasl_exceptions_networks">smtpd_sasl_exceptions_networks</a> (empty)</b>
-              What  remote  SMTP  clients the Postfix SMTP server
+              What remote SMTP clients the  Postfix  SMTP  server
               will not offer AUTH support to.
 
        Available in Postfix version 2.1 and 2.2:
 
        <b>smtpd_sasl_application_name (smtpd)</b>
-              The application name that the Postfix  SMTP  server
+              The  application  name that the Postfix SMTP server
               uses for SASL server initialization.
 
        Available in Postfix version 2.3 and later:
 
        <b><a href="postconf.5.html#smtpd_sasl_authenticated_header">smtpd_sasl_authenticated_header</a> (no)</b>
-              Report  the  SASL  authenticated  user  name in the
+              Report the SASL  authenticated  user  name  in  the
               <a href="smtpd.8.html"><b>smtpd</b>(8)</a> Received message header.
 
        <b><a href="postconf.5.html#smtpd_sasl_path">smtpd_sasl_path</a> (smtpd)</b>
-              Implementation-specific information that the  Post-
-              fix  SMTP server passes through to the SASL plug-in
-              implementation    that     is     selected     with
+              Implementation-specific  information that the Post-
+              fix SMTP server passes through to the SASL  plug-in
+              implementation     that     is     selected    with
               <b><a href="postconf.5.html#smtpd_sasl_type">smtpd_sasl_type</a></b>.
 
        <b><a href="postconf.5.html#smtpd_sasl_type">smtpd_sasl_type</a> (cyrus)</b>
-              The  SASL plug-in type that the Postfix SMTP server
+              The SASL plug-in type that the Postfix SMTP  server
               should use for authentication.
 
        Available in Postfix version 2.5 and later:
 
        <b><a href="postconf.5.html#cyrus_sasl_config_path">cyrus_sasl_config_path</a> (empty)</b>
-              Search path for Cyrus SASL  application  configura-
-              tion  files,  currently  used  only  to  locate the
+              Search  path  for Cyrus SASL application configura-
+              tion files,  currently  used  only  to  locate  the
               $<a href="postconf.5.html#smtpd_sasl_path">smtpd_sasl_path</a>.conf file.
 
 <b>STARTTLS SUPPORT CONTROLS</b>
-       Detailed information about STARTTLS configuration  may  be
+       Detailed  information  about STARTTLS configuration may be
        found in the <a href="TLS_README.html">TLS_README</a> document.
 
        <b><a href="postconf.5.html#smtpd_tls_security_level">smtpd_tls_security_level</a> (empty)</b>
-              The  SMTP  TLS  security level for the Postfix SMTP
-              server; when a non-empty value is  specified,  this
+              The SMTP TLS security level for  the  Postfix  SMTP
+              server;  when  a non-empty value is specified, this
               overrides the obsolete parameters <a href="postconf.5.html#smtpd_use_tls">smtpd_use_tls</a> and
               <a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a>.
 
        <b><a href="postconf.5.html#smtpd_sasl_tls_security_options">smtpd_sasl_tls_security_options</a>         ($<a href="postconf.5.html#smtpd_sasl_security_options">smtpd_sasl_secu</a>-</b>
        <b><a href="postconf.5.html#smtpd_sasl_security_options">rity_options</a>)</b>
-              The SASL authentication security options  that  the
-              Postfix  SMTP  server  uses  for TLS encrypted SMTP
+              The  SASL  authentication security options that the
+              Postfix SMTP server uses  for  TLS  encrypted  SMTP
               sessions.
 
        <b><a href="postconf.5.html#smtpd_starttls_timeout">smtpd_starttls_timeout</a> (300s)</b>
-              The time limit for Postfix SMTP  server  write  and
-              read  operations  during  TLS  startup and shutdown
+              The  time  limit  for Postfix SMTP server write and
+              read operations during  TLS  startup  and  shutdown
               handshake procedures.
 
        <b><a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a> (empty)</b>
-              A file containing (PEM format) CA  certificates  of
-              root  CAs trusted to sign either remote SMTP client
+              A  file  containing (PEM format) CA certificates of
+              root CAs trusted to sign either remote SMTP  client
               certificates or intermediate CA certificates.
 
        <b><a href="postconf.5.html#smtpd_tls_CApath">smtpd_tls_CApath</a> (empty)</b>
               A directory containing (PEM format) CA certificates
-              of  root  CAs  trusted  to  sign either remote SMTP
-              client certificates  or  intermediate  CA  certifi-
+              of root CAs trusted  to  sign  either  remote  SMTP
+              client  certificates  or  intermediate  CA certifi-
               cates.
 
        <b><a href="postconf.5.html#smtpd_tls_always_issue_session_ids">smtpd_tls_always_issue_session_ids</a> (yes)</b>
-              Force  the  Postfix SMTP server to issue a TLS ses-
-              sion id, even when TLS session  caching  is  turned
+              Force the Postfix SMTP server to issue a  TLS  ses-
+              sion  id,  even  when TLS session caching is turned
               off (<a href="postconf.5.html#smtpd_tls_session_cache_database">smtpd_tls_session_cache_database</a> is empty).
 
        <b><a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a> (no)</b>
-              Ask  a remote SMTP client for a client certificate.
+              Ask a remote SMTP client for a client  certificate.
 
        <b><a href="postconf.5.html#smtpd_tls_auth_only">smtpd_tls_auth_only</a> (no)</b>
               When TLS encryption is optional in the Postfix SMTP
-              server,  do not announce or accept SASL authentica-
+              server, do not announce or accept SASL  authentica-
               tion over unencrypted connections.
 
        <b><a href="postconf.5.html#smtpd_tls_ccert_verifydepth">smtpd_tls_ccert_verifydepth</a> (9)</b>
-              The verification depth for remote SMTP client  cer-
+              The  verification depth for remote SMTP client cer-
               tificates.
 
        <b><a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a> (empty)</b>
-              File  with  the Postfix SMTP server RSA certificate
+              File with the Postfix SMTP server  RSA  certificate
               in PEM format.
 
        <b><a href="postconf.5.html#smtpd_tls_exclude_ciphers">smtpd_tls_exclude_ciphers</a> (empty)</b>
@@ -423,56 +427,56 @@ SMTPD(8)                                                              SMTPD(8)
               SMTP server cipher list at all TLS security levels.
 
        <b><a href="postconf.5.html#smtpd_tls_dcert_file">smtpd_tls_dcert_file</a> (empty)</b>
-              File with the Postfix SMTP server  DSA  certificate
+              File  with  the Postfix SMTP server DSA certificate
               in PEM format.
 
        <b><a href="postconf.5.html#smtpd_tls_dh1024_param_file">smtpd_tls_dh1024_param_file</a> (empty)</b>
-              File  with  DH  parameters  that  the  Postfix SMTP
+              File with  DH  parameters  that  the  Postfix  SMTP
               server should use with EDH ciphers.
 
        <b><a href="postconf.5.html#smtpd_tls_dh512_param_file">smtpd_tls_dh512_param_file</a> (empty)</b>
-              File with  DH  parameters  that  the  Postfix  SMTP
+              File  with  DH  parameters  that  the  Postfix SMTP
               server should use with EDH ciphers.
 
        <b><a href="postconf.5.html#smtpd_tls_dkey_file">smtpd_tls_dkey_file</a> ($<a href="postconf.5.html#smtpd_tls_dcert_file">smtpd_tls_dcert_file</a>)</b>
-              File  with  the Postfix SMTP server DSA private key
+              File with the Postfix SMTP server DSA  private  key
               in PEM format.
 
        <b><a href="postconf.5.html#smtpd_tls_key_file">smtpd_tls_key_file</a> ($<a href="postconf.5.html#smtpd_tls_cert_file">smtpd_tls_cert_file</a>)</b>
-              File with the Postfix SMTP server RSA  private  key
+              File  with  the Postfix SMTP server RSA private key
               in PEM format.
 
        <b><a href="postconf.5.html#smtpd_tls_loglevel">smtpd_tls_loglevel</a> (0)</b>
-              Enable  additional  Postfix  SMTP server logging of
+              Enable additional Postfix SMTP  server  logging  of
               TLS activity.
 
        <b><a href="postconf.5.html#smtpd_tls_mandatory_ciphers">smtpd_tls_mandatory_ciphers</a> (medium)</b>
-              The minimum TLS cipher grade that the Postfix  SMTP
+              The  minimum TLS cipher grade that the Postfix SMTP
               server will use with mandatory TLS encryption.
 
        <b><a href="postconf.5.html#smtpd_tls_mandatory_exclude_ciphers">smtpd_tls_mandatory_exclude_ciphers</a> (empty)</b>
-              Additional  list  of  ciphers  or  cipher  types to
-              exclude from the SMTP server cipher list at  manda-
+              Additional list  of  ciphers  or  cipher  types  to
+              exclude  from the SMTP server cipher list at manda-
               tory TLS security levels.
 
        <b><a href="postconf.5.html#smtpd_tls_mandatory_protocols">smtpd_tls_mandatory_protocols</a> (SSLv3, TLSv1)</b>
-              The  SSL/TLS protocols accepted by the Postfix SMTP
+              The SSL/TLS protocols accepted by the Postfix  SMTP
               server with mandatory TLS encryption.
 
        <b><a href="postconf.5.html#smtpd_tls_received_header">smtpd_tls_received_header</a> (no)</b>
               Request  that  the  Postfix  SMTP  server  produces
               Received:  message headers that include information
-              about the protocol and cipher used, as well as  the
-              client  CommonName  and  client  certificate issuer
+              about  the protocol and cipher used, as well as the
+              client CommonName  and  client  certificate  issuer
               CommonName.
 
        <b><a href="postconf.5.html#smtpd_tls_req_ccert">smtpd_tls_req_ccert</a> (no)</b>
-              With mandatory TLS encryption,  require  a  trusted
-              remote  SMTP  client  certificate in order to allow
+              With  mandatory  TLS  encryption, require a trusted
+              remote SMTP client certificate in  order  to  allow
               TLS connections to proceed.
 
        <b><a href="postconf.5.html#smtpd_tls_session_cache_database">smtpd_tls_session_cache_database</a> (empty)</b>
-              Name of the file containing  the  optional  Postfix
+              Name  of  the  file containing the optional Postfix
               SMTP server TLS session cache.
 
        <b><a href="postconf.5.html#smtpd_tls_session_cache_timeout">smtpd_tls_session_cache_timeout</a> (3600s)</b>
@@ -480,14 +484,14 @@ SMTPD(8)                                                              SMTPD(8)
               sion cache information.
 
        <b><a href="postconf.5.html#smtpd_tls_wrappermode">smtpd_tls_wrappermode</a> (no)</b>
-              Run the Postfix SMTP  server  in  the  non-standard
-              "wrapper"  mode, instead of using the STARTTLS com-
+              Run  the  Postfix  SMTP  server in the non-standard
+              "wrapper" mode, instead of using the STARTTLS  com-
               mand.
 
        <b><a href="postconf.5.html#tls_daemon_random_bytes">tls_daemon_random_bytes</a> (32)</b>
-              The number of pseudo-random bytes that  an  <a href="smtp.8.html"><b>smtp</b>(8)</a>
-              or  <a href="smtpd.8.html"><b>smtpd</b>(8)</a>  process  requests  from the <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a>
-              server in order to seed its internal pseudo  random
+              The  number  of pseudo-random bytes that an <a href="smtp.8.html"><b>smtp</b>(8)</a>
+              or <a href="smtpd.8.html"><b>smtpd</b>(8)</a> process  requests  from  the  <a href="tlsmgr.8.html"><b>tlsmgr</b>(8)</a>
+              server  in order to seed its internal pseudo random
               number generator (PRNG).
 
        <b><a href="postconf.5.html#tls_high_cipherlist">tls_high_cipherlist</a></b>
@@ -499,7 +503,7 @@ SMTPD(8)                                                              SMTPD(8)
               ciphers.
 
        <b><a href="postconf.5.html#tls_low_cipherlist">tls_low_cipherlist</a> (ALL:!EXPORT:+RC4:@STRENGTH)</b>
-              The OpenSSL cipherlist for "LOW"  or  higher  grade
+              The  OpenSSL  cipherlist  for "LOW" or higher grade
               ciphers.
 
        <b><a href="postconf.5.html#tls_export_cipherlist">tls_export_cipherlist</a> (ALL:+RC4:@STRENGTH)</b>
@@ -507,26 +511,26 @@ SMTPD(8)                                                              SMTPD(8)
               ciphers.
 
        <b><a href="postconf.5.html#tls_null_cipherlist">tls_null_cipherlist</a> (eNULL:!aNULL)</b>
-              The OpenSSL cipherlist  for  "NULL"  grade  ciphers
+              The  OpenSSL  cipherlist  for  "NULL" grade ciphers
               that provide authentication without encryption.
 
        Available in Postfix version 2.5 and later:
 
        <b><a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> (md5)</b>
-              The  message  digest  algorithm  used  to construct
+              The message  digest  algorithm  used  to  construct
               client-certificate         fingerprints         for
               <b><a href="postconf.5.html#check_ccert_access">check_ccert_access</a></b> and <b><a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a></b>.
 
        Available in Postfix version 2.6 and later:
 
        <b><a href="postconf.5.html#smtpd_tls_protocols">smtpd_tls_protocols</a> (empty)</b>
-              List  of TLS protocols that the Postfix SMTP server
-              will exclude  or  include  with  opportunistic  TLS
+              List of TLS protocols that the Postfix SMTP  server
+              will  exclude  or  include  with  opportunistic TLS
               encryption.
 
        <b><a href="postconf.5.html#smtpd_tls_ciphers">smtpd_tls_ciphers</a> (export)</b>
-              The  minimum TLS cipher grade that the Postfix SMTP
-              server will use with opportunistic TLS  encryption.
+              The minimum TLS cipher grade that the Postfix  SMTP
+              server  will use with opportunistic TLS encryption.
 
        <b><a href="postconf.5.html#smtpd_tls_eccert_file">smtpd_tls_eccert_file</a> (empty)</b>
               File with the Postfix SMTP server ECDSA certificate
@@ -537,7 +541,7 @@ SMTPD(8)                                                              SMTPD(8)
               in PEM format.
 
        <b><a href="postconf.5.html#smtpd_tls_eecdh_grade">smtpd_tls_eecdh_grade</a> (see 'postconf -d' output)</b>
-              The   Postfix   SMTP   server  security  grade  for
+              The  Postfix  SMTP  server   security   grade   for
               ephemeral elliptic-curve Diffie-Hellman (EECDH) key
               exchange.
 
@@ -550,18 +554,18 @@ SMTPD(8)                                                              SMTPD(8)
               imally strong ephemeral ECDH key exchange.
 
 <b>OBSOLETE STARTTLS CONTROLS</b>
-       The  following configuration parameters exist for compati-
+       The following configuration parameters exist for  compati-
        bility with Postfix versions before 2.3. Support for these
        will be removed in a future release.
 
        <b><a href="postconf.5.html#smtpd_use_tls">smtpd_use_tls</a> (no)</b>
-              Opportunistic  TLS:  announce  STARTTLS  support to
-              SMTP clients, but do not require that  clients  use
+              Opportunistic TLS:  announce  STARTTLS  support  to
+              SMTP  clients,  but do not require that clients use
               TLS encryption.
 
        <b><a href="postconf.5.html#smtpd_enforce_tls">smtpd_enforce_tls</a> (no)</b>
-              Mandatory  TLS:  announce  STARTTLS support to SMTP
-              clients, and require that clients use  TLS  encryp-
+              Mandatory TLS: announce STARTTLS  support  to  SMTP
+              clients,  and  require that clients use TLS encryp-
               tion.
 
        <b><a href="postconf.5.html#smtpd_tls_cipherlist">smtpd_tls_cipherlist</a> (empty)</b>
@@ -569,64 +573,64 @@ SMTPD(8)                                                              SMTPD(8)
               server TLS cipher list.
 
 <b>VERP SUPPORT CONTROLS</b>
-       With VERP style delivery,  each  recipient  of  a  message
+       With  VERP  style  delivery,  each  recipient of a message
        receives a customized copy of the message with his/her own
-       recipient address encoded in the envelope sender  address.
+       recipient  address encoded in the envelope sender address.
        The <a href="VERP_README.html">VERP_README</a> file describes configuration and operation
-       details of Postfix support for  variable  envelope  return
+       details  of  Postfix  support for variable envelope return
        path addresses.  VERP style delivery is requested with the
-       SMTP XVERP command or with the "sendmail -V"  command-line
-       option  and is available in Postfix version 1.1 and later.
+       SMTP  XVERP command or with the "sendmail -V" command-line
+       option and is available in Postfix version 1.1 and  later.
 
        <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a> (+=)</b>
               The two default VERP delimiter characters.
 
        <b><a href="postconf.5.html#verp_delimiter_filter">verp_delimiter_filter</a> (-=+)</b>
-              The characters Postfix accepts  as  VERP  delimiter
-              characters  on the Postfix <a href="sendmail.1.html"><b>sendmail</b>(1)</a> command line
+              The  characters  Postfix  accepts as VERP delimiter
+              characters on the Postfix <a href="sendmail.1.html"><b>sendmail</b>(1)</a> command  line
               and in SMTP commands.
 
        Available in Postfix version 1.1 and 2.0:
 
        <b><a href="postconf.5.html#authorized_verp_clients">authorized_verp_clients</a> ($<a href="postconf.5.html#mynetworks">mynetworks</a>)</b>
-              What SMTP clients are allowed to specify the  XVERP
+              What  SMTP clients are allowed to specify the XVERP
               command.
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtpd_authorized_verp_clients">smtpd_authorized_verp_clients</a> ($<a href="postconf.5.html#authorized_verp_clients">authorized_verp_clients</a>)</b>
-              What  SMTP clients are allowed to specify the XVERP
+              What SMTP clients are allowed to specify the  XVERP
               command.
 
 <b>TROUBLE SHOOTING CONTROLS</b>
-       The <a href="DEBUG_README.html">DEBUG_README</a> document describes how to debug parts  of
-       the  Postfix mail system. The methods vary from making the
-       software log a lot of detail, to running some daemon  pro-
+       The  <a href="DEBUG_README.html">DEBUG_README</a> document describes how to debug parts of
+       the Postfix mail system. The methods vary from making  the
+       software  log a lot of detail, to running some daemon pro-
        cesses under control of a call tracer or debugger.
 
        <b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
-              The  increment  in  verbose  logging  level  when a
-              remote client or server matches a  pattern  in  the
+              The increment  in  verbose  logging  level  when  a
+              remote  client  or  server matches a pattern in the
               <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
 
        <b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
-              Optional  list  of remote client or server hostname
-              or network address patterns that cause the  verbose
-              logging  level  to increase by the amount specified
+              Optional list of remote client or  server  hostname
+              or  network address patterns that cause the verbose
+              logging level to increase by the  amount  specified
               in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
 
        <b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
-              The recipient  of  postmaster  notifications  about
-              mail  delivery  problems that are caused by policy,
+              The  recipient  of  postmaster  notifications about
+              mail delivery problems that are caused  by  policy,
               resource, software or protocol errors.
 
        <b><a href="postconf.5.html#internal_mail_filter_classes">internal_mail_filter_classes</a> (empty)</b>
-              What categories of Postfix-generated mail are  sub-
-              ject   to   before-queue   content   inspection  by
+              What  categories of Postfix-generated mail are sub-
+              ject  to   before-queue   content   inspection   by
               <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>, <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>.
 
        <b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
-              The list of error classes that are reported to  the
+              The  list of error classes that are reported to the
               postmaster.
 
        <b><a href="postconf.5.html#soft_bounce">soft_bounce</a> (no)</b>
@@ -636,22 +640,22 @@ SMTPD(8)                                                              SMTPD(8)
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtpd_authorized_xclient_hosts">smtpd_authorized_xclient_hosts</a> (empty)</b>
-              What SMTP clients are allowed to  use  the  XCLIENT
+              What  SMTP  clients  are allowed to use the XCLIENT
               feature.
 
 <b>KNOWN VERSUS UNKNOWN RECIPIENT CONTROLS</b>
-       As  of  Postfix  version 2.0, the SMTP server rejects mail
-       for unknown recipients. This prevents the mail queue  from
-       clogging  up  with  undeliverable  MAILER-DAEMON messages.
-       Additional  information  on   this   topic   is   in   the
+       As of Postfix version 2.0, the SMTP  server  rejects  mail
+       for  unknown recipients. This prevents the mail queue from
+       clogging up  with  undeliverable  MAILER-DAEMON  messages.
+       Additional   information   on   this   topic   is  in  the
        <a href="LOCAL_RECIPIENT_README.html">LOCAL_RECIPIENT_README</a> and <a href="ADDRESS_CLASS_README.html">ADDRESS_CLASS_README</a> documents.
 
        <b><a href="postconf.5.html#show_user_unknown_table_name">show_user_unknown_table_name</a> (yes)</b>
-              Display the name of  the  recipient  table  in  the
+              Display  the  name  of  the  recipient table in the
               "User unknown" responses.
 
        <b><a href="postconf.5.html#canonical_maps">canonical_maps</a> (empty)</b>
-              Optional  address mapping lookup tables for message
+              Optional address mapping lookup tables for  message
               headers and envelopes.
 
        <b><a href="postconf.5.html#recipient_canonical_maps">recipient_canonical_maps</a> (empty)</b>
@@ -662,7 +666,7 @@ SMTPD(8)                                                              SMTPD(8)
 
        <b><a href="postconf.5.html#mydestination">mydestination</a>  ($<a href="postconf.5.html#myhostname">myhostname</a>,  localhost.$<a href="postconf.5.html#mydomain">mydomain</a>,  local-</b>
        <b>host)</b>
-              The  list  of  domains  that  are delivered via the
+              The list of domains  that  are  delivered  via  the
               $<a href="postconf.5.html#local_transport">local_transport</a> mail delivery transport.
 
        <b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
@@ -671,146 +675,146 @@ SMTPD(8)                                                              SMTPD(8)
 
        <b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
               The network interface addresses that this mail sys-
-              tem receives mail on by way of a proxy  or  network
+              tem  receives  mail on by way of a proxy or network
               address translation unit.
 
        <b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (ipv4)</b>
-              The  Internet protocols Postfix will attempt to use
+              The Internet protocols Postfix will attempt to  use
               when making or accepting connections.
 
        <b><a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a>             (<a href="proxymap.8.html">proxy</a>:unix:passwd.byname</b>
        <b>$<a href="postconf.5.html#alias_maps">alias_maps</a>)</b>
-              Lookup tables with all names or addresses of  local
-              recipients:  a  recipient address is local when its
-              domain matches $<a href="postconf.5.html#mydestination">mydestination</a>, $<a href="postconf.5.html#inet_interfaces">inet_interfaces</a>  or
+              Lookup  tables with all names or addresses of local
+              recipients: a recipient address is local  when  its
+              domain  matches $<a href="postconf.5.html#mydestination">mydestination</a>, $<a href="postconf.5.html#inet_interfaces">inet_interfaces</a> or
               $<a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a>.
 
        <b><a href="postconf.5.html#unknown_local_recipient_reject_code">unknown_local_recipient_reject_code</a> (550)</b>
-              The  numerical  Postfix  SMTP  server response code
-              when   a   recipient   address   is   local,    and
-              $<a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a>  specifies  a  list of lookup
+              The numerical Postfix  SMTP  server  response  code
+              when    a   recipient   address   is   local,   and
+              $<a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a> specifies a  list  of  lookup
               tables that does not match the recipient.
 
-       Parameters concerning known/unknown  recipients  of  relay
+       Parameters  concerning  known/unknown  recipients of relay
        destinations:
 
        <b><a href="postconf.5.html#relay_domains">relay_domains</a> ($<a href="postconf.5.html#mydestination">mydestination</a>)</b>
-              What  destination  domains (and subdomains thereof)
+              What destination domains (and  subdomains  thereof)
               this system will relay mail to.
 
        <b><a href="postconf.5.html#relay_recipient_maps">relay_recipient_maps</a> (empty)</b>
-              Optional lookup tables with all valid addresses  in
+              Optional  lookup tables with all valid addresses in
               the domains that match $<a href="postconf.5.html#relay_domains">relay_domains</a>.
 
        <b><a href="postconf.5.html#unknown_relay_recipient_reject_code">unknown_relay_recipient_reject_code</a> (550)</b>
               The numerical Postfix SMTP server reply code when a
-              recipient  address  matches   $<a href="postconf.5.html#relay_domains">relay_domains</a>,   and
-              <a href="postconf.5.html#relay_recipient_maps">relay_recipient_maps</a>  specifies  a  list  of lookup
+              recipient   address   matches  $<a href="postconf.5.html#relay_domains">relay_domains</a>,  and
+              <a href="postconf.5.html#relay_recipient_maps">relay_recipient_maps</a> specifies  a  list  of  lookup
               tables that does not match the recipient address.
 
-       Parameters concerning known/unknown recipients in  virtual
+       Parameters  concerning known/unknown recipients in virtual
        alias domains:
 
        <b><a href="postconf.5.html#virtual_alias_domains">virtual_alias_domains</a> ($<a href="postconf.5.html#virtual_alias_maps">virtual_alias_maps</a>)</b>
               Postfix is final destination for the specified list
-              of virtual alias  domains,  that  is,  domains  for
-              which  all  addresses  are  aliased to addresses in
+              of  virtual  alias  domains,  that  is, domains for
+              which all addresses are  aliased  to  addresses  in
               other local or remote domains.
 
        <b><a href="postconf.5.html#virtual_alias_maps">virtual_alias_maps</a> ($<a href="postconf.5.html#virtual_maps">virtual_maps</a>)</b>
-              Optional lookup tables  that  alias  specific  mail
-              addresses  or  domains  to  other  local  or remote
+              Optional  lookup  tables  that  alias specific mail
+              addresses or  domains  to  other  local  or  remote
               address.
 
        <b><a href="postconf.5.html#unknown_virtual_alias_reject_code">unknown_virtual_alias_reject_code</a> (550)</b>
               The SMTP server reply code when a recipient address
-              matches     $<a href="postconf.5.html#virtual_alias_domains">virtual_alias_domains</a>,    and    $<a href="postconf.5.html#virtual_alias_maps">vir</a>-
-              <a href="postconf.5.html#virtual_alias_maps">tual_alias_maps</a> specifies a list of  lookup  tables
+              matches    $<a href="postconf.5.html#virtual_alias_domains">virtual_alias_domains</a>,    and     $<a href="postconf.5.html#virtual_alias_maps">vir</a>-
+              <a href="postconf.5.html#virtual_alias_maps">tual_alias_maps</a>  specifies  a list of lookup tables
               that does not match the recipient address.
 
-       Parameters  concerning known/unknown recipients in virtual
+       Parameters concerning known/unknown recipients in  virtual
        mailbox domains:
 
        <b><a href="postconf.5.html#virtual_mailbox_domains">virtual_mailbox_domains</a> ($<a href="postconf.5.html#virtual_mailbox_maps">virtual_mailbox_maps</a>)</b>
               Postfix is final destination for the specified list
-              of   domains;  mail  is  delivered  via  the  $<a href="postconf.5.html#virtual_transport">vir</a>-
+              of  domains;  mail  is  delivered  via  the   $<a href="postconf.5.html#virtual_transport">vir</a>-
               <a href="postconf.5.html#virtual_transport">tual_transport</a> mail delivery transport.
 
        <b><a href="postconf.5.html#virtual_mailbox_maps">virtual_mailbox_maps</a> (empty)</b>
-              Optional lookup tables with all valid addresses  in
+              Optional  lookup tables with all valid addresses in
               the domains that match $<a href="postconf.5.html#virtual_mailbox_domains">virtual_mailbox_domains</a>.
 
        <b><a href="postconf.5.html#unknown_virtual_mailbox_reject_code">unknown_virtual_mailbox_reject_code</a> (550)</b>
               The SMTP server reply code when a recipient address
-              matches   $<a href="postconf.5.html#virtual_mailbox_domains">virtual_mailbox_domains</a>,    and    $<a href="postconf.5.html#virtual_mailbox_maps">vir</a>-
+              matches    $<a href="postconf.5.html#virtual_mailbox_domains">virtual_mailbox_domains</a>,    and   $<a href="postconf.5.html#virtual_mailbox_maps">vir</a>-
               <a href="postconf.5.html#virtual_mailbox_maps">tual_mailbox_maps</a> specifies a list of lookup tables
               that does not match the recipient address.
 
 <b>RESOURCE AND RATE CONTROLS</b>
-       The following parameters limit resource usage by the  SMTP
+       The  following parameters limit resource usage by the SMTP
        server and/or control client request rates.
 
        <b><a href="postconf.5.html#line_length_limit">line_length_limit</a> (2048)</b>
-              Upon  input,  long lines are chopped up into pieces
-              of at most this length; upon delivery,  long  lines
+              Upon input, long lines are chopped up  into  pieces
+              of  at  most this length; upon delivery, long lines
               are reconstructed.
 
        <b><a href="postconf.5.html#queue_minfree">queue_minfree</a> (0)</b>
-              The  minimal  amount  of free space in bytes in the
+              The minimal amount of free space in  bytes  in  the
               queue file system that is needed to receive mail.
 
        <b><a href="postconf.5.html#message_size_limit">message_size_limit</a> (10240000)</b>
-              The maximal size in bytes of a  message,  including
+              The  maximal  size in bytes of a message, including
               envelope information.
 
        <b><a href="postconf.5.html#smtpd_recipient_limit">smtpd_recipient_limit</a> (1000)</b>
-              The  maximal  number of recipients that the Postfix
+              The maximal number of recipients that  the  Postfix
               SMTP server accepts per message delivery request.
 
        <b><a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> (normal: 300s, stress: 10s)</b>
-              The time limit for sending a  Postfix  SMTP  server
-              response  and  for  receiving  a remote SMTP client
+              The  time  limit  for sending a Postfix SMTP server
+              response and for receiving  a  remote  SMTP  client
               request.
 
        <b><a href="postconf.5.html#smtpd_history_flush_threshold">smtpd_history_flush_threshold</a> (100)</b>
-              The maximal number of lines  in  the  Postfix  SMTP
-              server  command  history  before it is flushed upon
+              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.
 
        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 remote SMTP client hostname,
-              and  verify  that  the  name  matches the client IP
+              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.
+       vice,  and are available in Postfix version 2.2 and later.
 
        <b><a href="postconf.5.html#smtpd_client_connection_count_limit">smtpd_client_connection_count_limit</a> (50)</b>
-              How  many  simultaneous  connections  any client is
+              How many simultaneous  connections  any  client  is
               allowed to make to this service.
 
        <b><a href="postconf.5.html#smtpd_client_connection_rate_limit">smtpd_client_connection_rate_limit</a> (0)</b>
               The  maximal  number  of  connection  attempts  any
-              client  is allowed to make to this service per time
+              client is allowed to make to this service per  time
               unit.
 
        <b><a href="postconf.5.html#smtpd_client_message_rate_limit">smtpd_client_message_rate_limit</a> (0)</b>
-              The maximal number  of  message  delivery  requests
-              that  any client is allowed to make to this service
+              The  maximal  number  of  message delivery requests
+              that any client is allowed to make to this  service
               per time unit, regardless of whether or not Postfix
               actually accepts those messages.
 
        <b><a href="postconf.5.html#smtpd_client_recipient_rate_limit">smtpd_client_recipient_rate_limit</a> (0)</b>
-              The  maximal number of recipient addresses that any
-              client is allowed to send to this service per  time
+              The maximal number of recipient addresses that  any
+              client  is allowed to send to this service per time
               unit, regardless of whether or not Postfix actually
               accepts those recipients.
 
        <b><a href="postconf.5.html#smtpd_client_event_limit_exceptions">smtpd_client_event_limit_exceptions</a> ($<a href="postconf.5.html#mynetworks">mynetworks</a>)</b>
-              Clients that are excluded  from  connection  count,
+              Clients  that  are  excluded from connection count,
               connection rate, or SMTP request rate restrictions.
 
        Available in Postfix version 2.3 and later:
@@ -821,52 +825,52 @@ SMTPD(8)                                                              SMTPD(8)
               tiate 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
-       to slow down run-away  software.   The  behavior  is  con-
-       trolled  by  an  error  counter  that counts the number of
-       errors within an SMTP session that a client makes  without
+       When  a  remote SMTP client makes errors, the Postfix SMTP
+       server can insert delays before responding. This can  help
+       to  slow  down  run-away  software.   The behavior is con-
+       trolled by an error counter  that  counts  the  number  of
+       errors  within an SMTP session that a client makes without
        delivering mail.
 
        <b><a href="postconf.5.html#smtpd_error_sleep_time">smtpd_error_sleep_time</a> (1s)</b>
               With Postfix version 2.1 and later: the SMTP server
-              response delay after a client has  made  more  than
-              $<a href="postconf.5.html#smtpd_soft_error_limit">smtpd_soft_error_limit</a>   errors,  and  fewer  than
-              $<a href="postconf.5.html#smtpd_hard_error_limit">smtpd_hard_error_limit</a> errors, without  delivering
+              response  delay  after  a client has made more than
+              $<a href="postconf.5.html#smtpd_soft_error_limit">smtpd_soft_error_limit</a>  errors,  and  fewer   than
+              $<a href="postconf.5.html#smtpd_hard_error_limit">smtpd_hard_error_limit</a>  errors, without delivering
               mail.
 
        <b><a href="postconf.5.html#smtpd_soft_error_limit">smtpd_soft_error_limit</a> (10)</b>
-              The  number  of  errors  a  remote  SMTP  client is
-              allowed to make without delivering mail before  the
+              The number  of  errors  a  remote  SMTP  client  is
+              allowed  to make without delivering mail before the
               Postfix SMTP server slows down all its responses.
 
        <b><a href="postconf.5.html#smtpd_hard_error_limit">smtpd_hard_error_limit</a> (normal: 20, stress: 1)</b>
-              The  maximal  number of errors a remote SMTP client
+              The maximal number of errors a remote  SMTP  client
               is allowed to make without delivering mail.
 
        <b><a href="postconf.5.html#smtpd_junk_command_limit">smtpd_junk_command_limit</a> (normal: 100, stress: 1)</b>
-              The number of junk commands (NOOP,  VRFY,  ETRN  or
+              The  number  of  junk commands (NOOP, VRFY, ETRN or
               RSET) that a remote SMTP client can send before the
-              Postfix SMTP server starts to increment  the  error
+              Postfix  SMTP  server starts to increment the error
               counter with each junk command.
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtpd_recipient_overshoot_limit">smtpd_recipient_overshoot_limit</a> (1000)</b>
-              The  number of recipients that a remote SMTP client
-              can send in excess  of  the  limit  specified  with
+              The number of recipients that a remote SMTP  client
+              can  send  in  excess  of  the limit specified with
               $<a href="postconf.5.html#smtpd_recipient_limit">smtpd_recipient_limit</a>,  before  the  Postfix  SMTP
-              server increments the per-session error  count  for
+              server  increments  the per-session error count for
               each excess recipient.
 
 <b>ACCESS POLICY DELEGATION CONTROLS</b>
-       As  of  version 2.1, Postfix can be configured to delegate
-       access policy decisions to an external  server  that  runs
-       outside  Postfix.   See  the  file <a href="SMTPD_POLICY_README.html">SMTPD_POLICY_README</a> for
+       As of version 2.1, Postfix can be configured  to  delegate
+       access  policy  decisions  to an external server that runs
+       outside Postfix.  See  the  file  <a href="SMTPD_POLICY_README.html">SMTPD_POLICY_README</a>  for
        more information.
 
        <b><a href="postconf.5.html#smtpd_policy_service_max_idle">smtpd_policy_service_max_idle</a> (300s)</b>
-              The time after which an idle SMTPD  policy  service
+              The  time  after which an idle SMTPD policy service
               connection is closed.
 
        <b><a href="postconf.5.html#smtpd_policy_service_max_ttl">smtpd_policy_service_max_ttl</a> (1000s)</b>
@@ -874,150 +878,150 @@ SMTPD(8)                                                              SMTPD(8)
               connection is closed.
 
        <b><a href="postconf.5.html#smtpd_policy_service_timeout">smtpd_policy_service_timeout</a> (100s)</b>
-              The time limit for connecting  to,  writing  to  or
+              The  time  limit  for  connecting to, writing to or
               receiving from a delegated SMTPD policy server.
 
 <b>ACCESS CONTROLS</b>
-       The  <a href="SMTPD_ACCESS_README.html">SMTPD_ACCESS_README</a> document gives an introduction to
+       The <a href="SMTPD_ACCESS_README.html">SMTPD_ACCESS_README</a> document gives an introduction  to
        all the SMTP server access control features.
 
        <b><a href="postconf.5.html#smtpd_delay_reject">smtpd_delay_reject</a> (yes)</b>
-              Wait until the RCPT TO  command  before  evaluating
+              Wait  until  the  RCPT TO command before evaluating
               $<a href="postconf.5.html#smtpd_client_restrictions">smtpd_client_restrictions</a>,    $smtpd_helo_restric-
               tions and $<a href="postconf.5.html#smtpd_sender_restrictions">smtpd_sender_restrictions</a>, or wait until
-              the      ETRN     command     before     evaluating
+              the     ETRN     command     before      evaluating
               $<a href="postconf.5.html#smtpd_client_restrictions">smtpd_client_restrictions</a> and $smtpd_helo_restric-
               tions.
 
-       <b><a href="postconf.5.html#parent_domain_matches_subdomains">parent_domain_matches_subdomains</a>  (see  'postconf -d' out-</b>
+       <b><a href="postconf.5.html#parent_domain_matches_subdomains">parent_domain_matches_subdomains</a> (see 'postconf  -d'  out-</b>
        <b>put)</b>
               What   Postfix   features   match   subdomains   of
               "domain.tld" automatically, instead of requiring an
               explicit ".domain.tld" pattern.
 
        <b><a href="postconf.5.html#smtpd_client_restrictions">smtpd_client_restrictions</a> (empty)</b>
-              Optional  SMTP  server  access  restrictions in the
+              Optional SMTP server  access  restrictions  in  the
               context of a client SMTP connection request.
 
        <b><a href="postconf.5.html#smtpd_helo_required">smtpd_helo_required</a> (no)</b>
               Require that a remote SMTP client introduces itself
-              at  the  beginning of an SMTP session with the HELO
+              at the beginning of an SMTP session with  the  HELO
               or EHLO command.
 
        <b><a href="postconf.5.html#smtpd_helo_restrictions">smtpd_helo_restrictions</a> (empty)</b>
-              Optional restrictions that the Postfix SMTP  server
+              Optional  restrictions that the Postfix SMTP server
               applies in the context of the SMTP HELO command.
 
        <b><a href="postconf.5.html#smtpd_sender_restrictions">smtpd_sender_restrictions</a> (empty)</b>
-              Optional  restrictions that the Postfix SMTP server
+              Optional restrictions that the Postfix SMTP  server
               applies in the context of the MAIL FROM command.
 
        <b><a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a>           (<a href="postconf.5.html#permit_mynetworks">permit_mynetworks</a>,</b>
        <b><a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>)</b>
               The  access  restrictions  that  the  Postfix  SMTP
-              server  applies  in the context of the RCPT TO com-
+              server applies in the context of the RCPT  TO  com-
               mand.
 
        <b><a href="postconf.5.html#smtpd_etrn_restrictions">smtpd_etrn_restrictions</a> (empty)</b>
-              Optional SMTP server  access  restrictions  in  the
+              Optional  SMTP  server  access  restrictions in the
               context of a client ETRN request.
 
        <b><a href="postconf.5.html#allow_untrusted_routing">allow_untrusted_routing</a> (no)</b>
-              Forward    mail   with   sender-specified   routing
-              (user[@%!]remote[@%!]site) from  untrusted  clients
+              Forward   mail   with   sender-specified    routing
+              (user[@%!]remote[@%!]site)  from  untrusted clients
               to destinations matching $<a href="postconf.5.html#relay_domains">relay_domains</a>.
 
        <b><a href="postconf.5.html#smtpd_restriction_classes">smtpd_restriction_classes</a> (empty)</b>
-              User-defined  aliases for groups of access restric-
+              User-defined aliases for groups of access  restric-
               tions.
 
        <b><a href="postconf.5.html#smtpd_null_access_lookup_key">smtpd_null_access_lookup_key</a> (</b>&lt;&gt;<b>)</b>
-              The lookup key to be used in SMTP <a href="access.5.html"><b>access</b>(5)</a>  tables
+              The  lookup key to be used in SMTP <a href="access.5.html"><b>access</b>(5)</a> tables
               instead of the null sender address.
 
        <b><a href="postconf.5.html#permit_mx_backup_networks">permit_mx_backup_networks</a> (empty)</b>
               Restrict  the  use  of  the  <a href="postconf.5.html#permit_mx_backup">permit_mx_backup</a>  SMTP
-              access feature to only  domains  whose  primary  MX
+              access  feature  to  only  domains whose primary MX
               hosts match the listed networks.
 
        Available in Postfix version 2.0 and later:
 
        <b><a href="postconf.5.html#smtpd_data_restrictions">smtpd_data_restrictions</a> (empty)</b>
-              Optional  access restrictions that the Postfix SMTP
+              Optional access restrictions that the Postfix  SMTP
               server applies in the context of the SMTP DATA com-
               mand.
 
        <b><a href="postconf.5.html#smtpd_expansion_filter">smtpd_expansion_filter</a> (see 'postconf -d' output)</b>
-              What  characters are allowed in $name expansions of
+              What characters are allowed in $name expansions  of
               RBL reply templates.
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtpd_reject_unlisted_sender">smtpd_reject_unlisted_sender</a> (no)</b>
-              Request that the Postfix SMTP server  rejects  mail
-              from   unknown   sender  addresses,  even  when  no
-              explicit <a href="postconf.5.html#reject_unlisted_sender">reject_unlisted_sender</a> access  restriction
+              Request  that  the Postfix SMTP server rejects mail
+              from  unknown  sender  addresses,  even   when   no
+              explicit  <a href="postconf.5.html#reject_unlisted_sender">reject_unlisted_sender</a> access restriction
               is specified.
 
        <b><a href="postconf.5.html#smtpd_reject_unlisted_recipient">smtpd_reject_unlisted_recipient</a> (yes)</b>
-              Request  that  the Postfix SMTP server rejects mail
+              Request that the Postfix SMTP server  rejects  mail
               for  unknown  recipient  addresses,  even  when  no
-              explicit  <a href="postconf.5.html#reject_unlisted_recipient">reject_unlisted_recipient</a> access restric-
+              explicit <a href="postconf.5.html#reject_unlisted_recipient">reject_unlisted_recipient</a> access  restric-
               tion is specified.
 
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#smtpd_end_of_data_restrictions">smtpd_end_of_data_restrictions</a> (empty)</b>
-              Optional access restrictions that the Postfix  SMTP
-              server  applies  in the context of the SMTP END-OF-
+              Optional  access restrictions that the Postfix SMTP
+              server applies in the context of the  SMTP  END-OF-
               DATA command.
 
 <b>SENDER AND RECIPIENT ADDRESS VERIFICATION CONTROLS</b>
-       Postfix  version  2.1  introduces  sender  and   recipient
-       address  verification.   This  feature  is  implemented by
-       sending probe email messages that are not actually  deliv-
-       ered.   This  feature  is requested via the reject_unveri-
-       fied_sender   and    <a href="postconf.5.html#reject_unverified_recipient">reject_unverified_recipient</a>    access
-       restrictions.   The status of verification probes is main-
+       Postfix   version  2.1  introduces  sender  and  recipient
+       address verification.   This  feature  is  implemented  by
+       sending  probe email messages that are not actually deliv-
+       ered.  This feature is requested  via  the  reject_unveri-
+       fied_sender    and    <a href="postconf.5.html#reject_unverified_recipient">reject_unverified_recipient</a>   access
+       restrictions.  The status of verification probes is  main-
        tained by the <a href="verify.8.html"><b>verify</b>(8)</a> server.  See the file <a href="ADDRESS_VERIFICATION_README.html">ADDRESS_VER</a>-
-       <a href="ADDRESS_VERIFICATION_README.html">IFICATION_README</a>  for  information  about how to configure
+       <a href="ADDRESS_VERIFICATION_README.html">IFICATION_README</a> for information about  how  to  configure
        and operate the Postfix sender/recipient address verifica-
        tion service.
 
        <b><a href="postconf.5.html#address_verify_poll_count">address_verify_poll_count</a> (3)</b>
-              How  many  times to query the <a href="verify.8.html"><b>verify</b>(8)</a> service for
-              the completion of an address  verification  request
+              How many times to query the <a href="verify.8.html"><b>verify</b>(8)</a>  service  for
+              the  completion  of an address verification request
               in progress.
 
        <b><a href="postconf.5.html#address_verify_poll_delay">address_verify_poll_delay</a> (3s)</b>
-              The  delay between queries for the completion of an
+              The delay between queries for the completion of  an
               address verification request in progress.
 
        <b><a href="postconf.5.html#address_verify_sender">address_verify_sender</a> ($<a href="postconf.5.html#double_bounce_sender">double_bounce_sender</a>)</b>
-              The sender address to use in  address  verification
+              The  sender  address to use in address verification
               probes; prior to Postfix 2.5 the default was "post-
               master".
 
        <b><a href="postconf.5.html#unverified_sender_reject_code">unverified_sender_reject_code</a> (450)</b>
-              The numerical Postfix  SMTP  server  response  code
-              when   a  recipient  address  is  rejected  by  the
+              The  numerical  Postfix  SMTP  server response code
+              when  a  recipient  address  is  rejected  by   the
               <a href="postconf.5.html#reject_unverified_sender">reject_unverified_sender</a> restriction.
 
        <b><a href="postconf.5.html#unverified_recipient_reject_code">unverified_recipient_reject_code</a> (450)</b>
-              The numerical Postfix SMTP server response  when  a
+              The  numerical  Postfix SMTP server response when a
               recipient address is rejected by the reject_unveri-
               fied_recipient restriction.
 
        Available in Postfix version 2.6 and later:
 
        <b><a href="postconf.5.html#unverified_sender_defer_code">unverified_sender_defer_code</a> (450)</b>
-              The numerical Postfix  SMTP  server  response  code
-              when  a  sender address probe fails due to a tempo-
+              The  numerical  Postfix  SMTP  server response code
+              when a sender address probe fails due to  a  tempo-
               rary error condition.
 
        <b><a href="postconf.5.html#unverified_recipient_defer_code">unverified_recipient_defer_code</a> (450)</b>
-              The numerical Postfix SMTP server response  when  a
-              recipient  address  probe  fails due to a temporary
+              The  numerical  Postfix SMTP server response when a
+              recipient address probe fails due  to  a  temporary
               error condition.
 
        <b><a href="postconf.5.html#unverified_sender_reject_reason">unverified_sender_reject_reason</a> (empty)</b>
@@ -1031,7 +1035,7 @@ SMTPD(8)                                                              SMTPD(8)
        <b><a href="postconf.5.html#unverified_sender_tempfail_action">unverified_sender_tempfail_action</a>           ($<a href="postconf.5.html#reject_tempfail_action">reject_temp</a>-</b>
        <b><a href="postconf.5.html#reject_tempfail_action">fail_action</a>)</b>
               The Postfix SMTP server's action when <a href="postconf.5.html#reject_unverified_sender">reject_unver</a>-
-              <a href="postconf.5.html#reject_unverified_sender">ified_sender</a>  fails due to a temporary error condi-
+              <a href="postconf.5.html#reject_unverified_sender">ified_sender</a> fails due to a temporary error  condi-
               tion.
 
        <b><a href="postconf.5.html#unverified_recipient_tempfail_action">unverified_recipient_tempfail_action</a>        ($<a href="postconf.5.html#reject_tempfail_action">reject_temp</a>-</b>
@@ -1041,7 +1045,7 @@ SMTPD(8)                                                              SMTPD(8)
               dition.
 
 <b>ACCESS CONTROL RESPONSES</b>
-       The following  parameters  control  numerical  SMTP  reply
+       The  following  parameters  control  numerical  SMTP reply
        codes and/or text responses.
 
        <b><a href="postconf.5.html#access_map_reject_code">access_map_reject_code</a> (554)</b>
@@ -1049,18 +1053,18 @@ SMTPD(8)                                                              SMTPD(8)
               an <a href="access.5.html"><b>access</b>(5)</a> map "reject" action.
 
        <b><a href="postconf.5.html#defer_code">defer_code</a> (450)</b>
-              The numerical Postfix  SMTP  server  response  code
-              when  a  remote  SMTP client request is rejected by
+              The  numerical  Postfix  SMTP  server response code
+              when a remote SMTP client request  is  rejected  by
               the "defer" restriction.
 
        <b><a href="postconf.5.html#invalid_hostname_reject_code">invalid_hostname_reject_code</a> (501)</b>
-              The numerical Postfix  SMTP  server  response  code
-              when  the  client HELO or EHLO command parameter is
-              rejected   by   the    <a href="postconf.5.html#reject_invalid_helo_hostname">reject_invalid_helo_hostname</a>
+              The  numerical  Postfix  SMTP  server response code
+              when the client HELO or EHLO command  parameter  is
+              rejected    by   the   <a href="postconf.5.html#reject_invalid_helo_hostname">reject_invalid_helo_hostname</a>
               restriction.
 
        <b><a href="postconf.5.html#maps_rbl_reject_code">maps_rbl_reject_code</a> (554)</b>
-              The  numerical  Postfix  SMTP  server response code
+              The numerical Postfix  SMTP  server  response  code
               when a remote SMTP client request is blocked by the
               <a href="postconf.5.html#reject_rbl_client">reject_rbl_client</a>,             <a href="postconf.5.html#reject_rhsbl_client">reject_rhsbl_client</a>,
               <a href="postconf.5.html#reject_rhsbl_sender">reject_rhsbl_sender</a>    or    <a href="postconf.5.html#reject_rhsbl_recipient">reject_rhsbl_recipient</a>
@@ -1068,53 +1072,53 @@ SMTPD(8)                                                              SMTPD(8)
 
        <b><a href="postconf.5.html#non_fqdn_reject_code">non_fqdn_reject_code</a> (504)</b>
               The numerical Postfix SMTP server reply code when a
-              client    request    is     rejected     by     the
+              client     request     is     rejected    by    the
               <a href="postconf.5.html#reject_non_fqdn_helo_hostname">reject_non_fqdn_helo_hostname</a>,
               <a href="postconf.5.html#reject_non_fqdn_sender">reject_non_fqdn_sender</a> or <a href="postconf.5.html#reject_non_fqdn_recipient">reject_non_fqdn_recipient</a>
               restriction.
 
        <b><a href="postconf.5.html#plaintext_reject_code">plaintext_reject_code</a> (450)</b>
-              The  numerical  Postfix  SMTP  server response code
-              when a request is  rejected  by  the  <b>reject_plain-</b>
+              The numerical Postfix  SMTP  server  response  code
+              when  a  request  is  rejected by the <b>reject_plain-</b>
               <b>text_session</b> restriction.
 
        <b><a href="postconf.5.html#reject_code">reject_code</a> (554)</b>
-              The  numerical  Postfix  SMTP  server response code
-              when a remote SMTP client request  is  rejected  by
+              The numerical Postfix  SMTP  server  response  code
+              when  a  remote  SMTP client request is rejected by
               the "reject" restriction.
 
        <b><a href="postconf.5.html#relay_domains_reject_code">relay_domains_reject_code</a> (554)</b>
-              The  numerical  Postfix  SMTP  server response code
-              when  a  client  request   is   rejected   by   the
+              The numerical Postfix  SMTP  server  response  code
+              when   a   client   request   is  rejected  by  the
               <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a> recipient restriction.
 
        <b><a href="postconf.5.html#unknown_address_reject_code">unknown_address_reject_code</a> (450)</b>
-              The  numerical  Postfix  SMTP  server response code
-              when a sender or recipient address is  rejected  by
+              The numerical Postfix  SMTP  server  response  code
+              when  a  sender or recipient address is rejected by
               the         <a href="postconf.5.html#reject_unknown_sender_domain">reject_unknown_sender_domain</a>         or
               <a href="postconf.5.html#reject_unknown_recipient_domain">reject_unknown_recipient_domain</a> restriction.
 
        <b><a href="postconf.5.html#unknown_client_reject_code">unknown_client_reject_code</a> (450)</b>
-              The numerical Postfix  SMTP  server  response  code
-              when  a  client without valid address &lt;=&gt; name map-
+              The  numerical  Postfix  SMTP  server response code
+              when a client without valid address &lt;=&gt;  name  map-
               ping is rejected by the reject_unknown_client_host-
               name restriction.
 
        <b><a href="postconf.5.html#unknown_hostname_reject_code">unknown_hostname_reject_code</a> (450)</b>
-              The  numerical  Postfix  SMTP  server response code
-              when the hostname specified with the HELO  or  EHLO
-              command        is       rejected       by       the
+              The numerical Postfix  SMTP  server  response  code
+              when  the  hostname specified with the HELO or EHLO
+              command       is       rejected       by        the
               <a href="postconf.5.html#reject_unknown_helo_hostname">reject_unknown_helo_hostname</a> restriction.
 
        Available in Postfix version 2.0 and later:
 
        <b><a href="postconf.5.html#default_rbl_reply">default_rbl_reply</a> (see 'postconf -d' output)</b>
-              The default SMTP server  response  template  for  a
-              request  that  is rejected by an RBL-based restric-
+              The  default  SMTP  server  response template for a
+              request that is rejected by an  RBL-based  restric-
               tion.
 
        <b><a href="postconf.5.html#multi_recipient_bounce_reject_code">multi_recipient_bounce_reject_code</a> (550)</b>
-              The numerical Postfix  SMTP  server  response  code
+              The  numerical  Postfix  SMTP  server response code
               when a remote SMTP client request is blocked by the
               <a href="postconf.5.html#reject_multi_recipient_bounce">reject_multi_recipient_bounce</a> restriction.
 
@@ -1125,38 +1129,38 @@ SMTPD(8)                                                              SMTPD(8)
 
        <b><a href="postconf.5.html#access_map_defer_code">access_map_defer_code</a> (450)</b>
               The numerical Postfix SMTP server response code for
-              an  <a href="access.5.html"><b>access</b>(5)</a>   map   "defer"   action,   including
+              an   <a href="access.5.html"><b>access</b>(5)</a>   map   "defer"   action,  including
               "<a href="postconf.5.html#defer_if_permit">defer_if_permit</a>" or "<a href="postconf.5.html#defer_if_reject">defer_if_reject</a>".
 
        <b><a href="postconf.5.html#reject_tempfail_action">reject_tempfail_action</a> (<a href="postconf.5.html#defer_if_permit">defer_if_permit</a>)</b>
               The Postfix SMTP server's action when a reject-type
-              restriction fails due to a temporary  error  condi-
+              restriction  fails  due to a temporary error condi-
               tion.
 
        <b><a href="postconf.5.html#unknown_helo_hostname_tempfail_action">unknown_helo_hostname_tempfail_action</a>       ($<a href="postconf.5.html#reject_tempfail_action">reject_temp</a>-</b>
        <b><a href="postconf.5.html#reject_tempfail_action">fail_action</a>)</b>
-              The    Postfix    SMTP    server's    action   when
+              The   Postfix    SMTP    server's    action    when
               <a href="postconf.5.html#reject_unknown_helo_hostname">reject_unknown_helo_hostname</a> fails due to an tempo-
               rary error condition.
 
        <b><a href="postconf.5.html#unknown_address_tempfail_action">unknown_address_tempfail_action</a> ($<a href="postconf.5.html#reject_tempfail_action">reject_tempfail_action</a>)</b>
-              The    Postfix    SMTP    server's    action   when
+              The   Postfix    SMTP    server's    action    when
               <a href="postconf.5.html#reject_unknown_sender_domain">reject_unknown_sender_domain</a>                     or
-              <a href="postconf.5.html#reject_unknown_recipient_domain">reject_unknown_recipient_domain</a>  fail due to a tem-
+              <a href="postconf.5.html#reject_unknown_recipient_domain">reject_unknown_recipient_domain</a> fail due to a  tem-
               porary error condition.
 
 <b>MISCELLANEOUS CONTROLS</b>
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The default location of  the  Postfix  <a href="postconf.5.html">main.cf</a>  and
+              The  default  location  of  the Postfix <a href="postconf.5.html">main.cf</a> and
               <a href="master.5.html">master.cf</a> 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#command_directory">command_directory</a> (see 'postconf -d' output)</b>
-              The  location  of  all  postfix administrative com-
+              The location of  all  postfix  administrative  com-
               mands.
 
        <b><a href="postconf.5.html#double_bounce_sender">double_bounce_sender</a> (double-bounce)</b>
@@ -1177,37 +1181,37 @@ SMTPD(8)                                                              SMTPD(8)
               and most Postfix daemon processes.
 
        <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  an  incoming  connection
+              The maximum amount of time  that  an  idle  Postfix
+              daemon  process  waits  for  an incoming connection
               before terminating voluntarily.
 
        <b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
-              The  maximal  number of incoming connections that a
-              Postfix daemon process will service  before  termi-
+              The maximal number of incoming connections  that  a
+              Postfix  daemon  process will service before termi-
               nating voluntarily.
 
        <b><a href="postconf.5.html#myhostname">myhostname</a> (see 'postconf -d' output)</b>
               The internet hostname of this mail system.
 
        <b><a href="postconf.5.html#mynetworks">mynetworks</a> (see 'postconf -d' output)</b>
-              The  list  of "trusted" SMTP clients that have more
+              The list of "trusted" SMTP clients that  have  more
               privileges than "strangers".
 
        <b><a href="postconf.5.html#myorigin">myorigin</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
               The domain name that locally-posted mail appears to
-              come  from,  and that locally posted mail is deliv-
+              come from, and that locally posted mail  is  deliv-
               ered to.
 
        <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#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
-              The location of the Postfix top-level queue  direc-
+              The  location of the Postfix top-level queue direc-
               tory.
 
        <b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> (empty)</b>
@@ -1215,28 +1219,28 @@ SMTPD(8)                                                              SMTPD(8)
               sions (user+foo).
 
        <b><a href="postconf.5.html#smtpd_banner">smtpd_banner</a> ($<a href="postconf.5.html#myhostname">myhostname</a> ESMTP $<a href="postconf.5.html#mail_name">mail_name</a>)</b>
-              The text that follows the 220 status  code  in  the
+              The  text  that  follows the 220 status code in the
               SMTP greeting banner.
 
        <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> (see 'postconf -d' output)</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".
 
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#smtpd_forbidden_commands">smtpd_forbidden_commands</a> (CONNECT, GET, POST)</b>
-              List  of  commands  that  causes  the  Postfix SMTP
-              server to immediately terminate the session with  a
+              List of  commands  that  causes  the  Postfix  SMTP
+              server  to immediately terminate the session with a
               221 code.
 
        Available in Postfix version 2.5 and later:
 
        <b><a href="postconf.5.html#smtpd_client_port_logging">smtpd_client_port_logging</a> (no)</b>
-              Enable  logging  of  the remote SMTP client port in
+              Enable logging of the remote SMTP  client  port  in
               addition to the hostname and IP address.
 
 <b>SEE ALSO</b>
@@ -1266,7 +1270,7 @@ SMTPD(8)                                                              SMTPD(8)
        <a href="XFORWARD_README.html">XFORWARD_README</a>, Postfix XFORWARD extension
 
 <b>LICENSE</b>
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 <b>AUTHOR(S)</b>
index 898a5329fe5c6bdff49245506c8008e771866684..24a00bfd4bf916e04561cf5f3be37a44c3cfce92 100644 (file)
@@ -6785,10 +6785,9 @@ last-resort tool to work around problems with clients that send
 invalid command syntax that would otherwise be rejected by Postfix.
 .PP
 Specify the name of a "type:table" lookup table. The search
-string is the SMTP command as received from the SMTP client. The
-only change made is to remove initial whitespace and the trailing
-<CR><LF> characters. The result value is executed by the Postfix
-SMTP server.
+string is the SMTP command as received from the SMTP client, except
+that initial whitespace and the trailing <CR><LF> are removed. The
+result value is executed by the Postfix SMTP server.
 .PP
 Examples:
 .PP
@@ -6823,7 +6822,7 @@ Examples:
 .nf
 .na
 .ft C
-    # Work around clients that enclose addresses in single quotes.
+    # Work around clients that send RCPT TO:<'user@domain'>.
     # WARNING: do not lose the parameters that follow the address.
     /^RCPT\es+TO:\es*<'([^[:space:]]+)'>(.*)/     RCPT TO:<$1>$2
 .fi
@@ -7172,6 +7171,24 @@ This feature is available in Postfix 2.1 and later.
 .PP
 The "inet:" and "unix:" prefixes are available in Postfix 2.3
 and later.
+.SH smtpd_proxy_options (default: empty)
+List of options that control how the Postfix SMTP server
+communicates with a before-queue content filter. Specify zero or
+more of the following, separated by comma or whitespace.
+.IP "\fBspeed_adjust\fR"
+Do not connect to a before-queue content filter until an entire
+message has been received. This reduces the number of simultaneous
+before-queue content filter processes.
+.PP
+NOTE 1: A filter must not \fIselectively\fR reject recipients
+of a multi-recipient message.  Rejecting all recipients is OK, as
+is accepting all recipients.
+.PP
+NOTE 2: This feature increases the minimum amount of free queue
+space by $message_size_limit. The extra space is needed to save the
+message to a temporary file.
+.PP
+This feature is available in Postfix 2.7 and later.
 .SH smtpd_proxy_timeout (default: 100s)
 The time limit for connecting to a proxy filter and for sending or
 receiving information.  When a connection fails the client gets a
index 6ef70df25eca6ac4e8dd14a9802d65998a22d4ca..e7ae824eba673002745a2d672589bbdee8ee593a 100644 (file)
@@ -109,8 +109,7 @@ Resolve an address that ends in the "@" null domain as if the
 local hostname were specified, instead of rejecting the address as
 invalid.
 .IP "\fBsmtpd_command_filter (empty)\fR"
-A mechanism to substitute incoming SMTP commands by other
-commands.
+A mechanism to substitute incoming SMTP commands.
 .IP "\fBsmtpd_reject_unlisted_sender (no)\fR"
 Request that the Postfix SMTP server rejects mail from unknown
 sender addresses, even when no explicit reject_unlisted_sender
@@ -187,6 +186,9 @@ details on how to configure and operate this feature.
 The hostname and TCP port of the mail filtering proxy server.
 .IP "\fBsmtpd_proxy_ehlo ($myhostname)\fR"
 How the Postfix SMTP server announces itself to the proxy filter.
+.IP "\fBsmtpd_proxy_options (empty)\fR"
+List of options that control how the Postfix SMTP server
+communicates with a before-queue content filter.
 .IP "\fBsmtpd_proxy_timeout (100s)\fR"
 The time limit for connecting to a proxy filter and for sending or
 receiving information.
index a07af388dc1fda40549142cf2c9928b42aa49b18..4157f325064c32f80b959c8e63bfb570229229f0 100755 (executable)
@@ -511,6 +511,7 @@ while (<>) {
     s;\bsmtpd_proxy_ehlo\b;<a href="postconf.5.html#smtpd_proxy_ehlo">$&</a>;g;
     s;\bsmtpd_proxy_filter\b;<a href="postconf.5.html#smtpd_proxy_filter">$&</a>;g;
     s;\bsmtpd_proxy_timeout\b;<a href="postconf.5.html#smtpd_proxy_timeout">$&</a>;g;
+    s;\bsmtpd_proxy_options\b;<a href="postconf.5.html#smtpd_proxy_options">$&</a>;g;
     s;\bsmtpd_recip[-</bB>]*\n* *[<bB>]*ient_limit\b;<a href="postconf.5.html#smtpd_recipient_limit">$&</a>;g;
     s;\bsmtpd_recip[-</bB>]*\n* *[<bB>]*ient_restrictions\b;<a href="postconf.5.html#smtpd_recipient_restrictions">$&</a>;g;
     s;\bsmtpd_reject_unlisted_recip[-</bB>]*\n* *[<bB>]*ient\b;<a href="postconf.5.html#smtpd_reject_unlisted_recipient">$&</a>;g;
index 114a1767e54d73bafb0d108730bc83be9c8e7b05..b0aebba8475762b2c31e39e3af9bf61feaea88a5 100644 (file)
@@ -19,9 +19,9 @@
 
 <h2>WARNING </h2>
 
-<p> The before-queue content filtering feature described in
-this document is suitable only for low-traffic sites. See the "<a
-href="#pros_cons">Pros and Cons</a>" section below for details.
+<p> The before-queue content filtering feature described in this
+document limits the amount of mail that a site can handle. See the
+"<a href="#pros_cons">Pros and Cons</a>" section below for details.
 </p>
 
 <h2>The Postfix before-queue content filter feature</h2>
@@ -154,14 +154,27 @@ a deadline.  As the system load increases, fewer and fewer CPU
 cycles remain available to answer within the deadline, and eventually
 you either have to stop accepting mail or you have to stop filtering
 mail. It is for this reason that the before-queue content filter
-can be used only on low-traffic sites. </p>
+limits the amount of mail that a site can handle. </p>
 
 <li> <p> Con: Content filtering software can use lots of memory
-resources. In order to not run out of memory you have to reduce
-the number of before-filter SMTP server processes so that a burst
-of mail will not drive your system into the ground with too many
-content filter processes. This, in turn, means that SMTP clients
-have to wait for a long time before they receive service.  </p>
+resources. You have to reduce the number of simultaneous content
+filter processes so that a burst of mail will not drive your system
+into the ground. </p>
+<ul>
+
+<li> <p> With Postfix versions 2.7 and later, SMTP clients will
+experience an increase in the delay between the time the client
+sends "end-of-message" and the time the Postfix SMTP server replies
+(here, the number of before-filter SMTP server processes can be
+larger than the number of filter processes).  </p>
+
+<li> <p> With Postfix versions before 2.7, SMTP clients will
+experience an increase in the delay before they can receive service
+(here, the number of before-filter SMTP server processes is always
+equal to the number of filter processes).  </p>
+
+</ul>
 
 </ul>
 
@@ -239,6 +252,8 @@ http://bent.latency.net/smtpprox/. </p>
     smtp      inet  n       -       n       -       20      smtpd
         -o smtpd_proxy_filter=127.0.0.1:10025
         -o smtpd_client_connection_count_limit=10
+       # Postfix 2.7 and later performance feature.
+       # -o smtpd_proxy_options=speed_adjust
     #
     # After-filter SMTP server. Receive mail from the content filter
     # on localhost port 10026.
@@ -276,10 +291,24 @@ the top of the master.cf file: </p>
     later.  Earlier Postfix versions will ignore it.  </p>
 
     <li> <p> The "-o smtpd_proxy_filter=127.0.0.1:10025" tells the
-    before filter SMTP server that it should give incoming mail to
+    before-filter SMTP server that it should give incoming mail to
     the content filter that listens on localhost TCP port 10025.
 
-    <li> <p> Postfix 2.3 supports both TCP and UNIX-domain filters.
+    <li> <p> The "-o smtpd_proxy_options=speed_adjust" tells the
+    before-filter SMTP server that it should receive an entire email
+    message before it connects to a content filter. This reduces
+    the number of simultaneous filter processes. </p>
+
+    <p> NOTE 1: When this option is turned on, a content filter must
+    not <i>selectively</i> reject recipients of a multi-recipient
+    message.  Rejecting all recipients is OK, as is accepting all
+    recipients.  </p>
+
+    <p> NOTE 2: This feature increases the minimum amount of free
+    queue space by $message_size_limit. The extra space is needed
+    to save the message to a temporary file. </p>
+
+    <li> <p> Postfix &ge; 2.3 supports both TCP and UNIX-domain filters.
     The above filter could be specified as "inet:127.0.0.1:10025".
     To specify a UNIX-domain filter, specify "unix:<i>pathname</i>".
     A relative pathname is interpreted relative to the Postfix queue
@@ -299,7 +328,7 @@ the top of the master.cf file: </p>
 
     <li> <p> The "-o smtpd_authorized_xforward_hosts=127.0.0.0/8"
     allows the after-filter SMTP server to receive remote SMTP
-    client information from the before filter SMTP server, so that
+    client information from the before-filter SMTP server, so that
     the after-filter Postfix daemons log the remote SMTP client
     information instead of logging localhost[127.0.0.1]. </p>
 
index 5340ea671fc1069ec873b3a285d8a1adb3b81e4a..5fff74cfa9930f64a8df352d98d0b286d24de2c2 100644 (file)
@@ -5362,6 +5362,35 @@ By default, the Postfix hostname is used.
 This feature is available in Postfix 2.1 and later.
 </p>
 
+%PARAM smtpd_proxy_options 
+
+<p>
+List of options that control how the Postfix SMTP server
+communicates with a before-queue content filter. Specify zero or
+more of the following, separated by comma or whitespace.  </p>
+
+<dl>
+
+<dt><b>speed_adjust</b></dt>
+
+<dd> Do not connect to a before-queue content filter until an entire
+message has been received. This reduces the number of simultaneous
+before-queue content filter processes. </p>
+
+<p> NOTE 1: A filter must not <i>selectively</i> reject recipients
+of a multi-recipient message.  Rejecting all recipients is OK, as
+is accepting all recipients. </p>
+
+<p> NOTE 2: This feature increases the minimum amount of free queue
+space by $message_size_limit. The extra space is needed to save the
+message to a temporary file. </p> </dd>
+
+</dl>
+
+<p>
+This feature is available in Postfix 2.7 and later.
+</p>
+
 %CLASS smtpd-proxy SMTP Proxy filter
 
 <p>
@@ -12567,10 +12596,9 @@ invalid command syntax that would otherwise be rejected by Postfix.
 </p>
 
 <p> Specify the name of a "type:table" lookup table. The search
-string is the SMTP command as received from the SMTP client. The
-only change made is to remove initial whitespace and the trailing
-<CR><LF> characters. The result value is executed by the Postfix
-SMTP server.  </p>
+string is the SMTP command as received from the SMTP client, except
+that initial whitespace and the trailing <CR><LF> are removed. The
+result value is executed by the Postfix SMTP server.  </p>
 
 <p> Examples: </p>
 
@@ -12591,7 +12619,7 @@ SMTP server.  </p>
 </pre>
 
 <pre>
-    # Work around clients that enclose addresses in single quotes.
+    # Work around clients that send RCPT TO:<'user@domain'>.
     # WARNING: do not lose the parameters that follow the address.
     /^RCPT\s+TO:\s*<'([^[:space:]]+)'>(.*)/     RCPT TO:<$1>$2
 </pre>
index 3dc6c609683204bb23726fa9600ed3d616f31595..b8157e71b1236f0b80e31257e09aee8aa499c9f2 100644 (file)
@@ -278,7 +278,8 @@ int     mail_copy(const char *sender,
 #ifndef NO_TRUNCATE
     if ((flags & MAIL_COPY_TOFILE) != 0)
        if (corrupt_error || read_error || write_error)
-           ftruncate(vstream_fileno(dst), orig_length);
+           /* Complain about ignored "undo" errors? So sue me. */
+           (void) ftruncate(vstream_fileno(dst), orig_length);
 #endif
     write_error |= vstream_fclose(dst);
 
index d53931c6ea8172ad55b110dc2d3a15e09a5f1739..f5350a1acdabb0580b1613708dee6b9c46e236a3 100644 (file)
@@ -510,7 +510,7 @@ void    mail_params_init()
        VAR_SYSLOG_FACILITY, DEF_SYSLOG_FACILITY, &var_syslog_facility, 1, 0,
        VAR_INET_PROTOCOLS, DEF_INET_PROTOCOLS, &var_inet_protocols, 1, 0,
        VAR_MULTI_CONF_DIRS, DEF_MULTI_CONF_DIRS, &var_multi_conf_dirs, 0, 0,
-       VAR_MULTI_WRAPPER, DEF_MULTI_WRAPPER, &var_multi_wrapper, 0, 0,
+       /* multi_instance_wrapper may have dependencies but not dependents. */
        VAR_MULTI_GROUP, DEF_MULTI_GROUP, &var_multi_group, 0, 0,
        VAR_MULTI_NAME, DEF_MULTI_NAME, &var_multi_name, 0, 0,
        0,
@@ -567,6 +567,8 @@ void    mail_params_init()
        VAR_PROXYMAP_SERVICE, DEF_PROXYMAP_SERVICE, &var_proxymap_service, 1, 0,
        VAR_PROXYWRITE_SERVICE, DEF_PROXYWRITE_SERVICE, &var_proxywrite_service, 1, 0,
        VAR_INT_FILT_CLASSES, DEF_INT_FILT_CLASSES, &var_int_filt_classes, 0, 0,
+       /* multi_instance_wrapper may have dependencies but not dependents. */
+       VAR_MULTI_WRAPPER, DEF_MULTI_WRAPPER, &var_multi_wrapper, 0, 0,
        0,
     };
     static const CONFIG_STR_FN_TABLE function_str_defaults_2[] = {
index 4b84ca45489f605f3d023c6e49b7682372aa1c87..6a1fa321cb16d79e64bd1d041c1babfbc46ed096 100644 (file)
@@ -2714,6 +2714,10 @@ extern char *var_smtpd_proxy_ehlo;
 #define DEF_SMTPD_PROXY_TMOUT          "100s"
 extern int var_smtpd_proxy_tmout;
 
+#define VAR_SMTPD_PROXY_OPTS           "smtpd_proxy_options"
+#define DEF_SMTPD_PROXY_OPTS           ""
+extern char *var_smtpd_proxy_opts;
+
  /*
   * Transparency options for mail input interfaces and for the cleanup server
   * behind them. These should turn off stuff we don't want to happen, because
index 58e17d8668ed34034f9b200bce9ec4e8b4383b6c..e0c2c22a3b4f7577c52ff36fc421d18582249047 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      "20091023"
+#define MAIL_RELEASE_DATE      "20091109"
 #define MAIL_VERSION_NUMBER    "2.7"
 
 #ifdef SNAPSHOT
index 42c2ad9d2ffb64c96befb7c053e951c11d29d474..5fd068241abf5b15b87aec5186f408cadd072dc7 100644 (file)
@@ -514,6 +514,7 @@ NORETURN event_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
     int     alone = 0;
     int     zerolimit = 0;
     WATCHDOG *watchdog;
+    char   *oname_val;
     char   *oname;
     char   *oval;
     const char *err;
@@ -595,11 +596,13 @@ NORETURN event_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
            service_name = optarg;
            break;
        case 'o':
-           if ((err = split_nameval(mystrdup(optarg), &oname, &oval)) != 0)
+           oname_val = mystrdup(optarg);
+           if ((err = split_nameval(oname_val, &oname, &oval)) != 0)
                msg_fatal("invalid \"-o %s\" option value: %s", optarg, err);
            mail_conf_update(oname, oval);
            if (strcmp(oname, VAR_SYSLOG_NAME) == 0)
                redo_syslog_init = 1;
+           myfree(oname_val);
            break;
        case 's':
            if ((socket_count = atoi(optarg)) <= 0)
index 671aa65bfe1bcd65cc02006ef6db45b26b671f08..dc39471295800e7949bd4226a70f66f24be3f23e 100644 (file)
@@ -515,6 +515,7 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
     int     alone = 0;
     int     zerolimit = 0;
     WATCHDOG *watchdog;
+    char   *oname_val;
     char   *oname;
     char   *oval;
     const char *err;
@@ -596,11 +597,13 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
            service_name = optarg;
            break;
        case 'o':
-           if ((err = split_nameval(mystrdup(optarg), &oname, &oval)) != 0)
+           oname_val = mystrdup(optarg);
+           if ((err = split_nameval(oname_val, &oname, &oval)) != 0)
                msg_fatal("invalid \"-o %s\" option value: %s", optarg, err);
            mail_conf_update(oname, oval);
            if (strcmp(oname, VAR_SYSLOG_NAME) == 0)
                redo_syslog_init = 1;
+           myfree(oname_val);
            break;
        case 's':
            if ((socket_count = atoi(optarg)) <= 0)
index f6923344bd148b310674958ec0ad96f60d6e0bf2..009f3acee1774b1f8ee1d9e5e5af3c2565a96e30 100644 (file)
@@ -404,6 +404,7 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...)
     int     alone = 0;
     int     zerolimit = 0;
     WATCHDOG *watchdog;
+    char   *oname_val;
     char   *oname;
     char   *oval;
     const char *err;
@@ -485,11 +486,13 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...)
            service_name = optarg;
            break;
        case 'o':
-           if ((err = split_nameval(mystrdup(optarg), &oname, &oval)) != 0)
+           oname_val = mystrdup(optarg);
+           if ((err = split_nameval(oname_val, &oname, &oval)) != 0)
                msg_fatal("invalid \"-o %s\" option value: %s", optarg, err);
            mail_conf_update(oname, oval);
            if (strcmp(oname, VAR_SYSLOG_NAME) == 0)
                redo_syslog_init = 1;
+           myfree(oname_val);
            break;
        case 's':
            if ((socket_count = atoi(optarg)) <= 0)
index a5d0ade28fcdf48c6eee47145d8bf3938b2cf219..b40df7f5e6db07556ebb5571735471b6f248e38a 100644 (file)
@@ -415,6 +415,7 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,..
     int     alone = 0;
     int     zerolimit = 0;
     WATCHDOG *watchdog;
+    char   *oname_val;
     char   *oname;
     char   *oval;
     const char *err;
@@ -496,11 +497,13 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,..
            service_name = optarg;
            break;
        case 'o':
-           if ((err = split_nameval(mystrdup(optarg), &oname, &oval)) != 0)
+           oname_val = mystrdup(optarg);
+           if ((err = split_nameval(oname_val, &oname, &oval)) != 0)
                msg_fatal("invalid \"-o %s\" option value: %s", optarg, err);
            mail_conf_update(oname, oval);
            if (strcmp(oname, VAR_SYSLOG_NAME) == 0)
                redo_syslog_init = 1;
+           myfree(oname_val);
            break;
        case 's':
            if ((socket_count = atoi(optarg)) <= 0)
index 91a274b683f824cc9c4ba23441fd018ac12c1e9f..eac71d225f751bd4ed59869d3443e5ff58213a2a 100644 (file)
@@ -312,6 +312,7 @@ smtpd_milter.o: ../../include/name_mask.h
 smtpd_milter.o: ../../include/quote_821_local.h
 smtpd_milter.o: ../../include/quote_flags.h
 smtpd_milter.o: ../../include/resolve_clnt.h
+smtpd_milter.o: ../../include/split_at.h
 smtpd_milter.o: ../../include/sys_defs.h
 smtpd_milter.o: ../../include/tls.h
 smtpd_milter.o: ../../include/vbuf.h
@@ -354,13 +355,16 @@ smtpd_proxy.o: ../../include/iostuff.h
 smtpd_proxy.o: ../../include/mail_error.h
 smtpd_proxy.o: ../../include/mail_params.h
 smtpd_proxy.o: ../../include/mail_proto.h
+smtpd_proxy.o: ../../include/mail_queue.h
 smtpd_proxy.o: ../../include/mail_stream.h
 smtpd_proxy.o: ../../include/milter.h
 smtpd_proxy.o: ../../include/msg.h
 smtpd_proxy.o: ../../include/myaddrinfo.h
+smtpd_proxy.o: ../../include/mymalloc.h
 smtpd_proxy.o: ../../include/name_code.h
 smtpd_proxy.o: ../../include/name_mask.h
 smtpd_proxy.o: ../../include/rec_type.h
+smtpd_proxy.o: ../../include/record.h
 smtpd_proxy.o: ../../include/smtp_stream.h
 smtpd_proxy.o: ../../include/stringops.h
 smtpd_proxy.o: ../../include/sys_defs.h
index 787c4841444b8e4d5c8f8e904179d6ae68285716..cc4f803cbcf0f14d9c4bf14524ce5da390b64a59 100644 (file)
@@ -93,8 +93,7 @@
 /*     local hostname were specified, instead of rejecting the address as
 /*     invalid.
 /* .IP "\fBsmtpd_command_filter (empty)\fR"
-/*     A mechanism to substitute incoming SMTP commands by other
-/*     commands.
+/*     A mechanism to substitute incoming SMTP commands.
 /* .IP "\fBsmtpd_reject_unlisted_sender (no)\fR"
 /*     Request that the Postfix SMTP server rejects mail from unknown
 /*     sender addresses, even when no explicit reject_unlisted_sender
 /*     The hostname and TCP port of the mail filtering proxy server.
 /* .IP "\fBsmtpd_proxy_ehlo ($myhostname)\fR"
 /*     How the Postfix SMTP server announces itself to the proxy filter.
+/* .IP "\fBsmtpd_proxy_options (empty)\fR"
+/*     List of options that control how the Postfix SMTP server
+/*     communicates with a before-queue content filter.
 /* .IP "\fBsmtpd_proxy_timeout (100s)\fR"
 /*     The time limit for connecting to a proxy filter and for sending or
 /*     receiving information.
@@ -1143,6 +1145,7 @@ int     var_verify_poll_delay;
 char   *var_smtpd_proxy_filt;
 int     var_smtpd_proxy_tmout;
 char   *var_smtpd_proxy_ehlo;
+char   *var_smtpd_proxy_opts;
 char   *var_input_transp;
 int     var_smtpd_policy_tmout;
 int     var_smtpd_policy_idle;
@@ -1229,6 +1232,8 @@ char   *var_unk_addr_tf_act;
 char   *var_unv_rcpt_tf_act;
 char   *var_unv_from_tf_act;
 
+int     smtpd_proxy_opts;
+
  /*
   * Silly little macros.
   */
@@ -1694,10 +1699,11 @@ static int mail_open_stream(SMTPD_STATE *state)
      */
     if (state->proxy_mail) {
        smtpd_check_rewrite(state);
-       if (smtpd_proxy_open(state, var_smtpd_proxy_filt,
-                            var_smtpd_proxy_tmout, var_smtpd_proxy_ehlo,
-                            state->proxy_mail) != 0) {
-           smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
+       if (smtpd_proxy_create(state, smtpd_proxy_opts, var_smtpd_proxy_filt,
+                              var_smtpd_proxy_tmout, var_smtpd_proxy_ehlo,
+                              state->proxy_mail) != 0) {
+           smtpd_chat_reply(state, "%s", STR(state->proxy->buffer));
+           smtpd_proxy_free(state);
            return (-1);
        }
     }
@@ -2219,9 +2225,13 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
     }
 
     /*
-     * Check the queue file space, if applicable.
+     * Check the queue file space, if applicable. The optional before-filter
+     * speed-adjust buffers use disk space. However, we don't know if they
+     * compete for storage space with the after-filter queue, so we can't
+     * simply bump up the free space requirement to 2.5 * message_size_limit.
      */
-    if (!USE_SMTPD_PROXY(state)) {
+    if (!USE_SMTPD_PROXY(state)
+       || (smtpd_proxy_opts & SMTPD_PROXY_FLAG_SPEED_ADJUST)) {
        if (SMTPD_STAND_ALONE(state) == 0
            && (err = smtpd_check_queue(state)) != 0) {
            /* XXX Reset access map side effects. */
@@ -2315,14 +2325,8 @@ static void mail_reset(SMTPD_STATE *state)
     VSTRING_RESET(state->instance);
     VSTRING_TERMINATE(state->instance);
 
-    /*
-     * Try to be nice. Don't bother when we lost the connection. Don't bother
-     * waiting for a reply, it just increases latency.
-     */
-    if (state->proxy) {
-       (void) smtpd_proxy_cmd(state, SMTPD_PROX_WANT_NONE, SMTPD_CMD_QUIT);
-       smtpd_proxy_close(state);
-    }
+    if (state->proxy)
+       smtpd_proxy_free(state);
     if (state->xforward.flags)
        smtpd_xforward_reset(state);
     if (state->prepend)
@@ -2342,6 +2346,7 @@ static void mail_reset(SMTPD_STATE *state)
 
 static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
 {
+    SMTPD_PROXY *proxy;
     const char *err;
     int     narg;
     char   *arg;
@@ -2485,9 +2490,10 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
      * the recipient then we can have a proxy connection without having
      * accepted a recipient.
      */
-    if (state->proxy && smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK,
-                                       "%s", STR(state->buffer)) != 0) {
-       smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
+    proxy = state->proxy;
+    if (proxy != 0 && proxy->cmd(state, SMTPD_PROX_WANT_OK,
+                                "%s", STR(state->buffer)) != 0) {
+       smtpd_chat_reply(state, "%s", STR(proxy->buffer));
        return (-1);
     }
 
@@ -2674,6 +2680,7 @@ static void comment_sanitize(VSTRING *comment_string)
 
 static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
 {
+    SMTPD_PROXY *proxy;
     const char *err;
     char   *start;
     int     len;
@@ -2732,9 +2739,10 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
        smtpd_chat_reply(state, "%s", err);
        return (-1);
     }
-    if (state->proxy && smtpd_proxy_cmd(state, SMTPD_PROX_WANT_MORE,
-                                       "%s", STR(state->buffer)) != 0) {
-       smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
+    proxy = state->proxy;
+    if (proxy != 0 && proxy->cmd(state, SMTPD_PROX_WANT_MORE,
+                                "%s", STR(state->buffer)) != 0) {
+       smtpd_chat_reply(state, "%s", STR(proxy->buffer));
        return (-1);
     }
 
@@ -2743,10 +2751,10 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
      * operation. We want to avoid massive code duplication within tons of
      * if-else clauses.
      */
-    if (state->proxy) {
-       out_stream = state->proxy;
-       out_record = smtpd_proxy_rec_put;
-       out_fprintf = smtpd_proxy_rec_fprintf;
+    if (proxy) {
+       out_stream = proxy->stream;
+       out_record = proxy->rec_put;
+       out_fprintf = proxy->rec_fprintf;
        out_error = CLEANUP_STAT_PROXY;
     } else {
        out_stream = state->cleanup;
@@ -2788,7 +2796,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
      * Suppress our own Received: header in the unlikely case that we are an
      * intermediate proxy.
      */
-    if (!state->proxy || state->xforward.flags == 0) {
+    if (!proxy || state->xforward.flags == 0) {
        out_fprintf(out_stream, REC_TYPE_NORM,
                    "Received: from %s (%s [%s])",
                    state->helo_name ? state->helo_name : state->name,
@@ -2907,7 +2915,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
                out_record(out_stream, REC_TYPE_NORM, "", 0);
        }
        if (prev_rec_type != REC_TYPE_CONT && *start == '.'
-           && (state->proxy == 0 ? (++start, --len) == 0 : len == 1))
+           && (proxy == 0 ? (++start, --len) == 0 : len == 1))
            break;
        if (state->err == CLEANUP_STAT_OK) {
            if (var_message_limit > 0 && var_message_limit - state->act_size < len + 2) {
@@ -2926,7 +2934,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
        && SMTPD_STAND_ALONE(state) == 0
        && (err = smtpd_check_eod(state)) != 0) {
        smtpd_chat_reply(state, "%s", err);
-       if (state->proxy) {
+       if (proxy) {
            smtpd_proxy_close(state);
        } else {
            mail_stream_cleanup(state->dest);
@@ -2939,23 +2947,13 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
     /*
      * Send the end of DATA and finish the proxy connection. Set the
      * CLEANUP_STAT_PROXY error flag in case of trouble.
-     * 
-     * XXX The low-level proxy output routines should set "state" error
-     * attributes. This requires making "state" a context attribute of the
-     * VSTREAM.
      */
-    if (state->proxy) {
+    if (proxy) {
        if (state->err == CLEANUP_STAT_OK) {
-           (void) smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, ".");
+           (void) proxy->cmd(state, SMTPD_PROX_WANT_ANY, ".");
            if (state->err == CLEANUP_STAT_OK &&
-               *STR(state->proxy_buffer) != '2')
+               *STR(proxy->buffer) != '2')
                state->err = CLEANUP_STAT_CONT;
-       } else if (state->err != CLEANUP_STAT_SIZE) {
-           state->err |= CLEANUP_STAT_PROXY;
-           detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
-           vstring_sprintf(state->proxy_buffer,
-                           "%d %s Error: %s",
-                           detail->smtp, detail->dsn, detail->text);
        }
     }
 
@@ -3038,11 +3036,11 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
        state->error_count = 0;
        state->error_mask = 0;
        state->junk_cmds = 0;
-       if (state->queue_id)
+       if (proxy)
+           smtpd_chat_reply(state, "%s", STR(proxy->buffer));
+       else
            smtpd_chat_reply(state,
                             "250 2.0.0 Ok: queued as %s", state->queue_id);
-       else
-           smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
     } else if (why && IS_SMTP_REJECT(STR(why))) {
        state->error_mask |= MAIL_ERROR_POLICY;
        smtpd_chat_reply(state, "%s", STR(why));
@@ -3074,8 +3072,8 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
     } else if ((state->err & CLEANUP_STAT_CONT) != 0) {
        state->error_mask |= MAIL_ERROR_POLICY;
        detail = cleanup_stat_detail(CLEANUP_STAT_CONT);
-       if (state->proxy_buffer) {
-           smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
+       if (proxy) {
+           smtpd_chat_reply(state, "%s", STR(proxy->buffer));
        } else if (why && LEN(why) > 0) {
            /* Allow address-specific DSN status in header/body_checks. */
            smtpd_chat_reply(state, "%d %s", detail->smtp, STR(why));
@@ -3090,7 +3088,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
                         detail->smtp, detail->dsn, detail->text);
     } else if ((state->err & CLEANUP_STAT_PROXY) != 0) {
        state->error_mask |= MAIL_ERROR_SOFTWARE;
-       smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
+       smtpd_chat_reply(state, "%s", STR(proxy->buffer));
     } else {
        state->error_mask |= MAIL_ERROR_SOFTWARE;
        detail = cleanup_stat_detail(CLEANUP_STAT_BAD);
@@ -4857,6 +4855,15 @@ static void post_jail_init(char *unused_name, char **unused_argv)
     smtpd_input_transp_mask =
     input_transp_mask(VAR_INPUT_TRANSP, var_input_transp);
 
+    /*
+     * Initialize before-queue filter options: do we want speed-matching
+     * support so that the entire message is received before we contact a
+     * before-queue content filter?
+     */
+    if (*var_smtpd_proxy_filt)
+       smtpd_proxy_opts =
+           smtpd_proxy_parse_opts(VAR_SMTPD_PROXY_OPTS, var_smtpd_proxy_opts);
+
     /*
      * Sendmail mail filters.
      * 
@@ -5037,6 +5044,7 @@ int     main(int argc, char **argv)
        VAR_VERP_CLIENTS, DEF_VERP_CLIENTS, &var_verp_clients, 0, 0,
        VAR_SMTPD_PROXY_FILT, DEF_SMTPD_PROXY_FILT, &var_smtpd_proxy_filt, 0, 0,
        VAR_SMTPD_PROXY_EHLO, DEF_SMTPD_PROXY_EHLO, &var_smtpd_proxy_ehlo, 0, 0,
+       VAR_SMTPD_PROXY_OPTS, DEF_SMTPD_PROXY_OPTS, &var_smtpd_proxy_opts, 0, 0,
        VAR_INPUT_TRANSP, DEF_INPUT_TRANSP, &var_input_transp, 0, 0,
        VAR_XCLIENT_HOSTS, DEF_XCLIENT_HOSTS, &var_xclient_hosts, 0, 0,
        VAR_XFORWARD_HOSTS, DEF_XFORWARD_HOSTS, &var_xforward_hosts, 0, 0,
index 3f921bcbd368e6b0468b87db5c193859374b4b9a..8900dce15cbb23404af58cbe78a82c7aaf625ec0 100644 (file)
@@ -64,7 +64,7 @@ typedef struct {
     char   *domain;                    /* rewrite context */
 } SMTPD_XFORWARD_ATTR;
 
-typedef struct SMTPD_STATE {
+typedef struct {
     int     flags;                     /* see below */
     int     err;                       /* cleanup server/queue file errors */
     VSTREAM *client;                   /* SMTP client handle */
@@ -153,10 +153,8 @@ typedef struct SMTPD_STATE {
     /*
      * Pass-through proxy client.
      */
-    VSTREAM *proxy;                    /* proxy handle */
-    VSTRING *proxy_buffer;             /* proxy query/reply buffer */
+    struct SMTPD_PROXY *proxy;
     char   *proxy_mail;                        /* owned by mail_cmd() */
-    int     proxy_xforward_features;   /* XFORWARD proxy state */
 
     /*
      * XFORWARD server state.
@@ -350,6 +348,11 @@ extern int smtpd_input_transp_mask;
   */
 extern MILTERS *smtpd_milters;
 
+ /*
+  * Message size multiplication factor for free space check.
+  */
+extern double smtpd_space_multf;
+
 /* LICENSE
 /* .ad
 /* .fi
index 0eb305b035015f954d4f01ae1a144e7d22c0541a..7b7e53b3adeac665326477ce10eda4ddca0f443f 100644 (file)
@@ -435,6 +435,12 @@ typedef struct {
     const char *txt;                   /* randomly selected trimmed TXT rr */
 } SMTPD_RBL_EXPAND_CONTEXT;
 
+ /*
+  * Multiplication factor for free space check. Free space must be at least
+  * smtpd_space_multf * message_size_limit.
+  */
+double  smtpd_space_multf = 1.5;
+
 /* policy_client_register - register policy service endpoint */
 
 static void policy_client_register(const char *name)
@@ -4622,13 +4628,14 @@ char   *smtpd_check_queue(SMTPD_STATE *state)
                 (unsigned long) var_queue_minfree,
                 (unsigned long) var_message_limit);
     if (BLOCKS(var_queue_minfree) >= fsbuf.block_free
-       || BLOCKS(var_message_limit) >= fsbuf.block_free / 1.5) {
+       || BLOCKS(var_message_limit) >= fsbuf.block_free / smtpd_space_multf) {
        (void) smtpd_check_reject(state, MAIL_ERROR_RESOURCE,
                                  452, "4.3.1",
                                  "Insufficient system storage");
        msg_warn("not enough free space in mail queue: %lu bytes < "
-                "1.5*message size limit",
-                (unsigned long) fsbuf.block_free * fsbuf.block_size);
+                "%g*message size limit",
+                (unsigned long) fsbuf.block_free * fsbuf.block_size,
+               smtpd_space_multf);
        return (STR(error_text));
     }
     return (0);
index 1df887cbac05a13ee00e656c35917368689e1436..87055ff70389e7ab3f3f2fedf6726a987beaacc3 100644 (file)
 /*
 /*     typedef struct {
 /* .in +4
-/*             /* other fields... */
-/*             VSTREAM *proxy;         /* connection to SMTP proxy */
-/*             VSTRING *proxy_buffer;  /* last SMTP proxy response */
+/*             VSTREAM *stream;        /* SMTP proxy or replay log */
+/*             VSTRING *buffer;        /* last SMTP proxy response */
 /*             /* other fields... */
 /* .in -4
-/*     } SMTPD_STATE;
+/*     } SMTPD_PROXY;
 /*
-/*     int     smtpd_proxy_open(state, service, timeout, ehlo_name, mail_from)
+/*     int     smtpd_proxy_create(state, flags, service, timeout,
+/*                                     ehlo_name, mail_from)
 /*     SMTPD_STATE *state;
+/*     int     flags;
 /*     const char *service;
 /*     int     timeout;
 /*     const char *ehlo_name;
 /*     const char *mail_from;
 /*
-/*     int     smtpd_proxy_cmd(state, expect, format, ...)
+/*     int     proxy->cmd(state, expect, format, ...)
+/*     SMTPD_PROXY *proxy;
 /*     SMTPD_STATE *state;
 /*     int     expect;
-/*     cont char *format;
+/*     const char *format;
+/*
+/*     void    smtpd_proxy_disconnect(state)
+/*     SMTPD_STATE *state;
 /*
-/*     void    smtpd_proxy_close(state)
+/*     void    smtpd_proxy_free(state)
 /*     SMTPD_STATE *state;
+/*
+/*     int     smtpd_proxy_parse_opts(param_name, param_val)
+/*     const char *param_name;
+/*     const char *param_val;
 /* RECORD-LEVEL ROUTINES
-/*     int     smtpd_proxy_rec_put(stream, rec_type, data, len)
-/*     VSTREAM *stream;
+/*     int     proxy->rec_put(proxy->stream, rec_type, data, len)
+/*     SMTPD_PROXY *proxy;
 /*     int     rec_type;
 /*     const char *data;
 /*     ssize_t len;
 /*
-/*     int     smtpd_proxy_rec_fprintf(stream, rec_type, format, ...)
-/*     VSTREAM *stream;
+/*     int     proxy->rec_fprintf(proxy->stream, rec_type, format, ...)
+/*     SMTPD_PROXY *proxy;
 /*     int     rec_type;
 /*     cont char *format;
 /* DESCRIPTION
 /*     The functions in this module implement a pass-through proxy
 /*     client.
 /*
-/*     In order to minimize the intrusiveness of pass-through proxying, 1) the
-/*     proxy server must support the same MAIL FROM/RCPT syntax that Postfix
-/*     supports, 2) the record-level routines for message content proxying
-/*     have the same interface as the routines that are used for non-proxied
-/*     mail.
+/*     In order to minimize the intrusiveness of pass-through
+/*     proxying, 1) the proxy server must support the same MAIL
+/*     FROM/RCPT syntax that Postfix supports, 2) the record-level
+/*     routines for message content proxying have the same interface
+/*     as the routines that are used for non-proxied mail.
+/*
+/*     smtpd_proxy_create() takes a description of a before-queue
+/*     filter.  Depending on flags, it either arranges to buffer
+/*     up commands and message content until the entire message
+/*     is received, or it immediately connects to the proxy service,
+/*     sends EHLO, sends client information with the XFORWARD
+/*     command if possible, sends the MAIL FROM command, and
+/*     receives the reply.
+/*     A non-zero result value means trouble: either the proxy is
+/*     unavailable, or it did not send the expected reply.
+/*     All results are reported via the proxy->buffer field in a
+/*     form that can be sent to the SMTP client.  An unexpected
+/*     2xx or 3xx proxy server response is replaced by a generic
+/*     error response to avoid support problems.
+/*     In case of error, smtpd_proxy_create() updates the
+/*     state->error_mask and state->err fields, and leaves the the
+/*     proxy handle in an unconnected state.  Destroy the handle
+/*     after reporting the error reply in the proxy->buffer field.
+/*
+/*     proxy->cmd() formats and either buffers up the command and
+/*     expected response until the entire message is received, or
+/*     it immediately sends the specified command to the proxy
+/*     server, and receives the proxy server reply.
+/*     A non-zero result value means trouble: either the proxy is
+/*     unavailable, or it did not send the expected reply.
+/*     All results are reported via the proxy->buffer field in a
+/*     form that can be sent to the SMTP client.  An unexpected
+/*     2xx or 3xx proxy server response is replaced by a generic
+/*     error response to avoid support problems.
+/*     In case of error, proxy->cmd() updates the state->error_mask
+/*     and state->err fields.
 /*
-/*     smtpd_proxy_open() connects to the proxy service, sends EHLO, sends
-/*     client information with the XFORWARD command if possible, sends
-/*     the MAIL FROM command, and receives the reply. A non-zero result means
-/*     trouble: either the proxy is unavailable, or it did not send the
-/*     expected reply.
-/*     The result is reported via the state->proxy_buffer field in a form
-/*     that can be sent to the SMTP client. In case of error, the
-/*     state->error_mask and state->err fields are updated.
-/*     A state->proxy_buffer field is created automatically; this field
-/*     persists beyond the end of a proxy session.
+/*     smtpd_proxy_disconnect() disconnects from a proxy server.
+/*     The last proxy server reply or error description remains
+/*     available via the proxy->buffer field.
 /*
-/*     smtpd_proxy_cmd() formats and sends the specified command to the
-/*     proxy server, and receives the proxy server reply. A non-zero result
-/*     means trouble: either the proxy is unavailable, or it did not send the
-/*      expected reply.
-/*     All results are reported via the state->proxy_buffer field in a form
-/*     that can be sent to the SMTP client. In case of error, the
-/*     state->error_mask and state->err fields are updated.
+/*     smtpd_proxy_free() destroys a proxy server handle and resets
+/*     the state->proxy field.
 /*
-/*     smtpd_proxy_close() disconnects from a proxy server and resets
-/*     the state->proxy field. The last proxy server reply or error
-/*     description remains available via state->proxy-reply.
+/*     smtpd_proxy_parse_opts() parses main.cf processing options.
 /*
-/*     smtpd_proxy_rec_put() is a rec_put() clone that passes arbitrary
-/*     message content records to the proxy server. The data is expected
-/*     to be in SMTP dot-escaped form. All errors are reported as a
-/*     REC_TYPE_ERROR result value.
+/*     proxy->rec_put() is a rec_put() clone that either buffers
+/*     up arbitrary message content records until the entire message
+/*     is received, or that immediately sends it to the proxy
+/*     server.
+/*     All data is expected to be in SMTP dot-escaped form.
+/*     All errors are reported as a REC_TYPE_ERROR result value,
+/*     with the state->error_mask, state->err and proxy-buffer
+/*     fields given appropriate values.
 /*
-/*     smtpd_proxy_rec_fprintf() is a rec_fprintf() clone that formats
-/*     message content and sends it to the proxy server. Leading dots are
-/*     not escaped. All errors are reported as a REC_TYPE_ERROR result
-/*     value.
+/*     proxy->rec_fprintf() is a rec_fprintf() clone that formats
+/*     message content and either buffers up the record until the
+/*     entire message is received, or that immediately sends it
+/*     to the proxy server.
+/*     All data is expected to be in SMTP dot-escaped form.
+/*     All errors are reported as a REC_TYPE_ERROR result value,
+/*     with the state->error_mask, state->err and proxy-buffer
+/*     fields given appropriate values.
 /*
 /* Arguments:
+/* .IP flags
+/*     Zero, or SMTPD_PROXY_FLAG_SPEED_ADJUST to buffer up the entire
+/*     message before contacting a before-queue content filter.
+/*     Note: when this feature is requested, the before-queue
+/*     filter MUST use the same 2xx, 4xx or 5xx reply code for all
+/*     recipients of a multi-recipient message.
 /* .IP server
 /*     The SMTP proxy server host:port. The host or host: part is optional.
+/*     This argument is not duplicated.
 /* .IP timeout
 /*     Time limit for connecting to the proxy server and for
 /*     sending and receiving proxy server commands and replies.
 /* .IP ehlo_name
 /*     The EHLO Hostname that will be sent to the proxy server.
+/*     This argument is not duplicated.
 /* .IP mail_from
-/*     The MAIL FROM command.
+/*     The MAIL FROM command. This argument is not duplicated.
 /* .IP state
 /*     SMTP server state.
 /* .IP expect
 /* SEE ALSO
 /*     smtpd(8) Postfix smtp server
 /* DIAGNOSTICS
+/*     Panic: internal API violations.
+/*
 /*     Fatal errors: memory allocation problem.
 /*
 /*     Warnings: unexpected response from proxy server, unable
-/*     to connect to proxy server, proxy server read/write error.
+/*     to connect to proxy server, proxy server read/write error,
+/*     proxy speed-adjust buffer read/write error.
 /* LICENSE
 /* .ad
 /* .fi
 /* System library. */
 
 #include <sys_defs.h>
+#include <sys/stat.h>
 #include <ctype.h>
+#include <unistd.h>
 
 #ifdef STRCASECMP_IN_STRINGS_H
 #include <strings.h>
 #include <stringops.h>
 #include <connect.h>
 #include <name_code.h>
+#include <mymalloc.h>
 
 /* Global library. */
 
 #include <mail_proto.h>
 #include <mail_params.h>               /* null_format_string */
 #include <xtext.h>
+#include <record.h>
+#include <mail_queue.h>
 
 /* Application-specific. */
 
 #define SMTPD_PROXY_XFORWARD_DOMAIN (1<<5)     /* origin type */
 #define SMTPD_PROXY_XFORWARD_PORT  (1<<6)      /* client port */
 
+ /*
+  * Spead-matching: we use an unlinked file for transient storage.
+  */
+static VSTREAM *smtpd_proxy_replay_stream;
+
+ /*
+  * Forward declarations.
+  */
+static void smtpd_proxy_fake_server_reply(SMTPD_STATE *, int);
+static int smtpd_proxy_rdwr_error(SMTPD_STATE *, int);
+static int smtpd_proxy_cmd(SMTPD_STATE *, int, const char *,...);
+static int smtpd_proxy_rec_put(VSTREAM *, int, const char *, ssize_t);
+
  /*
   * SLMs.
   */
 #define STR(x) vstring_str(x)
 #define LEN(x) VSTRING_LEN(x)
-#define SMTPD_PROXY_CONNECT null_format_string
+#define SMTPD_PROXY_CONN_FMT null_format_string
 #define STREQ(x, y)    (strcmp((x), (y)) == 0)
 
-/* smtpd_xforward_flush - flush forwarding information */
+/* smtpd_proxy_xforward_flush - flush forwarding information */
 
-static int smtpd_xforward_flush(SMTPD_STATE *state, VSTRING *buf)
+static int smtpd_proxy_xforward_flush(SMTPD_STATE *state, VSTRING *buf)
 {
     int     ret;
 
@@ -203,10 +266,12 @@ static int smtpd_xforward_flush(SMTPD_STATE *state, VSTRING *buf)
     return (0);
 }
 
-/* smtpd_xforward - send forwarding information */
+/* smtpd_proxy_xforward_send - send forwarding information */
 
-static int smtpd_xforward(SMTPD_STATE *state, VSTRING *buf, const char *name,
-                                 int value_available, const char *value)
+static int smtpd_proxy_xforward_send(SMTPD_STATE *state, VSTRING *buf,
+                                            const char *name,
+                                            int value_available,
+                                            const char *value)
 {
     size_t  new_len;
     int     ret;
@@ -236,26 +301,25 @@ static int smtpd_xforward(SMTPD_STATE *state, VSTRING *buf, const char *name,
      * Flush the buffer if we need to, and store the attribute.
      */
     if (VSTRING_LEN(buf) > 0 && VSTRING_LEN(buf) + new_len > PAYLOAD_LIMIT)
-       if ((ret = smtpd_xforward_flush(state, buf)) < 0)
+       if ((ret = smtpd_proxy_xforward_flush(state, buf)) < 0)
            return (ret);
     vstring_sprintf_append(buf, " %s=%s", name, STR(state->expand_buf));
 
     return (0);
 }
 
-/* smtpd_proxy_open - open proxy connection */
+/* smtpd_proxy_connect - open proxy connection */
 
-int     smtpd_proxy_open(SMTPD_STATE *state, const char *service,
-                                int timeout, const char *ehlo_name,
-                                const char *mail_from)
+static int smtpd_proxy_connect(SMTPD_STATE *state)
 {
+    SMTPD_PROXY *proxy = state->proxy;
     int     fd;
     char   *lines;
     char   *words;
     VSTRING *buf;
     int     bad;
     char   *word;
-    static const NAME_CODE xforward_features[] = {
+    static const NAME_CODE known_xforward_features[] = {
        XFORWARD_NAME, SMTPD_PROXY_XFORWARD_NAME,
        XFORWARD_ADDR, SMTPD_PROXY_XFORWARD_ADDR,
        XFORWARD_PORT, SMTPD_PROXY_XFORWARD_PORT,
@@ -264,61 +328,45 @@ int     smtpd_proxy_open(SMTPD_STATE *state, const char *service,
        XFORWARD_DOMAIN, SMTPD_PROXY_XFORWARD_DOMAIN,
        0, 0,
     };
-    const CLEANUP_STAT_DETAIL *detail;
+    int     server_xforward_features;
     int     (*connect_fn) (const char *, int, int);
     const char *endpoint;
 
-    /*
-     * This buffer persists beyond the end of a proxy session so we can
-     * inspect the last command's reply.
-     */
-    if (state->proxy_buffer == 0)
-       state->proxy_buffer = vstring_alloc(10);
-
     /*
      * Find connection method (default inet)
      */
-    if (strncasecmp("unix:", service, 5) == 0) {
-       endpoint = service + 5;
+    if (strncasecmp("unix:", proxy->service_name, 5) == 0) {
+       endpoint = proxy->service_name + 5;
        connect_fn = unix_connect;
     } else {
-       if (strncasecmp("inet:", service, 5) == 0)
-           endpoint = service + 5;
+       if (strncasecmp("inet:", proxy->service_name, 5) == 0)
+           endpoint = proxy->service_name + 5;
        else
-           endpoint = service;
+           endpoint = proxy->service_name;
        connect_fn = inet_connect;
     }
 
     /*
      * Connect to proxy.
      */
-    if ((fd = connect_fn(endpoint, BLOCKING, timeout)) < 0) {
-       state->error_mask |= MAIL_ERROR_SOFTWARE;
-       state->err |= CLEANUP_STAT_PROXY;
-       msg_warn("connect to proxy service %s: %m", service);
-       detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
-       vstring_sprintf(state->proxy_buffer,
-                       "%d %s Error: %s",
-                       detail->smtp, detail->dsn, detail->text);
-       return (-1);
-    }
-    state->proxy = vstream_fdopen(fd, O_RDWR);
-    vstream_control(state->proxy, VSTREAM_CTL_PATH, service, VSTREAM_CTL_END);
-    smtp_timeout_setup(state->proxy, timeout);
+    if ((fd = connect_fn(endpoint, BLOCKING, proxy->timeout)) < 0)
+       return (smtpd_proxy_rdwr_error(state, 0));
+    proxy->service_stream = vstream_fdopen(fd, O_RDWR);
+    /* Needed by our DATA-phase record emulation routines. */
+    vstream_control(proxy->service_stream, VSTREAM_CTL_CONTEXT,
+                   (char *) state, VSTREAM_CTL_END);
+    smtp_timeout_setup(proxy->service_stream, proxy->timeout);
 
     /*
      * Get server greeting banner.
      * 
      * If this fails then we have a problem because the proxy should always
      * accept our connection. Make up our own response instead of passing
-     * back the greeting banner: the proxy open might be delayed to the point
-     * that the client expects a MAIL FROM or RCPT TO reply.
-     */
-    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, SMTPD_PROXY_CONNECT) != 0) {
-       detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
-       vstring_sprintf(state->proxy_buffer,
-                       "%d %s Error: %s",
-                       detail->smtp, detail->dsn, detail->text);
+     * back a negative greeting banner: the proxy open is delayed to the
+     * point that the client expects a MAIL FROM or RCPT TO reply.
+     */
+    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, SMTPD_PROXY_CONN_FMT)) {
+       smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
        smtpd_proxy_close(state);
        return (-1);
     }
@@ -326,15 +374,13 @@ int     smtpd_proxy_open(SMTPD_STATE *state, const char *service,
     /*
      * Send our own EHLO command. If this fails then we have a problem
      * because the proxy should always accept our EHLO command. Make up our
-     * own response instead of passing back the EHLO reply: the proxy open
-     * might be delayed to the point that the client expects a MAIL FROM or
+     * own response instead of passing back a negative EHLO reply: the proxy
+     * open is delayed to the point that the client expects a MAIL FROM or
      * RCPT TO reply.
      */
-    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "EHLO %s", ehlo_name) != 0) {
-       detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
-       vstring_sprintf(state->proxy_buffer,
-                       "%d %s Error: %s",
-                       detail->smtp, detail->dsn, detail->text);
+    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "EHLO %s",
+                       proxy->ehlo_name)) {
+       smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
        smtpd_proxy_close(state);
        return (-1);
     }
@@ -342,61 +388,56 @@ int     smtpd_proxy_open(SMTPD_STATE *state, const char *service,
     /*
      * Parse the EHLO reply and see if we can forward logging information.
      */
-    state->proxy_xforward_features = 0;
-    lines = STR(state->proxy_buffer);
+    server_xforward_features = 0;
+    lines = STR(proxy->buffer);
     while ((words = mystrtok(&lines, "\n")) != 0) {
        if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) {
            if (strcasecmp(word, XFORWARD_CMD) == 0)
                while ((word = mystrtok(&words, " \t")) != 0)
-                   state->proxy_xforward_features |=
-                       name_code(xforward_features, NAME_CODE_FLAG_NONE, word);
+                   server_xforward_features |=
+                       name_code(known_xforward_features,
+                                 NAME_CODE_FLAG_NONE, word);
        }
     }
 
     /*
      * Send XFORWARD attributes. For robustness, explicitly specify what SMTP
-     * session attributes are known and unknown.
+     * session attributes are known and unknown. Make up our own response
+     * instead of passing back a negative XFORWARD reply: the proxy open is
+     * delayed to the point that the client expects a MAIL FROM or RCPT TO
+     * reply.
      */
-    if (state->proxy_xforward_features) {
+    if (server_xforward_features) {
        buf = vstring_alloc(100);
-       bad = 0;
-       if (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_NAME)
-           bad = smtpd_xforward(state, buf, XFORWARD_NAME,
-                                IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state)),
-                                FORWARD_NAME(state));
-       if (bad == 0
-           && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_ADDR))
-           bad = smtpd_xforward(state, buf, XFORWARD_ADDR,
-                                IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)),
-                                FORWARD_ADDR(state));
-       if (bad == 0
-           && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_PORT))
-           bad = smtpd_xforward(state, buf, XFORWARD_PORT,
-                                IS_AVAIL_CLIENT_PORT(FORWARD_PORT(state)),
-                                FORWARD_PORT(state));
-       if (bad == 0
-           && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_HELO))
-           bad = smtpd_xforward(state, buf, XFORWARD_HELO,
-                                IS_AVAIL_CLIENT_HELO(FORWARD_HELO(state)),
-                                FORWARD_HELO(state));
-       if (bad == 0
-           && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_PROTO))
-           bad = smtpd_xforward(state, buf, XFORWARD_PROTO,
-                                IS_AVAIL_CLIENT_PROTO(FORWARD_PROTO(state)),
-                                FORWARD_PROTO(state));
-       if (bad == 0
-         && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_DOMAIN))
-           bad = smtpd_xforward(state, buf, XFORWARD_DOMAIN, 1,
+       bad =
+           (((server_xforward_features & SMTPD_PROXY_XFORWARD_NAME)
+             && smtpd_proxy_xforward_send(state, buf, XFORWARD_NAME,
+                                 IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state)),
+                                          FORWARD_NAME(state)))
+            || ((server_xforward_features & SMTPD_PROXY_XFORWARD_ADDR)
+                && smtpd_proxy_xforward_send(state, buf, XFORWARD_ADDR,
+                                 IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)),
+                                             FORWARD_ADDR(state)))
+            || ((server_xforward_features & SMTPD_PROXY_XFORWARD_PORT)
+                && smtpd_proxy_xforward_send(state, buf, XFORWARD_PORT,
+                                 IS_AVAIL_CLIENT_PORT(FORWARD_PORT(state)),
+                                             FORWARD_PORT(state)))
+            || ((server_xforward_features & SMTPD_PROXY_XFORWARD_HELO)
+                && smtpd_proxy_xforward_send(state, buf, XFORWARD_HELO,
+                                 IS_AVAIL_CLIENT_HELO(FORWARD_HELO(state)),
+                                             FORWARD_HELO(state)))
+            || ((server_xforward_features & SMTPD_PROXY_XFORWARD_PROTO)
+                && smtpd_proxy_xforward_send(state, buf, XFORWARD_PROTO,
+                               IS_AVAIL_CLIENT_PROTO(FORWARD_PROTO(state)),
+                                             FORWARD_PROTO(state)))
+            || ((server_xforward_features & SMTPD_PROXY_XFORWARD_DOMAIN)
+                && smtpd_proxy_xforward_send(state, buf, XFORWARD_DOMAIN, 1,
                         STREQ(FORWARD_DOMAIN(state), MAIL_ATTR_RWR_LOCAL) ?
-                                XFORWARD_DOM_LOCAL : XFORWARD_DOM_REMOTE);
-       if (bad == 0)
-           bad = smtpd_xforward_flush(state, buf);
+                                 XFORWARD_DOM_LOCAL : XFORWARD_DOM_REMOTE))
+            || smtpd_proxy_xforward_flush(state, buf));
        vstring_free(buf);
        if (bad) {
-           detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
-           vstring_sprintf(state->proxy_buffer,
-                           "%d %s Error: %s",
-                           detail->smtp, detail->dsn, detail->text);
+           smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
            smtpd_proxy_close(state);
            return (-1);
        }
@@ -407,35 +448,237 @@ int     smtpd_proxy_open(SMTPD_STATE *state, const char *service,
      * have a problem because the proxy should always accept any MAIL FROM
      * command that was accepted by us.
      */
-    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "%s", mail_from) != 0) {
+    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "%s",
+                       proxy->mail_from) != 0) {
+       /* NOT: smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY); */
        smtpd_proxy_close(state);
        return (-1);
     }
     return (0);
 }
 
+/* smtpd_proxy_fake_server_reply - produce generic error response */
+
+static void smtpd_proxy_fake_server_reply(SMTPD_STATE *state, int status)
+{
+    const CLEANUP_STAT_DETAIL *detail;
+
+    /*
+     * Either we have no server reply (connection refused), or we have an
+     * out-of-protocol server reply, so we make up a generic server error
+     * response instead.
+     */
+    detail = cleanup_stat_detail(status);
+    vstring_sprintf(state->proxy->buffer,
+                   "%d %s Error: %s",
+                   detail->smtp, detail->dsn, detail->text);
+}
+
+/* smtpd_proxy_replay_rdwr_error - report replay log I/O error */
+
+static int smtpd_proxy_replay_rdwr_error(SMTPD_STATE *state)
+{
+
+    /*
+     * Log an appropriate warning message.
+     */
+    msg_warn("proxy speed-adjust log I/O error: %m");
+
+    /*
+     * Set the appropriate flags and server reply.
+     */
+    state->error_mask |= MAIL_ERROR_RESOURCE;
+    /* Update state->err in case we are past the client's DATA command. */
+    state->err |= CLEANUP_STAT_PROXY;
+    smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
+    return (-1);
+}
+
 /* smtpd_proxy_rdwr_error - report proxy communication error */
 
-static int smtpd_proxy_rdwr_error(VSTREAM *stream, int err)
+static int smtpd_proxy_rdwr_error(SMTPD_STATE *state, int err)
 {
+    const char *myname = "smtpd_proxy_rdwr_error";
+    SMTPD_PROXY *proxy = state->proxy;
+
+    /*
+     * Sanity check.
+     */
+    if (err != 0 && err != SMTP_ERR_NONE && proxy == 0)
+       msg_panic("%s: proxy error %d without proxy handle", myname, err);
+
+    /*
+     * Log an appropriate warning message.
+     */
     switch (err) {
-       case SMTP_ERR_EOF:
-       msg_warn("lost connection with proxy %s", VSTREAM_PATH(stream));
-       return (err);
+    case 0:
+    case SMTP_ERR_NONE:
+       break;
+    case SMTP_ERR_EOF:
+       msg_warn("lost connection with proxy %s", proxy->service_name);
+       break;
     case SMTP_ERR_TIME:
-       msg_warn("timeout talking to proxy %s", VSTREAM_PATH(stream));
-       return (err);
+       msg_warn("timeout talking to proxy %s", proxy->service_name);
+       break;
     default:
-       msg_panic("smtpd_proxy_rdwr_error: unknown proxy %s stream error %d",
-                 VSTREAM_PATH(stream), err);
+       msg_panic("%s: unknown proxy %s error %d",
+                 myname, proxy->service_name, err);
+    }
+
+    /*
+     * Set the appropriate flags and server reply.
+     */
+    state->error_mask |= MAIL_ERROR_SOFTWARE;
+    /* Update state->err in case we are past the client's DATA command. */
+    state->err |= CLEANUP_STAT_PROXY;
+    smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
+    return (-1);
+}
+
+/* smtpd_proxy_replay_send - replay saved SMTP session from speed-match log */
+
+static int smtpd_proxy_replay_send(SMTPD_STATE *state)
+{
+    const char *myname = "smtpd_proxy_replay_send";
+    static VSTRING *replay_buf = 0;
+    SMTPD_PROXY *proxy = state->proxy;
+    int     rec_type;
+    int     expect = SMTPD_PROX_WANT_BAD;
+
+    /*
+     * Sanity check.
+     */
+    if (smtpd_proxy_replay_stream == 0)
+       msg_panic("%s: no before-queue filter speed-adjust log", myname);
+
+    /*
+     * Errors first.
+     */
+    if (vstream_ferror(smtpd_proxy_replay_stream)
+       || vstream_feof(smtpd_proxy_replay_stream)
+       || vstream_fflush(smtpd_proxy_replay_stream))
+       /* NOT: fsync(vstream_fileno(smtpd_proxy_replay_stream)) */
+       return (smtpd_proxy_replay_rdwr_error(state));
+
+    /*
+     * Delayed connection to the before-queue filter.
+     */
+    if (smtpd_proxy_connect(state) < 0)
+       return (-1);
+
+    /*
+     * Replay the speed-match log. We do sanity check record content, but we
+     * don't implement a protocol state engine here, since we are reading
+     * from a file that we just wrote ourselves.
+     */
+    if (replay_buf == 0)
+       replay_buf = vstring_alloc(100);
+    if (vstream_fseek(smtpd_proxy_replay_stream, (off_t) 0, SEEK_SET) < 0)
+       return (smtpd_proxy_replay_rdwr_error(state));
+
+    for (;;) {
+       switch (rec_type = rec_get(smtpd_proxy_replay_stream, replay_buf,
+                                  REC_FLAG_NONE)) {
+
+           /*
+            * Message content.
+            */
+       case REC_TYPE_NORM:
+       case REC_TYPE_CONT:
+           if (smtpd_proxy_rec_put(proxy->service_stream, rec_type,
+                                   STR(replay_buf), LEN(replay_buf)) < 0)
+               return (-1);
+           break;
+
+           /*
+            * Expected server reply type.
+            */
+       case REC_TYPE_RCPT:
+           if (!alldig(STR(replay_buf))
+               || (expect = atoi(STR(replay_buf))) == SMTPD_PROX_WANT_BAD)
+               msg_panic("%s: malformed server reply type: %s",
+                         myname, STR(replay_buf));
+           break;
+
+           /*
+            * Client command, or void. Bail out on the first negative proxy
+            * response. This is OK, because the filter must use the same
+            * reply code for all recipients of a multi-recipient message.
+            */
+       case REC_TYPE_FROM:
+           if (expect == SMTPD_PROX_WANT_BAD)
+               msg_panic("%s: missing server reply type", myname);
+           if (smtpd_proxy_cmd(state, expect, *STR(replay_buf) ? "%s" :
+                               SMTPD_PROXY_CONN_FMT, STR(replay_buf)) < 0)
+               return (-1);
+           expect = SMTPD_PROX_WANT_BAD;
+           break;
+
+           /*
+            * End of replay log.
+            */
+       case REC_TYPE_EOF:
+           return (0);
+
+           /*
+            * Errors.
+            */
+       case REC_TYPE_ERROR:
+           return (smtpd_proxy_replay_rdwr_error(state));
+       default:
+           msg_panic("%s: unexpected record type; %d", myname, rec_type);
+       }
     }
 }
 
-/* smtpd_proxy_cmd_error - report unexpected proxy reply */
+/* smtpd_proxy_save_cmd - save SMTP command + expected response to replay log */
 
-static void smtpd_proxy_cmd_error(SMTPD_STATE *state, const char *fmt,
-                                         va_list ap)
+static int smtpd_proxy_save_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
 {
+    va_list ap;
+
+    /*
+     * Errors first.
+     */
+    if (vstream_ferror(smtpd_proxy_replay_stream)
+       || vstream_feof(smtpd_proxy_replay_stream))
+       return (smtpd_proxy_replay_rdwr_error(state));
+
+    /*
+     * Save the expected reply first, so that the replayer can safely
+     * overwrite the input buffer with the command.
+     */
+    rec_fprintf(smtpd_proxy_replay_stream, REC_TYPE_RCPT, "%d", expect);
+
+    /*
+     * The command can be omitted at the start of an SMTP session. This is
+     * not documented as part of the official interface because it is used
+     * only internally to this module. Use an explicit null string in case
+     * the SMTPD_PROXY_CONN_FMT implementation details change.
+     */
+    if (fmt == SMTPD_PROXY_CONN_FMT)
+       fmt = "";
+
+    /*
+     * Save the command to the replay log, and send it to the before-queue
+     * filter after we have received the entire message.
+     */
+    va_start(ap, fmt);
+    rec_vfprintf(smtpd_proxy_replay_stream, REC_TYPE_FROM, fmt, ap);
+    va_end(ap);
+
+    /*
+     * If we just saved the "." command, replay the log.
+     */
+    return (strcmp(fmt, ".") ? 0 : smtpd_proxy_replay_send(state));
+}
+
+/* smtpd_proxy_cmd_warn - report unexpected proxy reply */
+
+static void smtpd_proxy_cmd_warn(SMTPD_STATE *state, const char *fmt,
+                                        va_list ap)
+{
+    SMTPD_PROXY *proxy = state->proxy;
     VSTRING *buf;
 
     /*
@@ -444,38 +687,31 @@ static void smtpd_proxy_cmd_error(SMTPD_STATE *state, const char *fmt,
      * because it is used only internally to this module.
      */
     buf = vstring_alloc(100);
-    vstring_vsprintf(buf, fmt == SMTPD_PROXY_CONNECT ?
+    vstring_vsprintf(buf, fmt == SMTPD_PROXY_CONN_FMT ?
                     "connection request" : fmt, ap);
-    msg_warn("proxy %s rejected \"%s\": \"%s\"", VSTREAM_PATH(state->proxy),
-            STR(buf), STR(state->proxy_buffer));
+    msg_warn("proxy %s rejected \"%s\": \"%s\"",
+            proxy->service_name, STR(buf), STR(proxy->buffer));
     vstring_free(buf);
 }
 
 /* smtpd_proxy_cmd - send command to proxy, receive reply */
 
-int     smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
+static int smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
 {
+    SMTPD_PROXY *proxy = state->proxy;
     va_list ap;
     char   *cp;
     int     last_char;
     int     err = 0;
     static VSTRING *buffer = 0;
-    const CLEANUP_STAT_DETAIL *detail;
 
     /*
      * Errors first. Be prepared for delayed errors from the DATA phase.
      */
-    if (vstream_ferror(state->proxy)
-       || vstream_feof(state->proxy)
-       || ((err = vstream_setjmp(state->proxy)) != 0
-           && smtpd_proxy_rdwr_error(state->proxy, err))) {
-       state->error_mask |= MAIL_ERROR_SOFTWARE;
-       state->err |= CLEANUP_STAT_PROXY;
-       detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
-       vstring_sprintf(state->proxy_buffer,
-                       "%d %s Error: %s",
-                       detail->smtp, detail->dsn, detail->text);
-       return (-1);
+    if (vstream_ferror(proxy->service_stream)
+       || vstream_feof(proxy->service_stream)
+       || (err = vstream_setjmp(proxy->service_stream)) != 0) {
+       return (smtpd_proxy_rdwr_error(state, err));
     }
 
     /*
@@ -483,13 +719,13 @@ int     smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
      * not documented as part of the official interface because it is used
      * only internally to this module.
      */
-    if (fmt != SMTPD_PROXY_CONNECT) {
+    if (fmt != SMTPD_PROXY_CONN_FMT) {
 
        /*
         * Format the command.
         */
        va_start(ap, fmt);
-       vstring_vsprintf(state->proxy_buffer, fmt, ap);
+       vstring_vsprintf(proxy->buffer, fmt, ap);
        va_end(ap);
 
        /*
@@ -497,20 +733,19 @@ int     smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
         * what the program is trying to do.
         */
        if (msg_verbose)
-           msg_info("> %s: %s", VSTREAM_PATH(state->proxy),
-                    STR(state->proxy_buffer));
+           msg_info("> %s: %s", proxy->service_name, STR(proxy->buffer));
 
        /*
         * Send the command to the proxy server. Since we're going to read a
         * reply immediately, there is no need to flush buffers.
         */
-       smtp_fputs(STR(state->proxy_buffer), LEN(state->proxy_buffer),
-                  state->proxy);
+       smtp_fputs(STR(proxy->buffer), LEN(proxy->buffer),
+                  proxy->service_stream);
     }
 
     /*
      * Early return if we don't want to wait for a server reply (such as
-     * after sending QUIT.
+     * after sending QUIT).
      */
     if (expect == SMTPD_PROX_WANT_NONE)
        return (0);
@@ -519,28 +754,27 @@ int     smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
      * Censor out non-printable characters in server responses and save
      * complete multi-line responses if possible.
      */
-    VSTRING_RESET(state->proxy_buffer);
+    VSTRING_RESET(proxy->buffer);
     if (buffer == 0)
        buffer = vstring_alloc(10);
     for (;;) {
-       last_char = smtp_get(buffer, state->proxy, var_line_limit);
+       last_char = smtp_get(buffer, proxy->service_stream, var_line_limit);
        printable(STR(buffer), '?');
        if (last_char != '\n')
            msg_warn("%s: response longer than %d: %.30s...",
-                    VSTREAM_PATH(state->proxy), var_line_limit,
+                    proxy->service_name, var_line_limit,
                     STR(buffer));
        if (msg_verbose)
-           msg_info("< %s: %.100s", VSTREAM_PATH(state->proxy),
-                    STR(buffer));
+           msg_info("< %s: %.100s", proxy->service_name, STR(buffer));
 
        /*
         * Defend against a denial of service attack by limiting the amount
         * of multi-line text that we are willing to store.
         */
-       if (LEN(state->proxy_buffer) < var_line_limit) {
-           if (VSTRING_LEN(state->proxy_buffer))
-               VSTRING_ADDCH(state->proxy_buffer, '\n');
-           vstring_strcat(state->proxy_buffer, STR(buffer));
+       if (LEN(proxy->buffer) < var_line_limit) {
+           if (VSTRING_LEN(proxy->buffer))
+               VSTRING_ADDCH(proxy->buffer, '\n');
+           vstring_strcat(proxy->buffer, STR(buffer));
        }
 
        /*
@@ -558,28 +792,24 @@ int     smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
                break;
        }
        msg_warn("received garbage from proxy %s: %.100s",
-                VSTREAM_PATH(state->proxy), STR(buffer));
+                proxy->service_name, STR(buffer));
     }
 
     /*
      * Log a warning in case the proxy does not send the expected response.
      * Silently accept any response when the client expressed no expectation.
      * 
-     * Don't pass through misleading 2xx replies. It confuses naive users and
-     * SMTP clients, and creates support problems.
+     * Starting with Postfix 2.6 we don't pass through unexpected 2xx or 3xx
+     * proxy replies. They are a source of support problems, so we replace
+     * them by generic server error replies.
      */
-    if (expect != SMTPD_PROX_WANT_ANY && expect != *STR(state->proxy_buffer)) {
+    if (expect != SMTPD_PROX_WANT_ANY && expect != *STR(proxy->buffer)) {
        va_start(ap, fmt);
-       smtpd_proxy_cmd_error(state, fmt, ap);
+       smtpd_proxy_cmd_warn(state, fmt, ap);
        va_end(ap);
-       if (*STR(state->proxy_buffer) == SMTPD_PROX_WANT_OK
-           || *STR(state->proxy_buffer) == SMTPD_PROX_WANT_MORE) {
-           state->error_mask |= MAIL_ERROR_SOFTWARE;
-           state->err |= CLEANUP_STAT_PROXY;
-           detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
-           vstring_sprintf(state->proxy_buffer,
-                           "%d %s Error: %s",
-                           detail->smtp, detail->dsn, detail->text);
+       if (*STR(proxy->buffer) == SMTPD_PROX_WANT_OK
+           || *STR(proxy->buffer) == SMTPD_PROX_WANT_MORE) {
+           smtpd_proxy_rdwr_error(state, 0);
        }
        return (-1);
     } else {
@@ -587,20 +817,56 @@ int     smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
     }
 }
 
+/* smtpd_proxy_save_rec_put - save message content to replay log */
+
+static int smtpd_proxy_save_rec_put(VSTREAM *stream, int rec_type,
+                                           const char *data, ssize_t len)
+{
+    const char *myname = "smtpd_proxy_save_rec_put";
+    int     ret;
+
+#define VSTREAM_TO_SMTPD_STATE(s) ((SMTPD_STATE *) vstream_context(s))
+
+    /*
+     * Sanity check.
+     */
+    if (stream == 0)
+       msg_panic("%s: attempt to use closed stream", myname);
+
+    /*
+     * Send one content record. Errors and results must be as with rec_put().
+     */
+    if (rec_type == REC_TYPE_NORM || rec_type == REC_TYPE_CONT)
+       ret = rec_put(stream, rec_type, data, len);
+    else
+       msg_panic("%s: need REC_TYPE_NORM or REC_TYPE_CONT", myname);
+
+    /*
+     * Errors last.
+     */
+    if (ret != rec_type) {
+       (void) smtpd_proxy_replay_rdwr_error(VSTREAM_TO_SMTPD_STATE(stream));
+       return (REC_TYPE_ERROR);
+    }
+    return (rec_type);
+}
+
 /* smtpd_proxy_rec_put - send message content, rec_put() clone */
 
-int     smtpd_proxy_rec_put(VSTREAM *stream, int rec_type,
-                                   const char *data, ssize_t len)
+static int smtpd_proxy_rec_put(VSTREAM *stream, int rec_type,
+                                      const char *data, ssize_t len)
 {
-    int     err;
+    const char *myname = "smtpd_proxy_rec_put";
+    int     err = 0;
 
     /*
      * Errors first.
      */
-    if (vstream_ferror(stream) || vstream_feof(stream))
+    if (vstream_ferror(stream) || vstream_feof(stream)
+       || (err = vstream_setjmp(stream)) != 0) {
+       (void) smtpd_proxy_rdwr_error(VSTREAM_TO_SMTPD_STATE(stream), err);
        return (REC_TYPE_ERROR);
-    if ((err = vstream_setjmp(stream)) != 0)
-       return (smtpd_proxy_rdwr_error(stream, err), REC_TYPE_ERROR);
+    }
 
     /*
      * Send one content record. Errors and results must be as with rec_put().
@@ -610,25 +876,63 @@ int     smtpd_proxy_rec_put(VSTREAM *stream, int rec_type,
     else if (rec_type == REC_TYPE_CONT)
        smtp_fwrite(data, len, stream);
     else
-       msg_panic("smtpd_proxy_rec_put: need REC_TYPE_NORM or REC_TYPE_CONT");
+       msg_panic("%s: need REC_TYPE_NORM or REC_TYPE_CONT", myname);
+    return (rec_type);
+}
+
+/* smtpd_proxy_save_rec_fprintf - save message content to replay log */
+
+static int smtpd_proxy_save_rec_fprintf(VSTREAM *stream, int rec_type,
+                                               const char *fmt,...)
+{
+    const char *myname = "smtpd_proxy_save_rec_fprintf";
+    va_list ap;
+    int     ret;
+
+    /*
+     * Sanity check.
+     */
+    if (stream == 0)
+       msg_panic("%s: attempt to use closed stream", myname);
+
+    /*
+     * Save one content record. Errors and results must be as with
+     * rec_fprintf().
+     */
+    va_start(ap, fmt);
+    if (rec_type == REC_TYPE_NORM)
+       ret = rec_vfprintf(stream, rec_type, fmt, ap);
+    else
+       msg_panic("%s: need REC_TYPE_NORM", myname);
+    va_end(ap);
+
+    /*
+     * Errors last.
+     */
+    if (ret != rec_type) {
+       (void) smtpd_proxy_replay_rdwr_error(VSTREAM_TO_SMTPD_STATE(stream));
+       return (REC_TYPE_ERROR);
+    }
     return (rec_type);
 }
 
 /* smtpd_proxy_rec_fprintf - send message content, rec_fprintf() clone */
 
-int     smtpd_proxy_rec_fprintf(VSTREAM *stream, int rec_type,
-                                       const char *fmt,...)
+static int smtpd_proxy_rec_fprintf(VSTREAM *stream, int rec_type,
+                                          const char *fmt,...)
 {
+    const char *myname = "smtpd_proxy_rec_fprintf";
     va_list ap;
-    int     err;
+    int     err = 0;
 
     /*
      * Errors first.
      */
-    if (vstream_ferror(stream) || vstream_feof(stream))
+    if (vstream_ferror(stream) || vstream_feof(stream)
+       || (err = vstream_setjmp(stream)) != 0) {
+       (void) smtpd_proxy_rdwr_error(VSTREAM_TO_SMTPD_STATE(stream), err);
        return (REC_TYPE_ERROR);
-    if ((err = vstream_setjmp(stream)) != 0)
-       return (smtpd_proxy_rdwr_error(stream, err), REC_TYPE_ERROR);
+    }
 
     /*
      * Send one content record. Errors and results must be as with
@@ -638,15 +942,226 @@ int     smtpd_proxy_rec_fprintf(VSTREAM *stream, int rec_type,
     if (rec_type == REC_TYPE_NORM)
        smtp_vprintf(stream, fmt, ap);
     else
-       msg_panic("smtpd_proxy_rec_fprintf: need REC_TYPE_NORM");
+       msg_panic("%s: need REC_TYPE_NORM", myname);
     va_end(ap);
     return (rec_type);
 }
 
-/* smtpd_proxy_close - close proxy connection */
+#ifndef NO_TRUNCATE
+
+/* smtpd_proxy_replay_setup - prepare the replay logfile */
+
+static int smtpd_proxy_replay_setup(SMTPD_STATE *state)
+{
+    const char *myname = "smtpd_proxy_replay_setup";
+    struct stat st;
+
+    /*
+     * Where possible reuse an existing replay logfile, because creating a
+     * file is expensive compared to reading or writing. For security reasons
+     * we must truncate the file before reuse. For performance reasons we
+     * should truncate the file immediately after the end of a mail
+     * transaction. We enforce the security guarantee here by requiring that
+     * the file is emtpy when it is reused. This is less expensive than
+     * truncating the file redundantly.
+     */
+    if (smtpd_proxy_replay_stream != 0) {
+       if (vstream_fseek(smtpd_proxy_replay_stream, (off_t) 0, SEEK_SET) < 0) {
+           msg_warn("seek before-queue filter speed-adjust log: %m");
+           (void) vstream_fclose(smtpd_proxy_replay_stream);
+           smtpd_proxy_replay_stream = 0;
+       } else if (fstat(vstream_fileno(smtpd_proxy_replay_stream), &st) < 0) {
+           msg_warn("fstat before-queue filter speed-adjust log: %m");
+           (void) vstream_fclose(smtpd_proxy_replay_stream);
+           smtpd_proxy_replay_stream = 0;
+       } else {
+           if (st.st_size > 0)
+               msg_panic("%s: non-empty before-queue filter speed-adjust log",
+                         myname);
+           vstream_clearerr(smtpd_proxy_replay_stream);
+           if (msg_verbose)
+               msg_info("%s: reuse speed-adjust stream fd=%d", myname,
+                        vstream_fileno(smtpd_proxy_replay_stream));
+           /* Here, smtpd_proxy_replay_stream != 0 */
+       }
+    }
+
+    /*
+     * Create a new replay logfile.
+     */
+    if (smtpd_proxy_replay_stream == 0) {
+       smtpd_proxy_replay_stream = mail_queue_enter(MAIL_QUEUE_INCOMING, 0,
+                                                    (struct timeval *) 0);
+       if (smtpd_proxy_replay_stream == 0)
+           return (smtpd_proxy_replay_rdwr_error(state));
+       if (unlink(VSTREAM_PATH(smtpd_proxy_replay_stream)) < 0)
+           msg_warn("remove before-queue filter speed-adjust log %s: %m",
+                    VSTREAM_PATH(smtpd_proxy_replay_stream));
+       if (msg_verbose)
+           msg_info("%s: new speed-adjust stream fd=%d", myname,
+                    vstream_fileno(smtpd_proxy_replay_stream));
+    }
+
+    /*
+     * Needed by our DATA-phase record emulation routines.
+     */
+    vstream_control(smtpd_proxy_replay_stream, VSTREAM_CTL_CONTEXT,
+                   (char *) state, VSTREAM_CTL_END);
+    return (0);
+}
+
+#endif
+
+/* smtpd_proxy_create - set up smtpd proxy handle */
+
+int     smtpd_proxy_create(SMTPD_STATE *state, int flags, const char *service,
+                                  int timeout, const char *ehlo_name,
+                                  const char *mail_from)
+{
+    SMTPD_PROXY *proxy;
+
+    /*
+     * When an operation has many arguments it is safer to use named
+     * parameters, and have the compiler enforce the argument count.
+     */
+#define SMTPD_PROXY_ALLOC(p, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) \
+       ((p) = (SMTPD_PROXY *) mymalloc(sizeof(*(p))), (p)->a1, (p)->a2, \
+        (p)->a3, (p)->a4, (p)->a5, (p)->a6, (p)->a7, (p)->a8, (p)->a9, \
+        (p)->a10, (p)->a11, (p))
+
+    /*
+     * Connect to the before-queue filter immediately.
+     */
+    if ((flags & SMTPD_PROXY_FLAG_SPEED_ADJUST) == 0) {
+       state->proxy =
+           SMTPD_PROXY_ALLOC(proxy, stream = 0, buffer = vstring_alloc(10),
+                             cmd = smtpd_proxy_cmd,
+                             rec_fprintf = smtpd_proxy_rec_fprintf,
+                             rec_put = smtpd_proxy_rec_put,
+                             flags = flags, service_stream = 0,
+                             service_name = service, timeout = timeout,
+                             ehlo_name = ehlo_name, mail_from = mail_from);
+       if (smtpd_proxy_connect(state) < 0) {
+           /* NOT: smtpd_proxy_free(state); we still need proxy->buffer. */
+           return (-1);
+       }
+       proxy->stream = proxy->service_stream;
+       return (0);
+    }
+
+    /*
+     * Connect to the before-queue filter after we receive the entire
+     * message. Open the replay logfile early to simplify code. The file is
+     * reused for multiple mail transactions, so there is no need to minimize
+     * its life time.
+     */
+    else {
+#ifdef NO_TRUNCATE
+       msg_panic("smtpd_proxy_create: speed-adjust support is not available");
+#else
+       if (smtpd_proxy_replay_setup(state) < 0)
+           return (-1);
+       state->proxy =
+           SMTPD_PROXY_ALLOC(proxy, stream = smtpd_proxy_replay_stream,
+                             buffer = vstring_alloc(10),
+                             cmd = smtpd_proxy_save_cmd,
+                             rec_fprintf = smtpd_proxy_save_rec_fprintf,
+                             rec_put = smtpd_proxy_save_rec_put,
+                             flags = flags, service_stream = 0,
+                             service_name = service, timeout = timeout,
+                             ehlo_name = ehlo_name, mail_from = mail_from);
+       return (0);
+#endif
+    }
+}
+
+/* smtpd_proxy_close - close proxy connection without destroying handle */
 
 void    smtpd_proxy_close(SMTPD_STATE *state)
 {
-    (void) vstream_fclose(state->proxy);
+    SMTPD_PROXY *proxy = state->proxy;
+
+    /*
+     * XXX We can't send QUIT if the stream is still good, because that would
+     * overwrite the last server reply in proxy->buffer. We probably should
+     * just bite the bullet and allocate separate buffers for sending and
+     * receiving.
+     */
+    if (proxy->service_stream != 0) {
+#if 0
+       if (vstream_feof(proxy->service_stream) == 0
+           && vstream_ferror(proxy->service_stream) == 0)
+           (void) smtpd_proxy_cmd(state, SMTPD_PROX_WANT_NONE,
+                                  SMTPD_CMD_QUIT);
+#endif
+       (void) vstream_fclose(proxy->service_stream);
+       if (proxy->stream == proxy->service_stream)
+           proxy->stream = 0;
+       proxy->service_stream = 0;
+    }
+}
+
+/* smtpd_proxy_free - destroy smtpd proxy handle */
+
+void    smtpd_proxy_free(SMTPD_STATE *state)
+{
+    SMTPD_PROXY *proxy = state->proxy;
+
+    /*
+     * Clean up.
+     */
+    if (proxy->service_stream != 0)
+       (void) smtpd_proxy_close(state);
+    if (proxy->buffer != 0)
+       vstring_free(proxy->buffer);
+    myfree((char *) proxy);
     state->proxy = 0;
+
+    /*
+     * Reuse the replay logfile if possible. For security reasons we must
+     * truncate the replay logfile before reuse. For performance reasons we
+     * should truncate the replay logfile immediately after the end of a mail
+     * transaction. We truncate the file here, and enforce the security
+     * guarantee by requiring that the file is empty when it is reused.
+     */
+    if (smtpd_proxy_replay_stream == 0)
+       return;
+    if (vstream_ferror(smtpd_proxy_replay_stream)) {
+       (void) vstream_fclose(smtpd_proxy_replay_stream);
+       smtpd_proxy_replay_stream = 0;
+       return;
+    }
+    if (ftruncate(vstream_fileno(smtpd_proxy_replay_stream), (off_t) 0) < 0) {
+       msg_warn("truncate before-queue filter speed-adjust log: %m");
+       (void) vstream_fclose(smtpd_proxy_replay_stream);
+       smtpd_proxy_replay_stream = 0;
+       return;
+    }
+}
+
+/* smtpd_proxy_parse_opts - parse main.cf options */
+
+int     smtpd_proxy_parse_opts(const char *param_name, const char *param_val)
+{
+    static const NAME_MASK proxy_opts_table[] = {
+       SMTPD_PROXY_NAME_SPEED_ADJUST, SMTPD_PROXY_FLAG_SPEED_ADJUST,
+       0, 0,
+    };
+    int     flags;
+
+    /*
+     * The optional before-filter speed-adjust buffers use disk space.
+     * However, we don't know if they compete for storage space with the
+     * after-filter queue, so we can't simply bump up the free space
+     * requirement to 2.5 * message_size_limit.
+     */
+    flags = name_mask(param_name, proxy_opts_table, param_val);
+    if (flags & SMTPD_PROXY_FLAG_SPEED_ADJUST) {
+#ifdef NO_TRUNCATE
+       msg_warn("smtpd_proxy %s support is not available",
+                SMTPD_PROXY_NAME_SPEED_ADJUST);
+       flags &= ~SMTPD_PROXY_FLAG_SPEED_ADJUST;
+#endif
+    }
+    return (flags);
 }
index 11f59ff97c9735a36671a968c5901ecaf624f75b..a0a7252ff4b69632470c974006c81585fca46b23 100644 (file)
  /*
   * Application-specific.
   */
+typedef int PRINTFPTRLIKE(3, 4) (*SMTPD_PROXY_CMD_FN) (SMTPD_STATE *, int, const char *,...);
+typedef int PRINTFPTRLIKE(3, 4) (*SMTPD_PROXY_REC_FPRINTF_FN) (VSTREAM *, int, const char *,...);
+typedef int (*SMTPD_PROXY_REC_PUT_FN) (VSTREAM *, int, const char *, ssize_t);
+
+typedef struct SMTPD_PROXY {
+    /* Public. */
+    VSTREAM *stream;
+    VSTRING *buffer;                   /* proxy query/reply buffer */
+    SMTPD_PROXY_CMD_FN cmd;
+    SMTPD_PROXY_REC_FPRINTF_FN rec_fprintf;
+    SMTPD_PROXY_REC_PUT_FN rec_put;
+    /* Private. */
+    int     flags;
+    VSTREAM *service_stream;
+    const char *service_name;
+    int     timeout;
+    const char *ehlo_name;
+    const char *mail_from;
+} SMTPD_PROXY;
+
+#define SMTPD_PROXY_FLAG_SPEED_ADJUST  (1<<0)
+
+#define SMTPD_PROXY_NAME_SPEED_ADJUST  "speed_adjust"
+
+#define SMTPD_PROX_WANT_BAD    0xff    /* Do not use */
 #define SMTPD_PROX_WANT_NONE   '\0'    /* Do not receive reply */
 #define SMTPD_PROX_WANT_ANY    '0'     /* Expect any reply */
 #define SMTPD_PROX_WANT_OK     '2'     /* Expect 2XX reply */
 #define SMTPD_PROX_WANT_MORE   '3'     /* Expect 3XX reply */
 
-extern int smtpd_proxy_open(SMTPD_STATE *, const char *, int, const char *, const char *);
-extern int PRINTFLIKE(3, 4) smtpd_proxy_cmd(SMTPD_STATE *, int, const char *,...);
-extern int smtpd_proxy_rec_put(VSTREAM *, int, const char *, ssize_t);
-extern int PRINTFLIKE(3, 4) smtpd_proxy_rec_fprintf(VSTREAM *, int, const char *,...);
+extern int smtpd_proxy_create(SMTPD_STATE *, int, const char *, int, const char *, const char *);
 extern void smtpd_proxy_close(SMTPD_STATE *);
+extern void smtpd_proxy_free(SMTPD_STATE *);
+extern int smtpd_proxy_parse_opts(const char *, const char *);
 
 /* LICENSE
 /* .ad
index 5ecf2d1f2b8d36aceab60661192aa7d0856322c6..24091fa610c4f1a634d2e615b16fe65931d2b242 100644 (file)
@@ -118,9 +118,7 @@ void    smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream,
     state->expand_buf = 0;
     state->prepend = 0;
     state->proxy = 0;
-    state->proxy_buffer = 0;
     state->proxy_mail = 0;
-    state->proxy_xforward_features = 0;
     state->saved_filter = 0;
     state->saved_redirect = 0;
     state->saved_bcc = 0;
@@ -207,8 +205,6 @@ void    smtpd_state_reset(SMTPD_STATE *state)
        vstring_free(state->defer_if_reject.reason);
     if (state->expand_buf)
        vstring_free(state->expand_buf);
-    if (state->proxy_buffer)
-       vstring_free(state->proxy_buffer);
     if (state->instance)
        vstring_free(state->instance);
     if (state->dsn_buf)
index 4ae89f96980e1e0387820e5d44c38ab977bf70a4..053d3779a996eaeb6d9bff4f86811410c42ccb4f 100644 (file)
@@ -51,7 +51,7 @@
 
 #include <smtpd.h>
 
-/* smtpd_xforward_init - initialize XCLIENT attributes */
+/* smtpd_xforward_init - initialize xforward attributes */
 
 void    smtpd_xforward_init(SMTPD_STATE *state)
 {
@@ -95,7 +95,7 @@ void    smtpd_xforward_preset(SMTPD_STATE *state)
     /* Leave domain context at zero. */
 }
 
-/* smtpd_xforward_reset - reset XCLIENT attributes */
+/* smtpd_xforward_reset - reset xforward attributes */
 
 void    smtpd_xforward_reset(SMTPD_STATE *state)
 {
index a39e19a0ffafa7987e41e99a2f1fe53bb552cf41..5699f8ceda5b37e52718308490e9e9fcf5ef103c 100644 (file)
@@ -54,7 +54,7 @@
 /*
 /*     The TLS layer to network interface is realized with a BIO pair:
 /*
-/*      Postfix             |   TLS layer
+/*     Postfix SMTP layer   |   TLS layer
 /*                          |
 /*     smtp/smtpd           |
 /*      /\    ||            |
@@ -64,7 +64,7 @@
 /*                          |     ||    \/
 /*                          |   BIO pair (internal_bio)
 /*                          |   BIO pair (network_bio)
-/*                          |     /\    ||
+/*     Postfix socket layer |     /\    ||
 /*                          |     ||    \/
 /*     socket read/write  <===> BIO read/write
 /*      /\    ||            |
index 0d25348b50bc5bf98eaac1fc3a1f9daf246bfc8e..53b808a892cd2eaacf27c74019496b2e8f22fd81 100644 (file)
@@ -1484,11 +1484,24 @@ typedef int pid_t;
 #endif
 
 #ifndef SCANFLIKE
-#if (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) || __GNUC__ == 3
+#if (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) || __GNUC__ >= 3
 #define SCANFLIKE(x,y) __attribute__ ((format (scanf, (x), (y))))
 #else
 #define SCANFLIKE(x,y)
 #endif
+#endif
+
+ /*
+  * Some gcc implementations don't grok these attributes with pointer to
+  * function. Again, wild guess of what is supported. To override, specify
+  * #define PRINTPTRFLIKE  in the system-dependent sections above.
+  */
+#ifndef PRINTFPTRLIKE
+#if (__GNUC__ >= 3)                    /* XXX Rough estimate */
+#define PRINTFPTRLIKE(x,y) PRINTFLIKE(x,y)
+#else
+#define PRINTFPTRLIKE(x,y)
+#endif
 #endif
 
  /*