Mailing List Archive

svn commit: r1901033 - in /spamassassin/trunk: UPGRADE lib/Mail/SpamAssassin/Plugin/HashBL.pm t/data/spam/hashbl t/hashbl.t
Author: hege
Date: Wed May 18 12:40:40 2022
New Revision: 1901033

URL: http://svn.apache.org/viewvc?rev=1901033&view=rev
Log:
HashBL: add check_hashbl_attachments. Improve documentation.

Modified:
spamassassin/trunk/UPGRADE
spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/HashBL.pm
spamassassin/trunk/t/data/spam/hashbl
spamassassin/trunk/t/hashbl.t

Modified: spamassassin/trunk/UPGRADE
URL: http://svn.apache.org/viewvc/spamassassin/trunk/UPGRADE?rev=1901033&r1=1901032&r2=1901033&view=diff
==============================================================================
--- spamassassin/trunk/UPGRADE (original)
+++ spamassassin/trunk/UPGRADE Wed May 18 12:40:40 2022
@@ -204,6 +204,9 @@ Note for Users Upgrading to SpamAssassin
format (e.g. 192.168.1.1-192.168.255.255) for NetSet tables
(internal_networks, trusted_networks, msa_networks, uri_local_cidr).

+- HashBL: New functions check_hashbl_tag, check_hashbl_attachments.
+ New sha256 option.
+
Note for Users Upgrading to SpamAssassin 3.4.5
----------------------------------------------

@@ -279,7 +282,7 @@ Note for Users Upgrading to SpamAssassin
- DNSEval: add check_rbl_ns_from to check against an rbl for dns servers

- HashBL: Add check_hashbl_bodyre, check_hashbl_emails, check_hashbl_uris,
- check_hashbl_tag, hashbl_ignore. New sha256 option.
+ hashbl_ignore

- ASN: Support IPv6 with asn_lookup_ipv6 (Bug 7211)


Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/HashBL.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/HashBL.pm?rev=1901033&r1=1901032&r2=1901033&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/HashBL.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/HashBL.pm Wed May 18 12:40:40 2022
@@ -39,10 +39,14 @@ HashBL - query hashed (and unhashed) DNS
describe HASHBL_BTC Message contains BTC address found on BTCBL
tflags HASHBL_BTC net

- header HASHBL_URI eval:check_hashbl_uris('rbl.example.invalid', 'sha1', '127.0.0.32')
+ header HASHBL_URI eval:check_hashbl_uris('rbl.example.invalid', 'sha1', '^127\.0\.0\.32$')
describe HASHBL_URI Message contains uri found on rbl
tflags HASHBL_URI net

+ body HASHBL_ATTACHMENT eval:check_hashbl_attachments('attbl.example.invalid', 'sha256')
+ describe HASHBL_ATTACHMENT Message contains attachment found on attbl
+ tflags HASHBL_ATTACHMENT net
+
# Capture tag using SA 4.0 regex named capture feature
header __X_SOME_ID X-Some-ID =~ /^(?<XSOMEID>\d{10,20})$/
# Query the tag value as is from a DNSBL
@@ -50,39 +54,39 @@ HashBL - query hashed (and unhashed) DNS

=head1 DESCRIPTION

-This plugin support multiple types of hashed or unhashed DNS blocklists.
+This plugin supports multiple types of hashed or unhashed DNS blocklist queries.
+
+=over 4

-OPTS refers to multiple generic options:
+=item Common OPTS that apply to all functions:

- raw do not hash data, query as is
+ raw no hashing, query as is (can break if value is not valid DNS label)
md5 hash query with MD5
sha1 hash query with SHA1
sha256 hash query with Base32 encoded SHA256
case keep case before hashing, default is to lowercase
- max=x maximum number of queries
+ max=x maximum number of queries (defaults to 10 if not specified)
shuffle if max exceeded, random shuffle queries before truncating to limit

-Multiple options can be separated with slash or other non-word character.
-If OPTS is empty ('') or missing, default is used.
+Multiple options can be separated with slash.

-HEADERS refers to slash separated list of Headers to process:
-
- ALL all headers
- ALLFROM all From headers as returned by $pms->all_from_addrs()
- EnvelopeFrom message envelope from (Return-Path etc)
- HeaderName any header as used with $pms->get()
+When rule OPTS is empty ('') or missing, default is used as documented by
+each query type. If any options are defined, then all needed options must
+be explicitly defined.

