]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.8-20100830
authorWietse Venema <wietse@porcupine.org>
Sun, 29 Aug 2010 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:36:26 +0000 (06:36 +0000)
19 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/RELEASE_NOTES
postfix/html/postconf.5.html
postfix/html/postscreen.8.html
postfix/man/man5/postconf.5
postfix/man/man8/postscreen.8
postfix/mantools/postlink
postfix/proto/postconf.proto
postfix/src/dns/test_dns_lookup.c
postfix/src/dnsblog/dnsblog.c
postfix/src/global/mail_params.h
postfix/src/global/mail_proto.h
postfix/src/global/mail_version.h
postfix/src/postscreen/Makefile.in
postfix/src/postscreen/postscreen.c
postfix/src/postscreen/postscreen.h [new file with mode: 0644]
postfix/src/postscreen/postscreen_dict.c [new file with mode: 0644]
postfix/src/postscreen/postscreen_dnsbl.c [new file with mode: 0644]

index bb92580f813ef50b781dba179ba2eb12e6360e46..b07186824b344dffa94c33b016388ca9ef549453 100644 (file)
 -TPOSTMAP_KEY_STATE
 -TPOST_MAIL_STATE
 -TPRIVATE_STR_TABLE
--TPS_DNSBL_ENTRY
--TPS_DNS_STREAM
+-TPS_DNSBL_SITE
+-TPS_DNSBL_SCORE
 -TPS_STATE
 -TQMGR_ENTRY
 -TQMGR_FEEDBACK
index 6eb9d6e4a6cc5bdfc0d863025d679e05ff4591b8..038bf8aeb90e6c5b866ae683e13672101d48098f 100644 (file)
@@ -15908,3 +15908,30 @@ Apologies for any names omitted.
        "smtp_dns_resolver_options = res_defnames" to get the old
        behavior, which can produce unexpected results. Files:
        smtp/smtp.c, smtp/smtp_params.c, smtp/smtp_addr.c.
