]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.8-20100912
authorWietse Venema <wietse@porcupine.org>
Sun, 12 Sep 2010 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:36:29 +0000 (06:36 +0000)
69 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/AAAREADME
postfix/README_FILES/DATABASE_README
postfix/README_FILES/POSTSCREEN_README [new file with mode: 0644]
postfix/README_FILES/QSHAPE_README
postfix/README_FILES/SASL_README
postfix/README_FILES/XFORWARD_README
postfix/RELEASE_NOTES
postfix/WISHLIST
postfix/conf/postfix-files
postfix/html/DATABASE_README.html
postfix/html/POSTSCREEN_README.html [new file with mode: 0644]
postfix/html/QSHAPE_README.html
postfix/html/SASL_README.html
postfix/html/XFORWARD_README.html
postfix/html/index.html
postfix/html/postconf.1.html
postfix/html/postconf.5.html
postfix/html/postscreen.8.html
postfix/html/smtpd.8.html
postfix/man/man1/postconf.1
postfix/man/man5/postconf.5
postfix/man/man8/postscreen.8
postfix/man/man8/smtpd.8
postfix/mantools/postlink
postfix/proto/DATABASE_README.html
postfix/proto/Makefile.in
postfix/proto/POSTSCREEN_README.html [new file with mode: 0644]
postfix/proto/QSHAPE_README.html
postfix/proto/SASL_README.html
postfix/proto/XFORWARD_README.html
postfix/proto/postconf.proto
postfix/proto/stop
postfix/src/global/Makefile.in
postfix/src/global/dict_sqlite.c
postfix/src/global/mail_conf.h
postfix/src/global/mail_conf_nbool.c [new file with mode: 0644]
postfix/src/global/mail_conf_nint.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/master/event_server.c
postfix/src/master/mail_server.h
postfix/src/master/multi_server.c
postfix/src/master/single_server.c
postfix/src/master/trigger_server.c
postfix/src/postconf/Makefile.in
postfix/src/postconf/extract.awk
postfix/src/postconf/postconf.c
postfix/src/postscreen/Makefile.in
postfix/src/postscreen/postscreen.c
postfix/src/postscreen/postscreen.h
postfix/src/postscreen/postscreen_dnsbl.c
postfix/src/postscreen/postscreen_early.c [new file with mode: 0644]
postfix/src/postscreen/postscreen_misc.c [new file with mode: 0644]
postfix/src/postscreen/postscreen_send.c [new file with mode: 0644]
postfix/src/postscreen/postscreen_smtpd.c [new file with mode: 0644]
postfix/src/postscreen/postscreen_state.c [new file with mode: 0644]
postfix/src/postscreen/postscreen_tests.c [new file with mode: 0644]
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd_proxy.c
postfix/src/smtpstone/smtp-source.c
postfix/src/util/Makefile.in
postfix/src/util/dict.c
postfix/src/util/dict_db.c
postfix/src/util/dict_dbm.h
postfix/src/util/dict_open.c
postfix/src/util/dict_thash.c [new file with mode: 0644]
postfix/src/util/dict_thash.h [new file with mode: 0644]

index b07186824b344dffa94c33b016388ca9ef549453..a8b07760ba2f81b3c49a20e194d3062583c04e34 100644 (file)
@@ -40,6 +40,8 @@
 -TCONFIG_INT_TABLE
 -TCONFIG_LONG_FN_TABLE
 -TCONFIG_LONG_TABLE
+-TCONFIG_NBOOL_FN_TABLE
+-TCONFIG_NBOOL_TABLE
 -TCONFIG_NCODE_TABLE
 -TCONFIG_NINT_FN_TABLE
 -TCONFIG_NINT_TABLE
@@ -95,6 +97,7 @@
 -TDICT_SDBM
 -TDICT_SQLITE
 -TDICT_TCP
+-TDICT_TEXT
 -TDICT_UNIX
 -TDNS_FIXED
 -TDNS_REPLY
 -TPOSTMAP_KEY_STATE
 -TPOST_MAIL_STATE
 -TPRIVATE_STR_TABLE
--TPS_DNSBL_SITE
+-TPS_DNSBL_HEAD
 -TPS_DNSBL_SCORE
+-TPS_DNSBL_SITE
+-TPS_SMTPD_COMMAND
 -TPS_STATE
 -TQMGR_ENTRY
 -TQMGR_FEEDBACK
index 038bf8aeb90e6c5b866ae683e13672101d48098f..0f7610f99129fc82382b9e36224d4177966e68d1 100644 (file)
@@ -15935,3 +15935,37 @@ Apologies for any names omitted.
 
        Polished the postscreen documentation and comments to clarify
        the user interface and implementation. No code changes.
+
+20100831-910
+
+       Restructured postscreen and added support for a dummy SMTP
+       protocol engine. This engine logs rejected attempts to
+       deliver mail with helo/sender/recipient information, and
+       implements deep protocol tests.  The first deep protocol
+       test is for command pipelining, where a client sends multiple
+       commands instead of waiting for the server to respond to
+       each command. The second one implements the Postfix SMTP
+       server's smtpd_forbidden_commands feature.  Files:
+       postscreen/*.[hc]. See RELEASE_NOTES, postconf(5) and
+       postscreen(8) for incompatibilities, features, and configuration
+       parameters.
+
+20100910
+
+       Feature: boolean configuration parameters with string-valued
+       defaults, so that they can be subject to macro expansions.
+       This was needed to make some postscreen parameter defaults
+       to the values of the corresponding smtpd parameters.  Files:
+       global/mail_conf.h, global/mail_conf_nbool.c,
+       master/event_server.c, master/mail_server.h, master/multi_server.c,
+       master/single_server.c, master/trigger_server.c,
+       postconf/extract.awk, postconf/postconf.c.
+
+20100911
+
+       Feature: texthash read-only database. This is similar to
+       hash: files, except that you don't need to run the postmap(1)
+       command before you can use the file, and that it does not
+       detect changes after the file is read.  All information is
+       read into memory. Files: util/dict_open.c, util/dict_thash.[hc],
+       proto/DATABSE_README.html, postconf/postconf.c
index 5e026eb6ef23eb3cd333587e69696e60c161d769..605390d513f97d63e046250f0ffc4cf1a22479c8 100644 (file)
@@ -38,6 +38,7 @@ S\bSM\bMT\bTP\bP R\bRe\bel\bla\bay\by a\ban\bnd\bd a\bac\bcc\bce\bes\bss\bs c\bco\bon\bnt\btr\bro\bol\bl
   * SMTPD_POLICY_README: Access policy delegation
   * ADDRESS_VERIFICATION_README: Address verification
   * RESTRICTION_CLASS_README: Per-client/user/etc. access
+  * POSTSCREEN_README: SMTP connection triage
   * ETRN_README: ETRN Support
   * UUCP_README: LAN connected via UUCP
 
index 77d996a65a9b2a93f7dab09501a191307db9eb46..0e4e85d8b5f6dd99f2f04b1ef70f1549a7d37d7a 100644 (file)
@@ -252,6 +252,12 @@ To find out what database types your Postfix system supports, use the "p\bpo\bos\bs
         in tcp_table(5). The lookup table name is "tcp:host:port" where "host"
         specifies a symbolic hostname or a numeric IP address, and "port"
         specifies a symbolic service name or a numeric port number.
+    t\bte\bex\bxt\bth\bha\bas\bsh\bh (read-only)
+        This produces similar results as hash: files, except that you don't
+        have to run the postmap(1) command before you can use the file, and
+        that it does not detect changes after the file is read. The lookup
+        table name is "texthash:filename", where the file name is taken
+        literally; no suffix is appended.
     u\bun\bni\bix\bx (read-only)
         A limited way to query the UNIX authentication database. The following
         tables are implemented:
diff --git a/postfix/README_FILES/POSTSCREEN_README b/postfix/README_FILES/POSTSCREEN_README
new file mode 100644 (file)
index 0000000..3de9a96
--- /dev/null
@@ -0,0 +1,519 @@
+P\bPo\bos\bst\btf\bfi\bix\bx P\bPo\bos\bst\bts\bsc\bcr\bre\bee\ben\bn H\bHo\bow\bwt\bto\bo
+
+-------------------------------------------------------------------------------
+
+I\bIn\bnt\btr\bro\bod\bdu\buc\bct\bti\bio\bon\bn
+
+The Postfix postscreen(8) server performs triage on multiple inbound SMTP
+connections in parallel. While one postscreen(8) process keeps spambots away
+from Postfix SMTP server processes, more Postfix SMTP server processes remain
+available for legitimate clients.
+
+By doing these checks in a single postscreen(8) process, Postfix can avoid
+wasting one SMTP server process per connection. A side benefit of postscreen
+(8)'s DNSBL lookups is that DNS records are already cached before the Postfix
+SMTP server looks them up later.
+
+postscreen(8) maintains a temporary whitelist of positive decisions. Once an
+SMTP client is whitelisted, it is immediately forwarded to a real Postfix SMTP
+server process without further checking.
+
+By default, the program logs only statistics, and it does not run any checks on
+clients in mynetworks (primarily, to avoid problems with buggy SMTP
+implementations in network appliances).
+
+Many of the ideas in postscreen(8) have been explored in earlier work by
+Michael Tokarev, in OpenBSD spamd, and in MailChannels Traffic Control.
+
+Topics in this document:
+
+  * Introduction
+  * The basic idea behind postscreen(8)
+  * General operation
+  * Quick tests before everything else
+  * Tests before the 220 SMTP server greeting
+  * Tests after the 220 SMTP server greeting
+  * Other errors
+  * When all tests succeed
+  * Configuring the postscreen(8) service
+
+T\bTh\bhe\be b\bba\bas\bsi\bic\bc i\bid\bde\bea\ba b\bbe\beh\bhi\bin\bnd\bd p\bpo\bos\bst\bts\bsc\bcr\bre\bee\ben\bn(\b(8\b8)\b)
+
+Spambots have a limited amount of time to send out spam before they become
+blacklisted. For this reason, spambots make compromises in their SMTP protocol
+implementation to speed up spam deliveries. For example, they speak before
+their turn.
+
+Many spambots avoid spamming the same site repeatedly. Thus, postscreen(8) must
+make a long-term decision after a single measurement. For example, allow a good
+client to skip the DNSBL test for 24 hours.
+
+To recognize spambots, postscreen(8) measures properties of the client IP
+address and of the client SMTP protocol implementation (the protocol
+compromises that were made to speed up delivery). These properties don't change
+with delivery attempts, and are therefore suitable for making a long-term
+decision after a single measurement.
+
+postscreen(8) does not inspect message content. The reason is that content can
+change with each delivery attempt, especially with legitimate clients. Message
+content is not good for making a long-term decision after a single measurement,
+and that is the problem that postscreen(8) is focused on.
+
+G\bGe\ben\bne\ber\bra\bal\bl o\bop\bpe\ber\bra\bat\bti\bio\bon\bn
+
+The postscreen(8) triage process involves a number of tests, in the order as
+described below. Some tests introduce a delay of a few seconds. Once a client
+passes all tests, its IP address is temporarily excluded from any tests,
+typically 24 hours for simple tests or 1 week for complex tests. This minimizes
+the impact of the tests on legitimate mail clients.
+
+After logging the result of its tests, postscreen(8) by default forwards all
+connections to a real SMTP server process. This mode is useful for non-
+destructive testing.
+
+In a typical production setting, postscreen(8) is configured to reject mail
+from clients that fail one or more tests, after logging the sender and
+recipient information.
+
+Note: postscreen(8) is not an SMTP proxy; this is intentional. The purpose is
+to prioritize legitimate clients with as little overhead as possible.
+
+Q\bQu\bui\bic\bck\bk t\bte\bes\bst\bts\bs b\bbe\bef\bfo\bor\bre\be e\bev\bve\ber\bry\byt\bth\bhi\bin\bng\bg e\bel\bls\bse\be
+
+Before engaging in SMTP-level tests. postscreen(8) queries a number of local
+black and whitelists. These tests speed up the handling of known clients.
+
+  * Permanent whitelist test
+  * Permanent blacklist test
+  * Temporary whitelist test
+
+P\bPe\ber\brm\bma\ban\bne\ben\bnt\bt w\bwh\bhi\bit\bte\bel\bli\bis\bst\bt t\bte\bes\bst\bt
+
+The postscreen_whitelist_networks parameter (default: $mynetworks) specifies a
+permanent whitelist for SMTP client IP addresses. When the SMTP client address
+matches the permanent whitelist, this is logged as:
+
+    W\bWH\bHI\bIT\bTE\bEL\bLI\bIS\bST\bTE\bED\bD address
+
+The action is not configurable: immediately forward the connection to a real
+SMTP server process.
+
+P\bPe\ber\brm\bma\ban\bne\ben\bnt\bt b\bbl\bla\bac\bck\bkl\bli\bis\bst\bt t\bte\bes\bst\bt
+
+The postscreen_blacklist_networks parameter (default: empty) specifies a
+permanent blacklist for SMTP client IP addresses. The address syntax is as with
+mynetworks. When the SMTP client address matches the permanent blacklist,
+postscreen(8) logs this as:
+
+    B\bBL\bLA\bAC\bCK\bKL\bLI\bIS\bST\bTE\bED\bD address
+
+The postscreen_blacklist_action parameter specifies the action that is taken
+next. See "When tests fail before the 220 SMTP server greeting" below.
+
+T\bTe\bem\bmp\bpo\bor\bra\bar\bry\by w\bwh\bhi\bit\bte\bel\bli\bis\bst\bt t\bte\bes\bst\bt
+
+The postscreen(8) daemon maintains a temporary whitelist for SMTP client IP
+addresses that have passed all the tests described below. The
+postscreen_cache_map parameter specifies the location of the temporary
+whitelist. The temporary whitelist is not used for SMTP client addresses that
+appear on the permanent blacklist or whitelist.
+
+When the SMTP client address appears on the temporary whitelist, postscreen(8)
+logs this as:
+
+    P\bPA\bAS\bSS\bS O\bOL\bLD\bD address
+
+The action is not configurable: immediately forward the connection to a real
+SMTP server process. The client is excluded from further tests until its
+temporary whitelist entry expires, as controlled with the postscreen_*_ttl
+parameters. Expired entries are silently renewed if possible.
+
+T\bTe\bes\bst\bts\bs b\bbe\bef\bfo\bor\bre\be t\bth\bhe\be 2\b22\b20\b0 S\bSM\bMT\bTP\bP s\bse\ber\brv\bve\ber\br g\bgr\bre\bee\bet\bti\bin\bng\bg
+
+The postscreen_greet_wait parameter specifies a short time interval before the
+"220 text..." server greeting, where postscreen(8) can run a number of tests in
+parallel.
+
+When a good client passes these tests, and no "deep protocol tests" are
+configured, postscreen(8) adds the client to the temporary whitelist and passes
+the "live" connection to a Postfix SMTP server process. The client can then
+continue as if postscreen(8) never even existed (except of course for the short
+postscreen_greet_wait delay).
+
+  * Pregreet test
+  * DNS Blocklist test
+  * When tests fail before the 220 SMTP server greeting
+
+P\bPr\bre\beg\bgr\bre\bee\bet\bt t\bte\bes\bst\bt
+
+The SMTP protocol is a classic example of a protocol where the server speaks
+before the client. postscreen(8) detects spambots that are in a hurry and that
+speak before their turn. This test is enabled by default.
+
+The postscreen_greet_banner parameter specifies the text portion of a "220-
+text..." teaser banner (default: $smtpd_banner). Note that this becomes the
+first part of a multi-line server greeting. The postscreen(8) daemon sends this
+before the postscreen_greet_wait timer is started. The purpose of the teaser
+banner is to confuse spambots so that they speak before their turn. It has no
+effect on SMTP clients that correctly implement the protocol.
+
+To avoid problems with poorly-implemented SMTP engines in network appliances or
+network testing tools, either exclude them from all tests with the
+postscreen_whitelist_networks feature or else specify an empty teaser banner
+with:
+
+/etc/postfix/main.cf:
+    postscreen_greet_banner =
+
+When an SMTP client sends a command before the postscreen_greet_wait time has
+elapsed, postscreen(8) logs this as:
+
+    P\bPR\bRE\bEG\bGR\bRE\bEE\bET\bT count a\baf\bft\bte\ber\br time f\bfr\bro\bom\bm address text...
+
+Translation: the client at address sent count bytes before its turn to speak.
+This happened time seconds after the postscreen_greet_wait timer was started.
+The text is what the client sent (truncated to 100 bytes, and with non-
+printable characters replaced with "?").
+
+The postscreen_greet_action parameter specifies the action that is taken next.
+See "When tests fail before the 220 SMTP server greeting" below.
+
+D\bDN\bNS\bS B\bBl\blo\boc\bck\bkl\bli\bis\bst\bt t\bte\bes\bst\bt
+
+The postscreen_dnsbl_sites parameter (default: empty) specifies a list of DNS
+blocklist servers with optional filters and weight factors. These servers will
+be queried in parallel with the reverse client IP address. This test is
+disabled by default.
+
+    CAUTION: when postscreen rejects mail, it replies with the DNSBL domain
+    name. Use the postscreen_dnsbl_reply_map feature to hide "password"
+    information in DNSBL domain names.
+
+When the postscreen_greet_wait time has elapsed, and the combined DNSBL score
+is equal to or greater than the postscreen_dnsbl_threshold parameter value,
+postscreen(8) logs this as:
+
+D\bDN\bNS\bSB\bBL\bL r\bra\ban\bnk\bk count f\bfo\bor\br address
+
+Translation: the SMTP client at address has a combined DNSBL score of count.
+
+The postscreen_dnsbl_action parameter specifies the action that is taken when
+the combined DNSBL score is equal to or greater than the threshold. See "When
+tests fail before the 220 SMTP server greeting" below.
+
+W\bWh\bhe\ben\bn t\bte\bes\bst\bts\bs f\bfa\bai\bil\bl b\bbe\bef\bfo\bor\bre\be t\bth\bhe\be 2\b22\b20\b0 S\bSM\bMT\bTP\bP s\bse\ber\brv\bve\ber\br g\bgr\bre\bee\bet\bti\bin\bng\bg
+
+When the client address matches the permanent blacklist, or when the client
+fails the pregreet or DNSBL tests, the action is specified with
+postscreen_blacklist_action, postscreen_greet_action, or
+postscreen_dnsbl_action, respectively.
+
+i\big\bgn\bno\bor\bre\be (default)
+    Ignore the failure of this test. Allow other tests to complete. Repeat this
+    test the next time the client connects. This option is useful for testing
+    and collecting statistics without interfering with mail deliveries.
+e\ben\bnf\bfo\bor\brc\bce\be
+    Allow other tests to complete. Reject attempts to deliver mail with a 550
+    SMTP reply, and log the helo/sender/recipient information. Repeat this test
+    the next time the client connects.
+d\bdr\bro\bop\bp
+    Drop the connection immediately with a 521 SMTP reply. Repeat this test the
+    next time the client connects.
+
+T\bTe\bes\bst\bts\bs a\baf\bft\bte\ber\br t\bth\bhe\be 2\b22\b20\b0 S\bSM\bMT\bTP\bP s\bse\ber\brv\bve\ber\br g\bgr\bre\bee\bet\bti\bin\bng\bg
+
+The tests in this phase use an SMTP protocol engine that is built into the
+postscreen(8) server.
+
+Important notes:
+
+  * These tests are disabled by default, because they are more intrusive than
+    the pregreet and DNSBL tests.
+
+    When a good client passes the deep protocol tests, postscreen(8) adds the
+    client to the temporary whitelist but it cannot pass the "live" connection
+    to a Postfix SMTP server process in the middle of the session. Instead,
+    postscreen(8) defers mail delivery attempts with a 4XX status, logs the
+    helo/sender/recipient information, and waits for the client to disconnect.
+
+    The next time the client connects it will be allowed to talk to a real SMTP
+    server process to deliver its mail.
+
+    To minimize the impact of these tests, postscreen(8) gives them relatively
+    long expiration times.
+
+  * postscreen(8) does not implement the AUTH, STARTTLS, XCLIENT, and XFORWARD
+    features. STARTTLS support may be added in a future version.
+
+End-user clients should connect directly to the submission service. Other
+systems that require the above features should directly connect to a Postfix
+SMTP server, or they should be placed on the postscreen(8) whitelist.
+
+  * Command pipelining test
+  * Non-SMTP command test
+  * Bare newline test
+  * When tests fail after the 220 SMTP server greeting
+
+C\bCo\bom\bmm\bma\ban\bnd\bd p\bpi\bip\bpe\bel\bli\bin\bni\bin\bng\bg t\bte\bes\bst\bt
+
+By default, SMTP is a half-duplex protocol: the sender and receiver send one
+command and one response at a time. Unlike the real Postfix SMTP server,
+postscreen(8) does not announce support for ESMTP command pipelining.
+Therefore, clients are not allowed to send multiple commands. This test is
+disabled by default.
+
+With "postscreen_pipelining_enable = yes", postscreen(8) detects spambots that
+send multiple commands, instead of sending one command and waiting for the
+server to reply.
+
+This test is opportunistically enabled when enabled when postscreen(8) has to
+use the built-in SMTP engine anyway, to make postscreen(8) logging more
+informative.
+
+When a client sends multiple commands, postscreen(8) logs this as:
+
+C\bCO\bOM\bMM\bMA\bAN\bND\bD P\bPI\bIP\bPE\bEL\bLI\bIN\bNI\bIN\bNG\bG a\baf\bft\bte\ber\br time f\bfr\bro\bom\bm address
+
+Translation: the SMTP client at address sent multiple SMTP commands, instead of
+sending one command and then waiting for the server to reply. This happened
+time seconds after the "220 " server greeting was sent.
+
+The postscreen_pipelining_action parameter specifies the action that is taken
+next. See "When tests fail after the 220 SMTP server greeting" below.
+
+N\bNo\bon\bn-\b-S\bSM\bMT\bTP\bP c\bco\bom\bmm\bma\ban\bnd\bd t\bte\bes\bst\bt
+
+With "postscreen_non_smtp_command_enable = yes", postscreen(8) detects spambots
+that send non-SMTP commands, such as commands specified with the
+postscreen_forbidden_commands parameter, and commands that have the syntax of a
+message header label.
+
+This test is disabled by default. The test is opportunistically enabled when
+postscreen(8) has to use the built-in SMTP engine anyway, to make postscreen(8)
+logging more informative.
+
+When a client sends non-SMTP commands, postscreen(8) logs this as:
+
+    N\bNO\bON\bN-\b-S\bSM\bMT\bTP\bP C\bCO\bOM\bMM\bMA\bAN\bND\bD f\bfr\bro\bom\bm address command
+
+Translation: the SMTP client at address sent a command that matches the
+postscreen_forbidden_commands parameter, or that has the syntax of a message
+header label.
+
+The postscreen_non_smtp_command_action parameter specifies the action that is
+taken next. See "When tests fail after the 220 SMTP server greeting" below.
+
+B\bBa\bar\bre\be n\bne\bew\bwl\bli\bin\bne\be t\bte\bes\bst\bt
+
+SMTP is a line-oriented protocol: lines have a limited length, and are
+terminated with <CR><LF>.
+
+With "postscreen_bare_newline_enable = yes", postscreen(8) detects spambots
+that send lines ending in bare newline characters, that is newline not preceded
+by carriage return.
+
+This test is disabled by default. The test is opportunistically enabled when
+postscreen(8) has to use the built-in SMTP engine anyway, to make postscreen(8)
+logging more informative.
+
+When a client sends bare newline characters, postscreen(8) logs this as:
+
+    B\bBA\bAR\bRE\bE N\bNE\bEW\bWL\bLI\bIN\bNE\bE f\bfr\bro\bom\bm address
+
+Translation: the SMTP client at address sent a bare newline character, that is
+newline not preceded by carriage return.
+
+The postscreen_bare_newline_action parameter specifies the action that is taken
+next. See "When tests fail after the 220 SMTP server greeting" below.
+
+W\bWh\bhe\ben\bn t\bte\bes\bst\bts\bs f\bfa\bai\bil\bl a\baf\bft\bte\ber\br t\bth\bhe\be 2\b22\b20\b0 S\bSM\bMT\bTP\bP s\bse\ber\brv\bve\ber\br g\bgr\bre\bee\bet\bti\bin\bng\bg
+
+When the client fails the pipelining, non-SMTP command or bare newline tests,
+the action is specified with postscreen_pipelining_action,
+postscreen_non_smtp_command_action or postscreen_bare_newline_action,
+respectively.
+
+i\big\bgn\bno\bor\bre\be (default for bare newline)
+    Ignore the failure of this test. Allow other tests to complete. Do NOT
+    repeat this test before the result from some other test expires. This
+    option is useful for testing and collecting statistics without blocking
+    mail permanently.
+e\ben\bnf\bfo\bor\brc\bce\be (default for pipelining)
+    Allow other tests to complete. Reject attempts to deliver mail with a 550
+    SMTP reply, and log the helo/sender/recipient information. Repeat this test
+    the next time the client connects.
+d\bdr\bro\bop\bp (default for non-SMTP commands)
+    Drop the connection immediately with a 521 SMTP reply. Repeat this test the
+    next time the client connects. This action is compatible with the Postfix
+    SMTP server's smtpd_forbidden_commands feature.
+
+O\bOt\bth\bhe\ber\br e\ber\brr\bro\bor\brs\bs
+
+When an SMTP client hangs up unexpectedly during any tests, postscreen(8) logs
+this as:
+
+    H\bHA\bAN\bNG\bGU\bUP\bP a\baf\bft\bte\ber\br time f\bfr\bro\bom\bm address i\bin\bn test name
+
+Translation: the SMTP client at address disconnected unexpectedly, time seconds
+after the start of the test named test name.
+
+The following errors are reported by the built-in SMTP engine. This engine
+never accepts mail, therefore it has per-session limits on the number of
+commands and on the session length.
+
+    C\bCO\bOM\bMM\bMA\bAN\bND\bD T\bTI\bIM\bME\bE L\bLI\bIM\bMI\bIT\bT f\bfr\bro\bom\bm address
+
+Translation: the SMTP client at address reached the per-command time limit as
+specified with the postscreen_command_time_limit parameter. The session is
+terminated immediately.
+
+    C\bCO\bOM\bMM\bMA\bAN\bND\bD C\bCO\bOU\bUN\bNT\bT L\bLI\bIM\bMI\bIT\bT f\bfr\bro\bom\bm address
+
+Translation: the SMTP client at address reached the per-session command count
+limit as specified with the postscreen_command_count_limit parameter. The
+session is terminated immediately.
+
+    C\bCO\bOM\bMM\bMA\bAN\bND\bD L\bLE\bEN\bNG\bGT\bTH\bH L\bLI\bIM\bMI\bIT\bT f\bfr\bro\bom\bm address
+
+Translation: the SMTP client at address reached the per-command length limit,
+as specified with the line_length_limit parameter. The session is terminated
+immediately.
+
+W\bWh\bhe\ben\bn a\bal\bll\bl t\bte\bes\bst\bts\bs s\bsu\buc\bcc\bce\bee\bed\bd
+
+When a new SMTP client passes all tests (i.e. it is not whitelisted via some
+mechanism), postscreen(8) logs this as:
+
+    P\bPA\bAS\bSS\bS N\bNE\bEW\bW address
+
+Where address is the client IP address. Then, postscreen(8) creates a temporary
+whitelist entry that excludes the client IP address from further tests until
+the temporary whitelist entry expires, as controlled with the postscreen_*_ttl
+parameters.
+
+When no "deep procol tests" are configured, postscreen(8) passes the "live"
+connection to a Postfix SMTP server process. The client can then continue as if
+postscreen(8) never even existed (except for the short postscreen_greet_wait
+delay).
+
+When any "deep procol tests" are configured, postscreen(8) cannot pass the
+"live" connection to a Postfix SMTP server process. Instead, postscreen(8)
+defers mail delivery attempts with a 4XX status, logs the helo/sender/recipient
+information, and waits for the client to disconnect. The next time the client
+connects it will be allowed to talk to a Postfix SMTP server process to deliver
+its mail.
+
+C\bCo\bon\bnf\bfi\big\bgu\bur\bri\bin\bng\bg t\bth\bhe\be p\bpo\bos\bst\bts\bsc\bcr\bre\bee\ben\bn(\b(8\b8)\b) s\bse\ber\brv\bvi\bic\bce\be
+
+postscreen(8) has been tested on FreeBSD [4-8] and Linux 2.[4-6] systems. It
+probably needs additional work before it can be used on Solaris.
+
+  * Turning on postscreen(8) without blocking mail
+  * Blocking mail with postscreen(8)
+  * Turning off postscreen(8)
+
+T\bTu\bur\brn\bni\bin\bng\bg o\bon\bn p\bpo\bos\bst\bts\bsc\bcr\bre\bee\ben\bn(\b(8\b8)\b) w\bwi\bit\bth\bho\bou\but\bt b\bbl\blo\boc\bck\bki\bin\bng\bg m\bma\bai\bil\bl
+
+To enable the postscreen(8) service and log client information without blocking
+mail:
+
+ 1. Comment out the "smtp inet ... smtpd" service in master.cf, including any
+    "-o parameter=value" entries that follow.
+
+ 2. Uncomment the new "smtpd pass ... smtpd" service in master.cf, and
+    duplicate any "-o parameter=value" entries from the smtpd service that was
+    commented out in step 1.
+
+ 3. Uncomment the new "smtp inet ... postscreen" service in master.cf.
+
+ 4. Uncomment the new "dnsblog unix ... dnsblog" service in master.cf. This
+    service does DNSBL lookups for postscreen(8) and logs results.
+
+ 5. To enable DNSBL lookups, list some DNS blocklist sites in main.cf,
+    separated by whitespace. Different sites can have different weights. For
+    example:
+
+        postscreen_dnsbl_threshold = 2
+        postscreen_dnsbl_sites = zen.spamhaus.org*2 example.com*1 example.net*1
+
+    Note: if your DNSBL queries have a "secret" in the domain name, you must
+    censor this information from the postscreen(8) SMTP replies. For example:
+
+    /etc/postfix/main.cf:
+        postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
+
+    /etc/postfix/dnsbl_reply:
+        # Secret DNSBL name        Name in postscreen(8) replies
+        secret.zen.spamhaus.org    zen.spamhaus.org
+
+    The texthash: format is similar to hash: except that there is no need to
+    run postmap(1) before the file can be used, and that it does not detect
+    changes after the file is read. It is new with Postfix version 2.8.
+
+ 6. Read the new configuration with "postfix reload".
+
+Notes:
+
+  * See "Tests before the 220 SMTP server greeting" for details about the
+    logging from these postscreen(8) tests.
+
+  * By default, postscreen(8) whitelists all clients in mynetworks. This is a
+    safety feature to avoid you from getting into trouble with local users.
+
+  * If you run Postfix 2.6 or earlier you must stop and start the master daemon
+    ("postfix stop; postfix start"). This is needed because the Postfix "pass"
+    master service type did not work reliably on all systems.
+
+B\bBl\blo\boc\bck\bki\bin\bng\bg m\bma\bai\bil\bl w\bwi\bit\bth\bh p\bpo\bos\bst\bts\bsc\bcr\bre\bee\ben\bn(\b(8\b8)\b)
+
+To use the postscreen(8) service to block mail, edit main.cf and specify one or
+more of:
+
+  * "postscreen_dnsbl_action = enforce", to reject clients that are on DNS
+    blocklists, and to log the helo/sender/recipient information. With good
+    DNSBLs this reduces the amount of load on Postfix SMTP servers
+    dramatically.
+
+  * "postscreen_greet_action = enforce", to reject clients that talk before
+    their turn, and to log the helo/sender/recipient information. This stops
+    over half of all known-to-be illegitimate connections to Wietse's mail
+    server. It is backup protection for spambots that haven't yet been
+    blacklisted.
+
+  * You can also enable "deep protocol tests", but these are more intrusive
+    than the pregreet or DNSBL tests.
+
+    When a good client passes the "deep protocol tests", postscreen(8) adds the
+    client to the temporary whitelist but it cannot pass the "live" connection
+    to a Postfix SMTP server process in the middle of the session. Instead,
+    postscreen(8) defers mail delivery attempts with a 4XX status, logs the
+    helo/sender/recipient information, and waits for the client to disconnect.
+
+    When the client comes back in a later session, it is allowed to talk
+    directly to a Postfix SMTP server. See "after_220 Tests after the 220 SMTP
+    server greeting above for limitations with STARTTLS, AUTH and other
+    features that clients may need. Wietse enables "deep protocol tests" on his
+    own internet-facing mail server.
+
+  * There is also support for permanent blacklists and whitelists; see the
+    description of the postscreen_whitelist_networks and
+    postscreen_blacklist_networks parameters for details.
+
+T\bTu\bur\brn\bni\bin\bng\bg o\bof\bff\bf p\bpo\bos\bst\bts\bsc\bcr\bre\bee\ben\bn(\b(8\b8)\b)
+
+To turn off postscreen(8) and handle mail directly with Postfix SMTP server
+processes:
+
+ 1. Comment out the "smtp inet ... postscreen" service in master.cf, including
+    any "-o parameter=value" entries that follow.
+
+ 2. Comment out the "dnsblog unix ... dnsblog" service in master.cf.
+
+ 3. Comment out the "smtpd pass ... smtpd" service in master.cf, including any
+    "-o parameter=value" entries that follow.
+
+ 4. Uncomment the "smtp inet ... smtpd" service in master.cf, including any "-
+    o parameter=value" entries that follow.
+
+ 5. Read the new configuration with "postfix reload".
+
index eddcc9847d6f4f959b09fdea5b0e5baa204c55c8..311797a6f6ff7b13da298e1f35e3b4fb789a597e 100644 (file)
@@ -217,7 +217,9 @@ E\bEx\bxa\bam\bmp\bpl\ble\be 3\b3:\b: C\bCo\bon\bng\bge\bes\bst\bti\bio\bon\bn i\bin\bn t\bth\bhe\be a\ba
 This example is taken from a Feb 2004 discussion on the Postfix Users list.
 Congestion was reported with the active and incoming queues large and not
 shrinking despite very large delivery agent process limits. The thread is
-archived at: http://groups.google.com/groups?th=636626c645f5bbde
+archived at: http://groups.google.com/
+groups?threadm=c0b7js$2r65$1@FreeBSD.csie.NCTU.edu.tw and http://
+archives.neohapsis.com/archives/postfix/2004-02/thread.html#1371
 
 Using an older version of qshape(1) it was quickly determined that all the
 messages were for just a few destinations:
index 193e787b4aa3e1d4d695ad16064c06ebb04ad9fe..2ebd342cde3430dee644162b9a339195eb037991 100644 (file)
@@ -1167,8 +1167,8 @@ A more sophisticated policy allows plaintext mechanisms, but only over a TLS-
 encrypted connection:
 
     /etc/postfix/main.cf:
-        smtpd_sasl_security_options = noanonymous, noplaintext
-        smtpd_sasl_tls_security_options = noanonymous
+        smtp_sasl_security_options = noanonymous, noplaintext
+        smtp_sasl_tls_security_options = noanonymous
 
 P\bPo\bos\bst\btf\bfi\bix\bx S\bSM\bMT\bTP\bP/\b/L\bLM\bMT\bTP\bP c\bcl\bli\bie\ben\bnt\bt p\bpo\bol\bli\bic\bcy\by -\b- S\bSA\bAS\bSL\bL m\bme\bec\bch\bha\ban\bni\bis\bsm\bm n\bna\bam\bme\bes\bs
 
index 11a13cd0f058ec6d943e6e975748afdf0b74b3fa..84802ed80f1752c1f80f36f3d90f24ff7c5af654 100644 (file)
@@ -16,7 +16,7 @@ attributes. The XFORWARD command targets the following problem:
     information through the content filter to MTA2, so that the information
     could be logged as part of mail handling transactions.
 
-This extension is implemented as a separate EMSTP command, and can be used to
+This extension is implemented as a separate ESMTP command, and can be used to
 transmit client or message attributes incrementally. It is not implemented by
 passing additional parameters via the MAIL FROM command, because doing so would
 require extending the MAIL FROM command length limit by another 600 or more
index a3f0f9ea21750742d81a3f362526483816b9463e..36b2cec30811362df5e8a39377ffc893f6feaac6 100644 (file)
@@ -14,6 +14,69 @@ specifies the release date of a stable release or snapshot release.
 If you upgrade from Postfix 2.6 or earlier, read RELEASE_NOTES-2.7
 before proceeding.
 
+Postscreen notes:
+=================
+
+To turn on postscreen, see "Configuring the postscreen(8) service"
+in the POSTSCREEN_README file. This allows you to run postscreen
+without blocking mail first.
+
+The code is rock solid, but the user interface has dozens of
+parameters, so it literally is like using a machine that has wires
+hanging out on all sides. This makes it possible to do research.
+The idea is to reduce the number of parameters once things settle
+down.
+
+NOTE: Some postscreen parameters implement stress-dependent behavior.
+This is supported only when the default value is stress-dependent
+(that is, the default looks like ${stress?XX}${stress:YY}).  Other
+postscreen parameters always evaluate as if the stress value is 
+equal to the empty string.
+
+Incompatibility with snapshot 20100912
+======================================
+
+- The postscreen "continue" action is now called "ignore".  The old
+  name is still supported but no longer documented.
+
+- The postscreen_hangup_action parameter was removed. Postscreen
+  now always behaves as if "postscreen_hangup_action = drop".
+
+- The postscreen_cache_retention_time default was increased from
+  1d to 7d, to avoid deleting results from expensive deep SMTP
+  protocol tests too quickly.
+
+Major changes with snapshot 20100912
+====================================
+
+The main change is a new SMTP protocol engine for deep protocol
+tests, and for logging the helo/sender/recipient information when
+postscreen rejects an attempt to deliver mail.
+
+    CAUTION: when postscreen rejects mail, it replies with the DNSBL
+    domain name. Use the postscreen_dnsbl_reply_map feature to hide
+    "password" information in DNSBL domain names. See the poststconf(5)
+    manpage for a specific example.
+
+Deep protocol tests are implemented by a new SMTP protocol engine
+that defers or rejects all attempts to deliver mail. The first,
+test detects unauthorized SMTP command pipelining (an SMTP client
+sends multiple commands, instead of sending one command and waiting
+for the server response); a second deep protocol test implements
+the Postfix SMTP server's smtpd_forbidden_commands feature (a client
+sends commands such as CONNECT, GET, POST); and a third deep protocol
+test detects spambots that send SMTP commands that end in newline
+instead of carriage-return/newline.  Real spambots rarely make this
+mistake, but poorly-written software often does.
+
+Deep protocol tests are disabled by default, because the built-in
+SMTP engine cannot not hand off the "live" connection from a good
+SMTP client to a Postfix SMTP server process. Instead, postscreen(8)
+defers attempts to deliver mail with a 4XX status, and waits for
+the client to disconnect. The next time a good client connects,
+it will be allowed to talk to a Postfix SMTP server process to
+deliver mail.
+
 Incompatibility with snapshot 20100830
 ======================================
 
index c1303201ee9a4adef80478be69c556f882885987..3ead5e5c136bb4ab201f5ce79718cb5467168348 100644 (file)
@@ -2,6 +2,9 @@ Wish list:
 
        Remove this file from the stable release.
 
+       how to prevent postscreen's cache sweeper from deleting
+       records that have failed ignored tests?
+
        Update history in manpage/readme for SQLite driver.
 
        header_checks(5): document synopsis and feature subsets.
index 2f0552bed031289085cf226b001ec4d2531ff84c..69e18f976c16f2b1551c839e5db2eb630ff30323 100644 (file)
@@ -271,6 +271,7 @@ $readme_directory/OVERVIEW:f:root:-:644
 $readme_directory/PACKAGE_README:f:root:-:644
 $readme_directory/PCRE_README:f:root:-:644
 $readme_directory/PGSQL_README:f:root:-:644
+$readme_directory/POSTSCREEN_README:f:root:-:644
 $readme_directory/QMQP_README:f:root:-:644:o
 $readme_directory/QSHAPE_README:f:root:-:644
 $readme_directory/RELEASE_NOTES:f:root:-:644
@@ -323,6 +324,7 @@ $html_directory/OVERVIEW.html:f:root:-:644
 $html_directory/PACKAGE_README.html:f:root:-:644
 $html_directory/PCRE_README.html:f:root:-:644
 $html_directory/PGSQL_README.html:f:root:-:644
+$html_directory/POSTSCREEN_README.html:f:root:-:644
 $html_directory/QMQP_README.html:f:root:-:644:o
 $html_directory/QSHAPE_README.html:f:root:-:644
 $html_directory/RESTRICTION_CLASS_README.html:f:root:-:644
index ad42264fc43ca461e862ce0625a91239b3f6dd8d..5e2e59425db783baedac13916792f04b59d7e1fe 100644 (file)
@@ -379,6 +379,14 @@ and "port" specifies a symbolic service name or a numeric port
 number.
 </dd>
 
+<dt> <b>texthash</b> (read-only) </dt>
+
+<dd> This produces similar results as hash: files, except that you
+don't have to run the <a href="postmap.1.html">postmap(1)</a> command before you can use the
+file, and that it does not detect changes after the file is read.
+The lookup table name is "texthash:filename", where the file name
+is taken literally; no suffix is appended. </dd>
+
 <dt> <b>unix</b> (read-only) </dt>
 
 <dd> A limited way to query the UNIX authentication database. The
diff --git a/postfix/html/POSTSCREEN_README.html b/postfix/html/POSTSCREEN_README.html
new file mode 100644 (file)
index 0000000..a7a40a9
--- /dev/null
@@ -0,0 +1,709 @@
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<head>
+
+<title>Postfix Postscreen Howto</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 Postscreen Howto</h1>
+
+<hr>
+
+<h2> <a name="intro">Introduction</a> </h2>
+
+<p> The Postfix <a href="postscreen.8.html">postscreen(8)</a> server performs triage on multiple
+inbound SMTP connections in parallel. While one <a href="postscreen.8.html">postscreen(8)</a> process
+keeps spambots away from Postfix SMTP server processes, more Postfix
+SMTP server processes remain available for legitimate clients. </p>
+
+<p> By doing these checks in a single <a href="postscreen.8.html">postscreen(8)</a> process, Postfix
+can avoid wasting one SMTP server process per connection. A side
+benefit of <a href="postscreen.8.html">postscreen(8)</a>'s DNSBL lookups is that DNS records are
+already cached before the Postfix SMTP server looks them up later.
+</p>
+
+<p> <a href="postscreen.8.html">postscreen(8)</a> maintains a temporary whitelist of positive
+decisions.  Once an SMTP client is whitelisted, it is immediately
+forwarded to a real Postfix SMTP server process without further
+checking. </p>
+
+<p> By default, the program logs only statistics, and it does not
+run any checks on clients in <a href="postconf.5.html#mynetworks">mynetworks</a> (primarily, to avoid problems
+with buggy SMTP implementations in network appliances). </p>
+
+<p> Many of the ideas in <a href="postscreen.8.html">postscreen(8)</a> have been explored in earlier
+work by Michael Tokarev, in OpenBSD spamd, and in MailChannels
+Traffic Control.  </p>
+
+<p> Topics in this document: </p>
+
+<ul>
+
+<li> <a href="#intro">Introduction</a>
+
+<li> <a href="#basic">The basic idea behind postscreen(8)</a>
+
+<li> <a href="#general"> General operation </a>
+
+<li> <a href="#quick">Quick tests before everything else</a>
+
+<li> <a href="#before_220"> Tests before the 220 SMTP server greeting </a>
+
+<li> <a href="#after_220">Tests after the 220 SMTP server greeting</a>
+
+<li> <a href="#other_error">Other errors</a>
+
+<li> <a href="#victory">When all tests succeed</a>
+
+<li> <a href="#config"> Configuring the postscreen(8) service</a>
+
+</ul>
+
+<h2> <a name="basic">The basic idea behind postscreen(8)</a> </h2>
+
+<p> Spambots have a limited amount of time to send out spam before
+they become blacklisted. For this reason, spambots make compromises
+in their SMTP protocol implementation to speed up spam deliveries.
+For example, they speak before their turn. </p>
+
+<p> Many spambots avoid spamming the same site repeatedly.  Thus,
+<a href="postscreen.8.html">postscreen(8)</a> must make a long-term decision after a single
+measurement. For example, allow a good client to skip the DNSBL
+test for 24 hours. </p>
+
+<p> To recognize spambots, <a href="postscreen.8.html">postscreen(8)</a> measures properties of the
+client IP address and of the client SMTP protocol implementation
+(the protocol compromises that were made to speed up delivery).
+These properties don't change with delivery attempts, and are
+therefore suitable for making a long-term decision after a single
+measurement.  </p>
+
+<p> <a href="postscreen.8.html">postscreen(8)</a> does not inspect message content. The reason is
+that content can change with each delivery attempt, especially with
+legitimate clients. Message content is not good for making a long-term
+decision after a single measurement, and that is the problem that
+<a href="postscreen.8.html">postscreen(8)</a> is focused on. </p>
+
+<h2> <a name="general"> General operation </a> </h2>
+
+<p> The <a href="postscreen.8.html">postscreen(8)</a> triage process involves a number of tests,
+in the order as described below.  Some tests introduce a delay of
+a few seconds.  Once a client passes all tests, its IP address is
+temporarily excluded from any tests, typically 24 hours for simple
+tests or 1 week for complex tests.  This minimizes the impact of
+the tests on legitimate mail clients. </p>
+
+<p> After logging the result of its tests, <a href="postscreen.8.html">postscreen(8)</a> by default
+forwards all connections to a real SMTP server process. This mode
+is useful for non-destructive testing. </p>
+
+<p> In a typical production setting, <a href="postscreen.8.html">postscreen(8)</a> is configured
+to reject mail from clients that fail one or more tests, after
+logging the sender and recipient information. </p>
+
+<p> Note: <a href="postscreen.8.html">postscreen(8)</a> is not an SMTP proxy; this is intentional.
+The purpose is to prioritize legitimate clients with as little
+overhead as possible. </p>
+
+<h2> <a name="quick">Quick tests before everything else</a> </h2>
+
+<p> Before engaging in SMTP-level tests. <a href="postscreen.8.html">postscreen(8)</a> queries a
+number of local black and whitelists. These tests speed up the
+handling of known clients. </p>
+
+<ul>
+
+<li> <a href="#perm_white"> Permanent whitelist test </a>
+
+<li> <a href="#perm_black"> Permanent blacklist test </a>
+
+<li> <a href="#temp_white"> Temporary whitelist test </a>
+
+</ul>
+
+<h3> <a name="perm_white"> Permanent whitelist test </a> </h3>
+
+<p> The <a href="postconf.5.html#postscreen_whitelist_networks">postscreen_whitelist_networks</a> parameter (default: $<a href="postconf.5.html#mynetworks">mynetworks</a>)
+specifies a permanent whitelist for SMTP client IP addresses.  When
+the SMTP client address matches the permanent whitelist, this is
+logged as: </p>
+
+<pre>
+    <b>WHITELISTED</b> <i>address</i>
+</pre>
+
+<p> The action is not configurable: immediately forward the
+connection to a real SMTP server process. </p>
+
+<h3> <a name="perm_black"> Permanent blacklist test </a>  </h3>
+
+<p> The <a href="postconf.5.html#postscreen_blacklist_networks">postscreen_blacklist_networks</a> parameter (default: empty)
+specifies a permanent blacklist for SMTP client IP addresses.  The
+address syntax is as with <a href="postconf.5.html#mynetworks">mynetworks</a>.  When the SMTP client address
+matches the permanent blacklist, <a href="postscreen.8.html">postscreen(8)</a> logs this as: </p>
+
+<pre>
+    <b>BLACKLISTED</b> <i>address</i>
+</pre>
+
+<p> The <a href="postconf.5.html#postscreen_blacklist_action">postscreen_blacklist_action</a> parameter specifies the action
+that is taken next.  See "<a href="#fail_before_220">When tests
+fail before the 220 SMTP server greeting</a>" below. </p>
+
+<h3> <a name="temp_white"> Temporary whitelist test </a> </h3>
+
+<p> The <a href="postscreen.8.html">postscreen(8)</a> daemon maintains a <i>temporary</i>
+whitelist for SMTP client IP addresses that have passed all
+the tests described below. The <a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a> parameter
+specifies the location of the temporary whitelist.  The
+temporary whitelist is not used for SMTP client addresses
+that appear on the <i>permanent</i> blacklist or whitelist. </p>
+
+<p> When the SMTP client address appears on the temporary
+whitelist, <a href="postscreen.8.html">postscreen(8)</a> logs this as: </p>
+
+<pre>
+    <b>PASS OLD</b> <i>address</i>
+</pre>
+
+<p> The action is not configurable: immediately forward the
+connection to a real SMTP server process.  The client is
+excluded from further tests until its temporary whitelist
+entry expires, as controlled with the postscreen_*_ttl
+parameters.  Expired entries are silently renewed if possible. </p>
+
+<h2> <a name="before_220"> Tests before the 220 SMTP server greeting </a> </h2>
+
+<p> The <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> parameter specifies a short time
+interval before the "220 <i>text</i>..." server greeting, where
+<a href="postscreen.8.html">postscreen(8)</a> can run a number of tests in parallel. </p>
+
+<p> When a good client passes these tests, and no "<a
+href="#after_220">deep protocol tests</a>" are configured, postscreen(8)
+adds the client to the temporary whitelist and passes the "live"
+connection to a Postfix SMTP server process.  The client can then
+continue as if <a href="postscreen.8.html">postscreen(8)</a> never even existed (except of course
+for the short <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> delay).  </p>
+
+<ul>
+
+<li> <a href="#pregreet"> Pregreet test </a>
+
+<li> <a href="#dnsbl"> DNS Blocklist test </a>
+
+<li> <a href="#fail_before_220">When tests fail before the 220 SMTP server greeting</a>
+
+</ul>
+
+<h3> <a name="pregreet"> Pregreet test </a> </h3>
+
+<p> The SMTP protocol is a classic example of a protocol where the
+server speaks before the client. <a href="postscreen.8.html">postscreen(8)</a> detects spambots
+that are in a hurry and that speak before their turn. This test is
+enabled by default. </p>
+
+<p> The <a href="postconf.5.html#postscreen_greet_banner">postscreen_greet_banner</a> parameter specifies the <i>text</i>
+portion of a "220-<i>text</i>..." teaser banner (default: $<a href="postconf.5.html#smtpd_banner">smtpd_banner</a>).
+Note that this becomes the first part of a multi-line server greeting.
+The <a href="postscreen.8.html">postscreen(8)</a> daemon sends this before the <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a>
+timer is started.  The purpose of the teaser banner is to confuse
+spambots so that they speak before their turn. It has no effect on
+SMTP clients that correctly implement the protocol.  </p>
+
+<p> To avoid problems with poorly-implemented SMTP engines in network
+appliances or network testing tools, either exclude them from all
+tests with the <a href="postconf.5.html#postscreen_whitelist_networks">postscreen_whitelist_networks</a> feature or else specify
+an empty teaser banner with: </p>
+
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#postscreen_greet_banner">postscreen_greet_banner</a> =
+</pre>
+
+<p> When an SMTP client sends a command before the
+<a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> time has elapsed, <a href="postscreen.8.html">postscreen(8)</a> logs this as:
+</p>
+
+<pre>
+    <b>PREGREET</b> <i>count</i> <b>after</b> <i>time</i> <b>from</b> <i>address text...</i>
+</pre>
+
+<p> Translation: the client at <i>address</i> sent <i>count</i>
+bytes before its turn to speak. This happened <i>time</i> seconds
+after the <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> timer was started.  The <i>text</i>
+is what the client sent (truncated to 100 bytes, and with non-printable
+characters replaced with "?"). </p>
+
+<p> The <a href="postconf.5.html#postscreen_greet_action">postscreen_greet_action</a> parameter specifies the action that
+is taken next.  See "<a href="#fail_before_220">When tests fail
+before the 220 SMTP server greeting</a>" below. </p>
+
+<h3> <a name="dnsbl"> DNS Blocklist test </a> </h3>
+
+<p> The <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> parameter (default: empty) specifies
+a list of DNS blocklist servers with optional filters and weight
+factors.  These servers will be queried in parallel with the reverse
+client IP address. This test is disabled by default. </p>
+
+<blockquote>
+<p>
+CAUTION: when postscreen rejects mail, it replies with the DNSBL
+domain name. Use the <a href="postconf.5.html#postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a> feature to hide
+"password" information in DNSBL domain names.
+</p>
+</blockquote>
+
+<p> When the <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> time has elapsed, and the combined
+DNSBL score is equal to or greater than the <a href="postconf.5.html#postscreen_dnsbl_threshold">postscreen_dnsbl_threshold</a>
+parameter value, <a href="postscreen.8.html">postscreen(8)</a> logs this as: </p>
+
+<b>DNSBL rank</b> <i>count</i> <b>for</b> <i>address</i>
+
+<p> Translation: the SMTP client at <i>address</i> has a combined
+DNSBL score of <i>count</i>. </p>
+
+<p> The <a href="postconf.5.html#postscreen_dnsbl_action">postscreen_dnsbl_action</a> parameter specifies the action that
+is taken when the combined DNSBL score is equal to or greater than
+the threshold.  See "<a href="#fail_before_220">When tests fail
+before the 220 SMTP server greeting</a>" below. </p>
+
+<h3> <a name="fail_before_220">When tests fail before the 220 SMTP server greeting</a> </h3>
+
+<p> When the client address matches the permanent blacklist, or
+when the client fails the pregreet or DNSBL tests, the action is
+specified with <a href="postconf.5.html#postscreen_blacklist_action">postscreen_blacklist_action</a>, <a href="postconf.5.html#postscreen_greet_action">postscreen_greet_action</a>,
+or <a href="postconf.5.html#postscreen_dnsbl_action">postscreen_dnsbl_action</a>, respectively. </p>
+
+<dl>
+
+<dt> <b>ignore</b> (default) </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Repeat this test the next time the client connects.  This option
+is useful for testing and collecting statistics without interfering
+with mail deliveries. </dd>
+
+<dt> <b>enforce</b> </dt>
+
+<dd> Allow other tests to complete.  Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
+
+<dt> <b>drop</b> </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply.  Repeat
+this test the next time the client connects. </dd>
+
+</dl>
+
+<h2> <a name="after_220">Tests after the 220 SMTP server greeting</a> </h2>
+
+<p> The tests in this phase use an SMTP protocol engine that is
+built into the <a href="postscreen.8.html">postscreen(8)</a> server. </p>
+
+<p> Important notes: </p>
+
+<ul>
+
+<li> <p> These tests are disabled by default, because they
+are more intrusive than the pregreet and DNSBL tests. </p>
+
+<p> When a good client passes the <a href="#after_220">deep
+protocol tests </a>, postscreen(8) adds the client to the temporary
+whitelist but it cannot pass the "live" connection to a Postfix
+SMTP server process in the middle of the session. Instead, <a href="postscreen.8.html">postscreen(8)</a>
+defers mail delivery attempts with a 4XX status, logs the
+helo/sender/recipient information, and waits for the client to
+disconnect. </p>
+
+<p> The next time the client connects it will be allowed to talk
+to a real SMTP server process to deliver its mail. </p>
+
+<p> To minimize the impact of these tests, <a href="postscreen.8.html">postscreen(8)</a> gives them
+relatively long expiration times. </p>
+
+<li> <p> <a href="postscreen.8.html">postscreen(8)</a> does not implement the AUTH, STARTTLS,
+XCLIENT, and XFORWARD features.  STARTTLS support may be added in
+a future version. </p>
+
+</ul>
+
+<p> End-user clients should connect directly to the submission
+service. Other systems that require the above features
+should directly connect to a Postfix SMTP server, or they
+should be placed on the <a href="postscreen.8.html">postscreen(8)</a> whitelist. </p>
+
+<ul>
+
+<li> <a href="#pipelining">Command pipelining test</a>
+
+<li> <a href="#non_smtp">Non-SMTP command test</a>
+
+<li> <a href="#barelf">Bare newline test</a>
+
+<li> <a href="#fail_after_220">When tests fail after the 220 SMTP server greeting</a>
+
+</ul>
+
+<h3> <a name="pipelining">Command pipelining test</a> </h3>
+
+<p> By default, SMTP is a half-duplex protocol: the sender and
+receiver send one command and one response at a time.  Unlike the
+real Postfix SMTP server, <a href="postscreen.8.html">postscreen(8)</a> does not announce support
+for ESMTP command pipelining.  Therefore, clients are not allowed
+to send multiple commands. This test is disabled by default. </p>
+
+<p> With "<a href="postconf.5.html#postscreen_pipelining_enable">postscreen_pipelining_enable</a> = yes", <a href="postscreen.8.html">postscreen(8)</a> detects
+spambots that send multiple commands, instead of sending one command
+and waiting for the server to reply. </p>
+
+<p> This test is opportunistically enabled when enabled when
+<a href="postscreen.8.html">postscreen(8)</a> has to use the built-in SMTP engine anyway, to make
+<a href="postscreen.8.html">postscreen(8)</a> logging more informative. </p>
+
+<p> When a client sends multiple commands, <a href="postscreen.8.html">postscreen(8)</a> logs this
+as: </p>
+
+<b>COMMAND PIPELINING after</b> <i>time</i> <b>from</b> <i>address</i>
+
+<p> Translation: the SMTP client at <i>address</i> sent multiple
+SMTP commands, instead of sending one command and then waiting for
+the server to reply. This happened <i>time</i> seconds after the
+"220 " server greeting was sent. </p>
+
+<p> The <a href="postconf.5.html#postscreen_pipelining_action">postscreen_pipelining_action</a> parameter specifies the action
+that is taken next.  See "<a href="#fail_after_220">When tests fail
+after the 220 SMTP server greeting</a>" below. </p>
+
+<h3> <a name="non_smtp">Non-SMTP command test</a> </h3>
+
+<p> With "<a href="postconf.5.html#postscreen_non_smtp_command_enable">postscreen_non_smtp_command_enable</a> = yes", <a href="postscreen.8.html">postscreen(8)</a>
+detects spambots that send non-SMTP commands, such as commands
+specified with the <a href="postconf.5.html#postscreen_forbidden_commands">postscreen_forbidden_commands</a> parameter, and
+commands that have the syntax of a message header label.  </p>
+
+<p> This test is disabled by default. The test is opportunistically
+enabled when <a href="postscreen.8.html">postscreen(8)</a> has to use the built-in SMTP engine
+anyway, to make <a href="postscreen.8.html">postscreen(8)</a> logging more informative.  </p>
+
+<p> When a client sends non-SMTP commands, <a href="postscreen.8.html">postscreen(8)</a> logs this
+as: </p>
+
+<pre>
+    <b>NON-SMTP COMMAND from</b> <i>address command</i>
+</pre>
+
+<p> Translation: the SMTP client at <i>address</i> sent a
+<i>command</i> that matches the <a href="postconf.5.html#postscreen_forbidden_commands">postscreen_forbidden_commands</a>
+parameter, or that has the syntax of a message header label. </p>
+
+<p> The <a href="postconf.5.html#postscreen_non_smtp_command_action">postscreen_non_smtp_command_action</a> parameter specifies
+the action that is taken next.  See "<a href="#fail_after_220">When
+tests fail after the 220 SMTP server greeting</a>" below. </p>
+
+<h3> <a name="barelf">Bare newline test</a> </h3>
+
+<p> SMTP is a line-oriented protocol: lines have a limited
+length, and are terminated with &lt;CR&gt;&lt;LF&gt;. </p>
+
+<p> With "<a href="postconf.5.html#postscreen_bare_newline_enable">postscreen_bare_newline_enable</a> = yes", <a href="postscreen.8.html">postscreen(8)</a>
+detects spambots that send lines ending in bare newline
+characters, that is newline not preceded by carriage return. </p>
+
+<p> This test is disabled by default. The test is opportunistically
+enabled when <a href="postscreen.8.html">postscreen(8)</a> has to use the built-in SMTP engine
+anyway, to make <a href="postscreen.8.html">postscreen(8)</a> logging more informative. </p>
+
+<p> When a client sends bare newline characters, <a href="postscreen.8.html">postscreen(8)</a> logs
+this as:
+</p>
+
+<pre>
+    <b>BARE NEWLINE from</b> <i>address</i>
+</pre>
+
+<p> Translation: the SMTP client at <i>address</i> sent a bare
+newline character, that is newline not preceded by carriage
+return. </p>
+
+<p> The <a href="postconf.5.html#postscreen_bare_newline_action">postscreen_bare_newline_action</a> parameter specifies the
+action that is taken next.  See "<a href="#fail_after_220">When
+tests fail after the 220 SMTP server greeting</a>" below. </p>
+
+<h3> <a name="fail_after_220">When tests fail after the 220 SMTP server greeting</a> </h3>
+
+<p> When the client fails the pipelining, non-SMTP command or bare
+newline tests, the action is specified with <a href="postconf.5.html#postscreen_pipelining_action">postscreen_pipelining_action</a>,
+<a href="postconf.5.html#postscreen_non_smtp_command_action">postscreen_non_smtp_command_action</a> or <a href="postconf.5.html#postscreen_bare_newline_action">postscreen_bare_newline_action</a>,
+respectively. </p>
+
+<dl>
+
+<dt> <b>ignore</b> (default for bare newline) </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Do NOT repeat this test before the result from some other test
+expires.
+
+This option is useful for testing and collecting statistics without
+blocking mail permanently. </dd>
+
+<dt> <b>enforce</b> (default for pipelining) </dt>
+
+<dd> Allow other tests to complete.  Reject attempts to deliver
+mail with a 550 SMTP reply, and log the helo/sender/recipient
+information.  Repeat this test the next time the client connects.
+</dd>
+
+<dt> <b>drop</b> (default for non-SMTP commands) </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply.  Repeat
+this test the next time the client connects.  This action is
+compatible with the Postfix SMTP server's <a href="postconf.5.html#smtpd_forbidden_commands">smtpd_forbidden_commands</a>
+feature. </dd>
+
+</dl>
+
+<h2> <a name="other_error">Other errors</a> </h2>
+
+<p> When an SMTP client hangs up unexpectedly during any tests,
+<a href="postscreen.8.html">postscreen(8)</a> logs this as: </p>
+
+<pre>
+    <b>HANGUP after</b> <i>time</i> <b>from</b> <i>address</i> <b>in</b> <i>test name</i>
+</pre>
+
+<p> Translation: the SMTP client at <i>address</i> disconnected
+unexpectedly, <i>time</i> seconds after the start of the
+test named <i>test name</i>. </p>
+
+<p> The following errors are reported by the built-in SMTP engine.
+This engine never accepts mail, therefore it has per-session limits
+on the number of commands and on the session length. </p>
+
+<pre>
+    <b>COMMAND TIME LIMIT</b> <b>from</b> <i>address</i>
+</pre>
+
+<p> Translation: the SMTP client at <i>address</i> reached the
+per-command time limit as specified with the <a href="postconf.5.html#postscreen_command_time_limit">postscreen_command_time_limit</a>
+parameter.  The session is terminated immediately. </p>
+
+<pre>
+    <b>COMMAND COUNT LIMIT from</b> <i>address</i>
+</pre>
+
+<p> Translation: the SMTP client at <i>address</i> reached the
+per-session command count limit as specified with the
+<a href="postconf.5.html#postscreen_command_count_limit">postscreen_command_count_limit</a> parameter.  The session is terminated
+immediately. </p>
+
+<pre>
+    <b>COMMAND LENGTH LIMIT from</b> <i>address</i>
+</pre>
+
+<p> Translation: the SMTP client at <i>address</i> reached the
+per-command length limit, as specified with the <a href="postconf.5.html#line_length_limit">line_length_limit</a>
+parameter.  The session is terminated immediately. </p>
+
+<h2> <a name="victory">When all tests succeed</a> </h2>
+
+<p> When a new SMTP client passes all tests (i.e. it is not whitelisted
+via some mechanism), <a href="postscreen.8.html">postscreen(8)</a> logs this as: </p>
+
+<pre>
+    <b>PASS NEW</b> <i>address</i>
+</pre>
+
+<p> Where <i>address</i> is the client IP address. Then, <a href="postscreen.8.html">postscreen(8)</a>
+creates a temporary whitelist entry that excludes the client IP
+address from further tests until the temporary whitelist entry
+expires, as controlled with the postscreen_*_ttl parameters. </p>
+
+<p> When no "<a href="#after_220">deep procol tests</a>" are
+configured, <a href="postscreen.8.html">postscreen(8)</a> passes the "live" connection to a Postfix
+SMTP server process.  The client can then continue as if <a href="postscreen.8.html">postscreen(8)</a>
+never even existed (except for the short <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> delay).
+</p>
+
+<p> When any "<a href="#after_220">deep procol tests</a>" are
+configured, <a href="postscreen.8.html">postscreen(8)</a> cannot pass the "live" connection to a
+Postfix SMTP server process.  Instead, <a href="postscreen.8.html">postscreen(8)</a> defers mail
+delivery attempts with a 4XX status, logs the helo/sender/recipient
+information, and waits for the client to disconnect.  The next time
+the client connects it will be allowed to talk to a Postfix SMTP
+server process to deliver its mail. </p>
+
+<h2> <a name="config"> Configuring the postscreen(8) service</a>
+</h2>
+
+<p> <a href="postscreen.8.html">postscreen(8)</a> has been tested on FreeBSD [4-8] and Linux 2.[4-6]
+systems.  It probably needs additional work before it can be used
+on Solaris. </p>
+
+<ul>
+
+<li> <a href="#enable"> Turning on postscreen(8) without blocking
+mail</a>
+
+<li> <a href="#blocking"> Blocking mail with postscreen(8) </a>
+
+<li> <a href="#turnoff"> Turning off postscreen(8) </a>
+
+</ul>
+
+<h3> <a name="enable"> Turning on postscreen(8) without blocking mail</a> </h3>
+
+<p> To enable the <a href="postscreen.8.html">postscreen(8)</a> service and log client information
+without blocking mail: </p>
+
+<ol>
+
+<li> <p> Comment out the "<tt>smtp  inet ... smtpd</tt>" service
+in <a href="master.5.html">master.cf</a>, including any "<tt>-o parameter=value</tt>" entries
+that follow.  </p>
+
+<li> <p> Uncomment the new "<tt>smtpd pass ... smtpd</tt>" service
+in <a href="master.5.html">master.cf</a>, and duplicate any "<tt>-o parameter=value</tt>" entries
+from the smtpd service that was commented out in step 1. </p>
+
+<li> <p> Uncomment the new "<tt>smtp inet ... postscreen</tt>"
+service in <a href="master.5.html">master.cf</a>. </p>
+
+<li> <p> Uncomment the new "<tt>dnsblog  unix ... dnsblog</tt>"
+service in <a href="master.5.html">master.cf</a>.  This service does DNSBL lookups for <a href="postscreen.8.html">postscreen(8)</a>
+and logs results. </p>
+
+<li> <p> To enable DNSBL lookups, list some DNS blocklist sites in
+<a href="postconf.5.html">main.cf</a>, separated by whitespace. Different sites can have different
+weights. For example:
+
+<pre>
+    <a href="postconf.5.html#postscreen_dnsbl_threshold">postscreen_dnsbl_threshold</a> = 2
+    <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> = zen.spamhaus.org*2 example.com*1 example.net*1
+</pre>
+
+<p> Note: if your DNSBL queries have a "secret" in the domain name,
+you must censor this information from the <a href="postscreen.8.html">postscreen(8)</a> SMTP replies.
+For example: </p>
+
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a> = texthash:/etc/postfix/dnsbl_reply
+</pre>
+
+<pre>
+/etc/postfix/dnsbl_reply:
+    # Secret DNSBL name        Name in <a href="postscreen.8.html">postscreen(8)</a> replies
+    secret.zen.spamhaus.org    zen.spamhaus.org
+</pre>
+
+<p> The texthash: format is similar to hash: except that there is
+no need to run <a href="postmap.1.html">postmap(1)</a> before the file can be used, and that it
+does not detect changes after the file is read. It is new with
+Postfix version 2.8. </p>
+
+<li> <p> Read the new configuration with "<tt>postfix reload</tt>".
+</p>
+
+</ol>
+
+<p> Notes: </p>
+
+<ul>
+
+<li> <p> See "<a href="#before_220">Tests before the 220 SMTP server
+greeting</a>" for details about the logging from these postscreen(8)
+tests. </p>
+
+<li> <p> By default, <a href="postscreen.8.html">postscreen(8)</a> whitelists all clients in
+<a href="postconf.5.html#mynetworks">mynetworks</a>.  This is a safety feature to avoid you from getting
+into trouble with local users. </p>
+
+<li> <p> If you run Postfix 2.6 or earlier you must stop and start
+the master daemon ("<tt>postfix stop; postfix start</tt>").  This
+is needed because the Postfix "pass" master service type did not
+work reliably on all systems. </p>
+
+</ul>
+
+<h3> <a name="blocking"> Blocking mail with postscreen(8) </a> </h3>
+
+<p> To use the <a href="postscreen.8.html">postscreen(8)</a> service to block mail, edit <a href="postconf.5.html">main.cf</a> and
+specify one or more of: </p>
+
+<ul>
+
+<li> <p> "<tt><a href="postconf.5.html#postscreen_dnsbl_action">postscreen_dnsbl_action</a> = enforce</tt>", to reject
+clients that are on DNS blocklists, and to log the helo/sender/recipient
+information. With good DNSBLs this reduces the amount of load on
+Postfix SMTP servers dramatically.  </p>
+
+<li> <p> "<tt><a href="postconf.5.html#postscreen_greet_action">postscreen_greet_action</a> = enforce</tt>", to reject
+clients that talk before their turn, and to log the helo/sender/recipient
+information. This stops over half of all known-to-be illegitimate
+connections to Wietse's mail server. It is backup protection for
+spambots that haven't yet been blacklisted. </p>
+
+<li> <p> You can also enable "<a href="#after_220">deep protocol
+tests</a>", but these are more intrusive than the pregreet or DNSBL
+tests. </p>
+
+<p> When a good client passes the "<a href="#after_220">deep
+protocol tests</a>", postscreen(8) adds the client to the temporary
+whitelist but it cannot pass the "live" connection to a Postfix
+SMTP server process in the middle of the session. Instead, <a href="postscreen.8.html">postscreen(8)</a>
+defers mail delivery attempts with a 4XX status, logs the
+helo/sender/recipient information, and waits for the client to
+disconnect. </p>
+
+<p> When the client comes back in a later session, it is allowed
+to talk directly to a Postfix SMTP server.  See "after_220 <a
+href="#after_220">Tests after the 220 SMTP server greeting</a> above
+for limitations with STARTTLS, AUTH and other features that clients
+may need. Wietse enables "<a href="#after_220">deep protocol
+tests</a>" on his own internet-facing mail server.  </p>
+
+<li> <p> There is also support for permanent blacklists and whitelists;
+see the description of the <a href="postconf.5.html#postscreen_whitelist_networks">postscreen_whitelist_networks</a> and
+<a href="postconf.5.html#postscreen_blacklist_networks">postscreen_blacklist_networks</a> parameters for details. </p>
+
+</ul>
+
+<h3> <a name="turnoff"> Turning off postscreen(8) </a> </h3>
+
+<p> To turn off <a href="postscreen.8.html">postscreen(8)</a> and handle mail directly with Postfix
+SMTP server processes: </p>
+
+<ol>
+
+<li> <p> Comment out the "<tt>smtp inet ... postscreen</tt>" service
+in <a href="master.5.html">master.cf</a>, including any "<tt>-o parameter=value</tt>" entries
+that follow. </p>
+
+<li> <p> Comment out the "<tt>dnsblog  unix ... dnsblog</tt>" service
+in <a href="master.5.html">master.cf</a>.  </p>
+
+<li> <p> Comment out the "<tt>smtpd pass ... smtpd</tt>" service
+in <a href="master.5.html">master.cf</a>, including any "<tt>-o parameter=value</tt>" entries
+that follow. </p>
+
+<li> <p> Uncomment the "<tt>smtp  inet ... smtpd</tt>" service in
+<a href="master.5.html">master.cf</a>, including any "<tt>-o parameter=value</tt>" entries that
+follow.  </p>
+
+<li> <p> Read the new configuration with "<tt>postfix reload</tt>".
+</p>
+
+</ol>
+
+</body>
+
+</html>
+
index 185862a98e8edc4b0c0d839fa9f7025083050d43..1ada2e5ee860da8c75e6d1953fdeeeee5f1f34c0 100644 (file)
@@ -321,7 +321,10 @@ queue</a></h2>
 Users list.  Congestion was reported with the active and incoming
 queues large and not shrinking despite very large delivery agent
 process limits.  The thread is archived at:
-<a href="http://groups.google.com/groups?th=636626c645f5bbde">http://groups.google.com/groups?th=636626c645f5bbde</a> </p>
+<a href="http://groups.google.com/groups?threadm=c0b7js$2r65$1@FreeBSD.csie.NCTU.edu.tw">http://groups.google.com/groups?threadm=c0b7js$2r65$1@FreeBSD.csie.NCTU.edu.tw</a>
+and
+<a href="http://archives.neohapsis.com/archives/postfix/2004-02/thread.html#1371">http://archives.neohapsis.com/archives/postfix/2004-02/thread.html#1371</a>
+</p>
 
 <p> Using an older version of <a href="qshape.1.html">qshape(1)</a> it was quickly determined
 that all the messages were for just a few destinations: </p>
index 07132e92df69c0939f2b05bca482892a0cdb052c..d6dd2e696e61293a7bfe2a3f9b746b0c123c5431 100644 (file)
@@ -1867,8 +1867,8 @@ only over a TLS-encrypted connection: </p>
 <blockquote>
 <pre>
 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
-    <a href="postconf.5.html#smtpd_sasl_security_options">smtpd_sasl_security_options</a> = noanonymous, noplaintext
-    <a href="postconf.5.html#smtpd_sasl_tls_security_options">smtpd_sasl_tls_security_options</a> = noanonymous
+    <a href="postconf.5.html#smtp_sasl_security_options">smtp_sasl_security_options</a> = noanonymous, noplaintext
+    <a href="postconf.5.html#smtp_sasl_tls_security_options">smtp_sasl_tls_security_options</a> = noanonymous
 </pre>
 </blockquote>
 
index e2eb7c8502fc26d23a710eb69fd52269de3db32d..cdd79dcc935e18e1bd4009e6c51304947aa4b697 100644 (file)
@@ -38,7 +38,7 @@ the following problem: </p>
 
 </ul>
 
-<p> This extension is implemented as a separate EMSTP command, and
+<p> This extension is implemented as a separate ESMTP command, and
 can be used to transmit client or message attributes incrementally.
 It is not implemented by passing additional parameters via the MAIL
 FROM command, because doing so would require extending the MAIL
index d17f762cd0e1ffc99120fd6d708022b521609c73..6199868361b379215eb85cd93fe1d5ee8fb596cd 100644 (file)
@@ -112,6 +112,8 @@ overview </a>
 <li> <a href="RESTRICTION_CLASS_README.html">
 Per-client/user/etc. access </a>
 
+<li> <a href="POSTSCREEN_README.html"> SMTP connection triage </a>
+
 <li> <a href="ETRN_README.html"> ETRN Support  </a>
 
 <li> <a href="UUCP_README.html"> LAN connected via UUCP </a>
index 36f016d912b8f71a371c8c7273369091721158b1..614b7b0ab4eefad1a5e22d764805da3faf34bd9a 100644 (file)
@@ -191,27 +191,32 @@ POSTCONF(1)                                                        POSTCONF(1)
               <b>tcp</b> (read-only)
                      Perform lookups using a simple request-reply
                      protocol  that is described in <a href="tcp_table.5.html"><b>tcp_table</b>(5)</a>.
-                     This feature is not included with the stable
-                     Postfix release.
+
+              <b>texthash</b> (read-only)
+                     Produces similar  results  as  hash:  files,
+                     except  that  you  don't  need  to  run  the
+                     <a href="postmap.1.html">postmap(1)</a> command before you  can  use  the
+                     file,  and  that  it does not detect changes
+                     after the file is read.
 
               <b>unix</b> (read-only)
-                     A  limited way to query the UNIX authentica-
+                     A limited way to query the UNIX  authentica-
                      tion  database.  The  following  tables  are
                      implemented:
 
                      <b>unix:passwd.byname</b>
-                            The  table is the UNIX password data-
-                            base. The key is a login  name.   The
-                            result  is  a  password file entry in
+                            The table is the UNIX password  data-
+                            base.  The  key is a login name.  The
+                            result is a password  file  entry  in
                             <b>passwd</b>(5) format.
 
                      <b>unix:group.byname</b>
                             The table is the UNIX group database.
-                            The  key is a group name.  The result
-                            is a group  file  entry  in  <b>group</b>(5)
+                            The key is a group name.  The  result
+                            is  a  group  file  entry in <b>group</b>(5)
                             format.
 
-              Other  table types may exist depending on how Post-
+              Other table types may exist depending on how  Post-
               fix was built.
 
        <b>-n</b>     Print parameter settings that are not left at their
@@ -220,29 +225,29 @@ POSTCONF(1)                                                        POSTCONF(1)
 
        <b>-t</b> [<i>template</i><b>_</b><i>file</i>]
               Display the templates for delivery status notifica-
-              tion  (DSN) messages. To override the built-in tem-
-              plates, specify a template file at the end  of  the
+              tion (DSN) messages. To override the built-in  tem-
+              plates,  specify  a template file at the end of the
               command line, or specify a template file in <a href="postconf.5.html">main.cf</a>
-              with the <b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a></b> parameter.  To  force
-              selection  of  the  built-in  templates, specify an
+              with  the <b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a></b> parameter.  To force
+              selection of the  built-in  templates,  specify  an
               empty template file name (in shell language: "").
 
-              This feature is  available  with  Postfix  2.3  and
+              This  feature  is  available  with  Postfix 2.3 and
               later.
 
        <b>-v</b>     Enable verbose logging for debugging purposes. Mul-
-              tiple <b>-v</b> options  make  the  software  increasingly
+              tiple  <b>-v</b>  options  make  the software increasingly
               verbose.
 
-       <b>-#</b>     Edit  the  <a href="postconf.5.html"><b>main.cf</b></a>  configuration file. The file is
+       <b>-#</b>     Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file.  The  file  is
               copied to a temporary file then renamed into place.
-              The  parameters  specified  on the command line are
+              The parameters specified on the  command  line  are
               commented-out, so that they revert to their default
-              values.  Specify  a  list  of  parameter names, not
-              name=value pairs.  There is no <b>postconf</b> command  to
+              values. Specify a  list  of  parameter  names,  not
+              name=value  pairs.  There is no <b>postconf</b> command to
               perform the reverse operation.
 
-              This  feature  is  available  with  Postfix 2.6 and
+              This feature is  available  with  Postfix  2.6  and
               later.
 
 <b>DIAGNOSTICS</b>
@@ -253,18 +258,18 @@ POSTCONF(1)                                                        POSTCONF(1)
               Directory with Postfix configuration files.
 
 <b>CONFIGURATION PARAMETERS</b>
-       The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are  especially  relevant
+       The  following  <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant
        to this program.
 
-       The  text  below  provides  only  a parameter summary. See
+       The text below provides  only  a  parameter  summary.  See
        <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
 
        <b><a href="postconf.5.html#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#bounce_template_file">bounce_template_file</a> (empty)</b>
-              Pathname  of  a configuration file with bounce mes-
+              Pathname of a configuration file with  bounce  mes-
               sage templates.
 
 <b>FILES</b>
@@ -278,7 +283,7 @@ POSTCONF(1)                                                        POSTCONF(1)
        <a href="DATABASE_README.html">DATABASE_README</a>, Postfix lookup table overview
 
 <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 ef30d5b9b188158023e098427116da3f36dbee2b..48630c66b10293e49eaf65fff25b63bcf159c115 100644 (file)
@@ -6608,10 +6608,76 @@ and enabled instances are processed in reverse order. </p>
 <p> This feature is available in Postfix 2.6 and later. </p>
 
 
+</DD>
+
+<DT><b><a name="postscreen_bare_newline_action">postscreen_bare_newline_action</a>
+(default: ignore)</b></DT><DD>
+
+<p> The action that <a href="postscreen.8.html">postscreen(8)</a> takes when an SMTP client sends
+a bare newline character, that is, a newline not preceded by carriage
+return.  Specify one of the following: </p>
+
+<dl>
+
+<dt> <b>ignore</b> </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Do <i>not</i> repeat this test before some the result from some
+other test expires.
+This option is useful for testing and collecting statistics
+without blocking mail permanently. </dd>
+
+<dt> <b>enforce</b> </dt>
+
+<dd> Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
+
+<dt> <b>drop</b> </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects.  </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.8. </p>
+
+
+</DD>
+
+<DT><b><a name="postscreen_bare_newline_enable">postscreen_bare_newline_enable</a>
+(default: no)</b></DT><DD>
+
+<p> Enable "bare newline" SMTP protocol tests in the <a href="postscreen.8.html">postscreen(8)</a>
+server. These tests are expensive: a client must disconnect after
+it passes the test, before it can talk to a real Postfix SMTP server.
+</p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+
+</DD>
+
+<DT><b><a name="postscreen_bare_newline_ttl">postscreen_bare_newline_ttl</a>
+(default: 30d)</b></DT><DD>
+
+<p> The amount of time that <a href="postscreen.8.html">postscreen(8)</a> will cache results from
+a successful "bare newline" SMTP protocol test. During this
+time, the client IP address is excluded from this test. The default
+is long because a client must disconnect after it passes the test,
+before it can talk to a real Postfix SMTP server. </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+
 </DD>
 
 <DT><b><a name="postscreen_blacklist_action">postscreen_blacklist_action</a>
-(default: continue)</b></DT><DD>
+(default: ignore)</b></DT><DD>
 
 <p> The action that <a href="postscreen.8.html">postscreen(8)</a> takes when an SMTP client is
 permanently blacklisted with the <a href="postconf.5.html#postscreen_blacklist_networks">postscreen_blacklist_networks</a>
@@ -6619,20 +6685,23 @@ parameter.  Specify one of the following: </p>
 
 <dl>
 
-<dt> continue </dt>
+<dt> <b>ignore</b> (default) </dt>
 
-<dd> Continue waiting until the <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> time has
-elapsed, and report whether the client triggers a PREGREET or HANGUP
-error, or whether the client's combined DNSBL score is equal to or
-greater than a threshold (as specified with the <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a>
-and <a href="postconf.5.html#postscreen_dnsbl_threshold">postscreen_dnsbl_threshold</a> parameters).  Take the corresponding
-action, or forward the connection to a real SMTP server process.
-</dd>
+<dd> Ignore  this result. Allow other tests to complete.  Repeat
+this test the next time the client connects.
+This option is useful for testing and collecting statistics
+without blocking mail. </dd>
 
-<dt> drop </dt>
+<dt> <b>enforce</b> </dt>
 
-<dd> Drop the connection immediately with a 521 SMTP reply, without
-reporting PREGREET, HANGUP or DNSBL results. </dd>
+<dd> Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
+
+<dt> <b>drop</b> </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects. </dd>
 
 </dl>
 
@@ -6689,12 +6758,13 @@ seconds. </p>
 </DD>
 
 <DT><b><a name="postscreen_cache_retention_time">postscreen_cache_retention_time</a>
-(default: 1d)</b></DT><DD>
+(default: 7d)</b></DT><DD>
 
 <p> The amount of time that <a href="postscreen.8.html">postscreen(8)</a> will cache an expired
 temporary whitelist entry before it is removed. This prevents clients
 from being logged as "NEW" just because their cache entry expired
-an hour ago.  </p>
+an hour ago. It also prevents the cache from filling up with clients
+that passed some deep protocol test once and never came back. </p>
 
 <p> Time units: s (seconds), m (minutes), h (hours), d (days), w
 (weeks).  </p>
@@ -6704,26 +6774,44 @@ an hour ago.  </p>
 
 </DD>
 
-<DT><b><a name="postscreen_cache_ttl">postscreen_cache_ttl</a>
-(default: 1d)</b></DT><DD>
+<DT><b><a name="postscreen_command_count_limit">postscreen_command_count_limit</a>
+(default: 20)</b></DT><DD>
 
-<p> The amount of time that <a href="postscreen.8.html">postscreen(8)</a> will cache a decision for
-a specific SMTP client IP address. During this time, the client IP
-address is excluded from tests. If possible, expired decisions are
-renewed automatically. Specify a non-zero time value (an integral
-value plus an optional one-letter suffix that specifies the time
-unit).  </p>
+<p> The limit on the total number of commands per SMTP session for
+<a href="postscreen.8.html">postscreen(8)</a>'s built-in SMTP protocol engine.  This SMTP engine
+defers or rejects all attempts to deliver mail, therefore there is
+no need to enforce separate limits on the number of junk commands
+and error commands.  </p>
 
-<p> Time units: s (seconds), m (minutes), h (hours), d (days), w
-(weeks).  </p>
+<p> This feature is available in Postfix 2.8.  </p>
 
-<p> This feature is available in Postfix 2.8. </p>
+
+</DD>
+
+<DT><b><a name="postscreen_command_time_limit">postscreen_command_time_limit</a>
+(default: ${stress?10}${stress:300}s)</b></DT><DD>
+
+<p> The command "read" time limit for <a href="postscreen.8.html">postscreen(8)</a>'s built-in SMTP
+protocol engine.  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+
+</DD>
+
+<DT><b><a name="postscreen_disable_vrfy_command">postscreen_disable_vrfy_command</a>
+(default: $<a href="postconf.5.html#disable_vrfy_command">disable_vrfy_command</a>)</b></DT><DD>
+
+<p> Disable the SMTP VRFY command in the <a href="postscreen.8.html">postscreen(8)</a> daemon.  See
+<a href="postconf.5.html#disable_vrfy_command">disable_vrfy_command</a> for details.  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
 
 
 </DD>
 
 <DT><b><a name="postscreen_dnsbl_action">postscreen_dnsbl_action</a>
-(default: continue)</b></DT><DD>
+(default: ignore)</b></DT><DD>
 
 <p>The action that <a href="postscreen.8.html">postscreen(8)</a> takes when an SMTP client's combined
 DNSBL score is equal to or greater than a threshold (as defined
@@ -6732,19 +6820,60 @@ parameters).  Specify one of the following: </p>
 
 <dl>
 
-<dt> continue </dt>
+<dt> <b>ignore</b> (default) </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Repeat this test the next time the client connects.
+This option is useful for testing and collecting statistics
+without blocking mail. </dd>
+
+<dt> <b>enforce</b> </dt>
 
-<dd> Forward the connection to a real SMTP server process. </dd>
+<dd> Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
 
-<dt> drop </dt>
+<dt> <b>drop</b> </dt>
 
-<dd> Drop the connection with a 521 SMTP reply. </dd>
+<dd> Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects. </dd>
 
 </dl>
 
 <p> This feature is available in Postfix 2.8. </p>
 
 
+</DD>
+
+<DT><b><a name="postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a>
+(default: empty)</b></DT><DD>
+
+<p> A mapping from actual DNSBL domain name which includes a secret
+password, to the DNSBL domain name that postscreen will reply with
+when it rejects mail.  When no mapping is found, the actual DNSBL
+domain will be used. </p>
+
+<p> For maximal stability it is best to use a file that is read
+into memory such as <a href="pcre_table.5.html">pcre</a>:, <a href="regexp_table.5.html">regexp</a>: or texthash: (a format similar
+to hash: except that there is no need to run <a href="postmap.1.html">postmap(1)</a> before the
+file can be used, and that it does not detect changes after the
+file is read). </p>
+
+<p> Example: </p>
+
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a> = texthash:/etc/postfix/dnsbl_reply
+</pre>
+
+<pre>
+/etc/postfix/dnsbl_reply:
+   secret.zen.spamhaus.org     zen.spamhaus.org
+</pre>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+
 </DD>
 
 <DT><b><a name="postscreen_dnsbl_sites">postscreen_dnsbl_sites</a>
@@ -6756,6 +6885,10 @@ query these domains with the IP addresses of non-whitelisted remote
 SMTP clients, and <a href="postscreen.8.html">postscreen(8)</a> will update an SMTP client's DNSBL
 score with each non-error reply. </p>
 
+<p> Caution: when postscreen rejects mail, it replies with the DNSBL
+domain name. Use the <a href="postconf.5.html#postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a> feature to hide
+"password" information in DNSBL domain names. </p>
+
 <p> When a client's score is equal to or greater than the threshold
 specified with <a href="postconf.5.html#postscreen_dnsbl_threshold">postscreen_dnsbl_threshold</a>, <a href="postscreen.8.html">postscreen(8)</a> can drop
 the connection with the SMTP client. </p>
@@ -6814,10 +6947,40 @@ parameter. </p>
 <p> This feature is available in Postfix 2.8.  </p>
 
 
+</DD>
+
+<DT><b><a name="postscreen_dnsbl_ttl">postscreen_dnsbl_ttl</a>
+(default: 1d)</b></DT><DD>
+
+<p> The amount of time that <a href="postscreen.8.html">postscreen(8)</a> will cache results from
+a successful DNS blocklist test. During this time, the client IP address
+is excluded from this test. The default is relatively short, because a
+good client can immediately talk to a real Postfix SMTP server.
+</p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+
+</DD>
+
+<DT><b><a name="postscreen_forbidden_commands">postscreen_forbidden_commands</a>
+(default: $<a href="postconf.5.html#smtpd_forbidden_commands">smtpd_forbidden_commands</a>)</b></DT><DD>
+
+<p> List of commands that <a href="postscreen.8.html">postscreen(8)</a> server considers in violation
+of the SMTP protocol. See also: <a href="postconf.5.html#postscreen_non_smtp_command_action">postscreen_non_smtp_command_action</a>.
+</p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+
 </DD>
 
 <DT><b><a name="postscreen_greet_action">postscreen_greet_action</a>
-(default: continue)</b></DT><DD>
+(default: ignore)</b></DT><DD>
 
 <p>The action that <a href="postscreen.8.html">postscreen(8)</a> takes when an SMTP client speaks
 before its turn within the time specified with the <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a>
@@ -6825,19 +6988,23 @@ parameter.  Specify one of the following: </p>
 
 <dl>
 
-<dt> continue </dt>
+<dt> <b>ignore</b> (default) </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Repeat this test the next time the client connects.
+This option is useful for testing and collecting statistics
+without blocking mail. </dd>
+
+<dt> <b>enforce</b> </dt>
 
-<dd> Continue waiting until the <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> time has
-elapsed. If the client's combined DNSBL score is equal to or greater
-than a threshold (as specified with the <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> and
-<a href="postconf.5.html#postscreen_dnsbl_threshold">postscreen_dnsbl_threshold</a> parameters), execute the action specified
-with the <a href="postconf.5.html#postscreen_dnsbl_action">postscreen_dnsbl_action</a> parameter, otherwise forward the
-connection to a real SMTP server process. </dd>
+<dd> Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
 
-<dt> drop </dt>
+<dt> <b>drop</b> </dt>
 
-<dd> Drop the connection immediately with a 521 SMTP reply, without
-examining DNSBL lookup results. </dd>
+<dd> Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects. </dd>
 
 </dl>
 
@@ -6862,17 +7029,35 @@ value to disable this feature.  </p>
 <p> This feature is available in Postfix 2.8. </p>
 
 
+</DD>
+
+<DT><b><a name="postscreen_greet_ttl">postscreen_greet_ttl</a>
+(default: 1d)</b></DT><DD>
+
+<p> The amount of time that <a href="postscreen.8.html">postscreen(8)</a> will cache results from
+a successful PREGREET test. During this time, the client IP address
+is excluded from this test. The default is relatively short, because
+a good client can immediately talk to a real Postfix SMTP server. </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).  </p>
+
+<p> This feature is available in Postfix 2.8. </p>
+
+
 </DD>
 
 <DT><b><a name="postscreen_greet_wait">postscreen_greet_wait</a>
-(default: 4s)</b></DT><DD>
+(default: ${stress?2}${stress:6}s)</b></DT><DD>
 
 <p> The amount of time that <a href="postscreen.8.html">postscreen(8)</a> will wait for an SMTP
 client to send a command before its turn, and for DNS blocklist
-lookup results to arrive. This is done only when the SMTP client
-IP address is not permanently whitelisted, and when it has no cached
-decision.  Specify a non-zero time value (an integral value plus
-an optional one-letter suffix that specifies the time unit).  </p>
+lookup results to arrive (default: 2 seconds under stress, 6 seconds
+normally).  This is done only when the SMTP client IP address is
+not permanently whitelisted, and when it has no cached decision.
+Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  </p>
 
 <p> Time units: s (seconds), m (minutes), h (hours), d (days), w
 (weeks).  </p>
@@ -6882,35 +7067,148 @@ an optional one-letter suffix that specifies the time unit).  </p>
 
 </DD>
 
-<DT><b><a name="postscreen_hangup_action">postscreen_hangup_action</a>
-(default: continue)</b></DT><DD>
+<DT><b><a name="postscreen_helo_required">postscreen_helo_required</a>
+(default: $<a href="postconf.5.html#smtpd_helo_required">smtpd_helo_required</a>)</b></DT><DD>
+
+<p> Require that a remote SMTP client sends HELO or EHLO before
+commencing a MAIL transaction. </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
 
-<p>The action that <a href="postscreen.8.html">postscreen(8)</a> takes when an SMTP client disconnects
-without sending data, within the time specified with the
-<a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> parameter.  Specify one of the following:
-</p>
+
+</DD>
+
+<DT><b><a name="postscreen_non_smtp_command_action">postscreen_non_smtp_command_action</a>
+(default: drop)</b></DT><DD>
+
+<p> The action that <a href="postscreen.8.html">postscreen(8)</a> takes when an SMTP client sends
+non-SMTP commands as specified with the <a href="postconf.5.html#postscreen_forbidden_commands">postscreen_forbidden_commands</a>
+parameter.  Specify one of the following: </p>
 
 <dl>
 
-<dt> continue </dt>
+<dt> <b>ignore</b> </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Do <i>not</i> repeat this test before some the result from some
+other test expires.
+This option is useful for testing and collecting statistics
+without blocking mail permanently. </dd>
+
+<dt> <b>enforce</b> </dt>
+
+<dd> Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
+
+<dt> <b>drop</b> </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects. This action is the
+same as with the Postfix SMTP server's <a href="postconf.5.html#smtpd_forbidden_commands">smtpd_forbidden_commands</a>
+feature.  </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.8. </p>
+
+
+</DD>
+
+<DT><b><a name="postscreen_non_smtp_command_enable">postscreen_non_smtp_command_enable</a>
+(default: no)</b></DT><DD>
+
+<p> Enable "non-SMTP command" tests in the <a href="postscreen.8.html">postscreen(8)</a> server. These
+tests are expensive: a client must disconnect after it passes the
+test, before it can talk to a real Postfix SMTP server. </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
 
-<dd> Continue waiting until the <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> time has
-elapsed, and report whether the client's combined DNSBL score is
-equal to or greater than a threshold (as defined with the
-<a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> and <a href="postconf.5.html#postscreen_dnsbl_threshold">postscreen_dnsbl_threshold</a> parameters).
-Do not forward the broken connection to a real SMTP server process.
-</dd>
 
-<dt> drop </dt>
+</DD>
+
+<DT><b><a name="postscreen_non_smtp_command_ttl">postscreen_non_smtp_command_ttl</a>
+(default: 30d)</b></DT><DD>
+
+<p> The amount of time that <a href="postscreen.8.html">postscreen(8)</a> will cache results from
+a successful "non_smtp_command" SMTP protocol test. During this
+time, the client IP address is excluded from this test. The default
+is long because a client must disconnect after it passes the test,
+before it can talk to a real Postfix SMTP server. </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+
+</DD>
+
+<DT><b><a name="postscreen_pipelining_action">postscreen_pipelining_action</a>
+(default: enforce)</b></DT><DD>
 
-<dd> Drop the connection immediately, without reporting DNSBL lookup
-results. </dd>
+<p> The action that <a href="postscreen.8.html">postscreen(8)</a> takes when an SMTP client sends
+multiple commands instead of sending one command and waiting for
+the server to respond.  Specify one of the following: </p>
+
+<dl>
+
+<dt> <b>ignore</b> </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Do <i>not</i> repeat this test before some the result from some
+other test expires.
+This option is useful for testing and collecting statistics
+without blocking mail permanently. </dd>
+
+<dt> <b>enforce</b> </dt>
+
+<dd> Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
+
+<dt> <b>drop</b> </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects. </dd>
 
 </dl>
 
 <p> This feature is available in Postfix 2.8. </p>
 
 
+</DD>
+
+<DT><b><a name="postscreen_pipelining_enable">postscreen_pipelining_enable</a>
+(default: no)</b></DT><DD>
+
+<p> Enable "pipelining" SMTP protocol tests in the <a href="postscreen.8.html">postscreen(8)</a>
+server. These tests are expensive: a good client must disconnect
+after it passes the test, before it can talk to a real Postfix SMTP
+server. </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+
+</DD>
+
+<DT><b><a name="postscreen_pipelining_ttl">postscreen_pipelining_ttl</a>
+(default: 30d)</b></DT><DD>
+
+<p> The amount of time that <a href="postscreen.8.html">postscreen(8)</a> will cache results from
+a successful "pipelining" SMTP protocol test. During this time, the
+client IP address is excluded from this test. The default is
+long because a good client must disconnect after it passes the test,
+before it can talk to a real Postfix SMTP server. </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+
 </DD>
 
 <DT><b><a name="postscreen_post_queue_limit">postscreen_post_queue_limit</a>
@@ -6936,6 +7234,26 @@ receive a 421 reponse. </p>
 <p> This feature is available in Postfix 2.8. </p>
 
 
+</DD>
+
+<DT><b><a name="postscreen_watchdog_timeout">postscreen_watchdog_timeout</a>
+(default: 10s)</b></DT><DD>
+
+<p> How much time a <a href="postscreen.8.html">postscreen(8)</a> process may take to respond to
+an SMTP client command or to perform a cache operation before it
+is terminated by a built-in watchdog timer.  This is a safety
+mechanism that prevents <a href="postscreen.8.html">postscreen(8)</a> from becoming non-responsive
+due to a bug in Postfix itself or in system software.  To avoid
+false alarms and unnecessary cache corruption this limit cannot be
+set under 10s.  </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+
 </DD>
 
 <DT><b><a name="postscreen_whitelist_networks">postscreen_whitelist_networks</a>
@@ -11813,7 +12131,7 @@ This feature is available in Postfix 2.0 and later.
 (default: CONNECT, GET, POST)</b></DT><DD>
 
 <p>
-List of commands that causes the Postfix SMTP server to immediately
+List of commands that cause the Postfix SMTP server to immediately
 terminate the session with a 221 code. This can be used to disconnect
 clients that obviously attempt to abuse the system. In addition to the
 commands listed in this parameter, commands that follow the "Label:"
@@ -12982,7 +13300,7 @@ Examples:
 
 </DD>
 
-<DT><b><a name="smtpd_service">smtpd_service</a>
+<DT><b><a name="smtpd_service_name">smtpd_service_name</a>
 (default: smtpd)</b></DT><DD>
 
 <p> The internal service that <a href="postscreen.8.html">postscreen(8)</a> forwards allowed
index 0620620cf743a058f717979c42522386e145fdc6..fa87b132106515bbc428b47b750ef0ea0c371e3f 100644 (file)
@@ -15,198 +15,35 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
 <b>DESCRIPTION</b>
        The Postfix <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server performs triage on multi-
        ple  inbound   SMTP   connections   in   parallel.   While
-       <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  keeps  zombies and other bogus clients away
-       from Postfix SMTP  server  processes,  more  Postfix  SMTP
-       server  processes remain available for legitimate clients.
-
-<b>GENERAL OPERATION</b>
-       The triage process involves a  number  of  tests,  in  the
-       order as described below.  Some tests introduce a delay of
-       a few seconds.  Once a client passes  all  tests,  its  IP
-       address  is temporarily excluded from the tests, typically
-       for 24 hours.  This minimizes the impact of the  tests  on
-       legitimate mail clients.
-
-       After  logging  the  result of its tests, <a href="postscreen.8.html"><b>postscreen</b>(8)</a> by
-       default forwards all connections to  a  real  SMTP  server
-       process.  This mode is useful for non-destructive testing.
+       <a href="postscreen.8.html"><b>postscreen</b>(8)</a> keeps spambots away from Postfix SMTP server
+       processes,  more  Postfix  SMTP  server  processes  remain
+       available for legitimate clients.
+
+       <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  maintains a temporary whitelist of positive
+       decisions. Once an SMTP client is whitelisted, it is imme-
+       diately  forwarded  to  a real Postfix SMTP server process
+       without further checking.
+
+       By default, the program logs only statistics, and it  does
+       not  run  any tests against clients in <a href="postconf.5.html#mynetworks">mynetworks</a> (primar-
+       ily, to avoid problems with non-standard SMTP  implementa-
+       tions   in   network   appliances   and   test  programs).
+       <a href="postscreen.8.html"><b>postscreen</b>(8)</a> by default forwards  all  connections  to  a
+       real  SMTP  server  process.  This mode is useful for non-
+       destructive testing.
 
        In a typical production setting, <a href="postscreen.8.html"><b>postscreen</b>(8)</a> is  config-
-       ured to disconnect clients that fail some tests.  A future
-       implementation may pass the connection  to  a  dummy  SMTP
-       protocol engine that logs sender and recipient information
-       before hanging up.
-
-       Note: <a href="postscreen.8.html"><b>postscreen</b>(8)</a> is not an SMTP proxy; this  is  inten-
-       tional.  The  purpose  is to prioritize legitimate clients
-       with as little overhead as possible.
-
-<b>1. PERMANENT WHITELIST TEST</b>
-       The  <a href="postconf.5.html#postscreen_whitelist_networks">postscreen_whitelist_networks</a>   parameter   (default:
-       $<a href="postconf.5.html#mynetworks">mynetworks</a>)  specifies  a  permanent  whitelist  for SMTP
-       client IP addresses.
-
-       When  the  SMTP  client  address  matches  the   permanent
-       whitelist, this is logged as:
-
-       <b>WHITELISTED</b> <i>address</i>
-
-       The  action  is  not configurable: immediately forward the
-       connection to a real SMTP server process.
-
-<b>2. PERMANENT BLACKLIST TEST</b>
-       The  <a href="postconf.5.html#postscreen_blacklist_networks">postscreen_blacklist_networks</a>   parameter   (default:
-       empty)  specifies a permanent blacklist for SMTP client IP
-       addresses.  The address syntax is as with <a href="postconf.5.html#mynetworks">mynetworks</a>.
-
-       When the SMTP client address matches the permanent  black-
-       list, this is logged as:
-
-       <b>BLACKLISTED</b> <i>address</i>
-
-       The  <a href="postconf.5.html#postscreen_blacklist_action">postscreen_blacklist_action</a>  parameter  specifies the
-       action that is taken next:
-
-       <b>continue</b> (default)
-              Continue with the SMTP GREETING PHASE TESTS  below.
-
-       <b>drop</b>   Drop  the  connection  immediately  with a 521 SMTP
-              reply.  In a future implementation, the  connection
-              may  instead  be  passed  to  a dummy SMTP protocol
-              engine that logs sender and recipient  information.
-
-<b>3. TEMPORARY WHITELIST TEST</b>
-       The  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  daemon maintains a <i>temporary</i> whitelist
-       for SMTP client IP addresses  that  have  passed  all  the
-       tests  described below. The <a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a> parameter
-       specifies the location of the  temporary  whitelist.   The
-       temporary  whitelist is not used for SMTP client addresses
-       that appear on the <i>permanent</i> blacklist or whitelist.
-
-       When the SMTP client  address  appears  on  the  temporary
-       whitelist, this is logged as:
-
-       <b>PASS OLD</b> <i>address</i>
-
-       The  action  is  not configurable: immediately forward the
-       connection to a real SMTP server process.  The  client  is
-       excluded  from further tests until its temporary whitelist
-       entry expires, as controlled with the <a href="postconf.5.html#postscreen_cache_ttl">postscreen_cache_ttl</a>
-       parameter.  Expired entries are silently renewed if possi-
-       ble.
-
-<b>4. SMTP GREETING PHASE TESTS</b>
-       The  <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a>  parameter  specifies  a   time
-       interval during which <a href="postscreen.8.html"><b>postscreen</b>(8)</a> runs a number of tests
-       in parallel.  These tests are described below, and are run
-       before  the  client  may  see  the real SMTP server's "220
-       text..." server greeting.
-
-       When the SMTP client passes all greeting-phase tests, this
-       is logged as:
-
-       <b>PASS NEW</b> <i>address</i>
-
-       The  action  is  to  forward the connection to a real SMTP
-       server process and to create a temporary  whitelist  entry
-       that  excludes  the  client  IP address from further tests
-       until the temporary whitelist entry expires, as controlled
-       with the <a href="postconf.5.html#postscreen_cache_ttl">postscreen_cache_ttl</a> parameter.
-
-       In  a  future  implementation, the connection may first be
-       passed to a dummy SMTP  protocol  engine  that  implements
-       more  protocol  tests  including  greylisting,  before the
-       client is allowed to talk to a real SMTP server process.
-
-<b>4A. PREGREET TEST</b>
-       The <a href="postconf.5.html#postscreen_greet_banner">postscreen_greet_banner</a> parameter specifies  the  <i>text</i>
-       portion   of   a  "220-<i>text</i>..."  teaser  banner  (default:
-       $<a href="postconf.5.html#smtpd_banner">smtpd_banner</a>).   The  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  daemon  sends   this
-       before  the  <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a>  timer is started.  The
-       purpose of the teaser banner is to confuse SPAM clients so
-       that  they  speak  before  their turn. It has no effect on
-       SMTP clients that correctly implement the protocol.
-
-       To avoid problems with  broken  SMTP  engines  in  network
-       appliances,  either  exclude  them from all tests with the
-       <a href="postconf.5.html#postscreen_whitelist_networks">postscreen_whitelist_networks</a> feature or else  specify  an
-       empty   <a href="postconf.5.html#postscreen_greet_banner">postscreen_greet_banner</a>   value   to  disable  the
-       "220-text..."  teaser banner.
-
-       When  an  SMTP  client  sends   a   command   before   the
-       <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> time has elapsed, this is logged as:
-
-       <b>PREGREET</b> <i>count</i> <b>after</b> <i>time</i> <b>from</b> <i>address text...</i>
-
-       Translation: the client at <i>address</i> sent <i>count</i> bytes before
-       its  turn  to  speak, and this happened <i>time</i> seconds after
-       the <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> timer was started.  The <i>text</i>  is
-       what  the  client  sent  (truncated to 100 bytes, and with
-       non-printable characters replaced with "?").
-
-       The <a href="postconf.5.html#postscreen_greet_action">postscreen_greet_action</a> parameter specifies the action
-       that is taken next:
-
-       <b>continue</b> (default)
-              Wait   until  the  <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a>  time  has
-              elapsed, then report DNSBL lookup results if appli-
-              cable. Either perform DNSBL-related actions or for-
-              ward the connection to a real SMTP server  process.
-
-       <b>drop</b>   Drop  the  connection  immediately  with a 521 SMTP
-              reply.  In a future implementation, the  connection
-              may  instead  be  passed  to  a dummy SMTP protocol
-              engine that logs sender and recipient  information.
-
-<b>4B. HANGUP TEST</b>
-       When  the  SMTP  client  hangs up without sending any data
-       before the <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> time has elapsed, this is
-       logged as:
-
-       <b>HANGUP after</b> <i>time</i> <b>from</b> <i>address</i>
-
-       The  <a href="postconf.5.html#postscreen_hangup_action">postscreen_hangup_action</a> specifies the action that is
-       taken next:
-
-       <b>continue</b> (default)
-              Wait  until  the  <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a>  time   has
-              elapsed, then report DNSBL lookup results if appli-
-              cable. Do not forward the broken  connection  to  a
-              real SMTP server process.
-
-       <b>drop</b>   Drop the connection immediately.
-
-<b>4C. DNS BLOCKLIST TEST</b>
-       The   <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a>  parameter  (default:  empty)
-       specifies a list of DNS blocklist servers.  These  lookups
-       are made in parallel.
-
-       When  the  <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> time has elapsed, and the
-       combined DNSBL score is  equal  to  or  greater  than  the
-       <a href="postconf.5.html#postscreen_dnsbl_threshold">postscreen_dnsbl_threshold</a> parameter value, this is logged
-       as:
-
-       <b>DNSBL rank</b> <i>count</i> <b>for</b> <i>address</i>
-
-       Translation: the SMTP client at  <i>address</i>  has  a  combined
-       DNSBL score of <i>count</i>.
-
-       The <a href="postconf.5.html#postscreen_dnsbl_action">postscreen_dnsbl_action</a> parameter specifies the action
-       that is taken when the combined DNSBL score is equal to or
-       greater than the threshold:
-
-       <b>continue</b> (default)
-              Forward  the  connection  to  a  real  SMTP  server
-              process.
+       ured  to  reject  mail  from clients that fail one or more
+       tests, after logging the helo, sender and recipient infor-
+       mation.
 
-       <b>drop</b>   Drop the connection immediately  with  a  521  SMTP
-              reply.   In a future implementation, the connection
-              may instead be passed  to  a  dummy  SMTP  protocol
-              engine  that logs sender and recipient information.
+       <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  is  not an SMTP proxy; this is intentional.
+       The purpose is to keep spambots  away  from  Postfix  SMTP
+       server processes, not to control traffic flows.
 
 <b>SECURITY</b>
        The <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server is moderately security-sensitive.
-       It  talks to untrusted clients on the network. The process
+       It talks to untrusted clients on the network. The  process
        can be run chrooted at fixed low privilege.
 
 <b>STANDARDS</b>
@@ -216,16 +53,42 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
 <b>DIAGNOSTICS</b>
        Problems and transactions are logged to <b>syslogd</b>(8).
 
+<b>BUGS</b>
+       When  successful  tests  involve  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>'s built-in
+       SMTP protocol engine, <a href="postscreen.8.html"><b>postscreen</b>(8)</a> adds the client to the
+       temporary  whitelist but it cannot not hand off the "live"
+       connection from a good  SMTP  client  to  a  Postfix  SMTP
+       server process.  Instead, <a href="postscreen.8.html"><b>postscreen</b>(8)</a> defers attempts to
+       deliver mail with a 4XX status, and waits for  the  client
+       to  disconnect.   The next time a good client connects, it
+       will be allowed to talk to a Postfix SMTP  server  process
+       to deliver mail.
+
 <b>CONFIGURATION PARAMETERS</b>
-       Changes to <a href="postconf.5.html">main.cf</a> are not  picked  up  automatically,  as
-       <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  processes  may  run for several hours.  Use
+       Changes  to  <a href="postconf.5.html">main.cf</a>  are  not picked up automatically, as
+       <a href="postscreen.8.html"><b>postscreen</b>(8)</a> processes may run for  several  hours.   Use
        the command "postfix reload" after a configuration 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.
 
+       NOTE: Some parameters implement stress-dependent behavior.
+       This is supported only when the default value  is  stress-
+       dependent   (${stress?X}${stress:Y}).   Other   parameters
+       always evaluate as  if  the  stress  value  is  the  empty
+       string.
+
 <b>TRIAGE PARAMETERS</b>
-       <b><a href="postconf.5.html#postscreen_blacklist_action">postscreen_blacklist_action</a> (continue)</b>
+       <b><a href="postconf.5.html#postscreen_bare_newline_action">postscreen_bare_newline_action</a> (ignore)</b>
+              The  action  that  <a href="postscreen.8.html"><b>postscreen</b>(8)</a> takes when an SMTP
+              client sends a bare newline character, that  is,  a
+              newline not preceded by carriage return.
+
+       <b><a href="postconf.5.html#postscreen_bare_newline_enable">postscreen_bare_newline_enable</a> (no)</b>
+              Enable  "bare  newline"  SMTP protocol tests in the
+              <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server.
+
+       <b><a href="postconf.5.html#postscreen_blacklist_action">postscreen_blacklist_action</a> (ignore)</b>
               The action that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> takes  when  an  SMTP
               client   is   permanently   blacklisted   with  the
               <a href="postconf.5.html#postscreen_blacklist_networks">postscreen_blacklist_networks</a> parameter.
@@ -235,12 +98,22 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
               see  the  <a href="postconf.5.html#postscreen_blacklist_action">postscreen_blacklist_action</a> parameter for
               possible actions.
 
-       <b><a href="postconf.5.html#postscreen_dnsbl_action">postscreen_dnsbl_action</a> (continue)</b>
-              The action that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> takes  when  an  SMTP
+       <b><a href="postconf.5.html#postscreen_disable_vrfy_command">postscreen_disable_vrfy_command</a> ($<a href="postconf.5.html#disable_vrfy_command">disable_vrfy_command</a>)</b>
+              Disable the SMTP VRFY command in the  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>
+              daemon.
+
+       <b><a href="postconf.5.html#postscreen_dnsbl_action">postscreen_dnsbl_action</a> (ignore)</b>
+              The  action  that  <a href="postscreen.8.html"><b>postscreen</b>(8)</a> takes when an SMTP
               client's  combined  DNSBL  score  is  equal  to  or
-              greater than  a  threshold  (as  defined  with  the
-              <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> and postscreen_dnsbl_thresh-
-              old parameters).
+              greater  than  a  threshold  (as  defined  with the
+              <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> and <a href="postconf.5.html#postscreen_dnsbl_threshold">postscreen_dnsbl_thresh</a>-
+              <a href="postconf.5.html#postscreen_dnsbl_threshold">old</a> parameters).
+
+       <b><a href="postconf.5.html#postscreen_dnsbl_reply_map">postscreen_dnsbl_reply_map</a> (empty)</b>
+              A  mapping  from  actual  DNSBL  domain  name which
+              includes a secret password,  to  the  DNSBL  domain
+              name  that  postscreen  will  reply  with  when  it
+              rejects mail.
 
        <b><a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> (empty)</b>
               Optional list of DNS blocklist domains, filters and
@@ -251,8 +124,12 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
               client,  based  on  its  combined  DNSBL  score  as
               defined  with the <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> parameter.
 
-       <b><a href="postconf.5.html#postscreen_greet_action">postscreen_greet_action</a> (continue)</b>
-              The action that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> takes  when  an  SMTP
+       <b><a href="postconf.5.html#postscreen_forbidden_commands">postscreen_forbidden_commands</a> ($<a href="postconf.5.html#smtpd_forbidden_commands">smtpd_forbidden_commands</a>)</b>
+              List of commands that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server  consid-
+              ers in violation of the SMTP protocol.
+
+       <b><a href="postconf.5.html#postscreen_greet_action">postscreen_greet_action</a> (ignore)</b>
+              The  action  that  <a href="postscreen.8.html"><b>postscreen</b>(8)</a> takes when an SMTP
               client speaks before its turn within the time spec-
               ified with the <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> parameter.
 
@@ -260,72 +137,125 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
               The  <i>text</i>  in  the  optional  "220-<i>text</i>..."  server
               response that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> sends ahead of the real
               Postfix SMTP server's "220 text..." response, in an
-              attempt  to  confuse  bad SMTP clients so that they
+              attempt to confuse bad SMTP clients  so  that  they
               speak before their turn (pre-greet).
 
-       <b><a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> (4s)</b>
+       <b><a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> (${stress?2}${stress:6}s)</b>
               The amount of time that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> will wait for
-              an  SMTP  client to send a command before its turn,
-              and for DNS blocklist lookup results to arrive.
+              an SMTP client to send a command before  its  turn,
+              and  for  DNS  blocklist  lookup  results to arrive
+              (default: 2 seconds under stress,  6  seconds  nor-
+              mally).
 
-       <b><a href="postconf.5.html#postscreen_hangup_action">postscreen_hangup_action</a> (continue)</b>
+       <b><a href="postconf.5.html#postscreen_helo_required">postscreen_helo_required</a> ($<a href="postconf.5.html#smtpd_helo_required">smtpd_helo_required</a>)</b>
+              Require  that  a  remote  SMTP client sends HELO or
+              EHLO before commencing a MAIL transaction.
+
+       <b><a href="postconf.5.html#postscreen_non_smtp_command_action">postscreen_non_smtp_command_action</a> (drop)</b>
               The action that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> takes  when  an  SMTP
-              client disconnects without sending data, within the
-              time  specified  with   the   <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a>
-              parameter.
+              client  sends  non-SMTP  commands as specified with
+              the <a href="postconf.5.html#postscreen_forbidden_commands">postscreen_forbidden_commands</a> parameter.
 
-       <b><a href="postconf.5.html#postscreen_post_queue_limit">postscreen_post_queue_limit</a> ($<a href="postconf.5.html#default_process_limit">default_process_limit</a>)</b>
-              The  number of clients that can be waiting for ser-
-              vice from a real SMTP server process.
+       <b><a href="postconf.5.html#postscreen_non_smtp_command_enable">postscreen_non_smtp_command_enable</a> (no)</b>
+              Enable   "non-SMTP   command"    tests    in    the
+              <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server.
 
-       <b><a href="postconf.5.html#postscreen_pre_queue_limit">postscreen_pre_queue_limit</a> ($<a href="postconf.5.html#default_process_limit">default_process_limit</a>)</b>
-              The number of non-whitelisted clients that  can  be
-              waiting  for  a  decision whether they will receive
-              service from a real SMTP server process.
+       <b><a href="postconf.5.html#postscreen_pipelining_action">postscreen_pipelining_action</a> (enforce)</b>
+              The  action  that  <a href="postscreen.8.html"><b>postscreen</b>(8)</a> takes when an SMTP
+              client sends multiple commands instead  of  sending
+              one  command and waiting for the server to respond.
+
+       <b><a href="postconf.5.html#postscreen_pipelining_enable">postscreen_pipelining_enable</a> (no)</b>
+              Enable "pipelining"  SMTP  protocol  tests  in  the
+              <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server.
 
        <b><a href="postconf.5.html#postscreen_whitelist_networks">postscreen_whitelist_networks</a> ($<a href="postconf.5.html#mynetworks">mynetworks</a>)</b>
               Network addresses that are permanently whitelisted,
-              and  that  will  not  be subjected to <a href="postscreen.8.html"><b>postscreen</b>(8)</a>
+              and that will not  be  subjected  to  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>
               checks.
 
-       <b><a href="postconf.5.html#smtpd_service">smtpd_service</a> (smtpd)</b>
-              The internal service  that  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  forwards
+       <b><a href="postconf.5.html#smtpd_service_name">smtpd_service_name</a> (smtpd)</b>
+              The  internal  service  that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> forwards
               allowed connections to.
 
 <b>CACHE CONTROLS</b>
        <b><a href="postconf.5.html#postscreen_cache_cleanup_interval">postscreen_cache_cleanup_interval</a> (12h)</b>
-              The  amount  of  time  between  <a href="postscreen.8.html"><b>postscreen</b>(8)</a> cache
+              The amount  of  time  between  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  cache
               cleanup runs.
 
        <b><a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a> (btree:$<a href="postconf.5.html#data_directory">data_directory</a>/ps_cache)</b>
-              Persistent storage  for  the  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  server
+              Persistent  storage  for  the  <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server
               decisions.
 
-       <b><a href="postconf.5.html#postscreen_cache_retention_time">postscreen_cache_retention_time</a> (1d)</b>
+       <b><a href="postconf.5.html#postscreen_cache_retention_time">postscreen_cache_retention_time</a> (7d)</b>
               The amount of time that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> will cache an
-              expired temporary  whitelist  entry  before  it  is
+              expired  temporary  whitelist  entry  before  it is
               removed.
 
-       <b><a href="postconf.5.html#postscreen_cache_ttl">postscreen_cache_ttl</a> (1d)</b>
-              The  amount of time that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> will cache a
-              decision for a specific SMTP client IP address.
+       <b><a href="postconf.5.html#postscreen_bare_newline_ttl">postscreen_bare_newline_ttl</a> (30d)</b>
+              The amount of time that  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  will  cache
+              results  from a successful "bare newline" SMTP pro-
+              tocol test.
+
+       <b><a href="postconf.5.html#postscreen_dnsbl_ttl">postscreen_dnsbl_ttl</a> (1d)</b>
+              The amount of time that  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  will  cache
+              results from a successful DNS blocklist test.
+
+       <b><a href="postconf.5.html#postscreen_greet_ttl">postscreen_greet_ttl</a> (1d)</b>
+              The  amount  of  time that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> will cache
+              results from a successful PREGREET test.
+
+       <b><a href="postconf.5.html#postscreen_non_smtp_command_ttl">postscreen_non_smtp_command_ttl</a> (30d)</b>
+              The amount of time that  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  will  cache
+              results  from  a successful "non_smtp_command" SMTP
+              protocol test.
+
+       <b><a href="postconf.5.html#postscreen_pipelining_ttl">postscreen_pipelining_ttl</a> (30d)</b>
+              The amount of time that  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>  will  cache
+              results  from a successful "pipelining" SMTP proto-
+              col test.
+
+<b>RESOURCE CONTROLS</b>
+       <b><a href="postconf.5.html#line_length_limit">line_length_limit</a> (2048)</b>
+              Upon input, long lines are chopped up  into  pieces
+              of  at  most this length; upon delivery, long lines
+              are reconstructed.
+
+       <b><a href="postconf.5.html#postscreen_command_count_limit">postscreen_command_count_limit</a> (20)</b>
+              The limit on the total number of commands per  SMTP
+              session  for <a href="postscreen.8.html"><b>postscreen</b>(8)</a>'s built-in SMTP protocol
+              engine.
+
+       <b><a href="postconf.5.html#postscreen_command_time_limit">postscreen_command_time_limit</a> (${stress?10}${stress:300}s)</b>
+              The command "read" time limit  for  <a href="postscreen.8.html"><b>postscreen</b>(8)</a>'s
+              built-in SMTP protocol engine.
+
+       <b><a href="postconf.5.html#postscreen_post_queue_limit">postscreen_post_queue_limit</a> ($<a href="postconf.5.html#default_process_limit">default_process_limit</a>)</b>
+              The  number of clients that can be waiting for ser-
+              vice from a real SMTP server process.
+
+       <b><a href="postconf.5.html#postscreen_pre_queue_limit">postscreen_pre_queue_limit</a> ($<a href="postconf.5.html#default_process_limit">default_process_limit</a>)</b>
+              The number of non-whitelisted clients that  can  be
+              waiting  for  a  decision whether they will receive
+              service from a real SMTP server process.
+
+       <b><a href="postconf.5.html#postscreen_watchdog_timeout">postscreen_watchdog_timeout</a> (10s)</b>
+              How much time a <a href="postscreen.8.html"><b>postscreen</b>(8)</a> process may  take  to
+              respond  to  an SMTP client command or to perform a
+              cache operation before it is terminated by a built-
+              in watchdog timer.
 
 <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
-              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#command_directory">command_directory</a> (see 'postconf -d' output)</b>
-              The location of  all  postfix  administrative  com-
+              The  location  of  all  postfix administrative com-
               mands.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
@@ -333,24 +263,24 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
               over an internal communication channel.
 
        <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
-              The maximum amount of time  that  an  idle  Postfix
-              daemon  process  waits  for  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#process_id">process_id</a> (read-only)</b>
-              The process ID  of  a  Postfix  command  or  daemon
+              The  process  ID  of  a  Postfix  command or daemon
               process.
 
        <b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
-              The  process  name  of  a Postfix command or daemon
+              The process name of a  Postfix  command  or  daemon
               process.
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
-              The mail system  name  that  is  prepended  to  the
-              process  name  in  syslog  records, so that "smtpd"
+              The  mail  system  name  that  is  prepended to the
+              process name in syslog  records,  so  that  "smtpd"
               becomes, for example, "postfix/smtpd".
 
 <b>SEE ALSO</b>
@@ -358,6 +288,9 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
        <a href="dnsblog.8.html">dnsblog(8)</a>, temporary DNS helper
        syslogd(8), system logging
 
+<b>README FILES</b>
+       <a href="POSTSCREEN_README.html">POSTSCREEN_README</a>, Postfix Postscreen Howto
+
 <b>LICENSE</b>
        The Secure Mailer license must be  distributed  with  this
        software.
index 39cc376e6e4c7de3f84a265f2a8c48a8b2ac8724..b2e67efb2792192215f3fdeb39824b10c5d33f0a 100644 (file)
@@ -1237,9 +1237,9 @@ SMTPD(8)                                                              SMTPD(8)
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#smtpd_forbidden_commands">smtpd_forbidden_commands</a> (CONNECT, GET, POST)</b>
-              List  of  commands  that  causes  the  Postfix SMTP
-              server to immediately terminate the session with  a
-              221 code.
+              List of commands that cause the Postfix SMTP server
+              to immediately terminate the  session  with  a  221
+              code.
 
        Available in Postfix version 2.5 and later:
 
index bf70ffed7073b184eb6217f98979b976b999cbc9..e5dd6949ab2b65f9bf5f5ddea56ce9c93bcca003 100644 (file)
@@ -165,7 +165,10 @@ result.
 .IP "\fBtcp\fR (read-only)"
 Perform lookups using a simple request-reply protocol that is
 described in \fBtcp_table\fR(5).
-This feature is not included with the stable Postfix release.
+.IP "\fBtexthash\fR (read-only)"
+Produces similar results as hash: files, except that you don't
+need to run the postmap(1) command before you can use the file,
+and that it does not detect changes after the file is read.
 .IP "\fBunix\fR (read-only)"
 A limited way to query the UNIX authentication database. The
 following tables are implemented:
index cae19dc47608fda9442b45f6ebdf38d273a3c245..dce9b81c5dcd283b99f39011cece274b385d0047 100644 (file)
@@ -3719,20 +3719,59 @@ as "stop" commands. For these commands, disabled instances are skipped,
 and enabled instances are processed in reverse order.
 .PP
 This feature is available in Postfix 2.6 and later.
-.SH postscreen_blacklist_action (default: continue)
+.SH postscreen_bare_newline_action (default: ignore)
+The action that \fBpostscreen\fR(8) takes when an SMTP client sends
+a bare newline character, that is, a newline not preceded by carriage
+return.  Specify one of the following:
+.IP "\fBignore\fR"
+Ignore the failure of this test. Allow other tests to complete.
+Do \fInot\fR repeat this test before some the result from some
+other test expires.
+This option is useful for testing and collecting statistics
+without blocking mail permanently.
+.IP "\fBenforce\fR"
+Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects.
+.IP "\fBdrop\fR"
+Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects.
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_bare_newline_enable (default: no)
+Enable "bare newline" SMTP protocol tests in the \fBpostscreen\fR(8)
+server. These tests are expensive: a client must disconnect after
+it passes the test, before it can talk to a real Postfix SMTP server.
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_bare_newline_ttl (default: 30d)
+The amount of time that \fBpostscreen\fR(8) will cache results from
+a successful "bare newline" SMTP protocol test. During this
+time, the client IP address is excluded from this test. The default
+is long because a client must disconnect after it passes the test,
+before it can talk to a real Postfix SMTP server.
+.PP
+Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_blacklist_action (default: ignore)
 The action that \fBpostscreen\fR(8) takes when an SMTP client is
 permanently blacklisted with the postscreen_blacklist_networks
 parameter.  Specify one of the following:
-.IP "continue"
-Continue waiting until the postscreen_greet_wait time has
-elapsed, and report whether the client triggers a PREGREET or HANGUP
-error, or whether the client's combined DNSBL score is equal to or
-greater than a threshold (as specified with the postscreen_dnsbl_sites
-and postscreen_dnsbl_threshold parameters).  Take the corresponding
-action, or forward the connection to a real SMTP server process.
-.IP "drop"
-Drop the connection immediately with a 521 SMTP reply, without
-reporting PREGREET, HANGUP or DNSBL results.
+.IP "\fBignore\fR (default)"
+Ignore  this result. Allow other tests to complete.  Repeat
+this test the next time the client connects.
+This option is useful for testing and collecting statistics
+without blocking mail.
+.IP "\fBenforce\fR"
+Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects.
+.IP "\fBdrop\fR"
+Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects.
 .PP
 This feature is available in Postfix 2.8.
 .SH postscreen_blacklist_networks (default: empty)
@@ -3764,37 +3803,85 @@ This feature is available in Postfix 2.8.
 Persistent storage for the \fBpostscreen\fR(8) server decisions.
 .PP
 This feature is available in Postfix 2.8.
-.SH postscreen_cache_retention_time (default: 1d)
+.SH postscreen_cache_retention_time (default: 7d)
 The amount of time that \fBpostscreen\fR(8) will cache an expired
 temporary whitelist entry before it is removed. This prevents clients
 from being logged as "NEW" just because their cache entry expired
-an hour ago.
+an hour ago. It also prevents the cache from filling up with clients
+that passed some deep protocol test once and never came back.
 .PP
 Time units: s (seconds), m (minutes), h (hours), d (days), w
 (weeks).
 .PP
 This feature is available in Postfix 2.8.
-.SH postscreen_cache_ttl (default: 1d)
-The amount of time that \fBpostscreen\fR(8) will cache a decision for
-a specific SMTP client IP address. During this time, the client IP
-address is excluded from tests. If possible, expired decisions are
-renewed automatically. Specify a non-zero time value (an integral
-value plus an optional one-letter suffix that specifies the time
-unit).
+.SH postscreen_command_count_limit (default: 20)
+The limit on the total number of commands per SMTP session for
+\fBpostscreen\fR(8)'s built-in SMTP protocol engine.  This SMTP engine
+defers or rejects all attempts to deliver mail, therefore there is
+no need to enforce separate limits on the number of junk commands
+and error commands.
 .PP
-Time units: s (seconds), m (minutes), h (hours), d (days), w
-(weeks).
+This feature is available in Postfix 2.8.
+.SH postscreen_command_time_limit (default: ${stress?10}${stress:300}s)
+The command "read" time limit for \fBpostscreen\fR(8)'s built-in SMTP
+protocol engine.
 .PP
 This feature is available in Postfix 2.8.
-.SH postscreen_dnsbl_action (default: continue)
+.SH postscreen_disable_vrfy_command (default: $disable_vrfy_command)
+Disable the SMTP VRFY command in the \fBpostscreen\fR(8) daemon.  See
+disable_vrfy_command for details.
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_dnsbl_action (default: ignore)
 The action that \fBpostscreen\fR(8) takes when an SMTP client's combined
 DNSBL score is equal to or greater than a threshold (as defined
 with the postscreen_dnsbl_sites and postscreen_dnsbl_threshold
 parameters).  Specify one of the following:
-.IP "continue"
-Forward the connection to a real SMTP server process.
-.IP "drop"
-Drop the connection with a 521 SMTP reply.
+.IP "\fBignore\fR (default)"
+Ignore the failure of this test. Allow other tests to complete.
+Repeat this test the next time the client connects.
+This option is useful for testing and collecting statistics
+without blocking mail.
+.IP "\fBenforce\fR"
+Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects.
+.IP "\fBdrop\fR"
+Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects.
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_dnsbl_reply_map (default: empty)
+A mapping from actual DNSBL domain name which includes a secret
+password, to the DNSBL domain name that postscreen will reply with
+when it rejects mail.  When no mapping is found, the actual DNSBL
+domain will be used.
+.PP
+For maximal stability it is best to use a file that is read
+into memory such as pcre:, regexp: or texthash: (a format similar
+to hash: except that there is no need to run \fBpostmap\fR(1) before the
+file can be used, and that it does not detect changes after the
+file is read).
+.PP
+Example:
+.PP
+.nf
+.na
+.ft C
+/etc/postfix/main.cf:
+    postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
+.fi
+.ad
+.ft R
+.PP
+.nf
+.na
+.ft C
+/etc/postfix/dnsbl_reply:
+   secret.zen.spamhaus.org     zen.spamhaus.org
+.fi
+.ad
+.ft R
 .PP
 This feature is available in Postfix 2.8.
 .SH postscreen_dnsbl_sites (default: empty)
@@ -3804,6 +3891,10 @@ query these domains with the IP addresses of non-whitelisted remote
 SMTP clients, and \fBpostscreen\fR(8) will update an SMTP client's DNSBL
 score with each non-error reply.
 .PP
+Caution: when postscreen rejects mail, it replies with the DNSBL
+domain name. Use the postscreen_dnsbl_reply_map feature to hide
+"password" information in DNSBL domain names.
+.PP
 When a client's score is equal to or greater than the threshold
 specified with postscreen_dnsbl_threshold, \fBpostscreen\fR(8) can drop
 the connection with the SMTP client.
@@ -3856,20 +3947,38 @@ its combined DNSBL score as defined with the postscreen_dnsbl_sites
 parameter.
 .PP
 This feature is available in Postfix 2.8.
-.SH postscreen_greet_action (default: continue)
+.SH postscreen_dnsbl_ttl (default: 1d)
+The amount of time that \fBpostscreen\fR(8) will cache results from
+a successful DNS blocklist test. During this time, the client IP address
+is excluded from this test. The default is relatively short, because a
+good client can immediately talk to a real Postfix SMTP server.
+.PP
+Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_forbidden_commands (default: $smtpd_forbidden_commands)
+List of commands that \fBpostscreen\fR(8) server considers in violation
+of the SMTP protocol. See also: postscreen_non_smtp_command_action.
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_greet_action (default: ignore)
 The action that \fBpostscreen\fR(8) takes when an SMTP client speaks
 before its turn within the time specified with the postscreen_greet_wait
 parameter.  Specify one of the following:
-.IP "continue"
-Continue waiting until the postscreen_greet_wait time has
-elapsed. If the client's combined DNSBL score is equal to or greater
-than a threshold (as specified with the postscreen_dnsbl_sites and
-postscreen_dnsbl_threshold parameters), execute the action specified
-with the postscreen_dnsbl_action parameter, otherwise forward the
-connection to a real SMTP server process.
-.IP "drop"
-Drop the connection immediately with a 521 SMTP reply, without
-examining DNSBL lookup results.
+.IP "\fBignore\fR (default)"
+Ignore the failure of this test. Allow other tests to complete.
+Repeat this test the next time the client connects.
+This option is useful for testing and collecting statistics
+without blocking mail.
+.IP "\fBenforce\fR"
+Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects.
+.IP "\fBdrop\fR"
+Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects.
 .PP
 In either case, \fBpostscreen\fR(8) will not whitelist the SMTP client
 IP address.
@@ -3884,31 +3993,110 @@ that they speak before their turn (pre-greet).  Specify an empty
 value to disable this feature.
 .PP
 This feature is available in Postfix 2.8.
-.SH postscreen_greet_wait (default: 4s)
+.SH postscreen_greet_ttl (default: 1d)
+The amount of time that \fBpostscreen\fR(8) will cache results from
+a successful PREGREET test. During this time, the client IP address
+is excluded from this test. The default is relatively short, because
+a good client can immediately talk to a real Postfix SMTP server.
+.PP
+Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_greet_wait (default: ${stress?2}${stress:6}s)
 The amount of time that \fBpostscreen\fR(8) will wait for an SMTP
 client to send a command before its turn, and for DNS blocklist
-lookup results to arrive. This is done only when the SMTP client
-IP address is not permanently whitelisted, and when it has no cached
-decision.  Specify a non-zero time value (an integral value plus
-an optional one-letter suffix that specifies the time unit).
+lookup results to arrive (default: 2 seconds under stress, 6 seconds
+normally).  This is done only when the SMTP client IP address is
+not permanently whitelisted, and when it has no cached decision.
+Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).
 .PP
 Time units: s (seconds), m (minutes), h (hours), d (days), w
 (weeks).
 .PP
 This feature is available in Postfix 2.8.
-.SH postscreen_hangup_action (default: continue)
-The action that \fBpostscreen\fR(8) takes when an SMTP client disconnects
-without sending data, within the time specified with the
-postscreen_greet_wait parameter.  Specify one of the following:
-.IP "continue"
-Continue waiting until the postscreen_greet_wait time has
-elapsed, and report whether the client's combined DNSBL score is
-equal to or greater than a threshold (as defined with the
-postscreen_dnsbl_sites and postscreen_dnsbl_threshold parameters).
-Do not forward the broken connection to a real SMTP server process.
-.IP "drop"
-Drop the connection immediately, without reporting DNSBL lookup
-results.
+.SH postscreen_helo_required (default: $smtpd_helo_required)
+Require that a remote SMTP client sends HELO or EHLO before
+commencing a MAIL transaction.
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_non_smtp_command_action (default: drop)
+The action that \fBpostscreen\fR(8) takes when an SMTP client sends
+non-SMTP commands as specified with the postscreen_forbidden_commands
+parameter.  Specify one of the following:
+.IP "\fBignore\fR"
+Ignore the failure of this test. Allow other tests to complete.
+Do \fInot\fR repeat this test before some the result from some
+other test expires.
+This option is useful for testing and collecting statistics
+without blocking mail permanently.
+.IP "\fBenforce\fR"
+Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects.
+.IP "\fBdrop\fR"
+Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects. This action is the
+same as with the Postfix SMTP server's smtpd_forbidden_commands
+feature.
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_non_smtp_command_enable (default: no)
+Enable "non-SMTP command" tests in the \fBpostscreen\fR(8) server. These
+tests are expensive: a client must disconnect after it passes the
+test, before it can talk to a real Postfix SMTP server.
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_non_smtp_command_ttl (default: 30d)
+The amount of time that \fBpostscreen\fR(8) will cache results from
+a successful "non_smtp_command" SMTP protocol test. During this
+time, the client IP address is excluded from this test. The default
+is long because a client must disconnect after it passes the test,
+before it can talk to a real Postfix SMTP server.
+.PP
+Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_pipelining_action (default: enforce)
+The action that \fBpostscreen\fR(8) takes when an SMTP client sends
+multiple commands instead of sending one command and waiting for
+the server to respond.  Specify one of the following:
+.IP "\fBignore\fR"
+Ignore the failure of this test. Allow other tests to complete.
+Do \fInot\fR repeat this test before some the result from some
+other test expires.
+This option is useful for testing and collecting statistics
+without blocking mail permanently.
+.IP "\fBenforce\fR"
+Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects.
+.IP "\fBdrop\fR"
+Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects.
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_pipelining_enable (default: no)
+Enable "pipelining" SMTP protocol tests in the \fBpostscreen\fR(8)
+server. These tests are expensive: a good client must disconnect
+after it passes the test, before it can talk to a real Postfix SMTP
+server.
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_pipelining_ttl (default: 30d)
+The amount of time that \fBpostscreen\fR(8) will cache results from
+a successful "pipelining" SMTP protocol test. During this time, the
+client IP address is excluded from this test. The default is
+long because a good client must disconnect after it passes the test,
+before it can talk to a real Postfix SMTP server.
+.PP
+Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).
 .PP
 This feature is available in Postfix 2.8.
 .SH postscreen_post_queue_limit (default: $default_process_limit)
@@ -3924,6 +4112,20 @@ process. When this queue is full, all non-whitelisted clients will
 receive a 421 reponse.
 .PP
 This feature is available in Postfix 2.8.
+.SH postscreen_watchdog_timeout (default: 10s)
+How much time a \fBpostscreen\fR(8) process may take to respond to
+an SMTP client command or to perform a cache operation before it
+is terminated by a built-in watchdog timer.  This is a safety
+mechanism that prevents \fBpostscreen\fR(8) from becoming non-responsive
+due to a bug in Postfix itself or in system software.  To avoid
+false alarms and unnecessary cache corruption this limit cannot be
+set under 10s.
+.PP
+Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).
+.PP
+This feature is available in Postfix 2.8.
 .SH postscreen_whitelist_networks (default: $mynetworks)
 Network addresses that are permanently whitelisted, and that
 will not be subjected to \fBpostscreen\fR(8) checks. This parameter uses
@@ -7316,7 +7518,7 @@ This parameter is not subjected to $parameter expansion.
 .PP
 This feature is available in Postfix 2.0 and later.
 .SH smtpd_forbidden_commands (default: CONNECT, GET, POST)
-List of commands that causes the Postfix SMTP server to immediately
+List of commands that cause the Postfix SMTP server to immediately
 terminate the session with a 221 code. This can be used to disconnect
 clients that obviously attempt to abuse the system. In addition to the
 commands listed in this parameter, commands that follow the "Label:"
@@ -8087,7 +8289,7 @@ smtpd_sender_restrictions = reject_unknown_sender_domain,
 .fi
 .ad
 .ft R
-.SH smtpd_service (default: smtpd)
+.SH smtpd_service_name (default: smtpd)
 The internal service that \fBpostscreen\fR(8) forwards allowed
 connections to. In a future version there may be different
 classes of SMTP service.
index aa890119bc6ad1813c222652df6196bbeb75e904..7b09383dc46d5997451283cf8f773b59b97511e0 100644 (file)
@@ -14,212 +14,30 @@ Postfix SMTP triage server
 .fi
 The Postfix \fBpostscreen\fR(8) server performs triage on
 multiple inbound SMTP connections in parallel. While
-\fBpostscreen\fR(8) keeps zombies and other bogus clients
-away from Postfix SMTP server processes, more Postfix SMTP
-server processes remain available for legitimate clients.
-.SH "GENERAL OPERATION"
-.na
-.nf
-.ad
-.fi
-The triage process involves a number of tests, in the order
-as described below.  Some tests introduce a delay of a few
-seconds.  Once a client passes all tests, its IP address
-is temporarily excluded from the tests, typically for 24
-hours.  This minimizes the impact of the tests on legitimate
-mail clients.
+\fBpostscreen\fR(8) keeps spambots away from Postfix SMTP
+server processes, more Postfix SMTP server processes remain
+available for legitimate clients.
+
+\fBpostscreen\fR(8) maintains a temporary whitelist of
+positive decisions. Once an SMTP client is whitelisted, it
+is immediately forwarded to a real Postfix SMTP server
+process without further checking.
 
-After logging the result of its tests, \fBpostscreen\fR(8)
+By default, the program logs only statistics, and it does
+not run any tests against clients in mynetworks (primarily,
+to avoid problems with non-standard SMTP implementations
+in network appliances and test programs).  \fBpostscreen\fR(8)
 by default forwards all connections to a real SMTP server
 process. This mode is useful for non-destructive testing.
 
 In a typical production setting, \fBpostscreen\fR(8) is
-configured to disconnect clients that fail some tests.  A
-future implementation may pass the connection to a dummy
-SMTP protocol engine that logs sender and recipient information
-before hanging up.
-
-Note: \fBpostscreen\fR(8) is not an SMTP proxy; this is
-intentional. The purpose is to prioritize legitimate clients
-with as little overhead as possible.
-.SH 1. PERMANENT WHITELIST TEST
-.ad
-.fi
-The postscreen_whitelist_networks parameter (default:
-$mynetworks) specifies a permanent whitelist for SMTP client
-IP addresses.
-
-When the SMTP client address matches the permanent whitelist,
-this is logged as:
-.sp
-.nf
-\fBWHITELISTED \fIaddress\fR
-.fi
-.sp
-The action is not configurable: immediately forward the
-connection to a real SMTP server process.
-.SH 2. PERMANENT BLACKLIST TEST
-.ad
-.fi
-The postscreen_blacklist_networks parameter (default: empty)
-specifies a permanent blacklist for SMTP client IP addresses.
-The address syntax is as with mynetworks.
-
-When the SMTP client address matches the permanent blacklist,
-this is logged as:
-.sp
-.nf
-\fBBLACKLISTED \fIaddress\fR
-.fi
-.sp
-The postscreen_blacklist_action parameter specifies the
-action that is taken next:
-.IP "\fBcontinue\fR (default)"
-Continue with the SMTP GREETING PHASE TESTS below.
-.IP \fBdrop\fR
-Drop the connection immediately with a 521 SMTP reply.  In
-a future implementation, the connection may instead be
-passed to a dummy SMTP protocol engine that logs sender and
-recipient information.
-.SH 3. TEMPORARY WHITELIST TEST
-.ad
-.fi
-The \fBpostscreen\fR(8) daemon maintains a \fItemporary\fR
-whitelist for SMTP client IP addresses that have passed all
-the tests described below. The postscreen_cache_map parameter
-specifies the location of the temporary whitelist.  The
-temporary whitelist is not used for SMTP client addresses
-that appear on the \fIpermanent\fR blacklist or whitelist.
-
-When the SMTP client address appears on the temporary
-whitelist, this is logged as:
-.sp
-.nf
-\fBPASS OLD \fIaddress\fR
-.fi
-.sp
-The action is not configurable: immediately forward the
-connection to a real SMTP server process.  The client is
-excluded from further tests until its temporary whitelist
-entry expires, as controlled with the postscreen_cache_ttl
-parameter.  Expired entries are silently renewed if possible.
-.SH 4. SMTP GREETING PHASE TESTS
-.ad
-.fi
-The postscreen_greet_wait parameter specifies a time interval
-during which \fBpostscreen\fR(8) runs a number of tests in
-parallel.  These tests are described below, and are run
-before the client may see the real SMTP server's "220
-text..." server greeting.
-
-When the SMTP client passes all greeting-phase tests, this
-is logged as:
-.sp
-.nf
-\fBPASS NEW \fIaddress\fR
-.fi
-.sp
-The action is to forward the connection to a real SMTP
-server process and to create a temporary whitelist entry
-that excludes the client IP address from further tests until
-the temporary whitelist entry expires, as controlled with
-the postscreen_cache_ttl parameter.
-
-In a future implementation, the connection may first be passed to
-a dummy SMTP protocol engine that implements more protocol
-tests including greylisting, before the client is allowed
-to talk to a real SMTP server process.
-.SH 4A. PREGREET TEST
-.ad
-.fi
-The postscreen_greet_banner parameter specifies the \fItext\fR
-portion of a "220-\fItext\fR..." teaser banner (default:
-$smtpd_banner).
-The \fBpostscreen\fR(8) daemon sends this before the
-postscreen_greet_wait timer is started.  The purpose of the
-teaser banner is to confuse SPAM clients so that they speak
-before their turn. It has no effect on SMTP clients that
-correctly implement the protocol.
-
-To avoid problems with broken SMTP engines in network
-appliances, either exclude them from all tests with the
-postscreen_whitelist_networks feature or else specify an
-empty postscreen_greet_banner value to disable the "220-text..."
-teaser banner.
-
-When an SMTP client sends a command before the
-postscreen_greet_wait time has elapsed, this is logged as:
-.sp
-.nf
-\fBPREGREET \fIcount \fBafter \fItime \fBfrom \fIaddress text...\fR
-.fi
-.sp
-Translation: the client at \fIaddress\fR sent \fIcount\fR
-bytes before its turn to speak, and this happened \fItime\fR
-seconds after the postscreen_greet_wait timer was started.
-The \fItext\fR is what the client sent (truncated to 100
-bytes, and with non-printable characters replaced with "?").
-
-The postscreen_greet_action parameter specifies the action
-that is taken next:
-.IP "\fBcontinue\fR (default)"
-Wait until the postscreen_greet_wait time has elapsed, then
-report DNSBL lookup results if applicable. Either perform
-DNSBL-related actions or forward the connection to a real
-SMTP server process.
-.IP \fBdrop\fR
-Drop the connection immediately with a 521 SMTP reply.
-In a future implementation, the connection may instead be passed
-to a dummy SMTP protocol engine that logs sender and recipient
+configured to reject mail from clients that fail one or
+more tests, after logging the helo, sender and recipient
 information.
-.SH 4B. HANGUP TEST
-.ad
-.fi
-When the SMTP client hangs up without sending any data
-before the postscreen_greet_wait time has elapsed, this is
-logged as:
-.sp
-.nf
-\fBHANGUP after \fItime \fBfrom \fIaddress\fR
-.fi
-.sp
-The postscreen_hangup_action specifies the action
-that is taken next:
-.IP "\fBcontinue\fR (default)"
-Wait until the postscreen_greet_wait time has elapsed, then
-report DNSBL lookup results if applicable. Do not forward
-the broken connection to a real SMTP server process.
-.IP \fBdrop\fR
-Drop the connection immediately.
-.SH 4C. DNS BLOCKLIST TEST
-.ad
-.fi
-The postscreen_dnsbl_sites parameter (default: empty)
-specifies a list of DNS blocklist servers. These lookups
-are made in parallel.
 
-When the postscreen_greet_wait time has elapsed, and the
-combined DNSBL score is equal to or greater than the
-postscreen_dnsbl_threshold parameter value, this is logged
-as:
-.sp
-.nf
-\fBDNSBL rank \fIcount \fBfor \fIaddress\fR
-.fi
-.sp
-Translation: the SMTP client at \fIaddress\fR has a combined
-DNSBL score of \fIcount\fR.
-
-The postscreen_dnsbl_action parameter specifies the action
-that is taken when the combined DNSBL score is equal to or
-greater than the threshold:
-.IP "\fBcontinue\fR (default)"
-Forward the connection to a real SMTP server process.
-.IP \fBdrop\fR
-Drop the connection immediately with a 521 SMTP reply.
-In a future implementation, the connection may instead be passed
-to a dummy SMTP protocol engine that logs sender and recipient
-information.
+\fBpostscreen\fR(8) is not an SMTP proxy; this is intentional.
+The purpose is to keep spambots away from Postfix SMTP
+server processes, not to control traffic flows.
 .SH "SECURITY"
 .na
 .nf
@@ -237,6 +55,18 @@ RFC 2920 (SMTP Pipelining)
 .ad
 .fi
 Problems and transactions are logged to \fBsyslogd\fR(8).
+.SH BUGS
+.ad
+.fi
+When successful tests involve \fBpostscreen\fR(8)'s built-in
+SMTP protocol engine, \fBpostscreen\fR(8) adds the client
+to the temporary whitelist but it cannot not hand off the
+"live" connection from a good SMTP client to a Postfix SMTP
+server process.  Instead, \fBpostscreen\fR(8) defers attempts
+to deliver mail with a 4XX status, and waits for the client
+to disconnect.  The next time a good client connects, it
+will be allowed to talk to a Postfix SMTP server process
+to deliver mail.
 .SH "CONFIGURATION PARAMETERS"
 .na
 .nf
@@ -249,23 +79,41 @@ change.
 
 The text below provides only a parameter summary. See
 \fBpostconf\fR(5) for more details including examples.
+
+NOTE: Some parameters implement stress-dependent behavior.
+This is supported only when the default value is stress-dependent
+(${stress?X}${stress:Y}). Other parameters always evaluate
+as if the stress value is the empty string.
 .SH "TRIAGE PARAMETERS"
 .na
 .nf
 .ad
 .fi
-.IP "\fBpostscreen_blacklist_action (continue)\fR"
+.IP "\fBpostscreen_bare_newline_action (ignore)\fR"
+The action that \fBpostscreen\fR(8) takes when an SMTP client sends
+a bare newline character, that is, a newline not preceded by carriage
+return.
+.IP "\fBpostscreen_bare_newline_enable (no)\fR"
+Enable "bare newline" SMTP protocol tests in the \fBpostscreen\fR(8)
+server.
+.IP "\fBpostscreen_blacklist_action (ignore)\fR"
 The action that \fBpostscreen\fR(8) takes when an SMTP client is
 permanently blacklisted with the postscreen_blacklist_networks
 parameter.
 .IP "\fBpostscreen_blacklist_networks (empty)\fR"
 Network addresses that are permanently blacklisted; see the
 postscreen_blacklist_action parameter for possible actions.
-.IP "\fBpostscreen_dnsbl_action (continue)\fR"
+.IP "\fBpostscreen_disable_vrfy_command ($disable_vrfy_command)\fR"
+Disable the SMTP VRFY command in the \fBpostscreen\fR(8) daemon.
+.IP "\fBpostscreen_dnsbl_action (ignore)\fR"
 The action that \fBpostscreen\fR(8) takes when an SMTP client's combined
 DNSBL score is equal to or greater than a threshold (as defined
 with the postscreen_dnsbl_sites and postscreen_dnsbl_threshold
 parameters).
+.IP "\fBpostscreen_dnsbl_reply_map (empty)\fR"
+A mapping from actual DNSBL domain name which includes a secret
+password, to the DNSBL domain name that postscreen will reply with
+when it rejects mail.
 .IP "\fBpostscreen_dnsbl_sites (empty)\fR"
 Optional list of DNS blocklist domains, filters and weight
 factors.
@@ -273,7 +121,10 @@ factors.
 The inclusive lower bound for blocking an SMTP client, based on
 its combined DNSBL score as defined with the postscreen_dnsbl_sites
 parameter.
-.IP "\fBpostscreen_greet_action (continue)\fR"
+.IP "\fBpostscreen_forbidden_commands ($smtpd_forbidden_commands)\fR"
+List of commands that \fBpostscreen\fR(8) server considers in violation
+of the SMTP protocol.
+.IP "\fBpostscreen_greet_action (ignore)\fR"
 The action that \fBpostscreen\fR(8) takes when an SMTP client speaks
 before its turn within the time specified with the postscreen_greet_wait
 parameter.
@@ -283,25 +134,31 @@ response that
 \fBpostscreen\fR(8) sends ahead of the real Postfix SMTP server's "220
 text..." response, in an attempt to confuse bad SMTP clients so
 that they speak before their turn (pre-greet).
-.IP "\fBpostscreen_greet_wait (4s)\fR"
+.IP "\fBpostscreen_greet_wait (${stress?2}${stress:6}s)\fR"
 The amount of time that \fBpostscreen\fR(8) will wait for an SMTP
 client to send a command before its turn, and for DNS blocklist
-lookup results to arrive.
-.IP "\fBpostscreen_hangup_action (continue)\fR"
-The action that \fBpostscreen\fR(8) takes when an SMTP client disconnects
-without sending data, within the time specified with the
-postscreen_greet_wait parameter.
-.IP "\fBpostscreen_post_queue_limit ($default_process_limit)\fR"
-The number of clients that can be waiting for service from a
-real SMTP server process.
-.IP "\fBpostscreen_pre_queue_limit ($default_process_limit)\fR"
-The number of non-whitelisted clients that can be waiting for
-a decision whether they will receive service from a real SMTP server
-process.
+lookup results to arrive (default: 2 seconds under stress, 6 seconds
+normally).
+.IP "\fBpostscreen_helo_required ($smtpd_helo_required)\fR"
+Require that a remote SMTP client sends HELO or EHLO before
+commencing a MAIL transaction.
+.IP "\fBpostscreen_non_smtp_command_action (drop)\fR"
+The action that \fBpostscreen\fR(8) takes when an SMTP client sends
+non-SMTP commands as specified with the postscreen_forbidden_commands
+parameter.
+.IP "\fBpostscreen_non_smtp_command_enable (no)\fR"
+Enable "non-SMTP command" tests in the \fBpostscreen\fR(8) server.
+.IP "\fBpostscreen_pipelining_action (enforce)\fR"
+The action that \fBpostscreen\fR(8) takes when an SMTP client sends
+multiple commands instead of sending one command and waiting for
+the server to respond.
+.IP "\fBpostscreen_pipelining_enable (no)\fR"
+Enable "pipelining" SMTP protocol tests in the \fBpostscreen\fR(8)
+server.
 .IP "\fBpostscreen_whitelist_networks ($mynetworks)\fR"
 Network addresses that are permanently whitelisted, and that
 will not be subjected to \fBpostscreen\fR(8) checks.
-.IP "\fBsmtpd_service (smtpd)\fR"
+.IP "\fBsmtpd_service_name (smtpd)\fR"
 The internal service that \fBpostscreen\fR(8) forwards allowed
 connections to.
 .SH "CACHE CONTROLS"
@@ -313,12 +170,49 @@ connections to.
 The amount of time between \fBpostscreen\fR(8) cache cleanup runs.
 .IP "\fBpostscreen_cache_map (btree:$data_directory/ps_cache)\fR"
 Persistent storage for the \fBpostscreen\fR(8) server decisions.
-.IP "\fBpostscreen_cache_retention_time (1d)\fR"
+.IP "\fBpostscreen_cache_retention_time (7d)\fR"
 The amount of time that \fBpostscreen\fR(8) will cache an expired
 temporary whitelist entry before it is removed.
-.IP "\fBpostscreen_cache_ttl (1d)\fR"
-The amount of time that \fBpostscreen\fR(8) will cache a decision for
-a specific SMTP client IP address.
+.IP "\fBpostscreen_bare_newline_ttl (30d)\fR"
+The amount of time that \fBpostscreen\fR(8) will cache results from
+a successful "bare newline" SMTP protocol test.
+.IP "\fBpostscreen_dnsbl_ttl (1d)\fR"
+The amount of time that \fBpostscreen\fR(8) will cache results from
+a successful DNS blocklist test.
+.IP "\fBpostscreen_greet_ttl (1d)\fR"
+The amount of time that \fBpostscreen\fR(8) will cache results from
+a successful PREGREET test.
+.IP "\fBpostscreen_non_smtp_command_ttl (30d)\fR"
+The amount of time that \fBpostscreen\fR(8) will cache results from
+a successful "non_smtp_command" SMTP protocol test.
+.IP "\fBpostscreen_pipelining_ttl (30d)\fR"
+The amount of time that \fBpostscreen\fR(8) will cache results from
+a successful "pipelining" SMTP protocol test.
+.SH "RESOURCE CONTROLS"
+.na
+.nf
+.ad
+.fi
+.IP "\fBline_length_limit (2048)\fR"
+Upon input, long lines are chopped up into pieces of at most
+this length; upon delivery, long lines are reconstructed.
+.IP "\fBpostscreen_command_count_limit (20)\fR"
+The limit on the total number of commands per SMTP session for
+\fBpostscreen\fR(8)'s built-in SMTP protocol engine.
+.IP "\fBpostscreen_command_time_limit (${stress?10}${stress:300}s)\fR"
+The command "read" time limit for \fBpostscreen\fR(8)'s built-in SMTP
+protocol engine.
+.IP "\fBpostscreen_post_queue_limit ($default_process_limit)\fR"
+The number of clients that can be waiting for service from a
+real SMTP server process.
+.IP "\fBpostscreen_pre_queue_limit ($default_process_limit)\fR"
+The number of non-whitelisted clients that can be waiting for
+a decision whether they will receive service from a real SMTP server
+process.
+.IP "\fBpostscreen_watchdog_timeout (10s)\fR"
+How much time a \fBpostscreen\fR(8) process may take to respond to
+an SMTP client command or to perform a cache operation before it
+is terminated by a built-in watchdog timer.
 .SH "MISCELLANEOUS CONTROLS"
 .na
 .nf
@@ -327,9 +221,6 @@ a specific SMTP client IP address.
 .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
 The default location of the Postfix main.cf and master.cf
 configuration files.
-.IP "\fBdaemon_timeout (18000s)\fR"
-How much time a Postfix daemon process may take to handle a
-request before it is terminated by a built-in watchdog timer.
 .IP "\fBdelay_logging_resolution_limit (2)\fR"
 The maximal number of digits after the decimal point when logging
 sub-second delay values.
@@ -356,6 +247,16 @@ records, so that "smtpd" becomes, for example, "postfix/smtpd".
 smtpd(8), Postfix SMTP server
 dnsblog(8), temporary DNS helper
 syslogd(8), system logging
+.SH "README FILES"
+.na
+.nf
+.ad
+.fi
+Use "\fBpostconf readme_directory\fR" or "\fBpostconf
+html_directory\fR" to locate this information.
+.nf
+.na
+POSTSCREEN_README, Postfix Postscreen Howto
 .SH "LICENSE"
 .na
 .nf
index 71eceea23c1638b130217744425b63f45bf6da8e..378605914318d8c0e9ef531892440dce3fc6942e 100644 (file)
@@ -962,7 +962,7 @@ records, so that "smtpd" becomes, for example, "postfix/smtpd".
 .PP
 Available in Postfix version 2.2 and later:
 .IP "\fBsmtpd_forbidden_commands (CONNECT, GET, POST)\fR"
-List of commands that causes the Postfix SMTP server to immediately
+List of commands that cause the Postfix SMTP server to immediately
 terminate the session with a 221 code.
 .PP
 Available in Postfix version 2.5 and later:
index e8e29fab169141b784266d01310b802001dca9bd..90e589407f3311d4e66af649f44f0a7ccdfe8e24 100755 (executable)
@@ -501,7 +501,7 @@ while (<>) {
     s;\bsmtpd_error_sleep_time\b;<a href="postconf.5.html#smtpd_error_sleep_time">$&</a>;g;
     s;\bsmtpd_etrn_restrictions\b;<a href="postconf.5.html#smtpd_etrn_restrictions">$&</a>;g;
     s;\bsmtpd_expansion_filter\b;<a href="postconf.5.html#smtpd_expansion_filter">$&</a>;g;
-    s;\bsmtpd_forbidden_commands\b;<a href="postconf.5.html#smtpd_forbidden_commands">$&</a>;g;
+    s;\bsmtpd_for[-</bB>]*\n*[ <bB>]*bidden_commands\b;<a href="postconf.5.html#smtpd_forbidden_commands">$&</a>;g;
     s;\bsmtpd_hard_error_limit\b;<a href="postconf.5.html#smtpd_hard_error_limit">$&</a>;g;
     s;\bsmtpd_helo_required\b;<a href="postconf.5.html#smtpd_helo_required">$&</a>;g;
     s;\bsmtpd_helo_restrictions\b;<a href="postconf.5.html#smtpd_helo_restrictions">$&</a>;g;
@@ -906,19 +906,35 @@ while (<>) {
 
     # postscreen
     s;\bpostscreen_cache_map\b;<a href="postconf.5.html#postscreen_cache_map">$&</a>;g;
-    s;\bpostscreen_cache_ttl\b;<a href="postconf.5.html#postscreen_cache_ttl">$&</a>;g;
     s;\bpostscreen_cache_cleanup_interval\b;<a href="postconf.5.html#postscreen_cache_cleanup_interval">$&</a>;g;
     s;\bpostscreen_cache_retention_time\b;<a href="postconf.5.html#postscreen_cache_retention_time">$&</a>;g;
-    s;\bsmtpd_service\b;<a href="postconf.5.html#smtpd_service">$&</a>;g;
+    s;\bpostscreen_command_count_limit\b;<a href="postconf.5.html#postscreen_command_count_limit">$&</a>;g;
+    s;\bpostscreen_com[-</bB>]*\n* *[<bB>]*mand_time_limit\b;<a href="postconf.5.html#postscreen_command_time_limit">$&</a>;g;
+    s;\bsmtpd_service_name\b;<a href="postconf.5.html#smtpd_service_name">$&</a>;g;
+    s;\bpostscreen_bare_newline_enable\b;<a href="postconf.5.html#postscreen_bare_newline_enable">$&</a>;g;
+    s;\bpostscreen_bare_newline_action\b;<a href="postconf.5.html#postscreen_bare_newline_action">$&</a>;g;
+    s;\bpostscreen_bare_newline_ttl\b;<a href="postconf.5.html#postscreen_bare_newline_ttl">$&</a>;g;
     s;\bpostscreen_post_queue_limit\b;<a href="postconf.5.html#postscreen_post_queue_limit">$&</a>;g;
     s;\bpostscreen_pre_queue_limit\b;<a href="postconf.5.html#postscreen_pre_queue_limit">$&</a>;g;
-    s;\bpostscreen_greet_banner\b;<a href="postconf.5.html#postscreen_greet_banner">$&</a>;g;
     s;\bpostscreen_greet_wait\b;<a href="postconf.5.html#postscreen_greet_wait">$&</a>;g;
+    s;\bpostscreen_greet_banner\b;<a href="postconf.5.html#postscreen_greet_banner">$&</a>;g;
     s;\bpostscreen_greet_action\b;<a href="postconf.5.html#postscreen_greet_action">$&</a>;g;
+    s;\bpostscreen_greet_ttl\b;<a href="postconf.5.html#postscreen_greet_ttl">$&</a>;g;
+    s;\bpostscreen_disable_vrfy_command\b;<a href="postconf.5.html#postscreen_disable_vrfy_command">$&</a>;g;
+    s;\bpostscreen_dnsbl_reply_map\b;<a href="postconf.5.html#postscreen_dnsbl_reply_map">$&</a>;g;
     s;\bpostscreen_dnsbl_sites\b;<a href="postconf.5.html#postscreen_dnsbl_sites">$&</a>;g;
-    s;\bpostscreen_dnsbl_threshold\b;<a href="postconf.5.html#postscreen_dnsbl_threshold">$&</a>;g;
+    s;\bpostscreen_dnsbl_thresh[-</bB>]*\n* *[<bB>]*old\b;<a href="postconf.5.html#postscreen_dnsbl_threshold">$&</a>;g;
     s;\bpostscreen_dnsbl_action\b;<a href="postconf.5.html#postscreen_dnsbl_action">$&</a>;g;
-    s;\bpostscreen_hangup_action\b;<a href="postconf.5.html#postscreen_hangup_action">$&</a>;g;
+    s;\bpostscreen_dnsbl_ttl\b;<a href="postconf.5.html#postscreen_dnsbl_ttl">$&</a>;g;
+    s;\bpostscreen_for[-</bB>]*\n*[ <bB>]*bidden_commands\b;<a href="postconf.5.html#postscreen_forbidden_commands">$&</a>;g;
+    s;\bpostscreen_helo_required\b;<a href="postconf.5.html#postscreen_helo_required">$&</a>;g;
+    s;\bpostscreen_non_smtp_command_enable\b;<a href="postconf.5.html#postscreen_non_smtp_command_enable">$&</a>;g;
+    s;\bpostscreen_non_smtp_command_action\b;<a href="postconf.5.html#postscreen_non_smtp_command_action">$&</a>;g;
+    s;\bpostscreen_non_smtp_command_ttl\b;<a href="postconf.5.html#postscreen_non_smtp_command_ttl">$&</a>;g;
+    s;\bpostscreen_pipelining_enable\b;<a href="postconf.5.html#postscreen_pipelining_enable">$&</a>;g;
+    s;\bpostscreen_pipelining_action\b;<a href="postconf.5.html#postscreen_pipelining_action">$&</a>;g;
+    s;\bpostscreen_pipelining_ttl\b;<a href="postconf.5.html#postscreen_pipelining_ttl">$&</a>;g;
+    s;\bpostscreen_watchdog_timeout\b;<a href="postconf.5.html#postscreen_watchdog_timeout">$&</a>;g;
     s;\bpostscreen_whitelist_networks\b;<a href="postconf.5.html#postscreen_whitelist_networks">$&</a>;g;
     s;\bpostscreen_black[-</bB>]*\n*[ <bB>]*list_networks\b;<a href="postconf.5.html#postscreen_blacklist_networks">$&</a>;g;
     s;\bpostscreen_black[-</bB>]*\n*[ <bB>]*list_action\b;<a href="postconf.5.html#postscreen_blacklist_action">$&</a>;g;
index 965f56ff5a8cd21dac6bbee41dba7f9eee5ebd21..63e30ff2aae09cf3f0daffbbc991725a1d6f8dcb 100644 (file)
@@ -379,6 +379,14 @@ and "port" specifies a symbolic service name or a numeric port
 number.
 </dd>
 
+<dt> <b>texthash</b> (read-only) </dt>
+
+<dd> This produces similar results as hash: files, except that you
+don't have to run the postmap(1) command before you can use the
+file, and that it does not detect changes after the file is read.
+The lookup table name is "texthash:filename", where the file name
+is taken literally; no suffix is appended. </dd>
+
 <dt> <b>unix</b> (read-only) </dt>
 
 <dd> A limited way to query the UNIX authentication database. The
index 5d41ede949257a0e79e7d208873086383bc730ef..39033c17d3f79d075879d95b68fb1395f0fa0a99 100644 (file)
@@ -29,6 +29,7 @@ HTML  = ../html/ADDRESS_CLASS_README.html \
        ../html/OVERVIEW.html \
        ../html/PACKAGE_README.html ../html/PCRE_README.html \
        ../html/PGSQL_README.html \
+       ../html/POSTSCREEN_README.html \
        ../html/QSHAPE_README.html \
        ../html/RESTRICTION_CLASS_README.html ../html/SASL_README.html \
        ../html/SCHEDULER_README.html ../html/SMTPD_ACCESS_README.html \
@@ -68,6 +69,7 @@ README        = ../README_FILES/ADDRESS_CLASS_README \
        ../README_FILES/OVERVIEW \
        ../README_FILES/PACKAGE_README ../README_FILES/PCRE_README \
        ../README_FILES/PGSQL_README \
+       ../README_FILES/POSTSCREEN_README \
        ../README_FILES/QSHAPE_README \
        ../README_FILES/RESTRICTION_CLASS_README \
        ../README_FILES/SASL_README ../README_FILES/SCHEDULER_README \
@@ -221,6 +223,9 @@ clobber:
 ../html/PGSQL_README.html: PGSQL_README.html
        $(POSTLINK) $? >$@
 
+../html/POSTSCREEN_README.html: POSTSCREEN_README.html
+       $(POSTLINK) $? >$@
+
 ../html/QMQP_README.html: QMQP_README.html
        $(POSTLINK) $? >$@
 
@@ -374,6 +379,9 @@ clobber:
 ../README_FILES/PGSQL_README: PGSQL_README.html
        $(HT2READ) $? >$@
 
+../README_FILES/POSTSCREEN_README: POSTSCREEN_README.html
+       $(HT2READ) $? >$@
+
 ../README_FILES/QMQP_README: QMQP_README.html
        $(HT2READ) $? >$@
 
diff --git a/postfix/proto/POSTSCREEN_README.html b/postfix/proto/POSTSCREEN_README.html
new file mode 100644 (file)
index 0000000..8d34238
--- /dev/null
@@ -0,0 +1,709 @@
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<head>
+
+<title>Postfix Postscreen Howto</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 Postscreen Howto</h1>
+
+<hr>
+
+<h2> <a name="intro">Introduction</a> </h2>
+
+<p> The Postfix postscreen(8) server performs triage on multiple
+inbound SMTP connections in parallel. While one postscreen(8) process
+keeps spambots away from Postfix SMTP server processes, more Postfix
+SMTP server processes remain available for legitimate clients. </p>
+
+<p> By doing these checks in a single postscreen(8) process, Postfix
+can avoid wasting one SMTP server process per connection. A side
+benefit of postscreen(8)'s DNSBL lookups is that DNS records are
+already cached before the Postfix SMTP server looks them up later.
+</p>
+
+<p> postscreen(8) maintains a temporary whitelist of positive
+decisions.  Once an SMTP client is whitelisted, it is immediately
+forwarded to a real Postfix SMTP server process without further
+checking. </p>
+
+<p> By default, the program logs only statistics, and it does not
+run any checks on clients in mynetworks (primarily, to avoid problems
+with buggy SMTP implementations in network appliances). </p>
+
+<p> Many of the ideas in postscreen(8) have been explored in earlier
+work by Michael Tokarev, in OpenBSD spamd, and in MailChannels
+Traffic Control.  </p>
+
+<p> Topics in this document: </p>
+
+<ul>
+
+<li> <a href="#intro">Introduction</a>
+
+<li> <a href="#basic">The basic idea behind postscreen(8)</a>
+
+<li> <a href="#general"> General operation </a>
+
+<li> <a href="#quick">Quick tests before everything else</a>
+
+<li> <a href="#before_220"> Tests before the 220 SMTP server greeting </a>
+
+<li> <a href="#after_220">Tests after the 220 SMTP server greeting</a>
+
+<li> <a href="#other_error">Other errors</a>
+
+<li> <a href="#victory">When all tests succeed</a>
+
+<li> <a href="#config"> Configuring the postscreen(8) service</a>
+
+</ul>
+
+<h2> <a name="basic">The basic idea behind postscreen(8)</a> </h2>
+
+<p> Spambots have a limited amount of time to send out spam before
+they become blacklisted. For this reason, spambots make compromises
+in their SMTP protocol implementation to speed up spam deliveries.
+For example, they speak before their turn. </p>
+
+<p> Many spambots avoid spamming the same site repeatedly.  Thus,
+postscreen(8) must make a long-term decision after a single
+measurement. For example, allow a good client to skip the DNSBL
+test for 24 hours. </p>
+
+<p> To recognize spambots, postscreen(8) measures properties of the
+client IP address and of the client SMTP protocol implementation
+(the protocol compromises that were made to speed up delivery).
+These properties don't change with delivery attempts, and are
+therefore suitable for making a long-term decision after a single
+measurement.  </p>
+
+<p> postscreen(8) does not inspect message content. The reason is
+that content can change with each delivery attempt, especially with
+legitimate clients. Message content is not good for making a long-term
+decision after a single measurement, and that is the problem that
+postscreen(8) is focused on. </p>
+
+<h2> <a name="general"> General operation </a> </h2>
+
+<p> The postscreen(8) triage process involves a number of tests,
+in the order as described below.  Some tests introduce a delay of
+a few seconds.  Once a client passes all tests, its IP address is
+temporarily excluded from any tests, typically 24 hours for simple
+tests or 1 week for complex tests.  This minimizes the impact of
+the tests on legitimate mail clients. </p>
+
+<p> After logging the result of its tests, postscreen(8) by default
+forwards all connections to a real SMTP server process. This mode
+is useful for non-destructive testing. </p>
+
+<p> In a typical production setting, postscreen(8) is configured
+to reject mail from clients that fail one or more tests, after
+logging the sender and recipient information. </p>
+
+<p> Note: postscreen(8) is not an SMTP proxy; this is intentional.
+The purpose is to prioritize legitimate clients with as little
+overhead as possible. </p>
+
+<h2> <a name="quick">Quick tests before everything else</a> </h2>
+
+<p> Before engaging in SMTP-level tests. postscreen(8) queries a
+number of local black and whitelists. These tests speed up the
+handling of known clients. </p>
+
+<ul>
+
+<li> <a href="#perm_white"> Permanent whitelist test </a>
+
+<li> <a href="#perm_black"> Permanent blacklist test </a>
+
+<li> <a href="#temp_white"> Temporary whitelist test </a>
+
+</ul>
+
+<h3> <a name="perm_white"> Permanent whitelist test </a> </h3>
+
+<p> The postscreen_whitelist_networks parameter (default: $mynetworks)
+specifies a permanent whitelist for SMTP client IP addresses.  When
+the SMTP client address matches the permanent whitelist, this is
+logged as: </p>
+
+<pre>
+    <b>WHITELISTED</b> <i>address</i>
+</pre>
+
+<p> The action is not configurable: immediately forward the
+connection to a real SMTP server process. </p>
+
+<h3> <a name="perm_black"> Permanent blacklist test </a>  </h3>
+
+<p> The postscreen_blacklist_networks parameter (default: empty)
+specifies a permanent blacklist for SMTP client IP addresses.  The
+address syntax is as with mynetworks.  When the SMTP client address
+matches the permanent blacklist, postscreen(8) logs this as: </p>
+
+<pre>
+    <b>BLACKLISTED</b> <i>address</i>
+</pre>
+
+<p> The postscreen_blacklist_action parameter specifies the action
+that is taken next.  See "<a href="#fail_before_220">When tests
+fail before the 220 SMTP server greeting</a>" below. </p>
+
+<h3> <a name="temp_white"> Temporary whitelist test </a> </h3>
+
+<p> The postscreen(8) daemon maintains a <i>temporary</i>
+whitelist for SMTP client IP addresses that have passed all
+the tests described below. The postscreen_cache_map parameter
+specifies the location of the temporary whitelist.  The
+temporary whitelist is not used for SMTP client addresses
+that appear on the <i>permanent</i> blacklist or whitelist. </p>
+
+<p> When the SMTP client address appears on the temporary
+whitelist, postscreen(8) logs this as: </p>
+
+<pre>
+    <b>PASS OLD</b> <i>address</i>
+</pre>
+
+<p> The action is not configurable: immediately forward the
+connection to a real SMTP server process.  The client is
+excluded from further tests until its temporary whitelist
+entry expires, as controlled with the postscreen_*_ttl
+parameters.  Expired entries are silently renewed if possible. </p>
+
+<h2> <a name="before_220"> Tests before the 220 SMTP server greeting </a> </h2>
+
+<p> The postscreen_greet_wait parameter specifies a short time
+interval before the "220 <i>text</i>..." server greeting, where
+postscreen(8) can run a number of tests in parallel. </p>
+
+<p> When a good client passes these tests, and no "<a
+href="#after_220">deep protocol tests</a>" are configured, postscreen(8)
+adds the client to the temporary whitelist and passes the "live"
+connection to a Postfix SMTP server process.  The client can then
+continue as if postscreen(8) never even existed (except of course
+for the short postscreen_greet_wait delay).  </p>
+
+<ul>
+
+<li> <a href="#pregreet"> Pregreet test </a>
+
+<li> <a href="#dnsbl"> DNS Blocklist test </a>
+
+<li> <a href="#fail_before_220">When tests fail before the 220 SMTP server greeting</a>
+
+</ul>
+
+<h3> <a name="pregreet"> Pregreet test </a> </h3>
+
+<p> The SMTP protocol is a classic example of a protocol where the
+server speaks before the client. postscreen(8) detects spambots
+that are in a hurry and that speak before their turn. This test is
+enabled by default. </p>
+
+<p> The postscreen_greet_banner parameter specifies the <i>text</i>
+portion of a "220-<i>text</i>..." teaser banner (default: $smtpd_banner).
+Note that this becomes the first part of a multi-line server greeting.
+The postscreen(8) daemon sends this before the postscreen_greet_wait
+timer is started.  The purpose of the teaser banner is to confuse
+spambots so that they speak before their turn. It has no effect on
+SMTP clients that correctly implement the protocol.  </p>
+
+<p> To avoid problems with poorly-implemented SMTP engines in network
+appliances or network testing tools, either exclude them from all
+tests with the postscreen_whitelist_networks feature or else specify
+an empty teaser banner with: </p>
+
+<pre>
+/etc/postfix/main.cf:
+    postscreen_greet_banner =
+</pre>
+
+<p> When an SMTP client sends a command before the
+postscreen_greet_wait time has elapsed, postscreen(8) logs this as:
+</p>
+
+<pre>
+    <b>PREGREET</b> <i>count</i> <b>after</b> <i>time</i> <b>from</b> <i>address text...</i>
+</pre>
+
+<p> Translation: the client at <i>address</i> sent <i>count</i>
+bytes before its turn to speak. This happened <i>time</i> seconds
+after the postscreen_greet_wait timer was started.  The <i>text</i>
+is what the client sent (truncated to 100 bytes, and with non-printable
+characters replaced with "?"). </p>
+
+<p> The postscreen_greet_action parameter specifies the action that
+is taken next.  See "<a href="#fail_before_220">When tests fail
+before the 220 SMTP server greeting</a>" below. </p>
+
+<h3> <a name="dnsbl"> DNS Blocklist test </a> </h3>
+
+<p> The postscreen_dnsbl_sites parameter (default: empty) specifies
+a list of DNS blocklist servers with optional filters and weight
+factors.  These servers will be queried in parallel with the reverse
+client IP address. This test is disabled by default. </p>
+
+<blockquote>
+<p>
+CAUTION: when postscreen rejects mail, it replies with the DNSBL
+domain name. Use the postscreen_dnsbl_reply_map feature to hide
+"password" information in DNSBL domain names.
+</p>
+</blockquote>
+
+<p> When the postscreen_greet_wait time has elapsed, and the combined
+DNSBL score is equal to or greater than the postscreen_dnsbl_threshold
+parameter value, postscreen(8) logs this as: </p>
+
+<b>DNSBL rank</b> <i>count</i> <b>for</b> <i>address</i>
+
+<p> Translation: the SMTP client at <i>address</i> has a combined
+DNSBL score of <i>count</i>. </p>
+
+<p> The postscreen_dnsbl_action parameter specifies the action that
+is taken when the combined DNSBL score is equal to or greater than
+the threshold.  See "<a href="#fail_before_220">When tests fail
+before the 220 SMTP server greeting</a>" below. </p>
+
+<h3> <a name="fail_before_220">When tests fail before the 220 SMTP server greeting</a> </h3>
+
+<p> When the client address matches the permanent blacklist, or
+when the client fails the pregreet or DNSBL tests, the action is
+specified with postscreen_blacklist_action, postscreen_greet_action,
+or postscreen_dnsbl_action, respectively. </p>
+
+<dl>
+
+<dt> <b>ignore</b> (default) </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Repeat this test the next time the client connects.  This option
+is useful for testing and collecting statistics without interfering
+with mail deliveries. </dd>
+
+<dt> <b>enforce</b> </dt>
+
+<dd> Allow other tests to complete.  Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
+
+<dt> <b>drop</b> </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply.  Repeat
+this test the next time the client connects. </dd>
+
+</dl>
+
+<h2> <a name="after_220">Tests after the 220 SMTP server greeting</a> </h2>
+
+<p> The tests in this phase use an SMTP protocol engine that is
+built into the postscreen(8) server. </p>
+
+<p> Important notes: </p>
+
+<ul>
+
+<li> <p> These tests are disabled by default, because they
+are more intrusive than the pregreet and DNSBL tests. </p>
+
+<p> When a good client passes the <a href="#after_220">deep
+protocol tests </a>, postscreen(8) adds the client to the temporary
+whitelist but it cannot pass the "live" connection to a Postfix
+SMTP server process in the middle of the session. Instead, postscreen(8)
+defers mail delivery attempts with a 4XX status, logs the
+helo/sender/recipient information, and waits for the client to
+disconnect. </p>
+
+<p> The next time the client connects it will be allowed to talk
+to a real SMTP server process to deliver its mail. </p>
+
+<p> To minimize the impact of these tests, postscreen(8) gives them
+relatively long expiration times. </p>
+
+<li> <p> postscreen(8) does not implement the AUTH, STARTTLS,
+XCLIENT, and XFORWARD features.  STARTTLS support may be added in
+a future version. </p>
+
+</ul>
+
+<p> End-user clients should connect directly to the submission
+service. Other systems that require the above features
+should directly connect to a Postfix SMTP server, or they
+should be placed on the postscreen(8) whitelist. </p>
+
+<ul>
+
+<li> <a href="#pipelining">Command pipelining test</a>
+
+<li> <a href="#non_smtp">Non-SMTP command test</a>
+
+<li> <a href="#barelf">Bare newline test</a>
+
+<li> <a href="#fail_after_220">When tests fail after the 220 SMTP server greeting</a>
+
+</ul>
+
+<h3> <a name="pipelining">Command pipelining test</a> </h3>
+
+<p> By default, SMTP is a half-duplex protocol: the sender and
+receiver send one command and one response at a time.  Unlike the
+real Postfix SMTP server, postscreen(8) does not announce support
+for ESMTP command pipelining.  Therefore, clients are not allowed
+to send multiple commands. This test is disabled by default. </p>
+
+<p> With "postscreen_pipelining_enable = yes", postscreen(8) detects
+spambots that send multiple commands, instead of sending one command
+and waiting for the server to reply. </p>
+
+<p> This test is opportunistically enabled when enabled when
+postscreen(8) has to use the built-in SMTP engine anyway, to make
+postscreen(8) logging more informative. </p>
+
+<p> When a client sends multiple commands, postscreen(8) logs this
+as: </p>
+
+<b>COMMAND PIPELINING after</b> <i>time</i> <b>from</b> <i>address</i>
+
+<p> Translation: the SMTP client at <i>address</i> sent multiple
+SMTP commands, instead of sending one command and then waiting for
+the server to reply. This happened <i>time</i> seconds after the
+"220 " server greeting was sent. </p>
+
+<p> The postscreen_pipelining_action parameter specifies the action
+that is taken next.  See "<a href="#fail_after_220">When tests fail
+after the 220 SMTP server greeting</a>" below. </p>
+
+<h3> <a name="non_smtp">Non-SMTP command test</a> </h3>
+
+<p> With "postscreen_non_smtp_command_enable = yes", postscreen(8)
+detects spambots that send non-SMTP commands, such as commands
+specified with the postscreen_forbidden_commands parameter, and
+commands that have the syntax of a message header label.  </p>
+
+<p> This test is disabled by default. The test is opportunistically
+enabled when postscreen(8) has to use the built-in SMTP engine
+anyway, to make postscreen(8) logging more informative.  </p>
+
+<p> When a client sends non-SMTP commands, postscreen(8) logs this
+as: </p>
+
+<pre>
+    <b>NON-SMTP COMMAND from</b> <i>address command</i>
+</pre>
+
+<p> Translation: the SMTP client at <i>address</i> sent a
+<i>command</i> that matches the postscreen_forbidden_commands
+parameter, or that has the syntax of a message header label. </p>
+
+<p> The postscreen_non_smtp_command_action parameter specifies
+the action that is taken next.  See "<a href="#fail_after_220">When
+tests fail after the 220 SMTP server greeting</a>" below. </p>
+
+<h3> <a name="barelf">Bare newline test</a> </h3>
+
+<p> SMTP is a line-oriented protocol: lines have a limited
+length, and are terminated with &lt;CR&gt;&lt;LF&gt;. </p>
+
+<p> With "postscreen_bare_newline_enable = yes", postscreen(8)
+detects spambots that send lines ending in bare newline
+characters, that is newline not preceded by carriage return. </p>
+
+<p> This test is disabled by default. The test is opportunistically
+enabled when postscreen(8) has to use the built-in SMTP engine
+anyway, to make postscreen(8) logging more informative. </p>
+
+<p> When a client sends bare newline characters, postscreen(8) logs
+this as:
+</p>
+
+<pre>
+    <b>BARE NEWLINE from</b> <i>address</i>
+</pre>
+
+<p> Translation: the SMTP client at <i>address</i> sent a bare
+newline character, that is newline not preceded by carriage
+return. </p>
+
+<p> The postscreen_bare_newline_action parameter specifies the
+action that is taken next.  See "<a href="#fail_after_220">When
+tests fail after the 220 SMTP server greeting</a>" below. </p>
+
+<h3> <a name="fail_after_220">When tests fail after the 220 SMTP server greeting</a> </h3>
+
+<p> When the client fails the pipelining, non-SMTP command or bare
+newline tests, the action is specified with postscreen_pipelining_action,
+postscreen_non_smtp_command_action or postscreen_bare_newline_action,
+respectively. </p>
+
+<dl>
+
+<dt> <b>ignore</b> (default for bare newline) </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Do NOT repeat this test before the result from some other test
+expires.
+
+This option is useful for testing and collecting statistics without
+blocking mail permanently. </dd>
+
+<dt> <b>enforce</b> (default for pipelining) </dt>
+
+<dd> Allow other tests to complete.  Reject attempts to deliver
+mail with a 550 SMTP reply, and log the helo/sender/recipient
+information.  Repeat this test the next time the client connects.
+</dd>
+
+<dt> <b>drop</b> (default for non-SMTP commands) </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply.  Repeat
+this test the next time the client connects.  This action is
+compatible with the Postfix SMTP server's smtpd_forbidden_commands
+feature. </dd>
+
+</dl>
+
+<h2> <a name="other_error">Other errors</a> </h2>
+
+<p> When an SMTP client hangs up unexpectedly during any tests,
+postscreen(8) logs this as: </p>
+
+<pre>
+    <b>HANGUP after</b> <i>time</i> <b>from</b> <i>address</i> <b>in</b> <i>test name</i>
+</pre>
+
+<p> Translation: the SMTP client at <i>address</i> disconnected
+unexpectedly, <i>time</i> seconds after the start of the
+test named <i>test name</i>. </p>
+
+<p> The following errors are reported by the built-in SMTP engine.
+This engine never accepts mail, therefore it has per-session limits
+on the number of commands and on the session length. </p>
+
+<pre>
+    <b>COMMAND TIME LIMIT</b> <b>from</b> <i>address</i>
+</pre>
+
+<p> Translation: the SMTP client at <i>address</i> reached the
+per-command time limit as specified with the postscreen_command_time_limit
+parameter.  The session is terminated immediately. </p>
+
+<pre>
+    <b>COMMAND COUNT LIMIT from</b> <i>address</i>
+</pre>
+
+<p> Translation: the SMTP client at <i>address</i> reached the
+per-session command count limit as specified with the
+postscreen_command_count_limit parameter.  The session is terminated
+immediately. </p>
+
+<pre>
+    <b>COMMAND LENGTH LIMIT from</b> <i>address</i>
+</pre>
+
+<p> Translation: the SMTP client at <i>address</i> reached the
+per-command length limit, as specified with the line_length_limit
+parameter.  The session is terminated immediately. </p>
+
+<h2> <a name="victory">When all tests succeed</a> </h2>
+
+<p> When a new SMTP client passes all tests (i.e. it is not whitelisted
+via some mechanism), postscreen(8) logs this as: </p>
+
+<pre>
+    <b>PASS NEW</b> <i>address</i>
+</pre>
+
+<p> Where <i>address</i> is the client IP address. Then, postscreen(8)
+creates a temporary whitelist entry that excludes the client IP
+address from further tests until the temporary whitelist entry
+expires, as controlled with the postscreen_*_ttl parameters. </p>
+
+<p> When no "<a href="#after_220">deep procol tests</a>" are
+configured, postscreen(8) passes the "live" connection to a Postfix
+SMTP server process.  The client can then continue as if postscreen(8)
+never even existed (except for the short postscreen_greet_wait delay).
+</p>
+
+<p> When any "<a href="#after_220">deep procol tests</a>" are
+configured, postscreen(8) cannot pass the "live" connection to a
+Postfix SMTP server process.  Instead, postscreen(8) defers mail
+delivery attempts with a 4XX status, logs the helo/sender/recipient
+information, and waits for the client to disconnect.  The next time
+the client connects it will be allowed to talk to a Postfix SMTP
+server process to deliver its mail. </p>
+
+<h2> <a name="config"> Configuring the postscreen(8) service</a>
+</h2>
+
+<p> postscreen(8) has been tested on FreeBSD [4-8] and Linux 2.[4-6]
+systems.  It probably needs additional work before it can be used
+on Solaris. </p>
+
+<ul>
+
+<li> <a href="#enable"> Turning on postscreen(8) without blocking
+mail</a>
+
+<li> <a href="#blocking"> Blocking mail with postscreen(8) </a>
+
+<li> <a href="#turnoff"> Turning off postscreen(8) </a>
+
+</ul>
+
+<h3> <a name="enable"> Turning on postscreen(8) without blocking mail</a> </h3>
+
+<p> To enable the postscreen(8) service and log client information
+without blocking mail: </p>
+
+<ol>
+
+<li> <p> Comment out the "<tt>smtp  inet ... smtpd</tt>" service
+in master.cf, including any "<tt>-o parameter=value</tt>" entries
+that follow.  </p>
+
+<li> <p> Uncomment the new "<tt>smtpd pass ... smtpd</tt>" service
+in master.cf, and duplicate any "<tt>-o parameter=value</tt>" entries
+from the smtpd service that was commented out in step 1. </p>
+
+<li> <p> Uncomment the new "<tt>smtp inet ... postscreen</tt>"
+service in master.cf. </p>
+
+<li> <p> Uncomment the new "<tt>dnsblog  unix ... dnsblog</tt>"
+service in master.cf.  This service does DNSBL lookups for postscreen(8)
+and logs results. </p>
+
+<li> <p> To enable DNSBL lookups, list some DNS blocklist sites in
+main.cf, separated by whitespace. Different sites can have different
+weights. For example:
+
+<pre>
+    postscreen_dnsbl_threshold = 2
+    postscreen_dnsbl_sites = zen.spamhaus.org*2 example.com*1 example.net*1
+</pre>
+
+<p> Note: if your DNSBL queries have a "secret" in the domain name,
+you must censor this information from the postscreen(8) SMTP replies.
+For example: </p>
+
+<pre>
+/etc/postfix/main.cf:
+    postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
+</pre>
+
+<pre>
+/etc/postfix/dnsbl_reply:
+    # Secret DNSBL name        Name in postscreen(8) replies
+    secret.zen.spamhaus.org    zen.spamhaus.org
+</pre>
+
+<p> The texthash: format is similar to hash: except that there is
+no need to run postmap(1) before the file can be used, and that it
+does not detect changes after the file is read. It is new with
+Postfix version 2.8. </p>
+
+<li> <p> Read the new configuration with "<tt>postfix reload</tt>".
+</p>
+
+</ol>
+
+<p> Notes: </p>
+
+<ul>
+
+<li> <p> See "<a href="#before_220">Tests before the 220 SMTP server
+greeting</a>" for details about the logging from these postscreen(8)
+tests. </p>
+
+<li> <p> By default, postscreen(8) whitelists all clients in
+mynetworks.  This is a safety feature to avoid you from getting
+into trouble with local users. </p>
+
+<li> <p> If you run Postfix 2.6 or earlier you must stop and start
+the master daemon ("<tt>postfix stop; postfix start</tt>").  This
+is needed because the Postfix "pass" master service type did not
+work reliably on all systems. </p>
+
+</ul>
+
+<h3> <a name="blocking"> Blocking mail with postscreen(8) </a> </h3>
+
+<p> To use the postscreen(8) service to block mail, edit main.cf and
+specify one or more of: </p>
+
+<ul>
+
+<li> <p> "<tt>postscreen_dnsbl_action = enforce</tt>", to reject
+clients that are on DNS blocklists, and to log the helo/sender/recipient
+information. With good DNSBLs this reduces the amount of load on
+Postfix SMTP servers dramatically.  </p>
+
+<li> <p> "<tt>postscreen_greet_action = enforce</tt>", to reject
+clients that talk before their turn, and to log the helo/sender/recipient
+information. This stops over half of all known-to-be illegitimate
+connections to Wietse's mail server. It is backup protection for
+spambots that haven't yet been blacklisted. </p>
+
+<li> <p> You can also enable "<a href="#after_220">deep protocol
+tests</a>", but these are more intrusive than the pregreet or DNSBL
+tests. </p>
+
+<p> When a good client passes the "<a href="#after_220">deep
+protocol tests</a>", postscreen(8) adds the client to the temporary
+whitelist but it cannot pass the "live" connection to a Postfix
+SMTP server process in the middle of the session. Instead, postscreen(8)
+defers mail delivery attempts with a 4XX status, logs the
+helo/sender/recipient information, and waits for the client to
+disconnect. </p>
+
+<p> When the client comes back in a later session, it is allowed
+to talk directly to a Postfix SMTP server.  See "after_220 <a
+href="#after_220">Tests after the 220 SMTP server greeting</a> above
+for limitations with STARTTLS, AUTH and other features that clients
+may need. Wietse enables "<a href="#after_220">deep protocol
+tests</a>" on his own internet-facing mail server.  </p>
+
+<li> <p> There is also support for permanent blacklists and whitelists;
+see the description of the postscreen_whitelist_networks and
+postscreen_blacklist_networks parameters for details. </p>
+
+</ul>
+
+<h3> <a name="turnoff"> Turning off postscreen(8) </a> </h3>
+
+<p> To turn off postscreen(8) and handle mail directly with Postfix
+SMTP server processes: </p>
+
+<ol>
+
+<li> <p> Comment out the "<tt>smtp inet ... postscreen</tt>" service
+in master.cf, including any "<tt>-o parameter=value</tt>" entries
+that follow. </p>
+
+<li> <p> Comment out the "<tt>dnsblog  unix ... dnsblog</tt>" service
+in master.cf.  </p>
+
+<li> <p> Comment out the "<tt>smtpd pass ... smtpd</tt>" service
+in master.cf, including any "<tt>-o parameter=value</tt>" entries
+that follow. </p>
+
+<li> <p> Uncomment the "<tt>smtp  inet ... smtpd</tt>" service in
+master.cf, including any "<tt>-o parameter=value</tt>" entries that
+follow.  </p>
+
+<li> <p> Read the new configuration with "<tt>postfix reload</tt>".
+</p>
+
+</ol>
+
+</body>
+
+</html>
+
index 972c567fbbe1aab9499732201f31220a0548cef7..14209fed8c9c6b53d4e32af1b98235996b399b07 100644 (file)
@@ -321,7 +321,10 @@ queue</a></h2>
 Users list.  Congestion was reported with the active and incoming
 queues large and not shrinking despite very large delivery agent
 process limits.  The thread is archived at:
-http://groups.google.com/groups?th=636626c645f5bbde </p>
+http://groups.google.com/groups?threadm=c0b7js$2r65$1@FreeBSD.csie.NCTU.edu.tw
+and
+http://archives.neohapsis.com/archives/postfix/2004-02/thread.html#1371
+</p>
 
 <p> Using an older version of qshape(1) it was quickly determined
 that all the messages were for just a few destinations: </p>
index 91a83888a8d9a6f61c847ed718dd24c8632bac2c..13117bd5e4a51c90da98a9d89b32f71b0c90b1f3 100644 (file)
@@ -1867,8 +1867,8 @@ only over a TLS-encrypted connection: </p>
 <blockquote>
 <pre>
 /etc/postfix/main.cf:
-    smtpd_sasl_security_options = noanonymous, noplaintext
-    smtpd_sasl_tls_security_options = noanonymous
+    smtp_sasl_security_options = noanonymous, noplaintext
+    smtp_sasl_tls_security_options = noanonymous
 </pre>
 </blockquote>
 
index 6ab2a49f9d9496e63761192783fad3ee37cfeed6..18cef6c95f614d1e66cfb6fb1383f4c10c6953d0 100644 (file)
@@ -38,7 +38,7 @@ the following problem: </p>
 
 </ul>
 
-<p> This extension is implemented as a separate EMSTP command, and
+<p> This extension is implemented as a separate ESMTP command, and
 can be used to transmit client or message attributes incrementally.
 It is not implemented by passing additional parameters via the MAIL
 FROM command, because doing so would require extending the MAIL
index f1bccef3342b38085cd37d3e88d6447a62983fcd..749f2086d8a382061f3e0f4ca2bf63f9707842bf 100644 (file)
@@ -5250,7 +5250,7 @@ This feature is available in Postfix 2.0 and later.
 %PARAM smtpd_forbidden_commands CONNECT, GET, POST
 
 <p>
-List of commands that causes the Postfix SMTP server to immediately
+List of commands that cause the Postfix SMTP server to immediately
 terminate the session with a 221 code. This can be used to disconnect
 clients that obviously attempt to abuse the system. In addition to the
 commands listed in this parameter, commands that follow the "Label:"
@@ -12518,7 +12518,7 @@ patch for Postfix 2.6. </p>
 
 <p> This feature is available in Postfix 2.8. </p>
 
-%PARAM smtpd_service smtpd
+%PARAM smtpd_service_name smtpd
 
 <p> The internal service that postscreen(8) forwards allowed
 connections to. In a future version there may be different
@@ -12543,26 +12543,26 @@ receive a 421 reponse. </p>
 
 <p> This feature is available in Postfix 2.8. </p>
 
-%PARAM postscreen_cache_ttl 1d
+%PARAM postscreen_greet_ttl 1d
 
-<p> The amount of time that postscreen(8) will cache a decision for
-a specific SMTP client IP address. During this time, the client IP
-address is excluded from tests. If possible, expired decisions are
-renewed automatically. Specify a non-zero time value (an integral
-value plus an optional one-letter suffix that specifies the time
-unit).  </p>
+<p> The amount of time that postscreen(8) will cache results from
+a successful PREGREET test. During this time, the client IP address
+is excluded from this test. The default is relatively short, because
+a good client can immediately talk to a real Postfix SMTP server. </p>
 
-<p> Time units: s (seconds), m (minutes), h (hours), d (days), w
-(weeks).  </p>
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).  </p>
 
 <p> This feature is available in Postfix 2.8. </p>
 
-%PARAM postscreen_cache_retention_time 1d
+%PARAM postscreen_cache_retention_time 7d
 
 <p> The amount of time that postscreen(8) will cache an expired
 temporary whitelist entry before it is removed. This prevents clients
 from being logged as "NEW" just because their cache entry expired
-an hour ago.  </p>
+an hour ago. It also prevents the cache from filling up with clients
+that passed some deep protocol test once and never came back. </p>
 
 <p> Time units: s (seconds), m (minutes), h (hours), d (days), w
 (weeks).  </p>
@@ -12588,14 +12588,15 @@ seconds. </p>
 
 <p> This feature is available in Postfix 2.8. </p>
 
-%PARAM postscreen_greet_wait 4s
+%PARAM postscreen_greet_wait ${stress?2}${stress:6}s
 
 <p> The amount of time that postscreen(8) will wait for an SMTP
 client to send a command before its turn, and for DNS blocklist
-lookup results to arrive. This is done only when the SMTP client
-IP address is not permanently whitelisted, and when it has no cached
-decision.  Specify a non-zero time value (an integral value plus
-an optional one-letter suffix that specifies the time unit).  </p>
+lookup results to arrive (default: 2 seconds under stress, 6 seconds
+normally).  This is done only when the SMTP client IP address is
+not permanently whitelisted, and when it has no cached decision.
+Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  </p>
 
 <p> Time units: s (seconds), m (minutes), h (hours), d (days), w
 (weeks).  </p>
@@ -12610,6 +12611,10 @@ query these domains with the IP addresses of non-whitelisted remote
 SMTP clients, and postscreen(8) will update an SMTP client's DNSBL
 score with each non-error reply. </p>
 
+<p> Caution: when postscreen rejects mail, it replies with the DNSBL
+domain name. Use the postscreen_dnsbl_reply_map feature to hide
+"password" information in DNSBL domain names. </p>
+
 <p> When a client's score is equal to or greater than the threshold
 specified with postscreen_dnsbl_threshold, postscreen(8) can drop
 the connection with the SMTP client. </p>
@@ -12655,7 +12660,7 @@ postscreen_dnsbl_sites = example.com=127.0.0.4
 
 <p> This feature is available in Postfix 2.8. </p>
 
-%PARAM postscreen_dnsbl_action continue
+%PARAM postscreen_dnsbl_action ignore
 
 <p>The action that postscreen(8) takes when an SMTP client's combined
 DNSBL score is equal to or greater than a threshold (as defined
@@ -12664,19 +12669,29 @@ parameters).  Specify one of the following: </p>
 
 <dl>
 
-<dt> continue </dt>
+<dt> <b>ignore</b> (default) </dt>
 
-<dd> Forward the connection to a real SMTP server process. </dd>
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Repeat this test the next time the client connects.
+This option is useful for testing and collecting statistics
+without blocking mail. </dd>
 
-<dt> drop </dt>
+<dt> <b>enforce</b> </dt>
 
-<dd> Drop the connection with a 521 SMTP reply. </dd>
+<dd> Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
+
+<dt> <b>drop</b> </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects. </dd>
 
 </dl>
 
 <p> This feature is available in Postfix 2.8. </p>
 
-%PARAM postscreen_greet_action continue
+%PARAM postscreen_greet_action ignore
 
 <p>The action that postscreen(8) takes when an SMTP client speaks
 before its turn within the time specified with the postscreen_greet_wait
@@ -12684,19 +12699,23 @@ parameter.  Specify one of the following: </p>
 
 <dl>
 
-<dt> continue </dt>
+<dt> <b>ignore</b> (default) </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Repeat this test the next time the client connects.
+This option is useful for testing and collecting statistics
+without blocking mail. </dd>
 
-<dd> Continue waiting until the postscreen_greet_wait time has
-elapsed. If the client's combined DNSBL score is equal to or greater
-than a threshold (as specified with the postscreen_dnsbl_sites and
-postscreen_dnsbl_threshold parameters), execute the action specified
-with the postscreen_dnsbl_action parameter, otherwise forward the
-connection to a real SMTP server process. </dd>
+<dt> <b>enforce</b> </dt>
 
-<dt> drop </dt>
+<dd> Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
 
-<dd> Drop the connection immediately with a 521 SMTP reply, without
-examining DNSBL lookup results. </dd>
+<dt> <b>drop</b> </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects. </dd>
 
 </dl>
 
@@ -12705,33 +12724,6 @@ IP address. </p>
 
 <p> This feature is available in Postfix 2.8. </p>
 
-%PARAM postscreen_hangup_action continue
-
-<p>The action that postscreen(8) takes when an SMTP client disconnects
-without sending data, within the time specified with the
-postscreen_greet_wait parameter.  Specify one of the following:
-</p>
-
-<dl>
-
-<dt> continue </dt>
-
-<dd> Continue waiting until the postscreen_greet_wait time has
-elapsed, and report whether the client's combined DNSBL score is
-equal to or greater than a threshold (as defined with the
-postscreen_dnsbl_sites and postscreen_dnsbl_threshold parameters).
-Do not forward the broken connection to a real SMTP server process.
-</dd>
-
-<dt> drop </dt>
-
-<dd> Drop the connection immediately, without reporting DNSBL lookup
-results. </dd>
-
-</dl>
-
-<p> This feature is available in Postfix 2.8. </p>
-
 %PARAM postscreen_whitelist_networks $mynetworks
 
 <p> Network addresses that are permanently whitelisted, and that
@@ -12762,7 +12754,7 @@ value to disable this feature.  </p>
 
 <p> This feature is available in Postfix 2.8. </p>
 
-%PARAM postscreen_blacklist_action continue
+%PARAM postscreen_blacklist_action ignore
 
 <p> The action that postscreen(8) takes when an SMTP client is
 permanently blacklisted with the postscreen_blacklist_networks
@@ -12770,20 +12762,23 @@ parameter.  Specify one of the following: </p>
 
 <dl>
 
-<dt> continue </dt>
+<dt> <b>ignore</b> (default) </dt>
 
-<dd> Continue waiting until the postscreen_greet_wait time has
-elapsed, and report whether the client triggers a PREGREET or HANGUP
-error, or whether the client's combined DNSBL score is equal to or
-greater than a threshold (as specified with the postscreen_dnsbl_sites
-and postscreen_dnsbl_threshold parameters).  Take the corresponding
-action, or forward the connection to a real SMTP server process.
-</dd>
+<dd> Ignore  this result. Allow other tests to complete.  Repeat
+this test the next time the client connects.
+This option is useful for testing and collecting statistics
+without blocking mail. </dd>
 
-<dt> drop </dt>
+<dt> <b>enforce</b> </dt>
 
-<dd> Drop the connection immediately with a 521 SMTP reply, without
-reporting PREGREET, HANGUP or DNSBL results. </dd>
+<dd> Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
+
+<dt> <b>drop</b> </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects. </dd>
 
 </dl>
 
@@ -13027,3 +13022,263 @@ its combined DNSBL score as defined with the postscreen_dnsbl_sites
 parameter. </p>
 
 <p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_command_count_limit 20
+
+<p> The limit on the total number of commands per SMTP session for
+postscreen(8)'s built-in SMTP protocol engine.  This SMTP engine
+defers or rejects all attempts to deliver mail, therefore there is
+no need to enforce separate limits on the number of junk commands
+and error commands.  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_command_time_limit ${stress?10}${stress:300}s
+
+<p> The command "read" time limit for postscreen(8)'s built-in SMTP
+protocol engine.  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_dnsbl_ttl 1d
+
+<p> The amount of time that postscreen(8) will cache results from
+a successful DNS blocklist test. During this time, the client IP address
+is excluded from this test. The default is relatively short, because a
+good client can immediately talk to a real Postfix SMTP server.
+</p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_pipelining_action enforce
+
+<p> The action that postscreen(8) takes when an SMTP client sends
+multiple commands instead of sending one command and waiting for
+the server to respond.  Specify one of the following: </p>
+
+<dl>
+
+<dt> <b>ignore</b> </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Do <i>not</i> repeat this test before some the result from some
+other test expires.
+This option is useful for testing and collecting statistics
+without blocking mail permanently. </dd>
+
+<dt> <b>enforce</b> </dt>
+
+<dd> Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
+
+<dt> <b>drop</b> </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects. </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.8. </p>
+
+%PARAM postscreen_pipelining_ttl 30d
+
+<p> The amount of time that postscreen(8) will cache results from
+a successful "pipelining" SMTP protocol test. During this time, the
+client IP address is excluded from this test. The default is
+long because a good client must disconnect after it passes the test,
+before it can talk to a real Postfix SMTP server. </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_pipelining_enable no
+
+<p> Enable "pipelining" SMTP protocol tests in the postscreen(8)
+server. These tests are expensive: a good client must disconnect
+after it passes the test, before it can talk to a real Postfix SMTP
+server. </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_watchdog_timeout 10s
+
+<p> How much time a postscreen(8) process may take to respond to
+an SMTP client command or to perform a cache operation before it
+is terminated by a built-in watchdog timer.  This is a safety
+mechanism that prevents postscreen(8) from becoming non-responsive
+due to a bug in Postfix itself or in system software.  To avoid
+false alarms and unnecessary cache corruption this limit cannot be
+set under 10s.  </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_helo_required $smtpd_helo_required
+
+<p> Require that a remote SMTP client sends HELO or EHLO before 
+commencing a MAIL transaction. </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_forbidden_commands $smtpd_forbidden_commands
+
+<p> List of commands that postscreen(8) server considers in violation
+of the SMTP protocol. See also: postscreen_non_smtp_command_action.
+</p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_disable_vrfy_command $disable_vrfy_command
+
+<p> Disable the SMTP VRFY command in the postscreen(8) daemon.  See
+disable_vrfy_command for details.  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_non_smtp_command_action drop
+
+<p> The action that postscreen(8) takes when an SMTP client sends
+non-SMTP commands as specified with the postscreen_forbidden_commands
+parameter.  Specify one of the following: </p>
+
+<dl>
+
+<dt> <b>ignore</b> </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Do <i>not</i> repeat this test before some the result from some
+other test expires.
+This option is useful for testing and collecting statistics
+without blocking mail permanently. </dd>
+
+<dt> <b>enforce</b> </dt>
+
+<dd> Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
+
+<dt> <b>drop</b> </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects. This action is the
+same as with the Postfix SMTP server's smtpd_forbidden_commands
+feature.  </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.8. </p>
+
+%PARAM postscreen_non_smtp_command_ttl 30d
+
+<p> The amount of time that postscreen(8) will cache results from
+a successful "non_smtp_command" SMTP protocol test. During this
+time, the client IP address is excluded from this test. The default
+is long because a client must disconnect after it passes the test,
+before it can talk to a real Postfix SMTP server. </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_non_smtp_command_enable no
+
+<p> Enable "non-SMTP command" tests in the postscreen(8) server. These
+tests are expensive: a client must disconnect after it passes the
+test, before it can talk to a real Postfix SMTP server. </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_dnsbl_reply_map
+
+<p> A mapping from actual DNSBL domain name which includes a secret
+password, to the DNSBL domain name that postscreen will reply with
+when it rejects mail.  When no mapping is found, the actual DNSBL
+domain will be used. </p>
+
+<p> For maximal stability it is best to use a file that is read
+into memory such as pcre:, regexp: or texthash: (a format similar
+to hash: except that there is no need to run postmap(1) before the
+file can be used, and that it does not detect changes after the
+file is read). </p>
+
+<p> Example: </p>
+
+<pre>
+/etc/postfix/main.cf:
+    postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
+</pre>
+
+<pre>
+/etc/postfix/dnsbl_reply:
+   secret.zen.spamhaus.org     zen.spamhaus.org
+</pre>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_bare_newline_action ignore
+
+<p> The action that postscreen(8) takes when an SMTP client sends
+a bare newline character, that is, a newline not preceded by carriage
+return.  Specify one of the following: </p>
+
+<dl>
+
+<dt> <b>ignore</b> </dt>
+
+<dd> Ignore the failure of this test. Allow other tests to complete.
+Do <i>not</i> repeat this test before some the result from some
+other test expires.
+This option is useful for testing and collecting statistics
+without blocking mail permanently. </dd>
+
+<dt> <b>enforce</b> </dt>
+
+<dd> Allow other tests to complete. Reject attempts to deliver mail
+with a 550 SMTP reply, and log the helo/sender/recipient information.
+Repeat this test the next time the client connects. </dd>
+
+<dt> <b>drop</b> </dt>
+
+<dd> Drop the connection immediately with a 521 SMTP reply. Repeat
+this test the next time the client connects.  </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 2.8. </p>
+
+%PARAM postscreen_bare_newline_ttl 30d
+
+<p> The amount of time that postscreen(8) will cache results from
+a successful "bare newline" SMTP protocol test. During this
+time, the client IP address is excluded from this test. The default
+is long because a client must disconnect after it passes the test,
+before it can talk to a real Postfix SMTP server. </p>
+
+<p> Specify a non-zero time value (an integral value plus an optional
+one-letter suffix that specifies the time unit).  Time units: s
+(seconds), m (minutes), h (hours), d (days), w (weeks).  </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+%PARAM postscreen_bare_newline_enable no
+
+<p> Enable "bare newline" SMTP protocol tests in the postscreen(8)
+server. These tests are expensive: a client must disconnect after
+it passes the test, before it can talk to a real Postfix SMTP server.
+</p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
index ab197d113db1bbd91ec2b523575ff4a5136c46a1..7fc6578997acc965763d5aabd5c9bda3bb0c9471 100644 (file)
@@ -1127,3 +1127,20 @@ tt
 unsubscribe
 wl
 zen
+Blocklist
+DNSBLs
+MailChannels
+Postscreen
+Spambots
+WHITELISTED
+blocklist
+dnsbl
+dnsblog
+postscreen
+postscreen's
+spambots
+spamd
+texthash
+ul
+whitelisted
+whitelists
index 7acea1162aa66d6318e50d92cefd6dbdaba2cadb..6c1e0166d638e32e500de312e86d1d4a4d44c164 100644 (file)
@@ -29,7 +29,7 @@ SRCS  = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.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 header_body_checks.c mkmap_proxy.c data_redirect.c \
-       match_service.c mail_conf_nint.c addr_match_list.c
+       match_service.c mail_conf_nint.c addr_match_list.c mail_conf_nbool.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 \
@@ -60,7 +60,7 @@ OBJS  = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.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 header_body_checks.o mkmap_proxy.o data_redirect.o \
-       match_service.o mail_conf_nint.o addr_match_list.o
+       match_service.o mail_conf_nint.o addr_match_list.o mail_conf_nbool.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 \
@@ -812,53 +812,10 @@ dict_ldap.o: db_common.h
 dict_ldap.o: dict_ldap.c
 dict_ldap.o: dict_ldap.h
 dict_ldap.o: string_list.h
-dict_mysql.o: ../../include/argv.h
-dict_mysql.o: ../../include/dict.h
-dict_mysql.o: ../../include/events.h
-dict_mysql.o: ../../include/find_inet.h
-dict_mysql.o: ../../include/match_list.h
-dict_mysql.o: ../../include/match_ops.h
-dict_mysql.o: ../../include/msg.h
-dict_mysql.o: ../../include/mymalloc.h
-dict_mysql.o: ../../include/myrand.h
-dict_mysql.o: ../../include/split_at.h
-dict_mysql.o: ../../include/stringops.h
 dict_mysql.o: ../../include/sys_defs.h
-dict_mysql.o: ../../include/vbuf.h
-dict_mysql.o: ../../include/vstream.h
-dict_mysql.o: ../../include/vstring.h
-dict_mysql.o: cfg_parser.h
-dict_mysql.o: db_common.h
 dict_mysql.o: dict_mysql.c
-dict_mysql.o: dict_mysql.h
-dict_mysql.o: string_list.h
-dict_sqlite.o: ../../include/dict.h
-dict_sqlite.o: ../../include/msg.h
-dict_sqlite.o: ../../include/sys_defs.h
-dict_sqlite.o: cfg_parser.h
-dict_sqlite.o: db_common.h
-dict_sqlite.o: dict_sqlite.c
-dict_sqlite.o: dict_sqlite.h
-dict_pgsql.o: ../../include/argv.h
-dict_pgsql.o: ../../include/dict.h
-dict_pgsql.o: ../../include/events.h
-dict_pgsql.o: ../../include/find_inet.h
-dict_pgsql.o: ../../include/match_list.h
-dict_pgsql.o: ../../include/match_ops.h
-dict_pgsql.o: ../../include/msg.h
-dict_pgsql.o: ../../include/mymalloc.h
-dict_pgsql.o: ../../include/myrand.h
-dict_pgsql.o: ../../include/split_at.h
-dict_pgsql.o: ../../include/stringops.h
 dict_pgsql.o: ../../include/sys_defs.h
-dict_pgsql.o: ../../include/vbuf.h
-dict_pgsql.o: ../../include/vstream.h
-dict_pgsql.o: ../../include/vstring.h
-dict_pgsql.o: cfg_parser.h
-dict_pgsql.o: db_common.h
 dict_pgsql.o: dict_pgsql.c
-dict_pgsql.o: dict_pgsql.h
-dict_pgsql.o: string_list.h
 dict_proxy.o: ../../include/argv.h
 dict_proxy.o: ../../include/attr.h
 dict_proxy.o: ../../include/dict.h
@@ -875,6 +832,22 @@ dict_proxy.o: dict_proxy.c
 dict_proxy.o: dict_proxy.h
 dict_proxy.o: mail_params.h
 dict_proxy.o: mail_proto.h
+dict_sqlite.o: ../../include/argv.h
+dict_sqlite.o: ../../include/dict.h
+dict_sqlite.o: ../../include/match_list.h
+dict_sqlite.o: ../../include/match_ops.h
+dict_sqlite.o: ../../include/msg.h
+dict_sqlite.o: ../../include/mymalloc.h
+dict_sqlite.o: ../../include/stringops.h
+dict_sqlite.o: ../../include/sys_defs.h
+dict_sqlite.o: ../../include/vbuf.h
+dict_sqlite.o: ../../include/vstream.h
+dict_sqlite.o: ../../include/vstring.h
+dict_sqlite.o: cfg_parser.h
+dict_sqlite.o: db_common.h
+dict_sqlite.o: dict_sqlite.c
+dict_sqlite.o: dict_sqlite.h
+dict_sqlite.o: string_list.h
 domain_list.o: ../../include/match_list.h
 domain_list.o: ../../include/match_ops.h
 domain_list.o: ../../include/sys_defs.h
@@ -1156,6 +1129,15 @@ mail_conf_long.o: ../../include/vstream.h
 mail_conf_long.o: ../../include/vstring.h
 mail_conf_long.o: mail_conf.h
 mail_conf_long.o: mail_conf_long.c
+mail_conf_nbool.o: ../../include/argv.h
+mail_conf_nbool.o: ../../include/dict.h
+mail_conf_nbool.o: ../../include/msg.h
+mail_conf_nbool.o: ../../include/sys_defs.h
+mail_conf_nbool.o: ../../include/vbuf.h
+mail_conf_nbool.o: ../../include/vstream.h
+mail_conf_nbool.o: ../../include/vstring.h
+mail_conf_nbool.o: mail_conf.h
+mail_conf_nbool.o: mail_conf_nbool.c
 mail_conf_nint.o: ../../include/argv.h
 mail_conf_nint.o: ../../include/dict.h
 mail_conf_nint.o: ../../include/msg.h
index 5d6659ab8ea40b6423315846cbb342dedee00bd7..c349930e343256e2a060920f4bf36c212aac8d93 100644 (file)
@@ -68,7 +68,7 @@
 #include <sqlite3.h>
 
 #if !defined(SQLITE_VERSION_NUMBER) || (SQLITE_VERSION_NUMBER < 3005004)
-#error "Your SQLite version is too old"
+#define sqlite3_prepare_v2 sqlite3_prepare
 #endif
 
 /* Utility library. */
index 0be29f2978337a6379bb8d872647216ea72d7b37..5c050921463e9a188bb0d06a3a6e14a905fb8f2c 100644 (file)
@@ -52,6 +52,7 @@ extern int get_mail_conf_bool(const char *, int);
 extern int get_mail_conf_time(const char *, const char *, int, int);
 extern int get_mail_conf_nint(const char *, const char *, int, int);
 extern char *get_mail_conf_raw(const char *, const char *, int, int);
+extern int get_mail_conf_nbool(const char *, const char *);
 
 extern char *get_mail_conf_str2(const char *, const char *, const char *, int, int);
 extern int get_mail_conf_int2(const char *, const char *, int, int, int);
@@ -69,6 +70,7 @@ extern int get_mail_conf_bool_fn(const char *, int (*) (void));
 extern int get_mail_conf_time_fn(const char *, const char *(*) (void), int, int, int);
 extern int get_mail_conf_nint_fn(const char *, const char *(*) (void), int, int);
 extern char *get_mail_conf_raw_fn(const char *, const char *(*) (void), int, int);
+extern int get_mail_conf_nbool_fn(const char *, const char *(*) (void));
 
  /*
   * Update dictionary.
@@ -81,6 +83,10 @@ extern void set_mail_conf_time(const char *, const char *);
 extern void set_mail_conf_time_int(const char *, int);
 extern void set_mail_conf_nint(const char *, const char *);
 extern void set_mail_conf_nint_int(const char *, int);
+extern void set_mail_conf_nbool(const char *, const char *);
+
+#define set_mail_conf_nbool_int(name, value) \
+    set_mail_conf_nbool((name), (value) ? CONFIG_BOOL_YES : CONFIG_BOOL_NO)
 
  /*
   * Tables that allow us to selectively copy values from the global
@@ -140,6 +146,12 @@ typedef struct {
     int     max;                       /* upper bound or zero */
 } CONFIG_NINT_TABLE;
 
+typedef struct {
+    const char *name;                  /* config variable name */
+    const char *defval;                        /* default value */
+    int    *target;                    /* pointer to global variable */
+} CONFIG_NBOOL_TABLE;
+
 extern void get_mail_conf_str_table(const CONFIG_STR_TABLE *);
 extern void get_mail_conf_int_table(const CONFIG_INT_TABLE *);
 extern void get_mail_conf_long_table(const CONFIG_LONG_TABLE *);
@@ -147,6 +159,7 @@ extern void get_mail_conf_bool_table(const CONFIG_BOOL_TABLE *);
 extern void get_mail_conf_time_table(const CONFIG_TIME_TABLE *);
 extern void get_mail_conf_nint_table(const CONFIG_NINT_TABLE *);
 extern void get_mail_conf_raw_table(const CONFIG_RAW_TABLE *);
+extern void get_mail_conf_nbool_table(const CONFIG_NBOOL_TABLE *);
 
  /*
   * Tables to initialize parameters from the global configuration file or
@@ -198,12 +211,19 @@ typedef struct {
     int     max;                       /* upper bound or zero */
 } CONFIG_NINT_FN_TABLE;
 
+typedef struct {
+    const char *name;                  /* config variable name */
+    const char *(*defval) (void);      /* default value provider */
+    int    *target;                    /* pointer to global variable */
+} CONFIG_NBOOL_FN_TABLE;
+
 extern void get_mail_conf_str_fn_table(const CONFIG_STR_FN_TABLE *);
 extern void get_mail_conf_int_fn_table(const CONFIG_INT_FN_TABLE *);
 extern void get_mail_conf_long_fn_table(const CONFIG_LONG_FN_TABLE *);
 extern void get_mail_conf_bool_fn_table(const CONFIG_BOOL_FN_TABLE *);
 extern void get_mail_conf_raw_fn_table(const CONFIG_RAW_FN_TABLE *);
 extern void get_mail_conf_nint_fn_table(const CONFIG_NINT_FN_TABLE *);
+extern void get_mail_conf_nbool_fn_table(const CONFIG_NBOOL_FN_TABLE *);
 
 /* LICENSE
 /* .ad
diff --git a/postfix/src/global/mail_conf_nbool.c b/postfix/src/global/mail_conf_nbool.c
new file mode 100644 (file)
index 0000000..3ffa6f4
--- /dev/null
@@ -0,0 +1,158 @@
+/*++
+/* NAME
+/*     mail_conf_nbool 3
+/* SUMMARY
+/*     boolean-valued configuration parameter support
+/* SYNOPSIS
+/*     #include <mail_conf.h>
+/*
+/*     int     get_mail_conf_nbool(name, defval)
+/*     const char *name;
+/*     const char *defval;
+/*
+/*     int     get_mail_conf_nbool_fn(name, defval)
+/*     const char *name;
+/*     const char *(*defval)();
+/*
+/*     void    set_mail_conf_nbool(name, value)
+/*     const char *name;
+/*     const char *value;
+/*
+/*     void    get_mail_conf_nbool_table(table)
+/*     const CONFIG_NBOOL_TABLE *table;
+/*
+/*     void    get_mail_conf_nbool_fn_table(table)
+/*     const CONFIG_NBOOL_TABLE *table;
+/* DESCRIPTION
+/*     This module implements configuration parameter support for
+/*     boolean values. The internal representation is zero (false)
+/*     and non-zero (true). The external representation is "no"
+/*     (false) and "yes" (true). The conversion from external
+/*     representation is case insensitive. Unlike mail_conf_bool(3)
+/*     the default is a string value which is subject to macro expansion.
+/*
+/*     get_mail_conf_nbool() looks up the named entry in the global
+/*     configuration dictionary. The specified default value is
+/*     returned when no value was found.
+/*
+/*     get_mail_conf_nbool_fn() is similar but specifies a function that
+/*     provides the default value. The function is called only
+/*     when the default value is needed.
+/*
+/*     set_mail_conf_nbool() updates the named entry in the global
+/*     configuration dictionary. This has no effect on values that
+/*     have been looked up earlier via the get_mail_conf_XXX() routines.
+/*
+/*     get_mail_conf_nbool_table() and get_mail_conf_int_fn_table() initialize
+/*     lists of variables, as directed by their table arguments. A table
+/*     must be terminated by a null entry.
+/* DIAGNOSTICS
+/*     Fatal errors: malformed boolean value.
+/* SEE ALSO
+/*     config(3) general configuration
+/*     mail_conf_str(3) string-valued configuration parameters
+/*     mail_conf_int(3) integer-valued configuration parameters
+/* 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 <stdlib.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <dict.h>
+
+/* Global library. */
+
+#include "mail_conf.h"
+
+/* convert_mail_conf_nbool - look up and convert boolean parameter value */
+
+static int convert_mail_conf_nbool(const char *name, int *intval)
+{
+    const char *strval;
+
+    if ((strval = mail_conf_lookup_eval(name)) == 0) {
+       return (0);
+    } else {
+       if (strcasecmp(strval, CONFIG_BOOL_YES) == 0) {
+           *intval = 1;
+       } else if (strcasecmp(strval, CONFIG_BOOL_NO) == 0) {
+           *intval = 0;
+       } else {
+           msg_fatal("bad boolean configuration: %s = %s", name, strval);
+       }
+       return (1);
+    }
+}
+
+/* get_mail_conf_nbool - evaluate boolean-valued configuration variable */
+
+int     get_mail_conf_nbool(const char *name, const char *defval)
+{
+    int     intval;
+
+    if (convert_mail_conf_nbool(name, &intval) == 0)
+       set_mail_conf_nbool(name, defval);
+    if (convert_mail_conf_nbool(name, &intval) == 0)
+       msg_panic("get_mail_conf_nbool: parameter not found: %s", name);
+    return (intval);
+}
+
+/* get_mail_conf_nbool_fn - evaluate boolean-valued configuration variable */
+
+typedef const char *(*stupid_indent_int) (void);
+
+int     get_mail_conf_nbool_fn(const char *name, stupid_indent_int defval)
+{
+    int     intval;
+
+    if (convert_mail_conf_nbool(name, &intval) == 0)
+       set_mail_conf_nbool(name, defval());
+    if (convert_mail_conf_nbool(name, &intval) == 0)
+       msg_panic("get_mail_conf_nbool_fn: parameter not found: %s", name);
+    return (intval);
+}
+
+/* set_mail_conf_nbool - update boolean-valued configuration dictionary entry */
+
+void    set_mail_conf_nbool(const char *name, const char *value)
+{
+    mail_conf_update(name, value);
+}
+
+/* get_mail_conf_nbool_table - look up table of booleans */
+
+void    get_mail_conf_nbool_table(const CONFIG_NBOOL_TABLE *table)
+{
+    while (table->name) {
+       table->target[0] = get_mail_conf_nbool(table->name, table->defval);
+       table++;
+    }
+}
+
+/* get_mail_conf_nbool_fn_table - look up booleans, defaults are functions */
+
+void    get_mail_conf_nbool_fn_table(const CONFIG_NBOOL_FN_TABLE *table)
+{
+    while (table->name) {
+       table->target[0] = get_mail_conf_nbool_fn(table->name, table->defval);
+       table++;
+    }
+}
index 6884859eeeab5b2797befc6fc3575f17879da899..0a2c50ab6475f7a593fc315b70419ccb33be33bc 100644 (file)
@@ -40,8 +40,8 @@
 /*     int     max;
 /* DESCRIPTION
 /*     This module implements configuration parameter support
-/*     for integer values. The default value can be a macro
-/*     expression ($name, ${name?value} and ${name:value}).
+/*     for integer values. Unlike mail_conf_int, the default
+/*     is a string, which can be subjected to macro expansion.
 /*
 /*     get_mail_conf_nint() looks up the named entry in the global
 /*     configuration dictionary. The default value is returned
index de9e6b8eaae6c96210397050b59f4a8c87045472..2635123d5816c08cca0de34d4f8811832c33feed 100644 (file)
@@ -3227,12 +3227,8 @@ extern int var_ps_post_queue_limit;
 #define DEF_PS_PRE_QLIMIT      "$" VAR_PROC_LIMIT
 extern int var_ps_pre_queue_limit;
 
-#define VAR_PS_CACHE_TTL       "postscreen_cache_ttl"
-#define DEF_PS_CACHE_TTL       "1d"
-extern int var_ps_cache_ttl;
-
 #define VAR_PS_CACHE_RET       "postscreen_cache_retention_time"
-#define DEF_PS_CACHE_RET       "1d"
+#define DEF_PS_CACHE_RET       "7d"
 extern int var_ps_cache_ret;
 
 #define VAR_PS_CACHE_SCAN      "postscreen_cache_cleanup_interval"
@@ -3240,12 +3236,24 @@ extern int var_ps_cache_ret;
 extern int var_ps_cache_scan;
 
 #define VAR_PS_GREET_WAIT      "postscreen_greet_wait"
-#define DEF_PS_GREET_WAIT      "4s"
+#define DEF_PS_GREET_WAIT      "${stress?2}${stress:6}s"
 extern int var_ps_greet_wait;
 
-#define VAR_PS_GREET_ACTION    "postscreen_greet_action"
-#define DEF_PS_GREET_ACTION    "continue"
-extern char *var_ps_greet_action;
+#define VAR_PS_PREGR_BANNER    "postscreen_greet_banner"
+#define DEF_PS_PREGR_BANNER    "$" VAR_SMTPD_BANNER
+extern char *var_ps_pregr_banner;
+
+#define VAR_PS_PREGR_ENABLE    "postscreen_greet_enable"
+#define DEF_PS_PREGR_ENABLE    no
+extern char *var_ps_pregr_enable;
+
+#define VAR_PS_PREGR_ACTION    "postscreen_greet_action"
+#define DEF_PS_PREGR_ACTION    "ignore"
+extern char *var_ps_pregr_action;
+
+#define VAR_PS_PREGR_TTL       "postscreen_greet_ttl"
+#define DEF_PS_PREGR_TTL       "1d"
+extern int var_ps_pregr_ttl;
 
 #define VAR_PS_DNSBL_SITES     "postscreen_dnsbl_sites"
 #define DEF_PS_DNSBL_SITES     ""
@@ -3255,13 +3263,57 @@ extern char *var_ps_dnsbl_sites;
 #define DEF_PS_DNSBL_THRESH    1
 extern int var_ps_dnsbl_thresh;
 
+#define VAR_PS_DNSBL_ENABLE    "postscreen_dnsbl_enable"
+#define DEF_PS_DNSBL_ENABLE    0
+extern char *var_ps_dnsbl_enable;
+
 #define VAR_PS_DNSBL_ACTION    "postscreen_dnsbl_action"
-#define DEF_PS_DNSBL_ACTION    "continue"
+#define DEF_PS_DNSBL_ACTION    "ignore"
 extern char *var_ps_dnsbl_action;
 
-#define VAR_PS_HUP_ACTION      "postscreen_hangup_action"
-#define DEF_PS_HUP_ACTION      "continue"
-extern char *var_ps_hangup_action;
+#define VAR_PS_DNSBL_TTL       "postscreen_dnsbl_ttl"
+#define DEF_PS_DNSBL_TTL       "1d"
+extern int var_ps_dnsbl_ttl;
+
+#define        VAR_PS_DNSBL_REPLY      "postscreen_dnsbl_reply_map"
+#define        DEF_PS_DNSBL_REPLY      ""
+extern char *var_ps_dnsbl_reply;
+
+#define VAR_PS_PIPEL_ENABLE    "postscreen_pipelining_enable"
+#define DEF_PS_PIPEL_ENABLE    0
+extern bool var_ps_pipel_enable;
+
+#define VAR_PS_PIPEL_ACTION    "postscreen_pipelining_action"
+#define DEF_PS_PIPEL_ACTION    "enforce"
+extern char *var_ps_pipel_action;
+
+#define VAR_PS_PIPEL_TTL       "postscreen_pipelining_ttl"
+#define DEF_PS_PIPEL_TTL       "30d"
+extern int var_ps_pipel_ttl;
+
+#define VAR_PS_NSMTP_ENABLE    "postscreen_non_smtp_command_enable"
+#define DEF_PS_NSMTP_ENABLE    0
+extern bool var_ps_nsmtp_enable;
+
+#define VAR_PS_NSMTP_ACTION    "postscreen_non_smtp_command_action"
+#define DEF_PS_NSMTP_ACTION    "drop"
+extern char *var_ps_nsmtp_action;
+
+#define VAR_PS_NSMTP_TTL       "postscreen_non_smtp_command_ttl"
+#define DEF_PS_NSMTP_TTL       "30d"
+extern int var_ps_nsmtp_ttl;
+
+#define VAR_PS_BARLF_ENABLE    "postscreen_bare_newline_enable"
+#define DEF_PS_BARLF_ENABLE    0
+extern bool var_ps_barlf_enable;
+
+#define VAR_PS_BARLF_ACTION    "postscreen_bare_newline_action"
+#define DEF_PS_BARLF_ACTION    "ignore"
+extern char *var_ps_barlf_action;
+
+#define VAR_PS_BARLF_TTL       "postscreen_bare_newline_ttl"
+#define DEF_PS_BARLF_TTL       "30d"
+extern int var_ps_barlf_ttl;
 
 #define VAR_PS_WLIST_NETS      "postscreen_whitelist_networks"
 #define DEF_PS_WLIST_NETS      "$" VAR_MYNETWORKS
@@ -3272,12 +3324,32 @@ extern char *var_ps_wlist_nets;
 extern char *var_ps_blist_nets;
 
 #define VAR_PS_BLIST_ACTION    "postscreen_blacklist_action"
-#define DEF_PS_BLIST_ACTION    "continue"
+#define DEF_PS_BLIST_ACTION    "ignore"
 extern char *var_ps_blist_nets;
 
-#define VAR_PS_GREET_BANNER    "postscreen_greet_banner"
-#define DEF_PS_GREET_BANNER    "$" VAR_SMTPD_BANNER
-extern char *var_ps_banner;
+#define VAR_PS_CMD_COUNT       "postscreen_command_count_limit"
+#define DEF_PS_CMD_COUNT       20
+extern int var_ps_cmd_count;
+
+#define VAR_PS_CMD_TIME                "postscreen_command_time_limit"
+#define DEF_PS_CMD_TIME                DEF_SMTPD_TMOUT
+extern char *var_ps_cmd_time;
+
+#define VAR_PS_WATCHDOG                "postscreen_watchdog_timeout"
+#define DEF_PS_WATCHDOG                "10s"
+extern int var_ps_watchdog;
+
+#define VAR_PS_FORBID_CMDS     "postscreen_forbidden_commands"
+#define DEF_PS_FORBID_CMDS     "$" VAR_SMTPD_FORBID_CMDS
+extern char *var_ps_forbid_cmds;
+
+#define VAR_PS_HELO_REQUIRED   "postscreen_helo_required"
+#define DEF_PS_HELO_REQUIRED   "$" VAR_HELO_REQUIRED
+extern bool var_ps_helo_required;
+
+#define VAR_PS_DISABLE_VRFY    "postscreen_disable_vrfy_command"
+#define DEF_PS_DISABLE_VRFY    "$" VAR_DISABLE_VRFY_CMD
+extern bool var_ps_disable_vrfy;
 
 /* LICENSE
 /* .ad
index e4906695f154da9d6ec3f8a118c979535e973b25..9a910abb1cb9e5fc27bf35bd75d256330e45aa44 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      "20100829"
+#define MAIL_RELEASE_DATE      "20100912"
 #define MAIL_VERSION_NUMBER    "2.8"
 
 #ifdef SNAPSHOT
index 5fd068241abf5b15b87aec5186f408cadd072dc7..0335d6922195809353180aa61ff9a3343ad3d6c6 100644 (file)
 /*     global Postfix configuration file. Tables are loaded in the
 /*     order as specified, and multiple instances of the same type
 /*     are allowed.
+/* .IP "MAIL_SERVER_NBOOL_TABLE (CONFIG_NBOOL_TABLE *)"
+/*     A table with configurable parameters, to be loaded from the
+/*     global Postfix configuration file. Tables are loaded in the
+/*     order as specified, and multiple instances of the same type
+/*     are allowed.
 /* .IP "MAIL_SERVER_PRE_INIT (void *(char *service_name, char **argv))"
 /*     A pointer to a function that is called once
 /*     by the skeleton after it has read the global configuration file
 /*     A pointer to a function that is called after "postfix reload"
 /*     or "master exit".  The application can call event_server_drain()
 /*     (see below) to finish ongoing activities in the background.
+/* .IP "MAIL_SERVER_WATCHDOG (int *)"
+/*     Override the default 1000s watchdog timeout. The value is
+/*     used after command-line and main.cf file processing.
 /* .PP
 /*     event_server_disconnect() should be called by the application
 /*     to close a client connection.
@@ -240,6 +248,7 @@ static int event_server_in_flow_delay;
 static unsigned event_server_generation;
 static void (*event_server_pre_disconn) (VSTREAM *, char *, char **);
 static void (*event_server_slow_exit) (char *, char **);
+static int event_server_watchdog = 1000;
 
 /* event_server_exit - normal termination */
 
@@ -256,6 +265,7 @@ static void event_server_abort(int unused_event, char *unused_context)
 {
     if (msg_verbose)
        msg_info("master disconnect -- exiting");
+    event_disable_readwrite(MASTER_STATUS_FD);
     if (event_server_slow_exit)
        event_server_slow_exit(event_server_name, event_server_argv);
     else
@@ -672,6 +682,9 @@ NORETURN event_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
        case MAIL_SERVER_NINT_TABLE:
            get_mail_conf_nint_table(va_arg(ap, CONFIG_NINT_TABLE *));
            break;
+       case MAIL_SERVER_NBOOL_TABLE:
+           get_mail_conf_nbool_table(va_arg(ap, CONFIG_NBOOL_TABLE *));
+           break;
        case MAIL_SERVER_PRE_INIT:
            pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
            break;
@@ -708,6 +721,9 @@ NORETURN event_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
                msg_fatal("service %s requires privileged operation",
                          service_name);
            break;
+       case MAIL_SERVER_WATCHDOG:
+           event_server_watchdog = *va_arg(ap, int *);
+           break;
        case MAIL_SERVER_SLOW_EXIT:
            event_server_slow_exit = va_arg(ap, MAIL_SERVER_SLOW_EXIT_FN);
            break;
@@ -842,7 +858,8 @@ NORETURN event_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
     close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC);
     close_on_exec(MASTER_FLOW_READ, CLOSE_ON_EXEC);
     close_on_exec(MASTER_FLOW_WRITE, CLOSE_ON_EXEC);
-    watchdog = watchdog_create(var_daemon_timeout, (WATCHDOG_FN) 0, (char *) 0);
+    watchdog = watchdog_create(event_server_watchdog,
+                              (WATCHDOG_FN) 0, (char *) 0);
 
     /*
      * The event loop, at last.
index 5a88b2ff800d77f5b0c6d3e7b5bd953759ff2985..793b64e02d5da1eabdc58d4d4ebefa6bc9462aca 100644 (file)
@@ -22,6 +22,7 @@
 #define MAIL_SERVER_TIME_TABLE 4
 #define MAIL_SERVER_RAW_TABLE  5
 #define MAIL_SERVER_NINT_TABLE 6
+#define MAIL_SERVER_NBOOL_TABLE        7
 
 #define        MAIL_SERVER_PRE_INIT    10
 #define MAIL_SERVER_POST_INIT  11
index dc39471295800e7949bd4226a70f66f24be3f23e..df67d3279f354fa300152e8d212746448ab978c4 100644 (file)
 /*     global Postfix configuration file. Tables are loaded in the
 /*     order as specified, and multiple instances of the same type
 /*     are allowed.
+/* .IP "MAIL_SERVER_NBOOL_TABLE (CONFIG_NBOOL_TABLE *)"
+/*     A table with configurable parameters, to be loaded from the
+/*     global Postfix configuration file. Tables are loaded in the
+/*     order as specified, and multiple instances of the same type
+/*     are allowed.
 /* .IP "MAIL_SERVER_PRE_INIT (void *(char *service_name, char **argv))"
 /*     A pointer to a function that is called once
 /*     by the skeleton after it has read the global configuration file
@@ -673,6 +678,9 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
        case MAIL_SERVER_NINT_TABLE:
            get_mail_conf_nint_table(va_arg(ap, CONFIG_NINT_TABLE *));
            break;
+       case MAIL_SERVER_NBOOL_TABLE:
+           get_mail_conf_nbool_table(va_arg(ap, CONFIG_NBOOL_TABLE *));
+           break;
        case MAIL_SERVER_PRE_INIT:
            pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
            break;
index 009f3acee1774b1f8ee1d9e5e5af3c2565a96e30..e2a9e72256685dacf1c5790af1628dd3cdb8415a 100644 (file)
 /*     global Postfix configuration file. Tables are loaded in the
 /*     order as specified, and multiple instances of the same type
 /*     are allowed.
+/* .IP "MAIL_SERVER_NBOOL_TABLE (CONFIG_NBOOL_TABLE *)"
+/*     A table with configurable parameters, to be loaded from the
+/*     global Postfix configuration file. Tables are loaded in the
+/*     order as specified, and multiple instances of the same type
+/*     are allowed.
 /* .IP "MAIL_SERVER_PRE_INIT (void *(char *service_name, char **argv))"
 /*     A pointer to a function that is called once
 /*     by the skeleton after it has read the global configuration file
@@ -562,6 +567,9 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...)
        case MAIL_SERVER_NINT_TABLE:
            get_mail_conf_nint_table(va_arg(ap, CONFIG_NINT_TABLE *));
            break;
+       case MAIL_SERVER_NBOOL_TABLE:
+           get_mail_conf_nbool_table(va_arg(ap, CONFIG_NBOOL_TABLE *));
+           break;
        case MAIL_SERVER_PRE_INIT:
            pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
            break;
index b40df7f5e6db07556ebb5571735471b6f248e38a..5d9127362ffac0dda398891b72e262c942f26368 100644 (file)
 /*     global Postfix configuration file. Tables are loaded in the
 /*     order as specified, and multiple instances of the same type
 /*     are allowed.
+/* .IP "MAIL_SERVER_NBOOL_TABLE (CONFIG_NBOOL_TABLE *)"
+/*     A table with configurable parameters, to be loaded from the
+/*     global Postfix configuration file. Tables are loaded in the
+/*     order as specified, and multiple instances of the same type
+/*     are allowed.
 /* .IP "MAIL_SERVER_PRE_INIT (void *(char *service_name, char **argv))"
 /*     A pointer to a function that is called once
 /*     by the skeleton after it has read the global configuration file
@@ -573,6 +578,9 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,..
        case MAIL_SERVER_NINT_TABLE:
            get_mail_conf_nint_table(va_arg(ap, CONFIG_NINT_TABLE *));
            break;
+       case MAIL_SERVER_NBOOL_TABLE:
+           get_mail_conf_nbool_table(va_arg(ap, CONFIG_NBOOL_TABLE *));
+           break;
        case MAIL_SERVER_PRE_INIT:
            pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
            break;
index 90217f04084b980e9db3ecd6bec9e04f32c59855..bce82c0c96e42f7973ba5f2ad27dce8429723337 100644 (file)
@@ -8,7 +8,7 @@ CFLAGS  = $(DEBUG) $(OPT) $(DEFS)
 TESTPROG= 
 MAKES  = bool_table.h bool_vars.h int_table.h int_vars.h str_table.h \
        str_vars.h time_table.h time_vars.h raw_table.h raw_vars.h \
-       nint_table.h nint_vars.h
+       nint_table.h nint_vars.h nbool_table.h nbool_vars.h
 AUTOS  = auto_table.h auto_vars.h
 DUMMIES        = makes_dummy autos_dummy # for "make -j"
 PROG   = postconf
@@ -121,6 +121,10 @@ postconf.o: install_table.h
 postconf.o: install_vars.h
 postconf.o: int_table.h
 postconf.o: int_vars.h
+postconf.o: nbool_table.h
+postconf.o: nbool_vars.h
+postconf.o: nint_table.h
+postconf.o: nint_vars.h
 postconf.o: postconf.c
 postconf.o: raw_table.h
 postconf.o: raw_vars.h
index 4e0c690c4ce2e22a3dbc1f737fe8da51238a3639..399c0248ea1466fe48b9534a78fe8dee5bb359fe 100644 (file)
        }
     }
 }
+/^(static| )*(const +)?CONFIG_NBOOL_TABLE .*\{/,/\};/ { 
+    if ($1 ~ /^VAR/) {
+       nbool_vars["int " substr($3,2,length($3)-2) ";"] = 1
+       if (++btab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) {
+           nbool_table[$0] = 1
+       }
+    }
+}
 
 END { 
     # Print parameter declarations without busting old AWK's file limit.
@@ -99,6 +107,11 @@ END {
        print key
     print "EOF"
 
+    print "cat >nbool_vars.h <<'EOF'"
+    for (key in nbool_vars)
+       print key
+    print "EOF"
+
     # Print parameter initializations without busting old AWK's file limit.
     print "sed 's/[    ][      ]*/ /g' >int_table.h <<'EOF'"
     for (key in int_table)
@@ -130,6 +143,11 @@ END {
        print key
     print "EOF"
 
+    print "sed 's/[    ][      ]*/ /g' >nbool_table.h <<'EOF'"
+    for (key in nbool_table)
+       print key
+    print "EOF"
+
     # Flush output nicely.
     exit(0);
 }
index 3abfc56bf29d3e1abf2a8a235ecbf189ab62bae6..82d8be07c6bd3a8d8ff58b92d9b84651916376f6 100644 (file)
 /* .IP "\fBtcp\fR (read-only)"
 /*     Perform lookups using a simple request-reply protocol that is
 /*     described in \fBtcp_table\fR(5).
-/*     This feature is not included with the stable Postfix release.
+/* .IP "\fBtexthash\fR (read-only)"
+/*     Produces similar results as hash: files, except that you don't
+/*     need to run the postmap(1) command before you can use the file,
+/*     and that it does not detect changes after the file is read.
 /* .IP "\fBunix\fR (read-only)"
 /*     A limited way to query the UNIX authentication database. The
 /*     following tables are implemented:
@@ -326,6 +329,7 @@ DICT   *text_table;
 #include "str_vars.h"
 #include "raw_vars.h"
 #include "nint_vars.h"
+#include "nbool_vars.h"
 
  /*
   * Manually extracted.
@@ -368,6 +372,11 @@ static const CONFIG_NINT_TABLE nint_table[] = {
     0,
 };
 
+static const CONFIG_NBOOL_TABLE nbool_table[] = {
+#include "nbool_table.h"
+    0,
+};
+
  /*
   * Parameters with default values obtained via function calls.
   */
@@ -688,6 +697,7 @@ static void hash_parameters(void)
     const CONFIG_STR_FN_TABLE *csft;
     const CONFIG_RAW_TABLE *rst;
     const CONFIG_NINT_TABLE *nst;
+    const CONFIG_NBOOL_TABLE *bst;
 
     param_table = htable_create(100);
 
@@ -707,6 +717,8 @@ static void hash_parameters(void)
        htable_enter(param_table, rst->name, (char *) rst);
     for (nst = nint_table; nst->name; nst++)
        htable_enter(param_table, nst->name, (char *) nst);
+    for (bst = nbool_table; bst->name; bst++)
+       htable_enter(param_table, bst->name, (char *) bst);
 }
 
 /* show_strval - show string-valued parameter */
@@ -926,6 +938,33 @@ static void print_nint(int mode, CONFIG_NINT_TABLE * rst)
     }
 }
 
+/* print_nbool - print new boolean parameter */
+
+static void print_nbool(int mode, CONFIG_NBOOL_TABLE * bst)
+{
+    const char *value;
+
+    if (mode & SHOW_EVAL)
+       msg_warn("parameter %s expands at run-time", bst->name);
+    mode &= ~SHOW_EVAL;
+
+    if (mode & SHOW_DEFS) {
+       show_strval(mode, bst->name, bst->defval);
+    } else {
+       value = dict_lookup(CONFIG_DICT, bst->name);
+       if ((mode & SHOW_NONDEF) == 0) {
+           if (value == 0) {
+               show_strval(mode, bst->name, bst->defval);
+           } else {
+               show_strval(mode, bst->name, value);
+           }
+       } else {
+           if (value != 0)
+               show_strval(mode, bst->name, value);
+       }
+    }
+}
+
 /* print_parameter - show specific parameter */
 
 static void print_parameter(int mode, char *ptr)
@@ -952,6 +991,8 @@ static void print_parameter(int mode, char *ptr)
        print_raw(mode, (CONFIG_RAW_TABLE *) ptr);
     if (INSIDE(ptr, nint_table))
        print_nint(mode, (CONFIG_NINT_TABLE *) ptr);
+    if (INSIDE(ptr, nbool_table))
+       print_nbool(mode, (CONFIG_NBOOL_TABLE *) ptr);
     if (msg_verbose)
        vstream_fflush(VSTREAM_OUT);
 }
index ef605a5f7cbd68454813db4edaa52c6fcfa70f1f..3b35045d270b8b464edca7537dec2f4d33f16ced 100644 (file)
@@ -1,6 +1,10 @@
 SHELL  = /bin/sh
-SRCS   = postscreen.c postscreen_dict.c postscreen_dnsbl.c
-OBJS   = postscreen.o postscreen_dict.o postscreen_dnsbl.o
+SRCS   = postscreen.c postscreen_dict.c postscreen_dnsbl.c \
+       postscreen_early.c postscreen_smtpd.c postscreen_misc.c \
+       postscreen_state.c postscreen_tests.c postscreen_send.c
+OBJS   = postscreen.o postscreen_dict.o postscreen_dnsbl.o \
+       postscreen_early.o postscreen_smtpd.o postscreen_misc.o \
+       postscreen_state.o postscreen_tests.o postscreen_send.o
 HDRS   = 
 TESTSRC        =
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -60,13 +64,10 @@ depend: $(MAKES)
 postscreen.o: ../../include/addr_match_list.h
 postscreen.o: ../../include/argv.h
 postscreen.o: ../../include/attr.h
-postscreen.o: ../../include/connect.h
 postscreen.o: ../../include/data_redirect.h
 postscreen.o: ../../include/dict.h
 postscreen.o: ../../include/dict_cache.h
 postscreen.o: ../../include/events.h
-postscreen.o: ../../include/format_tv.h
-postscreen.o: ../../include/htable.h
 postscreen.o: ../../include/iostuff.h
 postscreen.o: ../../include/mail_conf.h
 postscreen.o: ../../include/mail_params.h
@@ -79,9 +80,7 @@ postscreen.o: ../../include/msg.h
 postscreen.o: ../../include/myaddrinfo.h
 postscreen.o: ../../include/mymalloc.h
 postscreen.o: ../../include/name_code.h
-postscreen.o: ../../include/sane_accept.h
 postscreen.o: ../../include/set_eugid.h
-postscreen.o: ../../include/stringops.h
 postscreen.o: ../../include/sys_defs.h
 postscreen.o: ../../include/vbuf.h
 postscreen.o: ../../include/vstream.h
@@ -92,6 +91,7 @@ postscreen_dict.o: ../../include/addr_match_list.h
 postscreen_dict.o: ../../include/argv.h
 postscreen_dict.o: ../../include/dict.h
 postscreen_dict.o: ../../include/dict_cache.h
+postscreen_dict.o: ../../include/events.h
 postscreen_dict.o: ../../include/match_list.h
 postscreen_dict.o: ../../include/match_ops.h
 postscreen_dict.o: ../../include/msg.h
@@ -124,3 +124,108 @@ postscreen_dnsbl.o: ../../include/vstream.h
 postscreen_dnsbl.o: ../../include/vstring.h
 postscreen_dnsbl.o: postscreen.h
 postscreen_dnsbl.o: postscreen_dnsbl.c
+postscreen_early.o: ../../include/addr_match_list.h
+postscreen_early.o: ../../include/argv.h
+postscreen_early.o: ../../include/dict.h
+postscreen_early.o: ../../include/dict_cache.h
+postscreen_early.o: ../../include/events.h
+postscreen_early.o: ../../include/mail_params.h
+postscreen_early.o: ../../include/match_list.h
+postscreen_early.o: ../../include/match_ops.h
+postscreen_early.o: ../../include/msg.h
+postscreen_early.o: ../../include/mymalloc.h
+postscreen_early.o: ../../include/stringops.h
+postscreen_early.o: ../../include/sys_defs.h
+postscreen_early.o: ../../include/vbuf.h
+postscreen_early.o: ../../include/vstream.h
+postscreen_early.o: ../../include/vstring.h
+postscreen_early.o: postscreen.h
+postscreen_early.o: postscreen_early.c
+postscreen_misc.o: ../../include/addr_match_list.h
+postscreen_misc.o: ../../include/argv.h
+postscreen_misc.o: ../../include/dict.h
+postscreen_misc.o: ../../include/dict_cache.h
+postscreen_misc.o: ../../include/events.h
+postscreen_misc.o: ../../include/format_tv.h
+postscreen_misc.o: ../../include/iostuff.h
+postscreen_misc.o: ../../include/mail_params.h
+postscreen_misc.o: ../../include/match_list.h
+postscreen_misc.o: ../../include/match_ops.h
+postscreen_misc.o: ../../include/msg.h
+postscreen_misc.o: ../../include/sys_defs.h
+postscreen_misc.o: ../../include/vbuf.h
+postscreen_misc.o: ../../include/vstream.h
+postscreen_misc.o: ../../include/vstring.h
+postscreen_misc.o: postscreen.h
+postscreen_misc.o: postscreen_misc.c
+postscreen_send.o: ../../include/addr_match_list.h
+postscreen_send.o: ../../include/argv.h
+postscreen_send.o: ../../include/connect.h
+postscreen_send.o: ../../include/dict.h
+postscreen_send.o: ../../include/dict_cache.h
+postscreen_send.o: ../../include/events.h
+postscreen_send.o: ../../include/iostuff.h
+postscreen_send.o: ../../include/match_list.h
+postscreen_send.o: ../../include/match_ops.h
+postscreen_send.o: ../../include/msg.h
+postscreen_send.o: ../../include/sys_defs.h
+postscreen_send.o: ../../include/vbuf.h
+postscreen_send.o: ../../include/vstream.h
+postscreen_send.o: ../../include/vstring.h
+postscreen_send.o: postscreen.h
+postscreen_send.o: postscreen_send.c
+postscreen_smtpd.o: ../../include/addr_match_list.h
+postscreen_smtpd.o: ../../include/argv.h
+postscreen_smtpd.o: ../../include/attr.h
+postscreen_smtpd.o: ../../include/dict.h
+postscreen_smtpd.o: ../../include/dict_cache.h
+postscreen_smtpd.o: ../../include/events.h
+postscreen_smtpd.o: ../../include/iostuff.h
+postscreen_smtpd.o: ../../include/mail_params.h
+postscreen_smtpd.o: ../../include/mail_proto.h
+postscreen_smtpd.o: ../../include/match_list.h
+postscreen_smtpd.o: ../../include/match_ops.h
+postscreen_smtpd.o: ../../include/msg.h
+postscreen_smtpd.o: ../../include/mymalloc.h
+postscreen_smtpd.o: ../../include/stringops.h
+postscreen_smtpd.o: ../../include/sys_defs.h
+postscreen_smtpd.o: ../../include/vbuf.h
+postscreen_smtpd.o: ../../include/vstream.h
+postscreen_smtpd.o: ../../include/vstring.h
+postscreen_smtpd.o: postscreen.h
+postscreen_smtpd.o: postscreen_smtpd.c
+postscreen_state.o: ../../include/addr_match_list.h
+postscreen_state.o: ../../include/argv.h
+postscreen_state.o: ../../include/attr.h
+postscreen_state.o: ../../include/dict.h
+postscreen_state.o: ../../include/dict_cache.h
+postscreen_state.o: ../../include/events.h
+postscreen_state.o: ../../include/iostuff.h
+postscreen_state.o: ../../include/mail_proto.h
+postscreen_state.o: ../../include/mail_server.h
+postscreen_state.o: ../../include/match_list.h
+postscreen_state.o: ../../include/match_ops.h
+postscreen_state.o: ../../include/msg.h
+postscreen_state.o: ../../include/mymalloc.h
+postscreen_state.o: ../../include/name_mask.h
+postscreen_state.o: ../../include/sys_defs.h
+postscreen_state.o: ../../include/vbuf.h
+postscreen_state.o: ../../include/vstream.h
+postscreen_state.o: ../../include/vstring.h
+postscreen_state.o: postscreen.h
+postscreen_state.o: postscreen_state.c
+postscreen_tests.o: ../../include/addr_match_list.h
+postscreen_tests.o: ../../include/argv.h
+postscreen_tests.o: ../../include/dict.h
+postscreen_tests.o: ../../include/dict_cache.h
+postscreen_tests.o: ../../include/events.h
+postscreen_tests.o: ../../include/mail_params.h
+postscreen_tests.o: ../../include/match_list.h
+postscreen_tests.o: ../../include/match_ops.h
+postscreen_tests.o: ../../include/msg.h
+postscreen_tests.o: ../../include/sys_defs.h
+postscreen_tests.o: ../../include/vbuf.h
+postscreen_tests.o: ../../include/vstream.h
+postscreen_tests.o: ../../include/vstring.h
+postscreen_tests.o: postscreen.h
+postscreen_tests.o: postscreen_tests.c
index 38d534b5d96a429d9e3a861b0367e14726bda6d6..7ea057e06f69d88e08bbce8ff4d6ce6ad9c76d15 100644 (file)
 /* DESCRIPTION
 /*     The Postfix \fBpostscreen\fR(8) server performs triage on
 /*     multiple inbound SMTP connections in parallel. While
-/*     \fBpostscreen\fR(8) keeps zombies and other bogus clients
-/*     away from Postfix SMTP server processes, more Postfix SMTP
-/*     server processes remain available for legitimate clients.
-/* GENERAL OPERATION
-/* .ad
-/* .fi
-/*     The triage process involves a number of tests, in the order
-/*     as described below.  Some tests introduce a delay of a few
-/*     seconds.  Once a client passes all tests, its IP address
-/*     is temporarily excluded from the tests, typically for 24
-/*     hours.  This minimizes the impact of the tests on legitimate
-/*     mail clients.
+/*     \fBpostscreen\fR(8) keeps spambots away from Postfix SMTP
+/*     server processes, more Postfix SMTP server processes remain
+/*     available for legitimate clients.
 /*
-/*     After logging the result of its tests, \fBpostscreen\fR(8)
+/*     \fBpostscreen\fR(8) maintains a temporary whitelist of
+/*     positive decisions. Once an SMTP client is whitelisted, it
+/*     is immediately forwarded to a real Postfix SMTP server
+/*     process without further checking.
+/*
+/*     By default, the program logs only statistics, and it does
+/*     not run any tests against clients in mynetworks (primarily,
+/*     to avoid problems with non-standard SMTP implementations
+/*     in network appliances and test programs).  \fBpostscreen\fR(8)
 /*     by default forwards all connections to a real SMTP server
 /*     process. This mode is useful for non-destructive testing.
 /*
 /*     In a typical production setting, \fBpostscreen\fR(8) is
-/*     configured to disconnect clients that fail some tests.  A
-/*     future implementation may pass the connection to a dummy
-/*     SMTP protocol engine that logs sender and recipient information
-/*     before hanging up.
-/*
-/*     Note: \fBpostscreen\fR(8) is not an SMTP proxy; this is
-/*     intentional. The purpose is to prioritize legitimate clients
-/*     with as little overhead as possible.
-/* .SH 1. PERMANENT WHITELIST TEST
-/* .ad
-/* .fi
-/*     The postscreen_whitelist_networks parameter (default:
-/*     $mynetworks) specifies a permanent whitelist for SMTP client
-/*     IP addresses.
-/*
-/*     When the SMTP client address matches the permanent whitelist,
-/*     this is logged as:
-/* .sp
-/* .nf
-/*     \fBWHITELISTED \fIaddress\fR
-/* .fi
-/* .sp
-/*     The action is not configurable: immediately forward the
-/*     connection to a real SMTP server process.
-/* .SH 2. PERMANENT BLACKLIST TEST
-/* .ad
-/* .fi
-/*     The postscreen_blacklist_networks parameter (default: empty)
-/*     specifies a permanent blacklist for SMTP client IP addresses.
-/*     The address syntax is as with mynetworks.
-/*
-/*     When the SMTP client address matches the permanent blacklist,
-/*     this is logged as:
-/* .sp
-/* .nf
-/*     \fBBLACKLISTED \fIaddress\fR
-/* .fi
-/* .sp
-/*     The postscreen_blacklist_action parameter specifies the
-/*     action that is taken next:
-/* .IP "\fBcontinue\fR (default)"
-/*     Continue with the SMTP GREETING PHASE TESTS below.
-/* .IP \fBdrop\fR
-/*     Drop the connection immediately with a 521 SMTP reply.  In
-/*     a future implementation, the connection may instead be
-/*     passed to a dummy SMTP protocol engine that logs sender and
-/*     recipient information.
-/* .SH 3. TEMPORARY WHITELIST TEST
-/* .ad
-/* .fi
-/*     The \fBpostscreen\fR(8) daemon maintains a \fItemporary\fR
-/*     whitelist for SMTP client IP addresses that have passed all
-/*     the tests described below. The postscreen_cache_map parameter
-/*     specifies the location of the temporary whitelist.  The
-/*     temporary whitelist is not used for SMTP client addresses
-/*     that appear on the \fIpermanent\fR blacklist or whitelist.
-/*
-/*     When the SMTP client address appears on the temporary
-/*     whitelist, this is logged as:
-/* .sp
-/* .nf
-/*     \fBPASS OLD \fIaddress\fR
-/* .fi
-/* .sp
-/*     The action is not configurable: immediately forward the
-/*     connection to a real SMTP server process.  The client is
-/*     excluded from further tests until its temporary whitelist
-/*     entry expires, as controlled with the postscreen_cache_ttl
-/*     parameter.  Expired entries are silently renewed if possible.
-/* .SH 4. SMTP GREETING PHASE TESTS
-/* .ad
-/* .fi
-/*     The postscreen_greet_wait parameter specifies a time interval
-/*     during which \fBpostscreen\fR(8) runs a number of tests in
-/*     parallel.  These tests are described below, and are run
-/*     before the client may see the real SMTP server's "220
-/*     text..." server greeting.
-/*
-/*     When the SMTP client passes all greeting-phase tests, this
-/*     is logged as:
-/* .sp
-/* .nf
-/*     \fBPASS NEW \fIaddress\fR
-/* .fi
-/* .sp
-/*     The action is to forward the connection to a real SMTP
-/*     server process and to create a temporary whitelist entry
-/*     that excludes the client IP address from further tests until
-/*     the temporary whitelist entry expires, as controlled with
-/*     the postscreen_cache_ttl parameter.
-/*
-/*     In a future implementation, the connection may first be passed to
-/*     a dummy SMTP protocol engine that implements more protocol
-/*     tests including greylisting, before the client is allowed
-/*     to talk to a real SMTP server process.
-/* .SH 4A. PREGREET TEST
-/* .ad
-/* .fi
-/*     The postscreen_greet_banner parameter specifies the \fItext\fR
-/*     portion of a "220-\fItext\fR..." teaser banner (default:
-/*     $smtpd_banner).
-/*     The \fBpostscreen\fR(8) daemon sends this before the
-/*     postscreen_greet_wait timer is started.  The purpose of the
-/*     teaser banner is to confuse SPAM clients so that they speak
-/*     before their turn. It has no effect on SMTP clients that
-/*     correctly implement the protocol.
-/*
-/*     To avoid problems with broken SMTP engines in network
-/*     appliances, either exclude them from all tests with the
-/*     postscreen_whitelist_networks feature or else specify an
-/*     empty postscreen_greet_banner value to disable the "220-text..."
-/*     teaser banner.
-/*
-/*     When an SMTP client sends a command before the
-/*     postscreen_greet_wait time has elapsed, this is logged as:
-/* .sp
-/* .nf
-/*     \fBPREGREET \fIcount \fBafter \fItime \fBfrom \fIaddress text...\fR
-/* .fi
-/* .sp
-/*     Translation: the client at \fIaddress\fR sent \fIcount\fR
-/*     bytes before its turn to speak, and this happened \fItime\fR
-/*     seconds after the postscreen_greet_wait timer was started.
-/*     The \fItext\fR is what the client sent (truncated to 100
-/*     bytes, and with non-printable characters replaced with "?").
-/*
-/*     The postscreen_greet_action parameter specifies the action
-/*     that is taken next:
-/* .IP "\fBcontinue\fR (default)"
-/*     Wait until the postscreen_greet_wait time has elapsed, then
-/*     report DNSBL lookup results if applicable. Either perform
-/*     DNSBL-related actions or forward the connection to a real
-/*     SMTP server process.
-/* .IP \fBdrop\fR
-/*     Drop the connection immediately with a 521 SMTP reply.
-/*     In a future implementation, the connection may instead be passed
-/*     to a dummy SMTP protocol engine that logs sender and recipient
+/*     configured to reject mail from clients that fail one or
+/*     more tests, after logging the helo, sender and recipient
 /*     information.
-/* .SH 4B. HANGUP TEST
-/* .ad
-/* .fi
-/*     When the SMTP client hangs up without sending any data
-/*     before the postscreen_greet_wait time has elapsed, this is
-/*     logged as:
-/* .sp
-/* .nf
-/*     \fBHANGUP after \fItime \fBfrom \fIaddress\fR
-/* .fi
-/* .sp
-/*     The postscreen_hangup_action specifies the action
-/*     that is taken next:
-/* .IP "\fBcontinue\fR (default)"
-/*     Wait until the postscreen_greet_wait time has elapsed, then
-/*     report DNSBL lookup results if applicable. Do not forward
-/*     the broken connection to a real SMTP server process.
-/* .IP \fBdrop\fR
-/*     Drop the connection immediately.
-/* .SH 4C. DNS BLOCKLIST TEST
-/* .ad
-/* .fi
-/*     The postscreen_dnsbl_sites parameter (default: empty)
-/*     specifies a list of DNS blocklist servers. These lookups
-/*     are made in parallel.
 /*
-/*     When the postscreen_greet_wait time has elapsed, and the
-/*     combined DNSBL score is equal to or greater than the
-/*     postscreen_dnsbl_threshold parameter value, this is logged
-/*     as:
-/* .sp
-/* .nf
-/*     \fBDNSBL rank \fIcount \fBfor \fIaddress\fR
-/* .fi
-/* .sp
-/*     Translation: the SMTP client at \fIaddress\fR has a combined
-/*     DNSBL score of \fIcount\fR.
-/*
-/*     The postscreen_dnsbl_action parameter specifies the action
-/*     that is taken when the combined DNSBL score is equal to or
-/*     greater than the threshold:
-/* .IP "\fBcontinue\fR (default)"
-/*     Forward the connection to a real SMTP server process.
-/* .IP \fBdrop\fR
-/*     Drop the connection immediately with a 521 SMTP reply.
-/*     In a future implementation, the connection may instead be passed
-/*     to a dummy SMTP protocol engine that logs sender and recipient
-/*     information.
+/*     \fBpostscreen\fR(8) is not an SMTP proxy; this is intentional.
+/*     The purpose is to keep spambots away from Postfix SMTP
+/*     server processes, not to control traffic flows.
 /* SECURITY
 /* .ad
 /* .fi
 /*     RFC 2920 (SMTP Pipelining)
 /* DIAGNOSTICS
 /*     Problems and transactions are logged to \fBsyslogd\fR(8).
+/* BUGS
+/*     When successful tests involve \fBpostscreen\fR(8)'s built-in
+/*     SMTP protocol engine, \fBpostscreen\fR(8) adds the client
+/*     to the temporary whitelist but it cannot not hand off the
+/*     "live" connection from a good SMTP client to a Postfix SMTP
+/*     server process.  Instead, \fBpostscreen\fR(8) defers attempts
+/*     to deliver mail with a 4XX status, and waits for the client
+/*     to disconnect.  The next time a good client connects, it
+/*     will be allowed to talk to a Postfix SMTP server process
+/*     to deliver mail.
 /* CONFIGURATION PARAMETERS
 /* .ad
 /* .fi
 /*
 /*     The text below provides only a parameter summary. See
 /*     \fBpostconf\fR(5) for more details including examples.
+/*
+/*     NOTE: Some parameters implement stress-dependent behavior.
+/*     This is supported only when the default value is stress-dependent
+/*     (${stress?X}${stress:Y}). Other parameters always evaluate
+/*     as if the stress value is the empty string.
 /* TRIAGE PARAMETERS
 /* .ad
 /* .fi
-/* .IP "\fBpostscreen_blacklist_action (continue)\fR"
+/* .IP "\fBpostscreen_bare_newline_action (ignore)\fR"
+/*     The action that \fBpostscreen\fR(8) takes when an SMTP client sends
+/*     a bare newline character, that is, a newline not preceded by carriage
+/*     return.
+/* .IP "\fBpostscreen_bare_newline_enable (no)\fR"
+/*     Enable "bare newline" SMTP protocol tests in the \fBpostscreen\fR(8)
+/*     server.
+/* .IP "\fBpostscreen_blacklist_action (ignore)\fR"
 /*     The action that \fBpostscreen\fR(8) takes when an SMTP client is
 /*     permanently blacklisted with the postscreen_blacklist_networks
 /*     parameter.
 /* .IP "\fBpostscreen_blacklist_networks (empty)\fR"
 /*     Network addresses that are permanently blacklisted; see the
 /*     postscreen_blacklist_action parameter for possible actions.
-/* .IP "\fBpostscreen_dnsbl_action (continue)\fR"
+/* .IP "\fBpostscreen_disable_vrfy_command ($disable_vrfy_command)\fR"
+/*     Disable the SMTP VRFY command in the \fBpostscreen\fR(8) daemon.
+/* .IP "\fBpostscreen_dnsbl_action (ignore)\fR"
 /*     The action that \fBpostscreen\fR(8) takes when an SMTP client's combined
 /*     DNSBL score is equal to or greater than a threshold (as defined
 /*     with the postscreen_dnsbl_sites and postscreen_dnsbl_threshold
 /*     parameters).
+/* .IP "\fBpostscreen_dnsbl_reply_map (empty)\fR"
+/*     A mapping from actual DNSBL domain name which includes a secret
+/*     password, to the DNSBL domain name that postscreen will reply with
+/*     when it rejects mail.
 /* .IP "\fBpostscreen_dnsbl_sites (empty)\fR"
 /*     Optional list of DNS blocklist domains, filters and weight
 /*     factors.
 /*     The inclusive lower bound for blocking an SMTP client, based on
 /*     its combined DNSBL score as defined with the postscreen_dnsbl_sites
 /*     parameter.
-/* .IP "\fBpostscreen_greet_action (continue)\fR"
+/* .IP "\fBpostscreen_forbidden_commands ($smtpd_forbidden_commands)\fR"
+/*     List of commands that \fBpostscreen\fR(8) server considers in violation
+/*     of the SMTP protocol.
+/* .IP "\fBpostscreen_greet_action (ignore)\fR"
 /*     The action that \fBpostscreen\fR(8) takes when an SMTP client speaks
 /*     before its turn within the time specified with the postscreen_greet_wait
 /*     parameter.
 /*     \fBpostscreen\fR(8) sends ahead of the real Postfix SMTP server's "220
 /*     text..." response, in an attempt to confuse bad SMTP clients so
 /*     that they speak before their turn (pre-greet).
-/* .IP "\fBpostscreen_greet_wait (4s)\fR"
+/* .IP "\fBpostscreen_greet_wait (${stress?2}${stress:6}s)\fR"
 /*     The amount of time that \fBpostscreen\fR(8) will wait for an SMTP
 /*     client to send a command before its turn, and for DNS blocklist
-/*     lookup results to arrive.
-/* .IP "\fBpostscreen_hangup_action (continue)\fR"
-/*     The action that \fBpostscreen\fR(8) takes when an SMTP client disconnects
-/*     without sending data, within the time specified with the
-/*     postscreen_greet_wait parameter.
-/* .IP "\fBpostscreen_post_queue_limit ($default_process_limit)\fR"
-/*     The number of clients that can be waiting for service from a
-/*     real SMTP server process.
-/* .IP "\fBpostscreen_pre_queue_limit ($default_process_limit)\fR"
-/*     The number of non-whitelisted clients that can be waiting for
-/*     a decision whether they will receive service from a real SMTP server
-/*     process.
+/*     lookup results to arrive (default: 2 seconds under stress, 6 seconds
+/*     normally).
+/* .IP "\fBpostscreen_helo_required ($smtpd_helo_required)\fR"
+/*     Require that a remote SMTP client sends HELO or EHLO before
+/*     commencing a MAIL transaction.
+/* .IP "\fBpostscreen_non_smtp_command_action (drop)\fR"
+/*     The action that \fBpostscreen\fR(8) takes when an SMTP client sends
+/*     non-SMTP commands as specified with the postscreen_forbidden_commands
+/*     parameter.
+/* .IP "\fBpostscreen_non_smtp_command_enable (no)\fR"
+/*     Enable "non-SMTP command" tests in the \fBpostscreen\fR(8) server.
+/* .IP "\fBpostscreen_pipelining_action (enforce)\fR"
+/*     The action that \fBpostscreen\fR(8) takes when an SMTP client sends
+/*     multiple commands instead of sending one command and waiting for
+/*     the server to respond.
+/* .IP "\fBpostscreen_pipelining_enable (no)\fR"
+/*     Enable "pipelining" SMTP protocol tests in the \fBpostscreen\fR(8)
+/*     server.
 /* .IP "\fBpostscreen_whitelist_networks ($mynetworks)\fR"
 /*     Network addresses that are permanently whitelisted, and that
 /*     will not be subjected to \fBpostscreen\fR(8) checks.
-/* .IP "\fBsmtpd_service (smtpd)\fR"
+/* .IP "\fBsmtpd_service_name (smtpd)\fR"
 /*     The internal service that \fBpostscreen\fR(8) forwards allowed
 /*     connections to.
 /* CACHE CONTROLS
 /*     The amount of time between \fBpostscreen\fR(8) cache cleanup runs.
 /* .IP "\fBpostscreen_cache_map (btree:$data_directory/ps_cache)\fR"
 /*     Persistent storage for the \fBpostscreen\fR(8) server decisions.
-/* .IP "\fBpostscreen_cache_retention_time (1d)\fR"
+/* .IP "\fBpostscreen_cache_retention_time (7d)\fR"
 /*     The amount of time that \fBpostscreen\fR(8) will cache an expired
 /*     temporary whitelist entry before it is removed.
-/* .IP "\fBpostscreen_cache_ttl (1d)\fR"
-/*     The amount of time that \fBpostscreen\fR(8) will cache a decision for
-/*     a specific SMTP client IP address.
+/* .IP "\fBpostscreen_bare_newline_ttl (30d)\fR"
+/*     The amount of time that \fBpostscreen\fR(8) will cache results from
+/*     a successful "bare newline" SMTP protocol test.
+/* .IP "\fBpostscreen_dnsbl_ttl (1d)\fR"
+/*     The amount of time that \fBpostscreen\fR(8) will cache results from
+/*     a successful DNS blocklist test.
+/* .IP "\fBpostscreen_greet_ttl (1d)\fR"
+/*     The amount of time that \fBpostscreen\fR(8) will cache results from
+/*     a successful PREGREET test.
+/* .IP "\fBpostscreen_non_smtp_command_ttl (30d)\fR"
+/*     The amount of time that \fBpostscreen\fR(8) will cache results from
+/*     a successful "non_smtp_command" SMTP protocol test.
+/* .IP "\fBpostscreen_pipelining_ttl (30d)\fR"
+/*     The amount of time that \fBpostscreen\fR(8) will cache results from
+/*     a successful "pipelining" SMTP protocol test.
+/* RESOURCE CONTROLS
+/* .ad
+/* .fi
+/* .IP "\fBline_length_limit (2048)\fR"
+/*     Upon input, long lines are chopped up into pieces of at most
+/*     this length; upon delivery, long lines are reconstructed.
+/* .IP "\fBpostscreen_command_count_limit (20)\fR"
+/*     The limit on the total number of commands per SMTP session for
+/*     \fBpostscreen\fR(8)'s built-in SMTP protocol engine.
+/* .IP "\fBpostscreen_command_time_limit (${stress?10}${stress:300}s)\fR"
+/*     The command "read" time limit for \fBpostscreen\fR(8)'s built-in SMTP
+/*     protocol engine.
+/* .IP "\fBpostscreen_post_queue_limit ($default_process_limit)\fR"
+/*     The number of clients that can be waiting for service from a
+/*     real SMTP server process.
+/* .IP "\fBpostscreen_pre_queue_limit ($default_process_limit)\fR"
+/*     The number of non-whitelisted clients that can be waiting for
+/*     a decision whether they will receive service from a real SMTP server
+/*     process.
+/* .IP "\fBpostscreen_watchdog_timeout (10s)\fR"
+/*     How much time a \fBpostscreen\fR(8) process may take to respond to
+/*     an SMTP client command or to perform a cache operation before it
+/*     is terminated by a built-in watchdog timer.
 /* MISCELLANEOUS CONTROLS
 /* .ad
 /* .fi
 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
 /*     The default location of the Postfix main.cf and master.cf
 /*     configuration files.
-/* .IP "\fBdaemon_timeout (18000s)\fR"
-/*     How much time a Postfix daemon process may take to handle a
-/*     request before it is terminated by a built-in watchdog timer.
 /* .IP "\fBdelay_logging_resolution_limit (2)\fR"
 /*     The maximal number of digits after the decimal point when logging
 /*     sub-second delay values.
 /*     smtpd(8), Postfix SMTP server
 /*     dnsblog(8), temporary DNS helper
 /*     syslogd(8), system logging
+/* README FILES
+/* .ad
+/* .fi
+/*     Use "\fBpostconf readme_directory\fR" or "\fBpostconf
+/*     html_directory\fR" to locate this information.
+/* .nf
+/* .na
+/*     POSTSCREEN_README, Postfix Postscreen Howto
 /* LICENSE
 /* .ad
 /* .fi
 #include <sys/stat.h>
 #include <stdlib.h>
 
-#ifdef STRCASECMP_IN_STRINGS_H
-#include <strings.h>
-#endif
-
-#ifdef MISSING_STRTOUL
-#define strtoul strtol
-#endif
-
 /* Utility library. */
 
 #include <msg.h>
-#include <connect.h>
-#include <iostuff.h>
-#include <events.h>
 #include <mymalloc.h>
+#include <events.h>
 #include <myaddrinfo.h>
 #include <dict_cache.h>
-#include <sane_accept.h>
-#include <stringops.h>
 #include <set_eugid.h>
 #include <vstream.h>
-#include <format_tv.h>
-#include <htable.h>
 #include <name_code.h>
 
 /* Global library. */
 #include <mail_params.h>
 #include <mail_version.h>
 #include <mail_proto.h>
-#include <addr_match_list.h>
 #include <data_redirect.h>
+#include <string_list.h>
 
 /* Master server protocols. */
 
  /*
   * Configuration parameters.
   */
+int     var_proc_limit;
 char   *var_smtpd_service;
 char   *var_smtpd_banner;
+char   *var_smtpd_forbid_cmds;
+bool    var_disable_vrfy_cmd;
+bool    var_helo_required;
+
+char   *var_ps_forbid_cmds;
+
+bool    var_ps_disable_vrfy;
+bool    var_ps_helo_required;
+
 char   *var_ps_cache_map;
+int     var_ps_cache_scan;
+int     var_ps_cache_ret;
 int     var_ps_post_queue_limit;
 int     var_ps_pre_queue_limit;
-int     var_proc_limit;
-int     var_ps_cache_ttl;
-int     var_ps_cache_ret;
-int     var_ps_cache_scan;
-int     var_ps_greet_wait;
-char   *var_ps_dnsbl_sites;
-char   *var_ps_dnsbl_action;
-char   *var_ps_greet_action;
-char   *var_ps_hangup_action;
+int     var_ps_watchdog;
+
 char   *var_ps_wlist_nets;
 char   *var_ps_blist_nets;
-char   *var_ps_greet_banner;
 char   *var_ps_blist_action;
-int     var_ps_dnsbl_thresh;
-
- /*
-  * Per-session state. See also: new_session_state() and free_event_state()
-  * below.
-  */
-typedef struct {
-    int     flags;                     /* see below */
-    VSTREAM *smtp_client_stream;       /* remote SMTP client */
-    int     smtp_server_fd;            /* real SMTP server */
-    char   *smtp_client_addr;          /* client address */
-    char   *smtp_client_port;          /* client port */
-    struct timeval creation_time;      /* as the name says */
-} PS_STATE;
-
-#define PS_FLAG_NOCACHE                (1<<0)  /* don't store this client */
-#define PS_FLAG_NOFORWARD      (1<<1)  /* don't forward this session */
-#define PS_FLAG_EXPIRED                (1<<2)  /* whitelist expired */
-#define PS_FLAG_WHITELISTED    (1<<3)  /* whitelisted (temp or perm) */
-
- /*
-  * This program screens all inbound SMTP connections, so it better not waste
-  * time.
-  */
-#define PS_SEND_SOCK_CONNECT_TIMEOUT   1
-#define PS_SEND_SOCK_NOTIFY_TIMEOUT    100
-
-#define PS_READ_BUF_SIZE               1024
-
-#define DNSBL_SERVICE                  "dnsblog"
-#define DNSBLOG_TIMEOUT                        10
-
-#define PS_ACT_DROP                    1
-#define PS_ACT_CONT                    2
-
-static int check_queue_length;         /* connections being checked */
-static int post_queue_length;          /* being sent to real SMTPD */
-static DICT_CACHE *cache_map;          /* cache table handle */
-static VSTRING *temp;                  /* scratchpad */
-static char *smtp_service_name;                /* path to real SMTPD */
-static char *teaser_greeting;          /* spamware teaser banner */
-static int dnsbl_action;               /* PS_ACT_DROP or PS_ACT_CONT */
-static int greet_action;               /* PS_ACT_DROP or PS_ACT_CONT */
-static int hangup_action;              /* PS_ACT_DROP or PS_ACT_CONT */
-static ADDR_MATCH_LIST *wlist_nets;    /* permanently whitelisted networks */
-static ADDR_MATCH_LIST *blist_nets;    /* permanently blacklisted networks */
-static int blist_action;               /* PS_ACT_DROP or PS_ACT_CONT */
-
-/* new_session_state - fill in connection state for event processing */
-
-static PS_STATE *new_session_state(VSTREAM *stream, const char *addr,
-                                          const char *port)
-{
-    PS_STATE *state;
-
-    state = (PS_STATE *) mymalloc(sizeof(*state));
-    state->flags = 0;
-    if ((state->smtp_client_stream = stream) != 0)
-       check_queue_length++;
-    state->smtp_server_fd = (-1);
-    state->smtp_client_addr = mystrdup(addr);
-    state->smtp_client_port = mystrdup(port);
-    GETTIMEOFDAY(&state->creation_time);
-    return (state);
-}
 
-/* free_session_state - destroy connection state including connections */
-
-static void free_session_state(PS_STATE *state)
-{
-    if (state->smtp_client_stream != 0) {
-       event_server_disconnect(state->smtp_client_stream);
-       check_queue_length--;
-    }
-    if (state->smtp_server_fd >= 0) {
-       close(state->smtp_server_fd);
-       post_queue_length--;
-    }
-    myfree(state->smtp_client_addr);
-    myfree(state->smtp_client_port);
-    myfree((char *) state);
-
-    if (check_queue_length < 0 || post_queue_length < 0)
-       msg_panic("bad queue length: check_queue=%d, post_queue=%d",
-                 check_queue_length, post_queue_length);
-}
-
-/* mydelta_time - pretty-formatted delta time */
-
-static char *mydelta_time(VSTRING *buf, struct timeval tv, int *delta)
-{
-    DELTA_TIME pdelay;
-    struct timeval now;
-
-    GETTIMEOFDAY(&now);
-    PS_CALC_DELTA(pdelay, now, tv);
-    VSTRING_RESET(buf);
-    format_tv(buf, pdelay.dt_sec, pdelay.dt_usec, SIG_DIGS, var_delay_max_res);
-    *delta = pdelay.dt_sec;
-    return (STR(buf));
-}
-
-/* smtp_reply - reply to the client */
-
-static int smtp_reply(int smtp_client_fd, const char *smtp_client_addr,
-                             const char *smtp_client_port, const char *text)
-{
-    int     ret;
-
-    /*
-     * XXX Need to make sure that the TCP send buffer is large enough for any
-     * response, so that a nasty client can't cause this process to block.
-     */
-    ret = (write_buf(smtp_client_fd, text, strlen(text), 1) < 0);
-    if (ret != 0 && errno != EPIPE)
-       msg_warn("write %s:%s: %m", smtp_client_addr, smtp_client_port);
-    return (ret);
-}
-
-/* send_socket_close_event - file descriptor has arrived or timeout */
-
-static void send_socket_close_event(int event, char *context)
-{
-    const char *myname = "send_socket_close_event";
-    PS_STATE *state = (PS_STATE *) context;
-
-    if (msg_verbose)
-       msg_info("%s: sq=%d cq=%d event %d on send socket %d from %s:%s",
-                myname, post_queue_length, check_queue_length,
-                event, state->smtp_server_fd, state->smtp_client_addr,
-                state->smtp_client_port);
-
-    /*
-     * The real SMTP server has closed the local IPC channel, or we have
-     * reached the limit of our patience. In the latter case it is still
-     * possible that the real SMTP server will receive the socket so we
-     * should not interfere.
-     */
-    CLEAR_EVENT_REQUEST(state->smtp_server_fd, send_socket_close_event, context);
-
-    switch (event) {
-    case EVENT_TIME:
-       msg_warn("timeout sending connection to service %s", smtp_service_name);
-       break;
-    default:
-       break;
-    }
-    free_session_state(state);
-}
-
-/* send_socket - send socket to real smtpd */
-
-static void send_socket(PS_STATE *state)
-{
-    const char *myname = "send_socket";
-    int     window_size;
+char   *var_ps_greet_ttl;
+int     var_ps_greet_wait;
 
-    if (msg_verbose)
-       msg_info("%s: sq=%d cq=%d send socket %d from %s:%s",
-                myname, post_queue_length, check_queue_length,
-        vstream_fileno(state->smtp_client_stream), state->smtp_client_addr,
-                state->smtp_client_port);
+char   *var_ps_pregr_banner;
+char   *var_ps_pregr_action;
+int     var_ps_pregr_ttl;
 
-    /*
-     * This is where we would adjust the window size to a value that is
-     * appropriate for this client class.
-     */
-#if 0
-    window_size = 65535;
-    if (setsockopt(vstream_fileno(state->smtp_client_stream), SOL_SOCKET, SO_RCVBUF,
-                  (char *) &window_size, sizeof(window_size)) < 0)
-       msg_warn("setsockopt SO_RCVBUF %d: %m", window_size);
-#endif
-
-    /*
-     * Connect to the real SMTP service over a local IPC channel, send the
-     * file descriptor, and close the file descriptor to save resources.
-     * Experience has shown that some systems will discard information when
-     * we close a channel immediately after writing. Thus, we waste resources
-     * waiting for the remote side to close the local IPC channel first. The
-     * good side of waiting is that we learn when the real SMTP server is
-     * falling behind.
-     * 
-     * This is where we would forward the connection to an SMTP server that
-     * provides an appropriate level of service for this client class. For
-     * example, a server that is more forgiving, or one that is more
-     * suspicious. Alternatively, we could send attributes along with the
-     * socket with client reputation information, making everything even more
-     * Postfix-specific.
-     */
-    if ((state->smtp_server_fd = LOCAL_CONNECT(smtp_service_name, NON_BLOCKING,
-                                      PS_SEND_SOCK_CONNECT_TIMEOUT)) < 0) {
-       msg_warn("cannot connect to service %s: %m", smtp_service_name);
-       smtp_reply(vstream_fileno(state->smtp_client_stream),
-                  state->smtp_client_addr, state->smtp_client_port,
-                  "421 4.3.2 All server ports are busy\r\n");
-       free_session_state(state);
-       return;
-    }
-    post_queue_length++;
-    if (LOCAL_SEND_FD(state->smtp_server_fd,
-                     vstream_fileno(state->smtp_client_stream)) < 0) {
-       msg_warn("cannot pass connection to service %s: %m", smtp_service_name);
-       smtp_reply(vstream_fileno(state->smtp_client_stream), state->smtp_client_addr,
-             state->smtp_client_port, "421 4.3.2 No system resources\r\n");
-       free_session_state(state);
-       return;
-    } else {
+char   *var_ps_dnsbl_sites;
+char   *var_ps_dnsbl_reply;
+int     var_ps_dnsbl_thresh;
+char   *var_ps_dnsbl_action;
+int     var_ps_dnsbl_ttl;
 
-       /*
-        * Closing the smtp_client_fd here triggers a FreeBSD 7.1 kernel bug
-        * where smtp-source sometimes sees the connection being closed after
-        * it has already received the real SMTP server's 220 greeting!
-        */
-#if 0
-       event_server_disconnect(state->smtp_client_stream);
-       state->smtp_client_stream = 0;
-       check_queue_length--;
-#endif
-       READ_EVENT_REQUEST(state->smtp_server_fd, send_socket_close_event,
-                          (char *) state, PS_SEND_SOCK_NOTIFY_TIMEOUT);
-       return;
-    }
-}
+bool    var_ps_pipel_enable;
+char   *var_ps_pipel_action;
+int     var_ps_pipel_ttl;
 
-/* smtp_early_event - handle pre-greet, EOF or timeout. */
+bool    var_ps_nsmtp_enable;
+char   *var_ps_nsmtp_action;
+int     var_ps_nsmtp_ttl;
 
-static void smtp_early_event(int event, char *context)
-{
-    const char *myname = "smtp_early_event";
-    PS_STATE *state = (PS_STATE *) context;
-    char    read_buf[PS_READ_BUF_SIZE];
-    int     read_count;
-    int     dnsbl_score;
-    int     elapsed;
-    int     action;
+bool    var_ps_barlf_enable;
+char   *var_ps_barlf_action;
+int     var_ps_barlf_ttl;
 
-    if (msg_verbose)
-       msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from %s:%s",
-                myname, post_queue_length, check_queue_length,
-                event, vstream_fileno(state->smtp_client_stream),
-                state->smtp_client_addr, state->smtp_client_port);
+int     var_ps_cmd_count;
+char   *var_ps_cmd_time;
 
-    /*
-     * Either the remote SMTP client spoke before its turn, the connection
-     * was closed, or we reached the limit of our patience.
-     */
-    CLEAR_EVENT_REQUEST(vstream_fileno(state->smtp_client_stream),
-                       smtp_early_event, context);
+ /*
+  * Global variables.
+  */
+int     ps_check_queue_length;         /* connections being checked */
+int     ps_post_queue_length;          /* being sent to real SMTPD */
+DICT_CACHE *ps_cache_map;              /* cache table handle */
+VSTRING *ps_temp;                      /* scratchpad */
+char   *ps_smtpd_service_name;         /* path to real SMTPD */
+int     ps_pregr_action;               /* PS_ACT_DROP/ENFORCE/etc */
+int     ps_dnsbl_action;               /* PS_ACT_DROP/ENFORCE/etc */
+int     ps_pipel_action;               /* PS_ACT_DROP/ENFORCE/etc */
+int     ps_nsmtp_action;               /* PS_ACT_DROP/ENFORCE/etc */
+int     ps_barlf_action;               /* PS_ACT_DROP/ENFORCE/etc */
+int     ps_min_ttl;                    /* Update with new tests! */
+int     ps_max_ttl;                    /* Update with new tests! */
+STRING_LIST *ps_forbid_cmds;           /* CONNECT GET POST */
+int     ps_stress_greet_wait;          /* stressed greet wait */
+int     ps_normal_greet_wait;          /* stressed greet wait */
+int     ps_stress_cmd_time_limit;      /* stressed command limit */
+int     ps_normal_cmd_time_limit;      /* normal command time limit */
+int     ps_stress;                     /* stress level */
+int     ps_check_queue_length_lowat;   /* stress low-water mark */
+int     ps_check_queue_length_hiwat;   /* stress high-water mark */
+DICT   *ps_dnsbl_reply;                        /* DNSBL name mapper */
 
-    /*
-     * If this session ends here, we MUST read the blocklist cache otherwise
-     * we have a memory leak.
-     */
-    switch (event) {
-
-       /*
-        * The SMTP client did not speak before its turn. If it is DNS
-        * blocklisted, drop the connection, or continue and forward the
-        * connection to the real SMTP server.
-        * 
-        * This is where we would use a dummy SMTP protocol engine to further
-        * investigate whether the client cuts corners in the protocol,
-        * before allowing it to talk to a real SMTP server process.
-        */
-    case EVENT_TIME:
-       if (*var_ps_dnsbl_sites)
-           dnsbl_score = ps_dnsbl_retrieve(state->smtp_client_addr);
-       else
-           dnsbl_score = 0;
-       if (dnsbl_score >= var_ps_dnsbl_thresh) {
-           msg_info("DNSBL rank %d for %s",
-                    dnsbl_score, state->smtp_client_addr);
-           if (dnsbl_action == PS_ACT_DROP) {
-               smtp_reply(vstream_fileno(state->smtp_client_stream),
-                          state->smtp_client_addr, state->smtp_client_port,
-                          "521 5.7.1 Blocked by DNSBL\r\n");
-               state->flags |= PS_FLAG_NOFORWARD;
-           }
-           state->flags |= PS_FLAG_NOCACHE;
-       }
-       if (state->flags & PS_FLAG_NOFORWARD) {
-           free_session_state(state);
-       } else {
-           if ((state->flags & PS_FLAG_NOCACHE) == 0) {
-               msg_info("PASS %s %s", (state->flags & PS_FLAG_EXPIRED) ?
-                        "OLD" : "NEW", state->smtp_client_addr);
-               if (cache_map != 0) {
-                   vstring_sprintf(temp, "%ld", (long) event_time());
-                   ps_cache_update(cache_map, state->smtp_client_addr, STR(temp));
-               }
-           }
-           send_socket(state);
-       }
-       break;
-
-       /*
-        * EOF or the client spoke before its turn. We simply drop the
-        * connection, or we continue waiting and allow DNS replies to
-        * trickle in.
-        * 
-        * This is where we would use a dummy SMTP protocol engine to obtain the
-        * sender and recipient information before dropping a pregreeter's
-        * connection.
-        */
-    default:
-       if ((read_count = recv(vstream_fileno(state->smtp_client_stream),
-                          read_buf, sizeof(read_buf) - 1, MSG_PEEK)) > 0) {
-           read_buf[read_count] = 0;
-           msg_info("PREGREET %d after %s from %s: %.100s", read_count,
-                    mydelta_time(temp, state->creation_time, &elapsed),
-                    state->smtp_client_addr, printable(read_buf, '?'));
-           action = greet_action;
-           if (action == PS_ACT_DROP)
-               smtp_reply(vstream_fileno(state->smtp_client_stream),
-                          state->smtp_client_addr, state->smtp_client_port,
-                          "521 5.5.1 Protocol error\r\n");
-       } else {
-           msg_info("HANGUP after %s from %s",
-                    mydelta_time(temp, state->creation_time, &elapsed),
-                    state->smtp_client_addr);
-           action = hangup_action;
-           state->flags |= PS_FLAG_NOFORWARD;
-       }
-       if (action == PS_ACT_DROP) {
-           if (*var_ps_dnsbl_sites)
-               (void) ps_dnsbl_retrieve(state->smtp_client_addr);
-           free_session_state(state);
-       } else {
-           state->flags |= PS_FLAG_NOCACHE;
-           /* not: ps_dnsbl_retrieve */
-           if (elapsed > var_ps_greet_wait)
-               elapsed = var_ps_greet_wait;
-           event_request_timer(smtp_early_event, context,
-                               var_ps_greet_wait - elapsed);
-       }
-       break;
-    }
-}
+ /*
+  * Local variables.
+  */
+static ADDR_MATCH_LIST *ps_wlist_nets; /* permanently whitelisted networks */
+static ADDR_MATCH_LIST *ps_blist_nets; /* permanently blacklisted networks */
+static int ps_blist_action;            /* PS_ACT_DROP/ENFORCE/etc */
 
-/* postscreen_dump - dump some statistics before exit */
+/* ps_dump - dump some statistics before exit */
 
-static void postscreen_dump(void)
+static void ps_dump(void)
 {
 
     /*
@@ -763,15 +371,15 @@ static void postscreen_dump(void)
      * distinguish between "postfix reload" (we should restart) or "maximal
      * idle time reached" (we could finish the cache cleanup first).
      */
-    if (cache_map) {
-       dict_cache_close(cache_map);
-       cache_map = 0;
+    if (ps_cache_map) {
+       dict_cache_close(ps_cache_map);
+       ps_cache_map = 0;
     }
 }
 
-/* postscreen_drain - delayed exit after "postfix reload" */
+/* ps_drain - delayed exit after "postfix reload" */
 
-static void postscreen_drain(char *unused_service, char **unused_argv)
+static void ps_drain(char *unused_service, char **unused_argv)
 {
     int     count;
 
@@ -792,9 +400,9 @@ static void postscreen_drain(char *unused_service, char **unused_argv)
      * XXX Some Berkeley DB versions break with close-after-fork. Every new
      * version is an improvement over its predecessor.
      */
-    if (cache_map != 0) {
-       dict_cache_close(cache_map);
-       cache_map = 0;
+    if (ps_cache_map != 0) {
+       dict_cache_close(ps_cache_map);
+       ps_cache_map = 0;
     }
     for (count = 0; /* see below */ ; count++) {
        if (count >= 5) {
@@ -809,13 +417,13 @@ static void postscreen_drain(char *unused_service, char **unused_argv)
     }
 }
 
-/* postscreen_service - handle new client connection */
+/* ps_service - handle new client connection */
 
-static void postscreen_service(VSTREAM *smtp_client_stream,
-                                      char *unused_service,
-                                      char **unused_argv)
+static void ps_service(VSTREAM *smtp_client_stream,
+                              char *unused_service,
+                              char **unused_argv)
 {
-    const char *myname = "postscreen_service";
+    const char *myname = "ps_service";
     PS_STATE *state;
     struct sockaddr_storage addr_storage;
     SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage);
@@ -823,20 +431,21 @@ static void postscreen_service(VSTREAM *smtp_client_stream,
     MAI_SERVPORT_STR smtp_client_port;
     int     aierr;
     const char *stamp_str;
-    time_t  stamp_time;
     int     window_size;
-    int     state_flags = 0;
+    int     saved_flags;
 
     /*
      * This program handles all incoming connections, so it must not block.
      * We use event-driven code for all operations that introduce latency.
-     * 
-     * We use the event_server framework. This means we get already-accepted
-     * connections wrapped into a VSTREAM so we have to invoke getpeername()
-     * to find out the remote address and port.
      */
+    non_blocking(vstream_fileno(smtp_client_stream), NON_BLOCKING);
 
-#define PS_SERVICE_GIVEUP_AND_RETURN(stream) do { \
+    /*
+     * We use the event_server framework. This means we get already-accepted
+     * connections so we have to invoke getpeername() to find out the remote
+     * address and port.
+     */
+#define PS_SERVICE_DISCONNECT_AND_RETURN(stream) do { \
        event_server_disconnect(stream); \
        return; \
     } while (0);
@@ -847,10 +456,10 @@ static void postscreen_service(VSTREAM *smtp_client_stream,
     if (getpeername(vstream_fileno(smtp_client_stream), (struct sockaddr *)
                    & addr_storage, &addr_storage_len) < 0) {
        msg_warn("getpeername: %m");
-       smtp_reply(vstream_fileno(smtp_client_stream),
-                  "unknown_address", "unknown_port",
-                  "421 4.3.2 No system resources\r\n");
-       PS_SERVICE_GIVEUP_AND_RETURN(smtp_client_stream);
+       ps_send_reply(vstream_fileno(smtp_client_stream),
+                     "unknown_address", "unknown_port",
+                     "421 4.3.2 No system resources\r\n");
+       PS_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream);
     }
 
     /*
@@ -862,104 +471,122 @@ static void postscreen_service(VSTREAM *smtp_client_stream,
                                      &smtp_client_port, 0)) != 0) {
        msg_warn("cannot convert client address/port to string: %s",
                 MAI_STRERROR(aierr));
-       smtp_reply(vstream_fileno(smtp_client_stream),
-                  "unknown_address", "unknown_port",
-                  "421 4.3.2 No system resources\r\n");
-       PS_SERVICE_GIVEUP_AND_RETURN(smtp_client_stream);
+       ps_send_reply(vstream_fileno(smtp_client_stream),
+                     "unknown_address", "unknown_port",
+                     "421 4.3.2 No system resources\r\n");
+       PS_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream);
     }
     if (strncasecmp("::ffff:", smtp_client_addr.buf, 7) == 0)
        memmove(smtp_client_addr.buf, smtp_client_addr.buf + 7,
                sizeof(smtp_client_addr.buf) - 7);
-    if (msg_verbose)
+    if (msg_verbose > 1)
        msg_info("%s: sq=%d cq=%d connect from %s:%s",
-                myname, post_queue_length, check_queue_length,
+                myname, ps_post_queue_length, ps_check_queue_length,
                 smtp_client_addr.buf, smtp_client_port.buf);
 
+    /*
+     * Bundle up all the loose session pieces. This zeroes all flags and time
+     * stamps.
+     */
+    state = ps_new_session_state(smtp_client_stream, smtp_client_addr.buf,
+                                smtp_client_port.buf);
+
     /*
      * Reply with 421 when we can't forward more connections.
      */
     if (var_ps_post_queue_limit > 0
-       && post_queue_length >= var_ps_post_queue_limit) {
+       && ps_post_queue_length >= var_ps_post_queue_limit) {
        msg_info("reject: connect from %s:%s: all server ports busy",
-                smtp_client_addr.buf, smtp_client_port.buf);
-       smtp_reply(vstream_fileno(smtp_client_stream),
-                  smtp_client_addr.buf, smtp_client_port.buf,
-                  "421 4.3.2 All server ports are busy\r\n");
-       PS_SERVICE_GIVEUP_AND_RETURN(smtp_client_stream);
+                state->smtp_client_addr, state->smtp_client_port);
+       PS_DROP_SESSION_STATE(state,
+                             "421 4.3.2 All server ports are busy\r\n");
+       return;
     }
 
     /*
      * The permanent whitelist has highest precedence (never block mail from
-     * whitelisted sites).
+     * whitelisted sites, and never run tests against those sites).
      */
-    if (wlist_nets != 0
-       && ps_addr_match_list_match(wlist_nets, smtp_client_addr.buf) != 0) {
-       msg_info("WHITELISTED %s", smtp_client_addr.buf);
-       state_flags |= PS_FLAG_WHITELISTED;
+    if (ps_wlist_nets != 0
+      && ps_addr_match_list_match(ps_wlist_nets, state->smtp_client_addr)) {
+       msg_info("WHITELISTED %s", state->smtp_client_addr);
+       ps_conclude(state);
+       return;
     }
 
     /*
      * The permanent blacklist has second precedence. If the client is
      * permanently blacklisted, send some generic reply and hang up
-     * immediately, or torture them a little longer.
+     * immediately, or run more tests for logging purposes.
      */
-    else if (blist_nets != 0
-       && ps_addr_match_list_match(blist_nets, smtp_client_addr.buf) != 0) {
-       msg_info("BLACKLISTED %s", smtp_client_addr.buf);
-       if (blist_action == PS_ACT_DROP) {
-           smtp_reply(vstream_fileno(smtp_client_stream),
-                      smtp_client_addr.buf, smtp_client_port.buf,
-                      "521 5.3.2 Service currently not available\r\n");
-           PS_SERVICE_GIVEUP_AND_RETURN(smtp_client_stream);
+    if (ps_blist_nets != 0
+      && ps_addr_match_list_match(ps_blist_nets, state->smtp_client_addr)) {
+       msg_info("BLACKLISTED %s", state->smtp_client_addr);
+       PS_FAIL_SESSION_STATE(state, PS_STATE_FLAG_BLIST_FAIL);
+       switch (ps_blist_action) {
+       case PS_ACT_DROP:
+           PS_DROP_SESSION_STATE(state,
+                            "521 5.3.2 Service currently unavailable\r\n");
+           return;
+       case PS_ACT_ENFORCE:
+           PS_ENFORCE_SESSION_STATE(state,
+                            "550 5.3.2 Service currently unavailable\r\n");
+           break;
+       case PS_ACT_IGNORE:
+           PS_UNFAIL_SESSION_STATE(state, PS_STATE_FLAG_BLIST_FAIL);
+           /* Not: PS_PASS_SESSION_STATE. Repeat this test the next time. */
+           break;
+       default:
+           msg_panic("%s: unknown pregreet action value %d",
+                     myname, ps_blist_action);
        }
     }
 
     /*
-     * Finally, the temporary whitelist (i.e. the postscreen cache) has the
-     * lowest precedence.
+     * The temporary whitelist (i.e. the postscreen cache) has the lowest
+     * precedence. This cache contains information about the results of prior
+     * tests. Whitelist the client when all enabled test results are still
+     * valid.
      */
-    else if (cache_map != 0
-    && (stamp_str = ps_cache_lookup(cache_map, smtp_client_addr.buf)) != 0) {
-       stamp_time = strtoul(stamp_str, 0, 10);
-       if (stamp_time > event_time() - var_ps_cache_ttl) {
-           msg_info("PASS OLD %s", smtp_client_addr.buf);
-           state_flags |= PS_FLAG_WHITELISTED;
-       } else
-           state_flags |= PS_FLAG_EXPIRED;
-    }
-
-    /*
-     * If the client is permanently or temporarily whitelisted, send the
-     * socket to the real SMTP service and get out of the way.
-     */
-    if (state_flags & PS_FLAG_WHITELISTED) {
-       state = new_session_state(smtp_client_stream, smtp_client_addr.buf,
-                                 smtp_client_port.buf);
-       send_socket(state);
-       return;
+    if ((state->flags & PS_STATE_FLAG_ANY_FAIL) == 0
+       && ps_cache_map != 0
+       && (stamp_str = ps_cache_lookup(ps_cache_map, state->smtp_client_addr)) != 0) {
+       saved_flags = state->flags;
+       ps_parse_tests(state, stamp_str, event_time());
+       state->flags |= saved_flags;
+       if (msg_verbose)
+           msg_info("%s: cached + recent flags: %s",
+                    myname, ps_print_state_flags(state->flags, myname));
+       if ((state->flags & PS_STATE_FLAG_ANY_TODO) == 0) {
+           msg_info("PASS OLD %s", state->smtp_client_addr);
+           ps_conclude(state);
+           return;
+       }
+    } else {
+       saved_flags = state->flags;
+       ps_new_tests(state);
+       state->flags |= saved_flags;
+       if (msg_verbose)
+           msg_info("%s: new + recent flags: %s",
+                    myname, ps_print_state_flags(state->flags, myname));
     }
 
     /*
      * Reply with 421 when we can't analyze more connections.
      */
     if (var_ps_pre_queue_limit > 0
-      && check_queue_length - post_queue_length >= var_ps_pre_queue_limit) {
+       && ps_check_queue_length - ps_post_queue_length >= var_ps_pre_queue_limit) {
        msg_info("reject: connect from %s:%s: all screening ports busy",
-                smtp_client_addr.buf, smtp_client_port.buf);
-       smtp_reply(vstream_fileno(smtp_client_stream),
-                  smtp_client_addr.buf, smtp_client_port.buf,
-                  "421 4.3.2 All screening ports are busy\r\n");
-       PS_SERVICE_GIVEUP_AND_RETURN(smtp_client_stream);
+                state->smtp_client_addr, state->smtp_client_port);
+       PS_DROP_SESSION_STATE(state,
+                             "421 4.3.2 All screening ports are busy\r\n");
+       return;
     }
 
     /*
-     * If the client has no cached decision, send half the greeting banner,
-     * by way of teaser, then wait briefly to see if the client speaks before
-     * its turn.
-     * 
-     * Before sending the banner we could set the TCP window to the smallest
-     * possible value to save some network bandwidth, at least with spamware
-     * that waits until the server starts speaking.
+     * Before commencing the tests we could set the TCP window to the
+     * smallest possible value to save some network bandwidth, at least with
+     * spamware that waits until the server starts speaking.
      */
 #if 0
     window_size = 1;
@@ -967,42 +594,37 @@ static void postscreen_service(VSTREAM *smtp_client_stream,
                   (char *) &window_size, sizeof(window_size)) < 0)
        msg_warn("setsockopt SO_RCVBUF %d: %m", window_size);
 #endif
-    if (teaser_greeting != 0
-     && smtp_reply(vstream_fileno(smtp_client_stream), smtp_client_addr.buf,
-                  smtp_client_port.buf, teaser_greeting) != 0)
-       PS_SERVICE_GIVEUP_AND_RETURN(smtp_client_stream);
-
-    state = new_session_state(smtp_client_stream, smtp_client_addr.buf,
-                             smtp_client_port.buf);
-    state->flags |= state_flags;
-    READ_EVENT_REQUEST(vstream_fileno(state->smtp_client_stream),
-                      smtp_early_event, (char *) state, var_ps_greet_wait);
 
     /*
-     * Run a DNS blocklist query while we wait for the client to respond.
+     * If the client has no up-to-date results for some tests, do those tests
+     * first. Otherwise, skip the tests and hand off the connection.
      */
-    if (*var_ps_dnsbl_sites)
-       ps_dnsbl_request(smtp_client_addr.buf);
+    if (state->flags & PS_STATE_FLAG_EARLY_TODO)
+       ps_early_tests(state);
+    else if (state->flags & (PS_STATE_FLAG_SMTPD_TODO | PS_STATE_FLAG_NOFORWARD))
+       ps_smtpd_tests(state);
+    else
+       ps_conclude(state);
 }
 
-/* postscreen_cache_validator - validate one cache entry */
+/* ps_cache_validator - validate one cache entry */
 
-static int postscreen_cache_validator(const char *client_addr,
-                                             const char *stamp_str,
-                                             char *unused_context)
+static int ps_cache_validator(const char *client_addr,
+                                     const char *stamp_str,
+                                     char *unused_context)
 {
-    time_t  stamp_time;
+    PS_STATE dummy;
 
     /*
      * This function is called by the cache cleanup pseudo thread.
      * 
-     * XX Eliminate code duplication and abstract the parser into a separate
-     * routine.
-     * 
-     * Don't report a client as "NEW" just because their cache entry expired.
+     * When an entry is removed from the cache, the client will be reported as
+     * "NEW" in the next session where it passes all tests again. To avoid
+     * silly logging we remove the cache entry only after all tests have
+     * expired longer ago than the cache retention time.
      */
-    stamp_time = strtoul(stamp_str, 0, 10);
-    return (event_time() < stamp_time + var_ps_cache_ttl + var_ps_cache_ret);
+    ps_parse_tests(&dummy, stamp_str, event_time() - var_ps_cache_ret);
+    return ((dummy.flags & PS_STATE_FLAG_ANY_TODO) == 0);
 }
 
 /* pre_jail_init - pre-jail initialization */
@@ -1012,16 +634,19 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
     VSTRING *redirect;
 
     /*
-     * Open read-only maps as before dropping privilege, for consistency with
+     * Open read-only maps before dropping privilege, for consistency with
      * other Postfix daemons.
      */
     if (*var_ps_wlist_nets)
-       wlist_nets =
-           addr_match_list_init(MATCH_FLAG_NONE, var_ps_wlist_nets);
+       ps_wlist_nets = addr_match_list_init(MATCH_FLAG_NONE, var_ps_wlist_nets);
 
     if (*var_ps_blist_nets)
-       blist_nets =
-           addr_match_list_init(MATCH_FLAG_NONE, var_ps_blist_nets);
+       ps_blist_nets = addr_match_list_init(MATCH_FLAG_NONE, var_ps_blist_nets);
+    if (*var_ps_forbid_cmds)
+       ps_forbid_cmds = string_list_init(MATCH_FLAG_NONE, var_ps_forbid_cmds);
+    if (*var_ps_dnsbl_reply)
+       ps_dnsbl_reply = dict_open(var_ps_dnsbl_reply, O_RDONLY,
+                                  DICT_FLAG_DUP_WARN);
 
     /*
      * Never, ever, get killed by a master signal, as that would corrupt the
@@ -1042,15 +667,15 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
 
     /*
      * Keep state in persistent external map. As a safety measure we sync the
-     * database on each update. This hurts on LINUX systems that sync all
-     * their dirty disk blocks whenever any application invokes fsync().
+     * database on each update. This hurts on LINUX file systems that sync
+     * all dirty disk blocks whenever any application invokes fsync().
      * 
      * Start the cache maintenance pseudo thread after dropping privileges.
      */
 #define PS_DICT_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE)
 
     if (*var_ps_cache_map)
-       cache_map =
+       ps_cache_map =
            dict_cache_open(data_redirect_map(redirect, var_ps_cache_map),
                            O_CREAT | O_RDWR, PS_DICT_OPEN_FLAGS);
 
@@ -1066,8 +691,10 @@ static void pre_jail_init(char *unused_name, char **unused_argv)
 static void post_jail_init(char *unused_name, char **unused_argv)
 {
     const NAME_CODE actions[] = {
-       "drop", PS_ACT_DROP,
-       "continue", PS_ACT_CONT,
+       PS_NAME_ACT_DROP, PS_ACT_DROP,
+       PS_NAME_ACT_ENFORCE, PS_ACT_ENFORCE,
+       PS_NAME_ACT_IGNORE, PS_ACT_IGNORE,
+       PS_NAME_ACT_CONT, PS_ACT_IGNORE,/* compatibility */
        0, -1,
     };
     int     cache_flags;
@@ -1082,26 +709,31 @@ static void post_jail_init(char *unused_name, char **unused_argv)
     /*
      * Other one-time initialization.
      */
-    temp = vstring_alloc(10);
-    vstring_sprintf(temp, "%s/%s", MAIL_CLASS_PRIVATE, var_smtpd_service);
-    smtp_service_name = mystrdup(STR(temp));
-    if (*var_ps_greet_banner) {
-       vstring_sprintf(temp, "220-%s\r\n", var_ps_greet_banner);
-       teaser_greeting = mystrdup(STR(temp));
-    }
+    ps_temp = vstring_alloc(10);
+    vstring_sprintf(ps_temp, "%s/%s", MAIL_CLASS_PRIVATE, var_smtpd_service);
+    ps_smtpd_service_name = mystrdup(STR(ps_temp));
     ps_dnsbl_init();
-    if ((blist_action = name_code(actions, NAME_CODE_FLAG_NONE,
-                                 var_ps_blist_action)) < 0)
+    ps_early_init();
+    ps_smtpd_init();
+
+    if ((ps_blist_action = name_code(actions, NAME_CODE_FLAG_NONE,
+                                    var_ps_blist_action)) < 0)
        msg_fatal("bad %s value: %s", VAR_PS_BLIST_ACTION, var_ps_blist_action);
-    if ((dnsbl_action = name_code(actions, NAME_CODE_FLAG_NONE,
-                                 var_ps_dnsbl_action)) < 0)
+    if ((ps_dnsbl_action = name_code(actions, NAME_CODE_FLAG_NONE,
+                                    var_ps_dnsbl_action)) < 0)
        msg_fatal("bad %s value: %s", VAR_PS_DNSBL_ACTION, var_ps_dnsbl_action);
-    if ((greet_action = name_code(actions, NAME_CODE_FLAG_NONE,
-                                 var_ps_greet_action)) < 0)
-       msg_fatal("bad %s value: %s", VAR_PS_GREET_ACTION, var_ps_greet_action);
-    if ((hangup_action = name_code(actions, NAME_CODE_FLAG_NONE,
-                                  var_ps_hangup_action)) < 0)
-       msg_fatal("bad %s value: %s", VAR_PS_HUP_ACTION, var_ps_hangup_action);
+    if ((ps_pregr_action = name_code(actions, NAME_CODE_FLAG_NONE,
+                                    var_ps_pregr_action)) < 0)
+       msg_fatal("bad %s value: %s", VAR_PS_PREGR_ACTION, var_ps_pregr_action);
+    if ((ps_pipel_action = name_code(actions, NAME_CODE_FLAG_NONE,
+                                    var_ps_pipel_action)) < 0)
+       msg_fatal("bad %s value: %s", VAR_PS_PIPEL_ACTION, var_ps_pipel_action);
+    if ((ps_nsmtp_action = name_code(actions, NAME_CODE_FLAG_NONE,
+                                    var_ps_nsmtp_action)) < 0)
+       msg_fatal("bad %s value: %s", VAR_PS_NSMTP_ACTION, var_ps_nsmtp_action);
+    if ((ps_barlf_action = name_code(actions, NAME_CODE_FLAG_NONE,
+                                    var_ps_barlf_action)) < 0)
+       msg_fatal("bad %s value: %s", VAR_PS_BARLF_ACTION, var_ps_barlf_action);
 
     /*
      * Start the cache maintenance pseudo thread last. Early cleanup makes
@@ -1109,15 +741,49 @@ static void post_jail_init(char *unused_name, char **unused_argv)
      * the cleanup thread runs).
      */
     cache_flags = DICT_CACHE_FLAG_STATISTICS;
-    if (msg_verbose)
+    if (msg_verbose > 1)
        cache_flags |= DICT_CACHE_FLAG_VERBOSE;
-    if (cache_map != 0 && var_ps_cache_scan > 0)
-       dict_cache_control(cache_map,
+    if (ps_cache_map != 0 && var_ps_cache_scan > 0)
+       dict_cache_control(ps_cache_map,
                           DICT_CACHE_CTL_FLAGS, cache_flags,
                           DICT_CACHE_CTL_INTERVAL, var_ps_cache_scan,
-                      DICT_CACHE_CTL_VALIDATOR, postscreen_cache_validator,
+                          DICT_CACHE_CTL_VALIDATOR, ps_cache_validator,
                           DICT_CACHE_CTL_CONTEXT, (char *) 0,
                           DICT_CACHE_CTL_END);
+
+    /*
+     * Pre-compute the minimal and maximal TTL.
+     */
+    ps_min_ttl =
+       PS_MIN(PS_MIN(var_ps_pregr_ttl, var_ps_dnsbl_ttl),
+              PS_MIN(PS_MIN(var_ps_pipel_ttl, var_ps_nsmtp_ttl),
+                     var_ps_barlf_ttl));
+    ps_max_ttl =
+       PS_MAX(PS_MAX(var_ps_pregr_ttl, var_ps_dnsbl_ttl),
+              PS_MAX(PS_MAX(var_ps_pipel_ttl, var_ps_nsmtp_ttl),
+                     var_ps_barlf_ttl));
+
+    /*
+     * Pre-compute the stress and normal command time limits.
+     */
+    mail_conf_update(VAR_STRESS, "yes");
+    ps_stress_cmd_time_limit =
+       get_mail_conf_time(VAR_PS_CMD_TIME, DEF_PS_CMD_TIME, 1, 0);
+    ps_stress_greet_wait =
+       get_mail_conf_time(VAR_PS_GREET_WAIT, DEF_PS_GREET_WAIT, 1, 0);
+
+    mail_conf_update(VAR_STRESS, "");
+    ps_normal_cmd_time_limit =
+       get_mail_conf_time(VAR_PS_CMD_TIME, DEF_PS_CMD_TIME, 1, 0);
+    ps_normal_greet_wait =
+       get_mail_conf_time(VAR_PS_GREET_WAIT, DEF_PS_GREET_WAIT, 1, 0);
+
+    ps_check_queue_length_lowat = .7 * var_ps_pre_queue_limit;
+    ps_check_queue_length_hiwat = .9 * var_ps_pre_queue_limit;
+    if (msg_verbose)
+       msg_info(VAR_PS_CMD_TIME ": stress=%d normal=%d lowat=%d hiwat=%d",
+                ps_stress_cmd_time_limit, ps_normal_cmd_time_limit,
+                ps_check_queue_length_lowat, ps_check_queue_length_hiwat);
 }
 
 MAIL_VERSION_STAMP_DECLARE;
@@ -1126,35 +792,68 @@ MAIL_VERSION_STAMP_DECLARE;
 
 int     main(int argc, char **argv)
 {
+
+    /*
+     * List smtpd(8) parameters before any postscreen(8) parameters that have
+     * defaults dependencies on them.
+     */
     static const CONFIG_STR_TABLE str_table[] = {
        VAR_SMTPD_SERVICE, DEF_SMTPD_SERVICE, &var_smtpd_service, 1, 0,
-       VAR_PS_CACHE_MAP, DEF_PS_CACHE_MAP, &var_ps_cache_map, 0, 0,
        VAR_SMTPD_BANNER, DEF_SMTPD_BANNER, &var_smtpd_banner, 1, 0,
+       VAR_SMTPD_FORBID_CMDS, DEF_SMTPD_FORBID_CMDS, &var_smtpd_forbid_cmds, 0, 0,
+       VAR_PS_CACHE_MAP, DEF_PS_CACHE_MAP, &var_ps_cache_map, 0, 0,
+       VAR_PS_PREGR_BANNER, DEF_PS_PREGR_BANNER, &var_ps_pregr_banner, 0, 0,
+       VAR_PS_PREGR_ACTION, DEF_PS_PREGR_ACTION, &var_ps_pregr_action, 1, 0,
        VAR_PS_DNSBL_SITES, DEF_PS_DNSBL_SITES, &var_ps_dnsbl_sites, 0, 0,
-       VAR_PS_GREET_ACTION, DEF_PS_GREET_ACTION, &var_ps_greet_action, 1, 0,
        VAR_PS_DNSBL_ACTION, DEF_PS_DNSBL_ACTION, &var_ps_dnsbl_action, 1, 0,
-       VAR_PS_HUP_ACTION, DEF_PS_HUP_ACTION, &var_ps_hangup_action, 1, 0,
+       VAR_PS_PIPEL_ACTION, DEF_PS_PIPEL_ACTION, &var_ps_pipel_action, 1, 0,
+       VAR_PS_NSMTP_ACTION, DEF_PS_NSMTP_ACTION, &var_ps_nsmtp_action, 1, 0,
+       VAR_PS_BARLF_ACTION, DEF_PS_BARLF_ACTION, &var_ps_barlf_action, 1, 0,
        VAR_PS_WLIST_NETS, DEF_PS_WLIST_NETS, &var_ps_wlist_nets, 0, 0,
        VAR_PS_BLIST_NETS, DEF_PS_BLIST_NETS, &var_ps_blist_nets, 0, 0,
-       VAR_PS_GREET_BANNER, DEF_PS_GREET_BANNER, &var_ps_greet_banner, 0, 0,
        VAR_PS_BLIST_ACTION, DEF_PS_BLIST_ACTION, &var_ps_blist_action, 1, 0,
+       VAR_PS_FORBID_CMDS, DEF_PS_FORBID_CMDS, &var_ps_forbid_cmds, 0, 0,
+       VAR_PS_DNSBL_REPLY, DEF_PS_DNSBL_REPLY, &var_ps_dnsbl_reply, 0, 0,
        0,
     };
     static const CONFIG_INT_TABLE int_table[] = {
        VAR_PROC_LIMIT, DEF_PROC_LIMIT, &var_proc_limit, 1, 0,
        VAR_PS_DNSBL_THRESH, DEF_PS_DNSBL_THRESH, &var_ps_dnsbl_thresh, 0, 0,
+       VAR_PS_CMD_COUNT, DEF_PS_CMD_COUNT, &var_ps_cmd_count, 1, 0,
        0,
     };
     static const CONFIG_NINT_TABLE nint_table[] = {
-       VAR_PS_POST_QLIMIT, DEF_PS_POST_QLIMIT, &var_ps_post_queue_limit, 1, 0,
-       VAR_PS_PRE_QLIMIT, DEF_PS_PRE_QLIMIT, &var_ps_pre_queue_limit, 1, 0,
+       VAR_PS_POST_QLIMIT, DEF_PS_POST_QLIMIT, &var_ps_post_queue_limit, 10, 0,
+       VAR_PS_PRE_QLIMIT, DEF_PS_PRE_QLIMIT, &var_ps_pre_queue_limit, 10, 0,
        0,
     };
     static const CONFIG_TIME_TABLE time_table[] = {
-       VAR_PS_CACHE_TTL, DEF_PS_CACHE_TTL, &var_ps_cache_ttl, 1, 0,
        VAR_PS_GREET_WAIT, DEF_PS_GREET_WAIT, &var_ps_greet_wait, 1, 0,
-       VAR_PS_CACHE_RET, DEF_PS_CACHE_RET, &var_ps_cache_ret, 0, 0,
-       VAR_PS_CACHE_SCAN, DEF_PS_CACHE_SCAN, &var_ps_cache_scan, 0, 0,
+       VAR_PS_PREGR_TTL, DEF_PS_PREGR_TTL, &var_ps_pregr_ttl, 1, 0,
+       VAR_PS_DNSBL_TTL, DEF_PS_DNSBL_TTL, &var_ps_dnsbl_ttl, 1, 0,
+       VAR_PS_PIPEL_TTL, DEF_PS_PIPEL_TTL, &var_ps_pipel_ttl, 1, 0,
+       VAR_PS_NSMTP_TTL, DEF_PS_NSMTP_TTL, &var_ps_nsmtp_ttl, 1, 0,
+       VAR_PS_BARLF_TTL, DEF_PS_BARLF_TTL, &var_ps_barlf_ttl, 1, 0,
+       VAR_PS_CACHE_RET, DEF_PS_CACHE_RET, &var_ps_cache_ret, 1, 0,
+       VAR_PS_CACHE_SCAN, DEF_PS_CACHE_SCAN, &var_ps_cache_scan, 1, 0,
+       VAR_PS_WATCHDOG, DEF_PS_WATCHDOG, &var_ps_watchdog, 10, 0,
+       0,
+    };
+    static const CONFIG_BOOL_TABLE bool_table[] = {
+       VAR_HELO_REQUIRED, DEF_HELO_REQUIRED, &var_helo_required,
+       VAR_DISABLE_VRFY_CMD, DEF_DISABLE_VRFY_CMD, &var_disable_vrfy_cmd,
+       VAR_PS_PIPEL_ENABLE, DEF_PS_PIPEL_ENABLE, &var_ps_pipel_enable,
+       VAR_PS_NSMTP_ENABLE, DEF_PS_NSMTP_ENABLE, &var_ps_nsmtp_enable,
+       VAR_PS_BARLF_ENABLE, DEF_PS_BARLF_ENABLE, &var_ps_barlf_enable,
+       0,
+    };
+    static const CONFIG_RAW_TABLE raw_table[] = {
+       VAR_PS_CMD_TIME, DEF_PS_CMD_TIME, &var_ps_cmd_time, 1, 0,
+       0,
+    };
+    static const CONFIG_NBOOL_TABLE nbool_table[] = {
+       VAR_PS_HELO_REQUIRED, DEF_PS_HELO_REQUIRED, &var_ps_helo_required,
+       VAR_PS_DISABLE_VRFY, DEF_PS_DISABLE_VRFY, &var_ps_disable_vrfy,
        0,
     };
 
@@ -1163,15 +862,19 @@ int     main(int argc, char **argv)
      */
     MAIL_VERSION_STAMP_ALLOCATE;
 
-    event_server_main(argc, argv, postscreen_service,
+    event_server_main(argc, argv, ps_service,
                      MAIL_SERVER_STR_TABLE, str_table,
                      MAIL_SERVER_INT_TABLE, int_table,
                      MAIL_SERVER_NINT_TABLE, nint_table,
                      MAIL_SERVER_TIME_TABLE, time_table,
+                     MAIL_SERVER_BOOL_TABLE, bool_table,
+                     MAIL_SERVER_RAW_TABLE, raw_table,
+                     MAIL_SERVER_NBOOL_TABLE, nbool_table,
                      MAIL_SERVER_PRE_INIT, pre_jail_init,
                      MAIL_SERVER_POST_INIT, post_jail_init,
                      MAIL_SERVER_SOLITARY,
-                     MAIL_SERVER_SLOW_EXIT, postscreen_drain,
-                     MAIL_SERVER_EXIT, postscreen_dump,
+                     MAIL_SERVER_SLOW_EXIT, ps_drain,
+                     MAIL_SERVER_EXIT, ps_dump,
+                     MAIL_SERVER_WATCHDOG, &var_ps_watchdog,
                      0);
 }
index b834db986c88b5607d2d50e3edcedbcc304eeab9..2c357e737987adb84ff2f4eee358093ac878edc9 100644 (file)
 /* DESCRIPTION
 /* .nf
 
+ /*
+  * System library.
+  */
+
  /*
   * Utility library.
   */
 #include <dict_cache.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <events.h>
 
  /*
   * Global library.
   */
 #include <addr_match_list.h>
+#include <string_list.h>
+
+ /*
+  * Preliminary stuff, to be fixed.
+  */
+#define PS_READ_BUF_SIZE       1024
+
+ /*
+  * Per-session state.
+  */
+typedef struct {
+    int     flags;                     /* see below */
+    /* Socket state. */
+    VSTREAM *smtp_client_stream;       /* remote SMTP client */
+    int     smtp_server_fd;            /* real SMTP server */
+    char   *smtp_client_addr;          /* client address */
+    char   *smtp_client_port;          /* client port */
+    const char *final_reply;           /* cause for hanging up */
+    /* Test context. */
+    struct timeval start_time;         /* start of current test */
+    const char *test_name;             /* name of current test */
+    /* Before-handshake tests. */
+    time_t  pregr_stamp;               /* pregreet expiration time */
+    time_t  dnsbl_stamp;               /* dnsbl expiration time */
+    VSTRING *dnsbl_reply;              /* dnsbl reject text */
+    /* Built-in SMTP protocol engine. */
+    time_t  pipel_stamp;               /* pipelining expiration time */
+    time_t  nsmtp_stamp;               /* non-smtp command expiration time */
+    time_t  barlf_stamp;               /* bare newline expiration time */
+    const char *rcpt_reply;            /* how to reject recipients */
+    int     command_count;             /* error + junk command count */
+    const char *protocol;              /* SMTP or ESMTP */
+    char   *helo_name;                 /* SMTP helo/ehlo */
+    char   *sender;                    /* MAIL FROM */
+    VSTRING *cmd_buffer;               /* command read buffer */
+    int     read_state;                        /* command read state machine */
+} PS_STATE;
+
+#define PS_TIME_STAMP_NEW              (0)     /* test was never passed */
+#define PS_TIME_STAMP_DISABLED         (1)     /* never passed but disabled */
+#define PS_TIME_STAMP_INVALID          (-1)    /* must not be cached */
+
+#define PS_STATE_FLAG_NOFORWARD                (1<<0)  /* don't forward this session */
+#define PS_STATE_FLAG_UNUSED1          (1<<1)  /* use me! */
+#define PS_STATE_FLAG_UNUSED2          (1<<2)  /* use me! */
+#define PS_STATE_FLAG_NEW              (1<<3)  /* some test was never passed */
+#define PS_STATE_FLAG_BLIST_FAIL       (1<<4)  /* blacklisted */
+#define PS_STATE_FLAG_HANGUP           (1<<5)  /* NOT a test failure */
+#define PS_STATE_FLAG_CACHE_EXPIRED    (1<<6)  /* cache retention expired */
+
+ /*
+  * Important: every MUMBLE_TODO flag must have a MUMBLE_PASS flag, such that
+  * MUMBLE_PASS == PS_STATE_FLAGS_TODO_TO_PASS(MUMBLE_TODO).
+  * 
+  * MUMBLE_TODO flags must not be cleared once they are raised. The code in
+  * ps_conclude() depends on this when it decides that all unfinished tests
+  * are completed.
+  */
+#define PS_STATE_FLAGS_TODO_TO_PASS(todo_flags) ((todo_flags) >> 1)
+
+ /* Room here for one more before-handshake test. */
+
+#define PS_STATE_FLAG_PREGR_FAIL       (1<<10) /* failed pregreet test */
+#define PS_STATE_FLAG_PREGR_PASS       (1<<11) /* passed pregreet test */
+#define PS_STATE_FLAG_PREGR_TODO       (1<<12) /* pregreet test expired */
+
+#define PS_STATE_FLAG_DNSBL_FAIL       (1<<13) /* failed DNSBL test */
+#define PS_STATE_FLAG_DNSBL_PASS       (1<<14) /* passed DNSBL test */
+#define PS_STATE_FLAG_DNSBL_TODO       (1<<15) /* DNSBL test expired */
+
+ /* Room here for one more after-handshake test. */
+
+#define PS_STATE_FLAG_PIPEL_FAIL       (1<<20) /* failed pipelining test */
+#define PS_STATE_FLAG_PIPEL_PASS       (1<<21) /* passed pipelining test */
+#define PS_STATE_FLAG_PIPEL_TODO       (1<<22) /* pipelining test expired */
+#define PS_STATE_FLAG_PIPEL_SKIP       (1<<23) /* action is already logged */
+
+#define PS_STATE_FLAG_NSMTP_FAIL       (1<<24) /* failed non-SMTP test */
+#define PS_STATE_FLAG_NSMTP_PASS       (1<<25) /* passed non-SMTP test */
+#define PS_STATE_FLAG_NSMTP_TODO       (1<<26) /* non-SMTP test expired */
+#define PS_STATE_FLAG_NSMTP_SKIP       (1<<27) /* action is already logged */
+
+#define PS_STATE_FLAG_BARLF_FAIL       (1<<28) /* failed bare newline test */
+#define PS_STATE_FLAG_BARLF_PASS       (1<<29) /* passed bare newline test */
+#define PS_STATE_FLAG_BARLF_TODO       (1<<30) /* bare newline test expired */
+#define PS_STATE_FLAG_BARLF_SKIP       (1<<31) /* action is already logged */
+
+ /*
+  * Aggregates for individual tests.
+  */
+#define PS_STATE_FLAG_PREGR_TODO_FAIL \
+       (PS_STATE_FLAG_PREGR_TODO | PS_STATE_FLAG_PREGR_FAIL)
+#define PS_STATE_FLAG_DNSBL_TODO_FAIL \
+       (PS_STATE_FLAG_DNSBL_TODO | PS_STATE_FLAG_DNSBL_FAIL)
+#define PS_STATE_FLAG_PIPEL_TODO_FAIL \
+       (PS_STATE_FLAG_PIPEL_TODO | PS_STATE_FLAG_PIPEL_FAIL)
+#define PS_STATE_FLAG_NSMTP_TODO_FAIL \
+       (PS_STATE_FLAG_NSMTP_TODO | PS_STATE_FLAG_NSMTP_FAIL)
+#define PS_STATE_FLAG_BARLF_TODO_FAIL \
+       (PS_STATE_FLAG_BARLF_TODO | PS_STATE_FLAG_BARLF_FAIL)
+
+#define PS_STATE_FLAG_PIPEL_TODO_SKIP \
+       (PS_STATE_FLAG_PIPEL_TODO | PS_STATE_FLAG_PIPEL_SKIP)
+#define PS_STATE_FLAG_NSMTP_TODO_SKIP \
+       (PS_STATE_FLAG_NSMTP_TODO | PS_STATE_FLAG_NSMTP_SKIP)
+#define PS_STATE_FLAG_BARLF_TODO_SKIP \
+       (PS_STATE_FLAG_BARLF_TODO | PS_STATE_FLAG_BARLF_SKIP)
+
+#define PS_STATE_FLAG_PIPEL_TODO_PASS_FAIL \
+       (PS_STATE_FLAG_PIPEL_TODO_FAIL | PS_STATE_FLAG_PIPEL_PASS)
+#define PS_STATE_FLAG_NSMTP_TODO_PASS_FAIL \
+       (PS_STATE_FLAG_NSMTP_TODO_FAIL | PS_STATE_FLAG_NSMTP_PASS)
+#define PS_STATE_FLAG_BARLF_TODO_PASS_FAIL \
+       (PS_STATE_FLAG_BARLF_TODO_FAIL | PS_STATE_FLAG_BARLF_PASS)
+
+ /*
+  * Separate aggregates for early tests and deep tests.
+  */
+#define PS_STATE_FLAG_EARLY_TODO \
+       (PS_STATE_FLAG_PREGR_TODO | PS_STATE_FLAG_DNSBL_TODO)
+#define PS_STATE_FLAG_EARLY_PASS \
+       (PS_STATE_FLAG_PREGR_PASS | PS_STATE_FLAG_DNSBL_PASS)
+#define PS_STATE_FLAG_EARLY_FAIL \
+       (PS_STATE_FLAG_PREGR_FAIL | PS_STATE_FLAG_DNSBL_FAIL)
+
+#define PS_STATE_FLAG_SMTPD_TODO \
+       (PS_STATE_FLAG_PIPEL_TODO | PS_STATE_FLAG_NSMTP_TODO | \
+       PS_STATE_FLAG_BARLF_TODO)
+#define PS_STATE_FLAG_SMTPD_PASS \
+       (PS_STATE_FLAG_PIPEL_PASS | PS_STATE_FLAG_NSMTP_PASS | \
+       PS_STATE_FLAG_BARLF_PASS)
+#define PS_STATE_FLAG_SMTPD_FAIL \
+       (PS_STATE_FLAG_PIPEL_FAIL | PS_STATE_FLAG_NSMTP_FAIL | \
+       PS_STATE_FLAG_BARLF_FAIL)
+
+ /*
+  * Super-aggregates for all tests combined.
+  */
+#define PS_STATE_FLAG_ANY_FAIL \
+       (PS_STATE_FLAG_BLIST_FAIL | \
+       PS_STATE_FLAG_EARLY_FAIL | PS_STATE_FLAG_SMTPD_FAIL)
+
+#define PS_STATE_FLAG_ANY_PASS \
+       (PS_STATE_FLAG_EARLY_PASS | PS_STATE_FLAG_SMTPD_PASS)
+
+#define PS_STATE_FLAG_ANY_TODO \
+       (PS_STATE_FLAG_EARLY_TODO | PS_STATE_FLAG_SMTPD_TODO)
 
  /*
   * See log_adhoc.c for discussion.
@@ -28,37 +182,107 @@ typedef struct {
 
 #define PS_CALC_DELTA(x, y, z) \
     do { \
-        (x).dt_sec = (y).tv_sec - (z).tv_sec; \
-        (x).dt_usec = (y).tv_usec - (z).tv_usec; \
-        while ((x).dt_usec < 0) { \
-            (x).dt_usec += 1000000; \
-            (x).dt_sec -= 1; \
-        } \
-        while ((x).dt_usec >= 1000000) { \
-            (x).dt_usec -= 1000000; \
-            (x).dt_sec += 1; \
-        } \
-        if ((x).dt_sec < 0) \
-            (x).dt_sec = (x).dt_usec = 0; \
+       (x).dt_sec = (y).tv_sec - (z).tv_sec; \
+       (x).dt_usec = (y).tv_usec - (z).tv_usec; \
+       while ((x).dt_usec < 0) { \
+           (x).dt_usec += 1000000; \
+           (x).dt_sec -= 1; \
+       } \
+       while ((x).dt_usec >= 1000000) { \
+           (x).dt_usec -= 1000000; \
+           (x).dt_sec += 1; \
+       } \
+       if ((x).dt_sec < 0) \
+           (x).dt_sec = (x).dt_usec = 0; \
     } while (0)
 
 #define SIG_DIGS        2
 
-/* READ_EVENT_REQUEST - prepare for transition to next state */
+ /*
+  * Event management.
+  */
+
+/* PS_READ_EVENT_REQUEST - prepare for transition to next state */
+
+#define PS_READ_EVENT_REQUEST(fd, action, context, timeout) do { \
+       if (msg_verbose > 1) \
+           msg_info("%s: read-request fd=%d", myname, (fd)); \
+       event_enable_read((fd), (action), (context)); \
+       event_request_timer((action), (context), (timeout)); \
+    } while (0)
+
+#define PS_READ_EVENT_REQUEST2(fd, read_act, time_act, context, timeout) do { \
+       if (msg_verbose > 1) \
+           msg_info("%s: read-request fd=%d", myname, (fd)); \
+       event_enable_read((fd), (read_act), (context)); \
+       event_request_timer((time_act), (context), (timeout)); \
+    } while (0)
+
+/* PS_CLEAR_EVENT_REQUEST - complete state transition */
 
-#define READ_EVENT_REQUEST(fd, action, context, timeout) do { \
-    if (msg_verbose) msg_info("%s: read-request fd=%d", myname, (fd)); \
-    event_enable_read((fd), (action), (context)); \
-    event_request_timer((action), (context), (timeout)); \
-} while (0)
+#define PS_CLEAR_EVENT_REQUEST(fd, time_act, context) do { \
+       if (msg_verbose > 1) \
+           msg_info("%s: clear-request fd=%d", myname, (fd)); \
+       event_disable_readwrite(fd); \
+       event_cancel_timer((time_act), (context)); \
+    } while (0)
+
+ /*
+  * Failure enforcement policies.
+  */
+#define PS_NAME_ACT_DROP       "drop"
+#define PS_NAME_ACT_ENFORCE    "enforce"
+#define PS_NAME_ACT_IGNORE     "ignore"
+#define PS_NAME_ACT_CONT       "continue"
+
+#define PS_ACT_DROP            1
+#define PS_ACT_ENFORCE         2
+#define PS_ACT_IGNORE          3
 
-/* CLEAR_EVENT_REQUEST - complete state transition */
+ /*
+  * Global variables.
+  */
+extern int ps_check_queue_length;      /* connections being checked */
+extern int ps_post_queue_length;       /* being sent to real SMTPD */
+extern DICT_CACHE *ps_cache_map;       /* cache table handle */
+extern VSTRING *ps_temp;               /* scratchpad */
+extern char *ps_smtpd_service_name;    /* path to real SMTPD */
+extern int ps_pregr_action;            /* PS_ACT_DROP etc. */
+extern int ps_dnsbl_action;            /* PS_ACT_DROP etc. */
+extern int ps_pipel_action;            /* PS_ACT_DROP etc. */
+extern int ps_nsmtp_action;            /* PS_ACT_DROP etc. */
+extern int ps_barlf_action;            /* PS_ACT_DROP etc. */
+extern int ps_min_ttl;                 /* Update with new tests! */
+extern int ps_max_ttl;                 /* Update with new tests! */
+extern STRING_LIST *ps_forbid_cmds;    /* CONNECT GET POST */
+extern int ps_stress_greet_wait;       /* stressed greet wait */
+extern int ps_normal_greet_wait;       /* stressed greet wait */
+extern int ps_stress_cmd_time_limit;   /* stressed command limit */
+extern int ps_normal_cmd_time_limit;   /* normal command time limit */
+extern int ps_stress;                  /* stress level */
+extern int ps_check_queue_length_lowat;        /* stress low-water mark */
+extern int ps_check_queue_length_hiwat;        /* stress high-water mark */
+extern DICT *ps_dnsbl_reply;           /* DNSBL name mapper */
+
+#define PS_EFF_GREET_WAIT \
+       (ps_stress ? ps_stress_greet_wait : ps_normal_greet_wait)
+#define PS_EFF_CMD_TIME_LIMIT \
+       (ps_stress ? ps_stress_cmd_time_limit : ps_normal_cmd_time_limit)
+
+ /*
+  * String plumbing macros.
+  */
+#define PS_STRING_UPDATE(str, text) do { \
+       if (str) myfree(str); \
+       (str) = ((text) ? mystrdup(text) : 0); \
+    } while (0)
 
-#define CLEAR_EVENT_REQUEST(fd, action, context) do { \
-    if (msg_verbose) msg_info("%s: clear-request fd=%d", myname, (fd)); \
-    event_disable_readwrite(fd); \
-    event_cancel_timer((action), (context)); \
-} while (0)
+#define PS_STRING_RESET(str) do { \
+       if (str) { \
+           myfree(str); \
+           (str) = 0; \
+       } \
+    } while (0)
 
  /*
   * SLMs.
@@ -66,6 +290,63 @@ typedef struct {
 #define STR(x)  vstring_str(x)
 #define LEN(x)  VSTRING_LEN(x)
 
+ /*
+  * postscreen_state.c
+  */
+#define PS_CLIENT_ADDR_PORT(state) \
+       (state)->smtp_client_addr, (state)->smtp_client_port
+
+#define PS_PASS_SESSION_STATE(state, what, bits) do { \
+       if (msg_verbose) \
+           msg_info("PASS %s %s:%s", (what), PS_CLIENT_ADDR_PORT(state)); \
+       (state)->flags |= (bits); \
+    } while (0)
+#define PS_FAIL_SESSION_STATE(state, bits) do { \
+       if (msg_verbose) \
+           msg_info("FAIL %s:%s", PS_CLIENT_ADDR_PORT(state)); \
+       (state)->flags |= (bits); \
+    } while (0)
+#define PS_SKIP_SESSION_STATE(state, what, bits) do { \
+       if (msg_verbose) \
+           msg_info("SKIP %s %s:%s", (what), PS_CLIENT_ADDR_PORT(state)); \
+       (state)->flags |= (bits); \
+    } while (0)
+#define PS_DROP_SESSION_STATE(state, reply) do { \
+       if (msg_verbose) \
+           msg_info("DROP %s:%s", PS_CLIENT_ADDR_PORT(state)); \
+       (state)->flags |= PS_STATE_FLAG_NOFORWARD; \
+       (state)->final_reply = (reply); \
+       ps_conclude(state); \
+    } while (0)
+#define PS_ENFORCE_SESSION_STATE(state, reply) do { \
+       if (msg_verbose) \
+           msg_info("ENFORCE %s:%s", PS_CLIENT_ADDR_PORT(state)); \
+       (state)->rcpt_reply = (reply); \
+       (state)->flags |= PS_STATE_FLAG_NOFORWARD; \
+    } while (0)
+#define PS_UNPASS_SESSION_STATE(state, bits) do { \
+       if (msg_verbose) \
+           msg_info("UNPASS %s:%s", PS_CLIENT_ADDR_PORT(state)); \
+       (state)->flags &= ~(bits); \
+    } while (0)
+#define PS_UNFAIL_SESSION_STATE(state, bits) do { \
+       if (msg_verbose) \
+           msg_info("UNFAIL %s:%s", PS_CLIENT_ADDR_PORT(state)); \
+       (state)->flags &= ~(bits); \
+    } while (0)
+#define PS_ADD_SERVER_STATE(state, fd) do { \
+       (state)->smtp_server_fd = (fd); \
+       ps_post_queue_length++; \
+    } while (0)
+#define PS_DEL_CLIENT_STATE(state) do { \
+       event_server_disconnect((state)->smtp_client_stream); \
+       (state)->smtp_client_stream = 0; \
+       ps_check_queue_length--; \
+    } while (0)
+extern PS_STATE *ps_new_session_state(VSTREAM *, const char *, const char *);
+extern void ps_free_session_state(PS_STATE *);
+extern const char *ps_print_state_flags(int, const char *);
+
  /*
   * postscreen_dict.c
   */
@@ -77,9 +358,60 @@ extern void ps_cache_update(DICT_CACHE *, const char *, const char *);
   * postscreen_dnsbl.c
   */
 extern void ps_dnsbl_init(void);
-extern int ps_dnsbl_retrieve(const char *);
+extern int ps_dnsbl_retrieve(const char *, const char **);
 extern void ps_dnsbl_request(const char *);
 
+ /*
+  * postscreen_tests.c
+  */
+#define PS_INIT_TESTS(dst) do { \
+       (dst)->flags = 0; \
+       (dst)->pregr_stamp = PS_TIME_STAMP_INVALID; \
+       (dst)->dnsbl_stamp = PS_TIME_STAMP_INVALID; \
+       (dst)->pipel_stamp = PS_TIME_STAMP_INVALID; \
+    } while (0)
+#define PS_BEGIN_TESTS(state, name) do { \
+       (state)->test_name = (name); \
+       GETTIMEOFDAY(&(state)->start_time); \
+    } while (0)
+extern void ps_new_tests(PS_STATE *);
+extern void ps_parse_tests(PS_STATE *, const char *, time_t);
+extern char *ps_print_tests(VSTRING *, PS_STATE *);
+extern char *ps_print_grey_key(VSTRING *, const char *, const char *, const char *, const char *);
+
+#define PS_MIN(x, y) ((x) < (y) ? (x) : (y))
+#define PS_MAX(x, y) ((x) > (y) ? (x) : (y))
+
+ /*
+  * postscreen_early.c
+  */
+extern void ps_early_tests(PS_STATE *);
+extern void ps_early_init(void);
+
+ /*
+  * postscreen_smtpd.c
+  */
+extern void ps_smtpd_tests(PS_STATE *);
+extern void ps_smtpd_init(void);
+
+ /*
+  * postscreen_misc.c
+  */
+extern char *ps_format_delta_time(VSTRING *, struct timeval, int *);
+extern void ps_conclude(PS_STATE *);
+extern void ps_hangup_event(PS_STATE *);
+
+ /*
+  * postscreen_send.c
+  */
+#define PS_SEND_REPLY(state, text) \
+    ps_send_reply(vstream_fileno((state)->smtp_client_stream), \
+                 (state)->smtp_client_addr, \
+                 (state)->smtp_client_port, \
+                 (text))
+extern int ps_send_reply(int, const char *, const char *, const char *);
+extern void ps_send_socket(PS_STATE *);
+
 /* LICENSE
 /* .ad
 /* .fi
index a39d9941f4e36303b87a52033f37916c1bae77f4..1016ea65b856e00b3f2288a4a99a7813ec407b87 100644 (file)
 /*     void    ps_dnsbl_request(client_addr)
 /*     char    *client_addr;
 /*
-/*     int     ps_dnsbl_retrieve(client_addr)
+/*     int     ps_dnsbl_retrieve(client_addr, dnsbl_name)
 /*     char    *client_addr;
+/*     const char **dnsbl_name;
 /* DESCRIPTION
-/*     This module implements preliminary support for DNSBL lookups
-/*     that complete in the background. Multiple requests for the
-/*     same information are handled with reference counts.
+/*     This module implements preliminary support for DNSBL lookups.
+/*     Multiple requests for the same information are handled with
+/*     reference counts.
 /*
 /*     ps_dnsbl_init() initializes this module, and must be called
 /*     once before any of the other functions in this module.
 /*
-/*     ps_dnsbl_request() requests a blocklist score for the specified
-/*     client IP address and increments the reference count. The
+/*     ps_dnsbl_request() requests a blocklist score for the
+/*     specified client IP address and increments the reference
+/*     count. The request completes in the background. The
 /*     client IP address must be in inet_ntop(3) output format.
 /*
 /*     ps_dnsbl_retrieve() retrieves the result score requested with
   * 
   * Each DNSBL domain can be specified more than once, each time with a
   * different (filter, weight) pair. We group (filter, weight) pairs in a
-  * linked list under their DNSBL domain name.
+  * linked list under their DNSBL domain name. The list head has a reference
+  * to a "safe name" for the DNSBL, in case the name includes a password.
   */
 static HTABLE *dnsbl_site_cache;       /* indexed by DNSBNL domain */
 static HTABLE_INFO **dnsbl_site_list;  /* flattened cache */
 
+typedef struct {
+    const char *safe_dnsbl;            /* from postscreen_dnsbl_reply_map */
+    struct PS_DNSBL_SITE *first;       /* list of (filter, weight) tuples */
+} PS_DNSBL_HEAD;
+
 typedef struct PS_DNSBL_SITE {
     char   *filter;                    /* reply filter (default: null) */
     int     weight;                    /* reply weight (default: 1) */
@@ -101,6 +109,7 @@ typedef struct PS_DNSBL_SITE {
 static HTABLE *dnsbl_score_cache;      /* indexed by client address */
 
 typedef struct {
+    const char *dnsbl;                 /* one contributing DNSBL */
     int     total;                     /* combined blocklist score */
     int     refcount;                  /* score reference count */
 } PS_DNSBL_SCORE;
@@ -122,12 +131,13 @@ static void ps_dnsbl_add_site(const char *site)
 {
     const char *myname = "ps_dnsbl_add_site";
     char   *saved_site = mystrdup(site);
-    PS_DNSBL_SITE *old_site;
+    PS_DNSBL_HEAD *head;
     PS_DNSBL_SITE *new_site;
     char    junk;
     const char *weight_text;
     const char *pattern_text;
     int     weight;
+    HTABLE_INFO *ht;
 
     /*
      * Parse the required DNSBL domain name, the optional reply filter and
@@ -153,28 +163,36 @@ static void ps_dnsbl_add_site(const char *site)
        msg_fatal("bad DNSBL domain name \"%s\" in \"%s\"",
                  saved_site, site);
 
-    if (msg_verbose)
+    if (msg_verbose > 1)
        msg_info("%s: \"%s\" -> domain=\"%s\" pattern=\"%s\" weight=%d",
                 myname, site, saved_site, pattern_text ? pattern_text :
                 "null", weight);
 
     /*
-     * Add a new node for this DNSBL domain name. One DNSBL domain name can
-     * be specified multiple times with different filters and weights. These
-     * are stored as a linked list under the DNSBL domain name.
+     * Look up or create the (filter, weight) list head for this DNSBL domain
+     * name.
+     */
+    if ((head = (PS_DNSBL_HEAD *)
+        htable_find(dnsbl_site_cache, saved_site)) == 0) {
+       head = (PS_DNSBL_HEAD *) mymalloc(sizeof(*head));
+       ht = htable_enter(dnsbl_site_cache, saved_site, (char *) head);
+       /* Translate the DNSBL name into a safe name if available. */
+       if (ps_dnsbl_reply == 0
+         || (head->safe_dnsbl = dict_get(ps_dnsbl_reply, saved_site)) == 0)
+           head->safe_dnsbl = ht->key;
+       head->first = 0;
+    }
+
+    /*
+     * Append the new (filter, weight) node to the list for this DNSBL domain
+     * name.
      */
     new_site = (PS_DNSBL_SITE *) mymalloc(sizeof(*new_site));
     new_site->filter = (pattern_text ? mystrdup(pattern_text) : 0);
     new_site->weight = weight;
+    new_site->next = head->first;
+    head->first = new_site;
 
-    if ((old_site = (PS_DNSBL_SITE *)
-        htable_find(dnsbl_site_cache, saved_site)) != 0) {
-       new_site->next = old_site->next;
-       old_site->next = new_site;
-    } else {
-       (void) htable_enter(dnsbl_site_cache, saved_site, (char *) new_site);
-       new_site->next = 0;
-    }
     myfree(saved_site);
 }
 
@@ -195,7 +213,7 @@ static int ps_dnsbl_match(const char *filter, ARGV *reply)
 
 /* ps_dnsbl_retrieve - retrieve blocklist score, decrement reference count */
 
-int     ps_dnsbl_retrieve(const char *client_addr)
+int     ps_dnsbl_retrieve(const char *client_addr, const char **dnsbl_name)
 {
     const char *myname = "ps_dnsbl_retrieve";
     PS_DNSBL_SCORE *score;
@@ -212,9 +230,10 @@ int     ps_dnsbl_retrieve(const char *client_addr)
      * Reads are destructive.
      */
     result_score = score->total;
+    *dnsbl_name = score->dnsbl;
     score->refcount -= 1;
     if (score->refcount < 1) {
-       if (msg_verbose)
+       if (msg_verbose > 1)
            msg_info("%s: delete blocklist score for %s", myname, client_addr);
        htable_delete(dnsbl_score_cache, client_addr, myfree);
     }
@@ -228,10 +247,11 @@ static void ps_dnsbl_receive(int event, char *context)
     const char *myname = "ps_dnsbl_receive";
     VSTREAM *stream = (VSTREAM *) context;
     PS_DNSBL_SCORE *score;
+    PS_DNSBL_HEAD *head;
     PS_DNSBL_SITE *site;
     ARGV   *reply_argv;
 
-    CLEAR_EVENT_REQUEST(vstream_fileno(stream), ps_dnsbl_receive, context);
+    PS_CLEAR_EVENT_REQUEST(vstream_fileno(stream), ps_dnsbl_receive, context);
 
     /*
      * Receive the DNSBL lookup result.
@@ -266,18 +286,19 @@ static void ps_dnsbl_receive(int event, char *context)
         * Don't panic when the DNSBL domain name is not found. The DNSBLOG
         * server may be messed up.
         */
-       if (msg_verbose)
+       if (msg_verbose > 1)
            msg_info("%s: client=\"%s\" score=%d domain=\"%s\" reply=\"%s\"",
                     myname, STR(reply_client), score->total,
                     STR(reply_dnsbl), STR(reply_addr));
-       for (reply_argv = 0, site = (PS_DNSBL_SITE *)
-            htable_find(dnsbl_site_cache, STR(reply_dnsbl));
-            site != 0; site = site->next) {
+       head = (PS_DNSBL_HEAD *) htable_find(dnsbl_site_cache, STR(reply_dnsbl));
+       site = (head ? head->first : (PS_DNSBL_SITE *) 0);
+       for (reply_argv = 0; site != 0; site = site->next) {
            if (site->filter == 0
                || ps_dnsbl_match(site->filter, reply_argv ? reply_argv :
                         (reply_argv = argv_split(STR(reply_addr), " ")))) {
+               score->dnsbl = head->safe_dnsbl;
                score->total += site->weight;
-               if (msg_verbose)
+               if (msg_verbose > 1)
                    msg_info("%s: filter=\"%s\" weight=%d score=%d",
                             myname, site->filter ? site->filter : "null",
                             site->weight, score->total);
@@ -304,13 +325,16 @@ void    ps_dnsbl_request(const char *client_addr)
      * store a reference-counted DNSBL score under its client IP address. We
      * increment the reference count with each request, and decrement the
      * reference count with each retrieval.
+     * 
+     * XXX Notify the requestor as soon as all DNS replies are in, to avoid
+     * unnecessary delay when we only need the DNSBL score.
      */
     if ((score = (PS_DNSBL_SCORE *)
         htable_find(dnsbl_score_cache, client_addr)) != 0) {
        score->refcount += 1;
        return;
     }
-    if (msg_verbose)
+    if (msg_verbose > 1)
        msg_info("%s: create blocklist score for %s", myname, client_addr);
     score = (PS_DNSBL_SCORE *) mymalloc(sizeof(*score));
     score->total = 0;
@@ -338,8 +362,8 @@ void    ps_dnsbl_request(const char *client_addr)
            vstream_fclose(stream);
            return;
        }
-       READ_EVENT_REQUEST(vstream_fileno(stream), ps_dnsbl_receive,
-                          (char *) stream, DNSBLOG_TIMEOUT);
+       PS_READ_EVENT_REQUEST(vstream_fileno(stream), ps_dnsbl_receive,
+                             (char *) stream, DNSBLOG_TIMEOUT);
     }
 }
 
diff --git a/postfix/src/postscreen/postscreen_early.c b/postfix/src/postscreen/postscreen_early.c
new file mode 100644 (file)
index 0000000..e6ed4d9
--- /dev/null
@@ -0,0 +1,254 @@
+/*++
+/* NAME
+/*     postscreen_early 3
+/* SUMMARY
+/*     postscreen pre-handshake tests
+/* SYNOPSIS
+/*     #include <postscreen.h>
+/*
+/*     void    ps_early_init(void)
+/*
+/*     void    ps_early_tests(state)
+/*     PS_STATE *state;
+/* DESCRIPTION
+/*     ps_early_tests() performs protocol tests before the SMTP
+/*     handshake: the pregreet test and the DNSBL test. Control
+/*     is passed to the ps_smtpd_tests() routine as appropriate.
+/*
+/*     ps_early_init() performs one-time initialization.
+/* 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 <sys/socket.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+#include <mymalloc.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <postscreen.h>
+
+static char *ps_teaser_greeting;
+
+/* ps_early_event - handle pre-greet, EOF, and DNSBL results. */
+
+static void ps_early_event(int event, char *context)
+{
+    const char *myname = "ps_early_event";
+    PS_STATE *state = (PS_STATE *) context;
+    char    read_buf[PS_READ_BUF_SIZE];
+    int     read_count;
+    int     dnsbl_score;
+    int     elapsed;
+    const char *dnsbl_name;
+
+    if (msg_verbose > 1)
+       msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from %s:%s flags=%s",
+                myname, ps_post_queue_length, ps_check_queue_length,
+                event, vstream_fileno(state->smtp_client_stream),
+                state->smtp_client_addr, state->smtp_client_port,
+                ps_print_state_flags(state->flags, myname));
+
+    PS_CLEAR_EVENT_REQUEST(vstream_fileno(state->smtp_client_stream),
+                          ps_early_event, context);
+
+    /*
+     * XXX Be sure to empty the DNSBL lookup buffer otherwise we have a
+     * memory leak.
+     * 
+     * XXX We can avoid "forgetting" to do this by keeping a pointer to the
+     * DNSBL lookup buffer in the PS_STATE structure. This also allows us to
+     * shave off a hash table lookup when retrieving the DNSBL result.
+     */
+    switch (event) {
+
+       /*
+        * We reached the end of the early tests time limit.
+        */
+    case EVENT_TIME:
+
+       /*
+        * Check if the SMTP client spoke before its turn.
+        */
+       if ((state->flags & PS_STATE_FLAG_PREGR_TODO_FAIL)
+           == PS_STATE_FLAG_PREGR_TODO) {
+           state->pregr_stamp = event_time() + var_ps_pregr_ttl;
+           PS_PASS_SESSION_STATE(state, "pregreet test",
+                                 PS_STATE_FLAG_PREGR_PASS);
+       }
+       if ((state->flags & PS_STATE_FLAG_PREGR_FAIL)
+           && ps_pregr_action == PS_ACT_IGNORE) {
+           PS_UNFAIL_SESSION_STATE(state, PS_STATE_FLAG_PREGR_FAIL);
+           /* Not: PS_PASS_SESSION_STATE. Repeat this test the next time. */
+       }
+
+       /*
+        * If the client is DNS blocklisted, drop the connection, send the
+        * client to a dummy protocol engine, or continue to the next test.
+        */
+#define PS_DNSBL_FORMAT \
+       "%s 5.7.1 Service unavailable; client [%s] blocked using %s\r\n"
+
+       if (state->flags & PS_STATE_FLAG_DNSBL_TODO) {
+           dnsbl_score =
+               ps_dnsbl_retrieve(state->smtp_client_addr, &dnsbl_name);
+           if (dnsbl_score < var_ps_dnsbl_thresh) {
+               state->dnsbl_stamp = event_time() + var_ps_pregr_ttl;
+               PS_PASS_SESSION_STATE(state, "dnsbl test",
+                                     PS_STATE_FLAG_DNSBL_PASS);
+           } else {
+               msg_info("DNSBL rank %d for %s",
+                        dnsbl_score, state->smtp_client_addr);
+               PS_FAIL_SESSION_STATE(state, PS_STATE_FLAG_DNSBL_FAIL);
+               switch (ps_dnsbl_action) {
+               case PS_ACT_DROP:
+                   state->dnsbl_reply = vstring_sprintf(vstring_alloc(100),
+                                                    PS_DNSBL_FORMAT, "521",
+                                      state->smtp_client_addr, dnsbl_name);
+                   PS_DROP_SESSION_STATE(state, STR(state->dnsbl_reply));
+                   return;
+               case PS_ACT_ENFORCE:
+                   state->dnsbl_reply = vstring_sprintf(vstring_alloc(100),
+                                                    PS_DNSBL_FORMAT, "550",
+                                      state->smtp_client_addr, dnsbl_name);
+                   PS_ENFORCE_SESSION_STATE(state, STR(state->dnsbl_reply));
+                   break;
+               case PS_ACT_IGNORE:
+                   PS_UNFAIL_SESSION_STATE(state, PS_STATE_FLAG_DNSBL_FAIL);
+                   /* Not: PS_PASS_SESSION_STATE. Repeat this test. */
+                   break;
+               default:
+                   msg_panic("%s: unknown dnsbl action value %d",
+                             myname, ps_dnsbl_action);
+
+               }
+           }
+       }
+
+       /*
+        * Pass the connection to a real SMTP server, or enter the dummy
+        * engine for deep tests.
+        */
+       if (state->flags & (PS_STATE_FLAG_NOFORWARD | PS_STATE_FLAG_SMTPD_TODO))
+           ps_smtpd_tests(state);
+       else
+           ps_conclude(state);
+       return;
+
+       /*
+        * EOF, or the client spoke before its turn. We simply drop the
+        * connection, or we continue waiting and allow DNS replies to
+        * trickle in.
+        * 
+        * XXX Reset the pregreet timer when the DNS results are complete.
+        */
+    default:
+       if ((read_count = recv(vstream_fileno(state->smtp_client_stream),
+                         read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) {
+           /* Avoid memory leak. */
+           if (state->flags & PS_STATE_FLAG_DNSBL_TODO)
+               (void) ps_dnsbl_retrieve(state->smtp_client_addr, &dnsbl_name);
+           /* XXX Wait for DNS replies to come in. */
+           ps_hangup_event(state);
+           return;
+       }
+       read_buf[read_count] = 0;
+       msg_info("PREGREET %d after %s from %s: %.100s", read_count,
+                ps_format_delta_time(ps_temp, state->start_time, &elapsed),
+                state->smtp_client_addr, printable(read_buf, '?'));
+       PS_FAIL_SESSION_STATE(state, PS_STATE_FLAG_PREGR_FAIL);
+       switch (ps_pregr_action) {
+       case PS_ACT_DROP:
+           /* Avoid memory leak. */
+           if (state->flags & PS_STATE_FLAG_DNSBL_TODO)
+               (void) ps_dnsbl_retrieve(state->smtp_client_addr, &dnsbl_name);
+           PS_DROP_SESSION_STATE(state, "521 5.5.1 Protocol error\r\n");
+           return;
+       case PS_ACT_ENFORCE:
+           /* We call ps_dnsbl_retrieve() when the timer expires. */
+           PS_ENFORCE_SESSION_STATE(state, "550 5.5.1 Protocol error\r\n");
+           break;
+       case PS_ACT_IGNORE:
+           /* We call ps_dnsbl_retrieve() when the timer expires. */
+           /* We must handle this case after the timer expires. */
+           break;
+       default:
+           msg_panic("%s: unknown pregreet action value %d",
+                     myname, ps_pregr_action);
+       }
+       if (elapsed > PS_EFF_GREET_WAIT)
+           elapsed = PS_EFF_GREET_WAIT;
+       event_request_timer(ps_early_event, context,
+                           PS_EFF_GREET_WAIT - elapsed);
+       return;
+    }
+}
+
+/* ps_early_tests - start the early (before protocol) tests */
+
+void    ps_early_tests(PS_STATE *state)
+{
+    const char *myname = "ps_early_tests";
+
+    /*
+     * Report errors and progress in the context of this test.
+     */
+    PS_BEGIN_TESTS(state, "tests before SMTP handshake");
+
+    /*
+     * Run a PREGREET test. Send half the greeting banner, by way of teaser,
+     * then wait briefly to see if the client speaks before its turn.
+     */
+    if ((state->flags & PS_STATE_FLAG_PREGR_TODO) != 0
+       && ps_teaser_greeting != 0
+       && ps_send_reply(vstream_fileno(state->smtp_client_stream),
+                        state->smtp_client_addr, state->smtp_client_port,
+                        ps_teaser_greeting) != 0) {
+       ps_hangup_event(state);
+       return;
+    }
+
+    /*
+     * Run a DNS blocklist query.
+     */
+    if ((state->flags & PS_STATE_FLAG_DNSBL_TODO) != 0)
+       ps_dnsbl_request(state->smtp_client_addr);
+
+    /*
+     * Wait for the client to respond or for DNS lookup to complete.
+     */
+    if ((state->flags & PS_STATE_FLAG_PREGR_TODO) != 0)
+       PS_READ_EVENT_REQUEST(vstream_fileno(state->smtp_client_stream),
+                        ps_early_event, (char *) state, PS_EFF_GREET_WAIT);
+    else
+       event_request_timer(ps_early_event, (char *) state, PS_EFF_GREET_WAIT);
+}
+
+/* ps_early_init - initialize early tests */
+
+void    ps_early_init(void)
+{
+    if (*var_ps_pregr_banner) {
+       vstring_sprintf(ps_temp, "220-%s\r\n", var_ps_pregr_banner);
+       ps_teaser_greeting = mystrdup(STR(ps_temp));
+    }
+}
diff --git a/postfix/src/postscreen/postscreen_misc.c b/postfix/src/postscreen/postscreen_misc.c
new file mode 100644 (file)
index 0000000..94e4875
--- /dev/null
@@ -0,0 +1,154 @@
+/*++
+/* NAME
+/*     postscreen_misc 3
+/* SUMMARY
+/*     postscreen misc routines
+/* SYNOPSIS
+/*     #include <postscreen.h>
+/*
+/*     char    *ps_format_delta_time(buf, tv, delta)
+/*     VSTRING *buf;
+/*     struct timeval tv;
+/*     int     *delta;
+/*
+/*     void    ps_conclude(state)
+/*     PS_STATE *state;
+/*
+/*     void    ps_hangup_event(state)
+/*     PS_STATE *state;
+/* DESCRIPTION
+/*     ps_format_delta_time() computes the time difference between
+/*     tv (past) and the present, formats the time difference with
+/*     sub-second resolution in a human-readable way, and returns
+/*     the integer time difference in seconds through the delta
+/*     argument.
+/*
+/*     ps_conclude() logs when a client passes all necessary tests,
+/*     updates the postscreen cache for any testes that were passed,
+/*     and either forwards the connection to a real SMTP server or
+/*     replies with the text in state->error_reply and hangs up the
+/*     connection (by default, state->error_reply is set to a
+/*     default 421 reply).
+/*
+/*     ps_hangup_event() cleans up after a client connection breaks
+/*     unexpectedly. If logs the test where the break happened,
+/*     and how much time as spent in that test before the connection
+/*     broke.
+/* 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>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <iostuff.h>
+#include <format_tv.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <postscreen.h>
+
+/* ps_format_delta_time - pretty-formatted delta time */
+
+char   *ps_format_delta_time(VSTRING *buf, struct timeval tv, int *delta)
+{
+    DELTA_TIME pdelay;
+    struct timeval now;
+
+    GETTIMEOFDAY(&now);
+    PS_CALC_DELTA(pdelay, now, tv);
+    VSTRING_RESET(buf);
+    format_tv(buf, pdelay.dt_sec, pdelay.dt_usec, SIG_DIGS, var_delay_max_res);
+    *delta = pdelay.dt_sec;
+    return (STR(buf));
+}
+
+/* ps_conclude - bring this session to a conclusion */
+
+void    ps_conclude(PS_STATE *state)
+{
+    const char *myname = "ps_conclude";
+
+    if (msg_verbose)
+       msg_info("flags for %s: %s",
+                myname, ps_print_state_flags(state->flags, myname));
+
+    /*
+     * Handle clients that passed at least one test other than permanent
+     * whitelisting, and that didn't fail any test including permanent
+     * blacklisting. There may still be unfinished tests; those tests will
+     * need to be completed when the client returns in a later session.
+     */
+    if ((state->flags & PS_STATE_FLAG_ANY_PASS) != 0
+       && (state->flags & PS_STATE_FLAG_ANY_FAIL) == 0) {
+
+       /*
+        * Log our final blessing when all unfinished tests were completed.
+        */
+       if ((state->flags & PS_STATE_FLAG_ANY_PASS) ==
+        PS_STATE_FLAGS_TODO_TO_PASS(state->flags & PS_STATE_FLAG_ANY_TODO))
+           msg_info("PASS %s %s", (state->flags & PS_STATE_FLAG_NEW) == 0 ?
+                    "OLD" : "NEW", state->smtp_client_addr);
+
+       /*
+        * Update the postscreen cache. This still supports a scenario where
+        * a client gets whitelisted in the course of multiple sessions, as
+        * long as that client does not "fail" any test.
+        */
+       if (ps_cache_map != 0) {
+           ps_print_tests(ps_temp, state);
+           ps_cache_update(ps_cache_map, state->smtp_client_addr, STR(ps_temp));
+       }
+    }
+
+    /*
+     * Either hand off the socket to a real SMTP engine, or say bye-bye.
+     */
+    if ((state->flags & PS_STATE_FLAG_NOFORWARD) == 0) {
+       ps_send_socket(state);
+    } else {
+       if ((state->flags & PS_STATE_FLAG_HANGUP) == 0)
+           (void) ps_send_reply(vstream_fileno(state->smtp_client_stream),
+                          state->smtp_client_addr, state->smtp_client_port,
+                                state->final_reply);
+       ps_free_session_state(state);
+    }
+}
+
+/* ps_hangup_event - handle unexpected disconnect */
+
+void    ps_hangup_event(PS_STATE *state)
+{
+    int     elapsed;
+
+    /*
+     * Sessions can break at any time, even after the client passes all tests
+     * (some MTAs including Postfix don't send QUIT when connection reuse is
+     * enabled). This must not be treated as a protocol test failure.
+     * 
+     * Log the current test phase, and the elapsed time after the start of that
+     * phase.
+     */
+    state->flags |= PS_STATE_FLAG_HANGUP;
+    msg_info("HANGUP after %s from %s in %s",
+            ps_format_delta_time(ps_temp, state->start_time, &elapsed),
+            state->smtp_client_addr, state->test_name);
+    state->flags |= PS_STATE_FLAG_NOFORWARD;
+    ps_conclude(state);
+}
diff --git a/postfix/src/postscreen/postscreen_send.c b/postfix/src/postscreen/postscreen_send.c
new file mode 100644 (file)
index 0000000..5b8fc1f
--- /dev/null
@@ -0,0 +1,197 @@
+/*++
+/* NAME
+/*     postscreen_send 3
+/* SUMMARY
+/*     postscreen low-level output
+/* SYNOPSIS
+/*     #include <postscreen.h>
+/*
+/*     int     ps_send_reply(client_fd, client_addr, client_port, text)
+/*     int     client_fd;
+/*     const char *client_addr;
+/*     const char *client_port;
+/*     const char *text;
+/*
+/*     int     PS_SEND_REPLY(state, text)
+/*     const char *text;
+/*
+/*     void    ps_send_socket(state)
+/*     PS_STATE *state;
+/* DESCRIPTION
+/*     ps_send_reply() sends the specified text to the specified
+/*     remote SMTP client.  In case of an immediate error, it logs
+/*     a warning (except EPIPE) with the client address and port,
+/*     and returns -1 (including EPIPE). Otherwise, the result
+/*     value is the number of bytes sent.
+/*
+/*     PS_SEND_REPLY() is a convenience wrapper for ps_send_reply().
+/*     It is an unsafe macro that evaluates its arguments multiple
+/*     times.
+/*
+/*     ps_send_socket() sends the specified socket to the real
+/*     Postfix SMTP server. The socket is delivered in the background.
+/*     This function must be called after all other session-related
+/*     work is finished including postscreen cache updates.
+/*
+/*     In case of an immediate error, ps_send_socket() sends a 421
+/*     reply to the remote SMTP client and closes the connection.
+/* 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 <string.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+#include <connect.h>
+
+/* Application-specific. */
+
+#include <postscreen.h>
+
+ /*
+  * This program screens all inbound SMTP connections, so it better not waste
+  * time.
+  */
+#define PS_SEND_SOCK_CONNECT_TIMEOUT   1
+#define PS_SEND_SOCK_NOTIFY_TIMEOUT    100
+#define PS_SEND_TEXT_TIMEOUT           1
+
+/* ps_send_reply - send reply to remote SMTP client */
+
+int     ps_send_reply(int smtp_client_fd, const char *smtp_client_addr,
+                             const char *smtp_client_port, const char *text)
+{
+    int     ret;
+
+    if (msg_verbose)
+       msg_info("> %s:%s: %.*s", smtp_client_addr, smtp_client_port,
+                (int) strlen(text) - 2, text);
+
+    /*
+     * XXX Need to make sure that the TCP send buffer is large enough for any
+     * response, so that a nasty client can't cause this process to block.
+     */
+    ret = (write_buf(smtp_client_fd, text, strlen(text),
+                    PS_SEND_TEXT_TIMEOUT) < 0);
+    if (ret != 0 && errno != EPIPE)
+       msg_warn("write %s:%s: %m", smtp_client_addr, smtp_client_port);
+    return (ret);
+}
+
+/* ps_send_socket_close_event - file descriptor has arrived or timeout */
+
+static void ps_send_socket_close_event(int event, char *context)
+{
+    const char *myname = "ps_send_socket_close_event";
+    PS_STATE *state = (PS_STATE *) context;
+
+    if (msg_verbose > 1)
+       msg_info("%s: sq=%d cq=%d event %d on send socket %d from %s:%s",
+                myname, ps_post_queue_length, ps_check_queue_length,
+                event, state->smtp_server_fd, state->smtp_client_addr,
+                state->smtp_client_port);
+
+    /*
+     * The real SMTP server has closed the local IPC channel, or we have
+     * reached the limit of our patience. In the latter case it is still
+     * possible that the real SMTP server will receive the socket so we
+     * should not interfere.
+     */
+    PS_CLEAR_EVENT_REQUEST(state->smtp_server_fd, ps_send_socket_close_event,
+                          context);
+    if (event == EVENT_TIME)
+       msg_warn("timeout sending connection to service %s",
+                ps_smtpd_service_name);
+    ps_free_session_state(state);
+}
+
+/* ps_send_socket - send socket to real SMTP server process */
+
+void    ps_send_socket(PS_STATE *state)
+{
+    const char *myname = "ps_send_socket";
+    int     server_fd;
+    int     window_size;
+
+    if (msg_verbose > 1)
+       msg_info("%s: sq=%d cq=%d send socket %d from %s:%s",
+                myname, ps_post_queue_length, ps_check_queue_length,
+                vstream_fileno(state->smtp_client_stream),
+                state->smtp_client_addr, state->smtp_client_port);
+
+    /*
+     * This is where we would adjust the window size to a value that is
+     * appropriate for this client class.
+     */
+#if 0
+    window_size = 65535;
+    if (setsockopt(vstream_fileno(state->smtp_client_stream), SOL_SOCKET, SO_RCVBUF,
+                  (char *) &window_size, sizeof(window_size)) < 0)
+       msg_warn("setsockopt SO_RCVBUF %d: %m", window_size);
+#endif
+
+    /*
+     * Connect to the real SMTP service over a local IPC channel, send the
+     * file descriptor, and close the file descriptor to save resources.
+     * Experience has shown that some systems will discard information when
+     * we close a channel immediately after writing. Thus, we waste resources
+     * waiting for the remote side to close the local IPC channel first. The
+     * good side of waiting is that we learn when the real SMTP server is
+     * falling behind.
+     * 
+     * This is where we would forward the connection to an SMTP server that
+     * provides an appropriate level of service for this client class. For
+     * example, a server that is more forgiving, or one that is more
+     * suspicious. Alternatively, we could send attributes along with the
+     * socket with client reputation information, making everything even more
+     * Postfix-specific.
+     */
+    if ((server_fd =
+        LOCAL_CONNECT(ps_smtpd_service_name, NON_BLOCKING,
+                      PS_SEND_SOCK_CONNECT_TIMEOUT)) < 0) {
+       msg_warn("cannot connect to service %s: %m", ps_smtpd_service_name);
+       ps_send_reply(vstream_fileno(state->smtp_client_stream),
+                     state->smtp_client_addr, state->smtp_client_port,
+                     "421 4.3.2 All server ports are busy\r\n");
+       ps_free_session_state(state);
+       return;
+    }
+    PS_ADD_SERVER_STATE(state, server_fd);
+    if (LOCAL_SEND_FD(state->smtp_server_fd,
+                     vstream_fileno(state->smtp_client_stream)) < 0) {
+       msg_warn("cannot pass connection to service %s: %m",
+                ps_smtpd_service_name);
+       ps_send_reply(vstream_fileno(state->smtp_client_stream),
+                     state->smtp_client_addr, state->smtp_client_port,
+                     "421 4.3.2 No system resources\r\n");
+       ps_free_session_state(state);
+       return;
+    } else {
+
+       /*
+        * Closing the smtp_client_fd here triggers a FreeBSD 7.1 kernel bug
+        * where smtp-source sometimes sees the connection being closed after
+        * it has already received the real SMTP server's 220 greeting!
+        */
+#if 0
+       PS_DEL_CLIENT_STATE(state);
+#endif
+       PS_READ_EVENT_REQUEST(state->smtp_server_fd, ps_send_socket_close_event,
+                             (char *) state, PS_SEND_SOCK_NOTIFY_TIMEOUT);
+       return;
+    }
+}
diff --git a/postfix/src/postscreen/postscreen_smtpd.c b/postfix/src/postscreen/postscreen_smtpd.c
new file mode 100644 (file)
index 0000000..281c411
--- /dev/null
@@ -0,0 +1,901 @@
+/*++
+/* NAME
+/*     postscreen_smtpd 3
+/* SUMMARY
+/*     postscreen built-in SMTP server engine
+/* SYNOPSIS
+/*     #include <postscreen.h>
+/*
+/*     void    ps_smtpd_init(void)
+/*
+/*     void    ps_smtpd_tests(state)
+/*     PS_STATE *state;
+/* DESCRIPTION
+/*     ps_smtpd_init() performs one-time per-process initialization.
+/*
+/*     ps_smtpd_tests() starts up an SMTP server engine for deep
+/*     protocol tests and for collecting helo/sender/recipient
+/*     information.
+/*
+/*     Unlike the Postfix SMTP server, this engine does not announce
+/*     PIPELINING support. This exposes spambots that pipeline
+/*     their commands anyway. To pass this test, the client has
+/*     to speak SMTP all the way to the RCPT TO command.
+/*
+/*     No support is announced for AUTH, STARTTLS, XCLIENT or
+/*     XFORWARD. Clients that need this should be whitelisted or
+/*     should talk directly to the submission service.  Support
+/*     for STARTTLS may be added later.
+/*
+/*     The engine rejects RCPT TO and VRFY commands with the
+/*     state->rcpt_reply response which depends on program history,
+/*     rejects ETRN with a generic response, and closes the
+/*     connection after QUIT.
+/*
+/*     Since this engine defers or rejects all non-junk commands,
+/*     there is no point maintaining separate counters for "error"
+/*     commands and "junk" commands.  Instead, the engine maintains
+/*     a per-session command counter, and terminates the session
+/*     with a 421 reply when the command count exceeds the limit.
+/*
+/*     We limit the command count so that we don't have to worry
+/*     about becoming blocked while sending responses (20 replies
+/*     of about 40 bytes plus greeting banners). Otherwise we would
+/*     have to make the output event-driven, just like the input.
+/* PROTOCOL INSPECTION VERSUS CONTENT INSPECTION
+/*     The goal of postscreen is to keep spambots away from Postfix.
+/*     To recognize spambots, postscreen measures properties of
+/*     the client IP address and of the client SMTP protocol
+/*     implementation.  These client properties don't change with
+/*     each delivery attempt.  Therefore it is possible to make a
+/*     long-term decision after a single measurement.  For example,
+/*     allow a good client to skip the DNSBL test for 24 hours,
+/*     or to skip the pipelining test for one week.
+/*
+/*     If postscreen were to measure properties of message content
+/*     (MIME compliance, etc.) then it would measure properties
+/*     that may change with each delivery attempt.  Here, it would
+/*     be wrong to make a long-term decision after a single
+/*     measurement. Instead, postscreen would need to develop a
+/*     ranking based on the content of multiple messages from the
+/*     same client.
+/*
+/*     Many spambots avoid spamming the same site repeatedly.
+/*     Thus, postscreen must make decisions after a single
+/*     measurement. Message content is not a good indicator for
+/*     making long-term decisions after single measurements, and
+/*     that is why postscreen does not inspect message content.
+/* REJECTING RCPT TO VERSUS SENDING LIVE SOCKETS TO SMTPD(8)
+/*     When deep protocol tests are enabled, postscreen rejects
+/*     the RCPT TO command from a good client, and forces it to
+/*     deliver mail in a later session. This is why deep protocol
+/*     tests have a longer expiration time than pre-handshake
+/*     tests.
+/*
+/*     Instead, postscreen could send the network socket to smtpd(8)
+/*     and ship the session history (including TLS and other SMTP
+/*     or non-SMTP attributes) as auxiliary data. The Postfix SMTP
+/*     server would then use new code to replay the session history,
+/*     and would use existing code to validate the client, helo,
+/*     sender and recipient address.
+/*
+/*     Such an approach would increase the implementation and
+/*     maintenance effort, because:
+/*
+/*     1) New replay code would be needed in smtpd(8), such that
+/*     the HELO, EHLO, and MAIL command handlers can delay their
+/*     error responses until the RCPT TO reply.
+/*
+/*     2) postscreen(8) would have to implement more of smtpd(8)'s
+/*     syntax checks, to avoid confusing delayed "syntax error"
+/*     and other error responses syntax error responses while
+/*     replaying history.
+/*
+/*     3) New code would be needed in postscreen(8) and smtpd(8)
+/*     to send and receive the session history (including TLS and
+/*     other SMTP or non-SMTP attributes) as auxiliary data while
+/*     sending the network socket from postscreen(8) to smtpd(8).
+/* REJECTING RCPT TO VERSUS PROXYING LIVE SESSIONS TO SMTPD(8)
+/*     An alternative would be to proxy the session history to a
+/*     real Postfix SMTP process, presumably passing TLS and other
+/*     attributes via an extended XCLIENT implementation. That
+/*     would require all the work described in 2) above, plus
+/*     duplication of all the features of the smtpd(8) TLS engine,
+/*     plus additional XCLIENT support for a lot more attributes.
+/* 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 <string.h>
+#include <ctype.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+#include <mymalloc.h>
+#include <iostuff.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <is_header.h>
+#include <string_list.h>
+
+/* Application-specific. */
+
+#include <postscreen.h>
+
+ /*
+  * Plan for future body processing. See smtp-sink.c. For now, we have no
+  * per-session push-back except for the single-character push-back that
+  * VSTREAM guarantees after we read one character.
+  */
+#define PS_SMTPD_HAVE_PUSH_BACK(state) (0)
+#define PS_SMTPD_PUSH_BACK_CHAR(state, ch) \
+       vstream_ungetc((state)->smtp_client_stream, (ch))
+#define PS_SMTPD_NEXT_CHAR(state) \
+       VSTREAM_GETC((state)->smtp_client_stream)
+
+ /*
+  * Dynamic reply strings. To minimize overhead we format these once.
+  */
+static char *ps_smtpd_greeting;                /* smtp banner */
+static char *ps_smtpd_helo_reply;      /* helo reply */
+static char *ps_smtpd_ehlo_reply;      /* multi-line ehlo reply */
+static char *ps_smtpd_timeout_reply;   /* timeout reply */
+static char *ps_smtpd_421_reply;       /* generic final_reply value */
+
+ /*
+  * Forward declaration, needed by PS_CLEAR_EVENT_REQUEST.
+  */
+static void ps_smtpd_time_event(int, char *);
+
+ /*
+  * Command parser support.
+  */
+#define PS_SMTPD_NEXT_TOKEN(ptr) mystrtok(&(ptr), " ")
+
+ /*
+  * Encapsulation. We must not forget turn off input/timer events when we
+  * terminate the SMTP protocol engine.
+  * 
+  * It would be safer to turn off input/timer events after each event, and to
+  * turn on input/timer events again when we want more input. But experience
+  * with the Postfix smtp-source and smtp-sink tools shows that this would
+  * noticeably increase the run-time cost.
+  */
+#define PS_CLEAR_EVENT_DROP_SESSION_STATE(state, event, reply) do { \
+       PS_CLEAR_EVENT_REQUEST(vstream_fileno((state)->smtp_client_stream), \
+                                  (event), (char *) (state)); \
+       PS_DROP_SESSION_STATE((state), (reply)); \
+    } while (0);
+
+#define PS_CLEAR_EVENT_HANGUP(state, event) do { \
+       PS_CLEAR_EVENT_REQUEST(vstream_fileno((state)->smtp_client_stream), \
+                                  (event), (char *) (state)); \
+       ps_hangup_event(state); \
+    } while (0);
+
+/* ps_helo_cmd - record HELO and respond */
+
+static int ps_helo_cmd(PS_STATE *state, char *args)
+{
+    char   *helo_name = PS_SMTPD_NEXT_TOKEN(args);
+
+    /*
+     * smtpd(8) incompatibility: we ignore extra words; smtpd(8) saves them.
+     */
+    if (helo_name == 0)
+       return (PS_SEND_REPLY(state, "501 Syntax: HELO hostname\r\n"));
+
+    PS_STRING_UPDATE(state->helo_name, helo_name);
+    PS_STRING_RESET(state->sender);
+    /* Don't downgrade state->protocol, in case some test depends on this. */
+    return (PS_SEND_REPLY(state, ps_smtpd_helo_reply));
+}
+
+/* ps_ehlo_cmd - record EHLO and respond */
+
+static int ps_ehlo_cmd(PS_STATE *state, char *args)
+{
+    char   *helo_name = PS_SMTPD_NEXT_TOKEN(args);
+
+    /*
+     * smtpd(8) incompatibility: we ignore extra words; smtpd(8) saves them.
+     */
+    if (helo_name == 0)
+       return (PS_SEND_REPLY(state, "501 Syntax: EHLO hostname\r\n"));
+
+    PS_STRING_UPDATE(state->helo_name, helo_name);
+    PS_STRING_RESET(state->sender);
+    state->protocol = MAIL_PROTO_ESMTP;
+    return (PS_SEND_REPLY(state, ps_smtpd_ehlo_reply));
+}
+
+/* ps_extract_addr - extract MAIL/RCPT address, unquoted form */
+
+static char *ps_extract_addr(VSTRING *result, const char *string)
+{
+    const unsigned char *cp = (const unsigned char *) string;
+    int     stop_at;
+    int     inquote = 0;
+
+    /*
+     * smtpd(8) incompatibility: we allow more invalid address forms, and we
+     * don't strip @site1,site2:user@site3 route addresses. We are not going
+     * to deliver them so we won't have to worry about addresses that end up
+     * being nonsense after stripping. This may have to change when we pass
+     * the socket to a real SMTP server and replay message envelope commands.
+     */
+
+    /* Skip SP characters. */
+    while (*cp && *cp == ' ')
+       cp++;
+
+    /* Choose the terminator for <addr> or bare addr. */
+    if (*cp == '<') {
+       cp++;
+       stop_at = '>';
+    } else {
+       stop_at = ' ';
+    }
+
+    /* Skip to terminator or end. */
+    VSTRING_RESET(result);
+    for ( /* void */ ; *cp; cp++) {
+       if (!inquote && *cp == stop_at)
+           break;
+       if (*cp == '"') {
+           inquote = !inquote;
+       } else {
+           if (*cp == '\\' && *++cp == 0)
+               break;
+           VSTRING_ADDCH(result, *cp);
+       }
+    }
+    VSTRING_TERMINATE(result);
+    return (STR(result));
+}
+
+/* ps_mail_cmd - record MAIL and respond */
+
+static int ps_mail_cmd(PS_STATE *state, char *args)
+{
+    char   *colon;
+    char   *addr;
+
+    /*
+     * smtpd(8) incompatibility: we never reject the sender, and we ignore
+     * additional arguments.
+     */
+    if (var_ps_helo_required && state->helo_name == 0)
+       return (PS_SEND_REPLY(state,
+                             "503 5.5.1 Error: send HELO/EHLO first\r\n"));
+    if (state->sender != 0)
+       return (PS_SEND_REPLY(state,
+                             "503 5.5.1 Error: nested MAIL command\r\n"));
+    if (args == 0 || (colon = strchr(args, ':')) == 0)
+       return (PS_SEND_REPLY(state,
+                             "501 5.5.4 Syntax: MAIL FROM:<address>\r\n"));
+    if ((addr = ps_extract_addr(ps_temp, colon + 1)) == 0)
+       return (PS_SEND_REPLY(state,
+                             "501 5.1.7 Bad sender address syntax\r\n"));
+    PS_STRING_UPDATE(state->sender, addr);
+    return (PS_SEND_REPLY(state, "250 2.1.0 Ok\r\n"));
+}
+
+/* ps_rcpt_cmd record RCPT and respond */
+
+static int ps_rcpt_cmd(PS_STATE *state, char *args)
+{
+    char   *colon;
+    char   *addr;
+
+    /*
+     * smtpd(8) incompatibility: we reject all recipients, and ignore
+     * additional arguments.
+     */
+    if (state->sender == 0)
+       return (PS_SEND_REPLY(state,
+                             "503 5.5.1 Error: need MAIL command\r\n"));
+    if (args == 0 || (colon = strchr(args, ':')) == 0)
+       return (PS_SEND_REPLY(state,
+                             "501 5.5.4 Syntax: RCPT TO:<address>\r\n"));
+    if ((addr = ps_extract_addr(ps_temp, colon + 1)) == 0)
+       return (PS_SEND_REPLY(state,
+                             "501 5.1.3 Bad recipient address syntax\r\n"));
+    msg_info("NOQUEUE: reject: RCPT from [%s]: %.*s; "
+            "from=<%s>, to=<%s>, proto=%s, helo=<%s>",
+            state->smtp_client_addr,
+            (int) strlen(state->rcpt_reply) - 2, state->rcpt_reply,
+            state->sender, addr, state->protocol,
+            state->helo_name ? state->helo_name : "");
+    return (PS_SEND_REPLY(state, state->rcpt_reply));
+}
+
+/* ps_data_cmd - respond to DATA and disconnect */
+
+static int ps_data_cmd(PS_STATE *state, char *args)
+{
+
+    /*
+     * smtpd(8) incompatibility: we reject all requests.
+     */
+    if (PS_SMTPD_NEXT_TOKEN(args) != 0)
+       return (PS_SEND_REPLY(state,
+                             "501 5.5.4 Syntax: DATA\r\n"));
+    if (state->sender == 0)
+       return (PS_SEND_REPLY(state,
+                             "503 5.5.1 Error: need RCPT command\r\n"));
+
+    /*
+     * We really would like to hang up the connection as early as possible,
+     * so that we dont't have to deal with broken zombies that fall silent at
+     * the first reject response. For now we rely on stress-dependent command
+     * read timeouts.
+     */
+    return (PS_SEND_REPLY(state,
+                         "554 5.5.1 Error: no valid recipients\r\n"));
+}
+
+/* ps_rset_cmd - reset, send 250 OK */
+
+static int ps_rset_cmd(PS_STATE *state, char *unused_args)
+{
+    PS_STRING_RESET(state->sender);
+    return (PS_SEND_REPLY(state, "250 2.0.0 Ok\r\n"));
+}
+
+/* ps_noop_cmd - respond to something */
+
+static int ps_noop_cmd(PS_STATE *state, char *unused_args)
+{
+    return (PS_SEND_REPLY(state, "250 2.0.0 Ok\r\n"));
+}
+
+/* ps_vrfy_cmd - respond to VRFY */
+
+static int ps_vrfy_cmd(PS_STATE *state, char *args)
+{
+
+    /*
+     * smtpd(8) incompatibility: we reject all requests, and ignore
+     * additional arguments.
+     */
+    if (PS_SMTPD_NEXT_TOKEN(args) == 0)
+       return (PS_SEND_REPLY(state,
+                             "501 5.5.4 Syntax: VRFY address\r\n"));
+    if (var_ps_disable_vrfy)
+       return (PS_SEND_REPLY(state,
+                             "502 5.5.1 VRFY command is disabled\r\n"));
+    return (PS_SEND_REPLY(state, state->rcpt_reply));
+}
+
+/* ps_etrn_cmd - reset, send 250 OK */
+
+static int ps_etrn_cmd(PS_STATE *state, char *args)
+{
+
+    /*
+     * smtpd(8) incompatibility: we reject all requests, and ignore
+     * additional arguments.
+     */
+    if (var_ps_helo_required && state->helo_name == 0)
+       return (PS_SEND_REPLY(state,
+                             "503 5.5.1 Error: send HELO/EHLO first\r\n"));
+    if (PS_SMTPD_NEXT_TOKEN(args) == 0)
+       return (PS_SEND_REPLY(state,
+                             "500 Syntax: ETRN domain\r\n"));
+    return (PS_SEND_REPLY(state, "458 Unable to queue messages\r\n"));
+}
+
+/* ps_quit_cmd - respond to QUIT and disconnect */
+
+static int ps_quit_cmd(PS_STATE *state, char *unused_args)
+{
+    const char *myname = "ps_quit_cmd";
+
+    PS_CLEAR_EVENT_DROP_SESSION_STATE(state, ps_smtpd_time_event,
+                                     "221 2.0.0 Bye\r\n");
+    /* Caution: state is now a dangling pointer. */
+    return (0);
+}
+
+/* ps_smtpd_time_event - handle per-session time limit */
+
+static void ps_smtpd_time_event(int event, char *context)
+{
+    const char *myname = "ps_smtpd_time_event";
+    PS_STATE *state = (PS_STATE *) context;
+
+    if (msg_verbose > 1)
+       msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from %s:%s flags=%s",
+                myname, ps_post_queue_length, ps_check_queue_length,
+                event, vstream_fileno(state->smtp_client_stream),
+                state->smtp_client_addr, state->smtp_client_port,
+                ps_print_state_flags(state->flags, myname));
+
+    msg_info("COMMAND TIME LIMIT from %s", state->smtp_client_addr);
+    PS_CLEAR_EVENT_DROP_SESSION_STATE(state, ps_smtpd_time_event,
+                                     ps_smtpd_timeout_reply);
+}
+
+ /*
+  * The table of all SMTP commands that we know.
+  */
+typedef struct {
+    const char *name;
+    int     (*action) (PS_STATE *, char *);
+    int     flags;                     /* see below */
+} PS_SMTPD_COMMAND;
+
+#define PS_SMTPD_CMD_FLAG_NONE         (0)     /* no flags (i.e. disabled) */
+#define PS_SMTPD_CMD_FLAG_ENABLE       (1<<0)  /* command is enabled */
+#define PS_SMTPD_CMD_FLAG_DESTROY      (1<<1)  /* dangling pointer alert */
+
+static const PS_SMTPD_COMMAND command_table[] = {
+    "HELO", ps_helo_cmd, PS_SMTPD_CMD_FLAG_ENABLE,
+    "EHLO", ps_ehlo_cmd, PS_SMTPD_CMD_FLAG_ENABLE,
+    "XCLIENT", ps_noop_cmd, PS_SMTPD_CMD_FLAG_NONE,
+    "XFORWARD", ps_noop_cmd, PS_SMTPD_CMD_FLAG_NONE,
+    "AUTH", ps_noop_cmd, PS_SMTPD_CMD_FLAG_NONE,
+    "MAIL", ps_mail_cmd, PS_SMTPD_CMD_FLAG_ENABLE,
+    "RCPT", ps_rcpt_cmd, PS_SMTPD_CMD_FLAG_ENABLE,
+    "DATA", ps_data_cmd, PS_SMTPD_CMD_FLAG_ENABLE,
+    /* ".", ps_dot_cmd, PS_SMTPD_CMD_FLAG_NONE, */
+    "RSET", ps_rset_cmd, PS_SMTPD_CMD_FLAG_ENABLE,
+    "NOOP", ps_noop_cmd, PS_SMTPD_CMD_FLAG_ENABLE,
+    "VRFY", ps_vrfy_cmd, PS_SMTPD_CMD_FLAG_ENABLE,
+    "ETRN", ps_etrn_cmd, PS_SMTPD_CMD_FLAG_ENABLE,
+    "QUIT", ps_quit_cmd, PS_SMTPD_CMD_FLAG_ENABLE | PS_SMTPD_CMD_FLAG_DESTROY,
+    0,
+};
+
+/* ps_smtpd_read_event - pseudo responder */
+
+static void ps_smtpd_read_event(int event, char *context)
+{
+    const char *myname = "ps_smtpd_read_event";
+    PS_STATE *state = (PS_STATE *) context;
+    int     ch;
+    struct cmd_trans {
+       int     state;
+       int     want;
+       int     next_state;
+    };
+
+#define PS_SMTPD_CMD_ST_ANY            0
+#define PS_SMTPD_CMD_ST_CR             1
+#define PS_SMTPD_CMD_ST_CR_LF          2
+
+    static const struct cmd_trans cmd_trans[] = {
+       PS_SMTPD_CMD_ST_ANY, '\r', PS_SMTPD_CMD_ST_CR,
+       PS_SMTPD_CMD_ST_CR, '\n', PS_SMTPD_CMD_ST_CR_LF,
+       0, 0, 0,
+    };
+    const struct cmd_trans *transp;
+    char   *cmd_buffer_ptr;
+    char   *command;
+    const PS_SMTPD_COMMAND *cmdp;
+    int     write_stat;
+
+    if (msg_verbose > 1)
+       msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from %s:%s flags=%s",
+                myname, ps_post_queue_length, ps_check_queue_length,
+                event, vstream_fileno(state->smtp_client_stream),
+                state->smtp_client_addr, state->smtp_client_port,
+                ps_print_state_flags(state->flags, myname));
+
+    /*
+     * Basic liveness requirements.
+     * 
+     * Drain all input in the VSTREAM buffer, otherwise this socket will not
+     * receive further read event notification until the client disconnects!
+     * 
+     * Don't try to read input before it has arrived, otherwise we would starve
+     * the pseudo threads of other sessions. Get out of here as soon as the
+     * VSTREAM read buffer dries up. Do not look for more input in kernel
+     * buffers. That input wasn't likely there when ps_smtpd_read_event() was
+     * called. Also, yielding the pseudo thread will improve fairness for
+     * other pseudo threads.
+     */
+#define PS_SMTPD_BUFFER_EMPTY(state) \
+       (!PS_SMTPD_HAVE_PUSH_BACK(state) \
+       && vstream_peek(state->smtp_client_stream) <= 0)
+
+    /*
+     * Note: on entry into this function the VSTREAM buffer is still empty,
+     * so we test the "no more input" condition at the bottom of the loops.
+     */
+    for (;;) {
+
+       /*
+        * Read one command line, possibly one fragment at a time.
+        */
+       for (;;) {
+
+           if ((ch = PS_SMTPD_NEXT_CHAR(state)) == VSTREAM_EOF) {
+               PS_CLEAR_EVENT_HANGUP(state, ps_smtpd_time_event);
+               return;
+           }
+
+           /*
+            * Sanity check. We don't want to store infinitely long commands.
+            */
+           if (state->read_state == PS_SMTPD_CMD_ST_ANY
+               && VSTRING_LEN(state->cmd_buffer) >= var_line_limit) {
+               msg_info("COMMAND LENGTH LIMIT from %s",
+                        state->smtp_client_addr);
+               PS_CLEAR_EVENT_DROP_SESSION_STATE(state, ps_smtpd_time_event,
+                                                 ps_smtpd_421_reply);
+               return;
+           }
+           VSTRING_ADDCH(state->cmd_buffer, ch);
+
+           /*
+            * Try to match the current character desired by the state
+            * machine. If that fails, try to restart the machine with a
+            * match for its first state. smtpd(8) incompatibility: we
+            * require that lines end in <CR><LF>, while smtpd(8) allows
+            * lines ending in <CR><LF> and bare <LF>.
+            */
+           for (transp = cmd_trans; transp->state != state->read_state; transp++)
+               if (transp->want == 0)
+                   msg_panic("%s: command_read: unknown state: %d",
+                             myname, state->read_state);
+           if (ch == transp->want)
+               state->read_state = transp->next_state;
+           else if (ch == cmd_trans[0].want)
+               state->read_state = cmd_trans[0].next_state;
+           else
+               state->read_state = PS_SMTPD_CMD_ST_ANY;
+           if (state->read_state == PS_SMTPD_CMD_ST_CR_LF) {
+               vstring_truncate(state->cmd_buffer,
+                                VSTRING_LEN(state->cmd_buffer) - 2);
+               break;
+           }
+
+           /*
+            * Bare newline test.
+            */
+           if (ch == '\n') {
+               if ((state->flags & PS_STATE_FLAG_BARLF_TODO_SKIP)
+                   == PS_STATE_FLAG_BARLF_TODO) {
+                   msg_info("BARE NEWLINE from %s", state->smtp_client_addr);
+                   PS_FAIL_SESSION_STATE(state, PS_STATE_FLAG_BARLF_FAIL);
+                   PS_UNPASS_SESSION_STATE(state, PS_STATE_FLAG_BARLF_PASS);
+                   state->barlf_stamp = PS_TIME_STAMP_DISABLED;        /* XXX */
+                   /* Skip this test for the remainder of this session. */
+                   PS_SKIP_SESSION_STATE(state, "bare newline test",
+                                         PS_STATE_FLAG_BARLF_SKIP);
+                   switch (ps_barlf_action) {
+                   case PS_ACT_DROP:
+                       PS_CLEAR_EVENT_DROP_SESSION_STATE(state,
+                                                       ps_smtpd_time_event,
+                                           "521 5.5.1 Protocol error\r\n");
+                       return;
+                   case PS_ACT_ENFORCE:
+                       PS_ENFORCE_SESSION_STATE(state,
+                                           "550 5.5.1 Protocol error\r\n");
+                       break;
+                   case PS_ACT_IGNORE:
+                       PS_UNFAIL_SESSION_STATE(state,
+                                               PS_STATE_FLAG_BARLF_FAIL);
+                       /* Temporarily whitelist until something expires. */
+                       PS_PASS_SESSION_STATE(state, "bare newline test",
+                                             PS_STATE_FLAG_BARLF_PASS);
+                       state->barlf_stamp = event_time() + ps_min_ttl;
+                       break;
+                   default:
+                       msg_panic("%s: unknown bare_newline action value %d",
+                                 myname, ps_barlf_action);
+                   }
+               }
+               vstring_truncate(state->cmd_buffer,
+                                VSTRING_LEN(state->cmd_buffer) - 1);
+               break;
+           }
+
+           /*
+            * Yield this pseudo thread when the VSTREAM buffer is empty.
+            * 
+            * Set a short per-command timeout if under stress.
+            */
+           if (PS_SMTPD_BUFFER_EMPTY(state)) {
+               event_request_timer(ps_smtpd_time_event, (char *) state,
+                                   PS_EFF_CMD_TIME_LIMIT);
+               return;
+           }
+       }
+
+       /*
+        * Terminate the command line, and reset the command buffer write
+        * pointer and state machine in preparation for the next command. For
+        * this to work as expected, VSTRING_RESET() must be non-destructive.
+        */
+       VSTRING_TERMINATE(state->cmd_buffer);
+       state->read_state = PS_SMTPD_CMD_ST_ANY;
+       VSTRING_RESET(state->cmd_buffer);
+
+       /*
+        * Process the command line.
+        * 
+        * Caution: some command handlers terminate the session and destroy the
+        * session state structure. When this happens we must leave the SMTP
+        * engine to avoid a dangling pointer problem.
+        */
+       cmd_buffer_ptr = vstring_str(state->cmd_buffer);
+       if (msg_verbose)
+           msg_info("< %s:%s: %s", state->smtp_client_addr,
+                    state->smtp_client_port, cmd_buffer_ptr);
+
+       /* Parse the command name. */
+       if ((command = PS_SMTPD_NEXT_TOKEN(cmd_buffer_ptr)) == 0)
+           command = "";
+
+       /*
+        * The non-SMTP, PIPELINING and command COUNT tests depend on the
+        * client command handler.
+        * 
+        * Caution: cmdp->name and cmdp->action may be null on loop exit.
+        */
+       for (cmdp = command_table; cmdp->name != 0; cmdp++)
+           if (strcasecmp(command, cmdp->name) == 0)
+               break;
+
+       /* Non-SMTP command test. */
+       if ((state->flags & PS_STATE_FLAG_NSMTP_TODO_SKIP)
+           == PS_STATE_FLAG_NSMTP_TODO && cmdp->name == 0
+           && (is_header(command)
+               || (*var_ps_forbid_cmds
+                   && string_list_match(ps_forbid_cmds, command)))) {
+           printable(command, '?');
+           msg_info("NON-SMTP COMMAND from %s %.100s",
+                    state->smtp_client_addr, command);
+           PS_FAIL_SESSION_STATE(state, PS_STATE_FLAG_NSMTP_FAIL);
+           PS_UNPASS_SESSION_STATE(state, PS_STATE_FLAG_NSMTP_PASS);
+           state->nsmtp_stamp = PS_TIME_STAMP_DISABLED;        /* XXX */
+           /* Skip this test for the remainder of this SMTP session. */
+           PS_SKIP_SESSION_STATE(state, "non-smtp test",
+                                 PS_STATE_FLAG_NSMTP_SKIP);
+           switch (ps_nsmtp_action) {
+           case PS_ACT_DROP:
+               PS_CLEAR_EVENT_DROP_SESSION_STATE(state,
+                                                 ps_smtpd_time_event,
+                  "521 5.7.0 Error: I can break rules, too. Goodbye.\r\n");
+               return;
+           case PS_ACT_ENFORCE:
+               PS_ENFORCE_SESSION_STATE(state,
+                                        "550 5.5.1 Protocol error\r\n");
+               break;
+           case PS_ACT_IGNORE:
+               PS_UNFAIL_SESSION_STATE(state,
+                                       PS_STATE_FLAG_NSMTP_FAIL);
+               /* Temporarily whitelist until something else expires. */
+               PS_PASS_SESSION_STATE(state, "non-smtp test",
+                                     PS_STATE_FLAG_NSMTP_PASS);
+               state->nsmtp_stamp = event_time() + ps_min_ttl;
+               break;
+           default:
+               msg_panic("%s: unknown non_smtp_command action value %d",
+                         myname, ps_nsmtp_action);
+           }
+       }
+       /* Command PIPELINING test. */
+       if ((state->flags & PS_STATE_FLAG_PIPEL_TODO_SKIP)
+           == PS_STATE_FLAG_PIPEL_TODO && !PS_SMTPD_BUFFER_EMPTY(state)) {
+           printable(command, '?');
+           msg_info("COMMAND PIPELINING from %s after %.100s",
+                    state->smtp_client_addr, command);
+           PS_FAIL_SESSION_STATE(state, PS_STATE_FLAG_PIPEL_FAIL);
+           PS_UNPASS_SESSION_STATE(state, PS_STATE_FLAG_PIPEL_PASS);
+           state->pipel_stamp = PS_TIME_STAMP_DISABLED;        /* XXX */
+           /* Skip this test for the remainder of this SMTP session. */
+           PS_SKIP_SESSION_STATE(state, "pipelining test",
+                                 PS_STATE_FLAG_PIPEL_SKIP);
+           switch (ps_pipel_action) {
+           case PS_ACT_DROP:
+               PS_CLEAR_EVENT_DROP_SESSION_STATE(state,
+                                                 ps_smtpd_time_event,
+                                           "521 5.5.1 Protocol error\r\n");
+               return;
+           case PS_ACT_ENFORCE:
+               PS_ENFORCE_SESSION_STATE(state,
+                                        "550 5.5.1 Protocol error\r\n");
+               break;
+           case PS_ACT_IGNORE:
+               PS_UNFAIL_SESSION_STATE(state,
+                                       PS_STATE_FLAG_PIPEL_FAIL);
+               /* Temporarily whitelist until something else expires. */
+               PS_PASS_SESSION_STATE(state, "pipelining test",
+                                     PS_STATE_FLAG_PIPEL_PASS);
+               state->pipel_stamp = event_time() + ps_min_ttl;
+               break;
+           default:
+               msg_panic("%s: unknown pipelining action value %d",
+                         myname, ps_pipel_action);
+           }
+       }
+
+       /*
+        * The following tests don't pass until the client gets all the way
+        * to the RCPT TO command. However, the client can still fail these
+        * tests with some later command.
+        */
+       if (cmdp->action == ps_rcpt_cmd) {
+           if ((state->flags & PS_STATE_FLAG_BARLF_TODO_PASS_FAIL)
+               == PS_STATE_FLAG_BARLF_TODO) {
+               PS_PASS_SESSION_STATE(state, "bare newline test",
+                                     PS_STATE_FLAG_BARLF_PASS);
+               /* XXX Reset to PS_TIME_STAMP_DISABLED on failure. */
+               state->barlf_stamp = event_time() + var_ps_barlf_ttl;
+           }
+           if ((state->flags & PS_STATE_FLAG_NSMTP_TODO_PASS_FAIL)
+               == PS_STATE_FLAG_NSMTP_TODO) {
+               PS_PASS_SESSION_STATE(state, "non-smtp test",
+                                     PS_STATE_FLAG_NSMTP_PASS);
+               /* XXX Reset to PS_TIME_STAMP_DISABLED on failure. */
+               state->nsmtp_stamp = event_time() + var_ps_nsmtp_ttl;
+           }
+           if ((state->flags & PS_STATE_FLAG_PIPEL_TODO_PASS_FAIL)
+               == PS_STATE_FLAG_PIPEL_TODO) {
+               PS_PASS_SESSION_STATE(state, "pipelining test",
+                                     PS_STATE_FLAG_PIPEL_PASS);
+               /* XXX Reset to PS_TIME_STAMP_DISABLED on failure. */
+               state->pipel_stamp = event_time() + var_ps_pipel_ttl;
+           }
+       }
+       /* Command COUNT limit test. */
+       if (++state->command_count > var_ps_cmd_count
+           && cmdp->action != ps_quit_cmd) {
+           msg_info("COMMAND COUNT LIMIT from %s", state->smtp_client_addr);
+           PS_CLEAR_EVENT_DROP_SESSION_STATE(state, ps_smtpd_time_event,
+                                             ps_smtpd_421_reply);
+           return;
+       }
+       /* Finally, execute the command. */
+       if (cmdp->name == 0 || (cmdp->flags & PS_SMTPD_CMD_FLAG_ENABLE) == 0) {
+           write_stat = PS_SEND_REPLY(state,
+                            "502 5.5.2 Error: command not recognized\r\n");
+       } else {
+           write_stat = cmdp->action(state, cmd_buffer_ptr);
+           if (cmdp->flags & PS_SMTPD_CMD_FLAG_DESTROY)
+               return;
+       }
+
+       /*
+        * Terminate the session after a write error.
+        */
+       if (write_stat < 0) {
+           PS_CLEAR_EVENT_HANGUP(state, ps_smtpd_time_event);
+           return;
+       }
+
+       /*
+        * Yield this pseudo thread when the VSTREAM buffer is empty.
+        * 
+        * Set a short per-command timeout if under stress.
+        */
+       if (PS_SMTPD_BUFFER_EMPTY(state)) {
+           event_request_timer(ps_smtpd_time_event, (char *) state,
+                               PS_EFF_CMD_TIME_LIMIT);
+           return;
+       }
+    }
+}
+
+/* ps_smtpd_tests - per-session deep protocol test initialization */
+
+void    ps_smtpd_tests(PS_STATE *state)
+{
+    static char *myname = "ps_smtpd_tests";
+
+    /*
+     * Report errors and progress in the context of this test.
+     */
+    PS_BEGIN_TESTS(state, "tests after SMTP handshake");
+
+    /*
+     * Initialize per-session state that is used only by the dummy engine:
+     * the command read buffer and the command read state machine.
+     */
+    state->cmd_buffer = vstring_alloc(100);
+    state->read_state = PS_SMTPD_CMD_ST_ANY;
+
+    /*
+     * Opportunistically make postscreen more useful by turning on the
+     * pipelining and non-SMTP command tests when a pre-handshake test
+     * failed, or when some deep test is configured as enabled.
+     * 
+     * XXX Make "opportunistically" configurable for each test.
+     */
+    state->flags |= (PS_STATE_FLAG_PIPEL_TODO | PS_STATE_FLAG_NSMTP_TODO | \
+                    PS_STATE_FLAG_BARLF_TODO);
+
+    /*
+     * Send the SMTP banner.
+     */
+    if (PS_SEND_REPLY(state, ps_smtpd_greeting) != 0) {
+       ps_hangup_event(state);
+       return;
+    }
+
+    /*
+     * Wait for the client to respond.
+     */
+    PS_READ_EVENT_REQUEST2(vstream_fileno(state->smtp_client_stream),
+                          ps_smtpd_read_event, ps_smtpd_time_event,
+                          (char *) state, PS_EFF_CMD_TIME_LIMIT);
+}
+
+/* ps_smtpd_init - per-process deep protocol test initialization */
+
+void    ps_smtpd_init(void)
+{
+
+    /*
+     * Initialize the server banner.
+     */
+    vstring_sprintf(ps_temp, "220 %s\r\n", var_smtpd_banner);
+    ps_smtpd_greeting = mystrdup(STR(ps_temp));
+
+    /*
+     * Initialize the HELO reply.
+     */
+    vstring_sprintf(ps_temp, "250 %s\r\n", var_myhostname);
+    ps_smtpd_helo_reply = mystrdup(STR(ps_temp));
+
+    /*
+     * Initialize the EHLO reply.
+     */
+    vstring_sprintf(ps_temp,
+                   "250-%s\r\n",               /* NOT: PIPELINING */
+                   var_myhostname);
+    if (var_message_limit)
+       vstring_sprintf_append(ps_temp,
+                              "250-SIZE %lu\r\n",
+                              (unsigned long) var_message_limit);
+    else
+       vstring_sprintf_append(ps_temp,
+                              "250-SIZE\r\n");
+    if (var_disable_vrfy_cmd == 0)
+       vstring_sprintf_append(ps_temp,
+                              "250-VRFY\r\n");
+    vstring_sprintf_append(ps_temp,
+                          "250-ETRN\r\n"
+                          "250-ENHANCEDSTATUSCODES\r\n"
+                          "250-8BITMIME\r\n"
+                          "250 DSN\r\n");
+    ps_smtpd_ehlo_reply = mystrdup(STR(ps_temp));
+
+    /*
+     * Initialize the 421 timeout reply.
+     */
+    vstring_sprintf(ps_temp, "421 4.4.2 %s Error: timeout exceeded\r\n",
+                   var_myhostname);
+    ps_smtpd_timeout_reply = mystrdup(STR(ps_temp));
+
+    /*
+     * Initialize the generic 421 reply.
+     */
+    vstring_sprintf(ps_temp, "421 %s Service unavailable - try again later\r\n",
+                   var_myhostname);
+    ps_smtpd_421_reply = mystrdup(STR(ps_temp));
+}
diff --git a/postfix/src/postscreen/postscreen_state.c b/postfix/src/postscreen/postscreen_state.c
new file mode 100644 (file)
index 0000000..b449f95
--- /dev/null
@@ -0,0 +1,254 @@
+/*++
+/* NAME
+/*     postscreen_state 3
+/* SUMMARY
+/*     postscreen session state and queue length management
+/* SYNOPSIS
+/*     #include <postscreen.h>
+/*
+/*     PS_STATE *ps_new_session_state(stream, addr, port)
+/*     VSTREAM *stream;
+/*     const char *addr;
+/*     const char *port;
+/*
+/*     void    ps_free_session_state(state)
+/*     PS_STATE *state;
+/*
+/*     char    *ps_print_state_flags(flags, context)
+/*     int     flags;
+/*     const char *context;
+/*
+/*     void    PS_ADD_SERVER_STATE(state, server_fd)
+/*     PS_STATE *state;
+/*     int     server_fd;
+/*
+/*     void    PS_DEL_CLIENT_STATE(state)
+/*     PS_STATE *state;
+/*
+/*     void    PS_DROP_SESSION_STATE(state, final_reply)
+/*     PS_STATE *state;
+/*     const char *final_reply;
+/*
+/*     void    PS_ENFORCE_SESSION_STATE(state, rcpt_reply)
+/*     PS_STATE *state;
+/*     const char *rcpt_reply;
+/*
+/*     void    PS_PASS_SESSION_STATE(state, testname, pass_flag)
+/*     PS_STATE *state;
+/*     const char *testname;
+/*     int     pass_flag;
+/*
+/*     void    PS_FAIL_SESSION_STATE(state, fail_flag)
+/*     PS_STATE *state;
+/*     int     fail_flag;
+/*
+/*     void    PS_UNFAIL_SESSION_STATE(state, fail_flag)
+/*     PS_STATE *state;
+/*     int     fail_flag;
+/* DESCRIPTION
+/*     This module maintains per-client session state, and two
+/*     global file descriptor counters:
+/* .IP ps_check_queue_length
+/*     The total number of remote SMTP client sockets.
+/* .IP ps_post_queue_length
+/*     The total number of server file descriptors that are currently
+/*     in use for client file descriptor passing. This number
+/*     equals the number of client file descriptors in transit.
+/* .PP
+/*     ps_new_session_state() creates a new session state object
+/*     for the specified client stream, and increments the
+/*     ps_check_queue_length counter.  The flags and per-test time
+/*     stamps are initialized with PS_INIT_TESTS().  The addr and
+/*     port arguments are null-terminated strings with the remote
+/*     SMTP client endpoint. The _reply members are set to
+/*     polite "try again" SMTP replies. The protocol member is set
+/*     to "SMTP". 
+/*
+/*     The ps_stress variable is set to non-zero when
+/*     ps_check_queue_length passes over a high-water mark.
+/*
+/*     ps_free_session_state() destroys the specified session state
+/*     object, closes the applicable I/O channels, and decrements
+/*     the applicable file descriptor counters: ps_check_queue_length
+/*     and ps_post_queue_length.
+/*
+/*     The ps_stress variable is reset to zero when ps_check_queue_length
+/*     passes under a low-water mark.
+/*
+/*     ps_print_state_flags() converts per-session flags into
+/*     human-readable form. The context is for error reporting.
+/*     The result is overwritten upon each call.
+/*
+/*     PS_ADD_SERVER_STATE() updates the specified session state
+/*     object with the specified server file descriptor, and
+/*     increments the global ps_post_queue_length file descriptor
+/*     counter.
+/*
+/*     PS_DEL_CLIENT_STATE() updates the specified session state
+/*     object, closes the client stream, and decrements the global
+/*     ps_check_queue_length file descriptor counter.
+/*
+/*     PS_DROP_SESSION_STATE() updates the specified session state
+/*     object and closes the client stream after sending the
+/*     specified SMTP reply.
+/*
+/*     PS_ENFORCE_SESSION_STATE() updates the specified session
+/*     state object. It arranges that the built-in SMTP engine
+/*     logs sender/recipient information and rejects all RCPT TO
+/*     commands with the specified SMTP reply.
+/*
+/*     PS_PASS_SESSION_STATE() sets the specified "pass" flag.
+/*     The testname is used for debug logging.
+/*
+/*     PS_FAIL_SESSION_STATE() sets the specified "fail" flag.
+/*
+/*     PS_UNFAIL_SESSION_STATE() unsets the specified "fail" flag.
+/* 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>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <name_mask.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+
+/* Master server protocols. */
+
+#include <mail_server.h>
+
+/* Application-specific. */
+
+#include <postscreen.h>
+
+/* ps_new_session_state - fill in connection state for event processing */
+
+PS_STATE *ps_new_session_state(VSTREAM *stream,
+                                      const char *addr,
+                                      const char *port)
+{
+    PS_STATE *state;
+
+    state = (PS_STATE *) mymalloc(sizeof(*state));
+    PS_INIT_TESTS(state);
+    if ((state->smtp_client_stream = stream) != 0)
+       ps_check_queue_length++;
+    state->smtp_server_fd = (-1);
+    state->smtp_client_addr = mystrdup(addr);
+    state->smtp_client_port = mystrdup(port);
+    state->test_name = "TEST NAME HERE";
+    state->dnsbl_reply = 0;
+    state->final_reply = "421 4.3.2 Service currently unavailable\r\n";
+    state->rcpt_reply = "450 4.3.2 Service currently unavailable\r\n";
+    state->command_count = 0;
+    state->protocol = MAIL_PROTO_SMTP;
+    state->helo_name = 0;
+    state->sender = 0;
+    state->cmd_buffer = 0;
+    state->read_state = 0;
+
+    /*
+     * Update the stress level.
+     */
+    if (ps_stress == 0
+       && ps_check_queue_length >= ps_check_queue_length_hiwat) {
+       ps_stress = 1;
+       msg_info("entering STRESS mode with %d connections",
+                ps_check_queue_length);
+    }
+    return (state);
+}
+
+/* ps_free_session_state - destroy connection state including connections */
+
+void    ps_free_session_state(PS_STATE *state)
+{
+    if (state->smtp_client_stream != 0) {
+       event_server_disconnect(state->smtp_client_stream);
+       ps_check_queue_length--;
+    }
+    if (state->smtp_server_fd >= 0) {
+       close(state->smtp_server_fd);
+       ps_post_queue_length--;
+    }
+    myfree(state->smtp_client_addr);
+    myfree(state->smtp_client_port);
+    if (state->dnsbl_reply)
+       vstring_free(state->dnsbl_reply);
+    if (state->helo_name)
+       myfree(state->helo_name);
+    if (state->sender)
+       myfree(state->sender);
+    if (state->cmd_buffer)
+       vstring_free(state->cmd_buffer);
+    myfree((char *) state);
+
+    if (ps_check_queue_length < 0 || ps_post_queue_length < 0)
+       msg_panic("bad queue length: check_queue=%d, post_queue=%d",
+                 ps_check_queue_length, ps_post_queue_length);
+
+    /*
+     * Update the stress level.
+     */
+    if (ps_stress != 0
+       && ps_check_queue_length <= ps_check_queue_length_lowat) {
+       ps_stress = 0;
+       msg_info("leaving STRESS mode with %d connections",
+                ps_check_queue_length);
+    }
+}
+
+/* ps_print_state_flags - format state flags */
+
+const char *ps_print_state_flags(int flags, const char *context)
+{
+    static const NAME_MASK flags_mask[] = {
+       "NOFORWARD", PS_STATE_FLAG_NOFORWARD,
+       "NEW", PS_STATE_FLAG_NEW,
+       "BLIST_FAIL", PS_STATE_FLAG_BLIST_FAIL,
+       "HANGUP", PS_STATE_FLAG_HANGUP,
+       "CACHE_EXPIRED", PS_STATE_FLAG_CACHE_EXPIRED,
+
+       "PREGR_FAIL", PS_STATE_FLAG_PREGR_FAIL,
+       "PREGR_PASS", PS_STATE_FLAG_PREGR_PASS,
+       "PREGR_TODO", PS_STATE_FLAG_PREGR_TODO,
+
+       "DNSBL_FAIL", PS_STATE_FLAG_DNSBL_FAIL,
+       "DNSBL_PASS", PS_STATE_FLAG_DNSBL_PASS,
+       "DNSBL_TODO", PS_STATE_FLAG_DNSBL_TODO,
+
+       "PIPEL_FAIL", PS_STATE_FLAG_PIPEL_FAIL,
+       "PIPEL_PASS", PS_STATE_FLAG_PIPEL_PASS,
+       "PIPEL_TODO", PS_STATE_FLAG_PIPEL_TODO,
+       "PIPEL_SKIP", PS_STATE_FLAG_PIPEL_SKIP,
+
+       "NSMTP_FAIL", PS_STATE_FLAG_NSMTP_FAIL,
+       "NSMTP_PASS", PS_STATE_FLAG_NSMTP_PASS,
+       "NSMTP_TODO", PS_STATE_FLAG_NSMTP_TODO,
+       "NSMTP_SKIP", PS_STATE_FLAG_NSMTP_SKIP,
+
+       "BARLF_FAIL", PS_STATE_FLAG_BARLF_FAIL,
+       "BARLF_PASS", PS_STATE_FLAG_BARLF_PASS,
+       "BARLF_TODO", PS_STATE_FLAG_BARLF_TODO,
+       "BARLF_SKIP", PS_STATE_FLAG_BARLF_SKIP,
+       0,
+    };
+
+    return (str_name_mask_opt((VSTRING *) 0, context, flags_mask, flags,
+                             NAME_MASK_PIPE | NAME_MASK_NUMBER));
+}
diff --git a/postfix/src/postscreen/postscreen_tests.c b/postfix/src/postscreen/postscreen_tests.c
new file mode 100644 (file)
index 0000000..cfd2de5
--- /dev/null
@@ -0,0 +1,291 @@
+/*++
+/* NAME
+/*     postscreen_tests 3
+/* SUMMARY
+/*     postscreen tests timestamp/flag bulk support
+/* SYNOPSIS
+/*     #include <postscreen.h>
+/*
+/*     void    PS_INIT_TESTS(state)
+/*     PS_STATE *state;
+/*
+/*     void    ps_new_tests(state)
+/*     PS_STATE *state;
+/*
+/*     void    ps_parse_tests(state, stamp_text, time_value)
+/*     PS_STATE *state;
+/*     const char *stamp_text;
+/*     time_t time_value;
+/*
+/*     char    *ps_print_tests(buffer, state)
+/*     VSTRING *buffer;
+/*     PS_STATE *state;
+/*
+/*     char    *ps_print_grey_key(buffer, client, helo, sender, rcpt)
+/*     VSTRING *buffer;
+/*     const char *client;
+/*     const char *helo;
+/*     const char *sender;
+/*     const char *rcpt;
+/* DESCRIPTION
+/*     The functions in this module overwrite the per-test expiration
+/*     time stamps and all flags bits.  Some functions are implemented
+/*     as unsafe macros, meaning they evaluate one ore more arguments
+/*     multiple times.
+/*
+/*     PS_INIT_TESTS() is an unsafe macro that sets the per-test
+/*     expiration time stamps to PS_TIME_STAMP_INVALID, and that
+/*     zeroes all the flags bits. These values are not meant to
+/*     be stored into the postscreen(8) cache.
+/*
+/*     ps_new_tests() sets all test expiration time stamps to
+/*     PS_TIME_STAMP_NEW, and overwrites all flags bits. Only
+/*     enabled tests are flagged with PS_STATE_FLAG_TODO; the
+/*     object is flagged with PS_STATE_FLAG_NEW.
+/*
+/*     ps_parse_tests() parses a cache file record and overwrites
+/*     all flags bits. Tests are considered "expired" when they
+/*     would be expired at the specified time value. Only enabled
+/*     tests are flagged as "expired"; the object is flagged as
+/*     "new" if some enabled tests have "new" time stamps.
+/*
+/*     ps_print_tests() creates a cache file record for the
+/*     specified flags and per-test expiration time stamps.
+/*     This may modify the time stamps for disabled tests.
+/*
+/*     ps_print_grey_key() prints a greylist lookup key.
+/* 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 <stdio.h>                     /* sscanf */
+
+/* Utility library. */
+
+#include <msg.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <postscreen.h>
+
+ /*
+  * Kludge to detect if some test is enabled.
+  */
+#define PS_PREGR_TEST_ENABLE() (*var_ps_pregr_banner != 0)
+#define PS_DNSBL_TEST_ENABLE() (*var_ps_dnsbl_sites != 0)
+
+ /*
+  * Format of a persistent cache entry (which is almost but not quite the
+  * same as the in-memory representation).
+  * 
+  * Each cache entry has one time stamp for each test.
+  * 
+  * - A time stamp of PS_TIME_STAMP_INVALID must never appear in the cache. It
+  * is reserved for in-memory objects that are still being initialized.
+  * 
+  * - A time stamp of PS_TIME_STAMP_NEW indicates that the test never passed.
+  * Postscreen will log the client with "pass new" when it passes the final
+  * test.
+  * 
+  * - A time stamp of PS_TIME_STAMP_DISABLED indicates that the test never
+  * passed, and that the test was disabled when the cache entry was written.
+  * 
+  * - Otherwise, the test was passed, and the time stamp indicates when that
+  * test result expires.
+  * 
+  * A cache entry is expired when the time stamps of all passed tests are
+  * expired.
+  */
+
+/* ps_new_tests - initialize new test results from scratch */
+
+void    ps_new_tests(PS_STATE *state)
+{
+
+    /*
+     * We know this client is brand new.
+     */
+    state->flags = PS_STATE_FLAG_NEW;
+
+    /*
+     * Give all tests a PS_TIME_STAMP_NEW time stamp, so that we can later
+     * recognize cache entries that haven't passed all enabled tests. When we
+     * write a cache entry to the database, any new-but-disabled tests will
+     * get a PS_TIME_STAMP_DISABLED time stamp.
+     */
+    state->pregr_stamp = PS_TIME_STAMP_NEW;
+    state->dnsbl_stamp = PS_TIME_STAMP_NEW;
+    state->pipel_stamp = PS_TIME_STAMP_NEW;
+    state->nsmtp_stamp = PS_TIME_STAMP_NEW;
+    state->barlf_stamp = PS_TIME_STAMP_NEW;
+
+    /*
+     * Don't flag disabled tests as "todo", because there would be no way to
+     * make those bits go away.
+     */
+    if (PS_PREGR_TEST_ENABLE())
+       state->flags |= PS_STATE_FLAG_PREGR_TODO;
+    if (PS_DNSBL_TEST_ENABLE())
+       state->flags |= PS_STATE_FLAG_DNSBL_TODO;
+    if (var_ps_pipel_enable)
+       state->flags |= PS_STATE_FLAG_PIPEL_TODO;
+    if (var_ps_nsmtp_enable)
+       state->flags |= PS_STATE_FLAG_NSMTP_TODO;
+    if (var_ps_barlf_enable)
+       state->flags |= PS_STATE_FLAG_BARLF_TODO;
+}
+
+/* ps_parse_tests - parse test results from cache */
+
+void    ps_parse_tests(PS_STATE *state,
+                              const char *stamp_str,
+                              time_t time_value)
+{
+    unsigned long pregr_stamp;
+    unsigned long dnsbl_stamp;
+    unsigned long pipel_stamp;
+    unsigned long nsmtp_stamp;
+    unsigned long barlf_stamp;
+
+    /*
+     * We don't know what tests have expired or have never passed.
+     */
+    state->flags = 0;
+
+    /*
+     * Parse the cache entry, and allow for older postscreen versions that
+     * implemented fewer tests. We pretend that these tests were disabled
+     * when the cache entry was written.
+     * 
+     * Flag the cache entry as "new" when the cache entry has fields for all
+     * enabled tests, but the remote SMTP client has not yet passed all those
+     * tests.
+     */
+    switch (sscanf(stamp_str, "%lu;%lu;%lu;%lu;%lu",
+                  &pregr_stamp, &dnsbl_stamp, &pipel_stamp, &nsmtp_stamp,
+                  &barlf_stamp)) {
+    case 0:
+       pregr_stamp = PS_TIME_STAMP_DISABLED;
+    case 1:
+       dnsbl_stamp = PS_TIME_STAMP_DISABLED;
+    case 2:
+       pipel_stamp = PS_TIME_STAMP_DISABLED;
+    case 3:
+       nsmtp_stamp = PS_TIME_STAMP_DISABLED;
+    case 4:
+       barlf_stamp = PS_TIME_STAMP_DISABLED;
+    default:
+       break;
+    }
+    if ((state->pregr_stamp = pregr_stamp) == PS_TIME_STAMP_NEW
+       || (state->dnsbl_stamp = dnsbl_stamp) == PS_TIME_STAMP_NEW
+       || (state->pipel_stamp = pipel_stamp) == PS_TIME_STAMP_NEW
+       || (state->nsmtp_stamp = nsmtp_stamp) == PS_TIME_STAMP_NEW
+       || (state->barlf_stamp = barlf_stamp) == PS_TIME_STAMP_NEW)
+       state->flags |= PS_STATE_FLAG_NEW;
+
+    /*
+     * Don't flag a cache entry as expired just because some test was never
+     * passed.
+     * 
+     * Don't flag disabled tests as "todo", because there would be no way to
+     * make those bits go away.
+     */
+    if (PS_PREGR_TEST_ENABLE() && time_value > state->pregr_stamp) {
+       state->flags |= PS_STATE_FLAG_PREGR_TODO;
+       if (state->pregr_stamp > PS_TIME_STAMP_DISABLED)
+           state->flags |= PS_STATE_FLAG_CACHE_EXPIRED;
+    }
+    if (PS_DNSBL_TEST_ENABLE() && time_value > state->dnsbl_stamp) {
+       state->flags |= PS_STATE_FLAG_DNSBL_TODO;
+       if (state->dnsbl_stamp > PS_TIME_STAMP_DISABLED)
+           state->flags |= PS_STATE_FLAG_CACHE_EXPIRED;
+    }
+    if (var_ps_pipel_enable && time_value > state->pipel_stamp) {
+       state->flags |= PS_STATE_FLAG_PIPEL_TODO;
+       if (state->pipel_stamp > PS_TIME_STAMP_DISABLED)
+           state->flags |= PS_STATE_FLAG_CACHE_EXPIRED;
+    }
+    if (var_ps_nsmtp_enable && time_value > state->nsmtp_stamp) {
+       state->flags |= PS_STATE_FLAG_NSMTP_TODO;
+       if (state->nsmtp_stamp > PS_TIME_STAMP_DISABLED)
+           state->flags |= PS_STATE_FLAG_CACHE_EXPIRED;
+    }
+    if (var_ps_barlf_enable && time_value > state->barlf_stamp) {
+       state->flags |= PS_STATE_FLAG_BARLF_TODO;
+       if (state->barlf_stamp > PS_TIME_STAMP_DISABLED)
+           state->flags |= PS_STATE_FLAG_CACHE_EXPIRED;
+    }
+
+    /*
+     * Gratuitously make postscreen logging more useful by turning on all
+     * enabled pre-handshake tests when any pre-handshake test is turned on.
+     */
+    if (state->flags & PS_STATE_FLAG_EARLY_TODO) {
+       if (PS_PREGR_TEST_ENABLE())
+           state->flags |= PS_STATE_FLAG_PREGR_TODO;
+       if (PS_DNSBL_TEST_ENABLE())
+           state->flags |= PS_STATE_FLAG_DNSBL_TODO;
+    }
+}
+
+/* ps_print_tests - print postscreen cache record */
+
+char   *ps_print_tests(VSTRING *buf, PS_STATE *state)
+{
+    const char *myname = "ps_print_tests";
+
+    /*
+     * Sanity check.
+     */
+    if ((state->flags & PS_STATE_FLAG_ANY_PASS) == 0)
+       msg_panic("%s: attempt to save a no-pass record", myname);
+
+    /*
+     * Give disabled tests a dummy time stamp so that we don't log a client
+     * with "pass new" when some disabled test becomes enabled at some later
+     * time.
+     */
+    if (PS_PREGR_TEST_ENABLE() == 0 && state->pregr_stamp == PS_TIME_STAMP_NEW)
+       state->pregr_stamp = PS_TIME_STAMP_DISABLED;
+    if (PS_DNSBL_TEST_ENABLE() == 0 && state->dnsbl_stamp == PS_TIME_STAMP_NEW)
+       state->dnsbl_stamp = PS_TIME_STAMP_DISABLED;
+    if (var_ps_pipel_enable == 0 && state->pipel_stamp == PS_TIME_STAMP_NEW)
+       state->pipel_stamp = PS_TIME_STAMP_DISABLED;
+    if (var_ps_nsmtp_enable == 0 && state->nsmtp_stamp == PS_TIME_STAMP_NEW)
+       state->nsmtp_stamp = PS_TIME_STAMP_DISABLED;
+    if (var_ps_barlf_enable == 0 && state->barlf_stamp == PS_TIME_STAMP_NEW)
+       state->barlf_stamp = PS_TIME_STAMP_DISABLED;
+
+    vstring_sprintf(buf, "%lu;%lu;%lu;%lu;%lu",
+                   (unsigned long) state->pregr_stamp,
+                   (unsigned long) state->dnsbl_stamp,
+                   (unsigned long) state->pipel_stamp,
+                   (unsigned long) state->nsmtp_stamp,
+                   (unsigned long) state->barlf_stamp);
+    return (STR(buf));
+}
+
+/* ps_print_grey_key - print postscreen cache record */
+
+char   *ps_print_grey_key(VSTRING *buf, const char *client,
+                                 const char *helo, const char *sender,
+                                 const char *rcpt)
+{
+    return (STR(vstring_sprintf(buf, "%s/%s/%s/%s",
+                               client, helo, sender, rcpt)));
+}
index 8637926c657d39d37c862848a45d9acc0a319430..ce6a4de794d56b614a1ed4db13dbdd56647c3b8a 100644 (file)
 /* .PP
 /*     Available in Postfix version 2.2 and later:
 /* .IP "\fBsmtpd_forbidden_commands (CONNECT, GET, POST)\fR"
-/*     List of commands that causes the Postfix SMTP server to immediately
+/*     List of commands that cause the Postfix SMTP server to immediately
 /*     terminate the session with a 221 code.
 /* .PP
 /*     Available in Postfix version 2.5 and later:
index a6a13bb0ca530b2fdb73247648c7502bfa58ce2a..a31cd83b561a5fd1437aec3a45d8b728a0727c00 100644 (file)
@@ -350,8 +350,10 @@ static int smtpd_proxy_connect(SMTPD_STATE *state)
     /*
      * Connect to proxy.
      */
-    if ((fd = connect_fn(endpoint, BLOCKING, proxy->timeout)) < 0)
+    if ((fd = connect_fn(endpoint, BLOCKING, proxy->timeout)) < 0) {
+       msg_warn("connect to proxy filter %s: %m", proxy->service_name);
        return (smtpd_proxy_rdwr_error(state, 0));
+    }
     proxy->service_stream = vstream_fdopen(fd, O_RDWR);
     /* Needed by our DATA-phase record emulation routines. */
     vstream_control(proxy->service_stream, VSTREAM_CTL_CONTEXT,
index 58b38ecef113f0885d18600478aef326d5ed3257..d9d8826bd7e887f810dc5cdb40359e902301451d 100644 (file)
@@ -230,6 +230,7 @@ static void send_rset(int, char *);
 static void rset_done(int, char *);
 static void send_quit(SESSION *);
 static void quit_done(int, char *);
+static void close_session(SESSION *);
 
 /* random_interval - generate a random value in 0 .. (small) interval */
 
@@ -573,6 +574,10 @@ static void helo_done(int unused_event, char *context)
         /* void */ ;
     } else if (allow_reject) {
        msg_warn("%s rejected: %d %s", protocol, resp->code, resp->str);
+       if (resp->code == 421 || resp->code == 521) {
+           close_session(session);
+           return;
+       }
     } else {
        msg_fatal("%s rejected: %d %s", protocol, resp->code, resp->str);
     }
@@ -621,6 +626,10 @@ static void mail_done(int unused, char *context)
        send_rcpt(unused, context);
     } else if (allow_reject) {
        msg_warn("sender rejected: %d %s", resp->code, resp->str);
+       if (resp->code == 421 || resp->code == 521) {
+           close_session(session);
+           return;
+       }
        send_rset(unused, context);
     } else {
        msg_fatal("sender rejected: %d %s", resp->code, resp->str);
@@ -673,6 +682,10 @@ static void rcpt_done(int unused, char *context)
        session->rcpt_accepted++;
     } else if (allow_reject) {
        msg_warn("recipient rejected: %d %s", resp->code, resp->str);
+       if (resp->code == 421 || resp->code == 521) {
+           close_session(session);
+           return;
+       }
     } else {
        msg_fatal("recipient rejected: %d %s", resp->code, resp->str);
     }
@@ -727,6 +740,10 @@ static void data_done(int unused, char *context)
         /* see below */ ;
     } else if (allow_reject) {
        msg_warn("data rejected: %d %s", resp->code, resp->str);
+       if (resp->code == 421 || resp->code == 521) {
+           close_session(session);
+           return;
+       }
        send_rset(unused, context);
        return;
     } else {
@@ -808,6 +825,10 @@ static void dot_done(int unused_event, char *context)
             /* void */ ;
        } else if (allow_reject) {
            msg_warn("end of data rejected: %d %s", resp->code, resp->str);
+           if (resp->code == 421 || resp->code == 521) {
+               close_session(session);
+               return;
+           }
        } else {
            msg_fatal("end of data rejected: %d %s", resp->code, resp->str);
        }
@@ -852,6 +873,10 @@ static void rset_done(int unused_event, char *context)
        /* void */
     } else if (allow_reject) {
        msg_warn("rset rejected: %d %s", resp->code, resp->str);
+       if (resp->code == 421 || resp->code == 521) {
+           close_session(session);
+           return;
+       }
     } else {
        msg_fatal("rset rejected: %d %s", resp->code, resp->str);
     }
@@ -888,6 +913,16 @@ static void quit_done(int unused_event, char *context)
     start_another(session);
 }
 
+/* close_session - disconnect, for example after 421 or 521 reply */
+
+static void close_session(SESSION *session)
+{
+    event_disable_readwrite(vstream_fileno(session->stream));
+    vstream_fclose(session->stream);
+    session->stream = 0;
+    start_another(session);
+}
+
 /* usage - explain */
 
 static void usage(char *myname)
index 87c006ca73a86ee20a8cbb1d706ea83c61440656..e79a4accdeddf82faac271c6f4faf3966f262c08 100644 (file)
@@ -32,7 +32,7 @@ SRCS  = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
        write_buf.c write_wait.c sane_basename.c format_tv.c allspace.c \
        allascii.c load_file.c killme_after.c vstream_tweak.c upass_connect.c \
        upass_listen.c upass_trigger.c edit_file.c inet_windowsize.c \
-       unix_pass_fd_fix.c dict_cache.c valid_utf_8.c
+       unix_pass_fd_fix.c dict_cache.c valid_utf_8.c dict_thash.c
 OBJS   = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
        attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -66,7 +66,7 @@ OBJS  = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        write_buf.o write_wait.o sane_basename.o format_tv.o allspace.o \
        allascii.o load_file.o killme_after.o vstream_tweak.o upass_connect.o \
        upass_listen.o upass_trigger.o edit_file.o inet_windowsize.o \
-       unix_pass_fd_fix.o dict_cache.o valid_utf_8.o
+       unix_pass_fd_fix.o dict_cache.o valid_utf_8.o dict_thash.o
 HDRS   = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
        chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \
        dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \
@@ -86,7 +86,7 @@ HDRS  = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
        stringops.h sys_defs.h timed_connect.h timed_wait.h trigger.h \
        username.h valid_hostname.h vbuf.h vbuf_print.h vstream.h vstring.h \
        vstring_vstream.h watchdog.h format_tv.h load_file.h killme_after.h \
-       edit_file.h dict_cache.h
+       edit_file.h dict_cache.h dict_thash.h
 TESTSRC        = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
        stream_test.c dup2_pass_on_exec.c test_send_fd test_recv_fd
 DEFS   = -I. -D$(SYSTYPE)
@@ -877,6 +877,7 @@ dict_open.o: dict_regexp.h
 dict_open.o: dict_sdbm.h
 dict_open.o: dict_static.h
 dict_open.o: dict_tcp.h
+dict_open.o: dict_thash.h
 dict_open.o: dict_unix.h
 dict_open.o: htable.h
 dict_open.o: msg.h
@@ -954,6 +955,20 @@ dict_tcp.o: vbuf.h
 dict_tcp.o: vstream.h
 dict_tcp.o: vstring.h
 dict_tcp.o: vstring_vstream.h
+dict_thash.o: argv.h
+dict_thash.o: dict.h
+dict_thash.o: dict_thash.c
+dict_thash.o: dict_thash.h
+dict_thash.o: htable.h
+dict_thash.o: iostuff.h
+dict_thash.o: msg.h
+dict_thash.o: mymalloc.h
+dict_thash.o: readlline.h
+dict_thash.o: stringops.h
+dict_thash.o: sys_defs.h
+dict_thash.o: vbuf.h
+dict_thash.o: vstream.h
+dict_thash.o: vstring.h
 dict_unix.o: argv.h
 dict_unix.o: dict.h
 dict_unix.o: dict_unix.c
@@ -1644,6 +1659,7 @@ valid_hostname.o: valid_hostname.h
 valid_hostname.o: vbuf.h
 valid_hostname.o: vstring.h
 valid_utf_8.o: stringops.h
+valid_utf_8.o: sys_defs.h
 valid_utf_8.o: valid_utf_8.c
 valid_utf_8.o: vbuf.h
 valid_utf_8.o: vstring.h
index b8813f83170661be372ea8874ab2cbcfcdea210e..10b2a3f6db80c8062cd1ee415803a60ab4723609 100644 (file)
@@ -391,7 +391,7 @@ void    dict_load_file(const char *dict_name, const char *path)
        after = time((time_t *) 0);
        if (st.st_mtime < before - 1 || st.st_mtime > after)
            break;
-       if (msg_verbose)
+       if (msg_verbose > 1)
            msg_info("pausing to let %s cool down", path);
        doze(300000);
     }
@@ -459,7 +459,7 @@ const char *dict_eval(const char *dict_name, const char *value, int recursive)
                        DONT_FILTER, dict_eval_lookup, (char *) dict_name);
     if (status & MAC_PARSE_ERROR)
        msg_fatal("dictionary %s: macro processing error", dict_name);
-    if (msg_verbose) {
+    if (msg_verbose > 1) {
        if (strcmp(value, STR(buf)) != 0)
            msg_info("%s: expand %s -> %s", myname, value, STR(buf));
        else
index 9e82f9b81725f2da18c3ab1cb9491adbaf12c7e1..c827b8d20dc82b5e57ce58fa40d0a36fc0d5a9eb 100644 (file)
@@ -547,7 +547,8 @@ static void dict_db_close(DICT *dict)
      * ignore, I'm going to report the bogus error as a non-error.
      */
     if (DICT_DB_CLOSE(dict_db->db) < 0)
-       msg_info("close database %s: %m", dict_db->dict.name);
+       msg_info("close database %s: %m (possible Berkeley DB bug)",
+                dict_db->dict.name);
     if (dict_db->key_buf)
        vstring_free(dict_db->key_buf);
     if (dict_db->val_buf)
index 0e2a3347e5a191f72774f924f3235957957de697..ecaab62b7991fc5cbda9fe1fe3207af03cf4ce95 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef _DICT_DBN_H_INCLUDED_
-#define _DICT_DBN_H_INCLUDED_
+#ifndef _DICT_DBM_H_INCLUDED_
+#define _DICT_DBM_H_INCLUDED_
 
 /*++
 /* NAME
index b807bc2b0f8b5312a7ee6229c9495cdfd3d61219..9e1358f495f0008668ca96290f27e1c45f92a814 100644 (file)
 /*     PERL-compatible regular expressions.
 /* .IP regexp
 /*     POSIX-compatible regular expressions.
+/* .IP texthash
+/*     Flat text in postmap(1) input format.
 /* .PP
 /*     dict_open3() takes separate arguments for dictionary type and
 /*     name, but otherwise performs the same functions as dict_open().
 #include <dict_static.h>
 #include <dict_cidr.h>
 #include <dict_ht.h>
+#include <dict_thash.h>
 #include <stringops.h>
 #include <split_at.h>
 #include <htable.h>
@@ -251,6 +254,7 @@ static const DICT_OPEN_INFO dict_open_info[] = {
 #endif
     DICT_TYPE_STATIC, dict_static_open,
     DICT_TYPE_CIDR, dict_cidr_open,
+    DICT_TYPE_THASH, dict_thash_open,
     0,
 };
 
diff --git a/postfix/src/util/dict_thash.c b/postfix/src/util/dict_thash.c
new file mode 100644 (file)
index 0000000..116ab5b
--- /dev/null
@@ -0,0 +1,264 @@
+/*++
+/* NAME
+/*     dict_thash 3
+/* SUMMARY
+/*     dictionary manager interface to hashed flat text files
+/* SYNOPSIS
+/*     #include <dict_thash.h>
+/*
+/*     DICT    *dict_thash_open(path, open_flags, dict_flags)
+/*     const char *name;
+/*     const char *path;
+/*     int     open_flags;
+/*     int     dict_flags;
+/* DESCRIPTION
+/*     dict_thash_open() opens the named flat text file, creates
+/*     an in-memory hash table, and makes it available via the
+/*     generic interface described in dict_open(3). The input
+/*     format is as with postmap(1).
+/* DIAGNOSTICS
+/*     Fatal errors: cannot open file, out of memory.
+/* SEE ALSO
+/*     dict(3) generic dictionary manager
+/* 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 <sys/stat.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <iostuff.h>
+#include <vstring.h>
+#include <stringops.h>
+#include <readlline.h>
+#include <dict.h>
+#include <dict_thash.h>
+
+/* Application-specific. */
+
+typedef struct {
+    DICT    dict;                      /* generic members */
+    HTABLE *table;                     /* in-memory hash */
+    HTABLE_INFO **info;                        /* for iterator */
+    HTABLE_INFO **cursor;              /* ditto */
+}       DICT_THASH;
+
+#define STR    vstring_str
+
+/* dict_thash_lookup - find database entry */
+
+static const char *dict_thash_lookup(DICT *dict, const char *name)
+{
+    DICT_THASH *dict_thash = (DICT_THASH *) dict;
+    const char *result = 0;
+
+    dict_errno = 0;
+
+    /*
+     * Optionally fold the key.
+     */
+    if (dict->flags & DICT_FLAG_FOLD_FIX) {
+       if (dict->fold_buf == 0)
+           dict->fold_buf = vstring_alloc(10);
+       vstring_strcpy(dict->fold_buf, name);
+       name = lowercase(vstring_str(dict->fold_buf));
+    }
+
+    /*
+     * Look up the value.
+     */
+    result = htable_find(dict_thash->table, name);
+
+    return (result);
+}
+
+/* dict_thash_sequence - traverse the dictionary */
+
+static int dict_thash_sequence(DICT *dict, int function,
+                                      const char **key, const char **value)
+{
+    const char *myname = "dict_thash_sequence";
+    DICT_THASH *dict_thash = (DICT_THASH *) dict;
+
+    /*
+     * Determine and execute the seek function.
+     */
+    switch (function) {
+    case DICT_SEQ_FUN_FIRST:
+       if (dict_thash->info == 0)
+           dict_thash->info = htable_list(dict_thash->table);
+       dict_thash->cursor = dict_thash->info;
+       break;
+    case DICT_SEQ_FUN_NEXT:
+       if (dict_thash->cursor[0])
+           dict_thash->cursor += 1;
+       break;
+    default:
+       msg_panic("%s: invalid function: %d", myname, function);
+    }
+
+    /*
+     * Return the entry under the cursor.
+     */
+    if (dict_thash->cursor[0]) {
+       *key = dict_thash->cursor[0]->key;
+       *value = dict_thash->cursor[0]->value;
+       return (0);
+    } else {
+       return (1);
+    }
+}
+
+/* dict_thash_close - disassociate from data base */
+
+static void dict_thash_close(DICT *dict)
+{
+    DICT_THASH *dict_thash = (DICT_THASH *) dict;
+
+    htable_free(dict_thash->table, myfree);
+    if (dict_thash->info)
+       myfree((char *) dict_thash->info);
+    if (dict->fold_buf)
+       vstring_free(dict->fold_buf);
+    dict_free(dict);
+}
+
+/* dict_thash_open - open flat text data base */
+
+DICT   *dict_thash_open(const char *path, int open_flags, int dict_flags)
+{
+    DICT_THASH *dict_thash;
+    VSTREAM *fp;
+    struct stat st;
+    time_t  before;
+    time_t  after;
+    VSTRING *line_buffer = vstring_alloc(100);
+    int     lineno;
+    char   *key;
+    char   *value;
+    HTABLE_INFO *ht;
+
+    /*
+     * Sanity checks.
+     */
+    if (open_flags != O_RDONLY)
+       msg_fatal("%s:%s map requires O_RDONLY access mode",
+                 DICT_TYPE_THASH, path);
+
+    /*
+     * Create the in-memory table.
+     */
+    dict_thash = (DICT_THASH *)
+       dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash));
+    dict_thash->dict.lookup = dict_thash_lookup;
+    dict_thash->dict.sequence = dict_thash_sequence;
+    dict_thash->dict.close = dict_thash_close;
+    dict_thash->dict.flags = dict_flags | DICT_FLAG_FIXED;
+    if (dict_flags & DICT_FLAG_FOLD_FIX)
+       dict_thash->dict.fold_buf = vstring_alloc(10);
+    dict_thash->info = 0;
+
+    /*
+     * Read the flat text file into in-memory hash. Read the file again if it
+     * may have changed while we were reading.
+     */
+    for (before = time((time_t *) 0); /* see below */ ; before = after) {
+       if ((fp = vstream_fopen(path, open_flags, 0644)) == 0)
+           msg_fatal("open database %s: %m", path);
+       lineno = 0;
+       dict_thash->table = htable_create(13);
+       while (readlline(line_buffer, fp, &lineno)) {
+
+           /*
+            * Split on the first whitespace character, then trim leading and
+            * trailing whitespace from key and value.
+            */
+           key = STR(line_buffer);
+           value = key + strcspn(key, " \t\r\n");
+           if (*value)
+               *value++ = 0;
+           while (ISSPACE(*value))
+               value++;
+           trimblanks(key, 0)[0] = 0;
+           trimblanks(value, 0)[0] = 0;
+
+           /*
+            * Enforce the "key whitespace value" format. Disallow missing
+            * keys or missing values.
+            */
+           if (*key == 0 || *value == 0) {
+               msg_warn("%s, line %d: expected format: key whitespace value"
+                        " -- ignoring this line", path, lineno);
+               continue;
+           }
+           if (key[strlen(key) - 1] == ':')
+               msg_warn("%s, line %d: record is in \"key: value\" format;"
+                        " is this an alias file?", path, lineno);
+
+           /*
+            * Optionally fold the key.
+            */
+           if (dict_thash->dict.flags & DICT_FLAG_FOLD_FIX)
+               lowercase(key);
+
+           /*
+            * Store the value under the key. Handle duplicates
+            * appropriately.
+            */
+           if ((ht = htable_locate(dict_thash->table, key)) != 0) {
+               if (dict_thash->dict.flags & DICT_FLAG_DUP_IGNORE) {
+                    /* void */ ;
+               } else if (dict_thash->dict.flags & DICT_FLAG_DUP_REPLACE) {
+                   myfree(ht->value);
+                   ht->value = mystrdup(value);
+               } else if (dict_thash->dict.flags & DICT_FLAG_DUP_WARN) {
+                   msg_warn("%s, line %d: duplicate entry: \"%s\"",
+                            path, lineno, key);
+               } else {
+                   msg_fatal("%s, line %d: duplicate entry: \"%s\"",
+                             path, lineno, key);
+               }
+           } else {
+               htable_enter(dict_thash->table, key, mystrdup(value));
+           }
+       }
+
+       /*
+        * See if the source file is hot.
+        */
+       if (fstat(vstream_fileno(fp), &st) < 0)
+           msg_fatal("fstat %s: %m", path);
+       if (vstream_fclose(fp))
+           msg_fatal("read %s: %m", path);
+       after = time((time_t *) 0);
+       if (st.st_mtime < before - 1 || st.st_mtime > after)
+           break;
+
+       /*
+        * Yes, it is hot. Discard the result and read the file again.
+        */
+       htable_free(dict_thash->table, myfree);
+       if (msg_verbose > 1)
+           msg_info("pausing to let file %s cool down", path);
+       doze(300000);
+    }
+    vstring_free(line_buffer);
+
+    return (DICT_DEBUG (&dict_thash->dict));
+}
diff --git a/postfix/src/util/dict_thash.h b/postfix/src/util/dict_thash.h
new file mode 100644 (file)
index 0000000..878366a
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _DICT_THASH_H_INCLUDED_
+#define _DICT_THASH_H_INCLUDED_
+
+/*++
+/* NAME
+/*     dict_thash 3h
+/* SUMMARY
+/*     dictionary manager interface to flat text files
+/* SYNOPSIS
+/*     #include <dict_thash.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <dict.h>
+
+ /*
+  * External interface.
+  */
+#define DICT_TYPE_THASH        "texthash"
+
+extern DICT *dict_thash_open(const char *, int, int);
+
+/* 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