-if HEADERS is empty ('') or missing, default is used.
+=back

=over 4

-=item header RULE check_hashbl_emails('bl.example.invalid/A', 'OPTS', 'HEADERS/body', '^127\.')
+=item header RULE check_hashbl_emails('bl.example.invalid/A', 'OPTS', 'HEADERS', '^127\.')
+
+Check email addresses from DNS list. Note that "body" can be specified
+along with headers to search message body for emails. Rule type must always
+be "header".

-Check email addresses from DNS list, "body" can be specified along with
-headers to search body for emails. Optional subtest regexp to match DNS
-answer. Note that eval rule type must always be "header".
+Optional DNS query type can be appended to list with /A (default) or /TXT.

-DNS query type can be appended to list with /A (default) or /TXT.
+Default OPTS: sha1/notag/noquote/max=10/shuffle

Additional supported OPTS:

@@ -91,13 +95,23 @@ Additional supported OPTS:
nouri ignore emails inside uris
noquote ignore emails inside < > or possible quotings

-Default OPTS: sha1/notag/noquote/max=10/shuffle
-
Default HEADERS: ALLFROM/Reply-To/body

+HEADERS refers to slash separated list of Headers to process:
+
+ ALL all headers
+ ALLFROM all From headers as returned by $pms->all_from_addrs()
+ EnvelopeFrom message envelope from (Return-Path etc)
+ <HeaderName> any header as used with header rules or $pms->get()
+ body all emails found in message body
+
+If HEADERS is empty ('') or missing, default is used.
+
+Optional subtest regexp to match DNS answer (default: '^127\.').
+
For existing public email blocklist, see: http://msbl.org/ebl.html

- # Working example, see http://msbl.org/ebl.html before usage
+ # Working example, see https://msbl.org/ebl.html before usage
header HASHBL_EMAIL eval:check_hashbl_emails('ebl.msbl.org')
describe HASHBL_EMAIL Message contains email address found on EBL
tflags HASHBL_EMAIL net
@@ -105,7 +119,7 @@ For existing public email blocklist, see
Default regex for matching and capturing emails can be overridden with
C<hashbl_email_regex>. Likewise, the default welcomelist can be changed with
C<hashbl_email_welcomelist>. Only change if you know what you are doing, see
-module source for the defaults. Example: hashbl_email_regex \S+@\S+.com
+plugin source code for the defaults. Example: hashbl_email_regex \S+@\S+.com

=back

@@ -113,22 +127,29 @@ module source for the defaults. Example

=item header RULE check_hashbl_uris('bl.example.invalid/A', 'OPTS', '^127\.')

-Check uris from DNS list, optional subtest regexp to match DNS
-answer.
+Check all URIs parsed from message from DNS list.

-DNS query type can be appended to list with /A (default) or /TXT.
+Optional DNS query type can be appended to list with /A (default) or /TXT.

Default OPTS: sha1/max=10/shuffle

+Optional subtest regexp to match DNS answer (default: '^127\.').
+
=back

=over 4

-=item body RULE check_hashbl_bodyre('bl.example.invalid/A', 'OPTS', '\b(match)\b', '^127\.')
+=item [raw]body RULE check_hashbl_bodyre('bl.example.invalid/A', 'OPTS', '\b(match)\b', '^127\.')

Search body for matching regexp and query the string captured. Regexp must
-have a single capture ( ) for the string ($1). Optional subtest regexp to
-match DNS answer. Note that eval rule type must be "body" or "rawbody".
+have a single capture ( ) for the string ($1). Rule type must be "body" or
+"rawbody".
+
+Optional DNS query type can be appended to list with /A (default) or /TXT.
+
+Default OPTS: sha1/max=10/shuffle
+
+Optional subtest regexp to match DNS answer (default: '^127\.').

=back

@@ -136,10 +157,9 @@ match DNS answer. Note that eval rule t

=item header RULE check_hashbl_tag('bl.example.invalid/A', 'OPTS', 'TAGNAME', '^127\.')

-Lookup value of SpamAssassin tag _TAGNAME_ from DNS list, optional subtest
-regexp to match DNS answer.
+Query value of SpamAssassin tag _TAGNAME_ from DNS list.