+
+20100828
+
+       Refactoring: postscreen source code broken up into multiple
+       files, and identifiers updated to match changes in their
+       purpose.  This will be the baseline for adding support for
+       DNSBL weighting, then a dummy engine to collect forensic
+       evidence with the option of future protocol checks.  Files:
+       postscreen/*.[hc], Makefile.in.
+
+20100829
+
+       Postscreen DNSBL support for optional fixed-string filters
+       and optional integral weight factors (use negative weights
+       for whitelisting). See RELEASE_NOTES and postconf(5) for
+       details.  Files: postscreen/postscreen_dnsbl.c,
+       proto/postconf.proto, mantools.postlink, global/mail_params.h.
+
+       Incompatibility: the postscreen-to-dnsblog protocol was
+       changed to support DNSBL query result filters. Use "postfix
+       reload" after installing the new version otherwise the
+       dnsblog(8) server may complain.
+
+20100830
+
+       Polished the postscreen documentation and comments to clarify
+       the user interface and implementation. No code changes.
index ee2f3f90fbf6dd52dba77a2f4392b9ef017c1283..a3f0f9ea21750742d81a3f362526483816b9463e 100644 (file)
@@ -14,6 +14,46 @@ specifies the release date of a stable release or snapshot release.
 If you upgrade from Postfix 2.6 or earlier, read RELEASE_NOTES-2.7
 before proceeding.
 
+Incompatibility with snapshot 20100830
+======================================
+
+Use "postfix reload" after installing this code, otherwise the
+dnsblog(8) daemon may complain.  The postscreen-to-dnsblog protocol
+had to be changed to support DNSBL query result filters.
+
+Major changes with snapshot 20100830
+====================================
+
+Postscreen DNSBL support is extended with optional fixed-string
+filters, with optional integral weight factors, and with an adjustable
+threshold to block SMTP clients with DNSBL score >= that threshold.
+Support for wild-card patterns will be added later.
+
+The updated postscreen configuration syntax is:
+
+    postscreen_dnsbl_sites = domain[=ipaddr][*weight] ...
+    postscreen_dnsbl_threshold = score
+
+Elements inside [] are optional, ipaddr is an IPv4 address, and
+weight and score are integral numbers. The [] are not part of the
+postscreen_dnsbl_sites input.  By default, weight and score are
+equal to 1, and entries without filter will match any non-error
+DNSBL reply.  Use a negative weight value for whitelisting.
+
+Examples:
+
+To use example.com as a high-confidence blocklist, and to block
+mail with example.net and example.org only when both agree, use:
+
+    postscreen_dnsbl_threshold = 2
+    postscreen_dnsbl_sites = example.com*2, example.net, example.org
+
+To filter only DNSBL replies containing 127.0.0.4, use:
+
+    postscreen_dnsbl_sites = example.com=127.0.0.4
+
+See also postconf(5) for the fine details.
+
 Incompatibility with snapshot 20100827
 ======================================
 
index c5a4be1a43eb010a108c2488a025450e6984cc65..ef30d5b9b188158023e098427116da3f36dbee2b 100644 (file)
@@ -6623,8 +6623,9 @@ parameter.  Specify one of the following: </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 triggers a PREGREET or HANGUP
-error, or whether the client is listed at the DNSBL sites specified
-with the <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> parameter.  Take the corresponding
+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>
 
@@ -6678,7 +6679,7 @@ seconds. </p>
 </DD>
 
 <DT><b><a name="postscreen_cache_map">postscreen_cache_map</a>
-(default: btree:$<a href="postconf.5.html#data_directory">data_directory</a>/ps_whitelist)</b></DT><DD>
+(default: btree:$<a href="postconf.5.html#data_directory">data_directory</a>/ps_cache)</b></DT><DD>
 
 <p> Persistent storage for the <a href="postscreen.8.html">postscreen(8)</a> server decisions. </p>
 
@@ -6724,9 +6725,10 @@ unit).  </p>
 <DT><b><a name="postscreen_dnsbl_action">postscreen_dnsbl_action</a>
 (default: continue)</b></DT><DD>
 
-<p>The action that <a href="postscreen.8.html">postscreen(8)</a> takes when an SMTP client is listed
-at the DNS blocklist domains specified with the <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a>
-parameter.  Specify one of the following: </p>
+<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
+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).  Specify one of the following: </p>
 
 <dl>
 
@@ -6748,14 +6750,70 @@ parameter.  Specify one of the following: </p>
 <DT><b><a name="postscreen_dnsbl_sites">postscreen_dnsbl_sites</a>
 (default: empty)</b></DT><DD>
 
-<p>Optional list of DNS blocklist domains. When the list is non-enpty,
-the <a href="dnsblog.8.html">dnsblog(8)</a> daemon will query these domains with the IP addresses
-of non-whitelisted <a href="postscreen.8.html">postscreen(8)</a> clients. Specify a list of domain
-names, separated by comma or whitespace. </p>
+<p>Optional list of DNS blocklist domains, filters and weight
+factors. When the list is non-empty, the <a href="dnsblog.8.html">dnsblog(8)</a> daemon will
+query these domains with the IP addresses of non-whitelisted remote
+SMTP clients, and <a href="postscreen.8.html">postscreen(8)</a> will update an SMTP client's DNSBL
+score with each non-error reply. </p>
+
+<p> 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> Specify a list of domain=filter*weight entries, separated by
+comma or whitespace.  </p>
+
+<ul>
+
+<li> <p> When no "=filter" is specified, <a href="postscreen.8.html">postscreen(8)</a> will use any
+non-error DNSBL reply.  Otherwise, the filter must be an IPv4
+address, and <a href="postscreen.8.html">postscreen(8)</a> uses only DNSBL replies that match the
+filter.  </p>
+
+<li> <p> When no "*weight" is specified, <a href="postscreen.8.html">postscreen(8)</a> increments
+the SMTP client's DNSBL score by 1.  Otherwise, the weight must be
+an integral number, and <a href="postscreen.8.html">postscreen(8)</a> adds the specified weight to
+the SMTP client's DNSBL score.  Specify a negative number for
+whitelisting.  </p>
+
+<li> <p> When one <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> entry produces multiple
+DNSBL responses, <a href="postscreen.8.html">postscreen(8)</a> applies the weight at most once.
+</p>
+
+</ul>
+
+<p> Examples: </p>
+
+<p> To use example.com as a high-confidence blocklist, and to
+block mail with example.net and example.org only when both agree:
+</p>
+
+<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> = example.com*2, example.net, example.org
+</pre>
+
+<p> To filter only DNSBL replies containing 127.0.0.4: </p>
+
+<pre>
+<a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> = example.com=127.0.0.4
+</pre>
 
 <p> This feature is available in Postfix 2.8. </p>
 
 
+</DD>
+
+<DT><b><a name="postscreen_dnsbl_threshold">postscreen_dnsbl_threshold</a>
+(default: 1)</b></DT><DD>
+
+<p> The inclusive lower bound for blocking an SMTP client, based on
+its combined DNSBL score as defined with the <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a>
+parameter. </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
+
+
 </DD>
 
 <DT><b><a name="postscreen_greet_action">postscreen_greet_action</a>
@@ -6770,10 +6828,11 @@ parameter.  Specify one of the following: </p>
 <dt> continue </dt>
 
 <dd> Continue waiting until the <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> time has
-elapsed. If the client is listed at the DNS blocklist domains
-specified with the <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> parameter, 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>
+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>
 
 <dt> drop </dt>
 
@@ -6836,9 +6895,11 @@ without sending data, within the time specified with the
 <dt> continue </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 is listed at the DNSBL sites
-specified with the <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> parameter. Do not
-forward the broken connection to a real SMTP server process. </dd>
+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>
 
@@ -8617,17 +8678,17 @@ discard EHLO keywords selectively. </p>
 (default: empty)</b></DT><DD>
 
 <p> DNS Resolver options for the Postfix SMTP client.  Specify zero
-or more of the following, separated by comma or whitespace. Option
-names are case-sensitive. Some options refer to domain names that
-are specified in /etc/resolv.conf or equivalent. </p>
+or more of the following options, separated by comma or whitespace.
+Option names are case-sensitive. Some options refer to domain names
+that are specified in the file /etc/resolv.conf or equivalent. </p>
 
 <dl>
 
 <dt><b>res_defnames</b></dt>
 
-<dd> Append the <a href="ADDRESS_CLASS_README.html#default_domain_class">default domain</a> name to single-component names (those
-that do not contain a dot). This can produce incorrect results,
-and was the behavior prior to Postfix 2.8. </dd>
+<dd> Append the current domain name to single-component names (those
+that do not contain a "." character). This can produce incorrect
+results, and is the hard-coded behavior prior to Postfix 2.8. </dd>
 
 <dt><b>res_dnsrch</b></dt>
 
index b90d47a146afa2c355b6279b5d6f7f536912991c..0620620cf743a058f717979c42522386e145fdc6 100644 (file)
@@ -182,30 +182,31 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
        are made in parallel.
 
        When  the  <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> time has elapsed, and the
-       SMTP client address is listed with at least one  of  these
-       blocklists, this is logged as:
+       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  client  at <i>address</i> is listed with <i>count</i>
-       DNSBL servers. The <i>count</i> does not depend on the number  of
-       DNS records that an individual DNSBL server returns.
+       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 next:
+       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.
 
-       <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>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>SECURITY</b>
        The <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server is moderately security-sensitive.
-       It talks to untrusted clients on the network. The  process
+       It  talks to untrusted clients on the network. The process
        can be run chrooted at fixed low privilege.
 
 <b>STANDARDS</b>
@@ -216,34 +217,42 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
        Problems and transactions are logged to <b>syslogd</b>(8).
 
 <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.
 
 <b>TRIAGE PARAMETERS</b>
        <b><a href="postconf.5.html#postscreen_blacklist_action">postscreen_blacklist_action</a> (continue)</b>
-              The  action  that  <a href="postscreen.8.html"><b>postscreen</b>(8)</a> takes when an SMTP
-              client  is   permanently   blacklisted   with   the
+              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.
 
        <b><a href="postconf.5.html#postscreen_blacklist_networks">postscreen_blacklist_networks</a> (empty)</b>
               Network addresses that are permanently blacklisted;
-              see the <a href="postconf.5.html#postscreen_blacklist_action">postscreen_blacklist_action</a>  parameter  for
+              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
-              client is listed at the DNS blocklist domains spec-
-              ified with the <a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> parameter.
+              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).
 
        <b><a href="postconf.5.html#postscreen_dnsbl_sites">postscreen_dnsbl_sites</a> (empty)</b>
-              Optional list of DNS blocklist domains.
+              Optional list of DNS blocklist domains, filters and
+              weight factors.
+
+       <b><a href="postconf.5.html#postscreen_dnsbl_threshold">postscreen_dnsbl_threshold</a> (1)</b>
+              The  inclusive  lower  bound  for  blocking an SMTP
+              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
+              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.
 
@@ -251,72 +260,72 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
               The  <i>text</i>  in  the  optional  "220-<i>text</i>..."  server
               response that <a href="postscreen.8.html"><b>postscreen</b>(8)</a> sends ahead of the real
               Postfix SMTP server's "220 text..." response, in an
-              attempt to confuse bad SMTP clients  so  that  they
+              attempt  to  confuse  bad SMTP clients so that they
               speak before their turn (pre-greet).
 
        <b><a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</a> (4s)</b>
               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,
+              an  SMTP  client to send a command before its turn,
               and for DNS blocklist lookup results to arrive.
 
        <b><a href="postconf.5.html#postscreen_hangup_action">postscreen_hangup_action</a> (continue)</b>
-              The  action  that  <a href="postscreen.8.html"><b>postscreen</b>(8)</a> takes when an SMTP
+              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>
+              time  specified  with   the   <a href="postconf.5.html#postscreen_greet_wait">postscreen_greet_wait</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-
+              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
+              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_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
+              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_whitelist)</b>
-              Persistent  storage  for  the  <a href="postscreen.8.html"><b>postscreen</b>(8)</a> server
+       <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
               decisions.
 
        <b><a href="postconf.5.html#postscreen_cache_retention_time">postscreen_cache_retention_time</a> (1d)</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
+              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>MISCELLANEOUS CONTROLS</b>
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The  default  location  of  the Postfix <a href="postconf.5.html">main.cf</a> and
+              The default location of  the  Postfix  <a href="postconf.5.html">main.cf</a>  and
               <a href="master.5.html">master.cf</a> configuration files.
 
        <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How much time a Postfix daemon process may take  to
-              handle  a  request  before  it  is  terminated by a
+              How  much time a Postfix daemon process may take to
+              handle a request  before  it  is  terminated  by  a
               built-in watchdog timer.
 
        <b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
-              The maximal number  of  digits  after  the  decimal
+              The  maximal  number  of  digits  after the decimal
               point when logging sub-second delay values.
 
        <b><a href="postconf.5.html#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>
@@ -324,24 +333,24 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
               over an internal communication channel.
 
        <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
-              The  maximum  amount  of  time that an idle Postfix
-              daemon process waits  for  an  incoming  connection
+              The maximum amount of time  that  an  idle  Postfix
+              daemon  process  waits  for  an incoming connection
               before terminating voluntarily.
 
        <b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
-              The  process  ID  of  a  Postfix  command or daemon
+              The process ID  of  a  Postfix  command  or  daemon
               process.
 
        <b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
-              The process name of a  Postfix  command  or  daemon
+              The  process  name  of  a Postfix command or daemon
               process.
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
-              The  mail  system  name  that  is  prepended to the
-              process name in syslog  records,  so  that  "smtpd"
+              The mail system  name  that  is  prepended  to  the
+              process  name  in  syslog  records, so that "smtpd"
               becomes, for example, "postfix/smtpd".
 
 <b>SEE ALSO</b>
@@ -350,7 +359,7 @@ POSTSCREEN(8)                                                    POSTSCREEN(8)
        syslogd(8), system logging
 
 <b>LICENSE</b>
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 <b>AUTHOR(S)</b>
index 63d69b9b156705e59f366fd4c40dde83eba8c8c9..cae19dc47608fda9442b45f6ebdf38d273a3c245 100644 (file)
@@ -3726,8 +3726,9 @@ 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 is listed at the DNSBL sites specified
-with the postscreen_dnsbl_sites parameter.  Take the corresponding
+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
@@ -3759,7 +3760,7 @@ Time units: s (seconds), m (minutes), h (hours), d (days), w
 (weeks).
 .PP
 This feature is available in Postfix 2.8.
-.SH postscreen_cache_map (default: btree:$data_directory/ps_whitelist)
+.SH postscreen_cache_map (default: btree:$data_directory/ps_cache)
 Persistent storage for the \fBpostscreen\fR(8) server decisions.
 .PP
 This feature is available in Postfix 2.8.
@@ -3786,9 +3787,10 @@ Time units: s (seconds), m (minutes), h (hours), d (days), w
 .PP
 This feature is available in Postfix 2.8.
 .SH postscreen_dnsbl_action (default: continue)
-The action that \fBpostscreen\fR(8) takes when an SMTP client is listed
-at the DNS blocklist domains specified with the postscreen_dnsbl_sites
-parameter.  Specify one of the following:
+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"
@@ -3796,10 +3798,62 @@ Drop the connection with a 521 SMTP reply.
 .PP
 This feature is available in Postfix 2.8.
 .SH postscreen_dnsbl_sites (default: empty)
-Optional list of DNS blocklist domains. When the list is non-enpty,
-the \fBdnsblog\fR(8) daemon will query these domains with the IP addresses
-of non-whitelisted \fBpostscreen\fR(8) clients. Specify a list of domain
-names, separated by comma or whitespace.
+Optional list of DNS blocklist domains, filters and weight
+factors. When the list is non-empty, the \fBdnsblog\fR(8) daemon will
+query these domains with the IP addresses of non-whitelisted remote
+SMTP clients, and \fBpostscreen\fR(8) will update an SMTP client's DNSBL
+score with each non-error reply.
+.PP
+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.
+.PP
+Specify a list of domain=filter*weight entries, separated by
+comma or whitespace.
+.IP \(bu
+When no "=filter" is specified, \fBpostscreen\fR(8) will use any
+non-error DNSBL reply.  Otherwise, the filter must be an IPv4
+address, and \fBpostscreen\fR(8) uses only DNSBL replies that match the
+filter.
+.IP \(bu
+When no "*weight" is specified, \fBpostscreen\fR(8) increments
+the SMTP client's DNSBL score by 1.  Otherwise, the weight must be
+an integral number, and \fBpostscreen\fR(8) adds the specified weight to
+the SMTP client's DNSBL score.  Specify a negative number for
+whitelisting.
+.IP \(bu
+When one postscreen_dnsbl_sites entry produces multiple
+DNSBL responses, \fBpostscreen\fR(8) applies the weight at most once.
+.PP
+Examples:
+.PP
+To use example.com as a high-confidence blocklist, and to
+block mail with example.net and example.org only when both agree:
+.PP
+.nf
+.na
+.ft C
+postscreen_dnsbl_threshold = 2
+postscreen_dnsbl_sites = example.com*2, example.net, example.org
+.fi
+.ad
+.ft R
+.PP
+To filter only DNSBL replies containing 127.0.0.4:
+.PP
+.nf
+.na
+.ft C
+postscreen_dnsbl_sites = example.com=127.0.0.4
+.fi
+.ad
+.ft R
+.PP
+This feature is available in Postfix 2.8.
+.SH postscreen_dnsbl_threshold (default: 1)
+The inclusive lower bound for blocking an SMTP client, based on
+its combined DNSBL score as defined with the postscreen_dnsbl_sites
+parameter.
 .PP
 This feature is available in Postfix 2.8.
 .SH postscreen_greet_action (default: continue)
@@ -3808,10 +3862,11 @@ 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 is listed at the DNS blocklist domains
-specified with the postscreen_dnsbl_sites parameter, execute the
-action specified with the postscreen_dnsbl_action parameter, otherwise
-forward the connection to a real SMTP server process.
+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.
@@ -3847,9 +3902,10 @@ 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 is listed at the DNSBL sites
-specified with the postscreen_dnsbl_sites parameter. Do not
-forward the broken connection to a real SMTP server process.
+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.
@@ -4903,13 +4959,13 @@ Use the smtp_discard_ehlo_keyword_address_maps feature to
 discard EHLO keywords selectively.
 .SH smtp_dns_resolver_options (default: empty)
 DNS Resolver options for the Postfix SMTP client.  Specify zero
-or more of the following, separated by comma or whitespace. Option
-names are case-sensitive. Some options refer to domain names that
-are specified in /etc/resolv.conf or equivalent.
+or more of the following options, separated by comma or whitespace.
+Option names are case-sensitive. Some options refer to domain names
+that are specified in the file /etc/resolv.conf or equivalent.
 .IP "\fBres_defnames\fR"
-Append the default domain name to single-component names (those
-that do not contain a dot). This can produce incorrect results,
-and was the behavior prior to Postfix 2.8.
+Append the current domain name to single-component names (those
+that do not contain a "." character). This can produce incorrect
+results, and is the hard-coded behavior prior to Postfix 2.8.
 .IP "\fBres_dnsrch\fR"
 Search for host names in the current domain and in parent
 domains. This can produce incorrect results and is therefore not
index ea3e7d941cb65b34c0b6861a317844547dc69882..aa890119bc6ad1813c222652df6196bbeb75e904 100644 (file)
@@ -199,20 +199,20 @@ specifies a list of DNS blocklist servers. These lookups
 are made in parallel.
 
 When the postscreen_greet_wait time has elapsed, and the
-SMTP client address is listed with at least one of these
-blocklists, this is logged as:
+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 client at \fIaddress\fR is listed with
-\fIcount\fR DNSBL servers. The \fIcount\fR does not
-depend on the number of DNS records that an individual DNSBL
-server returns.
+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 next:
+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
@@ -262,11 +262,17 @@ parameter.
 Network addresses that are permanently blacklisted; see the
 postscreen_blacklist_action parameter for possible actions.
 .IP "\fBpostscreen_dnsbl_action (continue)\fR"
-The action that \fBpostscreen\fR(8) takes when an SMTP client is listed
-at the DNS blocklist domains specified with the postscreen_dnsbl_sites
-parameter.
+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_sites (empty)\fR"
-Optional list of DNS blocklist domains.
+Optional list of DNS blocklist domains, filters and weight
+factors.
+.IP "\fBpostscreen_dnsbl_threshold (1)\fR"
+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"
 The action that \fBpostscreen\fR(8) takes when an SMTP client speaks
 before its turn within the time specified with the postscreen_greet_wait
@@ -305,7 +311,7 @@ connections to.
 .fi
 .IP "\fBpostscreen_cache_cleanup_interval (12h)\fR"
 The amount of time between \fBpostscreen\fR(8) cache cleanup runs.
-.IP "\fBpostscreen_cache_map (btree:$data_directory/ps_whitelist)\fR"
+.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"
 The amount of time that \fBpostscreen\fR(8) will cache an expired
index b52f16cc213a1917b818deee31171a48b01955b6..e8e29fab169141b784266d01310b802001dca9bd 100755 (executable)
@@ -916,6 +916,7 @@ while (<>) {
     s;\bpostscreen_greet_wait\b;<a href="postconf.5.html#postscreen_greet_wait">$&</a>;g;
     s;\bpostscreen_greet_action\b;<a href="postconf.5.html#postscreen_greet_action">$&</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_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_whitelist_networks\b;<a href="postconf.5.html#postscreen_whitelist_networks">$&</a>;g;
index e2d6e62150205376a50c00d1ced2a12d6951ad8a..f1bccef3342b38085cd37d3e88d6447a62983fcd 100644 (file)
@@ -12512,7 +12512,7 @@ inspection for DKIM-signed mail from known friendly domains. </p>
 <p> This feature is available in Postfix 2.7, and as an optional
 patch for Postfix 2.6. </p>
 
-%PARAM postscreen_cache_map btree:$data_directory/ps_whitelist
+%PARAM postscreen_cache_map btree:$data_directory/ps_cache
 
 <p> Persistent storage for the postscreen(8) server decisions. </p>
 
@@ -12604,18 +12604,63 @@ an optional one-letter suffix that specifies the time unit).  </p>
 
 %PARAM postscreen_dnsbl_sites
 
-<p>Optional list of DNS blocklist domains. When the list is non-enpty,
-the dnsblog(8) daemon will query these domains with the IP addresses
-of non-whitelisted postscreen(8) clients. Specify a list of domain
-names, separated by comma or whitespace. </p>
+<p>Optional list of DNS blocklist domains, filters and weight
+factors. When the list is non-empty, the dnsblog(8) daemon will
+query these domains with the IP addresses of non-whitelisted remote
+SMTP clients, and postscreen(8) will update an SMTP client's DNSBL
+score with each non-error reply. </p>
+
+<p> 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> Specify a list of domain=filter*weight entries, separated by
+comma or whitespace.  </p>
+
+<ul>
+
+<li> <p> When no "=filter" is specified, postscreen(8) will use any
+non-error DNSBL reply.  Otherwise, the filter must be an IPv4
+address, and postscreen(8) uses only DNSBL replies that match the
+filter.  </p>
+
+<li> <p> When no "*weight" is specified, postscreen(8) increments
+the SMTP client's DNSBL score by 1.  Otherwise, the weight must be
+an integral number, and postscreen(8) adds the specified weight to
+the SMTP client's DNSBL score.  Specify a negative number for
+whitelisting.  </p>
+
+<li> <p> When one postscreen_dnsbl_sites entry produces multiple
+DNSBL responses, postscreen(8) applies the weight at most once.
+</p>
+
+</ul>
+
+<p> Examples: </p>
+
+<p> To use example.com as a high-confidence blocklist, and to
+block mail with example.net and example.org only when both agree:
+</p>
+
+<pre> 
+postscreen_dnsbl_threshold = 2 
+postscreen_dnsbl_sites = example.com*2, example.net, example.org 
+</pre>
+
+<p> To filter only DNSBL replies containing 127.0.0.4: </p>
+
+<pre> 
+postscreen_dnsbl_sites = example.com=127.0.0.4 
+</pre>
 
 <p> This feature is available in Postfix 2.8. </p>
 
 %PARAM postscreen_dnsbl_action continue
 
-<p>The action that postscreen(8) takes when an SMTP client is listed
-at the DNS blocklist domains specified with the postscreen_dnsbl_sites
-parameter.  Specify one of the following: </p>
+<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
+with the postscreen_dnsbl_sites and postscreen_dnsbl_threshold
+parameters).  Specify one of the following: </p>
 
 <dl>
 
@@ -12642,10 +12687,11 @@ parameter.  Specify one of the following: </p>
 <dt> continue </dt>
 
 <dd> Continue waiting until the postscreen_greet_wait time has
-elapsed. If the client is listed at the DNS blocklist domains
-specified with the postscreen_dnsbl_sites parameter, execute the
-action specified with the postscreen_dnsbl_action parameter, otherwise
-forward the connection to a real SMTP server process. </dd>
+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> drop </dt>
 
@@ -12671,9 +12717,11 @@ postscreen_greet_wait parameter.  Specify one of the following:
 <dt> continue </dt>
 
 <dd> Continue waiting until the postscreen_greet_wait time has
-elapsed, and report whether the client is listed at the DNSBL sites
-specified with the postscreen_dnsbl_sites parameter. Do not
-forward the broken connection to a real SMTP server process. </dd>
+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>
 
@@ -12726,8 +12774,9 @@ parameter.  Specify one of the following: </p>
 
 <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 is listed at the DNSBL sites specified
-with the postscreen_dnsbl_sites parameter.  Take the corresponding
+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>
 
@@ -12942,17 +12991,17 @@ configuration parameter.  See there for details. </p>
 %PARAM smtp_dns_resolver_options 
 
 <p> DNS Resolver options for the Postfix SMTP client.  Specify zero
-or more of the following, separated by comma or whitespace. Option
-names are case-sensitive. Some options refer to domain names that
-are specified in /etc/resolv.conf or equivalent. </p>
+or more of the following options, separated by comma or whitespace.
+Option names are case-sensitive. Some options refer to domain names
+that are specified in the file /etc/resolv.conf or equivalent. </p>
 
 <dl>
 
 <dt><b>res_defnames</b></dt>
 
-<dd> Append the default domain name to single-component names (those
-that do not contain a dot). This can produce incorrect results,
-and was the behavior prior to Postfix 2.8. </dd>
+<dd> Append the current domain name to single-component names (those
+that do not contain a "." character). This can produce incorrect
+results, and is the hard-coded behavior prior to Postfix 2.8. </dd>
 
 <dt><b>res_dnsrch</b></dt>
 
@@ -12971,3 +13020,10 @@ configuration parameter.  See there for details. </p>
 
 <p> This feature is available in Postfix 2.8 and later.  </p>
 
+%PARAM postscreen_dnsbl_threshold 1
+
+<p> The inclusive lower bound for blocking an SMTP client, based on
+its combined DNSBL score as defined with the postscreen_dnsbl_sites
+parameter. </p>
+
+<p> This feature is available in Postfix 2.8.  </p>
index 9771fc545e8c82c93192a0ffc0872b408215306c..d7a80a4822e5325ae6934b9d5f1340052f033969 100644 (file)
@@ -99,7 +99,7 @@ int     main(int argc, char **argv)
     argv_free(types_argv);
     name = argv[2];
     msg_verbose = 1;
-    switch (dns_lookup_v(name, RES_DEFNAMES | RES_DEBUG, &rr, fqdn, why,
+    switch (dns_lookup_v(name, RES_DEBUG, &rr, fqdn, why,
                         DNS_REQ_FLAG_NONE, types)) {
     default:
        msg_fatal("%s", vstring_str(why));
index 60a943e026c3835a5a4a66eabe9e34265bbcc61a..5cbfa161a5866d8043cc08f33b33a2e84aa89482 100644 (file)
@@ -108,15 +108,18 @@ static VSTRING *rbl_domain;
 static VSTRING *addr;
 static VSTRING *query;
 static VSTRING *why;
+static VSTRING *result;
 
  /*
   * Silly little macros.
   */
 #define STR(x)                 vstring_str(x)
