]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.5-20071111
authorWietse Venema <wietse@porcupine.org>
Sun, 11 Nov 2007 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:33:24 +0000 (06:33 +0000)
60 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/AAAREADME
postfix/README_FILES/BACKSCATTER_README
postfix/README_FILES/MILTER_README
postfix/README_FILES/STRESS_README [new file with mode: 0644]
postfix/RELEASE_NOTES
postfix/conf/header_checks
postfix/conf/master.cf
postfix/conf/postfix-files
postfix/html/BACKSCATTER_README.html
postfix/html/CDB_README.html
postfix/html/DATABASE_README.html
postfix/html/MILTER_README.html
postfix/html/STRESS_README.html [new file with mode: 0644]
postfix/html/header_checks.5.html
postfix/html/index.html
postfix/html/lmtp.8.html
postfix/html/local.8.html
postfix/html/pipe.8.html
postfix/html/postconf.5.html
postfix/html/smtp.8.html
postfix/man/man5/header_checks.5
postfix/man/man5/postconf.5
postfix/man/man8/local.8
postfix/man/man8/pipe.8
postfix/man/man8/smtp.8
postfix/mantools/postlink
postfix/proto/BACKSCATTER_README.html
postfix/proto/MILTER_README.html
postfix/proto/Makefile.in
postfix/proto/STRESS_README.html [new file with mode: 0644]
postfix/proto/header_checks
postfix/proto/postconf.proto
postfix/src/global/Makefile.in
postfix/src/global/header_body_checks.c [new file with mode: 0644]
postfix/src/global/header_body_checks.h [new file with mode: 0644]
postfix/src/global/header_body_checks_ignore.ref [new file with mode: 0644]
postfix/src/global/header_body_checks_null.ref [new file with mode: 0644]
postfix/src/global/header_body_checks_prepend.ref [new file with mode: 0644]
postfix/src/global/header_body_checks_replace.ref [new file with mode: 0644]
postfix/src/global/header_body_checks_warn.ref [new file with mode: 0644]
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/global/mime_state.c
postfix/src/global/mime_state.h
postfix/src/local/command.c
postfix/src/local/local.c
postfix/src/milter/milter8.c
postfix/src/pipe/pipe.c
postfix/src/smtp/Makefile.in
postfix/src/smtp/lmtp_params.c
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_params.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtpstone/smtp-sink.c
postfix/src/util/events.c
postfix/src/util/inet_addr_local.c
postfix/src/util/sys_defs.h

index 7fde1b246cc3a0cd626111a95c8935fb7ab8652a..96fd9979929befc4080e796972e3e505dc12b442 100644 (file)
 -TEXPAND_ATTR
 -TFILE
 -TFORWARD_INFO
+-THBC_OUTPUT_CALL_BACKS
+-THBC_ACTION_CALL_BACKS
+-THBC_CHECKS
+-THBC_MAP_INFO
+-THBC_TEST_CONTEXT
 -THEADER_OPTS
 -THEADER_TOKEN
 -THOST
index 76ef2cb7fb42497c8d66e7a612dc21481da3db0a..59019d5b2aad9ab0c12cceec050ca05f55172296 100644 (file)
@@ -13708,10 +13708,11 @@ Apologies for any names omitted.
 
 20070911
 
-       Bugfix (introduced Postfix 2.2.11): certificate with
-       unparsable canonical name caused the SMTPD policy client
-       to allocate zero-length memory, triggering an assertion
-       that it shouldn't do such things.  File: smtpd/smtpd_check.c.
+       Bugfix (introduced Postfix 2.2.11): TLS client certificate
+       with unparsable canonical name caused the SMTP server's
+       policy client to allocate zero-length memory, triggering
+       an assertion that it shouldn't do such things.  File:
+       smtpd/smtpd_check.c.
 
 20070912
 
@@ -13771,3 +13772,44 @@ Apologies for any names omitted.
        Cleanup: send client port information "0" instead of "unknown"
        to Milter applications. Files: smtpd/smtpd.c, smtpd/smtpd_milter.c,
        cleanup/cleanup_milter.c.
+
+20071025
+
+       Portability: on Linux we no longer need /proc to find out
+       local IPv6 interface address information. LaMont Jones.
+       Files: util/sys_defs.h.
+
+20071030
+
+       Bugfix: Postfix mistakenly enforced the 64kbyte limit (for
+       sending body parts TO Milter applications) also while
+       receiving packets FROM Milter applications.  The limit is
+       now at least 1GB.  File: milter/milter8.c.
+
+20071105
+
+       Feature: ORIGINAL_RECIPIENT environment variable.  Corey
+       Hickey. File: local/local.c.
+
+20071108-10
+
+       Feature: general-purpose header/body_checks library module,
+       first used in the SMTP client. Actions that change the
+       message delivery time or destination can be implemented
+       with a simple extension mechanism (they make sense only in
+       before-queue filters).  Configuration parameters:
+       smtp_header_checks, smtp_mime_header_checks,
+       smtp_nested_header_checks, smtp_body_checks. Unlike the
+       cleanup server, the mime and nested header checks don't by
+       default assume the header_checks value.  Files:
+       global/header_body_checks.[hc], smtp/smtp_proto.c,
+       smtp/smtp_session.c.
+
+20071110
+
+       Feature: ${original_recipient} command-line macro.  Corey
+       Hickey. File: pipe/pipe.c.
+
+       Bugfix (introduced: 20071004) missing exception handling
+       in smtp-sink per-command delay feature. Victor Duchovni.
+       File: smtpstone/smtp-sink.c.
index 44e307ca7c52ff12f4f60c3a3343bfb49612c005..fb5d0a1d0db2daebf4dde63d51126420ca20e8a8 100644 (file)
@@ -16,6 +16,7 @@ G\bGe\ben\bne\ber\bra\bal\bl c\bco\bon\bnf\bfi\big\bgu\bur\bra\bat\bti\bio\bon\bn
 P\bPr\bro\bob\bbl\ble\bem\bm s\bso\bol\blv\bvi\bin\bng\bg
 
   * QSHAPE_README: Bottleneck analysis
+  * STRESS_README: Stress-dependent configuration
   * TUNING_README: Performance tuning
   * DEBUG_README: Debugging strategies
 
index 90a16bfa2e51827e4d374a3a57926b62e5b1c2b3..5a7db47e378365c219802e2a9742c62317ba55a2 100644 (file)
@@ -4,11 +4,7 @@ P\bPo\bos\bst\btf\bfi\bix\bx B\bBa\bac\bck\bks\bsc\bca\bat\btt\bte\ber\br H\bHo\bow\bwt\bto\bo
 
 O\bOv\bve\ber\brv\bvi\bie\bew\bw
 
-This document describes features that require Postfix version 2.0 or later. The
-examples use Perl Compatible Regular Expressions (Postfix pcre: tables), but
-also provide a translation to POSIX regular expressions (Postfix regexp:
-tables). PCRE is preferred primarily because the implementation is often
-faster.
+This document describes features that require Postfix version 2.0 or later.
 
 Topics covered in this document:
 
@@ -21,6 +17,11 @@ Topics covered in this document:
       o Blocking backscatter mail with other forged information
       o Blocking backscatter mail from virus scanners
 
+The examples use Perl Compatible Regular Expressions (Postfix pcre: tables),
+but also provide a translation to POSIX regular expressions (Postfix regexp:
+tables). PCRE is preferred primarily because the implementation is often
+faster.
+
 W\bWh\bha\bat\bt i\bis\bs b\bba\bac\bck\bks\bsc\bca\bat\btt\bte\ber\br m\bma\bai\bil\bl?\b?
 
 When a spammer or worm sends mail with forged sender addresses, innocent sites
index 6f0ca58e39c57a95e7a9b8bad7eb5bb776ed2b51..39e50336f7dc4ed89016d482558dcd10f7ea1a03 100644 (file)
@@ -300,7 +300,7 @@ message).
     |milter_content_timeout|300s      |HEADER, EOH, BODY, EOM         |
     |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
 
-Beware: 30s is not a lot for applications that do a lot of DNS lookups.
+Beware: 30s may be too short for applications doing lots of DNS lookups.
 However, if you increase the above timeouts too much, remote SMTP clients may
 hang up and mail may be delivered multiple times. This is an inherent problem
 with before-queue filtering.
@@ -390,7 +390,7 @@ message).
 
 W\bWo\bor\brk\bka\bar\bro\bou\bun\bnd\bds\bs
 
-Content filters may break domain key etc. signatures. If you use an SMTP-based
+Content filters may break DKIM etc. signatures. If you use an SMTP-based
 content filter, then you should add a line to master.cf with "-
 o disable_mime_output_conversion=yes" (note: no spaces around the "="), as
 described in the advanced content filter example.
@@ -407,7 +407,7 @@ Milter applications make assumptions that aren't true in a Postfix environment.
 
         sid-filter[36540]: WARNING: sendmail symbol 'i' not available
 
-    And they may insert a message header with "unknown-msgid" like this:
+    And they may insert an ugly message header with "unknown-msgid" like this:
 
         X-SenderID: Sendmail Sender-ID Filter vx.y.z host.example.com <unknown-
         msgid>
diff --git a/postfix/README_FILES/STRESS_README b/postfix/README_FILES/STRESS_README
new file mode 100644 (file)
index 0000000..b66be02
--- /dev/null
@@ -0,0 +1,349 @@
+P\bPo\bos\bst\btf\bfi\bix\bx S\bSt\btr\bre\bes\bss\bs-\b-D\bDe\bep\bpe\ben\bnd\bde\ben\bnt\bt C\bCo\bon\bnf\bfi\big\bgu\bur\bra\bat\bti\bio\bon\bn
+
+-------------------------------------------------------------------------------
+
+O\bOv\bve\ber\brv\bvi\bie\bew\bw
+
+This document describes the symptoms of Postfix SMTP server overload, and how
+to avoid the condition under normal conditions. When the condition is caused by
+botnets or other malware, the document suggests configuration settings that
+help to minimize the impact on legitimate mail. Finally, the document
+introduces Postfix stress-adaptive behavior, and how it can be used to
+automatically switch configuration settings under overload.
+
+Topics covered in this document:
+
+  * Symptoms of Postfix SMTP server overload
+  * Service more SMTP clients at the same time
+  * Spend less time per SMTP client
+  * Disconnect suspicious SMTP clients
+  * Take desperate measures
+  * Make Postfix behavior stress-adaptive
+  * Detecting support for stress-adaptive behavior
+  * Forcing stress-adaptive behavior on or off
+  * Credits
+
+S\bSy\bym\bmp\bpt\bto\bom\bms\bs o\bof\bf P\bPo\bos\bst\btf\bfi\bix\bx S\bSM\bMT\bTP\bP s\bse\ber\brv\bve\ber\br o\bov\bve\ber\brl\blo\boa\bad\bd
+
+Under normal conditions, Postfix responds immediately when a remote SMTP client
+connects. The time needed to deliver mail to Postfix may depend on how busy the
+CPU or disk are, but that should be noticeable only with very large messages.
+Performance degrades more dramatically when the number of remote SMTP clients
+exceeds the number of Postfix SMTP server processes. When a client connects
+while all server processes are busy, the client must wait until a server
+process becomes available.
+
+Overload may be caused by a legitimate mail (example: a DNS registrar opens a
+new zone for registrations), by mistake (mail explosion caused by a forwarding
+loop) or by illegitimate mail (worm outbreak, botnet, or other malware
+activity). Symptoms of Postfix SMTP mail server overload are:
+
+  * Postfix logs a warning that all server ports are busy:
+
+    Oct  3 20:39:27 spike postfix/master[28905]: warning: service "smtp"
+     (25) has reached its process limit "30": new clients may experience
+     noticeable delays
+    Oct  3 20:39:27 spike postfix/master[28905]: warning: to avoid this
+     condition, increase the process count in master.cf or reduce the
+     service time per client
+
+  * Remote SMTP clients experience a long delay before Postfix sends the "220
+    hostname.example.com ESMTP Postfix" greeting. If this affects end-user mail
+    clients, enable the "submission" service entry in master.cf (present since
+    Postfix 2.1), and tell users to connect to this instead of the public SMTP
+    service.
+
+  * The Postfix SMTP server logs an increased number of "lost connection after
+    CONNECT" events. This happens because remote SMTP clients disconnect before
+    Postfix answers the connection.
+
+NOTE: The last two symptoms also happen without overload.
+
+  * Broken DNS also causes lengthy delays before "220 hostname.example.com ..."
+    while the Postfix SMTP server tries to look up the client's hostname.
+
+  * A portscan for open SMTP ports also results in "lost connection ..."
+    logfile messages.
+
+Legitimate mail that doesn't get through during an episode of overload is not
+necessarily lost. It should still arrive once the situation returns to normal,
+as long as the overload condition is temporary.
+
+S\bSe\ber\brv\bvi\bic\bce\be m\bmo\bor\bre\be S\bSM\bMT\bTP\bP c\bcl\bli\bie\ben\bnt\bts\bs a\bat\bt t\bth\bhe\be s\bsa\bam\bme\be t\bti\bim\bme\be
+
+To service more SMTP clients simultaneously, you need to increase the number of
+SMTP server processes. This will improve the responsiveness for remote SMTP
+clients, as long as the server machine has enough hardware and software
+resources to run the additional processes, and as long as the file system can
+keep up with the additional load.
+
+  * You increase the number of SMTP server processes either by increasing the
+    default_process_limit in main.cf (line 3 below), or by increasing the SMTP
+    server's "maxproc" field in master.cf (line 10 below). Either way, you need
+    to issue a "postfix reload" command to make the change effective.
+
+  * Process limits above 1000 require Postfix version 2.4 or later, and an
+    operating system that supports kernel-based event filters (BSD kqueue(2),
+    Linux epoll(4), or Solaris /dev/poll).
+
+  * You can reduce the Postfix memory footprint by using cdb: lookup tables
+    instead of Berkeley DB.
+
+     1 /etc/postfix/main.cf:
+     2     # Raise the global process limit, 100 since Postfix 2.0.
+     3     default_process_limit = 200
+     4
+     5 /etc/postfix/master.cf:
+     6     # =============================================================
+     7     # service type  private unpriv  chroot  wakeup  maxproc command
+     8     # =============================================================
+     9     # Raise the SMTP service process limit only.
+    10     smtp      inet  n       -       n       -       200     smtpd
+
+  * NOTE: older versions of the SMTPD_POLICY_README document contain a mistake:
+    they configure a fixed number of policy daemon processes. When you raise
+    the SMTP server's "maxproc" field in master.cf, SMTP server processes will
+    report problems when connecting to policy server processes, because there
+    aren't enough of them. Examples of errors are "connection refused" or
+    "operation timed out". To fix, edit master.cf and specify a zero "maxproc"
+    field in all policy server entries; see line 6 in the example below. Issue
+    a "postfix reload" command to make the change effective.
+
+    1 /etc/postfix/master.cf:
+    2     # =============================================================
+    3     # service type  private unpriv  chroot  wakeup  maxproc command
+    4     # =============================================================
+    5     # Disable the policy service process limit.
+    6     policy    unix  -       n       n       -       0       spawn
+    7         user=nobody argv=/some/where/policy-server
+
+S\bSp\bpe\ben\bnd\bd l\ble\bes\bss\bs t\bti\bim\bme\be p\bpe\ber\br S\bSM\bMT\bTP\bP c\bcl\bli\bie\ben\bnt\bt
+
+When increasing the number of SMTP server processes is not practical, you can
+improve Postfix server responsiveness by eliminating unnecessary work. When
+Postfix spends less time per SMTP session, the same number of SMTP server
+processes can service more clients in the same amount of time.
+
+  * Eliminate non-functional RBL lookups (blocklists that are no longer in
+    operation). These lookups can degrade performance. Postfix logs a warning
+    when an RBL server does not respond.
+
+  * Eliminate redundant RBL lookups (people often use multiple Spamhaus RBLs
+    that include each other). To find out whether RBLs include other RBLs, look
+    up the websites that document the RBL's policies.
+
+  * Eliminate header_checks and body_checks, and keep just a few emergency
+    patterns to block the latest worm explosion or backscatter mail. See
+    BACKSCATTER_README for examples of the latter.
+
+  * Group your header_checks and body_checks patterns to avoid unnecessary
+    pattern matching operations.
+
+     1  /etc/postfix/header_checks:
+     2      if /^Subject:/
+     3      /^Subject: virus found in mail from you/ reject
+     4      /^Subject: ..../ ....
+     5      endif
+     6
+     7      if /^Received:/
+     8      /^Received: from (postfix\.org) / reject forged client name in
+    received header: $1
+     9      /^Received: from .../ ....
+    10      endif
+
+D\bDi\bis\bsc\bco\bon\bnn\bne\bec\bct\bt s\bsu\bus\bsp\bpi\bic\bci\bio\bou\bus\bs S\bSM\bMT\bTP\bP c\bcl\bli\bie\ben\bnt\bts\bs
+
+Under conditions of overload you can improve Postfix SMTP server responsiveness
+by hanging up on suspicious clients, so that other clients get a chance to talk
+to Postfix.
+
+  * Use "421" reply codes for botnet-related RBLs or for selected non-RBL
+    restrictions. This causes Postfix 2.3 and later to disconnect immediately
+    without waiting for the remote SMTP client to send a QUIT command.
+
+    You can set individual reject codes for RBLs, and for individual responses
+    from a specific RBL. We'll use zen.spamhaus.org as an example; by the time
+    you read this document, details may have changed. Right now, their
+    documents say that a response of 127.0.0.10 or 127.0.0.11 indicates a
+    dynamic client IP address, which means that the machine is probably running
+    a bot of some kind. To give a 421 response instead of the default 554
+    response, use something like:
+
+     1  /etc/postfix/main.cf:
+     2      smtpd_client_restrictions =
+     3         permit_mynetworks
+     4         reject_rbl_client zen.spamhaus.org=127.0.0.10
+     5         reject_rbl_client zen.spamhaus.org=127.0.0.11
+     6         reject_rbl_client zen.spamhaus.org
+     7
+     8      rbl_reply_maps = hash:/etc/postfix/rbl_reply_maps
+     9
+    10  /etc/postfix/rbl_reply_maps:
+    11      zen.spamhaus.org=127.0.0.10 421 4.7.1 Service unavailable;
+    12       $rbl_class [$rbl_what] blocked using
+    13       $rbl_domain${rbl_reason?; $rbl_reason}
+    14
+    15      zen.spamhaus.org=127.0.0.11 421 4.7.1 Service unavailable;
+    16       $rbl_class [$rbl_what] blocked using
+    17       $rbl_domain${rbl_reason?; $rbl_reason}
+
+    Although the above shows three RBL lookups (lines 4-6), Postfix will still
+    only do a single DNS query, so the performance difference is negligible.
+
+    The down-side of sending 421 instead of the default 554 is that it works
+    only for zombies and other malware. If the client is running a real MTA,
+    then it may connect again several times until the mail expires in its
+    queue. When this is a problem, stick with the default 554 reply, and use
+    "smtpd_hard_error_limit = 1" as described below.
+
+    With Postfix 2.5, or with earlier releases that contain the stress-adaptive
+    behavior patch, you can turn on the above under overload by replacing line
+    8 with:
+
+     8      rbl_reply_maps = ${stress?hash:/etc/postfix/rbl_reply_maps}
+
+    More information about automatic stress-adaptive behavior is at the end of
+    this document.
+
+T\bTa\bak\bke\be d\bde\bes\bsp\bpe\ber\bra\bat\bte\be m\bme\bea\bas\bsu\bur\bre\bes\bs
+
+The following measures will still allow m\bmo\bos\bst\bt legitimate clients to connect and
+send mail, but may affect some legitimate clients.
+
+  * Reduce smtpd_timeout (default: 300s). Experience on the postfix-users list
+    from a variety of sysadmins shows that reducing the "normal" smtpd_timeout
+    to 60s is unlikely to affect legitimate clients. However, it is unlikely to
+    become the Postfix default because it's not RFC compliant. Setting
+    smtpd_timeout to 10s (line 2 below) or even 5s under stress will still
+    allow m\bmo\bos\bst\bt legitimate clients to connect and send mail, but may delay mail
+    from some clients. No mail should be lost, as long as this measure is used
+    only temporarily.
+
+  * Reduce smtpd_hard_error_limit (default: 20). Setting this to 1 under stress
+    (line 3 below) helps by disconnecting clients after a single error, giving
+    other clients a chance to connect. However, this may cause significant
+    delays with legitimate mail, such as a mailing list that contains a few no-
+    longer-active user names that didn't bother to unsubscribe. No mail should
+    be lost, as long as this measure is used only temporarily.
+
+  * Disable remote SMTP client hostname lookups, so that all SMTP client
+    hostnames become "unknown" (line 5 below). This feature was introduced with
+    Postfix 2.3. Unfortunately, this measure is more problematic than the other
+    ones proposed sofar. First, this will result in loss of mail when you use
+    hostname-based access rules that reject mail from "unknown" SMTP clients
+    (examples: reject_unknown_client_hostname,
+    reject_unknown_reverse_client_hostname). Second, this may result in loss of
+    mail when you subject "unknown" SMTP clients to additional restrictions
+    such as reject_unverified_sender.
+
+    1  /etc/postfix/main.cf:
+    2      smtpd_timeout = 10
+    3      smtpd_hard_error_limit = 1
+    4      # Caution: line 5 may trigger REJECTs by hostname-based access rules
+
+    5      smtpd_peername_lookup = no
+
+Except with the last measure, no mail should be lost, as long as these measures
+are used only temporarily. The next section of this document introduces a way
+to automate this process.
+
+M\bMa\bak\bke\be P\bPo\bos\bst\btf\bfi\bix\bx b\bbe\beh\bha\bav\bvi\bio\bor\br s\bst\btr\bre\bes\bss\bs-\b-a\bad\bda\bap\bpt\bti\biv\bve\be
+
+Postfix version 2.5 introduces automatic stress-adaptive behavior. This is also
+available as an add-on patch for Postfix versions 2.4 and 2.3 from the mirrors
+listed at http://www.postfix.org/download.html.
+
+It works as follows. When a "public" network service runs into an "all server
+ports are busy" condition, the master(8) daemon logs a warning, restarts the
+service (without interrupting existing network sessions), and runs the service
+with "-o stress=yes" on the command line. Normally, it runs a stress-adaptive
+service with "-o stress=" on the command line (i.e. with an empty parameter
+value). Other services never have "-o stress" parameters on the command line,
+including services that listen on a loopback interface only.
+
+The stress pseudo-parameter value is the key to making main.cf parameter
+settings stress adaptive:
+
+    1  /etc/postfix/main.cf:
+    2      smtpd_timeout = ${stress?10}${stress:300}
+    3      smtpd_hard_error_limit = ${stress?1}${stress:20}
+
+Translation:
+
+  * Line 2: under conditions of stress, use an smtpd_timeout value of 10
+    seconds instead of the default 300 seconds,
+
+  * Line 3: under conditions of stress, use an smtpd_hard_error_limit of 1
+    instead of the default 20.
+
+The syntax of ${name?value} and ${name:value} is explained at the beginning of
+the postconf(5) manual page.
+
+NOTE: Please keep in mind that the stress-adaptive feature is a fairly
+desperate measure to keep s\bso\bom\bme\be legitimate mail flowing under overload
+conditions. If a site is reaching the SMTP server process limit when there
+isn't an attack or bot flood occurring, then either the process limit needs to
+be raised or more hardware needs to be added.
+
+D\bDe\bet\bte\bec\bct\bti\bin\bng\bg s\bsu\bup\bpp\bpo\bor\brt\bt f\bfo\bor\br s\bst\btr\bre\bes\bss\bs-\b-a\bad\bda\bap\bpt\bti\biv\bve\be b\bbe\beh\bha\bav\bvi\bio\bor\br
+
+To find out if your Postfix installation supports stress-adaptive behavior, use
+the "ps" command, and look for the smtpd processes. Postfix has stress-adaptive
+support when you see "-o stress=" or "-o stress=yes" command-line options.
+Remember that Postfix never enables stress-adaptive behavior on servers that
+listen on local addresses only.
+
+The following example is for FreeBSD or Linux. On Solaris, HP-UX and other
+System-V flavors, use "ps -ef" instead of "ps ax".
+
+    $ ps ax|grep smtpd
+    83326  ??  S      0:00.28 smtpd -n smtp -t inet -u -c -o stress=
+    84345  ??  Ss     0:00.11 /usr/bin/perl /usr/libexec/postfix/smtpd-
+    policy.pl
+
+You can't use postconf(1) to detect stress-adaptive support. The postconf(1)
+command ignores the existence of the stress parameter in main.cf, because the
+parameter has no effect there. Command-line "-o parameter" settings always take
+precedence over main.cf parameter settings.
+
+If you configure stress-adaptive behavior in main.cf when it isn't supported,
+nothing bad will happen. The processes will run as if the stress parameter
+always has an empty value.
+
+F\bFo\bor\brc\bci\bin\bng\bg s\bst\btr\bre\bes\bss\bs-\b-a\bad\bda\bap\bpt\bti\biv\bve\be b\bbe\beh\bha\bav\bvi\bio\bor\br o\bon\bn o\bor\br o\bof\bff\bf
+
+You can manually force stress-adaptive behavior on, by adding a "-o stress=yes"
+command-line option in master.cf. This can be useful for testing overrides on
+the SMTP service. Issue "postfix reload" to make the change effective.
+
+    1 /etc/postfix/master.cf:
+    2     # =============================================================
+    3     # service type  private unpriv  chroot  wakeup  maxproc command
+    4     # =============================================================
+    5     #
+    6     smtp      inet  n       -       n       -       -       smtpd
+    7         -o stress=yes
+    8         -o . . .
+
+To permanently force stress-adaptive behavior off with a specific service,
+specify "-o stress=" on its command line. This may be desirable for the
+"submission" service. Issue "postfix reload" to make the change effective.
+
+    1 /etc/postfix/master.cf:
+    2     # =============================================================
+    3     # service type  private unpriv  chroot  wakeup  maxproc command
+    4     # =============================================================
+    5     #
+    6     submission inet n       -       n       -       -       smtpd
+    7         -o stress=
+    8         -o . . .
+
+C\bCr\bre\bed\bdi\bit\bts\bs
+
+  * Thanks to the postfix-users mailing list members for sharing early
+    experiences with the stress-adaptive feature.
+  * The RBL example and several other paragraphs of text were adapted from
+    postfix-users postings by Noel Jones.
+  * Wietse implemented stress-adaptive behavior as the smallest possible patch
+    while he should be working on other things.
+
index 7a6526d90f6046732d0ac02ed880c8ab02eff391..62f58225d291cd73b2a03f6ee889a23da3523e97 100644 (file)
@@ -17,6 +17,15 @@ Incompatibility with Postfix 2.3 and earlier
 If you upgrade from Postfix 2.3 or earlier, read RELEASE_NOTES-2.4
 before proceeding.
 
+Major changes with Postfix snapshot 20071110
+============================================
+
+Header/body checks are now available in the SMTP client, after the
+implementation was moved from the cleanup server to a library module.
+The SMTP client provides only actions that don't change the message
+delivery time or destination: warn, replace, prepend, ignore, dunno,
+ok.
+
 Major changes with Postfix snapshot 20070911
 ============================================
 
index 5ae99a5b7422e5c85eed1c5e229af42d569a3acf..5b1aaad04a8af5c5c826212ee18230d186168c4a 100644 (file)
 #        respectively.
 # 
 #        /pattern/flags action
-#               When pattern matches the input string, execute  the
-#               corresponding  action. See below for a list of pos-
-#               sible actions.
+#               When /pattern/ matches the  input  string,  execute
+#               the  corresponding  action. See below for a list of
+#               possible actions.
 # 
 #        !/pattern/flags action
-#               When pattern does not match the input string,  exe-
-#               cute the corresponding action.
+#               When /pattern/ does not  match  the  input  string,
+#               execute the corresponding action.
 # 
 #        if /pattern/flags
 # 
 #        endif  Match the input string against the patterns between
 #               if and endif, if and only if the same input  string
-#               also matches pattern. The if..endif can nest.
+#               also matches /pattern/. The if..endif can nest.
 # 
 #               Note:  do not prepend whitespace to patterns inside
 #               if..endif.
 # 
 #        endif  Match the input string against the patterns between
 #               if  and endif, if and only if the same input string
-#               does not match pattern. The if..endif can nest.
+#               does not match /pattern/. The if..endif can nest.
 # 
 #        blank lines and comments
 #               Empty lines and whitespace-only lines are  ignored,
 #               a pattern before applying more drastic actions.
 # 
 # BUGS
-#        Many  people  overlook  the main limitations of header and
+#        Empty lines never match, because some map types mis-behave
+#        when given a zero-length search string.   This  limitation
+#        may  be  removed for regular expression tables in a future
+#        release.
+# 
+#        Many people overlook the main limitations  of  header  and
 #        body_checks rules.
 # 