-DNS query type can be appended to list with /A (default) or /TXT.
+Optional DNS query type can be appended to list with /A (default) or /TXT.

Default OPTS: sha1/max=10/shuffle

@@ -153,9 +173,46 @@ Additional supported OPTS:
tld only query if value has valid TLD (is_domain_valid)
trim trim name from hostname to domain (trim_domain)

-If both ip/ipv4/ipv6 and fqdn/tld are enabled, only either of them is
-required to match. Both fqdn and tld are needed for complete FQDN+TLD
-check.
+ If both ip/ipv4/ipv6 and fqdn/tld are enabled, only either of them is
+ required to match. Both fqdn and tld are needed for complete FQDN+TLD
+ check.
+
+Optional subtest regexp to match DNS answer (default: '^127\.').
+
+=back
+
+=over 4
+
+=item header RULE check_hashbl_attachments('bl.example.invalid/A', 'OPTS', '^127\.')
+
+Check all all message attachments (mimeparts) from DNS list.
+
+Optional DNS query type can be appended to list with /A (default) or /TXT.
+
+Default OPTS: sha1/max=10/shuffle
+
+Additional supported OPTS:
+
+ minsize=x skip any parts smaller than x bytes
+ maxsize=x skip any parts larger than x bytes
+
+Optional subtest regexp to match DNS answer (default: '^127\.').
+
+Specific attachment filenames can be skipped with C<hashbl_ignore>. For
+example "hashbl_ignore safe.pdf".
+
+Specific mime types can be skipped with C<hashbl_ignore>. For example
+"hashbl_ignore text/plain".
+
+=back
+
+=over 4
+
+=item hashbl_ignore value [value...]
+
+Skip any type of query, if either the hash or original value (email for
+example) matches. Multiple values can be defined, separated by whitespace.
+Matching is case-insensitive.

=back