+#define LEN(x)                 VSTRING_LEN(x)
 
 /* static void dnsblog_query - query DNSBL for client address */
 
-static int dnsblog_query(const char *dnsbl_domain, const char *addr)
+static VSTRING *dnsblog_query(VSTRING *result, const char *dnsbl_domain, 
+       const char *addr)
 {
     const char *myname = "dnsblog_query";
     ARGV   *octets;
@@ -127,7 +130,6 @@ static int dnsblog_query(const char *dnsbl_domain, const char *addr)
     DNS_RR *addr_list;
     DNS_RR *rr;
     MAI_HOSTADDR_STR hostaddr;
-    int     found = 0;
 
     if (msg_verbose)
        msg_info("%s: addr %s dnsbl_domain %s",
@@ -168,11 +170,11 @@ static int dnsblog_query(const char *dnsbl_domain, const char *addr)
     }
 
     /*
-     * Tack on the RBL domain name and query the DNS for an A record. Don't
-     * do this for AAAA records. Yet.
+     * Tack on the RBL domain name and query the DNS for an A record.
      */
     vstring_strcat(query, dnsbl_domain);
     dns_status = dns_lookup(STR(query), T_A, 0, &addr_list, (VSTRING *) 0, why);
+    VSTRING_RESET(result);
     if (dns_status == DNS_OK) {
        for (rr = addr_list; rr != 0; rr = rr->next) {
            if (dns_rr_to_pa(rr, &hostaddr) == 0) {
@@ -181,7 +183,9 @@ static int dnsblog_query(const char *dnsbl_domain, const char *addr)
            } else {
                msg_info("addr %s blocked by domain %s as %s",
                         addr, dnsbl_domain, hostaddr.buf);
-               found = 1;
+               if (LEN(result) > 0)
+                   vstring_strcat(result, " ");
+               vstring_strcat(result, hostaddr.buf);
            }
        }
        dns_rr_free(addr_list);
@@ -193,7 +197,8 @@ static int dnsblog_query(const char *dnsbl_domain, const char *addr)
        msg_warn("%s: lookup error for DNS query %s: %s",
                 myname, STR(query), STR(why));
     }
-    return (found);
+    VSTRING_TERMINATE(result);
+    return (result);
 }
 
 /* dnsblog_service - perform service for client */
