-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
-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
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
* 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
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:
--- /dev/null
+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".
+
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:
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
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
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
======================================
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.
$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
$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
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
--- /dev/null
+<!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 <CR><LF>. </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>
+
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>
<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>
</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
<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>
<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
<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>
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>
<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>
<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>
<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>
</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>
</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
<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>
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>
<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>
<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>
<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>
</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>
<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>
(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:"
</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
<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>
<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.
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
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.
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>
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>
<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.
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:
.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:
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)
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)
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.
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.
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)
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
.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:"
.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.
.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
.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
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.
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.
.SH "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.
+.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
.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
+.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
.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:
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;
# 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;
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
../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 \
../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 \
../html/PGSQL_README.html: PGSQL_README.html
$(POSTLINK) $? >$@
+../html/POSTSCREEN_README.html: POSTSCREEN_README.html
+ $(POSTLINK) $? >$@
+
../html/QMQP_README.html: QMQP_README.html
$(POSTLINK) $? >$@
../README_FILES/PGSQL_README: PGSQL_README.html
$(HT2READ) $? >$@
+../README_FILES/POSTSCREEN_README: POSTSCREEN_README.html
+ $(HT2READ) $? >$@
+
../README_FILES/QMQP_README: QMQP_README.html
$(HT2READ) $? >$@
--- /dev/null
+<!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 <CR><LF>. </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>
+
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>
<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>
</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
%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:"
<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
<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>
<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>
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>
<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
<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
<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>
<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
<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
<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>
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>
+
unsubscribe
wl
zen
+Blocklist
+DNSBLs
+MailChannels
+Postscreen
+Spambots
+WHITELISTED
+blocklist
+dnsbl
+dnsblog
+postscreen
+postscreen's
+spambots
+spamd
+texthash
+ul
+whitelisted
+whitelists
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 \
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 \
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
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
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
#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. */
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);
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.
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
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 *);
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
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
--- /dev/null
+/*++
+/* 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++;
+ }
+}
/* 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
#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"
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 ""
#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
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
* 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
/* 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.
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 */
{
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
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;
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;
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.
#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
/* 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
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;
/* 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
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;
/* 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
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;
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
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
}
}
}
+/^(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.
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)
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);
}
/* .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:
#include "str_vars.h"
#include "raw_vars.h"
#include "nint_vars.h"
+#include "nbool_vars.h"
/*
* Manually extracted.
0,
};
+static const CONFIG_NBOOL_TABLE nbool_table[] = {
+#include "nbool_table.h"
+ 0,
+};
+
/*
* Parameters with default values obtained via function calls.
*/
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);
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 */
}
}
+/* 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)
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);
}
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)
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
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
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
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
/* 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)
{
/*
* 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;
* 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) {
}
}
-/* 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);
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);
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);
}
/*
&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;
(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 */
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
/*
* 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);
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;
/*
* 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
* 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;
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,
};
*/
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);
}
/* 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.
#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.
#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
*/
* 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
/* 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) */
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;
{
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
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);
}
/* 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;
* 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);
}
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.
* 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);
* 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;
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);
}
}
--- /dev/null
+/*++
+/* 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));
+ }
+}
--- /dev/null
+/*++
+/* 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);
+}
--- /dev/null
+/*++
+/* 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;
+ }
+}
--- /dev/null
+/*++
+/* 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));
+}
--- /dev/null
+/*++
+/* 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));
+}
--- /dev/null
+/*++
+/* 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)));
+}
/* .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:
/*
* 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,
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 */
/* 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);
}
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);
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);
}
/* 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 {
/* 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);
}
/* 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);
}
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)
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 \
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 \
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)
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
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
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
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);
}
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
* 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)
-#ifndef _DICT_DBN_H_INCLUDED_
-#define _DICT_DBN_H_INCLUDED_
+#ifndef _DICT_DBM_H_INCLUDED_
+#define _DICT_DBM_H_INCLUDED_
/*++
/* NAME
/* 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>
#endif
DICT_TYPE_STATIC, dict_static_open,
DICT_TYPE_CIDR, dict_cidr_open,
+ DICT_TYPE_THASH, dict_thash_open,
0,
};
--- /dev/null
+/*++
+/* 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));
+}
--- /dev/null
+#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