-#        o      These rules operate on one logical  message  header
+#        o      These  rules  operate on one logical message header
 #               or one body line at a time. A decision made for one
 #               line is not carried over to the next line.
 # 
-#        o      If text in the message body is encoded  (RFC  2045)
+#        o      If  text  in the message body is encoded (RFC 2045)
 #               then the rules need to be specified for the encoded
 #               form.
 # 
-#        o      Likewise, when message  headers  are  encoded  (RFC
-#               2047)  then  the rules need to be specified for the
+#        o      Likewise,  when  message  headers  are encoded (RFC
+#               2047) then the rules need to be specified  for  the
 #               encoded form.
 # 
-#        Message headers added by the cleanup(8) daemon itself  are
+#        Message  headers added by the cleanup(8) daemon itself are
 #        excluded from inspection. Examples of such message headers
 #        are From:, To:, Message-ID:, Date:.
 # 
-#        Message headers deleted by the cleanup(8) daemon  will  be
+#        Message  headers  deleted by the cleanup(8) daemon will be
 #        examined before they are deleted. Examples are: Bcc:, Con-
 #        tent-Length:, Return-Path:.
 # 
 #        body_checks
 #               Lookup tables with content filter rules for message
 #               body lines.  These filters see one physical line at
-#               a time, in chunks  of  at  most  $line_length_limit
+#               a  time,  in  chunks  of at most $line_length_limit
 #               bytes.
 # 
 #        body_checks_size_limit
-#               The  amount  of  content  per  message body segment
+#               The amount of  content  per  message  body  segment
 #               (attachment) that is subjected to $body_checks fil-
 #               tering.
 # 
 # 
 #        nested_header_checks (default: $header_checks)
 #               Lookup tables with content filter rules for message
-#               header lines: respectively, these  are  applied  to
-#               the  initial  message  headers  (not including MIME
-#               headers), to the MIME headers anywhere in the  mes-
-#               sage,  and  to the initial headers of attached mes-
+#               header  lines:  respectively,  these are applied to
+#               the initial message  headers  (not  including  MIME
+#               headers),  to the MIME headers anywhere in the mes-
+#               sage, and to the initial headers of  attached  mes-
 #               sages.
 # 
-#               Note: these filters see one logical message  header
-#               at  a time, even when a message header spans multi-
-#               ple lines. Message headers  that  are  longer  than
+#               Note:  these filters see one logical message header
+#               at a time, even when a message header spans  multi-
+#               ple  lines.  Message  headers  that are longer than
 #               $header_size_limit characters are truncated.
 # 
 #        disable_mime_input_processing
-#               While  receiving mail, give no special treatment to
-#               MIME related message headers; all  text  after  the
+#               While receiving mail, give no special treatment  to
+#               MIME  related  message  headers; all text after the
 #               initial message headers is considered to be part of
-#               the message body. This means that header_checks  is
-#               applied  to  all  the  initial message headers, and
+#               the  message body. This means that header_checks is
+#               applied to all the  initial  message  headers,  and
 #               that body_checks is applied to the remainder of the
 #               message.
 # 
-#               Note:  when  used  in this manner, body_checks will
-#               process a multi-line message header one line  at  a
+#               Note: when used in this  manner,  body_checks  will
+#               process  a  multi-line message header one line at a
 #               time.
 # 
 # EXAMPLES
-#        Header  pattern  to  block  attachments with bad file name
+#        Header pattern to block attachments  with  bad  file  name
 #        extensions.
 # 
 #        /etc/postfix/main.cf:
 #        RFC 2047, message header encoding for non-ASCII text
 # 
 # README FILES
-#        Use  "postconf  readme_directory" or "postconf html_direc-
+#        Use "postconf readme_directory" or  "postconf  html_direc-
 #        tory" to locate this information.
 #        DATABASE_README, Postfix lookup table overview
 #        CONTENT_INSPECTION_README, Postfix content inspection overview
 #        BACKSCATTER_README, blocking returned forged mail
 # 
 # LICENSE
-#        The Secure Mailer license must be  distributed  with  this
+#        The  Secure  Mailer  license must be distributed with this
 #        software.
 # 
 # AUTHOR(S)
index 1bdc00c1b1a7ea11e9726215ba337d22451afe2f..c1677f8077f0e6e07189930a63de3374012ec870 100644 (file)
@@ -13,10 +13,12 @@ smtp      inet  n       -       n       -       -       smtpd
 #  -o smtpd_enforce_tls=yes
 #  -o smtpd_sasl_auth_enable=yes
 #  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
+#  -o milter_macro_daemon_name=ORIGINATING
 #smtps     inet  n       -       n       -       -       smtpd
 #  -o smtpd_tls_wrappermode=yes
 #  -o smtpd_sasl_auth_enable=yes
 #  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
+#  -o milter_macro_daemon_name=ORIGINATING
 #628      inet  n       -       n       -       -       qmqpd
 pickup    fifo  n       -       n       60      1       pickup
 cleanup   unix  n       -       n       -       0       cleanup
index e637975c2aa53370ce9c3c0a97d93f31978a9bdc..b63de9a35b30b94e21fd148aa17731b2e2f33530 100644 (file)
@@ -262,6 +262,7 @@ $readme_directory/SMTPD_ACCESS_README:f:root:-:644
 $readme_directory/SMTPD_POLICY_README:f:root:-:644
 $readme_directory/SMTPD_PROXY_README:f:root:-:644
 $readme_directory/STANDARD_CONFIGURATION_README:f:root:-:644
+$readme_directory/STRESS_README:f:root:-:644
 $readme_directory/TLS_LEGACY_README:f:root:-:644
 $readme_directory/TLS_README:f:root:-:644
 $readme_directory/TUNING_README:f:root:-:644
@@ -309,6 +310,7 @@ $html_directory/SMTPD_ACCESS_README.html:f:root:-:644
 $html_directory/SMTPD_POLICY_README.html:f:root:-:644
 $html_directory/SMTPD_PROXY_README.html:f:root:-:644
 $html_directory/STANDARD_CONFIGURATION_README.html:f:root:-:644
+$html_directory/STRESS_README.html:f:root:-:644
 $html_directory/TLS_LEGACY_README.html:f:root:-:644
 $html_directory/TLS_README.html:f:root:-:644
 $html_directory/TUNING_README.html:f:root:-:644
index 2ef613f5856dca4eedec6ed357b2ae5a4834632e..a10bc64fbb8ca92fa8f8584f2aecdad86ee9425f 100644 (file)
@@ -20,11 +20,8 @@ Backscatter Howto</h1>
 
 <h2>Overview </h2>
 
-This document describes features that require Postfix version 2.0
-or later.  The examples use Perl Compatible Regular Expressions
-(Postfix <a href="pcre_table.5.html">pcre</a>: tables), but also provide a translation to POSIX
-regular expressions (Postfix <a href="regexp_table.5.html">regexp</a>: tables). PCRE is preferred
-primarily because the implementation is often faster.</p>
+<p> This document describes features that require Postfix version
+2.0 or later. </p>
 
 <p> Topics covered in this document: </p>
 
@@ -56,6 +53,11 @@ scanners</a>
 
 </ul>
 
+<p> The examples use Perl Compatible Regular Expressions (Postfix
+<a href="pcre_table.5.html">pcre</a>: tables), but also provide a translation to POSIX regular
+expressions (Postfix <a href="regexp_table.5.html">regexp</a>: tables). PCRE is preferred primarily
+because the implementation is often faster.</p>
+
 <h2><a name="wtf">What is backscatter mail?</a></h2>
 
 <p> When a spammer or worm sends mail with forged sender addresses,
@@ -73,7 +75,7 @@ to=&lt;yyyyyy@your.domain.here&gt; proto=ESMTP helo=&lt;zzzzzz&gt;
 </pre>
 </blockquote>
 
-<p> What you see are lots of "user unknown" errors with "from=<>".
+<p> What you see are lots of "user unknown" errors with "from=&lt;&gt;".
 These are error reports from MAILER-DAEMONs elsewhere on the Internet.
 </p>
 
index a7e51350a4a2e70a1238196dbbe1cb2f9026b332..ba1c5e8d6db3cb4cc90de7805749770141c66ed8 100644 (file)
@@ -27,7 +27,7 @@ updates: no single-record inserts or deletes are supported.  CDB
 databases can be modified only by rebuilding them completely from
 scratch, hence the "constant" qualifier in the name.  </p>
 
-<p> Postfix CDB databases are specified as "cdb:<i>name</i>", where
+<p> Postfix CDB databases are specified as "<a href="CDB_README.html">cdb</a>:<i>name</i>", where
 <i>name</i> specifies the CDB file name without the ".cdb" suffix
 (another suffix, ".tmp", is used temporarily while a CDB file is
 under construction).  CDB databases are maintained with the <a href="postmap.1.html">postmap(1)</a>
index d28def7207565d997b4b90e702879aa9a482dd9c..642ce78c41574a012124c815bd09bed3df1c7333 100644 (file)
@@ -267,7 +267,7 @@ without the ".db" suffix.  </dd>
 
 <dd> A read-optimized structure with no support for incremental updates.
 Database files are created with the <a href="postmap.1.html">postmap(1)</a> or <a href="postalias.1.html">postalias(1)</a> command.
-The lookup table name as used in "cdb:table" is the database file name
+The lookup table name as used in "<a href="CDB_README.html">cdb</a>:table" is the database file name
 without the ".cdb" suffix.  This feature is available with Postfix 2.2
 and later. </dd>
 
index 4236a3bc6dc3c63157e790243150be1c2c1070f8..d5b6a09efa8007e8707252fdd2bef8ff7ec048d5 100644 (file)
@@ -501,7 +501,7 @@ EOH, BODY, EOM </td> </tr>
 
 </blockquote>
 
-<p> Beware: 30s is not a lot for applications that do a lot of DNS
+<p> Beware: 30s may be too short for applications doing lots of DNS
 lookups.  However, if you increase the above timeouts too much,
 remote SMTP clients may hang up and mail may be delivered multiple
 times. This is an inherent problem with before-queue filtering. </p>
@@ -624,7 +624,7 @@ TO </td> </tr>
 
 <h2><a name="workarounds">Workarounds</a></h2>
 
-<p> Content filters may break domain key etc. signatures. If you
+<p> Content filters may break DKIM etc. signatures. If you
 use an SMTP-based content filter, then you should add a line to
 <a href="master.5.html">master.cf</a> with "-o <a href="postconf.5.html#disable_mime_output_conversion">disable_mime_output_conversion</a>=yes" (note: no
 spaces around the "="), as described in the <a
@@ -650,8 +650,8 @@ sid-filter[36540]: WARNING: sendmail symbol 'i' not available
 </pre>
 </blockquote>
 
-<p> And they may insert a message header with "unknown-msgid" like
-this: </p>
+<p> And they may insert an ugly message header with "unknown-msgid"
+like this: </p>
 
 <blockquote>
 <pre>
diff --git a/postfix/html/STRESS_README.html b/postfix/html/STRESS_README.html
new file mode 100644 (file)
index 0000000..b63ebd2
--- /dev/null
@@ -0,0 +1,469 @@
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+
+<head>
+
+<title>Postfix Stress-Dependent Configuration</title>
+
+<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
+
+</head>
+
+<body>
+
+<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix
+Stress-Dependent Configuration</h1>
+
+<hr>
+
+<h2>Overview </h2>
+
+<p> This document describes the symptoms of Postfix SMTP server
+overload, and how to avoid the condition under normal conditions.
+When the condition is caused by botnets or other malware, the
+document suggests configuration settings that help to minimize the
+impact on legitimate mail.  Finally, the document introduces Postfix
+stress-adaptive behavior, and how it can be used to automatically
+switch configuration settings under overload.  </p>
+
+<p> Topics covered in this document: </p>
+
+<ul>
+
+<li><a href="#overload"> Symptoms of Postfix SMTP server overload </a> 
+
+<li><a href="#concurrency"> Service more SMTP clients at the same time </a> 
+
+<li><a href="#time"> Spend less time per SMTP client </a>
+
+<li><a href="#hangup"> Disconnect suspicious SMTP clients </a>
+
+<li><a href="#desperate"> Take desperate measures </a>
+
+<li><a href="#adapt"> Make Postfix behavior stress-adaptive </a>
+
+<li><a href="#feature"> Detecting support for stress-adaptive behavior </a>
+
+<li><a href="#forcing"> Forcing stress-adaptive behavior on or off </a>
+
+<li><a href="#credits"> Credits </a>
+
+</ul>
+
+<h2><a name="overload"> Symptoms of Postfix SMTP server overload </a></h2>
+
+<p> Under normal conditions, Postfix responds immediately when a
+remote SMTP client connects. The time needed to deliver mail to
+Postfix may depend on how busy the CPU or disk are, but that should
+be noticeable only with very large messages.  Performance degrades
+more dramatically when the number of remote SMTP clients exceeds
+the number of Postfix SMTP server processes.  When a client connects
+while all server processes are busy, the client must wait until a
+server process becomes available. </p>
+
+<p> Overload may be caused by a legitimate mail (example: a DNS
+registrar opens a new zone for registrations), by mistake (mail
+explosion caused by a forwarding loop) or by illegitimate mail (worm
+outbreak, botnet, or other malware activity).  Symptoms of Postfix
+SMTP mail server overload are: </p>
+
+<ul>
+
+<li> <p> Postfix logs a warning that all server ports are busy: </p>
+
+<pre>
+Oct  3 20:39:27 spike postfix/master[28905]: warning: service "smtp"
+ (25) has reached its process limit "30": new clients may experience
+ noticeable delays
+Oct  3 20:39:27 spike postfix/master[28905]: warning: to avoid this
+ condition, increase the process count in <a href="master.5.html">master.cf</a> or reduce the
+ service time per client
+</pre>
+
+<li> <p> Remote SMTP clients experience a long delay before Postfix
+sends the "220 hostname.example.com ESMTP Postfix" greeting.  If
+this affects end-user mail clients, enable the "submission" service
+entry in <a href="master.5.html">master.cf</a> (present since Postfix 2.1), and tell users to
+connect to this instead of the public SMTP service. </p>
+
+<li> <p> The Postfix SMTP server logs an increased number of "lost
+connection after CONNECT" events. This happens because remote SMTP
+clients disconnect before Postfix answers the connection. </p>
+
+</ul>
+
+<p> NOTE: The last two symptoms also happen without overload. </p>
+
+<ul>
+
+<li> <p> Broken DNS also causes lengthy delays before "220
+hostname.example.com
+..." while the Postfix SMTP server tries to look up the client's
+hostname.  </p>
+
+<li> <p> A portscan for open SMTP ports also results in "lost
+connection ..." logfile messages. </p>
+
+</ul>
+
+<p> Legitimate mail that doesn't get through during an episode of
+overload is not necessarily lost. It should still arrive once the
+situation returns to normal, as long as the overload condition is
+temporary.  </p>
+
+<h2><a name="concurrency"> Service more SMTP clients at the same time </a> </h2>
+
+<p> To service more SMTP clients simultaneously, you need to increase
+the number of SMTP server processes. This will improve the
+responsiveness for remote SMTP clients, as long as the server machine
+has enough hardware and software resources to run the additional
+processes, and as long as the file system can keep up with the
+additional load. </p>
+
+<ul>
+
+<li> <p> You increase the number of SMTP server processes either
+by increasing the <a href="postconf.5.html#default_process_limit">default_process_limit</a> in <a href="postconf.5.html">main.cf</a> (line 3 below),
+or by increasing the SMTP server's "maxproc" field in <a href="master.5.html">master.cf</a>
+(line 10 below).  Either way, you need to issue a "postfix reload"
+command to make the change effective.  </p>
+
+<li> <p> Process limits above 1000 require Postfix version 2.4 or
+later, and an operating system that supports kernel-based event
+filters (BSD kqueue(2), Linux epoll(4), or Solaris /dev/poll).
+</p>
+
+<li> <p> You can reduce the Postfix memory footprint by using <a href="CDB_README.html">cdb</a>:
+lookup tables instead of Berkeley DB. </p>
+
+<pre>
+ 1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ 2     # Raise the global process limit, 100 since Postfix 2.0.
+ 3     <a href="postconf.5.html#default_process_limit">default_process_limit</a> = 200
+ 4
+ 5 /etc/postfix/<a href="master.5.html">master.cf</a>:
+ 6     # =============================================================
+ 7     # service type  private unpriv  chroot  wakeup  maxproc command
+ 8     # =============================================================
+ 9     # Raise the SMTP service process limit only.
+10     smtp      inet  n       -       n       -       200     smtpd
+</pre>
+
+<li> <p> NOTE: older versions of the <a href="SMTPD_POLICY_README.html">SMTPD_POLICY_README</a> document
+contain a mistake: they configure a fixed number of policy daemon
+processes.  When you raise the SMTP server's "maxproc" field in
+<a href="master.5.html">master.cf</a>, SMTP server processes will report problems when connecting
+to policy server processes, because there aren't enough of them.
+Examples of errors are "connection refused" or "operation timed
+out".  To fix, edit <a href="master.5.html">master.cf</a> and specify a zero "maxproc" field
+in all policy server entries; see line 6 in the example below.
+Issue a "postfix reload" command to make the change effective.  </p>
+
+<pre>
+1 /etc/postfix/<a href="master.5.html">master.cf</a>:
+2     # =============================================================
+3     # service type  private unpriv  chroot  wakeup  maxproc command
+4     # =============================================================
+5     # Disable the policy service process limit.
+6     policy    unix  -       n       n       -       0       spawn
+7         user=nobody argv=/some/where/policy-server
+</pre>
+
+</ul>
+
+<h2><a name="time"> Spend less time per SMTP client </a></h2>
+
+<p> When increasing the number of SMTP server processes is not
+practical, you can improve Postfix server responsiveness by eliminating
+unnecessary work. When Postfix spends less time per SMTP session, the
+same number of SMTP server processes can service more clients in
+the same amount of time. </p>
+
+<ul>
+
+<li> <p> Eliminate non-functional RBL lookups (blocklists that are
+no longer in operation). These lookups can degrade performance.
+Postfix logs a warning when an RBL server does not respond. </p>
+
+<li> <p> Eliminate redundant RBL lookups (people often use multiple
+Spamhaus RBLs that include each other).  To find out whether RBLs
+include other RBLs, look up the websites that document the RBL's
+policies. </p>
+
+<li> <p> Eliminate <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>, and keep just a few
+emergency patterns to block the latest worm explosion or backscatter
+mail.  See <a href="BACKSCATTER_README.html">BACKSCATTER_README</a> for examples of the latter.
+
+<li> <p> Group your <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a> patterns to avoid
+unnecessary pattern matching operations.
+
+<pre>
+ 1  /etc/postfix/header_checks:
+ 2      if /^Subject:/
+ 3      /^Subject: virus found in mail from you/ reject
+ 4      /^Subject: ..../ ....
+ 5      endif
+ 6  
+ 7      if /^Received:/
+ 8      /^Received: from (postfix\.org) / reject forged client name in received header: $1
+ 9      /^Received: from .../ ....
+10      endif
+</pre>
+
+</ul>
+
+<h2><a name="hangup"> Disconnect suspicious SMTP clients </a></h2>
+
+<p> Under conditions of overload you can improve Postfix SMTP server
+responsiveness by hanging up on suspicious clients, so that other
+clients get a chance to talk to Postfix.  </p>
+
+<ul>
+
+<li> <p> Use "421" reply codes for botnet-related RBLs or for
+selected non-RBL restrictions. This causes Postfix 2.3 and later
+to disconnect immediately without waiting for the remote SMTP
+client to send a QUIT command. </p>
+
+<p> You can set individual reject codes for RBLs, and for individual
+responses from a specific RBL. We'll use zen.spamhaus.org as an
+example; by the time you read this document, details may have
+changed.  Right now, their documents say that a response of 127.0.0.10
+or 127.0.0.11 indicates a dynamic client IP address, which means
+that the machine is probably running a bot of some kind.  To give
+a 421 response instead of the default 554 response, use something
+like: </p>
+
+<pre>
+ 1  /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ 2      <a href="postconf.5.html#smtpd_client_restrictions">smtpd_client_restrictions</a> =
+ 3         <a href="postconf.5.html#permit_mynetworks">permit_mynetworks</a>
+ 4         <a href="postconf.5.html#reject_rbl_client">reject_rbl_client</a> zen.spamhaus.org=127.0.0.10
+ 5         <a href="postconf.5.html#reject_rbl_client">reject_rbl_client</a> zen.spamhaus.org=127.0.0.11
+ 6         <a href="postconf.5.html#reject_rbl_client">reject_rbl_client</a> zen.spamhaus.org
+ 7  
+ 8      <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> = hash:/etc/postfix/rbl_reply_maps
+ 9  
+10  /etc/postfix/rbl_reply_maps:
+11      zen.spamhaus.org=127.0.0.10 421 4.7.1 Service unavailable;
+12       $rbl_class [$rbl_what] blocked using
+13       $rbl_domain${rbl_reason?; $rbl_reason}
+14  
+15      zen.spamhaus.org=127.0.0.11 421 4.7.1 Service unavailable;
+16       $rbl_class [$rbl_what] blocked using
+17       $rbl_domain${rbl_reason?; $rbl_reason}
+</pre>
+
+<p> Although the above shows three RBL lookups (lines 4-6), Postfix
+will still only do a single DNS query, so the performance difference
+is negligible. </p>
+
+<p> The down-side of sending 421 instead of the default 554 is that
+it works only for zombies and other malware. If the client is running
+a real MTA, then it may connect again several times until the mail
+expires in its queue. When this is a problem, stick with the default
+554 reply, and use "<a href="postconf.5.html#smtpd_hard_error_limit">smtpd_hard_error_limit</a> = 1" as described below.
+</p>
+
+<p> With Postfix 2.5, or with earlier releases that contain the
+stress-adaptive behavior patch, you can turn on the above under
+overload by replacing line 8 with: </p>
+
+<pre>
+ 8      <a href="postconf.5.html#rbl_reply_maps">rbl_reply_maps</a> = ${stress?hash:/etc/postfix/rbl_reply_maps}
+</pre>
+
+<p> More information about automatic stress-adaptive behavior is
+at the end of this document. </p>
+
+</ul>
+
+<h2><a name="desperate"> Take desperate measures </a></h2>
+
+<p> The following measures will still allow <b>most</b> legitimate
+clients to connect and send mail, but may affect some legitimate
+clients. </p>
+
+<ul>
+
+<li> <p> Reduce <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> (default: 300s). Experience on the
+postfix-users list from a variety of sysadmins shows that reducing
+the "normal" <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> to 60s is unlikely to affect legitimate
+clients. However, it is unlikely to become the Postfix default
+because it's not RFC compliant. Setting <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> to 10s (line
+2 below) or even 5s under stress will still allow <b>most</b>
+legitimate clients to connect and send mail, but may delay mail
+from some clients.  No mail should be lost, as long as this measure
+is used only temporarily.  </p>
+
+<li> <p> Reduce <a href="postconf.5.html#smtpd_hard_error_limit">smtpd_hard_error_limit</a> (default: 20). Setting this
+to 1 under stress (line 3 below) helps by disconnecting clients
+after a single error, giving other clients a chance to connect.
+However, this may cause significant delays with legitimate mail,
+such as a mailing list that contains a few no-longer-active user
+names that didn't bother to unsubscribe. No mail should be lost,
+as long as this measure is used only temporarily. </p>
+
+<li> <p> Disable remote SMTP client hostname lookups, so that all
+SMTP client hostnames become "unknown" (line 5 below). This feature
+was introduced with Postfix 2.3. Unfortunately, this measure is
+more problematic than the other ones proposed sofar.  First, this
+will result in loss of mail when you use hostname-based access rules
+that reject mail from "unknown" SMTP clients (examples:
+<a href="postconf.5.html#reject_unknown_client_hostname">reject_unknown_client_hostname</a>, <a href="postconf.5.html#reject_unknown_reverse_client_hostname">reject_unknown_reverse_client_hostname</a>).
+Second, this may result in loss of mail when you subject "unknown"
+SMTP clients to additional restrictions such as <a href="postconf.5.html#reject_unverified_sender">reject_unverified_sender</a>.
+</p>
+
+</ul>
+
+<blockquote>
+<pre>
+1  /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+2      <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> = 10
+3      <a href="postconf.5.html#smtpd_hard_error_limit">smtpd_hard_error_limit</a> = 1
+4      # Caution: line 5 may trigger REJECTs by hostname-based access rules 
+5      <a href="postconf.5.html#smtpd_peername_lookup">smtpd_peername_lookup</a> = no
+</pre>
+</blockquote>
+
+<p> Except with the last measure, no mail should be lost, as long
+as these measures are used only temporarily. The next section of
+this document introduces a way to automate this process. </p>
+
+<h2><a name="adapt"> Make Postfix behavior stress-adaptive </a></h2>
+
+<p> Postfix version 2.5 introduces automatic stress-adaptive behavior.
+This is also available as an add-on patch for Postfix versions 2.4
+and 2.3 from the mirrors listed at <a href="http://www.postfix.org/download.html">http://www.postfix.org/download.html</a>.
+</p>
+
+<p> It works as follows. When a "public" network service runs into
+an "all server ports are busy" condition, the <a href="master.8.html">master(8)</a> daemon logs
+a warning, restarts the service (without interrupting existing
+network sessions), and runs the service with "-o stress=yes" on the
+command line. Normally, it runs a stress-adaptive service with "-o
+stress=" on the command line (i.e. with an empty parameter value).
+Other services never have "-o stress" parameters on the command
+line, including services that listen on a loopback interface only.
+</p>
+
+<p> The stress pseudo-parameter value is the key to making <a href="postconf.5.html">main.cf</a>
+parameter settings stress adaptive: </p>
+
+<blockquote>
+<pre>
+1  /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+2      <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> = ${stress?10}${stress:300}
+3      <a href="postconf.5.html#smtpd_hard_error_limit">smtpd_hard_error_limit</a> = ${stress?1}${stress:20}
+</pre>
+</blockquote>
+
+<p> Translation: <p>
+
+<ul>
+
+<li> <p> Line 2: under conditions of stress, use an <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a>
+value of 10 seconds instead of the default 300 seconds,
+
+<li> <p> Line 3: under conditions of stress, use an <a href="postconf.5.html#smtpd_hard_error_limit">smtpd_hard_error_limit</a>
+of 1 instead of the default 20. </p>
+
+</ul>
+
+<p> The syntax of ${name?value} and ${name:value} is explained at
+the beginning of the <a href="postconf.5.html">postconf(5)</a> manual page. </p>
+
+<p> NOTE: Please keep in mind that the stress-adaptive feature is
+a fairly desperate measure to keep <b>some</b> legitimate mail
+flowing under overload conditions.  If a site is reaching the SMTP
+server process limit when there isn't an attack or bot flood
+occurring, then either the process limit needs to be raised or more
+hardware needs to be added.  </p>
+
+<h2><a name="feature"> Detecting support for stress-adaptive behavior </a></h2>
+
+<p> To find out if your Postfix installation supports stress-adaptive
+behavior, use the "ps" command, and look for the smtpd processes.
+Postfix has stress-adaptive support when you see "-o stress=" or
+"-o stress=yes" command-line options. Remember that Postfix never
+enables stress-adaptive behavior on servers that listen on local
+addresses only. </p>
+
+<p> The following example is for FreeBSD or Linux. On Solaris, HP-UX
+and other System-V flavors, use "ps -ef" instead of "ps ax". </p>
+
+<blockquote>
+<pre>
+$ ps ax|grep smtpd
+83326  ??  S      0:00.28 smtpd -n smtp -t inet -u -c -o stress=
+84345  ??  Ss     0:00.11 /usr/bin/perl /usr/libexec/postfix/smtpd-policy.pl
+</pre>
+</blockquote>
+
+<p> You can't use <a href="postconf.1.html">postconf(1)</a> to detect stress-adaptive support.
+The <a href="postconf.1.html">postconf(1)</a> command ignores the existence of the stress parameter
+in <a href="postconf.5.html">main.cf</a>, because the parameter has no effect there.  Command-line
+"-o parameter" settings always take precedence over <a href="postconf.5.html">main.cf</a> parameter
+settings.  <p>
+
+<p> If you configure stress-adaptive behavior in <a href="postconf.5.html">main.cf</a> when it
+isn't supported, nothing bad will happen.  The processes will run
+as if the stress parameter always has an empty value. </p>
+
+<h2><a name="forcing"> Forcing stress-adaptive behavior on or off </a></h2>
+
+<p> You can manually force stress-adaptive behavior on, by adding
+a "-o stress=yes" command-line option in <a href="master.5.html">master.cf</a>. This can be
+useful for testing overrides on the SMTP service. Issue "postfix
+reload" to make the change effective.  </p>
+
+<blockquote>
+<pre>
+1 /etc/postfix/<a href="master.5.html">master.cf</a>:
+2     # =============================================================
+3     # service type  private unpriv  chroot  wakeup  maxproc command
+4     # =============================================================
+5     # 
+6     smtp      inet  n       -       n       -       -       smtpd
+7         -o stress=yes
+8         -o . . .
+</pre>
+</blockquote>
+
+<p> To permanently force stress-adaptive behavior off with a specific
+service, specify "-o stress=" on its command line.  This may be
+desirable for the "submission" service. Issue "postfix reload" to
+make the change effective.  </p>
+
+<blockquote>
+<pre>
+1 /etc/postfix/<a href="master.5.html">master.cf</a>:
+2     # =============================================================
+3     # service type  private unpriv  chroot  wakeup  maxproc command
+4     # =============================================================
+5     # 
+6     submission inet n       -       n       -       -       smtpd
+7         -o stress=
+8         -o . . .
+</pre>
+</blockquote>
+
+<h2><a name="credits"> Credits </a></h2>
+
+<ul>
+
+<li>  Thanks to the postfix-users mailing list members for sharing
+early experiences with the stress-adaptive feature.
+
+<li>  The RBL example and several other paragraphs of text were
+adapted from postfix-users postings by Noel Jones.
+
+<li>  Wietse implemented stress-adaptive behavior as the smallest
+possible patch while he should be working on other things.
+
+</ul>
+
+</body> </html>
index f0f68aa0e7fc3e1b160b28f9524e5b566537d7a7..f11df058e75faf8d3dce8096019d6b585e1d7884 100644 (file)
@@ -93,19 +93,19 @@ HEADER_CHECKS(5)                                              HEADER_CHECKS(5)
        respectively.
 
        <b>/</b><i>pattern</i><b>/</b><i>flags action</i>