@@ -201,7 +206,6 @@ static int dnsblog_query(const char *dnsbl_domain, const char *addr)
 static void dnsblog_service(VSTREAM *client_stream, char *unused_service,
                                    char **argv)
 {
-    int     found;
 
     /*
      * Sanity check. This service takes no command-line arguments.
@@ -217,13 +221,13 @@ static void dnsblog_service(VSTREAM *client_stream, char *unused_service,
     if (attr_scan(client_stream,
                  ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
                  ATTR_TYPE_STR, MAIL_ATTR_RBL_DOMAIN, rbl_domain,
-                 ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
+                 ATTR_TYPE_STR, MAIL_ATTR_ACT_CLIENT_ADDR, addr,
                  ATTR_TYPE_END) == 2) {
-       found = dnsblog_query(STR(rbl_domain), STR(addr));
+       (void) dnsblog_query(result, STR(rbl_domain), STR(addr));
        attr_print(client_stream, ATTR_FLAG_NONE,
                   ATTR_TYPE_STR, MAIL_ATTR_RBL_DOMAIN, STR(rbl_domain),
-                  ATTR_TYPE_STR, MAIL_ATTR_ADDR, STR(addr),
-                  ATTR_TYPE_INT, MAIL_ATTR_STATUS, found,
+                  ATTR_TYPE_STR, MAIL_ATTR_ACT_CLIENT_ADDR, STR(addr),
+                  ATTR_TYPE_STR, MAIL_ATTR_RBL_ADDR, STR(result),
                   ATTR_TYPE_END);
        vstream_fflush(client_stream);
     }
@@ -237,6 +241,7 @@ static void post_jail_init(char *unused_name, char **unused_argv)
     addr = vstring_alloc(100);
     query = vstring_alloc(100);
     why = vstring_alloc(100);
+    result = vstring_alloc(100);
 }
 
 MAIL_VERSION_STAMP_DECLARE;
index 200f34888d48561389cfb37cc5d2aa294c6036cc..de9e6b8eaae6c96210397050b59f4a8c87045472 100644 (file)
@@ -3251,6 +3251,10 @@ extern char *var_ps_greet_action;
 #define DEF_PS_DNSBL_SITES     ""
 extern char *var_ps_dnsbl_sites;
 
+#define VAR_PS_DNSBL_THRESH    "postscreen_dnsbl_threshold"
+#define DEF_PS_DNSBL_THRESH    1
+extern int var_ps_dnsbl_thresh;
+
 #define VAR_PS_DNSBL_ACTION    "postscreen_dnsbl_action"
 #define DEF_PS_DNSBL_ACTION    "continue"
 extern char *var_ps_dnsbl_action;
index e4cd574408bb3f4933766b39b36c8682767fc1da..c8b0e6a58cf1c2c9297fee427cd6f0dbd934e26e 100644 (file)
@@ -160,6 +160,7 @@ extern char *mail_pathname(const char *, const char *);
 #define MAIL_ATTR_RBL_TXT      "rbl_txt"       /* LaMont compatibility */
 #define MAIL_ATTR_RBL_CLASS    "rbl_class"
 #define MAIL_ATTR_RBL_CODE     "rbl_code"
+#define MAIL_ATTR_RBL_ADDR     "rbl_addr"
 
  /*
   * The following attribute names are stored in queue files. Changing this
index 3ee5e152902263c8f0a3fa40368cd0be7015f6eb..e4906695f154da9d6ec3f8a118c979535e973b25 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20100827"
+#define MAIL_RELEASE_DATE      "20100829"
 #define MAIL_VERSION_NUMBER    "2.8"
 
 #ifdef SNAPSHOT
index dc3403e6c4cf6c265e97e3b9bc5b3620c25e8a40..ef605a5f7cbd68454813db4edaa52c6fcfa70f1f 100644 (file)
@@ -1,6 +1,6 @@
 SHELL  = /bin/sh
-SRCS   = postscreen.c
-OBJS   = postscreen.o
+SRCS   = postscreen.c postscreen_dict.c postscreen_dnsbl.c
+OBJS   = postscreen.o postscreen_dict.o postscreen_dnsbl.o
 HDRS   = 
 TESTSRC        =
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -87,3 +87,40 @@ postscreen.o: ../../include/vbuf.h
 postscreen.o: ../../include/vstream.h
 postscreen.o: ../../include/vstring.h
 postscreen.o: postscreen.c
+postscreen.o: postscreen.h
+postscreen_dict.o: ../../include/addr_match_list.h
+postscreen_dict.o: ../../include/argv.h
+postscreen_dict.o: ../../include/dict.h
+postscreen_dict.o: ../../include/dict_cache.h
+postscreen_dict.o: ../../include/match_list.h
+postscreen_dict.o: ../../include/match_ops.h
+postscreen_dict.o: ../../include/msg.h
+postscreen_dict.o: ../../include/sys_defs.h
+postscreen_dict.o: ../../include/vbuf.h
+postscreen_dict.o: ../../include/vstream.h
+postscreen_dict.o: ../../include/vstring.h
+postscreen_dict.o: postscreen.h
+postscreen_dict.o: postscreen_dict.c
+postscreen_dnsbl.o: ../../include/addr_match_list.h
+postscreen_dnsbl.o: ../../include/argv.h
+postscreen_dnsbl.o: ../../include/attr.h
+postscreen_dnsbl.o: ../../include/connect.h
+postscreen_dnsbl.o: ../../include/dict.h
+postscreen_dnsbl.o: ../../include/dict_cache.h
+postscreen_dnsbl.o: ../../include/events.h
+postscreen_dnsbl.o: ../../include/htable.h
+postscreen_dnsbl.o: ../../include/iostuff.h
+postscreen_dnsbl.o: ../../include/mail_params.h
+postscreen_dnsbl.o: ../../include/mail_proto.h
+postscreen_dnsbl.o: ../../include/match_list.h
+postscreen_dnsbl.o: ../../include/match_ops.h
+postscreen_dnsbl.o: ../../include/msg.h
+postscreen_dnsbl.o: ../../include/mymalloc.h
+postscreen_dnsbl.o: ../../include/split_at.h
+postscreen_dnsbl.o: ../../include/sys_defs.h
+postscreen_dnsbl.o: ../../include/valid_hostname.h
+postscreen_dnsbl.o: ../../include/vbuf.h
+postscreen_dnsbl.o: ../../include/vstream.h
+postscreen_dnsbl.o: ../../include/vstring.h
+postscreen_dnsbl.o: postscreen.h
+postscreen_dnsbl.o: postscreen_dnsbl.c
index 4cfabad6c74ec1221cd9df33646b7bb21d30e92c..38d534b5d96a429d9e3a861b0367e14726bda6d6 100644 (file)
 /*     are made in parallel.
 /*
 /*     When the postscreen_greet_wait time has elapsed, and the
-/*     SMTP client address is listed with at least one of these
-/*     blocklists, this is logged as:
+/*     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 client at \fIaddress\fR is listed with
-/*     \fIcount\fR DNSBL servers. The \fIcount\fR does not
-/*     depend on the number of DNS records that an individual DNSBL
-/*     server returns.
+/*     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 next:
+/*     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
 /*     Network addresses that are permanently blacklisted; see the
 /*     postscreen_blacklist_action parameter for possible actions.
 /* .IP "\fBpostscreen_dnsbl_action (continue)\fR"
-/*     The action that \fBpostscreen\fR(8) takes when an SMTP client is listed
-/*     at the DNS blocklist domains specified with the postscreen_dnsbl_sites
-/*     parameter.
+/*     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_sites (empty)\fR"
-/*     Optional list of DNS blocklist domains.
+/*     Optional list of DNS blocklist domains, filters and weight
+/*     factors.
+/* .IP "\fBpostscreen_dnsbl_threshold (1)\fR"
+/*     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"
 /*     The action that \fBpostscreen\fR(8) takes when an SMTP client speaks
 /*     before its turn within the time specified with the postscreen_greet_wait
 /* .fi
 /* .IP "\fBpostscreen_cache_cleanup_interval (12h)\fR"
 /*     The amount of time between \fBpostscreen\fR(8) cache cleanup runs.
-/* .IP "\fBpostscreen_cache_map (btree:$data_directory/ps_whitelist)\fR"
+/* .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"
 /*     The amount of time that \fBpostscreen\fR(8) will cache an expired
 
 #include <mail_server.h>
 
+/* Application-specific. */
+
+#include <postscreen.h>
+
  /*
   * Configuration parameters.
   */
@@ -402,6 +412,7 @@ 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()
@@ -442,10 +453,6 @@ 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 ARGV *dnsbl_sites;              /* dns blocklist domains */
-static VSTRING *reply_addr;            /* address in DNSBL reply */
-static VSTRING *reply_domain;          /* domain in DNSBL reply */
-static HTABLE *dnsbl_cache;            /* entries being queried */
 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 */
@@ -453,239 +460,6 @@ 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 */
 
- /*
-  * See log_adhoc.c for discussion.
-  */
-typedef struct {
-    int     dt_sec;                    /* make sure it's signed */
-    int     dt_usec;                   /* make sure it's signed */
-} DELTA_TIME;
-
-#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; \
-    } while (0)
-
-#define SIG_DIGS        2
-
-/* READ_EVENT_REQUEST - prepare for transition to next state */
-
-#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)
-
-/* CLEAR_EVENT_REQUEST - complete state transition */
-
-#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)
-
-/* SLMs. */
-
-#define STR(x) vstring_str(x)
-#define LEN(x) VSTRING_LEN(x)
-
- /*
-  * Monitor time-critical operations.
-  */
-#define PS_GET_TIME_BEFORE_LOOKUP \
-    struct timeval _before, _after; \
-    DELTA_TIME _delta; \
-    GETTIMEOFDAY(&_before);
-
-#define PS_DELTA_MS(d) ((d).dt_sec * 1000 + (d).dt_usec / 1000)
-
-#define PS_CHECK_TIME_AFTER_LOOKUP(table, action) \
-    GETTIMEOFDAY(&_after); \
-    PS_CALC_DELTA(_delta, _after, _before); \
-    if (_delta.dt_sec > 1 || _delta.dt_usec > 100000) \
-       msg_warn("%s: %s %s took %d ms", \
-                myname, (table), (action), PS_DELTA_MS(_delta));
-
-/* ps_addr_match_list_match - time-critical address list lookup */
-
-static int ps_addr_match_list_match(ADDR_MATCH_LIST *addr_list,
-                                           const char *addr_str)
-{
-    const char *myname = "ps_addr_match_list_match";
-    int     result;
-
-    PS_GET_TIME_BEFORE_LOOKUP;
-    result = addr_match_list_match(addr_list, addr_str);
-    PS_CHECK_TIME_AFTER_LOOKUP("address list", "lookup");
-    return (result);
-}
-
-/* ps_dict_get - time-critical table lookup */
-
-static const char *ps_dict_get(DICT_CACHE *cache, const char *key)
-{
-    const char *myname = "ps_dict_get";
-    const char *result;
-
-    PS_GET_TIME_BEFORE_LOOKUP;
-    result = dict_cache_lookup(cache, key);
-    PS_CHECK_TIME_AFTER_LOOKUP(dict_cache_name(cache), "lookup");
-    return (result);
-}
-
-/* ps_dict_put - table dictionary update */
-
-static void ps_dict_put(DICT_CACHE *cache, const char *key, const char *value)
-{
-    const char *myname = "ps_dict_put";
-
-    PS_GET_TIME_BEFORE_LOOKUP;
-    dict_cache_update(cache, key, value);
-    PS_CHECK_TIME_AFTER_LOOKUP(dict_cache_name(cache), "update");
-}
-
- /*
-  * DNSBL lookup status per client IP address.
-  */
-typedef struct {
-    int     dnsbl_count;               /* is this address listed */
-    int     refcount;                  /* query reference count */
-} PS_DNSBL_ENTRY;
-
-/* postscreen_dnsbl_entry_create - create blocklist cache entry */
-
-static PS_DNSBL_ENTRY *postscreen_dnsbl_entry_create(void)
-{
-    PS_DNSBL_ENTRY *entry;
-
-    entry = (PS_DNSBL_ENTRY *) mymalloc(sizeof(*entry));
-    entry->dnsbl_count = 0;
-    entry->refcount = 0;
-    return (entry);
-}
-
-/* postscreen_dnsbl_done - get blocklist cache entry, decrement refcount */
-
-static int postscreen_dnsbl_done(const char *addr)
-{
-    const char *myname = "postscreen_dnsbl_done";
-    PS_DNSBL_ENTRY *entry;
-    int     dnsbl_count;
-
-    /*
-     * Sanity check.
-     */
-    if ((entry = (PS_DNSBL_ENTRY *) htable_find(dnsbl_cache, addr)) == 0)
-       msg_panic("%s: no blocklist cache entry for %s", myname, addr);
-
-    /*
-     * Yes, cache reads are destructive.
-     */
-    dnsbl_count = entry->dnsbl_count;
-    entry->refcount -= 1;
-    if (entry->refcount < 1) {
-       if (msg_verbose)
-           msg_info("%s: delete cache entry for %s", myname, addr);
-       htable_delete(dnsbl_cache, addr, myfree);
-    }
-    return (dnsbl_count);
-}
-
-/* postscreen_dnsbl_reply - receive dnsbl reply, update blocklist cache entry */
-
-static void postscreen_dnsbl_reply(int event, char *context)
-{
-    const char *myname = "postscreen_dnsbl_reply";
-    VSTREAM *stream = (VSTREAM *) context;
-    PS_DNSBL_ENTRY *entry;
-    int     dnsbl_count;
-
-    CLEAR_EVENT_REQUEST(vstream_fileno(stream), postscreen_dnsbl_reply, context);
-
-    /*
-     * Later, this will become an UDP-based DNS client that is built directly
-     * into the postscreen daemon.
-     * 
-     * Don't panic when no blocklist cache entry exists. It may be gone when the
-     * client triggered a "drop" action after pregreet, DNSBL lookup, or
-     * hangup.
-     */
-    if (event == EVENT_READ
-       && attr_scan(stream,
-                    ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
-                    ATTR_TYPE_STR, MAIL_ATTR_RBL_DOMAIN, reply_domain,
-                    ATTR_TYPE_STR, MAIL_ATTR_ADDR, reply_addr,
-                    ATTR_TYPE_INT, MAIL_ATTR_STATUS, &dnsbl_count,
-                    ATTR_TYPE_END) == 3) {
-       if ((entry = (PS_DNSBL_ENTRY *)
-            htable_find(dnsbl_cache, STR(reply_addr))) != 0)
-           entry->dnsbl_count += dnsbl_count;
-    }
-    vstream_fclose(stream);
-}
-
-/* postscreen_dnsbl_query  - send dnsbl query */
-
-static void postscreen_dnsbl_query(const char *addr)
-{
-    const char *myname = "postscreen_dnsbl_query";
-    int     fd;
-    VSTREAM *stream;
-    char  **cpp;
-    PS_DNSBL_ENTRY *entry;
-
-    /*
-     * Avoid duplicate effort when this lookup is already in progress. Now,
-     * we destroy the entry when the client replies. Later, we increment
-     * refcounts with queries sent, and decrement refcounts with replies
-     * received, so we can maintain state even after a client talks early,
-     * and update the external cache asynchronously.
-     */
-    if ((entry = (PS_DNSBL_ENTRY *) htable_find(dnsbl_cache, addr)) != 0) {
-       entry->refcount += 1;
-       return;
-    }
-    if (msg_verbose)
-       msg_info("%s: create cache entry for %s", myname, addr);
-    entry = postscreen_dnsbl_entry_create();
-    (void) htable_enter(dnsbl_cache, addr, (char *) entry);
-    entry->refcount = 1;
-
-    /*
-     * Later, this will become an UDP-based DNS client that is built directly
-     * into the postscreen daemon.
-     */
-    for (cpp = dnsbl_sites->argv; *cpp; cpp++) {
-       if ((fd = LOCAL_CONNECT("private/" DNSBL_SERVICE, NON_BLOCKING, 1)) < 0) {
-           msg_warn("%s: connect to " DNSBL_SERVICE " service: %m", myname);
-           return;
-       }
-       stream = vstream_fdopen(fd, O_RDWR);
-       attr_print(stream, ATTR_FLAG_NONE,
-                  ATTR_TYPE_STR, MAIL_ATTR_RBL_DOMAIN, *cpp,
-                  ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
-                  ATTR_TYPE_END);
-       if (vstream_fflush(stream) != 0) {
-           msg_warn("%s: error sending to " DNSBL_SERVICE " service: %m", myname);
-           vstream_fclose(stream);
-           return;
-       }
-       READ_EVENT_REQUEST(vstream_fileno(stream), postscreen_dnsbl_reply,
-                          (char *) stream, DNSBLOG_TIMEOUT);
-    }
-}
-
 /* new_session_state - fill in connection state for event processing */
 
 static PS_STATE *new_session_state(VSTREAM *stream, const char *addr,
@@ -842,7 +616,7 @@ static void send_socket(PS_STATE *state)
                      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");
+             state->smtp_client_port, "421 4.3.2 No system resources\r\n");
        free_session_state(state);
        return;
     } else {
@@ -863,15 +637,15 @@ static void send_socket(PS_STATE *state)
     }
 }
 