@@ -200,6 +257,7 @@ sub new {
'check_hashbl_uris' => $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS,
'check_hashbl_bodyre' => $Mail::SpamAssassin::Conf::TYPE_BODY_EVALS,
'check_hashbl_tag' => $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS,
+ 'check_hashbl_attachments' => $Mail::SpamAssassin::Conf::TYPE_BODY_EVALS,
};
while (my ($func, $type) = each %{$self->{evalfuncs}}) {
$self->register_eval_rule($func, $type);
@@ -803,6 +861,87 @@ sub _check_hashbl_tag {
return;
}

+sub check_hashbl_attachments {
+ my ($self, $pms, undef, $list, $opts, $subtest) = @_;
+
+ return 0 if !$self->{hashbl_available};
+ return 0 if !$pms->is_dns_available();
+
+ my $rulename = $pms->get_current_eval_rule_name();
+
+ if (!defined $list) {
+ warn "HashBL: $rulename blocklist argument missing\n";
+ return 0;
+ }
+
+ if ($subtest) {
+ my ($rec, $err) = compile_regexp($subtest, 0);
+ if (!$rec) {
+ warn "HashBL: $rulename invalid subtest regex: $@\n";
+ return 0;
+ }
+ $subtest = $rec;
+ }
+
+ # Parse opts, defaults
+ $opts = _parse_opts($opts || 'sha1/max=10/shuffle');
+
+ if ($opts->{raw}) {
+ warn "HashBL: $rulename raw option invalid\n";
+ return 0;
+ }
+
+ my %seen;
+ my @hashes;
+ foreach my $part ($pms->{msg}->find_parts(qr/./, 1, 1)) {
+ my $body = $part->decode();
+ next if !defined $body || $body eq '';
+ my $type = lc $part->{'type'} || '';
+ my $name = $part->{'name'} || '';
+ my $len = length($body);
+ dbg("found attachment, type: $type, length: $len, name: $name");
+ if (exists $pms->{conf}->{hashbl_ignore}->{$type}) {
+ dbg("query skipped, ignored type: $type");
+ next;
+ }
+ if (exists $pms->{conf}->{hashbl_ignore}->{lc $name}) {
+ dbg("query skipped, ignored filename: $name");
+ next;
+ }
+ if ($opts->{minsize} && $len < $opts->{minsize}) {
+ dbg("query skipped, size smaller than $opts->{minsize}");
+ next;
+ }
+ if ($opts->{maxsize} && $len > $opts->{minsize}) {
+ dbg("query skipped, size larger than $opts->{maxsize}");
+ next;
+ }
+ my $hash = $self->_hash($opts, $body);
+ next if $seen{$hash}++;
+ push @hashes, $hash;
+ }
+
+ return 0 unless @hashes;
+
+ # Randomize order
+ if ($opts->{shuffle}) {
+ Mail::SpamAssassin::Util::fisher_yates_shuffle(\@hashes);
+ }
+
+ # Truncate list
+ my $max = $opts->{max} || 10;
+ $#hashes = $max-1 if scalar @hashes > $max;
+
+ my $queries;
+ foreach my $hash (@hashes) {
+ my $ret = $self->_submit_query($pms, $rulename, $hash, $list, $opts, $subtest, 1);
+ $queries++ if defined $ret;
+ }
+
+ return 0 if !$queries; # no query started
+ return; # return undef for async status
+}
+
sub _hash {
my ($self, $opts, $value) = @_;

@@ -821,21 +960,21 @@ sub _hash {
}

sub _submit_query {
- my ($self, $pms, $rulename, $value, $list, $opts, $subtest) = @_;
+ my ($self, $pms, $rulename, $value, $list, $opts, $subtest, $already_hashed) = @_;

- if (exists $pms->{conf}->{hashbl_ignore}->{lc $value}) {
+ if (!$already_hashed && exists $pms->{conf}->{hashbl_ignore}->{lc $value}) {
dbg("query skipped, ignored string: $value");
return 0;
}

- my $hash = $self->_hash($opts, $value);
- dbg("querying $value ($hash) from $list");
-
- if (exists $pms->{conf}->{hashbl_ignore}->{$hash}) {
+ my $hash = $already_hashed ? $value : $self->_hash($opts, $value);
+ if (exists $pms->{conf}->{hashbl_ignore}->{lc $hash}) {
dbg("query skipped, ignored hash: $value");
return 0;
}

+ dbg("querying $value ($hash) from $list");
+
my $type = $list =~ s,/(A|TXT)$,,i ? uc($1) : 'A';
my $lookup = "$hash.$list";

@@ -888,5 +1027,6 @@ sub has_hashbl_email_welcomelist { 1 }
sub has_hashbl_email_whitelist { 1 }
sub has_hashbl_tag { 1 }
sub has_hashbl_sha256 { 1 }
+sub has_hashbl_attachments { 1 }

1;

Modified: spamassassin/trunk/t/data/spam/hashbl
URL: http://svn.apache.org/viewvc/spamassassin/trunk/t/data/spam/hashbl?rev=1901033&r1=1901032&r2=1901033&view=diff
==============================================================================
--- spamassassin/trunk/t/data/spam/hashbl (original)
+++ spamassassin/trunk/t/data/spam/hashbl Wed May 18 12:40:40 2022
@@ -15,6 +15,13 @@ Subject: There yours for FREE!
X-Original-Sender: hustl.er@gmail.com
X-Some-ID: 1234567890
To: undisclosed-recipients:;
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="ETDFsshmzrOmOVdZ"
+Content-Disposition: inline
+
+--ETDFsshmzrOmOVdZ
+Content-Type: text/plain; charset=utf-8
+Content-Disposition: inline

Hello

@@ -26,3 +33,248 @@ Some uris spammer.com https://spammer2.c

btc 1JaSs2bTZYVbj6jaqZ5Mjfs8gSLY9vYCrK

+--ETDFsshmzrOmOVdZ
+Content-Type: application/octet-stream
+Content-Disposition: attachment; filename="macro.xlsm"
+Content-Transfer-Encoding: base64
+
+UEsDBBQABgAIAAAAIQDxGuVmhQEAAE8FAAATANkBW0NvbnRlbnRfVHlwZXNdLnhtbCCi1QEo
+oAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzFTLbsIwELxX6j9EvlbYQKWq
+qhI4tPTYcqAfYJwNuDi25TU0/H034SEhpSmUSy+J8tiZ2ZlJ0nFVmmQDAbWzGRvwPkvAKpdr
+u8jYx+y198gSjNLm0jgLGdsCsvHo9iadbT1gQtMWM7aM0T8JgWoJpUTuPFh6UrhQykiXYSG8
+VCu5ADHs9x+EcjaCjb1YY7BR+gKFXJuYTCq6vVMy15Ylz7v3aqqMSe+NVjKSULGxOS+x54pC
+K+CbuZwG9wkqMjFK32mboHNIpjLEN1nSqKiMiCQNdscBJ3G/oJ+usGfKnVqXJJw3YHc1ys+E
+GLcG8Goq9AFkjkuAWBq+Az0wt/gWwOBlq+2T4TTZmItL7bGDodu7bk++XFjNnVud4QrFC5UC
+WrlenZdSBTexcm6AopfaHhS2xU05USE8CurMGVTdWUNdyhzynidICFHD0Z02bqpavWWjGkVz
+Gl6t4bQER/wuD1p03P8THdd/f3/w49gJ5QJcbsThK6mnW5ogmt/h6BsAAP//AwBQSwMEFAAG
+AAgAAAAhALVVMCP1AAAATAIAAAsAzgFfcmVscy8ucmVscyCiygEooAACAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAIySz07DMAzG70i8Q+T76m5ICKGlu0xIuyFUHsAk7h+1jaMkQPf2hAOCSmPb
+0fbnzz9b3u7maVQfHGIvTsO6KEGxM2J712p4rZ9WD6BiImdpFMcajhxhV93ebF94pJSbYtf7
+qLKLixq6lPwjYjQdTxQL8exypZEwUcphaNGTGahl3JTlPYa/HlAtPNXBaggHeweqPvo8+bK3
+NE1veC/mfWKXToxAnhM7y3blQ2YLqc/bqJpCy0mDFfOc0xHJ+yJjA54m2lxP9P+2OHEiS4nQ
+SODzPN+Kc0Dr64Eun2ip+L3OPOKnhOFNZPhhwcUPVF8AAAD//wMAUEsDBBQABgAIAAAAIQCc
+fziWGAEAAMEDAAAaAAgBeGwvX3JlbHMvd29ya2Jvb2sueG1sLnJlbHMgogQBKKAAAQAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+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
+AwAAsi4AAAAA
+
+--ETDFsshmzrOmOVdZ--
+

Modified: spamassassin/trunk/t/hashbl.t
URL: http://svn.apache.org/viewvc/spamassassin/trunk/t/hashbl.t?rev=1901033&r1=1901032&r2=1901033&view=diff
==============================================================================
--- spamassassin/trunk/t/hashbl.t (original)
+++ spamassassin/trunk/t/hashbl.t Wed May 18 12:40:40 2022
@@ -39,6 +39,7 @@ jykf2a5v6asavfel3stymlmieh4e66jeroxuw52m
6a42acf4133289d595e3875a9d677f810e80b7b4.hashbltest4.spamassassin.org
5c6205960a65b1f9078f0e12dcac970aab0015eb.hashbltest4.spamassassin.org
1234567890.hashbltest5.spamassassin.org
+w3hcrlct6yshq5vq6gjv2hf3pzk3jvsk6ilj5iaks4qwewudrr6q.hashbltest6.spamassassin.org
);

sub check_queries {
@@ -59,11 +60,11 @@ sub check_queries {
}
}
close WL;
+ diag("Invalid query launched: $_") foreach (keys %invalid);
unless (keys %found == @valid_queries) {
- diag("Not all queries launched");
+ diag("Incorrect amount of queries launched");
return 0;
}
- diag("Invalid query launched: $_") foreach (keys %invalid);
return !%invalid;
}

@@ -90,6 +91,12 @@ tstlocalrules(q{
header __X_SOME_ID X-Some-ID =~ /^(?<XSOMEID>\d{10,20})$/
header X_HASHBL_TAG eval:check_hashbl_tag('hashbltest5.spamassassin.org/A', 'raw', 'XSOMEID', '^127\.')

+ # Not supposed to hit, @valid_queries just checks that they are launched
+ hashbl_ignore text/plain
+ body X_HASHBL_ATT eval:check_hashbl_attachments('hashbltest6.spamassassin.org/A', 'sha256')
+ describe X_HASHBL_ATT Message contains attachment found on attbl
+ tflags X_HASHBL_ATT net
+
# Bug 7897 - test that meta rules depending on net rules hit
meta META_HASHBL_EMAIL X_HASHBL_EMAIL
# It also needs to hit even if priority is lower than dnsbl (-100)