-              When <i>pattern</i> matches the input string, execute  the
-              corresponding  <i>action</i>. See below for a list of pos-
-              sible actions.
+              When /<i>pattern</i>/ matches the  input  string,  execute
+              the  corresponding  <i>action</i>. See below for a list of
+              possible actions.
 
        <b>!/</b><i>pattern</i><b>/</b><i>flags action</i>
-              When <i>pattern</i> does <b>not</b> match the input string,  exe-
-              cute the corresponding <i>action</i>.
+              When /<i>pattern</i>/ does <b>not</b>  match  the  input  string,
+              execute the corresponding <i>action</i>.
 
        <b>if /</b><i>pattern</i><b>/</b><i>flags</i>
 
        <b>endif</b>  Match the input string against the patterns between
               <b>if</b> and <b>endif</b>, if and only if the same input  string
-              also matches <i>pattern</i>. The <b>if</b>..<b>endif</b> can nest.
+              also matches /<i>pattern</i>/. The <b>if</b>..<b>endif</b> can nest.
 
               Note:  do not prepend whitespace to patterns inside
               <b>if</b>..<b>endif</b>.
@@ -114,7 +114,7 @@ HEADER_CHECKS(5)                                              HEADER_CHECKS(5)
 
        <b>endif</b>  Match the input string against the patterns between
               <b>if</b>  and <b>endif</b>, if and only if the same input string
-              does <b>not</b> match <i>pattern</i>. The <b>if</b>..<b>endif</b> can nest.
+              does <b>not</b> match /<i>pattern</i>/. The <b>if</b>..<b>endif</b> can nest.
 
        blank lines and comments
               Empty lines and whitespace-only lines are  ignored,
@@ -292,26 +292,31 @@ HEADER_CHECKS(5)                                              HEADER_CHECKS(5)
               a pattern before applying more drastic actions.
 
 <b>BUGS</b>
-       Many  people  overlook  the main limitations of header and
+       Empty lines never match, because some map types mis-behave
+       when given a zero-length search string.   This  limitation
+       may  be  removed for regular expression tables in a future
+       release.
+
+       Many people overlook the main limitations  of  header  and
        <a href="postconf.5.html#body_checks">body_checks</a> rules.
 
-       <b>o</b>      These rules operate on one logical  message  header
+       <b>o</b>      These  rules  operate on one logical message header
               or one body line at a time. A decision made for one
               line is not carried over to the next line.
 
-       <b>o</b>      If text in the message body is encoded  (<a href="http://tools.ietf.org/html/rfc2045">RFC  2045</a>)
+       <b>o</b>      If  text  in the message body is encoded (<a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a>)
               then the rules need to be specified for the encoded
               form.
 
-       <b>o</b>      Likewise, when message  headers  are  encoded  (<a href="http://tools.ietf.org/html/rfc2047">RFC</a>
-              <a href="http://tools.ietf.org/html/rfc2047">2047</a>)  then  the rules need to be specified for the
+       <b>o</b>      Likewise,  when  message  headers  are encoded (<a href="http://tools.ietf.org/html/rfc2047">RFC</a>
+              <a href="http://tools.ietf.org/html/rfc2047">2047</a>) then the rules need to be specified  for  the
               encoded form.
 
-       Message headers added by the <a href="cleanup.8.html"><b>cleanup</b>(8)</a> daemon itself  are
+       Message  headers added by the <a href="cleanup.8.html"><b>cleanup</b>(8)</a> daemon itself are
        excluded from inspection. Examples of such message headers
        are <b>From:</b>, <b>To:</b>, <b>Message-ID:</b>, <b>Date:</b>.
 
-       Message headers deleted by the <a href="cleanup.8.html"><b>cleanup</b>(8)</a> daemon  will  be
+       Message  headers  deleted by the <a href="cleanup.8.html"><b>cleanup</b>(8)</a> daemon will be
        examined before they are deleted. Examples are: <b>Bcc:, Con-</b>
        <b>tent-Length:</b>, <b>Return-Path:</b>.
 
@@ -319,11 +324,11 @@ HEADER_CHECKS(5)                                              HEADER_CHECKS(5)
        <b><a href="postconf.5.html#body_checks">body_checks</a></b>
               Lookup tables with content filter rules for message
               body lines.  These filters see one physical line at
-              a time, in chunks  of  at  most  <b>$<a href="postconf.5.html#line_length_limit">line_length_limit</a></b>
+              a  time,  in  chunks  of at most <b>$<a href="postconf.5.html#line_length_limit">line_length_limit</a></b>
               bytes.
 
        <b><a href="postconf.5.html#body_checks_size_limit">body_checks_size_limit</a></b>
-              The  amount  of  content  per  message body segment
+              The amount of  content  per  message  body  segment
               (attachment) that is subjected to <b>$<a href="postconf.5.html#body_checks">body_checks</a></b> fil-
               tering.
 
@@ -333,32 +338,32 @@ HEADER_CHECKS(5)                                              HEADER_CHECKS(5)
 
        <b><a href="postconf.5.html#nested_header_checks">nested_header_checks</a></b> (default: <b>$<a href="postconf.5.html#header_checks">header_checks</a></b>)
               Lookup tables with content filter rules for message
-              header lines: respectively, these  are  applied  to
-              the  initial  message  headers  (not including MIME
-              headers), to the MIME headers anywhere in the  mes-
-              sage,  and  to the initial headers of attached mes-
+              header  lines:  respectively,  these are applied to
+              the initial message  headers  (not  including  MIME
+              headers),  to the MIME headers anywhere in the mes-
+              sage, and to the initial headers of  attached  mes-
               sages.
 
-              Note: these filters see one logical message  header
-              at  a time, even when a message header spans multi-
-              ple lines. Message headers  that  are  longer  than
+              Note:  these filters see one logical message header
+              at a time, even when a message header spans  multi-
+              ple  lines.  Message  headers  that are longer than
               <b>$<a href="postconf.5.html#header_size_limit">header_size_limit</a></b> characters are truncated.
 
        <b><a href="postconf.5.html#disable_mime_input_processing">disable_mime_input_processing</a></b>
-              While  receiving mail, give no special treatment to
-              MIME related message headers; all  text  after  the
+              While receiving mail, give no special treatment  to
+              MIME  related  message  headers; all text after the
               initial message headers is considered to be part of
-              the message body. This means that <b><a href="postconf.5.html#header_checks">header_checks</a></b>  is
-              applied  to  all  the  initial message headers, and
+              the  message body. This means that <b><a href="postconf.5.html#header_checks">header_checks</a></b> is
+              applied to all the  initial  message  headers,  and
               that <b><a href="postconf.5.html#body_checks">body_checks</a></b> is applied to the remainder of the
               message.
 
-              Note:  when  used  in this manner, <b><a href="postconf.5.html#body_checks">body_checks</a></b> will
-              process a multi-line message header one line  at  a
+              Note: when used in this  manner,  <b><a href="postconf.5.html#body_checks">body_checks</a></b>  will
+              process  a  multi-line message header one line at a
               time.
 
 <b>EXAMPLES</b>
-       Header  pattern  to  block  attachments with bad file name
+       Header pattern to block attachments  with  bad  file  name
        extensions.
 
        /etc/postfix/<a href="postconf.5.html">main.cf</a>:
@@ -396,7 +401,7 @@ HEADER_CHECKS(5)                                              HEADER_CHECKS(5)
        <a href="BACKSCATTER_README.html">BACKSCATTER_README</a>, blocking returned forged mail
 
 <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 4107fc0e1cfdad3a5ca09d1683ff5a06ec9852a1..b2e51c61ede36b76fc943e135383f68f4deca563 100644 (file)
@@ -54,6 +54,8 @@ configuration examples </a>
 
 <li> <a href="QSHAPE_README.html"> Bottleneck analysis  </a>
 
+<li> <a href="STRESS_README.html"> Stress-dependent configuration </a>
+
 <li> <a href="TUNING_README.html"> Performance tuning </a>
 
 <li> <a href="DEBUG_README.html"> Debugging strategies  </a>
index 26c6abba46454d58d5d3172aabe8a57d686794ea..9e1659cde673c16378c4987f1c22bf018712867c 100644 (file)
@@ -262,6 +262,24 @@ SMTP(8)                                                                SMTP(8)
               riZation ID (authzid); send only the SASL authenti-
               Cation ID (authcid) plus the authcid's password.
 
+       Available in Postfix version 2.5 and later:
+
+       <b><a href="postconf.5.html#smtp_header_checks">smtp_header_checks</a> (empty)</b>
+              Restricted  <a href="header_checks.5.html"><b>header_checks</b>(5)</a> tables for the Postfix
+              SMTP client.
+
+       <b><a href="postconf.5.html#smtp_mime_header_checks">smtp_mime_header_checks</a> (empty)</b>
+              Restricted  <b><a href="postconf.5.html#mime_header_checks">mime_header_checks</a></b>(5)  tables  for  the
+              Postfix SMTP client.
+
+       <b><a href="postconf.5.html#smtp_nested_header_checks">smtp_nested_header_checks</a> (empty)</b>
+              Restricted  <b><a href="postconf.5.html#nested_header_checks">nested_header_checks</a></b>(5)  tables for the
+              Postfix SMTP client.
+
+       <b><a href="postconf.5.html#smtp_body_checks">smtp_body_checks</a> (empty)</b>
+              Restricted <a href="header_checks.5.html"><b>body_checks</b>(5)</a> tables  for  the  Postfix
+              SMTP client.
+
 <b>MIME PROCESSING CONTROLS</b>
        Available in Postfix version 2.0 and later:
 
@@ -732,6 +750,9 @@ SMTP(8)                                                                SMTP(8)
               that can't be found or that are unreachable.
 
 <b>SEE ALSO</b>
+       <a href="generic.5.html">generic(5)</a>, output address rewriting
+       <a href="header_checks.5.html">header_checks(5)</a>, message header content inspection
+       <a href="header_checks.5.html">body_checks(5)</a>, body parts content inspection
        <a href="qmgr.8.html">qmgr(8)</a>, queue manager
        <a href="bounce.8.html">bounce(8)</a>, delivery status reports
        <a href="scache.8.html">scache(8)</a>, connection cache server
index 26708b5dfecd7c232f89e3beef914e60383adf74..6a5ff6c171c6d6209088daa98d4d6791cb4e8df0 100644 (file)
@@ -212,6 +212,10 @@ LOCAL(8)                                                              LOCAL(8)
        <b>LOCAL</b>  The entire recipient address localpart (text to the
               left of the rightmost @ character).
 
+       <b>ORIGINAL_RECIPIENT</b>
+              The  entire  recipient  address, before any address
+              rewriting or aliasing (Postfix 2.5 and later).
+
        <b>RECIPIENT</b>
               The entire recipient address.
 
@@ -221,97 +225,97 @@ LOCAL(8)                                                              LOCAL(8)
        the following environment variables:
 
        <b>CLIENT_ADDRESS</b>
-              Remote client  network  address.  Available  as  of
+              Remote  client  network  address.  Available  as of
               Postfix 2.2.
 
        <b>CLIENT_HELO</b>
-              Remote  client EHLO command parameter. Available as
+              Remote client EHLO command parameter. Available  as
               of Postfix 2.2.
 
        <b>CLIENT_HOSTNAME</b>
-              Remote client hostname.  Available  as  of  Postfix
+              Remote  client  hostname.  Available  as of Postfix
               2.2.
 
        <b>CLIENT_PROTOCOL</b>
-              Remote  client  protocol.  Available  as of Postfix
+              Remote client protocol.  Available  as  of  Postfix
               2.2.
 
        <b>SASL_METHOD</b>
-              SASL authentication method specified in the  remote
+              SASL  authentication method specified in the remote
               client AUTH command. Available as of Postfix 2.2.
 
        <b>SASL_SENDER</b>
-              SASL  sender address specified in the remote client
+              SASL sender address specified in the remote  client
               MAIL FROM command. Available as of Postfix 2.2.
 
        <b>SASL_USERNAME</b>
-              SASL username specified in the remote  client  AUTH
+              SASL  username  specified in the remote client AUTH
               command.  Available as of Postfix 2.2.
 
        The <b>PATH</b> environment variable is always reset to a system-
-       dependent default path, and  environment  variables  whose
-       names  are blessed by the <b><a href="postconf.5.html#export_environment">export_environment</a></b> configuration
+       dependent  default  path,  and environment variables whose
+       names are blessed by the <b><a href="postconf.5.html#export_environment">export_environment</a></b>  configuration
        parameter are exported unchanged.
 
        The current working directory is the mail queue directory.
 
-       The  <a href="local.8.html"><b>local</b>(8)</a>  daemon  prepends a "<b>From</b> <i>sender time</i><b>_</b><i>stamp</i>"
-       envelope header to each message, prepends  an  <b>X-Original-</b>
+       The <a href="local.8.html"><b>local</b>(8)</a> daemon prepends a  "<b>From</b>  <i>sender  time</i><b>_</b><i>stamp</i>"
+       envelope  header  to each message, prepends an <b>X-Original-</b>
        <b>To:</b> header with the recipient address as given to Postfix,
-       prepends an optional <b>Delivered-To:</b> header with  the  final
+       prepends  an  optional <b>Delivered-To:</b> header with the final
        recipient envelope address, prepends a <b>Return-Path:</b> header
-       with the sender envelope address,  and  appends  no  empty
+       with  the  sender  envelope  address, and appends no empty
        line.
 
 <b>EXTERNAL FILE DELIVERY</b>
-       The  delivery  format  depends on the destination filename
-       syntax.  The default is to use UNIX-style mailbox  format.
-       Specify  a  name  ending in <b>/</b> for <b>qmail</b>-compatible <b>maildir</b>
+       The delivery format depends on  the  destination  filename
+       syntax.   The default is to use UNIX-style mailbox format.
+       Specify a name ending in <b>/</b>  for  <b>qmail</b>-compatible  <b>maildir</b>
        delivery.
 
-       The <b><a href="postconf.5.html#allow_mail_to_files">allow_mail_to_files</a></b> configuration parameter  restricts
-       delivery  to  external  files. The default setting (<b>alias,</b>
+       The  <b><a href="postconf.5.html#allow_mail_to_files">allow_mail_to_files</a></b> configuration parameter restricts
+       delivery to external files. The  default  setting  (<b>alias,</b>
        <b>forward</b>) forbids file destinations in <b>:include:</b> files.
 
-       In the case of UNIX-style mailbox delivery,  the  <a href="local.8.html"><b>local</b>(8)</a>
+       In  the  case of UNIX-style mailbox delivery, the <a href="local.8.html"><b>local</b>(8)</a>
        daemon prepends a "<b>From</b> <i>sender time</i><b>_</b><i>stamp</i>" envelope header
-       to each message, prepends an  <b>X-Original-To:</b>  header  with
-       the  recipient  address  as  given to Postfix, prepends an
-       optional <b>Delivered-To:</b> header  with  the  final  recipient
-       envelope  address,  prepends a &gt; character to lines begin-
-       ning with "<b>From</b> ", and appends an empty line.   The  enve-
-       lope  sender  address  is  available  in  the <b>Return-Path:</b>
-       header.  When the destination is a  regular  file,  it  is
+       to  each  message,  prepends an <b>X-Original-To:</b> header with
+       the recipient address as given  to  Postfix,  prepends  an
+       optional  <b>Delivered-To:</b>  header  with  the final recipient
+       envelope address, prepends a &gt; character to  lines  begin-
+       ning  with  "<b>From</b> ", and appends an empty line.  The enve-
+       lope sender  address  is  available  in  the  <b>Return-Path:</b>
+       header.   When  the  destination  is a regular file, it is
        locked for exclusive access while delivery is in progress.
        In case of problems, an attempt is made to truncate a reg-
        ular file to its original length.
 
        In the case of <b>maildir</b> delivery, the local daemon prepends
-       an optional <b>Delivered-To:</b> header with the  final  envelope
-       recipient  address,  and prepends an <b>X-Original-To:</b> header
+       an  optional  <b>Delivered-To:</b> header with the final envelope
+       recipient address, and prepends an  <b>X-Original-To:</b>  header
        with the recipient address as given to Postfix.  The enve-
-       lope  sender  address  is  available  in  the <b>Return-Path:</b>
+       lope sender  address  is  available  in  the  <b>Return-Path:</b>
        header.
 
 <b>ADDRESS EXTENSION</b>
-       The optional <b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a></b>  configuration  parameter
-       specifies  how  to  separate address extensions from local
+       The  optional  <b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a></b> configuration parameter
+       specifies how to separate address  extensions  from  local
        recipient names.
 
-       For example, with  "<b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>  =  +</b>",  mail  for
-       <i>name</i>+<i>foo</i>  is  delivered  to  the  alias <i>name</i>+<i>foo</i> or to the
-       alias <i>name</i>, to  the  destinations  listed  in  ~<i>name</i>/.<b>for-</b>
+       For  example,  with  "<b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>  =  +</b>", mail for
+       <i>name</i>+<i>foo</i> is delivered to the  alias  <i>name</i>+<i>foo</i>  or  to  the
+       alias  <i>name</i>,  to  the  destinations  listed in ~<i>name</i>/.<b>for-</b>
        <b>ward</b>+<i>foo</i> or in ~<i>name</i>/.<b>forward</b>, to the mailbox owned by the
        user <i>name</i>, or it is sent back as undeliverable.
 
-       In all cases the  <a href="local.8.html"><b>local</b>(8)</a>  daemon  prepends  an  optional
-       `<b>Delivered-To:</b>   header  line  with  the  final  recipient
+       In  all  cases  the  <a href="local.8.html"><b>local</b>(8)</a>  daemon prepends an optional
+       `<b>Delivered-To:</b>  header  line  with  the  final   recipient
        address.
 
 <b>DELIVERY RIGHTS</b>
-       Deliveries to external files  and  external  commands  are
+       Deliveries  to  external  files  and external commands are
        made with the rights of the receiving user on whose behalf
-       the delivery is made.  In the absence of a  user  context,
+       the  delivery  is made.  In the absence of a user context,
        the <a href="local.8.html"><b>local</b>(8)</a> daemon uses the owner rights of the <b>:include:</b>
        file or alias database.  When those files are owned by the
        superuser, delivery is made with the rights specified with
@@ -322,48 +326,48 @@ LOCAL(8)                                                              LOCAL(8)
        <a href="http://tools.ietf.org/html/rfc3463">RFC 3463</a> (Enhanced status codes)
 
 <b>DIAGNOSTICS</b>
-       Problems and transactions are logged to <b>syslogd</b>(8).   Cor-
-       rupted  message files are marked so that the queue manager
+       Problems  and transactions are logged to <b>syslogd</b>(8).  Cor-
+       rupted message files are marked so that the queue  manager
        can move them to the <b>corrupt</b> queue afterwards.
 
-       Depending on the setting of the <b><a href="postconf.5.html#notify_classes">notify_classes</a></b>  parameter,
-       the  postmaster  is notified of bounces and of other trou-
+       Depending  on the setting of the <b><a href="postconf.5.html#notify_classes">notify_classes</a></b> parameter,
+       the postmaster is notified of bounces and of  other  trou-
        ble.
 
 <b>SECURITY</b>
        The <a href="local.8.html"><b>local</b>(8)</a> delivery agent needs a dual personality 1) to
        access the private Postfix queue and IPC mechanisms, 2) to
-       impersonate the recipient and deliver to  recipient-speci-
-       fied  files  or  commands. It is therefore security sensi-
+       impersonate  the recipient and deliver to recipient-speci-
+       fied files or commands. It is  therefore  security  sensi-
        tive.
 
-       The <a href="local.8.html"><b>local</b>(8)</a> delivery agent disallows  regular  expression
-       substitution  of $1 etc. in <b><a href="postconf.5.html#alias_maps">alias_maps</a></b>, because that would
+       The  <a href="local.8.html"><b>local</b>(8)</a>  delivery agent disallows regular expression
+       substitution of $1 etc. in <b><a href="postconf.5.html#alias_maps">alias_maps</a></b>, because that  would
        open a security hole.
 
-       The <a href="local.8.html"><b>local</b>(8)</a> delivery agent will silently ignore  requests
-       to  use  the <a href="proxymap.8.html"><b>proxymap</b>(8)</a> server within <b><a href="postconf.5.html#alias_maps">alias_maps</a></b>. Instead
-       it will open the table directly.  Before  Postfix  version
-       2.2,  the  <a href="local.8.html"><b>local</b>(8)</a>  delivery  agent will terminate with a
+       The  <a href="local.8.html"><b>local</b>(8)</a> delivery agent will silently ignore requests
+       to use the <a href="proxymap.8.html"><b>proxymap</b>(8)</a> server within  <b><a href="postconf.5.html#alias_maps">alias_maps</a></b>.  Instead
+       it  will  open the table directly.  Before Postfix version
+       2.2, the <a href="local.8.html"><b>local</b>(8)</a> delivery agent  will  terminate  with  a
        fatal error.
 
 <b>BUGS</b>
-       For security  reasons,  the  message  delivery  status  of
-       external  commands  or  of  external files is never check-
+       For  security  reasons,  the  message  delivery  status of
+       external commands or of external  files  is  never  check-
        pointed to file. As a result, the program may occasionally
        deliver more than once to a command or external file. Bet-
        ter safe than sorry.
 
-       Mutually-recursive aliases or  ~/.<b>forward</b>  files  are  not
-       detected  early.   The  resulting  mail forwarding loop is
+       Mutually-recursive  aliases  or  ~/.<b>forward</b>  files are not
+       detected early.  The resulting  mail  forwarding  loop  is
        broken by the use of the <b>Delivered-To:</b> message header.
 
 <b>CONFIGURATION PARAMETERS</b>
-       Changes  to  <a href="postconf.5.html"><b>main.cf</b></a>  are  picked  up  automatically,   as
-       <a href="local.8.html"><b>local</b>(8)</a>  processes run for only a limited amount of time.
+       Changes   to  <a href="postconf.5.html"><b>main.cf</b></a>  are  picked  up  automatically,  as
+       <a href="local.8.html"><b>local</b>(8)</a> processes run for only a limited amount of  time.
        Use the command "<b>postfix reload</b>" to speed up a change.
 
-       The text below provides  only  a  parameter  summary.  See
+       The  text  below  provides  only  a parameter summary. See
        <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
 
 <b>COMPATIBILITY CONTROLS</b>
@@ -373,13 +377,13 @@ LOCAL(8)                                                              LOCAL(8)
        <b><a href="postconf.5.html#expand_owner_alias">expand_owner_alias</a> (no)</b>
               When delivering to an alias "aliasname" that has an
               "owner-aliasname" companion alias, set the envelope
-              sender  address  to  the  expansion  of the "owner-
+              sender address to  the  expansion  of  the  "owner-
               aliasname" alias.
 
        <b><a href="postconf.5.html#owner_request_special">owner_request_special</a> (yes)</b>
-              Give special treatment to owner-listname and  list-
-              name-request  address  localparts: don't split such
-              addresses when the <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a>  is  set  to
+              Give  special treatment to owner-listname and list-
+              name-request address localparts: don't  split  such
+              addresses  when  the  <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> is set to
               "-".
 
        <b><a href="postconf.5.html#sun_mailtool_compatibility">sun_mailtool_compatibility</a> (no)</b>
@@ -388,66 +392,66 @@ LOCAL(8)                                                              LOCAL(8)
        Available in Postfix version 2.3 and later:
 
        <b><a href="postconf.5.html#frozen_delivered_to">frozen_delivered_to</a> (yes)</b>
-              Update  the  <a href="local.8.html"><b>local</b>(8)</a>  delivery agent's idea of the
-              Delivered-To:    address    (see     prepend_deliv-
-              ered_header)  only once, at the start of a delivery
-              attempt; do not update  the  Delivered-To:  address
+              Update the <a href="local.8.html"><b>local</b>(8)</a> delivery agent's  idea  of  the
+              Delivered-To:     address    (see    prepend_deliv-
+              ered_header) only once, at the start of a  delivery
+              attempt;  do  not  update the Delivered-To: address
               while expanding aliases or .forward files.
 
 <b>DELIVERY METHOD CONTROLS</b>
-       The  precedence  of <a href="local.8.html"><b>local</b>(8)</a> delivery methods from high to
-       low is: aliases, .forward  files,  <a href="postconf.5.html#mailbox_transport_maps">mailbox_transport_maps</a>,
-       <a href="postconf.5.html#mailbox_transport">mailbox_transport</a>,  <a href="postconf.5.html#mailbox_command_maps">mailbox_command_maps</a>, <a href="postconf.5.html#mailbox_command">mailbox_command</a>,
-       <a href="postconf.5.html#home_mailbox">home_mailbox</a>,    <a href="postconf.5.html#mail_spool_directory">mail_spool_directory</a>,     fallback_trans-
+       The precedence of <a href="local.8.html"><b>local</b>(8)</a> delivery methods from  high  to
+       low  is:  aliases, .forward files, <a href="postconf.5.html#mailbox_transport_maps">mailbox_transport_maps</a>,
+       <a href="postconf.5.html#mailbox_transport">mailbox_transport</a>, <a href="postconf.5.html#mailbox_command_maps">mailbox_command_maps</a>,  <a href="postconf.5.html#mailbox_command">mailbox_command</a>,
+       <a href="postconf.5.html#home_mailbox">home_mailbox</a>,     <a href="postconf.5.html#mail_spool_directory">mail_spool_directory</a>,    fallback_trans-
        port_maps, <a href="postconf.5.html#fallback_transport">fallback_transport</a>, and <a href="postconf.5.html#luser_relay">luser_relay</a>.
 
        <b><a href="postconf.5.html#alias_maps">alias_maps</a> (see 'postconf -d' output)</b>
-              The  alias  databases  that  are  used for <a href="local.8.html"><b>local</b>(8)</a>
+              The alias databases  that  are  used  for  <a href="local.8.html"><b>local</b>(8)</a>
               delivery.
 
        <b><a href="postconf.5.html#forward_path">forward_path</a> (see 'postconf -d' output)</b>
               The <a href="local.8.html"><b>local</b>(8)</a> delivery agent search list for finding
-              a  .forward file with user-specified delivery meth-
+              a .forward file with user-specified delivery  meth-
               ods.
 
        <b><a href="postconf.5.html#mailbox_transport_maps">mailbox_transport_maps</a> (empty)</b>
-              Optional lookup tables with  per-recipient  message
-              delivery  transports  to  use  for <a href="local.8.html"><b>local</b>(8)</a> mailbox
-              delivery, whether or not the recipients  are  found
+              Optional  lookup  tables with per-recipient message
+              delivery transports to  use  for  <a href="local.8.html"><b>local</b>(8)</a>  mailbox
+              delivery,  whether  or not the recipients are found
               in the UNIX passwd database.
 
        <b><a href="postconf.5.html#mailbox_transport">mailbox_transport</a> (empty)</b>
-              Optional   message   delivery  transport  that  the
-              <a href="local.8.html"><b>local</b>(8)</a> delivery  agent  should  use  for  mailbox
-              delivery  to  all  local recipients, whether or not
+              Optional  message  delivery  transport   that   the
+              <a href="local.8.html"><b>local</b>(8)</a>  delivery  agent  should  use  for mailbox
+              delivery to all local recipients,  whether  or  not
               they are found in the UNIX passwd database.
 
        <b><a href="postconf.5.html#mailbox_command_maps">mailbox_command_maps</a> (empty)</b>