-/* smtp_read_event - handle pre-greet, EOF or timeout. */
+/* smtp_early_event - handle pre-greet, EOF or timeout. */
 
-static void smtp_read_event(int event, char *context)
+static void smtp_early_event(int event, char *context)
 {
-    const char *myname = "smtp_read_event";
+    const char *myname = "smtp_early_event";
     PS_STATE *state = (PS_STATE *) context;
     char    read_buf[PS_READ_BUF_SIZE];
     int     read_count;
-    int     dnsbl_count;
+    int     dnsbl_score;
     int     elapsed;
     int     action;
 
@@ -886,7 +660,7 @@ static void smtp_read_event(int event, char *context)
      * was closed, or we reached the limit of our patience.
      */
     CLEAR_EVENT_REQUEST(vstream_fileno(state->smtp_client_stream),
-                       smtp_read_event, context);
+                       smtp_early_event, context);
 
     /*
      * If this session ends here, we MUST read the blocklist cache otherwise
@@ -905,12 +679,12 @@ static void smtp_read_event(int event, char *context)
         */
     case EVENT_TIME:
        if (*var_ps_dnsbl_sites)
-           dnsbl_count = postscreen_dnsbl_done(state->smtp_client_addr);
+           dnsbl_score = ps_dnsbl_retrieve(state->smtp_client_addr);
        else