-              Optional lookup tables with per-recipient  external
+              Optional  lookup tables with per-recipient external
               commands to use for <a href="local.8.html"><b>local</b>(8)</a> mailbox delivery.
 
        <b><a href="postconf.5.html#mailbox_command">mailbox_command</a> (empty)</b>
-              Optional  external command that the <a href="local.8.html"><b>local</b>(8)</a> deliv-
+              Optional external command that the <a href="local.8.html"><b>local</b>(8)</a>  deliv-
               ery agent should use for mailbox delivery.
 
        <b><a href="postconf.5.html#home_mailbox">home_mailbox</a> (empty)</b>
-              Optional pathname of a mailbox file relative  to  a
+              Optional  pathname  of a mailbox file relative to a
               <a href="local.8.html"><b>local</b>(8)</a> user's home directory.
 
        <b><a href="postconf.5.html#mail_spool_directory">mail_spool_directory</a> (see 'postconf -d' output)</b>
-              The  directory  where <a href="local.8.html"><b>local</b>(8)</a> UNIX-style mailboxes
+              The directory where <a href="local.8.html"><b>local</b>(8)</a>  UNIX-style  mailboxes
               are kept.
 
        <b><a href="postconf.5.html#fallback_transport_maps">fallback_transport_maps</a> (empty)</b>
-              Optional lookup tables with  per-recipient  message
-              delivery   transports   for   recipients  that  the
-              <a href="local.8.html"><b>local</b>(8)</a> delivery  agent  could  not  find  in  the
+              Optional  lookup  tables with per-recipient message
+              delivery  transports  for   recipients   that   the
+              <a href="local.8.html"><b>local</b>(8)</a>  delivery  agent  could  not  find  in the
               <a href="aliases.5.html"><b>aliases</b>(5)</a> or UNIX password database.
 
        <b><a href="postconf.5.html#fallback_transport">fallback_transport</a> (empty)</b>
-              Optional   message   delivery  transport  that  the
-              <a href="local.8.html"><b>local</b>(8)</a> delivery agent should use for  names  that
-              are  not  found  in the <a href="aliases.5.html"><b>aliases</b>(5)</a> or UNIX password
+              Optional  message  delivery  transport   that   the
+              <a href="local.8.html"><b>local</b>(8)</a>  delivery  agent should use for names that
+              are not found in the <a href="aliases.5.html"><b>aliases</b>(5)</a>  or  UNIX  password
               database.
 
        <b><a href="postconf.5.html#luser_relay">luser_relay</a> (empty)</b>
@@ -457,7 +461,7 @@ LOCAL(8)                                                              LOCAL(8)
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#command_execution_directory">command_execution_directory</a> (empty)</b>
-              The  <a href="local.8.html"><b>local</b>(8)</a>  delivery agent working directory for
+              The <a href="local.8.html"><b>local</b>(8)</a> delivery agent working  directory  for
               delivery to external command.
 
 <b>MAILBOX LOCKING CONTROLS</b>
@@ -466,15 +470,15 @@ LOCAL(8)                                                              LOCAL(8)
               sive lock on a mailbox file or <a href="bounce.8.html"><b>bounce</b>(8)</a> logfile.
 
        <b><a href="postconf.5.html#deliver_lock_delay">deliver_lock_delay</a> (1s)</b>
-              The  time  between attempts to acquire an exclusive
+              The time between attempts to acquire  an  exclusive
               lock on a mailbox file or <a href="bounce.8.html"><b>bounce</b>(8)</a> logfile.
 
        <b><a href="postconf.5.html#stale_lock_time">stale_lock_time</a> (500s)</b>
-              The time after  which  a  stale  exclusive  mailbox
+              The  time  after  which  a  stale exclusive mailbox
               lockfile is removed.
 
        <b><a href="postconf.5.html#mailbox_delivery_lock">mailbox_delivery_lock</a> (see 'postconf -d' output)</b>
-              How  to  lock  a UNIX-style <a href="local.8.html"><b>local</b>(8)</a> mailbox before
+              How to lock a UNIX-style  <a href="local.8.html"><b>local</b>(8)</a>  mailbox  before
               attempting delivery.
 
 <b>RESOURCE AND RATE CONTROLS</b>
@@ -482,17 +486,17 @@ LOCAL(8)                                                              LOCAL(8)
               Time limit for delivery to external commands.
 
        <b><a href="postconf.5.html#duplicate_filter_limit">duplicate_filter_limit</a> (1000)</b>
-              The maximal number of addresses remembered  by  the
-              address  duplicate  filter  for  <a href="aliases.5.html"><b>aliases</b>(5)</a> or <a href="virtual.5.html"><b>vir-</b></a>
+              The  maximal  number of addresses remembered by the
+              address duplicate filter  for  <a href="aliases.5.html"><b>aliases</b>(5)</a>  or  <a href="virtual.5.html"><b>vir-</b></a>
               <a href="virtual.5.html"><b>tual</b>(5)</a> alias expansion, or for <a href="showq.8.html"><b>showq</b>(8)</a> queue dis-
               plays.
 
        <b><a href="postconf.5.html#local_destination_concurrency_limit">local_destination_concurrency_limit</a> (2)</b>
-              The  maximal  number of parallel deliveries via the
+              The maximal number of parallel deliveries  via  the
               local mail delivery transport to the same recipient