-           dnsbl_count = 0;
-       if (dnsbl_count > 0) {
+           dnsbl_score = 0;
+       if (dnsbl_score >= var_ps_dnsbl_thresh) {
            msg_info("DNSBL rank %d for %s",
-                    dnsbl_count, state->smtp_client_addr);
+                    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,
@@ -927,7 +701,7 @@ static void smtp_read_event(int event, char *context)
                         "OLD" : "NEW", state->smtp_client_addr);
                if (cache_map != 0) {
                    vstring_sprintf(temp, "%ld", (long) event_time());
-                   ps_dict_put(cache_map, state->smtp_client_addr, STR(temp));
+                   ps_cache_update(cache_map, state->smtp_client_addr, STR(temp));
                }
            }
            send_socket(state);
@@ -964,14 +738,14 @@ static void smtp_read_event(int event, char *context)
        }
        if (action == PS_ACT_DROP) {
            if (*var_ps_dnsbl_sites)
-               (void) postscreen_dnsbl_done(state->smtp_client_addr);
+               (void) ps_dnsbl_retrieve(state->smtp_client_addr);
            free_session_state(state);
        } else {
            state->flags |= PS_FLAG_NOCACHE;
-           /* not: postscreen_dnsbl_done */
+           /* not: ps_dnsbl_retrieve */
            if (elapsed > var_ps_greet_wait)
                elapsed = var_ps_greet_wait;
-           event_request_timer(smtp_read_event, context,
+           event_request_timer(smtp_early_event, context,
                                var_ps_greet_wait - elapsed);
        }
        break;
@@ -1145,7 +919,7 @@ static void postscreen_service(VSTREAM *smtp_client_stream,
      * lowest precedence.
      */
     else if (cache_map != 0
-       && (stamp_str = ps_dict_get(cache_map, smtp_client_addr.buf)) != 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);
@@ -1202,13 +976,13 @@ static void postscreen_service(VSTREAM *smtp_client_stream,
                              smtp_client_port.buf);
     state->flags |= state_flags;
     READ_EVENT_REQUEST(vstream_fileno(state->smtp_client_stream),
-                      smtp_read_event, (char *) state, var_ps_greet_wait);
+                      smtp_early_event, (char *) state, var_ps_greet_wait);
 
     /*
      * Run a DNS blocklist query while we wait for the client to respond.
      */
     if (*var_ps_dnsbl_sites)
-       postscreen_dnsbl_query(smtp_client_addr.buf);
+       ps_dnsbl_request(smtp_client_addr.buf);
 }
 
 /* postscreen_cache_validator - validate one cache entry */
@@ -1315,10 +1089,7 @@ static void post_jail_init(char *unused_name, char **unused_argv)
        vstring_sprintf(temp, "220-%s\r\n", var_ps_greet_banner);
        teaser_greeting = mystrdup(STR(temp));
     }
-    dnsbl_sites = argv_split(var_ps_dnsbl_sites, ", \t\r\n");
-    dnsbl_cache = htable_create(13);
-    reply_addr = vstring_alloc(100);
-    reply_domain = vstring_alloc(100);
+    ps_dnsbl_init();
     if ((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);
@@ -1371,6 +1142,7 @@ int     main(int argc, char **argv)
     };
     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,
        0,
     };
     static const CONFIG_NINT_TABLE nint_table[] = {
diff --git a/postfix/src/postscreen/postscreen.h b/postfix/src/postscreen/postscreen.h
new file mode 100644 (file)
index 0000000..b834db9
--- /dev/null
@@ -0,0 +1,92 @@
+/*++
+/* NAME
+/*     postscreen 3h
+/* SUMMARY
+/*     postscreen internal interfaces
+/* SYNOPSIS
+/*     #include <postscreen.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <dict_cache.h>
+
+ /*
+  * Global library.
+  */
+#include <addr_match_list.h>
+
+ /*
+  * See log_adhoc.c for discussion.
+  */
+typedef struct {
+    int     dt_sec;                    /* make sure it's signed */
+    int     dt_usec;                   /* make sure it's signed */
+} DELTA_TIME;
+
+#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; \
+    } while (0)
+
+#define SIG_DIGS        2
+
+/* READ_EVENT_REQUEST - prepare for transition to next state */
+
+#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)
+
+/* CLEAR_EVENT_REQUEST - complete state transition */
+
+#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)
+
+ /*
+  * SLMs.
+  */
+#define STR(x)  vstring_str(x)
+#define LEN(x)  VSTRING_LEN(x)
+
+ /*
+  * postscreen_dict.c
+  */
+extern int ps_addr_match_list_match(ADDR_MATCH_LIST *, const char *);
+extern const char *ps_cache_lookup(DICT_CACHE *, const char *);
+extern void ps_cache_update(DICT_CACHE *, const char *, const char *);
+
+ /*
+  * postscreen_dnsbl.c
+  */
+extern void ps_dnsbl_init(void);
+extern int ps_dnsbl_retrieve(const char *);
+extern void ps_dnsbl_request(const char *);
+
+/* 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
+/*--*/
diff --git a/postfix/src/postscreen/postscreen_dict.c b/postfix/src/postscreen/postscreen_dict.c
new file mode 100644 (file)
index 0000000..03a92b7
--- /dev/null
@@ -0,0 +1,107 @@
+/*++
+/* NAME
+/*     postscreen_dict 3
+/* SUMMARY
+/*     postscreen table access wrappers
+/* SYNOPSIS
+/*     #include <postscreen.h>
+/*
+/*     int     ps_addr_match_list_match(match_list, client_addr)
+/*     ADDR_MATCH_LIST *match_list;
+/*     const char *client_addr;
+/*
+/*     const char *ps_cache_lookup(DICT_CACHE *cache, const char *key)
+/*     DICT_CACHE *cache;
+/*     const char *key;
+/*
+/*     void    ps_cache_update(cache, key, value)
+/*     DICT_CACHE *cache;
+/*     const char *key;
+/*     const char *value;
+/* DESCRIPTION
+/*     This module implements wrappers around time-critical table
+/*     access functions.  The functions log a warning when table
+/*     access takes a non-trivial amount of time.
+/*
+/*     ps_addr_match_list_match() is a wrapper around
+/*     addr_match_list_match().
+/*
+/*     ps_cache_lookup() and ps_cache_update() are wrappers around
+/*     the corresponding dict_cache() methods.
+/* 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>
+
+/* Application-specific. */
+
+#include <postscreen.h>
+
+ /*
+  * Monitor time-critical operations.
+  */
+#define PS_GET_TIME_BEFORE_LOOKUP \
+    struct timeval _before, _after; \
+    DELTA_TIME _delta; \
+    GETTIMEOFDAY(&_before);
+
+#define PS_DELTA_MS(d) ((d).dt_sec * 1000 + (d).dt_usec / 1000)
+
+#define PS_CHECK_TIME_AFTER_LOOKUP(table, action) \
+    GETTIMEOFDAY(&_after); \
+    PS_CALC_DELTA(_delta, _after, _before); \
+    if (_delta.dt_sec > 1 || _delta.dt_usec > 100000) \
+        msg_warn("%s: %s %s took %d ms", \
+                 myname, (table), (action), PS_DELTA_MS(_delta));
+
+/* ps_addr_match_list_match - time-critical address list lookup */
+
+int     ps_addr_match_list_match(ADDR_MATCH_LIST *addr_list,
+                                        const char *addr_str)
+{
+    const char *myname = "ps_addr_match_list_match";
+    int     result;
+
+    PS_GET_TIME_BEFORE_LOOKUP;
+    result = addr_match_list_match(addr_list, addr_str);
+    PS_CHECK_TIME_AFTER_LOOKUP("address list", "lookup");
+    return (result);
+}
+
+/* ps_cache_lookup - time-critical cache lookup */
+
+const char *ps_cache_lookup(DICT_CACHE *cache, const char *key)
+{
+    const char *myname = "ps_cache_lookup";
+    const char *result;
+
+    PS_GET_TIME_BEFORE_LOOKUP;
+    result = dict_cache_lookup(cache, key);
+    PS_CHECK_TIME_AFTER_LOOKUP(dict_cache_name(cache), "lookup");
+    return (result);
+}
+
+/* ps_cache_update - time-critical cache update */
+
+void    ps_cache_update(DICT_CACHE *cache, const char *key, const char *value)
+{
+    const char *myname = "ps_cache_update";
+
+    PS_GET_TIME_BEFORE_LOOKUP;
+    dict_cache_update(cache, key, value);
+    PS_CHECK_TIME_AFTER_LOOKUP(dict_cache_name(cache), "update");
+}
diff --git a/postfix/src/postscreen/postscreen_dnsbl.c b/postfix/src/postscreen/postscreen_dnsbl.c
new file mode 100644 (file)
index 0000000..a39d994
--- /dev/null
@@ -0,0 +1,382 @@
+/*++
+/* NAME
+/*     postscreen_dnsbl 3
+/* SUMMARY
+/*     postscreen DNSBL support
+/* SYNOPSIS
+/*     #include <postscreen.h>
+/*
+/*     void    ps_dnsbl_init(void)
+/*
+/*     void    ps_dnsbl_request(client_addr)
+/*     char    *client_addr;
+/*
+/*     int     ps_dnsbl_retrieve(client_addr)
+/*     char    *client_addr;
+/* 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.
+/*
+/*     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
+/*     client IP address must be in inet_ntop(3) output format.
+/*
+/*     ps_dnsbl_retrieve() retrieves the result score requested with
+/*     ps_dnsbl_request() and decrements the reference count. It
+/*     is an error to retrieve a score without requesting it first.
+/* 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>
+#include <mymalloc.h>
+#include <argv.h>
+#include <htable.h>
+#include <events.h>
+#include <vstream.h>
+#include <connect.h>
+#include <split_at.h>
+#include <valid_hostname.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_proto.h>
+
+/* Application-specific. */
+
+#include <postscreen.h>
+
+#define DNSBL_SERVICE                  "dnsblog"
+#define DNSBLOG_TIMEOUT                        10
+
+ /*
+  * Per-DNSBL filters and weights.
+  * 
+  * The postscreen_dnsbl_sites parameter specifies zero or more DNSBL domains.
+  * We provide multiple access methods, one for quick iteration when sending
+  * queries to all DNSBL servers, and one for quick location when receiving a
+  * reply from one DNSBL server.
+  * 
+  * 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.
+  */
+static HTABLE *dnsbl_site_cache;       /* indexed by DNSBNL domain */
+static HTABLE_INFO **dnsbl_site_list;  /* flattened cache */
+
+typedef struct PS_DNSBL_SITE {
+    char   *filter;                    /* reply filter (default: null) */
+    int     weight;                    /* reply weight (default: 1) */
+    struct PS_DNSBL_SITE *next;                /* linked list */
+} PS_DNSBL_SITE;
+
+ /*
+  * Per-client DNSBL scores.
+  * 
+  * One remote SMTP client may make parallel connections and thereby trigger
+  * parallel blocklist score requests. We combine identical requests under
+  * the client IP address in a single reference-counted entry. The reference
+  * count goes up with each score request, and it goes down with each score
+  * retrieval.
+  */
+static HTABLE *dnsbl_score_cache;      /* indexed by client address */
+
+typedef struct {
+    int     total;                     /* combined blocklist score */
+    int     refcount;                  /* score reference count */
+} PS_DNSBL_SCORE;
+
+ /*
+  * Per-request state.
+  * 
+  * This implementation stores the client IP address and DNSBL domain in the
+  * DNSBLOG query/reply stream. This simplifies code, and allows the DNSBLOG
+  * server to produce more informative logging.
+  */
+static VSTRING *reply_client;          /* client address in DNSBLOG reply */
+static VSTRING *reply_dnsbl;           /* domain in DNSBLOG reply */
+static VSTRING *reply_addr;            /* adress list in DNSBLOG reply */
+
+/* ps_dnsbl_add_site - add DNSBL site information */
+
+static void ps_dnsbl_add_site(const char *site)
+{
+    const char *myname = "ps_dnsbl_add_site";
+    char   *saved_site = mystrdup(site);
+    PS_DNSBL_SITE *old_site;
+    PS_DNSBL_SITE *new_site;
+    char    junk;
+    const char *weight_text;
+    const char *pattern_text;
+    int     weight;
+
+    /*
+     * Parse the required DNSBL domain name, the optional reply filter and
+     * the optional reply weight factor.
+     */
+#define DO_GRIPE       1
+
+    /* Negative weight means whitelist. */
+    if ((weight_text = split_at(saved_site, '*')) != 0) {
+       if (sscanf(weight_text, "%d%c", &weight, &junk) != 1)
+           msg_fatal("bad DNSBL weight factor \"%s\" in \"%s\"",
+                     weight_text, site);
+    } else {
+       weight = 1;
+    }
+    /* Preliminary fixed-string filter. */
+    if ((pattern_text = split_at(saved_site, '=')) != 0) {
+       if (valid_ipv4_hostaddr(pattern_text, DO_GRIPE) == 0)
+           msg_fatal("bad DNSBL filter syntax \"%s\" in \"%s\"",
+                     pattern_text, site);
+    }
+    if (valid_hostname(saved_site, DO_GRIPE) == 0)
+       msg_fatal("bad DNSBL domain name \"%s\" in \"%s\"",
+                 saved_site, site);
+
+    if (msg_verbose)
+       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.
+     */
+    new_site = (PS_DNSBL_SITE *) mymalloc(sizeof(*new_site));
+    new_site->filter = (pattern_text ? mystrdup(pattern_text) : 0);
+    new_site->weight = weight;
+
+    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_match - match DNSBL reply filter */
+
+static int ps_dnsbl_match(const char *filter, ARGV *reply)
+{
+    char  **cpp;
+
+    /*
+     * Preliminary fixed-string implementation.
+     */
+    for (cpp = reply->argv; *cpp != 0; cpp++)
+       if (strcmp(filter, *cpp) == 0)
+           return (1);
+    return (0);
+}
+
+/* ps_dnsbl_retrieve - retrieve blocklist score, decrement reference count */
+
+int     ps_dnsbl_retrieve(const char *client_addr)
+{
+    const char *myname = "ps_dnsbl_retrieve";
+    PS_DNSBL_SCORE *score;
+    int     result_score;
+
+    /*
+     * Sanity check.
+     */
+    if ((score = (PS_DNSBL_SCORE *)
+        htable_find(dnsbl_score_cache, client_addr)) == 0)
+       msg_panic("%s: no blocklist score for %s", myname, client_addr);
+
+    /*
+     * Reads are destructive.
+     */
+    result_score = score->total;
+    score->refcount -= 1;
+    if (score->refcount < 1) {
+       if (msg_verbose)
+           msg_info("%s: delete blocklist score for %s", myname, client_addr);
+       htable_delete(dnsbl_score_cache, client_addr, myfree);
+    }
+    return (result_score);
+}
+
+/* ps_dnsbl_receive - receive DNSBL reply, update blocklist score */
+
+static void ps_dnsbl_receive(int event, char *context)
+{
+    const char *myname = "ps_dnsbl_receive";
+    VSTREAM *stream = (VSTREAM *) context;
+    PS_DNSBL_SCORE *score;
+    PS_DNSBL_SITE *site;
+    ARGV   *reply_argv;
+
+    CLEAR_EVENT_REQUEST(vstream_fileno(stream), ps_dnsbl_receive, context);
+
+    /*
+     * Receive the DNSBL lookup result.
+     * 
+     * This is preliminary code to explore the field. Later, DNSBL lookup will
+     * be handled by an UDP-based DNS client that is built directly into some
+     * Postfix daemon.
+     * 
+     * Don't bother looking up the blocklist score when the client IP address is
+     * not listed at the DNSBL.
+     * 
+     * Don't panic when the blocklist score no longer exists. It may be deleted
+     * when the client triggers a "drop" action after pregreet, when the
+     * client does not pregreet and the DNSBL reply arrives late, or when the
+     * client triggers a "drop" action after hanging up.
+     */
+    if (event == EVENT_READ
+       && attr_scan(stream,
+                    ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
+                    ATTR_TYPE_STR, MAIL_ATTR_RBL_DOMAIN, reply_dnsbl,
+                    ATTR_TYPE_STR, MAIL_ATTR_ACT_CLIENT_ADDR, reply_client,
+                    ATTR_TYPE_STR, MAIL_ATTR_RBL_ADDR, reply_addr,
+                    ATTR_TYPE_END) == 3
+       && *STR(reply_addr) != 0
+       && (score = (PS_DNSBL_SCORE *)
+           htable_find(dnsbl_score_cache, STR(reply_client))) != 0) {
+
+       /*
+        * Run this response past all applicable DNSBL filters and update the
+        * blocklist score for this client IP address.
+        * 
+        * Don't panic when the DNSBL domain name is not found. The DNSBLOG
+        * server may be messed up.
+        */
+       if (msg_verbose)
+           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) {
+           if (site->filter == 0
+               || ps_dnsbl_match(site->filter, reply_argv ? reply_argv :
+                        (reply_argv = argv_split(STR(reply_addr), " ")))) {
+               score->total += site->weight;
+               if (msg_verbose)
+                   msg_info("%s: filter=\"%s\" weight=%d score=%d",
+                            myname, site->filter ? site->filter : "null",
+                            site->weight, score->total);
+           }
+       }
+       if (reply_argv != 0)
+           argv_free(reply_argv);
+    }
+    vstream_fclose(stream);
+}
+
+/* ps_dnsbl_request  - send dnsbl query, increment reference count */
+
+void    ps_dnsbl_request(const char *client_addr)
+{
+    const char *myname = "ps_dnsbl_request";
+    int     fd;
+    VSTREAM *stream;
+    HTABLE_INFO **ht;
+    PS_DNSBL_SCORE *score;
+
+    /*
+     * Avoid duplicate effort when this lookup is already in progress. We
+     * 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.
+     */
+    if ((score = (PS_DNSBL_SCORE *)
+        htable_find(dnsbl_score_cache, client_addr)) != 0) {
+       score->refcount += 1;
+       return;
+    }
+    if (msg_verbose)
+       msg_info("%s: create blocklist score for %s", myname, client_addr);
+    score = (PS_DNSBL_SCORE *) mymalloc(sizeof(*score));
+    score->total = 0;
+    score->refcount = 1;
+    (void) htable_enter(dnsbl_score_cache, client_addr, (char *) score);
+
+    /*
+     * Send a query to all DNSBL servers. Later, DNSBL lookup will be done
+     * with an UDP-based DNS client that is built directly into Postfix code.
+     * We therefore do not optimize the maximum out of this temporary
+     * implementation.
+     */
+    for (ht = dnsbl_site_list; *ht; ht++) {
+       if ((fd = LOCAL_CONNECT("private/" DNSBL_SERVICE, NON_BLOCKING, 1)) < 0) {
+           msg_warn("%s: connect to " DNSBL_SERVICE " service: %m", myname);
+           return;
+       }
+       stream = vstream_fdopen(fd, O_RDWR);
+       attr_print(stream, ATTR_FLAG_NONE,
+                  ATTR_TYPE_STR, MAIL_ATTR_RBL_DOMAIN, ht[0]->key,
+                  ATTR_TYPE_STR, MAIL_ATTR_ACT_CLIENT_ADDR, client_addr,
+                  ATTR_TYPE_END);
+       if (vstream_fflush(stream) != 0) {
+           msg_warn("%s: error sending to " DNSBL_SERVICE " service: %m", myname);
+           vstream_fclose(stream);
+           return;
+       }
+       READ_EVENT_REQUEST(vstream_fileno(stream), ps_dnsbl_receive,
+                          (char *) stream, DNSBLOG_TIMEOUT);
+    }
+}
+
+/* ps_dnsbl_init - initialize */
+
+void    ps_dnsbl_init(void)
+{
+    const char *myname = "ps_dnsbl_init";
+    ARGV   *dnsbl_site = argv_split(var_ps_dnsbl_sites, ", \t\r\n");
+    char  **cpp;
+
+    /*
+     * Sanity check.
+     */
+    if (dnsbl_site_cache != 0)
+       msg_panic("%s: called more than once", myname);
+
+    /*
+     * Prepare for quick iteration when sending out queries to all DNSBL
+     * servers, and for quick lookup when a reply arrives from a specific
+     * DNSBL server.
+     */
+    dnsbl_site_cache = htable_create(13);
+    for (cpp = dnsbl_site->argv; *cpp; cpp++)
+       ps_dnsbl_add_site(*cpp);
+    argv_free(dnsbl_site);
+    dnsbl_site_list = htable_list(dnsbl_site_cache);
+
+    /*
+     * The per-client blocklist score.
+     */
+    dnsbl_score_cache = htable_create(13);
+
+    /*
+     * Space for ad-hoc DNSBLOG server request/reply parameters.
+     */
+    reply_client = vstring_alloc(100);
+    reply_dnsbl = vstring_alloc(100);
+    reply_addr = vstring_alloc(100);
+}