-              (when  "<a href="postconf.5.html#local_destination_recipient_limit">local_destination_recipient_limit</a>  = 1") or
-              the maximal number of parallel  deliveries  to  the
-              same  <a href="ADDRESS_CLASS_README.html#local_domain_class">local domain</a> (when "local_destination_recipi-
+              (when "<a href="postconf.5.html#local_destination_recipient_limit">local_destination_recipient_limit</a> =  1")  or
+              the  maximal  number  of parallel deliveries to the
+              same <a href="ADDRESS_CLASS_README.html#local_domain_class">local domain</a> (when  "local_destination_recipi-
               ent_limit &gt; 1").
 
        <b><a href="postconf.5.html#local_destination_recipient_limit">local_destination_recipient_limit</a> (1)</b>
@@ -505,49 +509,49 @@ LOCAL(8)                                                              LOCAL(8)
 
 <b>SECURITY CONTROLS</b>
        <b><a href="postconf.5.html#allow_mail_to_commands">allow_mail_to_commands</a> (alias, forward)</b>
-              Restrict <a href="local.8.html"><b>local</b>(8)</a> mail delivery  to  external  com-
+              Restrict  <a href="local.8.html"><b>local</b>(8)</a>  mail  delivery to external com-
               mands.
 
        <b><a href="postconf.5.html#allow_mail_to_files">allow_mail_to_files</a> (alias, forward)</b>
-              Restrict  <a href="local.8.html"><b>local</b>(8)</a> mail delivery to external files.
+              Restrict <a href="local.8.html"><b>local</b>(8)</a> mail delivery to external  files.
 
        <b><a href="postconf.5.html#command_expansion_filter">command_expansion_filter</a> (see 'postconf -d' output)</b>
-              Restrict the characters that the <a href="local.8.html"><b>local</b>(8)</a>  delivery
-              agent  allows  in $name expansions of $<a href="postconf.5.html#mailbox_command">mailbox_com</a>-
+              Restrict  the characters that the <a href="local.8.html"><b>local</b>(8)</a> delivery
+              agent allows in $name expansions  of  $<a href="postconf.5.html#mailbox_command">mailbox_com</a>-
               <a href="postconf.5.html#mailbox_command">mand</a>.
 
        <b><a href="postconf.5.html#default_privs">default_privs</a> (nobody)</b>
-              The default rights used by  the  <a href="local.8.html"><b>local</b>(8)</a>  delivery
+              The  default  rights  used by the <a href="local.8.html"><b>local</b>(8)</a> delivery
               agent for delivery to external file or command.
 
        <b><a href="postconf.5.html#forward_expansion_filter">forward_expansion_filter</a> (see 'postconf -d' output)</b>
-              Restrict  the characters that the <a href="local.8.html"><b>local</b>(8)</a> delivery
-              agent allows in $name expansions of  $<a href="postconf.5.html#forward_path">forward_path</a>.
+              Restrict the characters that the <a href="local.8.html"><b>local</b>(8)</a>  delivery
+              agent  allows in $name expansions of $<a href="postconf.5.html#forward_path">forward_path</a>.
 
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#execution_directory_expansion_filter">execution_directory_expansion_filter</a>  (see  'postconf  -d'</b>
        <b>output)</b>
-              Restrict  the characters that the <a href="local.8.html"><b>local</b>(8)</a> delivery
+              Restrict the characters that the <a href="local.8.html"><b>local</b>(8)</a>  delivery
               agent allows in $name expansions of $<a href="postconf.5.html#command_execution_directory">command_execu</a>-
               <a href="postconf.5.html#command_execution_directory">tion_directory</a>.
 
 <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#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
-              The maximal number  of  digits  after  the  decimal
+              The  maximal  number  of  digits  after the decimal
               point when logging sub-second delay values.
 
        <b><a href="postconf.5.html#export_environment">export_environment</a> (see 'postconf -d' output)</b>
-              The  list  of  environment variables that a Postfix
+              The list of environment variables  that  a  Postfix
               process will export to non-Postfix processes.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
@@ -555,39 +559,39 @@ LOCAL(8)                                                              LOCAL(8)
               over an internal communication channel.
 
        <b><a href="postconf.5.html#local_command_shell">local_command_shell</a> (empty)</b>
-              Optional  shell  program  for  <a href="local.8.html"><b>local</b>(8)</a> delivery to
+              Optional shell program  for  <a href="local.8.html"><b>local</b>(8)</a>  delivery  to
               non-Postfix command.
 
        <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#prepend_delivered_header">prepend_delivered_header</a> (command, file, forward)</b>
-              The message delivery  contexts  where  the  Postfix
-              <a href="local.8.html"><b>local</b>(8)</a>  delivery  agent  prepends a Delivered-To:
-              message header with the address that the  mail  was
+              The  message  delivery  contexts  where the Postfix
+              <a href="local.8.html"><b>local</b>(8)</a> delivery agent  prepends  a  Delivered-To:
+              message  header  with the address that the mail was
               delivered 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#propagate_unmatched_extensions">propagate_unmatched_extensions</a> (canonical, virtual)</b>
-              What  address  lookup tables copy an address exten-
+              What address lookup tables copy an  address  exten-
               sion from the lookup key to the lookup result.
 
        <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>
@@ -595,15 +599,15 @@ LOCAL(8)                                                              LOCAL(8)
               sions (user+foo).
 
        <b><a href="postconf.5.html#require_home_directory">require_home_directory</a> (no)</b>
-              Whether or not a <a href="local.8.html"><b>local</b>(8)</a> recipient's  home  direc-
-              tory  must exist before mail delivery is attempted.
+              Whether  or  not a <a href="local.8.html"><b>local</b>(8)</a> recipient's home direc-
+              tory must exist before mail delivery is  attempted.
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
-              The mail system  name  that  is  prepended  to  the
-              process  name  in  syslog  records, so that "smtpd"
+              The  mail  system  name  that  is  prepended to the
+              process name in syslog  records,  so  that  "smtpd"
               becomes, for example, "postfix/smtpd".
 
 <b>FILES</b>
@@ -623,14 +627,14 @@ LOCAL(8)                                                              LOCAL(8)
        syslogd(8), system logging
 
 <b>LICENSE</b>
-       The Secure Mailer license must be  distributed  with  this
+       The  Secure  Mailer  license must be distributed with this
        software.
 
 <b>HISTORY</b>
        The <b>Delivered-To:</b> message header appears in the <b>qmail</b> sys-
        tem by Daniel Bernstein.
 
-       The <i>maildir</i> structure  appears  in  the  <b>qmail</b>  system  by
+       The  <i>maildir</i>  structure  appears  in  the  <b>qmail</b> system by
        Daniel Bernstein.
 
 <b>AUTHOR(S)</b>
index 7f26870f37295f1cbafcb805e542628fb365e1a9..7f578a15be88307e5697d66440381101faf99614 100644 (file)
@@ -124,34 +124,36 @@ PIPE(8)                                                                PIPE(8)
 
                      This feature is available as of Postfix 2.5.
 
-              <b>h</b>      Fold  the  command-line  <b>$recipient</b>  address
-                     domain part (text to the right of the right-
-                     most  <b>@</b>  character)  to lower case; fold the
-                     entire  command-line  <b>$domain</b>  and  <b>$nexthop</b>
-                     host  or  domain  information to lower case.
-                     This is recommended for delivery via <b>UUCP</b>.
-
-              <b>q</b>      Quote white space and other special  charac-
-                     ters in the command-line <b>$sender</b> and <b>$recip-</b>
-                     <b>ient</b> address localparts (text to the left of
-                     the right-most <b>@</b> character), according to an
-                     8-bit transparent version of <a href="http://tools.ietf.org/html/rfc822">RFC 822</a>.   This
-                     is  recommended  for  delivery  via  <b>UUCP</b> or
-                     <b>BSMTP</b>.
-
-                     The result is compatible  with  the  address
-                     parsing  of  command-line  recipients by the
+              <b>h</b>      Fold  the  command-line  <b>$original_recipient</b>
+                     and <b>$recipient</b> address domain part (text  to
+                     the  right of the right-most <b>@</b> character) to
+                     lower case;  fold  the  entire  command-line
+                     <b>$domain</b> and <b>$nexthop</b> host or domain informa-
+                     tion to lower case.  This is recommended for
+                     delivery via <b>UUCP</b>.
+
+              <b>q</b>      Quote  white space and other special charac-
+                     ters in the  command-line  <b>$sender</b>,  <b>$origi-</b>
+                     <b>nal_recipient</b>  and <b>$recipient</b> address local-
+                     parts (text to the left of the right-most  <b>@</b>
+                     character),  according to an 8-bit transpar-
+                     ent version of <a href="http://tools.ietf.org/html/rfc822">RFC 822</a>.  This is recommended
+                     for delivery via <b>UUCP</b> or <b>BSMTP</b>.
+
+                     The  result  is  compatible with the address
+                     parsing of command-line  recipients  by  the
                      Postfix <a href="sendmail.1.html"><b>sendmail</b>(1)</a> mail submission command.
 
-                     The  <b>q</b>  flag  affects only entire addresses,
+                     The <b>q</b> flag affects  only  entire  addresses,
                      not the partial address information from the
-                     <b>$user</b>,  <b>$extension</b>  or <b>$mailbox</b> command-line
+                     <b>$user</b>, <b>$extension</b> or  <b>$mailbox</b>  command-line
                      macros.
 
-              <b>u</b>      Fold  the  command-line  <b>$recipient</b>  address
-                     localpart  (text  to  the left of the right-
-                     most <b>@</b> character) to lower  case.   This  is
-                     recommended for delivery via <b>UUCP</b>.
+              <b>u</b>      Fold  the  command-line  <b>$original_recipient</b>
+                     and <b>$recipient</b> address  localpart  (text  to
+                     the  left  of the right-most <b>@</b> character) to
+                     lower case.  This is recommended for  deliv-
+                     ery via <b>UUCP</b>.
 
               <b>.</b>      Prepend "<b>.</b>" to lines starting with "<b>.</b>". This
                      is needed by, for example, <b>BSMTP</b> software.
@@ -294,6 +296,19 @@ PIPE(8)                                                                PIPE(8)
                      This  information  is modified by the <b>h</b> flag
                      for case folding.
 
+              <b>${original_recipient</b>}
+                     This macro expands to the complete recipient
+                     address  before  any  address  rewriting  or
+                     aliasing.
+
+                     A  command-line   argument   that   contains
+                     <b>${original_recipient</b>}  expands  to  as  many
+                     command-line arguments as there are  recipi-
+                     ents.
+
+                     This  information  is  modified  by  the <b>hqu</b>
+                     flags for quoting and case folding.
+
               <b>${recipient</b>}
                      This macro expands to the complete recipient
                      address.
index 77e571316d901746927a52282fb16a470c781103..5cef8f2dad46257eb5a3d7cf77a02c66a5888a5f 100644 (file)
@@ -4564,6 +4564,11 @@ and later.</dd>
 
 <dd>The recipient's username. </dd>
 
+<dt><b>ORIGINAL_RECIPIENT</b></dt>
+
+<dd>The entire recipient address, before any address rewriting or
+aliasing.  </dd>
+
 <dt><b>RECIPIENT</b></dt>
 
 <dd>The full recipient address. </dd>
@@ -7029,6 +7034,19 @@ IP hosting, but can be a problem on multi-homed firewalls. See the
 but this form is not recommended here. </p>
 
 
+</DD>
+
+<DT><b><a name="smtp_body_checks">smtp_body_checks</a>
+(default: empty)</b></DT><DD>
+
+<p> Restricted <a href="header_checks.5.html">body_checks(5)</a> tables for the Postfix SMTP client.
+These tables are searched while mail is being delivered.  Actions
+that change the delivery time or destination are not available.
+</p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="smtp_cname_overrides_servername">smtp_cname_overrides_servername</a>
@@ -7418,6 +7436,19 @@ examples are shown in the <a href="ADDRESS_REWRITING_README.html">ADDRESS_REWRIT
 <p> This feature is available in Postfix 2.2 and later.  </p>
 
 
+</DD>
+
+<DT><b><a name="smtp_header_checks">smtp_header_checks</a>
+(default: empty)</b></DT><DD>
+
+<p> Restricted <a href="header_checks.5.html">header_checks(5)</a> tables for the Postfix SMTP client.
+These tables are searched while mail is being delivered.  Actions
+that change the delivery time or destination are not available.
+</p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="smtp_helo_name">smtp_helo_name</a>
@@ -7534,6 +7565,19 @@ The default time unit is s (seconds).
 </p>
 
 
+</DD>
+
+<DT><b><a name="smtp_mime_header_checks">smtp_mime_header_checks</a>
+(default: empty)</b></DT><DD>
+
+<p> Restricted mime_<a href="header_checks.5.html">header_checks(5)</a> tables for the Postfix SMTP
+client. These tables are searched while mail is being delivered.
+Actions that change the delivery time or destination are not
+available.  </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="smtp_mx_address_limit">smtp_mx_address_limit</a>
@@ -7564,6 +7608,19 @@ complete the EHLO and TLS handshake (Postfix version 2.3 and later).  </p>
 <p> This feature is available in Postfix 2.1 and later.  </p>
 
 
+</DD>
+
+<DT><b><a name="smtp_nested_header_checks">smtp_nested_header_checks</a>
+(default: empty)</b></DT><DD>
+
+<p> Restricted nested_<a href="header_checks.5.html">header_checks(5)</a> tables for the Postfix SMTP
+client. These tables are searched while mail is being delivered.
+Actions that change the delivery time or destination are not
+available.  </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="smtp_never_send_ehlo">smtp_never_send_ehlo</a>
index 26c6abba46454d58d5d3172aabe8a57d686794ea..9e1659cde673c16378c4987f1c22bf018712867c 100644 (file)
@@ -262,6 +262,24 @@ SMTP(8)                                                                SMTP(8)
               riZation ID (authzid); send only the SASL authenti-
               Cation ID (authcid) plus the authcid's password.
 
+       Available in Postfix version 2.5 and later:
+
+       <b><a href="postconf.5.html#smtp_header_checks">smtp_header_checks</a> (empty)</b>
+              Restricted  <a href="header_checks.5.html"><b>header_checks</b>(5)</a> tables for the Postfix
+              SMTP client.
+
+       <b><a href="postconf.5.html#smtp_mime_header_checks">smtp_mime_header_checks</a> (empty)</b>
+              Restricted  <b><a href="postconf.5.html#mime_header_checks">mime_header_checks</a></b>(5)  tables  for  the
+              Postfix SMTP client.
+
+       <b><a href="postconf.5.html#smtp_nested_header_checks">smtp_nested_header_checks</a> (empty)</b>
+              Restricted  <b><a href="postconf.5.html#nested_header_checks">nested_header_checks</a></b>(5)  tables for the
+              Postfix SMTP client.
+
+       <b><a href="postconf.5.html#smtp_body_checks">smtp_body_checks</a> (empty)</b>
+              Restricted <a href="header_checks.5.html"><b>body_checks</b>(5)</a> tables  for  the  Postfix
+              SMTP client.
+
 <b>MIME PROCESSING CONTROLS</b>
        Available in Postfix version 2.0 and later:
 
@@ -732,6 +750,9 @@ SMTP(8)                                                                SMTP(8)
               that can't be found or that are unreachable.
 
 <b>SEE ALSO</b>
+       <a href="generic.5.html">generic(5)</a>, output address rewriting
+       <a href="header_checks.5.html">header_checks(5)</a>, message header content inspection
+       <a href="header_checks.5.html">body_checks(5)</a>, body parts content inspection
        <a href="qmgr.8.html">qmgr(8)</a>, queue manager
        <a href="bounce.8.html">bounce(8)</a>, delivery status reports
        <a href="scache.8.html">scache(8)</a>, connection cache server
index a961476f8dbe2131bf502a2f7780b8337bd7dca5..1b2b11bec549681720459a2abe4094ad650ef05e 100644 (file)
@@ -91,17 +91,17 @@ given below.
 For a discussion of specific pattern or flags syntax,
 see \fBpcre_table\fR(5) or \fBregexp_table\fR(5), respectively.
 .IP "\fB/\fIpattern\fB/\fIflags action\fR"
-When \fIpattern\fR matches the input string, execute
+When /\fIpattern\fR/ matches the input string, execute
 the corresponding \fIaction\fR. See below for a list
 of possible actions.
 .IP "\fB!/\fIpattern\fB/\fIflags action\fR"
-When \fIpattern\fR does \fBnot\fR match the input string,
+When /\fIpattern\fR/ does \fBnot\fR match the input string,
 execute the corresponding \fIaction\fR.
 .IP "\fBif /\fIpattern\fB/\fIflags\fR"
 .IP "\fBendif\fR"
 Match the input string against the patterns between \fBif\fR
 and \fBendif\fR, if and only if the same input string also
-matches \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest.
+matches /\fIpattern\fR/. The \fBif\fR..\fBendif\fR can nest.
 .sp
 Note: do not prepend whitespace to patterns inside
 \fBif\fR..\fBendif\fR.
@@ -109,7 +109,7 @@ Note: do not prepend whitespace to patterns inside
 .IP "\fBendif\fR"
 Match the input string against the patterns between \fBif\fR
 and \fBendif\fR, if and only if the same input string does
-\fBnot\fR match \fIpattern\fR. The \fBif\fR..\fBendif\fR
+\fBnot\fR match /\fIpattern\fR/. The \fBif\fR..\fBendif\fR
 can nest.
 .IP "blank lines and comments"
 Empty lines and whitespace-only lines are ignored, as
@@ -272,6 +272,10 @@ before applying more drastic actions.
 .SH BUGS
 .ad
 .fi
+Empty lines never match, because some map types mis-behave
+when given a zero-length search string.  This limitation may
+be removed for regular expression tables in a future release.
+
 Many people overlook the main limitations of header and body_checks
 rules.
 .IP \(bu
index 2c41ca371383009f125973e0b488e736d5d4e886..d558846de57a5b28e1b7a48409c2d373eecd17e2 100644 (file)
@@ -2461,6 +2461,9 @@ The recipient home directory.
 The recipient address localpart.
 .IP "\fBLOGNAME\fR"
 The recipient's username.
+.IP "\fBORIGINAL_RECIPIENT\fR"
+The entire recipient address, before any address rewriting or
+aliasing.
 .IP "\fBRECIPIENT\fR"
 The full recipient address.
 .IP "\fBSASL_METHOD\fR"
@@ -3911,6 +3914,12 @@ inet_interfaces documentation for more detail.
 .PP
 Note 2: address information may be enclosed inside [],
 but this form is not recommended here.
+.SH smtp_body_checks (default: empty)
+Restricted \fBbody_checks\fR(5) tables for the Postfix SMTP client.
+These tables are searched while mail is being delivered.  Actions
+that change the delivery time or destination are not available.
+.PP
+This feature is available in Postfix 2.5 and later.
 .SH smtp_cname_overrides_servername (default: version dependent)
 Allow DNS CNAME records to override the servername that the
 Postfix SMTP client uses for logging, SASL password lookup, TLS
@@ -4150,6 +4159,12 @@ examples are shown in the ADDRESS_REWRITING_README and
 STANDARD_CONFIGURATION_README documents.
 .PP
 This feature is available in Postfix 2.2 and later.
+.SH smtp_header_checks (default: empty)
+Restricted \fBheader_checks\fR(5) tables for the Postfix SMTP client.
+These tables are searched while mail is being delivered.  Actions
+that change the delivery time or destination are not available.
+.PP
+This feature is available in Postfix 2.5 and later.
 .SH smtp_helo_name (default: $myhostname)
 The hostname to send in the SMTP EHLO or HELO command.
 .PP
@@ -4204,6 +4219,13 @@ for receiving the server response.
 .PP
 Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
 The default time unit is s (seconds).
+.SH smtp_mime_header_checks (default: empty)
+Restricted \fBmime_header_checks\fR(5) tables for the Postfix SMTP
+client. These tables are searched while mail is being delivered.
+Actions that change the delivery time or destination are not
+available.
+.PP
+This feature is available in Postfix 2.5 and later.
 .SH smtp_mx_address_limit (default: 5)
 The maximal number of MX (mail exchanger) IP addresses that can
 result from mail exchanger lookups, or zero (no limit). Prior to
@@ -4218,6 +4240,13 @@ SMTP initial handshake (Postfix version 2.2 and earlier) or that fail to
 complete the EHLO and TLS handshake (Postfix version 2.3 and later).
 .PP
 This feature is available in Postfix 2.1 and later.
+.SH smtp_nested_header_checks (default: empty)
+Restricted \fBnested_header_checks\fR(5) tables for the Postfix SMTP
+client. These tables are searched while mail is being delivered.
+Actions that change the delivery time or destination are not
+available.
+.PP
+This feature is available in Postfix 2.5 and later.
 .SH smtp_never_send_ehlo (default: no)
 Never send EHLO at the start of an SMTP session. See also the
 smtp_always_send_ehlo parameter.
index 445200745a3113800e58d66af1c6ac1fa7dd0c46..2699ba6eede679d44d2e569d300059f5fa7a3d80 100644 (file)
@@ -226,6 +226,9 @@ The bare recipient name.
 .IP \fBLOCAL\fR
 The entire recipient address localpart (text to the left of the
 rightmost @ character).
+.IP \fBORIGINAL_RECIPIENT\fR
+The entire recipient address, before any address rewriting
+or aliasing (Postfix 2.5 and later).
 .IP \fBRECIPIENT\fR
 The entire recipient address.
 .IP \fBSENDER\fR
index 13c767013209b8fc094be4d11ba8853940416ce9..bf8b4e89030292ac318242fb9fa3fe4ff79da8c1 100644 (file)
@@ -118,14 +118,16 @@ from "relayed" into "delivered".
 .sp
 This feature is available as of Postfix 2.5.
 .IP \fBh\fR
-Fold the command-line \fB$recipient\fR address domain part
+Fold the command-line \fB$original_recipient\fR and
+\fB$recipient\fR address domain part
 (text to the right of the right-most \fB@\fR character) to
 lower case; fold the entire command-line \fB$domain\fR and
 \fB$nexthop\fR host or domain information to lower case.
 This is recommended for delivery via \fBUUCP\fR.
 .IP \fBq\fR
 Quote white space and other special characters in the command-line
-\fB$sender\fR and \fB$recipient\fR address localparts (text to the
+\fB$sender\fR, \fB$original_recipient\fR and \fB$recipient\fR
+address localparts (text to the
 left of the right-most \fB@\fR character), according to an 8-bit
 transparent version of RFC 822.
 This is recommended for delivery via \fBUUCP\fR or \fBBSMTP\fR.
@@ -137,7 +139,8 @@ The \fBq\fR flag affects only entire addresses, not the partial
 address information from the \fB$user\fR, \fB$extension\fR or
 \fB$mailbox\fR command-line macros.
 .IP \fBu\fR
-Fold the command-line \fB$recipient\fR address localpart (text to
+Fold the command-line \fB$original_recipient\fR and
+\fB$recipient\fR address localpart (text to
 the left of the right-most \fB@\fR character) to lower case.
 This is recommended for delivery via \fBUUCP\fR.
 .IP \fB.\fR
@@ -254,6 +257,16 @@ This information is modified by the \fBu\fR flag for case folding.
 This macro expands to the next-hop hostname.
 .sp
 This information is modified by the \fBh\fR flag for case folding.
+.IP \fB${\fBoriginal_recipient\fR}\fR
+This macro expands to the complete recipient address before any
+address rewriting or aliasing.
+.sp
+A command-line argument that contains
+\fB${\fBoriginal_recipient\fR}\fR expands to as many
+command-line arguments as there are recipients.
+.sp
+This information is modified by the \fBhqu\fR flags for quoting
+and case folding.
 .IP \fB${\fBrecipient\fR}\fR
 This macro expands to the complete recipient address.
 .sp
index a7390cd0fc313aff5dad071577081098d2d367bd..40532a4db677958059b975d8a744045e124f884d 100644 (file)
@@ -233,6 +233,18 @@ Available in Postfix version 2.4.4 and later:
 When authenticating to a remote SMTP or LMTP server with the
 default setting "no", send no SASL authoriZation ID (authzid); send
 only the SASL authentiCation ID (authcid) plus the authcid's password.
+.PP
+Available in Postfix version 2.5 and later:
+.IP "\fBsmtp_header_checks (empty)\fR"
+Restricted \fBheader_checks\fR(5) tables for the Postfix SMTP client.
+.IP "\fBsmtp_mime_header_checks (empty)\fR"
+Restricted \fBmime_header_checks\fR(5) tables for the Postfix SMTP
+client.
+.IP "\fBsmtp_nested_header_checks (empty)\fR"
+Restricted \fBnested_header_checks\fR(5) tables for the Postfix SMTP
+client.
+.IP "\fBsmtp_body_checks (empty)\fR"
+Restricted \fBbody_checks\fR(5) tables for the Postfix SMTP client.
 .SH "MIME PROCESSING CONTROLS"
 .na
 .nf
@@ -578,6 +590,9 @@ found or that are unreachable.
 .SH "SEE ALSO"
 .na
 .nf
+generic(5), output address rewriting
+header_checks(5), message header content inspection
+body_checks(5), body parts content inspection
 qmgr(8), queue manager
 bounce(8), delivery status reports
 scache(8), connection cache server
index 34421491a72511b1c06277a7b6b8e49a2d927b7b..cf1586db442ffe5e4c0708b73d5211cd6d3d1c82 100755 (executable)
@@ -563,6 +563,10 @@ while (<>) {
     s;\bsmtp_tls_session_cache_database\b;<a href="postconf.5.html#smtp_tls_session_cache_database">$&</a>;g;
     s;\bsmtp_tls_session_cache_timeout\b;<a href="postconf.5.html#smtp_tls_session_cache_timeout">$&</a>;g;
     s;\bsmtp_use_tls\b;<a href="postconf.5.html#smtp_use_tls">$&</a>;g;
+    s;\bsmtp_header_checks\b;<a href="postconf.5.html#smtp_header_checks">$&</a>;g;
+    s;\bsmtp_mime_header_checks\b;<a href="postconf.5.html#smtp_mime_header_checks">$&</a>;g;
+    s;\bsmtp_nested_header_checks\b;<a href="postconf.5.html#smtp_nested_header_checks">$&</a>;g;
+    s;\bsmtp_body_checks\b;<a href="postconf.5.html#smtp_body_checks">$&</a>;g;
     s;\bsmtpd_enforce_tls\b;<a href="postconf.5.html#smtpd_enforce_tls">$&</a>;g;
     s;\bsmtpd_sasl_tls_security_options\b;<a href="postconf.5.html#smtpd_sasl_tls_security_options">$&</a>;g;
     s;\bsmtpd_sasl_type\b;<a href="postconf.5.html#smtpd_sasl_type">$&</a>;g;
@@ -833,6 +837,7 @@ while (<>) {
 
     # Hyperlink map types.
 
+    s/\b(cdb):/<a href="CDB_README.html">$1<\/a>:/g;
     s/\b(cidr):/<a href="cidr_table.5.html">$1<\/a>:/g;
     s/\b(pcre):/<a href="pcre_table.5.html">$1<\/a>:/g;
     s/\b(proxy):/<a href="proxymap.8.html">$1<\/a>:/g;
index 376d896cf54ce9f002342bdad34e96960df53c44..ccd75b518bb813c3c1ac6527fe3160344ec4d4f2 100644 (file)
@@ -20,11 +20,8 @@ Backscatter Howto</h1>
 
 <h2>Overview </h2>
 
-This document describes features that require Postfix version 2.0
-or later.  The examples use Perl Compatible Regular Expressions
-(Postfix pcre: tables), but also provide a translation to POSIX
-regular expressions (Postfix regexp: tables). PCRE is preferred
-primarily because the implementation is often faster.</p>
+<p> This document describes features that require Postfix version
+2.0 or later. </p>
 
 <p> Topics covered in this document: </p>
 
@@ -56,6 +53,11 @@ scanners</a>
 
 </ul>
 
+<p> The examples use Perl Compatible Regular Expressions (Postfix
+pcre: tables), but also provide a translation to POSIX regular
+expressions (Postfix regexp: tables). PCRE is preferred primarily
+because the implementation is often faster.</p>
+
 <h2><a name="wtf">What is backscatter mail?</a></h2>
 
 <p> When a spammer or worm sends mail with forged sender addresses,
@@ -73,7 +75,7 @@ to=&lt;yyyyyy@your.domain.here&gt; proto=ESMTP helo=&lt;zzzzzz&gt;
 </pre>
 </blockquote>
 
-<p> What you see are lots of "user unknown" errors with "from=<>".
+<p> What you see are lots of "user unknown" errors with "from=&lt;&gt;".
 These are error reports from MAILER-DAEMONs elsewhere on the Internet.
 </p>
 
index edef90fde5842f3be8fdbc04eba6ecc91428fa71..a2867504120ed4d1968faa300ee0c0892a9acca3 100644 (file)
@@ -501,7 +501,7 @@ EOH, BODY, EOM </td> </tr>
 
 </blockquote>
 
-<p> Beware: 30s is not a lot for applications that do a lot of DNS
+<p> Beware: 30s may be too short for applications doing lots of DNS
 lookups.  However, if you increase the above timeouts too much,
 remote SMTP clients may hang up and mail may be delivered multiple
 times. This is an inherent problem with before-queue filtering. </p>
@@ -624,7 +624,7 @@ TO </td> </tr>
 
 <h2><a name="workarounds">Workarounds</a></h2>
 
-<p> Content filters may break domain key etc. signatures. If you
+<p> Content filters may break DKIM etc. signatures. If you
 use an SMTP-based content filter, then you should add a line to
 master.cf with "-o disable_mime_output_conversion=yes" (note: no
 spaces around the "="), as described in the <a
@@ -650,8 +650,8 @@ sid-filter[36540]: WARNING: sendmail symbol 'i' not available
 </pre>
 </blockquote>
 
-<p> And they may insert a message header with "unknown-msgid" like
-this: </p>
+<p> And they may insert an ugly message header with "unknown-msgid"
+like this: </p>
 
 <blockquote>
 <pre>
index e2e855b78bb6cd54bf7c034fb8cf59b7df4d3c33..ec7e0d6ae0b413f6f4341cee27014f09b1a79311 100644 (file)
@@ -34,6 +34,7 @@ HTML  = ../html/ADDRESS_CLASS_README.html \
        ../html/SMTPD_POLICY_README.html \
        ../html/SMTPD_PROXY_README.html \
        ../html/STANDARD_CONFIGURATION_README.html \
+       ../html/STRESS_README.html \
        ../html/TLS_README.html ../html/TLS_LEGACY_README.html \
        ../html/TUNING_README.html \
        ../html/UUCP_README.html \
@@ -69,6 +70,7 @@ README        = ../README_FILES/ADDRESS_CLASS_README \
        ../README_FILES/SMTPD_ACCESS_README \
        ../README_FILES/SMTPD_POLICY_README ../README_FILES/SMTPD_PROXY_README \
        ../README_FILES/STANDARD_CONFIGURATION_README \
+       ../README_FILES/STRESS_README \
        ../README_FILES/TLS_README ../README_FILES/TLS_LEGACY_README \
        ../README_FILES/TUNING_README \
        ../README_FILES/UUCP_README \
@@ -235,6 +237,9 @@ clobber:
 ../html/STANDARD_CONFIGURATION_README.html: STANDARD_CONFIGURATION_README.html
        $(POSTLINK) $? >$@
 
+../html/STRESS_README.html: STRESS_README.html
+       $(POSTLINK) $? >$@
+
 ../html/TUNING_README.html: TUNING_README.html
        $(POSTLINK) $? >$@
 
@@ -376,6 +381,9 @@ clobber:
 ../README_FILES/STANDARD_CONFIGURATION_README: STANDARD_CONFIGURATION_README.html
        $(HT2READ) $? >$@
 
+../README_FILES/STRESS_README: STRESS_README.html
+       $(HT2READ) $? >$@
+
 ../README_FILES/TUNING_README: TUNING_README.html
        $(HT2READ) $? >$@
 
diff --git a/postfix/proto/STRESS_README.html b/postfix/proto/STRESS_README.html
new file mode 100644 (file)
index 0000000..a313a0d
--- /dev/null
@@ -0,0 +1,469 @@
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+
+<head>
+
+<title>Postfix Stress-Dependent Configuration</title>
+
+<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
+
+</head>
+
+<body>
+
+<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix
+Stress-Dependent Configuration</h1>
+
+<hr>
+
+<h2>Overview </h2>
+
+<p> This document describes the symptoms of Postfix SMTP server
+overload, and how to avoid the condition under normal conditions.
+When the condition is caused by botnets or other malware, the
+document suggests configuration settings that help to minimize the
+impact on legitimate mail.  Finally, the document introduces Postfix
+stress-adaptive behavior, and how it can be used to automatically
+switch configuration settings under overload.  </p>
+
+<p> Topics covered in this document: </p>
+
+<ul>
+
+<li><a href="#overload"> Symptoms of Postfix SMTP server overload </a> 
+
+<li><a href="#concurrency"> Service more SMTP clients at the same time </a> 
+
+<li><a href="#time"> Spend less time per SMTP client </a>
+
+<li><a href="#hangup"> Disconnect suspicious SMTP clients </a>
+
+<li><a href="#desperate"> Take desperate measures </a>
+
+<li><a href="#adapt"> Make Postfix behavior stress-adaptive </a>
+
+<li><a href="#feature"> Detecting support for stress-adaptive behavior </a>
+
+<li><a href="#forcing"> Forcing stress-adaptive behavior on or off </a>
+
+<li><a href="#credits"> Credits </a>
+
+</ul>
+
+<h2><a name="overload"> Symptoms of Postfix SMTP server overload </a></h2>
+
+<p> Under normal conditions, Postfix responds immediately when a
+remote SMTP client connects. The time needed to deliver mail to
+Postfix may depend on how busy the CPU or disk are, but that should
+be noticeable only with very large messages.  Performance degrades
+more dramatically when the number of remote SMTP clients exceeds
+the number of Postfix SMTP server processes.  When a client connects
+while all server processes are busy, the client must wait until a
+server process becomes available. </p>
+
+<p> Overload may be caused by a legitimate mail (example: a DNS
+registrar opens a new zone for registrations), by mistake (mail
+explosion caused by a forwarding loop) or by illegitimate mail (worm
+outbreak, botnet, or other malware activity).  Symptoms of Postfix
+SMTP mail server overload are: </p>
+
+<ul>
+
+<li> <p> Postfix logs a warning that all server ports are busy: </p>
+
+<pre>
+Oct  3 20:39:27 spike postfix/master[28905]: warning: service "smtp"
+ (25) has reached its process limit "30": new clients may experience
+ noticeable delays
+Oct  3 20:39:27 spike postfix/master[28905]: warning: to avoid this
+ condition, increase the process count in master.cf or reduce the
+ service time per client
+</pre>
+
+<li> <p> Remote SMTP clients experience a long delay before Postfix
+sends the "220 hostname.example.com ESMTP Postfix" greeting.  If
+this affects end-user mail clients, enable the "submission" service
+entry in master.cf (present since Postfix 2.1), and tell users to
+connect to this instead of the public SMTP service. </p>
+
+<li> <p> The Postfix SMTP server logs an increased number of "lost
+connection after CONNECT" events. This happens because remote SMTP
+clients disconnect before Postfix answers the connection. </p>
+
+</ul>
+
+<p> NOTE: The last two symptoms also happen without overload. </p>
+
+<ul>
+
+<li> <p> Broken DNS also causes lengthy delays before "220
+hostname.example.com
+..." while the Postfix SMTP server tries to look up the client's
+hostname.  </p>
+
+<li> <p> A portscan for open SMTP ports also results in "lost
+connection ..." logfile messages. </p>
+
+</ul>
+
+<p> Legitimate mail that doesn't get through during an episode of
+overload is not necessarily lost. It should still arrive once the
+situation returns to normal, as long as the overload condition is
+temporary.  </p>
+
+<h2><a name="concurrency"> Service more SMTP clients at the same time </a> </h2>
+
+<p> To service more SMTP clients simultaneously, you need to increase
+the number of SMTP server processes. This will improve the
+responsiveness for remote SMTP clients, as long as the server machine
+has enough hardware and software resources to run the additional
+processes, and as long as the file system can keep up with the
+additional load. </p>
+
+<ul>
+
+<li> <p> You increase the number of SMTP server processes either
+by increasing the default_process_limit in main.cf (line 3 below),
+or by increasing the SMTP server's "maxproc" field in master.cf
+(line 10 below).  Either way, you need to issue a "postfix reload"
+command to make the change effective.  </p>
+
+<li> <p> Process limits above 1000 require Postfix version 2.4 or
+later, and an operating system that supports kernel-based event
+filters (BSD kqueue(2), Linux epoll(4), or Solaris /dev/poll).
+</p>
+
+<li> <p> You can reduce the Postfix memory footprint by using cdb:
+lookup tables instead of Berkeley DB. </p>
+
+<pre>
+ 1 /etc/postfix/main.cf:
+ 2     # Raise the global process limit, 100 since Postfix 2.0.
+ 3     default_process_limit = 200
+ 4
+ 5 /etc/postfix/master.cf:
+ 6     # =============================================================
+ 7     # service type  private unpriv  chroot  wakeup  maxproc command
+ 8     # =============================================================
+ 9     # Raise the SMTP service process limit only.
+10     smtp      inet  n       -       n       -       200     smtpd
+</pre>
+
+<li> <p> NOTE: older versions of the SMTPD_POLICY_README document
+contain a mistake: they configure a fixed number of policy daemon
+processes.  When you raise the SMTP server's "maxproc" field in
+master.cf, SMTP server processes will report problems when connecting
+to policy server processes, because there aren't enough of them.
+Examples of errors are "connection refused" or "operation timed
+out".  To fix, edit master.cf and specify a zero "maxproc" field
+in all policy server entries; see line 6 in the example below.
+Issue a "postfix reload" command to make the change effective.  </p>
+
+<pre>
+1 /etc/postfix/master.cf:
+2     # =============================================================
+3     # service type  private unpriv  chroot  wakeup  maxproc command
+4     # =============================================================
+5     # Disable the policy service process limit.
+6     policy    unix  -       n       n       -       0       spawn
+7         user=nobody argv=/some/where/policy-server
+</pre>
+
+</ul>
+
+<h2><a name="time"> Spend less time per SMTP client </a></h2>
+
+<p> When increasing the number of SMTP server processes is not
+practical, you can improve Postfix server responsiveness by eliminating
+unnecessary work. When Postfix spends less time per SMTP session, the
+same number of SMTP server processes can service more clients in
+the same amount of time. </p>
+
+<ul>
+
+<li> <p> Eliminate non-functional RBL lookups (blocklists that are
+no longer in operation). These lookups can degrade performance.
+Postfix logs a warning when an RBL server does not respond. </p>
+
+<li> <p> Eliminate redundant RBL lookups (people often use multiple
+Spamhaus RBLs that include each other).  To find out whether RBLs
+include other RBLs, look up the websites that document the RBL's
+policies. </p>
+
+<li> <p> Eliminate header_checks and body_checks, and keep just a few
+emergency patterns to block the latest worm explosion or backscatter
+mail.  See BACKSCATTER_README for examples of the latter.
+
+<li> <p> Group your header_checks and body_checks patterns to avoid
+unnecessary pattern matching operations.
+
+<pre>
+ 1  /etc/postfix/header_checks:
+ 2      if /^Subject:/
+ 3      /^Subject: virus found in mail from you/ reject
+ 4      /^Subject: ..../ ....
+ 5      endif
+ 6  
+ 7      if /^Received:/
+ 8      /^Received: from (postfix\.org) / reject forged client name in received header: $1
+ 9      /^Received: from .../ ....
+10      endif
+</pre>
+
+</ul>
+
+<h2><a name="hangup"> Disconnect suspicious SMTP clients </a></h2>
+
+<p> Under conditions of overload you can improve Postfix SMTP server
+responsiveness by hanging up on suspicious clients, so that other
+clients get a chance to talk to Postfix.  </p>
+
+<ul>
+
+<li> <p> Use "421" reply codes for botnet-related RBLs or for
+selected non-RBL restrictions. This causes Postfix 2.3 and later
+to disconnect immediately without waiting for the remote SMTP
+client to send a QUIT command. </p>
+
+<p> You can set individual reject codes for RBLs, and for individual
+responses from a specific RBL. We'll use zen.spamhaus.org as an
+example; by the time you read this document, details may have
+changed.  Right now, their documents say that a response of 127.0.0.10
+or 127.0.0.11 indicates a dynamic client IP address, which means
+that the machine is probably running a bot of some kind.  To give
+a 421 response instead of the default 554 response, use something
+like: </p>
+
+<pre>
+ 1  /etc/postfix/main.cf:
+ 2      smtpd_client_restrictions =
+ 3         permit_mynetworks
+ 4         reject_rbl_client zen.spamhaus.org=127.0.0.10
+ 5         reject_rbl_client zen.spamhaus.org=127.0.0.11
+ 6         reject_rbl_client zen.spamhaus.org
+ 7  
+ 8      rbl_reply_maps = hash:/etc/postfix/rbl_reply_maps
+ 9  
+10  /etc/postfix/rbl_reply_maps:
+11      zen.spamhaus.org=127.0.0.10 421 4.7.1 Service unavailable;
+12       $rbl_class [$rbl_what] blocked using
+13       $rbl_domain${rbl_reason?; $rbl_reason}
+14  
+15      zen.spamhaus.org=127.0.0.11 421 4.7.1 Service unavailable;
+16       $rbl_class [$rbl_what] blocked using
+17       $rbl_domain${rbl_reason?; $rbl_reason}
+</pre>
+
+<p> Although the above shows three RBL lookups (lines 4-6), Postfix
+will still only do a single DNS query, so the performance difference
+is negligible. </p>
+
+<p> The down-side of sending 421 instead of the default 554 is that
+it works only for zombies and other malware. If the client is running
+a real MTA, then it may connect again several times until the mail
+expires in its queue. When this is a problem, stick with the default
+554 reply, and use "smtpd_hard_error_limit = 1" as described below.
+</p>
+
+<p> With Postfix 2.5, or with earlier releases that contain the
+stress-adaptive behavior patch, you can turn on the above under
+overload by replacing line 8 with: </p>
+
+<pre>
+ 8      rbl_reply_maps = ${stress?hash:/etc/postfix/rbl_reply_maps}
+</pre>
+
+<p> More information about automatic stress-adaptive behavior is
+at the end of this document. </p>
+
+</ul>
+
+<h2><a name="desperate"> Take desperate measures </a></h2>
+
+<p> The following measures will still allow <b>most</b> legitimate
+clients to connect and send mail, but may affect some legitimate
+clients. </p>
+
+<ul>
+
+<li> <p> Reduce smtpd_timeout (default: 300s). Experience on the
+postfix-users list from a variety of sysadmins shows that reducing
+the "normal" smtpd_timeout to 60s is unlikely to affect legitimate
+clients. However, it is unlikely to become the Postfix default
+because it's not RFC compliant. Setting smtpd_timeout to 10s (line
+2 below) or even 5s under stress will still allow <b>most</b>
+legitimate clients to connect and send mail, but may delay mail
+from some clients.  No mail should be lost, as long as this measure
+is used only temporarily.  </p>
+
+<li> <p> Reduce smtpd_hard_error_limit (default: 20). Setting this
+to 1 under stress (line 3 below) helps by disconnecting clients
+after a single error, giving other clients a chance to connect.
+However, this may cause significant delays with legitimate mail,
+such as a mailing list that contains a few no-longer-active user
+names that didn't bother to unsubscribe. No mail should be lost,
+as long as this measure is used only temporarily. </p>
+
+<li> <p> Disable remote SMTP client hostname lookups, so that all
+SMTP client hostnames become "unknown" (line 5 below). This feature
+was introduced with Postfix 2.3. Unfortunately, this measure is
+more problematic than the other ones proposed sofar.  First, this
+will result in loss of mail when you use hostname-based access rules
+that reject mail from "unknown" SMTP clients (examples:
+reject_unknown_client_hostname, reject_unknown_reverse_client_hostname).
+Second, this may result in loss of mail when you subject "unknown"
+SMTP clients to additional restrictions such as reject_unverified_sender.
+</p>
+
+</ul>
+
+<blockquote>
+<pre>
+1  /etc/postfix/main.cf:
+2      smtpd_timeout = 10
+3      smtpd_hard_error_limit = 1
+4      # Caution: line 5 may trigger REJECTs by hostname-based access rules 
+5      smtpd_peername_lookup = no
+</pre>
+</blockquote>
+
+<p> Except with the last measure, no mail should be lost, as long
+as these measures are used only temporarily. The next section of
+this document introduces a way to automate this process. </p>
+
+<h2><a name="adapt"> Make Postfix behavior stress-adaptive </a></h2>
+
+<p> Postfix version 2.5 introduces automatic stress-adaptive behavior.
+This is also available as an add-on patch for Postfix versions 2.4
+and 2.3 from the mirrors listed at http://www.postfix.org/download.html.
+</p>
+
+<p> It works as follows. When a "public" network service runs into
+an "all server ports are busy" condition, the master(8) daemon logs
+a warning, restarts the service (without interrupting existing
+network sessions), and runs the service with "-o stress=yes" on the
+command line. Normally, it runs a stress-adaptive service with "-o
+stress=" on the command line (i.e. with an empty parameter value).
+Other services never have "-o stress" parameters on the command
+line, including services that listen on a loopback interface only.
+</p>
+
+<p> The stress pseudo-parameter value is the key to making main.cf
+parameter settings stress adaptive: </p>
+
+<blockquote>
+<pre>
+1  /etc/postfix/main.cf:
+2      smtpd_timeout = ${stress?10}${stress:300}
+3      smtpd_hard_error_limit = ${stress?1}${stress:20}
+</pre>
+</blockquote>
+
+<p> Translation: <p>
+
+<ul>
+
+<li> <p> Line 2: under conditions of stress, use an smtpd_timeout
+value of 10 seconds instead of the default 300 seconds,
+
+<li> <p> Line 3: under conditions of stress, use an smtpd_hard_error_limit
+of 1 instead of the default 20. </p>
+
+</ul>
+
+<p> The syntax of ${name?value} and ${name:value} is explained at
+the beginning of the postconf(5) manual page. </p>
+
+<p> NOTE: Please keep in mind that the stress-adaptive feature is
+a fairly desperate measure to keep <b>some</b> legitimate mail
+flowing under overload conditions.  If a site is reaching the SMTP
+server process limit when there isn't an attack or bot flood
+occurring, then either the process limit needs to be raised or more
+hardware needs to be added.  </p>
+
+<h2><a name="feature"> Detecting support for stress-adaptive behavior </a></h2>
+
+<p> To find out if your Postfix installation supports stress-adaptive
+behavior, use the "ps" command, and look for the smtpd processes.
+Postfix has stress-adaptive support when you see "-o stress=" or
+"-o stress=yes" command-line options. Remember that Postfix never
+enables stress-adaptive behavior on servers that listen on local
+addresses only. </p>
+
+<p> The following example is for FreeBSD or Linux. On Solaris, HP-UX
+and other System-V flavors, use "ps -ef" instead of "ps ax". </p>
+
+<blockquote>
+<pre>
+$ ps ax|grep smtpd
+83326  ??  S      0:00.28 smtpd -n smtp -t inet -u -c -o stress=
+84345  ??  Ss     0:00.11 /usr/bin/perl /usr/libexec/postfix/smtpd-policy.pl
+</pre>
+</blockquote>
+
+<p> You can't use postconf(1) to detect stress-adaptive support.
+The postconf(1) command ignores the existence of the stress parameter
+in main.cf, because the parameter has no effect there.  Command-line
+"-o parameter" settings always take precedence over main.cf parameter
+settings.  <p>
+
+<p> If you configure stress-adaptive behavior in main.cf when it
+isn't supported, nothing bad will happen.  The processes will run
+as if the stress parameter always has an empty value. </p>
+
+<h2><a name="forcing"> Forcing stress-adaptive behavior on or off </a></h2>
+
+<p> You can manually force stress-adaptive behavior on, by adding
+a "-o stress=yes" command-line option in master.cf. This can be
+useful for testing overrides on the SMTP service. Issue "postfix
+reload" to make the change effective.  </p>
+
+<blockquote>
+<pre>
+1 /etc/postfix/master.cf:
+2     # =============================================================
+3     # service type  private unpriv  chroot  wakeup  maxproc command
+4     # =============================================================
+5     # 
+6     smtp      inet  n       -       n       -       -       smtpd
+7         -o stress=yes
+8         -o . . .
+</pre>
+</blockquote>
+
+<p> To permanently force stress-adaptive behavior off with a specific
+service, specify "-o stress=" on its command line.  This may be
+desirable for the "submission" service. Issue "postfix reload" to
+make the change effective.  </p>
+
+<blockquote>
+<pre>
+1 /etc/postfix/master.cf:
+2     # =============================================================
+3     # service type  private unpriv  chroot  wakeup  maxproc command
+4     # =============================================================
+5     # 
+6     submission inet n       -       n       -       -       smtpd
+7         -o stress=
+8         -o . . .
+</pre>
+</blockquote>
+
+<h2><a name="credits"> Credits </a></h2>
+
+<ul>
+
+<li>  Thanks to the postfix-users mailing list members for sharing
+early experiences with the stress-adaptive feature.
+
+<li>  The RBL example and several other paragraphs of text were
+adapted from postfix-users postings by Noel Jones.
+
+<li>  Wietse implemented stress-adaptive behavior as the smallest
+possible patch while he should be working on other things.
+
+</ul>
+
+</body> </html>
index ae73e1df0d4e254f2412e5a25c5bd8dc7890801c..33f428e5fe207f4807210694bd646ccc4d6d2610 100644 (file)
 #      For a discussion of specific pattern or flags syntax,
 #      see \fBpcre_table\fR(5) or \fBregexp_table\fR(5), respectively.
 # .IP "\fB/\fIpattern\fB/\fIflags action\fR"
-#      When \fIpattern\fR matches the input string, execute
+#      When /\fIpattern\fR/ matches the input string, execute
 #      the corresponding \fIaction\fR. See below for a list
 #      of possible actions.
 # .IP "\fB!/\fIpattern\fB/\fIflags action\fR"
-#      When \fIpattern\fR does \fBnot\fR match the input string,
+#      When /\fIpattern\fR/ does \fBnot\fR match the input string,
 #      execute the corresponding \fIaction\fR.
 # .IP "\fBif /\fIpattern\fB/\fIflags\fR"
 # .IP "\fBendif\fR"
 #      Match the input string against the patterns between \fBif\fR
 #      and \fBendif\fR, if and only if the same input string also
-#      matches \fIpattern\fR. The \fBif\fR..\fBendif\fR can nest.
+#      matches /\fIpattern\fR/. The \fBif\fR..\fBendif\fR can nest.
 # .sp
 #      Note: do not prepend whitespace to patterns inside
 #      \fBif\fR..\fBendif\fR.
@@ -99,7 +99,7 @@
 # .IP "\fBendif\fR"
 #      Match the input string against the patterns between \fBif\fR
 #      and \fBendif\fR, if and only if the same input string does
-#      \fBnot\fR match \fIpattern\fR. The \fBif\fR..\fBendif\fR
+#      \fBnot\fR match /\fIpattern\fR/. The \fBif\fR..\fBendif\fR
 #      can nest.
 # .IP "blank lines and comments"
 #      Empty lines and whitespace-only lines are ignored, as
 #      action is useful for debugging and for testing a pattern
 #      before applying more drastic actions.
 # BUGS
+#      Empty lines never match, because some map types mis-behave
+#      when given a zero-length search string.  This limitation may
+#      be removed for regular expression tables in a future release.
+#
 #      Many people overlook the main limitations of header and body_checks
 #      rules.
 # .IP \(bu
index bc5ed79b404d90fbe9a0b59ab8f05135f46a8522..ce8fda2f1ced0d11318578aef758eb0f4ec3f9f8 100644 (file)
@@ -2360,6 +2360,11 @@ and later.</dd>
 
 <dd>The recipient's username. </dd>
 
+<dt><b>ORIGINAL_RECIPIENT</b></dt>
+
+<dd>The entire recipient address, before any address rewriting or
+aliasing.  </dd>
+
 <dt><b>RECIPIENT</b></dt>
 
 <dd>The full recipient address. </dd>
@@ -10633,3 +10638,39 @@ the hostname and IP address. The logging format is "host[address]:port".
 </p>
 
 <p> This feature is available in Postfix 2.5 and later. </p>
+
+%PARAM smtp_header_checks
+
+<p> Restricted header_checks(5) tables for the Postfix SMTP client.
+These tables are searched while mail is being delivered.  Actions
+that change the delivery time or destination are not available.
+</p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+%PARAM smtp_mime_header_checks
+
+<p> Restricted mime_header_checks(5) tables for the Postfix SMTP
+client. These tables are searched while mail is being delivered.
+Actions that change the delivery time or destination are not
+available.  </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+%PARAM smtp_nested_header_checks
+
+<p> Restricted nested_header_checks(5) tables for the Postfix SMTP
+client. These tables are searched while mail is being delivered.
+Actions that change the delivery time or destination are not
+available.  </p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
+
+%PARAM smtp_body_checks
+
+<p> Restricted body_checks(5) tables for the Postfix SMTP client.
+These tables are searched while mail is being delivered.  Actions
+that change the delivery time or destination are not available.
+</p>
+
+<p> This feature is available in Postfix 2.5 and later. </p>
index 881440680c049a72397fefa3f0035657082719c9..d077f3dc63fdc0e2cedae25147e7f958a1d9ec39 100644 (file)
@@ -28,7 +28,7 @@ SRCS  = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
        tok822_resolve.c tok822_rewrite.c tok822_tree.c trace.c \
        user_acl.c valid_mailhost_addr.c verify.c verify_clnt.c \
        verp_sender.c wildcard_inet_addr.c xtext.c delivered_hdr.c \
-       fold_addr.c
+       fold_addr.c header_body_checks.c
 OBJS   = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
        clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \
@@ -58,7 +58,7 @@ OBJS  = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        tok822_resolve.o tok822_rewrite.o tok822_tree.o trace.o \
        user_acl.o valid_mailhost_addr.o verify.o verify_clnt.o \
        verp_sender.o wildcard_inet_addr.o xtext.o delivered_hdr.o \
-       fold_addr.o
+       fold_addr.o header_body_checks.o
 HDRS   = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
        canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
        conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
@@ -82,7 +82,7 @@ HDRS  = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
        string_list.h strip_addr.h sys_exits.h timed_ipc.h tok822.h \
        trace.h user_acl.h valid_mailhost_addr.h verify.h verify_clnt.h \
        verp_sender.h wildcard_inet_addr.h xtext.h delivered_hdr.h \
-       fold_addr.h
+       fold_addr.h header_body_checks.h
 TESTSRC        = rec2stream.c stream2rec.c recdump.c
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -94,7 +94,7 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
        resolve_local rewrite_clnt stream2rec string_list tok822_parse \
        quote_821_local mail_conf_time mime_state strip_addr \
        verify_clnt xtext anvil_clnt scache ehlo_mask \
-       valid_mailhost_addr own_inet_addr
+       valid_mailhost_addr own_inet_addr header_body_checks
 
 LIBS   = ../../lib/libutil.a
 LIB_DIR        = ../../lib
@@ -271,13 +271,20 @@ valid_mailhost_addr: valid_mailhost_addr.c $(LIB) $(LIBS)
 own_inet_addr: own_inet_addr.c $(LIB) $(LIBS)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
 
+header_body_checks: header_body_checks.c $(LIB) $(LIBS)
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+
 tests: tok822_test mime_tests strip_addr_test tok822_limit_test \
        xtext_test scache_multi_test ehlo_mask_test \
-       namadr_list_test mail_conf_time_test
+       namadr_list_test mail_conf_time_test header_body_checks_tests
 
 mime_tests: mime_test mime_nest mime_8bit mime_dom mime_trunc mime_cvt \
        mime_cvt2 mime_cvt3 mime_garb1 mime_garb2 mime_garb3 mime_garb4
 
+header_body_checks_tests: header_body_checks_null_test \
+       header_body_checks_warn_test header_body_checks_prepend_test \
+       header_body_checks_ignore_test header_body_checks_replace_test
+
 root_tests: rewrite_clnt_test resolve_clnt_test
 
 tok822_test: tok822_parse tok822_parse.in tok822_parse.ref
@@ -416,6 +423,56 @@ mail_conf_time_test: mail_conf_time mail_conf_time.ref
        diff mail_conf_time.ref mail_conf_time.tmp
        rm -f mail_conf_time.tmp
 
+header_body_checks_null_test: header_body_checks header_body_checks_null.ref
+       ./header_body_checks "" "" "" "" \
+               <mime_test.in >header_body_checks_null.tmp 2>&1
+       cmp header_body_checks_null.ref header_body_checks_null.tmp
+       ./header_body_checks static:dunno static:dunno static:dunno static:dunno \
+               <mime_test.in >header_body_checks_null.tmp 2>&1
+       cmp header_body_checks_null.ref header_body_checks_null.tmp
+       ./header_body_checks static:ok static:ok static:ok static:ok \
+               <mime_test.in >header_body_checks_null.tmp 2>&1
+       cmp header_body_checks_null.ref header_body_checks_null.tmp
+       rm -f header_body_checks_null.tmp
+
+header_body_checks_warn_test: header_body_checks header_body_checks_warn.ref
+       ./header_body_checks static:warn static:warn static:warn static:warn \
+               <mime_test.in >header_body_checks_warn.tmp 2>&1
+       cmp header_body_checks_warn.ref header_body_checks_warn.tmp
+       rm -f header_body_checks_warn.tmp
+
+header_body_checks_prepend_test: header_body_checks header_body_checks_prepend.ref
+       echo /./ prepend header: head >header_body_checks_head
+       echo /./ prepend header: mime >header_body_checks_mime
+       echo /./ prepend header: nest >header_body_checks_nest
+       echo /./ prepend body >header_body_checks_body
+       ./header_body_checks regexp:header_body_checks_head regexp:header_body_checks_mime \
+               regexp:header_body_checks_nest regexp:header_body_checks_body \
+               <mime_test.in >header_body_checks_prepend.tmp 2>&1
+       cmp header_body_checks_prepend.ref header_body_checks_prepend.tmp
+       rm -f header_body_checks_prepend.tmp header_body_checks_head header_body_checks_mime header_body_checks_nest header_body_checks_body
+
+# Note: the IGNORE action will not strip empty lines. Postfix maps
+# currently never see null query strings because some map types raise
+# errors. We can eliminate this restriction by allowing individual
+# map types to advertise whether they can handle null queries.
+header_body_checks_ignore_test: header_body_checks header_body_checks_ignore.ref
+       ./header_body_checks static:ignore static:ignore static:ignore static:ignore \
+               <mime_test.in >header_body_checks_ignore.tmp 2>&1
+       cmp header_body_checks_ignore.ref header_body_checks_ignore.tmp
+       rm -f header_body_checks_ignore.tmp header_body_checks_head header_body_checks_mime header_body_checks_nest header_body_checks_body
+
+header_body_checks_replace_test: header_body_checks header_body_checks_replace.ref
+       echo /./ replace header: head >header_body_checks_head
+       echo /./ replace header: mime >header_body_checks_mime
+       echo /./ replace header: nest >header_body_checks_nest
+       echo /./ replace body >header_body_checks_body
+       ./header_body_checks regexp:header_body_checks_head regexp:header_body_checks_mime \
+               regexp:header_body_checks_nest regexp:header_body_checks_body \
+               <mime_test.in >header_body_checks_replace.tmp 2>&1
+       cmp header_body_checks_replace.ref header_body_checks_replace.tmp
+       rm -f header_body_checks_replace.tmp header_body_checks_head header_body_checks_mime header_body_checks_nest header_body_checks_body
+
 printfck: $(OBJS) $(PROG)
        rm -rf printfck
        mkdir printfck
@@ -709,6 +766,21 @@ delivered_hdr.o: quote_822_local.h
 delivered_hdr.o: quote_flags.h
 delivered_hdr.o: rec_type.h
 delivered_hdr.o: record.h
+header_body_checks.o: ../../include/argv.h
+header_body_checks.o: ../../include/dict.h
+header_body_checks.o: ../../include/msg.h
+header_body_checks.o: ../../include/mymalloc.h
+header_body_checks.o: ../../include/sys_defs.h
+header_body_checks.o: ../../include/vbuf.h
+header_body_checks.o: ../../include/vstream.h
+header_body_checks.o: ../../include/vstring.h
+header_body_checks.o: header_body_checks.c
+header_body_checks.o: header_body_checks.h
+header_body_checks.o: header_opts.h
+header_body_checks.o: is_header.h
+header_body_checks.o: maps.h
+header_body_checks.o: mime_state.h
+header_body_checks.o: rec_type.h
 dict_ldap.o: ../../include/argv.h
 dict_ldap.o: ../../include/binhash.h
 dict_ldap.o: ../../include/dict.h
diff --git a/postfix/src/global/header_body_checks.c b/postfix/src/global/header_body_checks.c
new file mode 100644 (file)
index 0000000..1ead675
--- /dev/null
@@ -0,0 +1,637 @@
+/*++
+/* NAME
+/*     header_body_checks 3
+/* SUMMARY
+/*     header/body checks
+/* SYNOPSIS
+/*     #include <header_body_checks.h>
+/*
+/*     typedef struct {
+/*             void    (*logger) (void *context, const char *action,
+/*                             const char *where, const char *line,
+/*                             const char *optional_text);
+/*             void    (*prepend) (void *context, int rec_type,
+/*                             const char *buf, ssize_t len, off_t offset);
+/*             char    *(*extend) (void *context, const char *command,
+/*                             int cmd_len, const char *cmd_args,
+/*                             const char *where, const char *line,
+/*                             ssize_t line_len, off_t offset);
+/*     } HBC_CALL_BACKS;
+/*
+/*     HBC_CHECKS *hbc_header_checks_create(
+/*                     header_checks_name, header_checks_value
+/*                     mime_header_checks_name, mime_header_checks_value,
+/*                     nested_header_checks_name, nested_header_checks_value,
+/*                     call_backs)
+/*     const char *header_checks_name;
+/*     const char *header_checks_value;
+/*     const char *mime_header_checks_name;
+/*     const char *mime_header_checks_value;
+/*     const char *nested_header_checks_name;
+/*     const char *nested_header_checks_value;
+/*     HBC_CALL_BACKS *call_backs;
+/*
+/*     HBC_CHECKS *hbc_body_checks_create(
+/*                     body_checks_name, body_checks_value,
+/*                     call_backs)
+/*     const char *body_checks_name;
+/*     const char *body_checks_value;
+/*     HBC_CALL_BACKS *call_backs;
+/*
+/*     char    *hbc_header_checks(context, hbc, header_class, hdr_opts, header)
+/*     void    *context;
+/*     HBC_CHECKS *hbc;
+/*     int     header_class;
+/*     HEADER_OPTS *hdr_opts;
+/*     VSTRING *header;
+/*
+/*     char    *hbc_body_checks(context, hbc, body_line, body_line_len)
+/*     void    *context;
+/*     HBC_CHECKS *hbc;
+/*     const char *body_line;
+/*     ssize_t body_line_len;
+/*
+/*     void    hbc_header_checks_free(hbc)
+/*     HBC_CHECKS *hbc;
+/*
+/*     void    hbc_body_checks_free(hbc)
+/*     HBC_CHECKS *hbc;
+/* DESCRIPTION
+/*     This module implements header_checks and body_checks.
+/*     Actions are executed while mail is being delivered. The
+/*     following actions are recognized: WARN, REPLACE, PREPEND,
+/*     IGNORE, DUNNO, and OK. These actions are safe for use in
+/*     delivery agents.
+/*
+/*     Other actions may be supplied via the extension mechanism
+/*     described below.  For example, actions that change the
+/*     message delivery time or destination. Such actions do not
+/*     make sense in delivery agents, but they can be appropriate
+/*     in, for example, before-queue filters.
+/*
+/*     hbc_header_checks_create() creates a context for header
+/*     inspection. This function is typically called once during
+/*     program initialization.  The result is a null pointer when
+/*     all _value arguments specify zero-length strings; in this
+/*     case, hbc_header_checks() and hbc_header_checks_free() must
+/*     not be called.
+/*
+/*     hbc_header_checks() inspects the specified logical header.
+/*     The result is either the original header, HBC_CHECK_STAT_IGNORE
+/*     (meaning: discard the header) or a new header (meaning:
+/*     replace the header and destroy the new header with myfree()).
+/*
+/*     hbc_header_checks_free() returns memory to the pool.
+/*
+/*     hbc_body_checks_create(), dbhc_body_checks(), dbhc_body_free()
+/*     perform similar functions for body lines.
+/*
+/*     Arguments:
+/* .IP body_line
+/*     One line of body text.
+/* .IP body_line_len
+/*     Body line length.
+/* .IP call_backs
+/*     Table with call-back function pointers. This argument is
+/*     not copied.  Note: the description below is not necessarily
+/*     in data structure order.
+/* .RS
+/* .IP logger
+/*     Call-back function for logging an action with the action's
+/*     name in lower case, a location within a message ("header"
+/*     or "body"), the content of the header or body line that
+/*     triggered the action, and optional text or a zero-length
+/*     string. This call-back feature must be specified.
+/* .IP prepend
+/*     Call-back function for the PREPEND action. The arguments
+/*     are the same as those of mime_state(3) body output call-back
+/*     functions.  Specify a null pointer to disable this action.
+/* .IP extend
+/*     Call-back function that logs and executes other actions.
+/*     This function receives as arguments the command name and
+/*     name length, the command arguments if any, the location
+/*     within the message ("header" or "body"), the content and
+/*     length of the header or body line that triggered the action,
+/*     and the input byte offset within the current header or body
+/*     segment.  The result value is either the original line
+/*     argument, HBC_CHECKS_STAT_IGNORE (delete the line from the
+/*     input stream) or HBC_CHECK_STAT_UNKNOWN (the command was
+/*     not recognized).  Specify a null pointer to disable this
+/*     feature.
+/* .RE
+/* .IP context
+/*     Application context for call-back functions specified with the
+/*     call_backs argument.
+/* .IP header
+/*     A logical message header. Lines within a multi-line header
+/*     are separated by a newline character.
+/* .IP "header_checks_name, mime_header_checks_name"
+/* .IP "nested_header_checks_name, body_checks_name"
+/*     The main.cf configuration parameter names for header and body
+/*     map lists.
+/* .IP "header_checks_value, mime_header_checks_value"
+/* .IP "nested_header_checks_value, body_checks_value"
+/*     The values of main.cf configuration parameters for header and body
+/*     map lists. Specify a zero-length string to disable a specific list.
+/* .IP header_class
+/*     A number in the range MIME_HDR_FIRST..MIME_HDR_LAST.
+/* .IP hbc
+/*     A handle created with hbc_header_checks_create() or
+/*     hbc_body_checks_create().
+/* .IP hdr_opts
+/*     Message header properties.
+/* SEE ALSO
+/*     msg(3) diagnostics interface
+/* DIAGNOSTICS
+/*     Fatal errors: memory allocation problem.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <ctype.h>
+#include <string.h>
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mime_state.h>
+#include <rec_type.h>
+#include <is_header.h>
+#include <cleanup_user.h>
+#include <dsn_util.h>
+#include <header_body_checks.h>
+
+/* Application-specific. */
+
+ /*
+  * Something that is guaranteed to be different from a real string result
+  * from header/body_checks.
+  */
+const char hbc_checks_unknown;
+
+ /*
+  * Header checks are stored as an array of HBC_MAP_INFO structures, one
+  * structure for each header class (MIME_HDR_PRIMARY, MIME_HDR_MULTIPART, or
+  * MIME_HDR_NESTED).
+  * 
+  * Body checks are stored as one single HBC_MAP_INFO structure, because we make
+  * no distinction between body segments.
+  */
+#define HBC_HEADER_INDEX(class)        ((class) - MIME_HDR_FIRST)
+#define HBC_BODY_INDEX (0)
+
+#define HBC_INIT(hbc, index, name, value) do { \
+       HBC_MAP_INFO *_mp = (hbc)->map_info + (index); \
+       if (*(value) != 0) { \
+           _mp->map_class = (name); \
+           _mp->maps = maps_create((name), (value), DICT_FLAG_LOCK); \
+       } else { \
+           _mp->map_class = 0; \
+           _mp->maps = 0; \
+       } \
+    } while (0)
+
+/* How does the action routine know where we are? */
+
+#define        HBC_CTXT_HEADER "header"
+#define HBC_CTXT_BODY  "body"
+
+/* Silly little macros. */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* hbc_action - act upon a header/body match */
+
+static char *hbc_action(void *context, HBC_CALL_BACKS *cb,
+                               const char *map_class, const char *where,
+                               const char *cmd, const char *line,
+                               ssize_t line_len, off_t offset)
+{
+    const char *cmd_args = cmd + strcspn(cmd, " \t");
+    int     cmd_len = cmd_args - cmd;
+    char   *ret;
+
+    /*
+     * XXX We don't delegate action logging to the action call-back
+     * functions, because actions without call-back must be logged here
+     * anyway. This means that some actions must report back whether the
+     * action should be logged. This is admittedly a little clumsy.
+     * 
+     * XXX We don't use a hash table for action lookup. Mail rarely triggers an
+     * action, and mail that triggers multiple actions is even rarer.
+     */
+    while (*cmd_args && ISSPACE(*cmd_args))
+       cmd_args++;
+
+#define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0)
+
+    if (cb->extend
+       && (ret = cb->extend(context, cmd, cmd_len, cmd_args, where, line,
+                            line_len, offset)) != HBC_CHECKS_STAT_UNKNOWN)
+       return (ret);
+
+    if (STREQUAL(cmd, "WARN", cmd_len)) {
+       cb->logger(context, "warning", where, line, cmd_args);
+       return ((char *) line);
+    }
+    if (STREQUAL(cmd, "REPLACE", cmd_len)) {
+       if (*cmd_args == 0) {
+           msg_warn("REPLACE action without text in %s map", map_class);
+           return ((char *) line);
+       } else if (strcmp(where, HBC_CTXT_HEADER) == 0
+                  && !is_header(cmd_args)) {
+           msg_warn("bad REPLACE header text \"%s\" in %s map -- "
+                  "need \"headername: headervalue\"", cmd_args, map_class);
+           return ((char *) line);
+       } else {
+           cb->logger(context, "replace", where, line, cmd_args);
+           return (mystrdup(cmd_args));
+       }
+    }
+    if (cb->prepend && STREQUAL(cmd, "PREPEND", cmd_len)) {
+       if (*cmd_args == 0) {
+           msg_warn("PREPEND action without text in %s map", map_class);
+       } else if (strcmp(where, HBC_CTXT_HEADER) == 0
+                  && !is_header(cmd_args)) {
+           msg_warn("bad PREPEND header text \"%s\" in %s map -- "
+                  "need \"headername: headervalue\"", cmd_args, map_class);
+       } else {
+           cb->logger(context, "prepend", where, line, cmd_args);
+           cb->prepend(context, REC_TYPE_NORM, cmd_args, strlen(cmd_args), offset);
+       }
+       return ((char *) line);
+    }
+    /* Allow and ignore optional text after the action. */
+
+    if (STREQUAL(cmd, "IGNORE", cmd_len))
+       /* XXX Not logged for compatibility with cleanup(8). */
+       return (HBC_CHECKS_STAT_IGNORE);
+
+    if (STREQUAL(cmd, "DUNNO", cmd_len)                /* preferred */
+       ||STREQUAL(cmd, "OK", cmd_len))         /* compatibility */
+       return ((char *) line);
+
+    msg_warn("unsupported command in %s map: %s", map_class, cmd);
+    return ((char *) line);
+}
+
+/* hbc_header_checks - process one complete header line */
+
+char   *hbc_header_checks(void *context, HBC_CHECKS *hbc, int header_class,
+                                 HEADER_OPTS *hdr_opts,
+                                 VSTRING *header, off_t offset)
+{
+    const char *myname = "hbc_header_checks";
+    const char *action;
+    HBC_MAP_INFO *mp;
+
+    if (msg_verbose)
+       msg_info("%s: '%.30s'", myname, STR(header));
+
+    /*
+     * XXX This is for compatibility with the cleanup(8) server.
+     */
+    if (hdr_opts && (hdr_opts->flags & HDR_OPT_MIME))
+       header_class = MIME_HDR_MULTIPART;
+
+    mp = hbc->map_info + HBC_HEADER_INDEX(header_class);
+
+    if (mp->maps != 0 && (action = maps_find(mp->maps, STR(header), 0)) != 0) {
+       return (hbc_action(context, hbc->call_backs,
+                          mp->map_class, HBC_CTXT_HEADER, action,
+                          STR(header), LEN(header), offset));
+    } else {
+       return (STR(header));
+    }
+}
+
+/* hbc_body_checks - inspect one body record */
+
+char   *hbc_body_checks(void *context, HBC_CHECKS *hbc, const char *line,
+                               ssize_t len, off_t offset)
+{
+    const char *myname = "hbc_body_checks";
+    const char *action;
+    HBC_MAP_INFO *mp;
+
+    if (msg_verbose)
+       msg_info("%s: '%.30s'", myname, line);
+
+    mp = hbc->map_info;
+
+    if ((action = maps_find(mp->maps, line, 0)) != 0) {
+       return (hbc_action(context, hbc->call_backs,
+                          mp->map_class, HBC_CTXT_BODY, action,
+                          line, len, offset));
+    } else {
+       return ((char *) line);
+    }
+}
+
+/* hbc_header_checks_create - create header checking context */
+
+HBC_CHECKS *hbc_header_checks_create(const char *header_checks_name,
+                                            const char *header_checks_value,
+                                       const char *mime_header_checks_name,
+                                      const char *mime_header_checks_value,
+                                     const char *nested_header_checks_name,
+                                    const char *nested_header_checks_value,
+                                            HBC_CALL_BACKS *call_backs)
+{
+    HBC_CHECKS *hbc;
+
+    /*
+     * Optimize for the common case.
+     */
+    if (*header_checks_value == 0 && *mime_header_checks_value == 0
+       && *nested_header_checks_value == 0) {
+       return (0);
+    } else {
+       hbc = (HBC_CHECKS *) mymalloc(sizeof(*hbc)
+                + (MIME_HDR_LAST - MIME_HDR_FIRST) * sizeof(HBC_MAP_INFO));
+       hbc->call_backs = call_backs;
+       HBC_INIT(hbc, HBC_HEADER_INDEX(MIME_HDR_PRIMARY),
+                header_checks_name, header_checks_value);
+       HBC_INIT(hbc, HBC_HEADER_INDEX(MIME_HDR_MULTIPART),
+                mime_header_checks_name, mime_header_checks_value);
+       HBC_INIT(hbc, HBC_HEADER_INDEX(MIME_HDR_NESTED),
+                nested_header_checks_name, nested_header_checks_value);
+       return (hbc);
+    }
+}
+
+/* hbc_body_checks_create - create body checking context */
+
+HBC_CHECKS *hbc_body_checks_create(const char *body_checks_name,
+                                          const char *body_checks_value,
+                                          HBC_CALL_BACKS *call_backs)
+{
+    HBC_CHECKS *hbc;
+
+    /*
+     * Optimize for the common case.
+     */
+    if (*body_checks_value == 0) {
+       return (0);
+    } else {
+       hbc = (HBC_CHECKS *) mymalloc(sizeof(*hbc));
+       hbc->call_backs = call_backs;
+       HBC_INIT(hbc, HBC_BODY_INDEX, body_checks_name, body_checks_value);
+       return (hbc);
+    }
+}
+
+/* _hbc_checks_free - destroy header/body checking context */
+
+void    _hbc_checks_free(HBC_CHECKS *hbc, ssize_t len)
+{
+    HBC_MAP_INFO *mp;
+
+    for (mp = hbc->map_info; mp < hbc->map_info + len; mp++)
+       if (mp->maps)
+           maps_free(mp->maps);
+    myfree((char *) hbc);
+}
+
+ /*
+  * Test program. Specify the four maps on the command line, and feed a
+  * MIME-formatted message on stdin.
+  */
+
+#ifdef TEST
+
+#include <stdlib.h>
+#include <stringops.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <rec_streamlf.h>
+
+typedef struct {
+    HBC_CHECKS *header_checks;
+    HBC_CHECKS *body_checks;
+    HBC_CALL_BACKS *call_backs;
+    VSTREAM *fp;
+    VSTRING *buf;
+    const char *queueid;
+    int     recno;
+} HBC_TEST_CONTEXT;
+
+/*#define REC_LEN      40*/
+#define REC_LEN        1024
+
+/* log_cb - log action with context */
+
+static void log_cb(void *context, const char *action, const char *where,
+                          const char *content, const char *text)
+{
+    const HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
+
+    if (*text) {
+       msg_info("%s: %s: %s %.200s: %s",
+                dp->queueid, action, where, content, text);
+    } else {
+       msg_info("%s: %s: %s %.200s",
+                dp->queueid, action, where, content);
+    }
+}
+
+/* out_cb - output call-back */
+
+static void out_cb(void *context, int rec_type, const char *buf,
+                          ssize_t len, off_t offset)
+{
+    const HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
+
+    vstream_fwrite(dp->fp, buf, len);
+    VSTREAM_PUTC('\n', dp->fp);
+    vstream_fflush(dp->fp);
+}
+
+/* head_out - MIME_STATE header call-back */
+
+static void head_out(void *context, int header_class, HEADER_OPTS *header_info,
+                            VSTRING *buf, off_t offset)
+{
+    HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
+    char   *out;
+
+    if (dp->header_checks == 0
+       || (out = hbc_header_checks(context, dp->header_checks, header_class,
+                                   header_info, buf, offset)) == STR(buf)) {
+       vstring_sprintf(dp->buf, "%d %s %ld\t|%s",
+                       dp->recno,
+                       header_class == MIME_HDR_PRIMARY ? "MAIN" :
+                       header_class == MIME_HDR_MULTIPART ? "MULT" :
+                       header_class == MIME_HDR_NESTED ? "NEST" :
+                       "ERROR", (long) offset, STR(buf));
+       out_cb(dp, REC_TYPE_NORM, STR(dp->buf), LEN(dp->buf), offset);
+    } else if (out != 0) {
+       vstring_sprintf(dp->buf, "%d %s %ld\t|%s",
+                       dp->recno,
+                       header_class == MIME_HDR_PRIMARY ? "MAIN" :
+                       header_class == MIME_HDR_MULTIPART ? "MULT" :
+                       header_class == MIME_HDR_NESTED ? "NEST" :
+                       "ERROR", (long) offset, out);
+       out_cb(dp, REC_TYPE_NORM, STR(dp->buf), LEN(dp->buf), offset);
+       myfree(out);
+    }
+    dp->recno += 1;
+}
+
+/* header_end - MIME_STATE end-of-header call-back */
+
+static void head_end(void *context)
+{
+    HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
+
+    out_cb(dp, 0, "HEADER END", sizeof("HEADER END") - 1, 0);
+}
+
+/* body_out - MIME_STATE body line call-back */
+
+static void body_out(void *context, int rec_type, const char *buf,
+                            ssize_t len, off_t offset)
+{
+    HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
+    char   *out;
+
+    if (dp->body_checks == 0
+       || (out = hbc_body_checks(context, dp->body_checks,
+                                 buf, len, offset)) == buf) {
+       vstring_sprintf(dp->buf, "%d BODY %c %ld\t|%s",
+                       dp->recno, rec_type, (long) offset, buf);
+       out_cb(dp, rec_type, STR(dp->buf), LEN(dp->buf), offset);
+    } else if (out != 0) {
+       vstring_sprintf(dp->buf, "%d BODY %c %ld\t|%s",
+                       dp->recno, rec_type, (long) offset, out);
+       out_cb(dp, rec_type, STR(dp->buf), LEN(dp->buf), offset);
+       myfree(out);
+    }
+    dp->recno += 1;
+}
+
+/* body_end - MIME_STATE end-of-message call-back */
+
+static void body_end(void *context)
+{
+    HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
+
+    out_cb(dp, 0, "BODY END", sizeof("BODY END") - 1, 0);
+}
+
+/* err_print - print MIME_STATE errors */
+
+static void err_print(void *unused_context, int err_flag,
+                             const char *text, ssize_t len)
+{
+    msg_warn("%s: %.*s", mime_state_error(err_flag),
+            len < 100 ? (int) len : 100, text);
+}
+
+int     var_header_limit = 2000;
+int     var_mime_maxdepth = 20;
+int     var_mime_bound_len = 2000;
+
+int     main(int argc, char **argv)
+{
+    int     rec_type;
+    VSTRING *buf;
+    int     err;
+    MIME_STATE *mime_state;
+    HBC_TEST_CONTEXT context;
+    static HBC_CALL_BACKS call_backs[1] = {
+       log_cb,                         /* logger */
+       out_cb,                         /* prepend */
+    };
+
+    /*
+     * Sanity check.
+     */
+    if (argc != 5)
+       msg_fatal("usage: %s header_checks mime_header_checks nested_header_checks body_checks", argv[0]);
+
+    /*
+     * Initialize.
+     */
+#define MIME_OPTIONS \
+            (MIME_OPT_REPORT_8BIT_IN_7BIT_BODY \
+            | MIME_OPT_REPORT_8BIT_IN_HEADER \
+            | MIME_OPT_REPORT_ENCODING_DOMAIN \
+            | MIME_OPT_REPORT_TRUNC_HEADER \
+            | MIME_OPT_REPORT_NESTING \
+            | MIME_OPT_DOWNGRADE)
+    msg_vstream_init(basename(argv[0]), VSTREAM_OUT);
+    buf = vstring_alloc(10);
+    mime_state = mime_state_alloc(MIME_OPTIONS,
+                                 head_out, head_end,
+                                 body_out, body_end,
+                                 err_print,
+                                 (void *) &context);
+    context.header_checks =
+       hbc_header_checks_create("header_checks", argv[1],
+                                "mime_header_checks", argv[2],
+                                "nested_header_checks", argv[3],
+                                call_backs);
+    context.body_checks =
+       hbc_body_checks_create("body_checks", argv[4], call_backs);
+    context.buf = vstring_alloc(100);
+    context.fp = VSTREAM_OUT;
+    context.queueid = "test-queueID";
+    context.recno = 0;
+
+    /*
+     * Main loop.
+     */
+    do {
+       rec_type = rec_streamlf_get(VSTREAM_IN, buf, REC_LEN);
+       VSTRING_TERMINATE(buf);
+       err = mime_state_update(mime_state, rec_type, STR(buf), LEN(buf));
+       vstream_fflush(VSTREAM_OUT);
+    } while (rec_type > 0);
+
+    /*
+     * Error reporting.
+     */
+    if (err & MIME_ERR_TRUNC_HEADER)
+       msg_warn("message header length exceeds safety limit");
+    if (err & MIME_ERR_NESTING)
+       msg_warn("MIME nesting exceeds safety limit");
+    if (err & MIME_ERR_8BIT_IN_HEADER)
+       msg_warn("improper use of 8-bit data in message header");
+    if (err & MIME_ERR_8BIT_IN_7BIT_BODY)
+       msg_warn("improper use of 8-bit data in message body");
+    if (err & MIME_ERR_ENCODING_DOMAIN)
+       msg_warn("improper message/* or multipart/* encoding domain");
+
+    /*
+     * Cleanup.
+     */
+    if (context.header_checks)
+       hbc_header_checks_free(context.header_checks);
+    if (context.body_checks)
+       hbc_body_checks_free(context.body_checks);
+    vstring_free(context.buf);
+    mime_state_free(mime_state);
+    vstring_free(buf);
+    exit(0);
+}
+
+#endif
diff --git a/postfix/src/global/header_body_checks.h b/postfix/src/global/header_body_checks.h
new file mode 100644 (file)
index 0000000..2fc3767
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef _HBC_H_INCLUDED_
+#define _HBC_H_INCLUDED_
+
+/*++
+/* NAME
+/*     header_body_checks 3h
+/* SUMMARY
+/*     delivery agent header/body checks
+/* SYNOPSIS
+/*     #include <header_body_checks.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Global library.
+  */
+#include <mime_state.h>
+#include <maps.h>
+
+ /*
+  * Postfix < 2.5 compatibility.
+  */
+#ifndef MIME_HDR_FIRST
+#define MIME_HDR_FIRST         (1)
+#define MIME_HDR_LAST          (3)
+#endif
+
+ /*
+  * External interface.
+  */
+typedef struct {
+    const char *map_class;             /* parameter name */
+    MAPS   *maps;                      /* map handle */
+} HBC_MAP_INFO;
+
+typedef struct {
+    void    (*logger) (void *, const char *, const char *, const char *, const char *);
+    void    (*prepend) (void *, int, const char *, ssize_t, off_t);
+    char   *(*extend) (void *, const char *, int, const char *, const char *, const char *, ssize_t, off_t);
+} HBC_CALL_BACKS;
+
+typedef struct {
+    HBC_CALL_BACKS *call_backs;
+    HBC_MAP_INFO map_info[1];          /* actually, a bunch */
+} HBC_CHECKS;
+
+#define HBC_CHECKS_STAT_IGNORE ((char *) 0)
+#define HBC_CHECKS_STAT_UNKNOWN        (&hbc_checks_unknown)
+
+extern HBC_CHECKS *hbc_header_checks_create(const char *, const char *,
+                                                const char *, const char *,
+                                                const char *, const char *,
+                                                   HBC_CALL_BACKS *);
+extern HBC_CHECKS *hbc_body_checks_create(const char *, const char *,
+                                                 HBC_CALL_BACKS *);
+extern char *hbc_header_checks(void *, HBC_CHECKS *, int, HEADER_OPTS *,
+                                      VSTRING *, off_t);
+extern char *hbc_body_checks(void *, HBC_CHECKS *, const char *, ssize_t, off_t);
+
+#define hbc_header_checks_free(hbc) _hbc_checks_free((hbc), HBC_HEADER_SIZE)
+#define hbc_body_checks_free(hbc) _hbc_checks_free((hbc), 1)
+
+ /*
+  * The following are NOT part of the external API.
+  */
+#define HBC_HEADER_SIZE        (MIME_HDR_LAST - MIME_HDR_FIRST + 1)
+extern void _hbc_checks_free(HBC_CHECKS *, ssize_t);
+extern const char hbc_checks_unknown;
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/postfix/src/global/header_body_checks_ignore.ref b/postfix/src/global/header_body_checks_ignore.ref
new file mode 100644 (file)
index 0000000..0d72838
--- /dev/null
@@ -0,0 +1,18 @@
+HEADER END
+2 BODY N 0     |
+4 BODY N 15    |
+7 BODY N 0     |
+header_body_checks: warning: invalid message/* or multipart/* encoding domain: base64
+11 BODY N 0    |
+13 BODY N 13   |
+16 BODY N 0    |
+18 BODY N 19   |
+21 BODY N 0    |
+23 BODY N 19   |
+26 BODY N 52   |
+28 BODY N 67   |
+31 BODY N 0    |
+33 BODY N 21   |
+35 BODY N 12   |
+BODY END
+header_body_checks: warning: improper message/* or multipart/* encoding domain
diff --git a/postfix/src/global/header_body_checks_null.ref b/postfix/src/global/header_body_checks_null.ref
new file mode 100644 (file)
index 0000000..ffe179c
--- /dev/null
@@ -0,0 +1,42 @@
+0 MAIN 0       |subject: primary subject
+1 MAIN 71      |content-type: multipart/(co\m\)ment)mumble mumble; boundary = "ab\cd 
+ ef" mumble
+HEADER END
+2 BODY N 0     |
+3 BODY N 1     |abcdef prolog
+4 BODY N 15    |
+5 BODY N 16    |--abcd ef
+6 MULT 0       |content-type: message/rfc822; mumble
+7 BODY N 0     |
+8 NEST 0       |subject: nested subject
+9 NEST 57      |content-type: multipart/mumble; boundary(comment)="pqrs"
+10 NEST 91     |content-transfer-encoding: base64
+header_body_checks: warning: invalid message/* or multipart/* encoding domain: base64
+11 BODY N 0    |
+12 BODY N 1    |pqrs prolog
+13 BODY N 13   |
+14 BODY N 14   |--pqrs
+15 MULT 0      |header: pqrs part 01
+16 BODY N 0    |
+17 BODY N 1    |body pqrs part 01
+18 BODY N 19   |
+19 BODY N 20   |--pqrs
+20 MULT 0      |header: pqrs part 02
+21 BODY N 0    |
+22 BODY N 1    |body pqrs part 02
+23 BODY N 19   |
+24 BODY N 20   |--bogus-boundary
+25 BODY N 37   |header: wietse
+26 BODY N 52   |
+27 BODY N 53   |body asdasads
+28 BODY N 67   |
+29 BODY N 68   |--abcd ef
+30 MULT 0      |header: abcdef part 02
+31 BODY N 0    |
+32 BODY N 1    |body abcdef part 02
+33 BODY N 21   |
+34 BODY N 0    |--abcd ef--
+35 BODY N 12   |
+36 BODY N 13   |epilog
+BODY END
+header_body_checks: warning: improper message/* or multipart/* encoding domain
diff --git a/postfix/src/global/header_body_checks_prepend.ref b/postfix/src/global/header_body_checks_prepend.ref
new file mode 100644 (file)
index 0000000..deaaefc
--- /dev/null
@@ -0,0 +1,88 @@
+header_body_checks: test-queueID: prepend: header subject: primary subject: header: head
+header: head
+0 MAIN 0       |subject: primary subject
+header_body_checks: test-queueID: prepend: header content-type: multipart/(co\m\)ment)mumble mumble; boundary = "ab\cd ? ef" mumble: header: mime
+header: mime
+1 MAIN 71      |content-type: multipart/(co\m\)ment)mumble mumble; boundary = "ab\cd 
+ ef" mumble
+HEADER END
+2 BODY N 0     |
+header_body_checks: test-queueID: prepend: body abcdef prolog: body
+body
+3 BODY N 1     |abcdef prolog
+4 BODY N 15    |
+header_body_checks: test-queueID: prepend: body --abcd ef: body
+body
+5 BODY N 16    |--abcd ef
+header_body_checks: test-queueID: prepend: header content-type: message/rfc822; mumble: header: mime
+header: mime
+6 MULT 0       |content-type: message/rfc822; mumble
+7 BODY N 0     |
+header_body_checks: test-queueID: prepend: header subject: nested subject: header: nest
+header: nest
+8 NEST 0       |subject: nested subject
+header_body_checks: test-queueID: prepend: header content-type: multipart/mumble; boundary(comment)="pqrs": header: mime
+header: mime
+9 NEST 57      |content-type: multipart/mumble; boundary(comment)="pqrs"
+header_body_checks: test-queueID: prepend: header content-transfer-encoding: base64: header: mime
+header: mime
+10 NEST 91     |content-transfer-encoding: base64
+header_body_checks: warning: invalid message/* or multipart/* encoding domain: base64
+11 BODY N 0    |
+header_body_checks: test-queueID: prepend: body pqrs prolog: body
+body
+12 BODY N 1    |pqrs prolog
+13 BODY N 13   |
+header_body_checks: test-queueID: prepend: body --pqrs: body
+body
+14 BODY N 14   |--pqrs
+header_body_checks: test-queueID: prepend: header header: pqrs part 01: header: mime
+header: mime
+15 MULT 0      |header: pqrs part 01
+16 BODY N 0    |
+header_body_checks: test-queueID: prepend: body body pqrs part 01: body
+body
+17 BODY N 1    |body pqrs part 01
+18 BODY N 19   |
+header_body_checks: test-queueID: prepend: body --pqrs: body
+body
+19 BODY N 20   |--pqrs
+header_body_checks: test-queueID: prepend: header header: pqrs part 02: header: mime
+header: mime
+20 MULT 0      |header: pqrs part 02
+21 BODY N 0    |
+header_body_checks: test-queueID: prepend: body body pqrs part 02: body
+body
+22 BODY N 1    |body pqrs part 02
+23 BODY N 19   |
+header_body_checks: test-queueID: prepend: body --bogus-boundary: body
+body
+24 BODY N 20   |--bogus-boundary
+header_body_checks: test-queueID: prepend: body header: wietse: body
+body
+25 BODY N 37   |header: wietse
+26 BODY N 52   |
+header_body_checks: test-queueID: prepend: body body asdasads: body
+body
+27 BODY N 53   |body asdasads
+28 BODY N 67   |
+header_body_checks: test-queueID: prepend: body --abcd ef: body
+body
+29 BODY N 68   |--abcd ef
+header_body_checks: test-queueID: prepend: header header: abcdef part 02: header: mime
+header: mime
+30 MULT 0      |header: abcdef part 02
+31 BODY N 0    |
+header_body_checks: test-queueID: prepend: body body abcdef part 02: body
+body
+32 BODY N 1    |body abcdef part 02
+33 BODY N 21   |
+header_body_checks: test-queueID: prepend: body --abcd ef--: body
+body
+34 BODY N 0    |--abcd ef--
+35 BODY N 12   |
+header_body_checks: test-queueID: prepend: body epilog: body
+body
+36 BODY N 13   |epilog
+BODY END
+header_body_checks: warning: improper message/* or multipart/* encoding domain
diff --git a/postfix/src/global/header_body_checks_replace.ref b/postfix/src/global/header_body_checks_replace.ref
new file mode 100644 (file)
index 0000000..31bbeb3
--- /dev/null
@@ -0,0 +1,64 @@
+header_body_checks: test-queueID: replace: header subject: primary subject: header: head
+0 MAIN 0       |header: head
+header_body_checks: test-queueID: replace: header content-type: multipart/(co\m\)ment)mumble mumble; boundary = "ab\cd ? ef" mumble: header: mime
+1 MAIN 71      |header: mime
+HEADER END
+2 BODY N 0     |
+header_body_checks: test-queueID: replace: body abcdef prolog: body
+3 BODY N 1     |body
+4 BODY N 15    |
+header_body_checks: test-queueID: replace: body --abcd ef: body
+5 BODY N 16    |body
+header_body_checks: test-queueID: replace: header content-type: message/rfc822; mumble: header: mime
+6 MULT 0       |header: mime
+7 BODY N 0     |
+header_body_checks: test-queueID: replace: header subject: nested subject: header: nest
+8 NEST 0       |header: nest
+header_body_checks: test-queueID: replace: header content-type: multipart/mumble; boundary(comment)="pqrs": header: mime
+9 NEST 57      |header: mime
+header_body_checks: test-queueID: replace: header content-transfer-encoding: base64: header: mime
+10 NEST 91     |header: mime
+header_body_checks: warning: invalid message/* or multipart/* encoding domain: base64
+11 BODY N 0    |
+header_body_checks: test-queueID: replace: body pqrs prolog: body
+12 BODY N 1    |body
+13 BODY N 13   |
+header_body_checks: test-queueID: replace: body --pqrs: body
+14 BODY N 14   |body
+header_body_checks: test-queueID: replace: header header: pqrs part 01: header: mime
+15 MULT 0      |header: mime
+16 BODY N 0    |
+header_body_checks: test-queueID: replace: body body pqrs part 01: body
+17 BODY N 1    |body
+18 BODY N 19   |
+header_body_checks: test-queueID: replace: body --pqrs: body
+19 BODY N 20   |body
+header_body_checks: test-queueID: replace: header header: pqrs part 02: header: mime
+20 MULT 0      |header: mime
+21 BODY N 0    |
+header_body_checks: test-queueID: replace: body body pqrs part 02: body
+22 BODY N 1    |body
+23 BODY N 19   |
+header_body_checks: test-queueID: replace: body --bogus-boundary: body
+24 BODY N 20   |body
+header_body_checks: test-queueID: replace: body header: wietse: body
+25 BODY N 37   |body
+26 BODY N 52   |
+header_body_checks: test-queueID: replace: body body asdasads: body
+27 BODY N 53   |body
+28 BODY N 67   |
+header_body_checks: test-queueID: replace: body --abcd ef: body
+29 BODY N 68   |body
+header_body_checks: test-queueID: replace: header header: abcdef part 02: header: mime
+30 MULT 0      |header: mime
+31 BODY N 0    |
+header_body_checks: test-queueID: replace: body body abcdef part 02: body
+32 BODY N 1    |body
+33 BODY N 21   |
+header_body_checks: test-queueID: replace: body --abcd ef--: body
+34 BODY N 0    |body
+35 BODY N 12   |
+header_body_checks: test-queueID: replace: body epilog: body
+36 BODY N 13   |body
+BODY END
+header_body_checks: warning: improper message/* or multipart/* encoding domain
diff --git a/postfix/src/global/header_body_checks_warn.ref b/postfix/src/global/header_body_checks_warn.ref
new file mode 100644 (file)
index 0000000..50afd35
--- /dev/null
@@ -0,0 +1,65 @@
+header_body_checks: test-queueID: warning: header subject: primary subject
+0 MAIN 0       |subject: primary subject
+header_body_checks: test-queueID: warning: header content-type: multipart/(co\m\)ment)mumble mumble; boundary = "ab\cd ? ef" mumble
+1 MAIN 71      |content-type: multipart/(co\m\)ment)mumble mumble; boundary = "ab\cd 
+ ef" mumble
+HEADER END
+2 BODY N 0     |
+header_body_checks: test-queueID: warning: body abcdef prolog
+3 BODY N 1     |abcdef prolog
+4 BODY N 15    |
+header_body_checks: test-queueID: warning: body --abcd ef
+5 BODY N 16    |--abcd ef
+header_body_checks: test-queueID: warning: header content-type: message/rfc822; mumble
+6 MULT 0       |content-type: message/rfc822; mumble
+7 BODY N 0     |
+header_body_checks: test-queueID: warning: header subject: nested subject
+8 NEST 0       |subject: nested subject
+header_body_checks: test-queueID: warning: header content-type: multipart/mumble; boundary(comment)="pqrs"
+9 NEST 57      |content-type: multipart/mumble; boundary(comment)="pqrs"
+header_body_checks: test-queueID: warning: header content-transfer-encoding: base64
+10 NEST 91     |content-transfer-encoding: base64
+header_body_checks: warning: invalid message/* or multipart/* encoding domain: base64
+11 BODY N 0    |
+header_body_checks: test-queueID: warning: body pqrs prolog
+12 BODY N 1    |pqrs prolog
+13 BODY N 13   |
+header_body_checks: test-queueID: warning: body --pqrs
+14 BODY N 14   |--pqrs
+header_body_checks: test-queueID: warning: header header: pqrs part 01
+15 MULT 0      |header: pqrs part 01
+16 BODY N 0    |
+header_body_checks: test-queueID: warning: body body pqrs part 01
+17 BODY N 1    |body pqrs part 01
+18 BODY N 19   |
+header_body_checks: test-queueID: warning: body --pqrs
+19 BODY N 20   |--pqrs
+header_body_checks: test-queueID: warning: header header: pqrs part 02
+20 MULT 0      |header: pqrs part 02
+21 BODY N 0    |
+header_body_checks: test-queueID: warning: body body pqrs part 02
+22 BODY N 1    |body pqrs part 02
+23 BODY N 19   |
+header_body_checks: test-queueID: warning: body --bogus-boundary
+24 BODY N 20   |--bogus-boundary
+header_body_checks: test-queueID: warning: body header: wietse
+25 BODY N 37   |header: wietse
+26 BODY N 52   |
+header_body_checks: test-queueID: warning: body body asdasads
+27 BODY N 53   |body asdasads
+28 BODY N 67   |
+header_body_checks: test-queueID: warning: body --abcd ef
+29 BODY N 68   |--abcd ef
+header_body_checks: test-queueID: warning: header header: abcdef part 02
+30 MULT 0      |header: abcdef part 02
+31 BODY N 0    |
+header_body_checks: test-queueID: warning: body body abcdef part 02
+32 BODY N 1    |body abcdef part 02
+33 BODY N 21   |
+header_body_checks: test-queueID: warning: body --abcd ef--
+34 BODY N 0    |--abcd ef--
+35 BODY N 12   |
+header_body_checks: test-queueID: warning: body epilog
+36 BODY N 13   |epilog
+BODY END
+header_body_checks: warning: improper message/* or multipart/* encoding domain
index bbae895c76a9b5a864814ca8851c306cba80f6d6..7b91101070d89cc9f95fae4d7d8a9177455d7f30 100644 (file)
@@ -2802,6 +2802,34 @@ extern bool var_smtpd_client_port_log;
 #define DEF_QMQPD_CLIENT_PORT_LOG              0
 extern bool var_qmqpd_client_port_log;
 
+ /*
+  * Header/body checks in delivery agents.
+  */
+#define VAR_SMTP_HEAD_CHKS     "smtp_header_checks"
+#define DEF_SMTP_HEAD_CHKS     ""
+extern char *var_smtp_head_chks;
+
+#define VAR_SMTP_MIME_CHKS     "smtp_mime_header_checks"
+#define DEF_SMTP_MIME_CHKS     ""
+extern char *var_smtp_mime_chks;
+
+#define VAR_SMTP_NEST_CHKS     "smtp_nested_header_checks"
+#define DEF_SMTP_NEST_CHKS     ""
+extern char *var_smtp_nest_chks;
+
+#define VAR_SMTP_BODY_CHKS     "smtp_body_checks"
+#define DEF_SMTP_BODY_CHKS     ""
+extern char *var_smtp_body_chks;
+
+#define VAR_LMTP_HEAD_CHKS     "lmtp_header_checks"
+#define DEF_LMTP_HEAD_CHKS     ""
+#define VAR_LMTP_MIME_CHKS     "lmtp_mime_header_checks"
+#define DEF_LMTP_MIME_CHKS     ""
+#define VAR_LMTP_NEST_CHKS     "lmtp_nested_header_checks"
+#define DEF_LMTP_NEST_CHKS     ""
+#define VAR_LMTP_BODY_CHKS     "lmtp_body_checks"
+#define DEF_LMTP_BODY_CHKS     ""
+
 /* LICENSE
 /* .ad
 /* .fi
index ec6dc43814328ce27f8e6534486b38b24b37d11e..b4d627ed70d4fa7c6967c3c86ba0b44ff17b71d9 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      "20071006"
+#define MAIL_RELEASE_DATE      "20071111"
 #define MAIL_VERSION_NUMBER    "2.5"
 
 #ifdef SNAPSHOT
index 4b4bf69f99c2fd54c66cdc1b2c46a61007db1ede..bea9e0ad4ec4f9b7b170632e7fa713f974716fd4 100644 (file)
 /*     At the start of a nested (e.g., message/rfc822) message.
 /* .RE
 /* .sp
+/*     For convenience, the macros MIME_HDR_FIRST and MIME_HDR_LAST
+/*     specify the range of MIME_HDR_MUMBLE macros.
+/* .sp
 /*     To find out if something is a MIME header at the beginning
-/*     of an RFC 822 message, look at the header_info argument.
+/*     of an RFC 822 message or an attached message, look at the
+/*     header_info argument.
 /* .IP header_info
 /*     Null pointer or information about the message header, see
 /*     header_opts(3).
index 78454a23e3a169446500bc46747e3b6335fbebbf..35f5c827f0c2cf5301f4cb13f4a0db3fd5ac3d5b 100644 (file)
@@ -73,12 +73,14 @@ extern MIME_STATE_DETAIL *mime_state_detail(int);
 extern const char *mime_state_error(int);
 
  /*
-  * Header classes. Look at the header_opts argument to find out if something
-  * is a MIME header in a primary or nested section.
+  * With header classes, look at the header_opts argument to recognize MIME
+  * headers in primary or nested sections.
   */
+#define MIME_HDR_FIRST         (1)     /* first class */
 #define MIME_HDR_PRIMARY       (1)     /* initial headers */
 #define MIME_HDR_MULTIPART     (2)     /* headers after multipart boundary */
 #define MIME_HDR_NESTED                (3)     /* attached message initial headers */
+#define MIME_HDR_LAST          (3)     /* last class */
 
 /* LICENSE
 /* .ad
index 8873d15bccdb512e07ad6d67acea7b7e9aed589c..7952583c246c4c0f6436ca1cb54e424d61392b54 100644 (file)
@@ -165,6 +165,9 @@ int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma
        argv_add(env, "DOMAIN", state.msg_attr.domain, ARGV_END);
     if (state.msg_attr.extension)
        argv_add(env, "EXTENSION", state.msg_attr.extension, ARGV_END);
+    if (state.msg_attr.rcpt.orig_addr && state.msg_attr.rcpt.orig_addr[0])
+       argv_add(env, "ORIGINAL_RECIPIENT", state.msg_attr.rcpt.orig_addr,
+                ARGV_END);
 
 #define EXPORT_REQUEST(name, value) \
        if ((value)[0]) argv_add(env, (name), (value), ARGV_END);
index b2992d7c48f5907419023db0b1bd82674a3ca3ee..6788fc80fa4edbadd99df01519636f77ed145e20 100644 (file)
 /* .IP \fBLOCAL\fR
 /*     The entire recipient address localpart (text to the left of the
 /*     rightmost @ character).
+/* .IP \fBORIGINAL_RECIPIENT\fR
+/*     The entire recipient address, before any address rewriting
+/*     or aliasing (Postfix 2.5 and later).
 /* .IP \fBRECIPIENT\fR
 /*     The entire recipient address.
 /* .IP \fBSENDER\fR
index d690240598a7a15a30ca25f988c55ef9a479e53f..2aad9612cddf3ad050d578e5359b7117f429b7ec 100644 (file)
@@ -63,6 +63,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
+#include <limits.h>                    /* INT_MAX */
 
 #ifndef SHUT_RDWR
 #define SHUT_RDWR      2
@@ -281,7 +282,7 @@ typedef struct {
  /*
   * We don't accept insane amounts of data.
   */
-#define XXX_MAX_DATA   (MILTER_CHUNK_SIZE * 2)
+#define XXX_MAX_DATA   (INT_MAX / 2)
 #define XXX_TIMEOUT    10
 
 #ifndef USE_LIBMILTER_INCLUDES
@@ -533,8 +534,8 @@ static int milter8_read_resp(MILTER8 *milter, int event, unsigned char *command,
                 milter->m.name, (long) pkt_len);
        return (milter8_comm_error(milter));
     } else if (pkt_len > XXX_MAX_DATA) {
-       msg_warn("milter %s: unreasonable packet length: %ld",
-                milter->m.name, (long) pkt_len);
+       msg_warn("milter %s: unreasonable packet length: %ld > %ld",
+                milter->m.name, (long) pkt_len, (long) XXX_MAX_DATA);
        return (milter8_comm_error(milter));
     }
 
index b768d6fc779779285ec2440bd0d5a1146aa1a5a3..e5c37b13f6a448dc7d91d62318c4e753f79d7ab5 100644 (file)
 /* .sp
 /*     This feature is available as of Postfix 2.5.
 /* .IP \fBh\fR
-/*     Fold the command-line \fB$recipient\fR address domain part
+/*     Fold the command-line \fB$original_recipient\fR and
+/*     \fB$recipient\fR address domain part
 /*     (text to the right of the right-most \fB@\fR character) to
 /*     lower case; fold the entire command-line \fB$domain\fR and
 /*     \fB$nexthop\fR host or domain information to lower case.
 /*     This is recommended for delivery via \fBUUCP\fR.
 /* .IP \fBq\fR
 /*     Quote white space and other special characters in the command-line
-/*     \fB$sender\fR and \fB$recipient\fR address localparts (text to the
+/*     \fB$sender\fR, \fB$original_recipient\fR and \fB$recipient\fR
+/*     address localparts (text to the
 /*     left of the right-most \fB@\fR character), according to an 8-bit
 /*     transparent version of RFC 822.
 /*     This is recommended for delivery via \fBUUCP\fR or \fBBSMTP\fR.
 /*     address information from the \fB$user\fR, \fB$extension\fR or
 /*     \fB$mailbox\fR command-line macros.
 /* .IP \fBu\fR
-/*     Fold the command-line \fB$recipient\fR address localpart (text to
+/*     Fold the command-line \fB$original_recipient\fR and
+/*     \fB$recipient\fR address localpart (text to
 /*     the left of the right-most \fB@\fR character) to lower case.
 /*     This is recommended for delivery via \fBUUCP\fR.
 /* .IP \fB.\fR
 /*     This macro expands to the next-hop hostname.
 /* .sp
 /*     This information is modified by the \fBh\fR flag for case folding.
+/* .IP \fB${\fBoriginal_recipient\fR}\fR
+/*     This macro expands to the complete recipient address before any
+/*     address rewriting or aliasing.
+/* .sp
+/*     A command-line argument that contains
+/*     \fB${\fBoriginal_recipient\fR}\fR expands to as many
+/*     command-line arguments as there are recipients.
+/* .sp
+/*     This information is modified by the \fBhqu\fR flags for quoting
+/*     and case folding.
 /* .IP \fB${\fBrecipient\fR}\fR
 /*     This macro expands to the complete recipient address.
 /* .sp
 #define PIPE_DICT_TABLE                "pipe_command"  /* table name */
 #define PIPE_DICT_NEXTHOP      "nexthop"       /* key */
 #define PIPE_DICT_RCPT         "recipient"     /* key */
+#define PIPE_DICT_ORIG_RCPT    "original_recipient"    /* key */
 #define PIPE_DICT_SENDER       "sender"/* key */
 #define PIPE_DICT_USER         "user"  /* key */
 #define PIPE_DICT_EXTENSION    "extension"     /* key */
 #define PIPE_FLAG_EXTENSION    (1<<2)
 #define PIPE_FLAG_MAILBOX      (1<<3)
 #define PIPE_FLAG_DOMAIN       (1<<4)
+#define PIPE_FLAG_ORIG_RCPT    (1<<5)
 
  /*
   * Additional flags. These are colocated with mail_copy() flags. Allow some
@@ -567,6 +582,7 @@ static int parse_callback(int type, VSTRING *buf, char *context)
     static struct cmd_flags cmd_flags[] = {
        PIPE_DICT_NEXTHOP, 0,
        PIPE_DICT_RCPT, PIPE_FLAG_RCPT,
+       PIPE_DICT_ORIG_RCPT, PIPE_FLAG_ORIG_RCPT,
        PIPE_DICT_SENDER, 0,
        PIPE_DICT_USER, PIPE_FLAG_USER,
        PIPE_DICT_EXTENSION, PIPE_FLAG_EXTENSION,
@@ -672,6 +688,14 @@ static ARGV *expand_argv(const char *service, char **argv,
                    dict_update(PIPE_DICT_TABLE, PIPE_DICT_RCPT, STR(buf));
                }
 
+               /*
+                * This argument contains $original_recipient.
+                */
+               if (state.expand_flag & PIPE_FLAG_ORIG_RCPT) {
+                   morph_recipient(buf, rcpt_list->info[i].orig_addr, flags);
+                   dict_update(PIPE_DICT_TABLE, PIPE_DICT_ORIG_RCPT, STR(buf));
+               }
+
                /*
                 * This argument contains $user. Extract the plain user name.
                 * Either anything to the left of the extension delimiter or,
index 8e578347da12741be5ec36165206c6905d4a1c63..48c9f8b5b13ff2d7aae0a698029ee47edc271456 100644 (file)
@@ -95,6 +95,8 @@ smtp.o: ../../include/dsn.h
 smtp.o: ../../include/dsn_buf.h
 smtp.o: ../../include/ext_prop.h
 smtp.o: ../../include/flush_clnt.h
+smtp.o: ../../include/header_body_checks.h
+smtp.o: ../../include/header_opts.h
 smtp.o: ../../include/htable.h
 smtp.o: ../../include/mail_conf.h
 smtp.o: ../../include/mail_params.h
@@ -103,6 +105,7 @@ smtp.o: ../../include/mail_version.h
 smtp.o: ../../include/maps.h
 smtp.o: ../../include/match_list.h
 smtp.o: ../../include/match_ops.h
+smtp.o: ../../include/mime_state.h
 smtp.o: ../../include/msg.h
 smtp.o: ../../include/msg_stats.h
 smtp.o: ../../include/mymalloc.h
@@ -131,6 +134,8 @@ smtp_addr.o: ../../include/dict.h
 smtp_addr.o: ../../include/dns.h
 smtp_addr.o: ../../include/dsn.h
 smtp_addr.o: ../../include/dsn_buf.h
+smtp_addr.o: ../../include/header_body_checks.h
+smtp_addr.o: ../../include/header_opts.h
 smtp_addr.o: ../../include/htable.h
 smtp_addr.o: ../../include/inet_addr_list.h
 smtp_addr.o: ../../include/inet_proto.h
@@ -138,6 +143,7 @@ smtp_addr.o: ../../include/mail_params.h
 smtp_addr.o: ../../include/maps.h
 smtp_addr.o: ../../include/match_list.h
 smtp_addr.o: ../../include/match_ops.h
+smtp_addr.o: ../../include/mime_state.h
 smtp_addr.o: ../../include/msg.h
 smtp_addr.o: ../../include/msg_stats.h
 smtp_addr.o: ../../include/myaddrinfo.h
@@ -168,6 +174,8 @@ smtp_chat.o: ../../include/dict.h
 smtp_chat.o: ../../include/dsn.h
 smtp_chat.o: ../../include/dsn_buf.h
 smtp_chat.o: ../../include/dsn_util.h
+smtp_chat.o: ../../include/header_body_checks.h
+smtp_chat.o: ../../include/header_opts.h
 smtp_chat.o: ../../include/htable.h
 smtp_chat.o: ../../include/int_filt.h
 smtp_chat.o: ../../include/line_wrap.h
@@ -177,6 +185,7 @@ smtp_chat.o: ../../include/mail_params.h
 smtp_chat.o: ../../include/maps.h
 smtp_chat.o: ../../include/match_list.h
 smtp_chat.o: ../../include/match_ops.h
+smtp_chat.o: ../../include/mime_state.h
 smtp_chat.o: ../../include/msg.h
 smtp_chat.o: ../../include/msg_stats.h
 smtp_chat.o: ../../include/mymalloc.h
@@ -205,6 +214,8 @@ smtp_connect.o: ../../include/dict.h
 smtp_connect.o: ../../include/dns.h
 smtp_connect.o: ../../include/dsn.h
 smtp_connect.o: ../../include/dsn_buf.h
+smtp_connect.o: ../../include/header_body_checks.h
+smtp_connect.o: ../../include/header_opts.h
 smtp_connect.o: ../../include/host_port.h
 smtp_connect.o: ../../include/htable.h
 smtp_connect.o: ../../include/inet_addr_list.h
@@ -215,6 +226,7 @@ smtp_connect.o: ../../include/mail_proto.h
 smtp_connect.o: ../../include/maps.h
 smtp_connect.o: ../../include/match_list.h
 smtp_connect.o: ../../include/match_ops.h
+smtp_connect.o: ../../include/mime_state.h
 smtp_connect.o: ../../include/msg.h
 smtp_connect.o: ../../include/msg_stats.h
 smtp_connect.o: ../../include/myaddrinfo.h
@@ -247,11 +259,14 @@ smtp_map11.o: ../../include/deliver_request.h
 smtp_map11.o: ../../include/dict.h
 smtp_map11.o: ../../include/dsn.h
 smtp_map11.o: ../../include/dsn_buf.h
+smtp_map11.o: ../../include/header_body_checks.h
+smtp_map11.o: ../../include/header_opts.h
 smtp_map11.o: ../../include/htable.h
 smtp_map11.o: ../../include/mail_addr_map.h
 smtp_map11.o: ../../include/maps.h
 smtp_map11.o: ../../include/match_list.h
 smtp_map11.o: ../../include/match_ops.h
+smtp_map11.o: ../../include/mime_state.h
 smtp_map11.o: ../../include/msg.h
 smtp_map11.o: ../../include/msg_stats.h
 smtp_map11.o: ../../include/name_code.h
@@ -283,6 +298,7 @@ smtp_proto.o: ../../include/dsn_buf.h
 smtp_proto.o: ../../include/dsn_mask.h
 smtp_proto.o: ../../include/ehlo_mask.h
 smtp_proto.o: ../../include/ext_prop.h
+smtp_proto.o: ../../include/header_body_checks.h
 smtp_proto.o: ../../include/header_opts.h
 smtp_proto.o: ../../include/htable.h
 smtp_proto.o: ../../include/iostuff.h
@@ -334,10 +350,13 @@ smtp_rcpt.o: ../../include/dict.h
 smtp_rcpt.o: ../../include/dsn.h
 smtp_rcpt.o: ../../include/dsn_buf.h
 smtp_rcpt.o: ../../include/dsn_mask.h
+smtp_rcpt.o: ../../include/header_body_checks.h
+smtp_rcpt.o: ../../include/header_opts.h
 smtp_rcpt.o: ../../include/htable.h
 smtp_rcpt.o: ../../include/maps.h
 smtp_rcpt.o: ../../include/match_list.h
 smtp_rcpt.o: ../../include/match_ops.h
+smtp_rcpt.o: ../../include/mime_state.h
 smtp_rcpt.o: ../../include/msg.h
 smtp_rcpt.o: ../../include/msg_stats.h
 smtp_rcpt.o: ../../include/mymalloc.h
@@ -363,11 +382,14 @@ smtp_reuse.o: ../../include/deliver_request.h
 smtp_reuse.o: ../../include/dict.h
 smtp_reuse.o: ../../include/dsn.h
 smtp_reuse.o: ../../include/dsn_buf.h
+smtp_reuse.o: ../../include/header_body_checks.h
+smtp_reuse.o: ../../include/header_opts.h
 smtp_reuse.o: ../../include/htable.h
 smtp_reuse.o: ../../include/mail_params.h
 smtp_reuse.o: ../../include/maps.h
 smtp_reuse.o: ../../include/match_list.h
 smtp_reuse.o: ../../include/match_ops.h
+smtp_reuse.o: ../../include/mime_state.h
 smtp_reuse.o: ../../include/msg.h
 smtp_reuse.o: ../../include/msg_stats.h
 smtp_reuse.o: ../../include/mymalloc.h
@@ -393,12 +415,15 @@ smtp_sasl_glue.o: ../../include/deliver_request.h
 smtp_sasl_glue.o: ../../include/dict.h
 smtp_sasl_glue.o: ../../include/dsn.h
 smtp_sasl_glue.o: ../../include/dsn_buf.h
+smtp_sasl_glue.o: ../../include/header_body_checks.h
+smtp_sasl_glue.o: ../../include/header_opts.h
 smtp_sasl_glue.o: ../../include/htable.h
 smtp_sasl_glue.o: ../../include/mail_addr_find.h
 smtp_sasl_glue.o: ../../include/mail_params.h
 smtp_sasl_glue.o: ../../include/maps.h
 smtp_sasl_glue.o: ../../include/match_list.h
 smtp_sasl_glue.o: ../../include/match_ops.h
+smtp_sasl_glue.o: ../../include/mime_state.h
 smtp_sasl_glue.o: ../../include/msg.h
 smtp_sasl_glue.o: ../../include/msg_stats.h
 smtp_sasl_glue.o: ../../include/mymalloc.h
@@ -426,11 +451,14 @@ smtp_sasl_proto.o: ../../include/deliver_request.h
 smtp_sasl_proto.o: ../../include/dict.h
 smtp_sasl_proto.o: ../../include/dsn.h
 smtp_sasl_proto.o: ../../include/dsn_buf.h
+smtp_sasl_proto.o: ../../include/header_body_checks.h
+smtp_sasl_proto.o: ../../include/header_opts.h
 smtp_sasl_proto.o: ../../include/htable.h
 smtp_sasl_proto.o: ../../include/mail_params.h
 smtp_sasl_proto.o: ../../include/maps.h
 smtp_sasl_proto.o: ../../include/match_list.h
 smtp_sasl_proto.o: ../../include/match_ops.h
+smtp_sasl_proto.o: ../../include/mime_state.h
 smtp_sasl_proto.o: ../../include/msg.h
 smtp_sasl_proto.o: ../../include/msg_stats.h
 smtp_sasl_proto.o: ../../include/mymalloc.h
@@ -457,6 +485,7 @@ smtp_session.o: ../../include/deliver_request.h
 smtp_session.o: ../../include/dict.h
 smtp_session.o: ../../include/dsn.h
 smtp_session.o: ../../include/dsn_buf.h
+smtp_session.o: ../../include/header_body_checks.h
 smtp_session.o: ../../include/header_opts.h
 smtp_session.o: ../../include/htable.h
 smtp_session.o: ../../include/mail_params.h
@@ -490,11 +519,14 @@ smtp_state.o: ../../include/deliver_request.h
 smtp_state.o: ../../include/dict.h
 smtp_state.o: ../../include/dsn.h
 smtp_state.o: ../../include/dsn_buf.h
+smtp_state.o: ../../include/header_body_checks.h
+smtp_state.o: ../../include/header_opts.h
 smtp_state.o: ../../include/htable.h
 smtp_state.o: ../../include/mail_params.h
 smtp_state.o: ../../include/maps.h
 smtp_state.o: ../../include/match_list.h
 smtp_state.o: ../../include/match_ops.h
+smtp_state.o: ../../include/mime_state.h
 smtp_state.o: ../../include/msg.h
 smtp_state.o: ../../include/msg_stats.h
 smtp_state.o: ../../include/mymalloc.h
@@ -522,11 +554,14 @@ smtp_trouble.o: ../../include/deliver_request.h
 smtp_trouble.o: ../../include/dict.h
 smtp_trouble.o: ../../include/dsn.h
 smtp_trouble.o: ../../include/dsn_buf.h
+smtp_trouble.o: ../../include/header_body_checks.h
+smtp_trouble.o: ../../include/header_opts.h
 smtp_trouble.o: ../../include/htable.h
 smtp_trouble.o: ../../include/mail_error.h
 smtp_trouble.o: ../../include/maps.h
 smtp_trouble.o: ../../include/match_list.h
 smtp_trouble.o: ../../include/match_ops.h
+smtp_trouble.o: ../../include/mime_state.h
 smtp_trouble.o: ../../include/msg.h
 smtp_trouble.o: ../../include/msg_stats.h
 smtp_trouble.o: ../../include/name_code.h
@@ -552,10 +587,13 @@ smtp_unalias.o: ../../include/dict.h
 smtp_unalias.o: ../../include/dns.h
 smtp_unalias.o: ../../include/dsn.h
 smtp_unalias.o: ../../include/dsn_buf.h
+smtp_unalias.o: ../../include/header_body_checks.h
+smtp_unalias.o: ../../include/header_opts.h
 smtp_unalias.o: ../../include/htable.h
 smtp_unalias.o: ../../include/maps.h
 smtp_unalias.o: ../../include/match_list.h
 smtp_unalias.o: ../../include/match_ops.h
+smtp_unalias.o: ../../include/mime_state.h
 smtp_unalias.o: ../../include/msg.h
 smtp_unalias.o: ../../include/msg_stats.h
 smtp_unalias.o: ../../include/myaddrinfo.h
index 3a40791c60b3829a3d5578d4073427259e981865..0ebb292b3300dce835ee731bd4a6e8fefe3e034f 100644 (file)
        VAR_LMTP_PIX_BUG_WORDS, DEF_LMTP_PIX_BUG_WORDS, &var_smtp_pix_bug_words, 0, 0,
        VAR_LMTP_PIX_BUG_MAPS, DEF_LMTP_PIX_BUG_MAPS, &var_smtp_pix_bug_maps, 0, 0,
        VAR_CYRUS_CONF_PATH, DEF_CYRUS_CONF_PATH, &var_cyrus_conf_path, 0, 0,
+       VAR_LMTP_HEAD_CHKS, DEF_LMTP_HEAD_CHKS, &var_smtp_head_chks, 0, 0,
+       VAR_LMTP_MIME_CHKS, DEF_LMTP_MIME_CHKS, &var_smtp_mime_chks, 0, 0,
+       VAR_LMTP_NEST_CHKS, DEF_LMTP_NEST_CHKS, &var_smtp_nest_chks, 0, 0,
+       VAR_LMTP_BODY_CHKS, DEF_LMTP_BODY_CHKS, &var_smtp_body_chks, 0, 0,
        0,
     };
     static CONFIG_TIME_TABLE lmtp_time_table[] = {
index 2f5abaf6a1e4bf7b54fd8e227938fd3b047fd789..356967c7ce0e88d39472092e5c279c0d8156c53d 100644 (file)
 /*     When authenticating to a remote SMTP or LMTP server with the
 /*     default setting "no", send no SASL authoriZation ID (authzid); send
 /*     only the SASL authentiCation ID (authcid) plus the authcid's password.
+/* .PP
+/*      Available in Postfix version 2.5 and later:
+/* .IP "\fBsmtp_header_checks (empty)\fR"
+/*     Restricted \fBheader_checks\fR(5) tables for the Postfix SMTP client.
+/* .IP "\fBsmtp_mime_header_checks (empty)\fR"
+/*     Restricted \fBmime_header_checks\fR(5) tables for the Postfix SMTP
+/*     client.
+/* .IP "\fBsmtp_nested_header_checks (empty)\fR"
+/*     Restricted \fBnested_header_checks\fR(5) tables for the Postfix SMTP
+/*     client.
+/* .IP "\fBsmtp_body_checks (empty)\fR"
+/*     Restricted \fBbody_checks\fR(5) tables for the Postfix SMTP client.
 /* MIME PROCESSING CONTROLS
 /* .ad
 /* .fi
 /*     Optional list of relay hosts for SMTP destinations that can't be
 /*     found or that are unreachable.
 /* SEE ALSO
+/*     generic(5), output address rewriting
+/*     header_checks(5), message header content inspection
+/*     body_checks(5), body parts content inspection
 /*     qmgr(8), queue manager
 /*     bounce(8), delivery status reports
 /*     scache(8), connection cache server
@@ -719,6 +734,10 @@ bool    var_smtp_cname_overr;
 char   *var_smtp_pix_bug_words;
 char   *var_smtp_pix_bug_maps;
 char   *var_cyrus_conf_path;
+char   *var_smtp_head_chks;
+char   *var_smtp_mime_chks;
+char   *var_smtp_nest_chks;
+char   *var_smtp_body_chks;
 
  /*
   * Global variables.
@@ -730,6 +749,8 @@ MAPS   *smtp_ehlo_dis_maps;
 MAPS   *smtp_generic_maps;
 int     smtp_ext_prop_mask;
 MAPS   *smtp_pix_bug_maps;
+HBC_CHECKS *smtp_header_checks;                /* limited header checks */
+HBC_CHECKS *smtp_body_checks;          /* limited body checks */
 
 #ifdef USE_TLS
 
@@ -944,6 +965,18 @@ static void pre_init(char *unused_name, char **unused_argv)
        smtp_generic_maps =
            maps_create(VAR_SMTP_GENERIC_MAPS, var_smtp_generic_maps,
                        DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
+
+    /*
+     * Header/body checks.
+     */
+    smtp_header_checks = hbc_header_checks_create(
+                                    VAR_SMTP_HEAD_CHKS, var_smtp_head_chks,
+                                    VAR_SMTP_MIME_CHKS, var_smtp_mime_chks,
+                                    VAR_SMTP_NEST_CHKS, var_smtp_nest_chks,
+                                                 smtp_hbc_callbacks);
+    smtp_body_checks = hbc_body_checks_create(
+                                    VAR_SMTP_BODY_CHKS, var_smtp_body_chks,
+                                             smtp_hbc_callbacks);
 }
 
 /* pre_accept - see if tables have changed */
index 72005bfc0c41e0474c7210e34f7fe1b2f92383df..d1e027cf1c4acbac9ce0e3553225c698558f521d 100644 (file)
@@ -30,6 +30,7 @@
 #include <maps.h>
 #include <tok822.h>
 #include <dsn_buf.h>
+#include <header_body_checks.h>
 
  /*
   * Postfix TLS library.
@@ -175,6 +176,9 @@ extern SSL_CTX *smtp_tls_ctx;               /* client-side TLS engine */
 
 #endif
 
+extern HBC_CHECKS *smtp_header_checks; /* limited header checks */
+extern HBC_CHECKS *smtp_body_checks;   /* limited body checks */
+
  /*
   * smtp_session.c
   */
@@ -259,6 +263,8 @@ extern int smtp_xfer(SMTP_STATE *);
 extern int smtp_rset(SMTP_STATE *);
 extern int smtp_quit(SMTP_STATE *);
 
+extern HBC_CALL_BACKS smtp_hbc_callbacks[];
+
  /*
   * A connection is re-usable if session->expire_time is > 0 and the
   * expiration time has not been reached.  This is subtle because the timer
index 463c20813ef46d1219f1bab0d3129c5d2a2d492b..947f5299c824a78e52b7ae02abc1995e9d119cbf 100644 (file)
        VAR_SMTP_PIX_BUG_WORDS, DEF_SMTP_PIX_BUG_WORDS, &var_smtp_pix_bug_words, 0, 0,
        VAR_SMTP_PIX_BUG_MAPS, DEF_SMTP_PIX_BUG_MAPS, &var_smtp_pix_bug_maps, 0, 0,
        VAR_CYRUS_CONF_PATH, DEF_CYRUS_CONF_PATH, &var_cyrus_conf_path, 0, 0,
+       VAR_SMTP_HEAD_CHKS, DEF_SMTP_HEAD_CHKS, &var_smtp_head_chks, 0, 0,
+       VAR_SMTP_MIME_CHKS, DEF_SMTP_MIME_CHKS, &var_smtp_mime_chks, 0, 0,
+       VAR_SMTP_NEST_CHKS, DEF_SMTP_NEST_CHKS, &var_smtp_nest_chks, 0, 0,
+       VAR_SMTP_BODY_CHKS, DEF_SMTP_BODY_CHKS, &var_smtp_body_chks, 0, 0,
        0,
     };
     static CONFIG_TIME_TABLE smtp_time_table[] = {
index a4acfa0cbcaadef087159ccc587dfdd39489f45c..ddee19f7cc3a14448a21041793b27872bdde056a 100644 (file)
@@ -238,6 +238,18 @@ char   *xfer_request[SMTP_STATE_LAST] = {
 
 static int smtp_start_tls(SMTP_STATE *);
 
+ /*
+  * Call-back information for header/body checks. We don't provide call-backs
+  * for actions that change the message delivery time or destination.
+  */
+static void smtp_hbc_logger(void *, const char *, const char *, const char *, const char *);
+static void smtp_text_out(void *, int, const char *, ssize_t, off_t);
+
+HBC_CALL_BACKS smtp_hbc_callbacks[1] = {
+    smtp_hbc_logger,
+    smtp_text_out,
+};
+
 /* smtp_helo - perform initial handshake with SMTP server */
 
 int     smtp_helo(SMTP_STATE *state)
@@ -810,6 +822,23 @@ static int smtp_start_tls(SMTP_STATE *state)
 
 #endif
 
+/* smtp_hbc_logger - logging call-back for header/body checks */
+
+static void smtp_hbc_logger(void *context, const char *action,
+                                   const char *where, const char *content,
+                                   const char *text)
+{
+    const SMTP_STATE *state = (SMTP_STATE *) context;
+
+    if (*text) {
+       msg_info("%s: %s: %s %.60s: %s",
+                state->request->queue_id, action, where, content, text);
+    } else {
+       msg_info("%s: %s: %s %.60s",
+                state->request->queue_id, action, where, content);
+    }
+}
+
 /* smtp_text_out - output one header/body record */
 
 static void smtp_text_out(void *context, int rec_type,
@@ -906,6 +935,21 @@ static void smtp_header_rewrite(void *context, int header_class,
     char   *start;
     char   *next_line;
     char   *end_line;
+    char   *result;
+
+    /*
+     * Apply optional header filtering.
+     */
+    if (smtp_header_checks) {
+       result = hbc_header_checks(context, smtp_header_checks, header_class,
+                                  header_info, buf, offset);
+       if (result == 0)
+           return;
+       if (result != STR(buf)) {
+           vstring_strcpy(buf, result);
+           myfree(result);
+       }
+    }
 
     /*
      * Rewrite primary header addresses that match the smtp_generic_maps. The
@@ -913,7 +957,7 @@ static void smtp_header_rewrite(void *context, int header_class,
      * and that all addresses are in proper form, so we don't have to repeat
      * that.
      */
-    if (header_info && header_class == MIME_HDR_PRIMARY
+    if (smtp_generic_maps && header_info && header_class == MIME_HDR_PRIMARY
        && (header_info->flags & (HDR_OPT_SENDER | HDR_OPT_RECIP)) != 0) {
        TOK822 *tree;
        TOK822 **addr_list;
@@ -982,6 +1026,29 @@ static void smtp_header_rewrite(void *context, int header_class,
     }
 }
 
+/* smtp_body_rewrite - rewrite message body before output */
+
+static void smtp_body_rewrite(void *context, int type,
+                                     const char *buf, ssize_t len,
+                                     off_t offset)
+{
+    SMTP_STATE *state = (SMTP_STATE *) context;
+    char   *result;
+
+    /*
+     * Apply optional body filtering.
+     */
+    if (smtp_body_checks) {
+       result = hbc_body_checks(context, smtp_body_checks, buf, len, offset);
+       if (result == buf) {
+           smtp_text_out(state, type, buf, len, offset);
+       } else if (result != 0) {
+           smtp_text_out(state, type, result, strlen(result), offset);
+           myfree(result);
+       }
+    }
+}
+
 /* smtp_mime_fail - MIME problem */
 
 static void smtp_mime_fail(SMTP_STATE *state, int mime_errs)
@@ -1706,15 +1773,19 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                 * XXX Don't downgrade just because generic_maps is turned
                 * on.
                 */
-               if (downgrading || smtp_generic_maps)
+               if (downgrading || smtp_generic_maps || smtp_header_checks
+                   || smtp_body_checks)
                    session->mime_state = mime_state_alloc(downgrading ?
                                                           MIME_OPT_DOWNGRADE
                                                 | MIME_OPT_REPORT_NESTING :
                                                      MIME_OPT_DISABLE_MIME,
-                                                        smtp_generic_maps ?
+                                                          smtp_generic_maps
+                                                    || smtp_header_checks ?
                                                       smtp_header_rewrite :
                                                           smtp_header_out,
                                                     (MIME_STATE_ANY_END) 0,
+                                                          smtp_body_checks ?
+                                                        smtp_body_rewrite :
                                                           smtp_text_out,
                                                     (MIME_STATE_ANY_END) 0,
                                                   (MIME_STATE_ERR_PRINT) 0,
index 242756124b5d1f037965a4a4773238073dc0269a..4b5842b23019a8ff529a4c253ed942e7da1d4305 100644 (file)
@@ -784,14 +784,35 @@ static void delay_event(int unused_event, char *context)
 {
     SINK_STATE *state = (SINK_STATE *) context;
 
-    state->delayed_response(state, state->delayed_args);
-    myfree(state->delayed_args);
-    state->delayed_args = 0;
+    switch (vstream_setjmp(state->stream)) {
+
+    default:
+       msg_panic("unknown read/write error");
+       /* NOTREACHED */
+
+    case SMTP_ERR_TIME:
+       msg_warn("write timeout");
+       disconnect(state);
+       return;
+
+    case SMTP_ERR_EOF:
+       msg_warn("lost connection");
+       disconnect(state);
+       return;
+
+    case 0:
+       state->delayed_response(state, state->delayed_args);
+       myfree(state->delayed_args);
+       state->delayed_args = 0;
+       break;
+    }
 
     if (state->delayed_response == quit_response) {
        disconnect(state);
        return;
     }
+    state->delayed_response = 0;
+
     /* Resume input event handling after the delayed response. */
     event_enable_read(vstream_fileno(state->stream), read_event, (char *) state);
     event_request_timer(read_timeout, (char *) state, var_tmout);
@@ -1311,7 +1332,7 @@ static void connect_event(int unused_event, char *unused_context)
        case 0:
            if (command_resp(state, command_table, "connect", "") < 0)
                disconnect(state);
-           else {
+           else if (command_table->delay == 0) {
                event_enable_read(fd, read_event, (char *) state);
                event_request_timer(read_timeout, (char *) state, var_tmout);
            }
index d0545aad19721ae4cbf323e7d214bc293228bc62..0aa9eee52da99e604263c361b8bc304215885ff6 100644 (file)
 /*     event_drain() repeatedly calls event_loop() until no more timer
 /*     events or I/O events are pending or until the time limit is reached.
 /*     This routine must not be called from an event_whatever() callback
-/*     routine.
+/*     routine. Note: this function ignores pending timer events, and
+/*     assumes that no new I/O events will be registered.
 /* DIAGNOSTICS
 /*     Panics: interface violations. Fatal errors: out of memory,
 /*     system call failure. Warnings: the number of available
index 6d6e93eaacc1999400db2253bfce7f203d49119c..1ebf3fc6e1782b7ee46f136cd5f1e8debb198bdc 100644 (file)
   * With SIOCGLIFNETMASK we can obtain the netmask for either address family.
   * Again, this is not present in all major operating systems.
   * 
-  * - On Linux, get IPv4 interface information with SIOCGIFCONF, and read IPv6
-  * address/prefix information from a file in the /proc filesystem. Linux
-  * does not return IPv6 addresses with SIOCGIFCONF.
+  * - On Linux, glibc's getifaddrs(3) has returned IPv4 information for some
+  * time, but IPv6 information was not returned until 2.3.3. With older Linux
+  * versions we get IPv4 interface information with SIOCGIFCONF, and read
+  * IPv6 address/prefix information from a file in the /proc filesystem.
   * 
   * - On other systems we expect SIOCGIFCONF to return IPv6 addresses. Since
   * SIOCGIFNETMASK does not work reliably for IPv6 addresses, we always set
@@ -437,7 +438,7 @@ static int ial_siocgif(INET_ADDR_LIST *addr_list,
 #ifdef HAS_PROCNET_IFINET6
 
 /*
- * Linux does not provide proper calls to retrieve IPv6 interface
+ * Older Linux versions lack proper calls to retrieve IPv6 interface
  * addresses. Instead, the addresses can be read from a file in the
  * /proc tree. The most important issue with this approach however
  * is that the /proc tree may not always be available, for example
index 3576d690089a8f10f12c80bbc903df5f89637466..af3b1fd176ffaf48d4121e3a26685b897937c8f4 100644 (file)
@@ -724,9 +724,14 @@ extern int initgroups(const char *, int);
 #endif
 #ifndef NO_IPV6
 # define HAS_IPV6
+#if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2,4)
+/* Really 2.3.3 or later, but there's no __GLIBC_MICRO version macro. */
+# define HAVE_GETIFADDRS
+#else
 # define HAS_PROCNET_IFINET6
 # define _PATH_PROCNET_IFINET6 "/proc/net/if_inet6"
 #endif
+#endif
 #include <linux/version.h>
 #if !defined(KERNEL_VERSION) 
 # define KERNEL_VERSION(a,b,c) (LINUX_VERSION_CODE + 1)