← Index
NYTProf Performance Profile   « line view »
For /usr/local/bin/sa-learn
  Run on Sun Nov 5 03:09:29 2017
Reported on Mon Nov 6 13:20:48 2017

Filename/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm
StatementsExecuted 176375 statements in 1.40s
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
23411602ms15.0sMail::SpamAssassin::Plugin::URIDNSBL::::parsed_metadataMail::SpamAssassin::Plugin::URIDNSBL::parsed_metadata
804311517ms5.68sMail::SpamAssassin::Plugin::URIDNSBL::::lookup_single_dnsblMail::SpamAssassin::Plugin::URIDNSBL::lookup_single_dnsbl
23411424ms8.36sMail::SpamAssassin::Plugin::URIDNSBL::::query_hosts_or_domainsMail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains
3831117.6ms1.09sMail::SpamAssassin::Plugin::URIDNSBL::::lookup_a_recordMail::SpamAssassin::Plugin::URIDNSBL::lookup_a_record
3171114.4ms892msMail::SpamAssassin::Plugin::URIDNSBL::::lookup_domain_nsMail::SpamAssassin::Plugin::URIDNSBL::lookup_domain_ns
34749114.3ms14.3msMail::SpamAssassin::Plugin::URIDNSBL::::CORE:matchMail::SpamAssassin::Plugin::URIDNSBL::CORE:match (opcode)
63113.70ms3.87msMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:783]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:783]
21111.57ms3.76msMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:662]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:662]
22211.50ms2.14msMail::SpamAssassin::Plugin::URIDNSBL::::parse_and_canonicalize_subtestMail::SpamAssassin::Plugin::URIDNSBL::parse_and_canonicalize_subtest
111142µs463µsMail::SpamAssassin::Plugin::URIDNSBL::::set_configMail::SpamAssassin::Plugin::URIDNSBL::set_config
2331118µs118µsMail::SpamAssassin::Plugin::URIDNSBL::::CORE:substMail::SpamAssassin::Plugin::URIDNSBL::CORE:subst (opcode)
111108µs310µsMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:609]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:609]
11191µs617µsMail::SpamAssassin::Plugin::URIDNSBL::::newMail::SpamAssassin::Plugin::URIDNSBL::new
11178µs90µsMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:581]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:581]
11147µs47µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@295Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@295
11131µs215µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@307Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@307
11130µs36µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@301Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@301
11128µs544µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@296Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@296
11125µs87µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@297Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@297
11125µs162µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@298Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@298
11124µs37µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@299Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@299
11122µs59µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@300Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@300
11122µs92µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@302Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@302
11122µs93µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@304Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@304
11115µs15µsMail::SpamAssassin::Plugin::URIDNSBL::::has_tflags_domains_onlyMail::SpamAssassin::Plugin::URIDNSBL::has_tflags_domains_only
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:1070]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:1070]
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:634]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:634]
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:687]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:687]
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:715]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:715]
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:740]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:740]
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:768]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:768]
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:799]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:799]
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:810]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:810]
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:913]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:913]
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:998]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:998]
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::check_uridnsblMail::SpamAssassin::Plugin::URIDNSBL::check_uridnsbl
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::complete_a_lookupMail::SpamAssassin::Plugin::URIDNSBL::complete_a_lookup
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::complete_dnsbl_lookupMail::SpamAssassin::Plugin::URIDNSBL::complete_dnsbl_lookup
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::complete_ns_lookupMail::SpamAssassin::Plugin::URIDNSBL::complete_ns_lookup
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::got_dnsbl_hitMail::SpamAssassin::Plugin::URIDNSBL::got_dnsbl_hit
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::has_subtest_for_rangesMail::SpamAssassin::Plugin::URIDNSBL::has_subtest_for_ranges
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::has_uridnsbl_for_aMail::SpamAssassin::Plugin::URIDNSBL::has_uridnsbl_for_a
0000s0sMail::SpamAssassin::Plugin::URIDNSBL::::lookup_dnsbl_for_ipMail::SpamAssassin::Plugin::URIDNSBL::lookup_dnsbl_for_ip
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# <@LICENSE>
2# Licensed to the Apache Software Foundation (ASF) under one or more
3# contributor license agreements. See the NOTICE file distributed with
4# this work for additional information regarding copyright ownership.
5# The ASF licenses this file to you under the Apache License, Version 2.0
6# (the "License"); you may not use this file except in compliance with
7# the License. You may obtain a copy of the License at:
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16# </@LICENSE>
17
18=head1 NAME
19
20URIDNSBL - look up URLs against DNS blocklists
21
22=head1 SYNOPSIS
23
24 loadplugin Mail::SpamAssassin::Plugin::URIDNSBL
25 uridnsbl URIBL_SBLXBL sbl-xbl.spamhaus.org. TXT
26
27=head1 DESCRIPTION
28
29This works by analysing message text and HTML for URLs, extracting host
30names from those, then querying various DNS blocklists for either:
31IP addresses of these hosts (uridnsbl,a) or their nameservers (uridnsbl,ns),
32or domain names of these hosts (urirhsbl), or domain names of their
33nameservers (urinsrhsbl, urifullnsrhsbl).
34
35=head1 USER SETTINGS
36
37=over 4
38
39=item skip_uribl_checks ( 0 | 1 ) (default: 0)
40
41Turning on the skip_uribl_checks setting will disable the URIDNSBL plugin.
42
43By default, SpamAssassin will run URI DNSBL checks. Individual URI blocklists
44may be disabled selectively by setting a score of a corresponding rule to 0
45or through the uridnsbl_skip_domain parameter.
46
47See also a related configuration parameter skip_rbl_checks,
48which controls the DNSEval plugin (documented in the Conf man page).
49
50=back
51
52=over 4
53
54=item uridnsbl_skip_domain domain1 domain2 ...
55
56Specify a domain, or a number of domains, which should be skipped for the
57URIBL checks. This is very useful to specify very common domains which are
58not going to be listed in URIBLs.
59
60=back
61
62=over 4
63
64=item clear_uridnsbl_skip_domain [domain1 domain2 ...]
65
66If no argument is given, then clears the entire list of domains declared
67by I<uridnsbl_skip_domain> configuration directives so far. Any subsequent
68I<uridnsbl_skip_domain> directives will start creating a new list of skip
69domains.
70
71When given a list of domains as arguments, only the specified domains
72are removed from the list of skipped domains.
73
74=back
75
76=head1 RULE DEFINITIONS AND PRIVILEGED SETTINGS
77
78=over 4
79
80=item uridnsbl NAME_OF_RULE dnsbl_zone lookuptype
81
82Specify a lookup. C<NAME_OF_RULE> is the name of the rule to be
83used, C<dnsbl_zone> is the zone to look up IPs in, and C<lookuptype>
84is the type of lookup (B<TXT> or B<A>). Note that you must also
85define a body-eval rule calling C<check_uridnsbl()> to use this.
86
87This works by collecting domain names from URLs and querying DNS
88blocklists with an IP address of host names found in URLs or with
89IP addresses of their name servers, according to tflags as follows.
90
91If the corresponding body rule has a tflag 'a', the DNS blocklist will
92be queried with an IP address of a host found in URLs.
93
94If the corresponding body rule has a tflag 'ns', DNS will be queried
95for name servers (NS records) of a domain name found in URLs, then
96these name server names will be resolved to their IP addresses, which
97in turn will be sent to DNS blocklist.
98
99Tflags directive may specify either 'a' or 'ns' or both flags. In absence
100of any of these two flags, a default is a 'ns', which is compatible with
101pre-3.4 versions of SpamAssassin.
102
103The choice of tflags must correspond to the policy and expected use of
104each DNS blocklist and is normally not a local decision. As an example,
105a blocklist expecting queries resulting from an 'a' tflag is a
106"black_a.txt" ( http://www.uribl.com/datasets.shtml ).
107
108Example:
109
110 uridnsbl URIBL_SBLXBL sbl-xbl.spamhaus.org. TXT
111 body URIBL_SBLXBL eval:check_uridnsbl('URIBL_SBLXBL')
112 describe URIBL_SBLXBL Contains a URL listed in the SBL/XBL blocklist
113 tflags URIBL_SBLXBL net ns
114
115=item uridnssub NAME_OF_RULE dnsbl_zone lookuptype subtest
116
117Specify a DNSBL-style domain lookup with a sub-test. C<NAME_OF_RULE> is the
118name of the rule to be used, C<dnsbl_zone> is the zone to look up IPs in,
119and C<lookuptype> is the type of lookup (B<TXT> or B<A>).
120
121Tflags 'ns' and 'a' on a corresponding body rule are recognized and have
122the same meaning as in the uridnsbl directive.
123
124C<subtest> is a sub-test to run against the returned data. The sub-test may
125be in one of the following forms: m, n1-n2, or n/m, where n,n1,n2,m can be
126any of: decimal digits, 0x followed by up to 8 hexadecimal digits, or an IPv4
127address in quad-dot form. The 'A' records (IPv4 dotted address) as returned
128by DNSBLs lookups are converted into a numerical form (r) and checked against
129the specified sub-test as follows:
130for a range n1-n2 the following must be true: (r >= n1 && r <= n2);
131for a n/m form the following must be true: (r & m) == (n & m);
132for a single value in quad-dot form the following must be true: r == n;
133for a single decimal or hex form the following must be true:
134 ((r & n) != 0) && ((r & 0xff000000) == 0x7f000000), i.e. within 127.0.0.0/8
135
136Some typical examples of a sub-test are: 127.0.1.2, 127.0.1.20-127.0.1.39,
137127.0.1.0/255.255.255.0, 0.0.0.16/0.0.0.16, 0x10/0x10, 16, 0x10 .
138
139Note that, as with C<uridnsbl>, you must also define a body-eval rule calling
140C<check_uridnsbl()> to use this.
141
142Example:
143
144 uridnssub URIBL_DNSBL_4 dnsbl.example.org. A 127.0.0.4
145 uridnssub URIBL_DNSBL_8 dnsbl.example.org. A 8
146
147=item urirhsbl NAME_OF_RULE rhsbl_zone lookuptype
148
149Specify a RHSBL-style domain lookup. C<NAME_OF_RULE> is the name of the rule
150to be used, C<rhsbl_zone> is the zone to look up domain names in, and
151C<lookuptype> is the type of lookup (B<TXT> or B<A>). Note that you must also
152define a body-eval rule calling C<check_uridnsbl()> to use this.
153
154An RHSBL zone is one where the domain name is looked up, as a string; e.g. a
155URI using the domain C<foo.com> will cause a lookup of
156C<foo.com.uriblzone.net>. Note that hostnames are stripped from the domain
157used in the URIBL lookup, so the domain C<foo.bar.com> will look up
158C<bar.com.uriblzone.net>, and C<foo.bar.co.uk> will look up
159C<bar.co.uk.uriblzone.net>.
160
161If an URI consists of an IP address instead of a hostname, the IP address is
162looked up (using the standard reversed quads method) in each C<rhsbl_zone>.
163
164Example:
165
166 urirhsbl URIBL_RHSBL rhsbl.example.org. TXT
167
168=item urirhssub NAME_OF_RULE rhsbl_zone lookuptype subtest
169
170Specify a RHSBL-style domain lookup with a sub-test. C<NAME_OF_RULE> is the
171name of the rule to be used, C<rhsbl_zone> is the zone to look up domain names
172in, and C<lookuptype> is the type of lookup (B<TXT> or B<A>).
173
174C<subtest> is a sub-test to run against the returned data. The sub-test may
175be in one of the following forms: m, n1-n2, or n/m, where n,n1,n2,m can be
176any of: decimal digits, 0x followed by up to 8 hexadecimal digits, or an IPv4
177address in quad-dot form. The 'A' records (IPv4 dotted address) as returned
178by DNSBLs lookups are converted into a numerical form (r) and checked against
179the specified sub-test as follows:
180for a range n1-n2 the following must be true: (r >= n1 && r <= n2);
181for a n/m form the following must be true: (r & m) == (n & m);
182for a single value in quad-dot form the following must be true: r == n;
183for a single decimal or hex form the following must be true:
184 ((r & n) != 0) && ((r & 0xff000000) == 0x7f000000), i.e. within 127.0.0.0/8
185
186Some typical examples of a sub-test are: 127.0.1.2, 127.0.1.20-127.0.1.39,
187127.2.3.0/255.255.255.0, 0.0.0.16/0.0.0.16, 0x10/0x10, 16, 0x10 .
188
189Note that, as with C<urirhsbl>, you must also define a body-eval rule calling
190C<check_uridnsbl()> to use this.
191
192Example:
193
194 urirhssub URIBL_RHSBL_4 rhsbl.example.org. A 127.0.0.4
195 urirhssub URIBL_RHSBL_8 rhsbl.example.org. A 8
196
197=item urinsrhsbl NAME_OF_RULE rhsbl_zone lookuptype
198
199Perform a RHSBL-style domain lookup against the contents of the NS records
200for each URI. In other words, a URI using the domain C<foo.com> will cause
201an NS lookup to take place; assuming that domain has an NS of C<ns0.bar.com>,
202that will cause a lookup of C<bar.com.uriblzone.net>. Note that hostnames
203are stripped from both the domain used in the URI, and the domain in the
204lookup.
205
206C<NAME_OF_RULE> is the name of the rule to be used, C<rhsbl_zone> is the zone
207to look up domain names in, and C<lookuptype> is the type of lookup (B<TXT> or
208B<A>).
209
210Note that, as with C<urirhsbl>, you must also define a body-eval rule calling
211C<check_uridnsbl()> to use this.
212
213=item urinsrhssub NAME_OF_RULE rhsbl_zone lookuptype subtest
214
215Specify a RHSBL-style domain-NS lookup, as above, with a sub-test.
216C<NAME_OF_RULE> is the name of the rule to be used, C<rhsbl_zone> is the zone
217to look up domain names in, and C<lookuptype> is the type of lookup (B<TXT> or
218B<A>). C<subtest> is the sub-test to run against the returned data; see
219<urirhssub>.
220
221Note that, as with C<urirhsbl>, you must also define a body-eval rule calling
222C<check_uridnsbl()> to use this.
223
224=item urifullnsrhsbl NAME_OF_RULE rhsbl_zone lookuptype
225
226Perform a RHSBL-style domain lookup against the contents of the NS records for
227each URI. In other words, a URI using the domain C<foo.com> will cause an NS
228lookup to take place; assuming that domain has an NS of C<ns0.bar.com>, that
229will cause a lookup of C<ns0.bar.com.uriblzone.net>. Note that hostnames are
230stripped from the domain used in the URI.
231
232C<NAME_OF_RULE> is the name of the rule to be used, C<rhsbl_zone> is the zone
233to look up domain names in, and C<lookuptype> is the type of lookup (B<TXT> or
234B<A>).
235
236Note that, as with C<urirhsbl>, you must also define a body-eval rule calling
237C<check_uridnsbl()> to use this.
238
239=item urifullnsrhssub NAME_OF_RULE rhsbl_zone lookuptype subtest
240
241Specify a RHSBL-style domain-NS lookup, as above, with a sub-test.
242C<NAME_OF_RULE> is the name of the rule to be used, C<rhsbl_zone> is the zone
243to look up domain names in, and C<lookuptype> is the type of lookup (B<TXT> or
244B<A>). C<subtest> is the sub-test to run against the returned data; see
245<urirhssub>.
246
247Note that, as with C<urirhsbl>, you must also define a body-eval rule calling
248C<check_uridnsbl()> to use this.
249
250=item tflags NAME_OF_RULE ips_only
251
252Only URIs containing IP addresses as the "host" component will be matched
253against the named "urirhsbl"/"urirhssub" rule.
254
255=item tflags NAME_OF_RULE domains_only
256
257Only URIs containing a non-IP-address "host" component will be matched against
258the named "urirhsbl"/"urirhssub" rule.
259
260=item tflags NAME_OF_RULE ns
261
262The 'ns' flag may be applied to rules corresponding to uridnsbl and uridnssub
263directives. Host names from URLs will be mapped to their name server IP
264addresses (a NS lookup followed by an A lookup), which in turn will be sent
265to blocklists. This is a default when neither 'a' nor 'ns' flags are specified.
266
267=item tflags NAME_OF_RULE a
268
269The 'a' flag may be applied to rules corresponding to uridnsbl and uridnssub
270directives. Host names from URLs will be mapped to their IP addresses, which
271will be sent to blocklists. When both 'ns' and 'a' flags are specified,
272both queries will be performed.
273
274=back
275
276=head1 ADMINISTRATOR SETTINGS
277
278=over 4
279
280=item uridnsbl_max_domains N (default: 20)
281
282The maximum number of domains to look up.
283
284=back
285
286=head1 NOTES
287
288The C<uridnsbl_timeout> option has been obsoleted by the C<rbl_timeout>
289option. See the C<Mail::SpamAssassin::Conf> POD for details on C<rbl_timeout>.
290
291=cut
292
293package Mail::SpamAssassin::Plugin::URIDNSBL;
294
295281µs147µs
# spent 47µs within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@295 which was called: # once (47µs+0s) by Mail::SpamAssassin::PluginHandler::load_plugin at line 295
use Mail::SpamAssassin::Plugin;
296267µs21.06ms
# spent 544µs (28+517) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@296 which was called: # once (28µs+517µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 296
use Mail::SpamAssassin::Constants qw(:ip);
# spent 544µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@296 # spent 517µs making 1 call to Exporter::import
297260µs2148µs
# spent 87µs (25+62) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@297 which was called: # once (25µs+62µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 297
use Mail::SpamAssassin::Util;
# spent 87µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@297 # spent 62µs making 1 call to Exporter::import
298267µs2298µs
# spent 162µs (25+137) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@298 which was called: # once (25µs+137µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 298
use Mail::SpamAssassin::Logger;
# spent 162µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@298 # spent 137µs making 1 call to Exporter::import
299256µs250µs
# spent 37µs (24+13) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@299 which was called: # once (24µs+13µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 299
use strict;
# spent 37µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@299 # spent 13µs making 1 call to strict::import
300260µs296µs
# spent 59µs (22+37) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@300 which was called: # once (22µs+37µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 300
use warnings;
# spent 59µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@300 # spent 37µs making 1 call to warnings::import
301258µs243µs
# spent 36µs (30+6) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@301 which was called: # once (30µs+6µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 301
use bytes;
# spent 36µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@301 # spent 6µs making 1 call to bytes::import
302266µs2162µs
# spent 92µs (22+70) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@302 which was called: # once (22µs+70µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 302
use re 'taint';
# spent 92µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@302 # spent 70µs making 1 call to re::import
303
304294µs2164µs
# spent 93µs (22+71) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@304 which was called: # once (22µs+71µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 304
use vars qw(@ISA);
# spent 93µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@304 # spent 71µs making 1 call to vars::import
305129µs@ISA = qw(Mail::SpamAssassin::Plugin);
306
30729.20ms2400µs
# spent 215µs (31+184) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@307 which was called: # once (31µs+184µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 307
use constant LOG_COMPLETION_TIMES => 0;
# spent 215µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@307 # spent 184µs making 1 call to constant::import
308
309# constructor
310
# spent 617µs (91+525) within Mail::SpamAssassin::Plugin::URIDNSBL::new which was called: # once (91µs+525µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 1 of (eval 35)[Mail/SpamAssassin/PluginHandler.pm:129]
sub new {
31113µs my $class = shift;
31213µs my $samain = shift;
313
314 # some boilerplate...
31513µs $class = ref($class) || $class;
316115µs125µs my $self = $class->SUPER::new($samain);
# spent 25µs making 1 call to Mail::SpamAssassin::Plugin::new
31713µs bless ($self, $class);
318
319 # this can be effectively global, at least in each process, safely
320
321110µs $self->{finished} = { };
322
323111µs138µs $self->register_eval_rule ("check_uridnsbl");
# spent 38µs making 1 call to Mail::SpamAssassin::Plugin::register_eval_rule
324110µs1463µs $self->set_config($samain->{conf});
# spent 463µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::set_config
325
326110µs return $self;
327}
328
329# this is just a placeholder; in fact the results are dealt with later
330sub check_uridnsbl {
331 return 0;
332}
333
334# ---------------------------------------------------------------------------
335
336# once the metadata is parsed, we can access the URI list. So start off
337# the lookups here!
338
# spent 15.0s (602ms+14.4) within Mail::SpamAssassin::Plugin::URIDNSBL::parsed_metadata which was called 234 times, avg 64.1ms/call: # 234 times (602ms+14.4s) by Mail::SpamAssassin::PluginHandler::callback at line 204 of Mail/SpamAssassin/PluginHandler.pm, avg 64.1ms/call
sub parsed_metadata {
339234550µs my ($self, $opts) = @_;
340234828µs my $pms = $opts->{permsgstatus};
341234686µs my $conf = $pms->{conf};
342
343234781µs return 0 if $conf->{skip_uribl_checks};
344
3452342.77ms2349.63ms if (!$pms->is_dns_available()) {
# spent 9.63ms making 234 calls to Mail::SpamAssassin::PerMsgStatus::is_dns_available, avg 41µs/call
346 $self->{dns_not_available} = 1;
347 return 0;
348 } else {
349 # due to re-testing dns may become available after being unavailable
350 # DOS: I don't think dns_not_available is even used anymore
351234738µs $self->{dns_not_available} = 0;
352 }
353
354234967µs $pms->{'uridnsbl_activerules'} = { };
355234800µs $pms->{'uridnsbl_hits'} = { };
356234790µs $pms->{'uridnsbl_seen_lookups'} = { };
357
358 # only hit DNSBLs for active rules (defined and score != 0)
359234827µs $pms->{'uridnsbl_active_rules_rhsbl'} = { };
360234843µs $pms->{'uridnsbl_active_rules_rhsbl_ipsonly'} = { };
361234764µs $pms->{'uridnsbl_active_rules_rhsbl_domsonly'} = { };
362234806µs $pms->{'uridnsbl_active_rules_nsrhsbl'} = { };
363234862µs $pms->{'uridnsbl_active_rules_fullnsrhsbl'} = { };
364234771µs $pms->{'uridnsbl_active_rules_nsrevipbl'} = { };
365234853µs $pms->{'uridnsbl_active_rules_arevipbl'} = { };
366
3674685.75ms foreach my $rulename (keys %{$conf->{uridnsbls}}) {
368538236.9ms5382244ms next unless ($conf->is_rule_active('body_evals',$rulename));
# spent 244ms making 5382 calls to Mail::SpamAssassin::Conf::is_rule_active, avg 45µs/call
369
370538241.6ms my $rulecf = $conf->{uridnsbls}->{$rulename};
371538233.0ms my $tflags = $conf->{tflags}->{$rulename};
37253828.91ms $tflags = '' if !defined $tflags;
37313806113ms my %tfl = map { ($_,1) } split(' ',$tflags);
374
375538228.9ms my $is_rhsbl = $rulecf->{is_rhsbl};
376538235.7ms if ( $is_rhsbl && $tfl{'ips_only'}) {
377 $pms->{uridnsbl_active_rules_rhsbl_ipsonly}->{$rulename} = 1;
378 } elsif ($is_rhsbl && $tfl{'domains_only'}) {
379234015.4ms $pms->{uridnsbl_active_rules_rhsbl_domsonly}->{$rulename} = 1;
380 } elsif ($is_rhsbl) {
381257417.0ms $pms->{uridnsbl_active_rules_rhsbl}->{$rulename} = 1;
382 } elsif ($rulecf->{is_fullnsrhsbl}) {
383 $pms->{uridnsbl_active_rules_fullnsrhsbl}->{$rulename} = 1;
384 } elsif ($rulecf->{is_nsrhsbl}) {
385 $pms->{uridnsbl_active_rules_nsrhsbl}->{$rulename} = 1;
386 } else { # just a plain dnsbl rule (IP based), not a RHS rule (name-based)
3874681.36ms if ($tfl{'a'}) { # tflag 'a' explicitly
388234750µs $pms->{uridnsbl_active_rules_arevipbl}->{$rulename} = 1;
389 }
3904682.67ms if ($tfl{'ns'} || !$tfl{'a'}) { # tflag 'ns' explicitly, or default
391234715µs $pms->{uridnsbl_active_rules_nsrevipbl}->{$rulename} = 1;
392 }
393 }
394 }
395
396 # get all domains in message
397
398 # don't keep dereferencing this
399234745µs my $skip_domains = $conf->{uridnsbl_skip_domains};
400234468µs $skip_domains = {} if !$skip_domains;
401
402 # list of hashes to use in order
403234465µs my @uri_ordered;
404
405 # Generate the full list of html-parsed domains.
4062342.52ms2345.76s my $uris = $pms->get_uri_detail_list();
# spent 5.76s making 234 calls to Mail::SpamAssassin::PerMsgStatus::get_uri_detail_list, avg 24.6ms/call
407
408 # go from uri => info to uri_ordered
409 # 0: a
410 # 1: form
411 # 2: img
412 # 3: !a_empty
413 # 4: parsed
414 # 5: a_empty
415302433.6ms while (my($uri, $info) = each %{$uris}) {
416 # we want to skip mailto: uris
417255631.6ms25569.30ms next if ($uri =~ /^mailto:/i);
# spent 9.30ms making 2556 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 4µs/call
418
419 # no hosts/domains were found via this uri, so skip
42024466.69ms next unless ($info->{hosts});
421
42224294.22ms my $entry = 3;
423
424242924.2ms if ($info->{types}->{a}) {
42510111.68ms $entry = 5;
426
427 # determine a vs a_empty
42820229.84ms foreach my $at (@{$info->{anchor_text}}) {
42910122.29ms if (length $at) {
43010061.69ms $entry = 0;
43110061.78ms last;
432 }
433 }
434 }
435 elsif ($info->{types}->{form}) {
436 $entry = 1;
437 }
438 elsif ($info->{types}->{img}) {
4397121.19ms $entry = 2;
440 }
4416901.38ms elsif ($info->{types}->{parsed} && (keys %{$info->{types}} == 1)) {
4426901.20ms $entry = 4;
443 }
444
445 # take the usable domains and add them to the ordered list
446729854.1ms while (my($host,$domain) = each( %{$info->{hosts}} )) {
44724409.15ms if ($skip_domains->{$domain}) {
44842499µs42332µs dbg("uridnsbl: domain $domain in skip list, host $host");
# spent 332µs making 42 calls to Mail::SpamAssassin::Logger::dbg, avg 8µs/call
449 } else {
450 # use hostname as a key, and drag along the stripped domain name part
45123985.74ms $uri_ordered[$entry]->{$host} = $domain;
452 }
453 }
454 }
455
456 # at this point, @uri_ordered is an ordered array of hostname hashes
457
458234545µs my %hostlist; # keys are host names, values are their domain parts
459
460234647µs my $umd = $conf->{uridnsbl_max_domains};
4612342.76ms while (keys %hostlist < $umd && @uri_ordered) {
4627201.37ms my $array = shift @uri_ordered;
4637202.32ms next unless $array;
464
465 # run through and find the new domains in this grouping
4667003.91ms my @hosts = grep(!$hostlist{$_}, keys %{$array});
467350987µs next unless @hosts;
468
469 # the new hosts are all useful, just add them in
4702802.92ms if (keys(%hostlist) + @hosts <= $umd) {
4712801.17ms foreach my $host (@hosts) {
4723831.81ms $hostlist{$host} = $array->{$host};
473 }
474 }
475 else {
476 dbg("uridnsbl: more than $umd URIs, picking a subset");
477 # trim down to a limited number - pick randomly
478 while (@hosts && keys %hostlist < $umd) {
479 my $r = int rand(scalar @hosts);
480 my $picked_host = splice(@hosts, $r, 1);
481 $hostlist{$picked_host} = $array->{$picked_host};
482 }
483 }
484 }
485
4862341.05ms my @hnames = keys %hostlist;
4872341.83ms15510.1ms $pms->set_tag('URIHOSTS',
# spent 10.1ms making 155 calls to Mail::SpamAssassin::PerMsgStatus::set_tag, avg 65µs/call
488 @hnames == 1 ? $hnames[0] : \@hnames) if @hnames;
489234921µs my @dnames = values %hostlist;
4902341.49ms1558.74ms $pms->set_tag('URIDOMAINS',
# spent 8.74ms making 155 calls to Mail::SpamAssassin::PerMsgStatus::set_tag, avg 56µs/call
491 @dnames == 1 ? $dnames[0] : \@dnames) if @dnames;
492
493 # and query
4942342.75ms2348.36s $self->query_hosts_or_domains($pms, \%hostlist);
# spent 8.36s making 234 calls to Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains, avg 35.7ms/call
495
4962343.01ms return 1;
497}
498
499# Accepts argument in one of the following forms: m, n1-n2, or n/m,
500# where n,n1,n2,m can be any of: decimal digits, 0x followed by up to 8
501# hexadecimal digits, or an IPv4 address in quad-dot form. The argument
502# is checked for syntax (undef is returned on syntax errors), hex numbers
503# are converted to decimal, and quad-dot is converted to decimal, then
504# reassembled into original string delimited by '-' or '/'. As a special
505# backward compatibility measure, a single quad-dot (with no second number)
506# is converted into n-n, to distinguish it from a traditional mask-only form.
507#
508# In practice, arguments like the following are anticipated:
509# 127.0.1.2 (same as 127.0.1.2-127.0.1.2 or 127.0.1.2/255.255.255.255)
510# 127.0.1.20-127.0.1.39 (= 0x7f000114-0x7f000127 or 2130706708-2130706727)
511# 0.0.0.16/0.0.0.16 (same as 0x10/0x10 or 16/0x10 or 16/16)
512# 16 (traditional style mask-only, same as 0x10)
513#
514
# spent 2.14ms (1.50+633µs) within Mail::SpamAssassin::Plugin::URIDNSBL::parse_and_canonicalize_subtest which was called 22 times, avg 97µs/call: # 21 times (1.40ms+554µs) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:662] at line 649, avg 93µs/call # once (107µs+79µs) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:609] at line 596
sub parse_and_canonicalize_subtest {
5152258µs my($subtest) = @_;
5162240µs my $digested_subtest;
517
5182278µs local($1,$2,$3);
51922303µs22101µs if ($subtest =~ m{^ ([^/-]+) (?: ([/-]) (.+) )? \z}xs) {
# spent 101µs making 22 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 5µs/call
52022104µs my($n1,$delim,$n2) = ($1,$2,$3);
5212234µs my $any_quad_dot;
52222100µs for ($n1,$n2) {
52344631µs44167µs if (!defined $_) {
# spent 167µs making 44 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 4µs/call
524 # ok, $n2 may not exist
525 } elsif (/^\d{1,10}\z/) {
526 # ok, already a decimal number
527 } elsif (/^0x[0-9a-zA-Z]{1,8}\z/) {
528 $_ = hex($_); # hex -> number
529 } elsif (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/) {
5301182µs11365µs $_ = Mail::SpamAssassin::Util::my_inet_aton($_); # quad-dot -> number
# spent 365µs making 11 calls to Mail::SpamAssassin::Util::my_inet_aton, avg 33µs/call
5311129µs $any_quad_dot = 1;
532 } else {
533 return;
534 }
535 }
53622127µs $digested_subtest = defined $n2 ? $n1.$delim.$n2
537 : $any_quad_dot ? $n1.'-'.$n1 : "$n1";
538 }
53922221µs return $digested_subtest;
540}
541
542
# spent 463µs (142+321) within Mail::SpamAssassin::Plugin::URIDNSBL::set_config which was called: # once (142µs+321µs) by Mail::SpamAssassin::Plugin::URIDNSBL::new at line 324
sub set_config {
54312µs my($self, $conf) = @_;
54412µs my @cmds;
545
54617µs push(@cmds, {
547 setting => 'skip_uribl_checks',
548 default => 0,
549 type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL,
550 });
551
55217µs push(@cmds, {
553 setting => 'uridnsbl_max_domains',
554 is_admin => 1,
555 default => 20,
556 type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
557 });
558
559 push (@cmds, {
560 setting => 'uridnsbl',
561 is_priv => 1,
562
# spent 90µs (78+12) within Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:581] which was called: # once (78µs+12µs) by Mail::SpamAssassin::Conf::Parser::parse at line 438 of Mail/SpamAssassin/Conf/Parser.pm
code => sub {
56316µs my ($self, $key, $value, $line) = @_;
56414µs local($1,$2,$3);
565137µs16µs if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)$/) {
56614µs my $rulename = $1;
56713µs my $zone = $2;
56813µs my $type = $3;
569123µs16µs $zone =~ s/\.\z//; # strip a redundant trailing dot
57019µs $self->{uridnsbls}->{$rulename} = {
571 zone => $zone, type => $type,
572 is_rhsbl => 0
573 };
574 }
575 elsif ($value =~ /^$/) {
576 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
577 }
578 else {
579 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
580 }
581 }
582116µs });
583
584 push (@cmds, {
585 setting => 'uridnssub',
586 is_priv => 1,
587
# spent 310µs (108+201) within Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:609] which was called: # once (108µs+201µs) by Mail::SpamAssassin::Conf::Parser::parse at line 438 of Mail/SpamAssassin/Conf/Parser.pm
code => sub {
58817µs my ($self, $key, $value, $line) = @_;
58917µs local($1,$2,$3,$4);
590152µs19µs if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(.*?)\s*$/) {
59114µs my $rulename = $1;
59213µs my $zone = $2;
59313µs my $type = $3;
59413µs my $subrule = $4;
595119µs16µs $zone =~ s/\.\z//; # strip a redundant trailing dot
596110µs1186µs $subrule = parse_and_canonicalize_subtest($subrule);
59712µs defined $subrule or return $Mail::SpamAssassin::Conf::INVALID_VALUE;
598114µs $self->{uridnsbls}->{$rulename} = {
599 zone => $zone, type => $type,
600 is_rhsbl => 0, subtest => $subrule,
601 };
602 }
603 elsif ($value =~ /^$/) {
604 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
605 }
606 else {
607 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
608 }
609 }
610117µs });
611
612 push (@cmds, {
613 setting => 'urirhsbl',
614 is_priv => 1,
615 code => sub {
616 my ($self, $key, $value, $line) = @_;
617 local($1,$2,$3);
618 if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)$/) {
619 my $rulename = $1;
620 my $zone = $2;
621 my $type = $3;
622 $zone =~ s/\.\z//; # strip a redundant trailing dot
623 $self->{uridnsbls}->{$rulename} = {
624 zone => $zone, type => $type,
625 is_rhsbl => 1
626 };
627 }
628 elsif ($value =~ /^$/) {
629 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
630 }
631 else {
632 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
633 }
634 }
63519µs });
636
637 push (@cmds, {
638 setting => 'urirhssub',
639 is_priv => 1,
640
# spent 3.76ms (1.57+2.19) within Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:662] which was called 21 times, avg 179µs/call: # 21 times (1.57ms+2.19ms) by Mail::SpamAssassin::Conf::Parser::parse at line 438 of Mail/SpamAssassin/Conf/Parser.pm, avg 179µs/call
code => sub {
64121114µs my ($self, $key, $value, $line) = @_;
64221105µs local($1,$2,$3,$4);
64321515µs21134µs if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(.*?)\s*$/) {
# spent 134µs making 21 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 6µs/call
6442166µs my $rulename = $1;
6452154µs my $zone = $2;
6462154µs my $type = $3;
6472152µs my $subrule = $4;
64821262µs21106µs $zone =~ s/\.\z//; # strip a redundant trailing dot
# spent 106µs making 21 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:subst, avg 5µs/call
64921158µs211.95ms $subrule = parse_and_canonicalize_subtest($subrule);
# spent 1.95ms making 21 calls to Mail::SpamAssassin::Plugin::URIDNSBL::parse_and_canonicalize_subtest, avg 93µs/call
6502139µs defined $subrule or return $Mail::SpamAssassin::Conf::INVALID_VALUE;
65121361µs $self->{uridnsbls}->{$rulename} = {
652 zone => $zone, type => $type,
653 is_rhsbl => 1, subtest => $subrule,
654 };
655 }
656 elsif ($value =~ /^$/) {
657 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
658 }
659 else {
660 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
661 }
662 }
663110µs });
664
665 push (@cmds, {
666 setting => 'urinsrhsbl',
667 is_priv => 1,
668 code => sub {
669 my ($self, $key, $value, $line) = @_;
670 local($1,$2,$3);
671 if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)$/) {
672 my $rulename = $1;
673 my $zone = $2;
674 my $type = $3;
675 $zone =~ s/\.\z//; # strip a redundant trailing dot
676 $self->{uridnsbls}->{$rulename} = {
677 zone => $zone, type => $type,
678 is_nsrhsbl => 1
679 };
680 }
681 elsif ($value =~ /^$/) {
682 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
683 }
684 else {
685 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
686 }
687 }
68819µs });
689
690 push (@cmds, {
691 setting => 'urinsrhssub',
692 is_priv => 1,
693 code => sub {
694 my ($self, $key, $value, $line) = @_;
695 local($1,$2,$3,$4);
696 if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(.*?)\s*$/) {
697 my $rulename = $1;
698 my $zone = $2;
699 my $type = $3;
700 my $subrule = $4;
701 $zone =~ s/\.\z//; # strip a redundant trailing dot
702 $subrule = parse_and_canonicalize_subtest($subrule);
703 defined $subrule or return $Mail::SpamAssassin::Conf::INVALID_VALUE;
704 $self->{uridnsbls}->{$rulename} = {
705 zone => $zone, type => $type,
706 is_nsrhsbl => 1, subtest => $subrule,
707 };
708 }
709 elsif ($value =~ /^$/) {
710 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
711 }
712 else {
713 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
714 }
715 }
71618µs });
717
718 push (@cmds, {
719 setting => 'urifullnsrhsbl',
720 is_priv => 1,
721 code => sub {
722 my ($self, $key, $value, $line) = @_;
723 local($1,$2,$3);
724 if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)$/) {
725 my $rulename = $1;
726 my $zone = $2;
727 my $type = $3;
728 $zone =~ s/\.\z//; # strip a redundant trailing dot
729 $self->{uridnsbls}->{$rulename} = {
730 zone => $zone, type => $type,
731 is_fullnsrhsbl => 1
732 };
733 }
734 elsif ($value =~ /^$/) {
735 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
736 }
737 else {
738 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
739 }
740 }
74118µs });
742
743 push (@cmds, {
744 setting => 'urifullnsrhssub',
745 is_priv => 1,
746 code => sub {
747 my ($self, $key, $value, $line) = @_;
748 local($1,$2,$3,$4);
749 if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(.*?)\s*$/) {
750 my $rulename = $1;
751 my $zone = $2;
752 my $type = $3;
753 my $subrule = $4;
754 $zone =~ s/\.\z//; # strip a redundant trailing dot
755 $subrule = parse_and_canonicalize_subtest($subrule);
756 defined $subrule or return $Mail::SpamAssassin::Conf::INVALID_VALUE;
757 $self->{uridnsbls}->{$rulename} = {
758 zone => $zone, type => $type,
759 is_fullnsrhsbl => 1, subtest => $subrule,
760 };
761 }
762 elsif ($value =~ /^$/) {
763 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
764 }
765 else {
766 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
767 }
768 }
76918µs });
770
771 push (@cmds, {
772 setting => 'uridnsbl_skip_domain',
773 default => {},
774 type => $Mail::SpamAssassin::Conf::CONF_TYPE_HASH_KEY_VALUE,
775
# spent 3.87ms (3.70+168µs) within Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:783] which was called 63 times, avg 61µs/call: # 63 times (3.70ms+168µs) by Mail::SpamAssassin::Conf::Parser::parse at line 438 of Mail/SpamAssassin/Conf/Parser.pm, avg 61µs/call
code => sub {
77663286µs my ($self, $key, $value, $line) = @_;
77763541µs63168µs if ($value =~ /^$/) {
# spent 168µs making 63 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 3µs/call
778 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
779 }
78063827µs foreach my $domain (split(/\s+/, $value)) {
7812152.31ms $self->{uridnsbl_skip_domains}->{lc $domain} = 1;
782 }
783 }
78418µs });
785
786 push (@cmds, {
787 setting => 'clear_uridnsbl_skip_domain',
788 type => $Mail::SpamAssassin::Conf::CONF_TYPE_HASH_KEY_VALUE,
789 code => sub {
790 my ($self, $key, $value, $line) = @_;
791 if (!defined $value || $value eq '') {
792 # clear the entire list
793 $self->{uridnsbl_skip_domains} = {};
794 } else {
795 foreach my $domain (split(/\s+/, $value)) {
796 delete $self->{uridnsbl_skip_domains}->{lc $domain};
797 }
798 }
799 }
80017µs });
801
802 # obsolete
803 push(@cmds, {
804 setting => 'uridnsbl_timeout',
805 code => sub {
806 # not a lint_warn(), since it's pretty harmless and we don't want
807 # to break stuff like sa-update
808 warn("config: 'uridnsbl_timeout' is obsolete, use 'rbl_timeout' instead");
809 return 0;
810 }
81116µs });
812
813118µs1321µs $conf->{parser}->register_commands(\@cmds);
814}
815
816# ---------------------------------------------------------------------------
817
818
# spent 8.36s (424ms+7.94) within Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains which was called 234 times, avg 35.7ms/call: # 234 times (424ms+7.94s) by Mail::SpamAssassin::Plugin::URIDNSBL::parsed_metadata at line 494, avg 35.7ms/call
sub query_hosts_or_domains {
819234550µs my ($self, $pms, $hosthash_ref) = @_;
820234817µs my $conf = $pms->{conf};
821234652µs my $seen_lookups = $pms->{'uridnsbl_seen_lookups'};
822
823234697µs my $rhsblrules = $pms->{uridnsbl_active_rules_rhsbl};
824234562µs my $rhsbliprules = $pms->{uridnsbl_active_rules_rhsbl_ipsonly};
825234546µs my $rhsbldomrules = $pms->{uridnsbl_active_rules_rhsbl_domsonly};
826234580µs my $nsrhsblrules = $pms->{uridnsbl_active_rules_nsrhsbl};
827234627µs my $fullnsrhsblrules = $pms->{uridnsbl_active_rules_fullnsrhsbl};
828234531µs my $nsreviprules = $pms->{uridnsbl_active_rules_nsrevipbl};
829234620µs my $areviprules = $pms->{uridnsbl_active_rules_arevipbl};
830
8312346.70ms while (my($host,$domain) = each(%$hosthash_ref)) {
8323831.15ms $domain = lc $domain; # just in case
8333831.17ms $host = lc $host;
8343833.79ms3832.98ms dbg("uridnsbl: considering host=$host, domain=$domain");
# spent 2.98ms making 383 calls to Mail::SpamAssassin::Logger::dbg, avg 8µs/call
8353832.79ms my $obj = { dom => $domain };
836
837383775µs my ($is_ip, $single_dnsbl);
8383835.97ms3832.42ms if ($host =~ /^\d+\.\d+\.\d+\.\d+$/) {
# spent 2.42ms making 383 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 6µs/call
839 my $IPV4_ADDRESS = IPV4_ADDRESS;
840 my $IP_PRIVATE = IP_PRIVATE;
841 # only look up the IP if it is public and valid
842 if ($host =~ /^$IPV4_ADDRESS$/o && $host !~ /^$IP_PRIVATE$/o) {
843 my $obj = { dom => $host };
844 $self->lookup_dnsbl_for_ip($pms, $obj, $host);
845 # and check the IP in RHSBLs too
846 local($1,$2,$3,$4);
847 if ($host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
848 $domain = "$4.$3.$2.$1";
849 $single_dnsbl = 1;
850 $is_ip = 1;
851 }
852 }
853 }
854 else {
855383829µs $single_dnsbl = 1;
856 }
857
8583832.63ms if ($single_dnsbl) {
859 # rule names which look up a domain in the basic RHSBL subset
8607665.59ms my @rhsblrules = keys %{$rhsblrules};
861
862 # and add the "domains_only" and "ips_only" subsets as appropriate
8633831.64ms if ($is_ip) {
864 push @rhsblrules, keys %{$rhsbliprules};
865 } else {
8667664.05ms push @rhsblrules, keys %{$rhsbldomrules};
867 }
868
8693832.15ms foreach my $rulename (@rhsblrules) {
870804332.7ms my $rulecf = $conf->{uridnsbls}->{$rulename};
871 $self->lookup_single_dnsbl($pms, $obj, $rulename,
872804383.1ms80435.68s $domain, $rulecf->{zone}, $rulecf->{type});
# spent 5.68s making 8043 calls to Mail::SpamAssassin::Plugin::URIDNSBL::lookup_single_dnsbl, avg 706µs/call
873
874 # note that these rules are now underway. important: unless the
875 # rule hits, in the current design, these will not be considered
876 # "finished" until harvest_dnsbl_queries() completes
877804374.4ms8043266ms $pms->register_async_rule_start($rulename);
# spent 266ms making 8043 calls to Mail::SpamAssassin::PerMsgStatus::register_async_rule_start, avg 33µs/call
878 }
879
880 # perform NS+A or A queries to look up the domain in the non-RHSBL subset,
881 # but only if there are active reverse-IP-URIBL rules
8823837.77ms3832.05ms if ($host !~ /^\d+\.\d+\.\d+\.\d+$/) {
# spent 2.05ms making 383 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 5µs/call
8833835.16ms if ( !$seen_lookups->{'NS:'.$domain} &&
884 (%$nsreviprules || %$nsrhsblrules || %$fullnsrhsblrules) ) {
8853171.39ms $seen_lookups->{'NS:'.$domain} = 1;
8863172.66ms317892ms $self->lookup_domain_ns($pms, $obj, $domain);
# spent 892ms making 317 calls to Mail::SpamAssassin::Plugin::URIDNSBL::lookup_domain_ns, avg 2.81ms/call
887 }
8883833.38ms if (%$areviprules && !$seen_lookups->{'A:'.$host}) {
8893832.31ms $seen_lookups->{'A:'.$host} = 1;
8903831.63ms my $obj = { dom => $host };
8913833.32ms3831.09s $self->lookup_a_record($pms, $obj, $host);
# spent 1.09s making 383 calls to Mail::SpamAssassin::Plugin::URIDNSBL::lookup_a_record, avg 2.84ms/call
8923836.66ms38312.5ms $pms->register_async_rule_start($_) for keys %$areviprules;
# spent 12.5ms making 383 calls to Mail::SpamAssassin::PerMsgStatus::register_async_rule_start, avg 33µs/call
893 }
894 }
895 }
896 }
897}
898
899# ---------------------------------------------------------------------------
900
901
# spent 892ms (14.4+878) within Mail::SpamAssassin::Plugin::URIDNSBL::lookup_domain_ns which was called 317 times, avg 2.81ms/call: # 317 times (14.4ms+878ms) by Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains at line 886, avg 2.81ms/call
sub lookup_domain_ns {
9023171.00ms my ($self, $pms, $obj, $dom, $rulename) = @_;
903
9043171.56ms my $key = "NS:" . $dom;
9053172.39ms my $ent = {
906 key => $key, zone => $dom, obj => $obj, type => "URI-NS",
907 rulename => $rulename,
908 };
909 # dig $dom ns
910 $ent = $pms->{async}->bgsend_and_start_lookup(
911 $dom, 'NS', undef, $ent,
912 sub { my ($ent2,$pkt) = @_;
913 $self->complete_ns_lookup($pms, $ent2, $pkt, $dom) },
9143174.79ms317878ms master_deadline => $pms->{master_deadline} );
# spent 878ms making 317 calls to Mail::SpamAssassin::AsyncLoop::bgsend_and_start_lookup, avg 2.77ms/call
915
9163173.13ms return $ent;
917}
918
919sub complete_ns_lookup {
920 my ($self, $pms, $ent, $pkt, $dom) = @_;
921
922 if (!$pkt) {
923 # $pkt will be undef if the DNS query was aborted (e.g. timed out)
924 dbg("uridnsbl: complete_ns_lookup aborted %s", $ent->{key});
925 return;
926 }
927
928 dbg("uridnsbl: complete_ns_lookup %s", $ent->{key});
929 my $conf = $pms->{conf};
930 my @answer = $pkt->answer;
931
932 my $IPV4_ADDRESS = IPV4_ADDRESS;
933 my $IP_PRIVATE = IP_PRIVATE;
934 my $nsrhsblrules = $pms->{uridnsbl_active_rules_nsrhsbl};
935 my $fullnsrhsblrules = $pms->{uridnsbl_active_rules_fullnsrhsbl};
936 my $seen_lookups = $pms->{'uridnsbl_seen_lookups'};
937
938 my $j = 0;
939 foreach my $rr (@answer) {
940 $j++;
941 my $str = $rr->string;
942 next unless (defined($str) && defined($dom));
943 dbg("uridnsbl: got($j) NS for $dom: $str");
944
945 if ($rr->type eq 'NS') {
946 my $nsmatch = lc $rr->nsdname; # available since at least Net::DNS 0.14
947 my $nsrhblstr = $nsmatch;
948 my $fullnsrhblstr = $nsmatch;
949
950 if ($nsmatch =~ /^\d+\.\d+\.\d+\.\d+$/) {
951 # only look up the IP if it is public and valid
952 if ($nsmatch =~ /^$IPV4_ADDRESS$/o && $nsmatch !~ /^$IP_PRIVATE$/o) {
953 $self->lookup_dnsbl_for_ip($pms, $ent->{obj}, $nsmatch);
954 }
955 $nsrhblstr = $nsmatch;
956 }
957 else {
958 if (!$seen_lookups->{'A:'.$nsmatch}) {
959 $seen_lookups->{'A:'.$nsmatch} = 1;
960 $self->lookup_a_record($pms, $ent->{obj}, $nsmatch);
961 }
962 $nsrhblstr = $self->{main}->{registryboundaries}->trim_domain($nsmatch);
963 }
964
965 foreach my $rulename (keys %{$nsrhsblrules}) {
966 my $rulecf = $conf->{uridnsbls}->{$rulename};
967 $self->lookup_single_dnsbl($pms, $ent->{obj}, $rulename,
968 $nsrhblstr, $rulecf->{zone}, $rulecf->{type});
969
970 $pms->register_async_rule_start($rulename);
971 }
972
973 foreach my $rulename (keys %{$fullnsrhsblrules}) {
974 my $rulecf = $conf->{uridnsbls}->{$rulename};
975 $self->lookup_single_dnsbl($pms, $ent->{obj}, $rulename,
976 $fullnsrhblstr, $rulecf->{zone}, $rulecf->{type});
977
978 $pms->register_async_rule_start($rulename);
979 }
980 }
981 }
982}
983
984# ---------------------------------------------------------------------------
985
986
# spent 1.09s (17.6ms+1.07) within Mail::SpamAssassin::Plugin::URIDNSBL::lookup_a_record which was called 383 times, avg 2.84ms/call: # 383 times (17.6ms+1.07s) by Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains at line 891, avg 2.84ms/call
sub lookup_a_record {
9873831.06ms my ($self, $pms, $obj, $hname, $rulename) = @_;
988
9893831.68ms my $key = "A:" . $hname;
9903832.91ms my $ent = {
991 key => $key, zone => $hname, obj => $obj, type => "URI-A",
992 rulename => $rulename,
993 };
994 # dig $hname a
995 $ent = $pms->{async}->bgsend_and_start_lookup(
996 $hname, 'A', undef, $ent,
997 sub { my ($ent2,$pkt) = @_;
998 $self->complete_a_lookup($pms, $ent2, $pkt, $hname) },
9993836.30ms3831.07s master_deadline => $pms->{master_deadline} );
# spent 1.07s making 383 calls to Mail::SpamAssassin::AsyncLoop::bgsend_and_start_lookup, avg 2.79ms/call
1000
10013833.59ms return $ent;
1002}
1003
1004sub complete_a_lookup {
1005 my ($self, $pms, $ent, $pkt, $hname) = @_;
1006
1007 if (!$pkt) {
1008 # $pkt will be undef if the DNS query was aborted (e.g. timed out)
1009 dbg("uridnsbl: complete_a_lookup aborted %s", $ent->{key});
1010 return;
1011 }
1012
1013 dbg("uridnsbl: complete_a_lookup %s", $ent->{key});
1014 my @answer = $pkt->answer;
1015 my $j = 0;
1016 foreach my $rr (@answer) {
1017 $j++;
1018 my $str = $rr->string;
1019 if (!defined $hname) {
1020 warn "complete_a_lookup-1: $j, (hname is undef), $str";
1021 } elsif (!defined $str) {
1022 warn "complete_a_lookup-2: $j, $hname, (str is undef)";
1023 next;
1024 }
1025 dbg("uridnsbl: complete_a_lookup got(%d) A for %s: %s", $j,$hname,$str);
1026
1027 if ($rr->type eq 'A') {
1028 my $ip_address = $rr->rdatastr;
1029 $self->lookup_dnsbl_for_ip($pms, $ent->{obj}, $ip_address);
1030 }
1031 }
1032}
1033
1034# ---------------------------------------------------------------------------
1035
1036sub lookup_dnsbl_for_ip {
1037 my ($self, $pms, $obj, $ip) = @_;
1038
1039 local($1,$2,$3,$4);
1040 $ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
1041 my $revip = "$4.$3.$2.$1";
1042
1043 my $conf = $pms->{conf};
1044 my $tflags = $conf->{tflags};
1045 my $cfns = $pms->{uridnsbl_active_rules_nsrevipbl};
1046 my $cfa = $pms->{uridnsbl_active_rules_arevipbl};
1047 foreach my $rulename (keys %$cfa, keys %$cfns) {
1048 my $rulecf = $conf->{uridnsbls}->{$rulename};
1049
1050 # ips_only/domains_only lookups should not act on this kind of BL
1051 next if defined $tflags->{$rulename} &&
1052 $tflags->{$rulename} =~ /\b(?:ips_only|domains_only)\b/;
1053
1054 $self->lookup_single_dnsbl($pms, $obj, $rulename,
1055 $revip, $rulecf->{zone}, $rulecf->{type});
1056 }
1057}
1058
1059
# spent 5.68s (517ms+5.16) within Mail::SpamAssassin::Plugin::URIDNSBL::lookup_single_dnsbl which was called 8043 times, avg 706µs/call: # 8043 times (517ms+5.16s) by Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains at line 872, avg 706µs/call
sub lookup_single_dnsbl {
1060804379.3ms my ($self, $pms, $obj, $rulename, $lookupstr, $dnsbl, $qtype) = @_;
1061
1062804338.7ms my $key = "DNSBL:" . $lookupstr . ':' . $dnsbl;
1063804395.5ms my $ent = {
1064 key => $key, zone => $dnsbl, obj => $obj, type => 'URI-DNSBL',
1065 rulename => $rulename,
1066 };
1067 $ent = $pms->{async}->bgsend_and_start_lookup(
1068 $lookupstr.".".$dnsbl, $qtype, undef, $ent,
1069 sub { my ($ent2,$pkt) = @_;
1070 $self->complete_dnsbl_lookup($pms, $ent2, $pkt) },
10718043153ms80435.16s master_deadline => $pms->{master_deadline} );
# spent 5.16s making 8043 calls to Mail::SpamAssassin::AsyncLoop::bgsend_and_start_lookup, avg 641µs/call
1072
10738043142ms return $ent;
1074}
1075
1076sub complete_dnsbl_lookup {
1077 my ($self, $pms, $ent, $pkt) = @_;
1078
1079 if (!$pkt) {
1080 # $pkt will be undef if the DNS query was aborted (e.g. timed out)
1081 dbg("uridnsbl: complete_dnsbl_lookup aborted %s %s",
1082 $ent->{rulename}, $ent->{key});
1083 return;
1084 }
1085
1086 dbg("uridnsbl: complete_dnsbl_lookup %s %s", $ent->{rulename}, $ent->{key});
1087 my $conf = $pms->{conf};
1088
1089 my $zone = $ent->{zone};
1090 my $dom = $ent->{obj}->{dom};
1091 my $rulename = $ent->{rulename};
1092 my $rulecf = $conf->{uridnsbls}->{$rulename};
1093
1094 my @subtests;
1095 my @answer = $pkt->answer;
1096 foreach my $rr (@answer)
1097 {
1098 my($rdatastr,$rdatanum);
1099 my $rr_type = $rr->type;
1100
1101 if ($rr_type eq 'A') {
1102 $rdatastr = $rr->rdatastr;
1103 if ($rdatastr =~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
1104 $rdatanum = Mail::SpamAssassin::Util::my_inet_aton($rdatastr);
1105 }
1106 } elsif ($rr_type eq 'TXT') {
1107 # txtdata returns a non- zone-file-format encoded result, unlike rdatastr;
1108 # avoid space-separated RDATA <character-string> fields if possible;
1109 # txtdata provides a list of strings in list context since Net::DNS 0.69
1110 $rdatastr = join('',$rr->txtdata);
1111 } else {
1112 next;
1113 }
1114
1115 my $subtest = $rulecf->{subtest};
1116
1117 dbg("uridnsbl: %s . %s -> %s, %s%s",
1118 $dom, $zone, $rdatastr, $rulename,
1119 !defined $subtest ? '' : ', subtest:'.$subtest);
1120
1121 my $match;
1122 if (!defined $subtest) {
1123 # this zone is a simple rule, not a set of subrules
1124 # skip any A record that isn't on 127/8
1125 if ($rr_type eq 'A' && $rdatastr !~ /^127\./) {
1126 warn("uridnsbl: bogus rr for domain=$dom, rule=$rulename, id=" .
1127 $pkt->header->id." rr=".$rr->string);
1128 next;
1129 }
1130 $match = 1;
1131 } elsif ($subtest eq $rdatastr) {
1132 $match = 1;
1133 } elsif ($subtest =~ m{^ (\d+) (?: ([/-]) (\d+) )? \z}x) {
1134 my($n1,$delim,$n2) = ($1,$2,$3);
1135 $match =
1136 !defined $n2 ? ($rdatanum & $n1) && # mask only
1137 (($rdatanum & 0xff000000) == 0x7f000000) # 127/8
1138 : $delim eq '-' ? $rdatanum >= $n1 && $rdatanum <= $n2 # range
1139 : $delim eq '/' ? ($rdatanum & $n2) == ($n1 & $n2) # value/mask
1140 : 0;
1141
1142 dbg("uridnsbl: %s . %s -> %s, %s, %08x %s %s",
1143 $dom, $zone, $rdatastr, $rulename, $rdatanum,
1144 !defined $n2 ? sprintf('& %08x', $n1)
1145 : $n1 == $n2 ? sprintf('== %08x', $n1)
1146 : sprintf('%08x%s%08x', $n1,$delim,$n2),
1147 $match ? 'match' : 'no');
1148 }
1149 $self->got_dnsbl_hit($pms, $ent, $rdatastr, $dom, $rulename) if $match;
1150 }
1151}
1152
1153sub got_dnsbl_hit {
1154 my ($self, $pms, $ent, $str, $dom, $rulename) = @_;
1155
1156 $str =~ s/\s+/ /gs; # long whitespace => short
1157 dbg("uridnsbl: domain \"$dom\" listed ($rulename): $str");
1158
1159 if (!defined $pms->{uridnsbl_hits}->{$rulename}) {
1160 $pms->{uridnsbl_hits}->{$rulename} = { };
1161 };
1162 $pms->{uridnsbl_hits}->{$rulename}->{$dom} = 1;
1163
1164 if ( $pms->{uridnsbl_active_rules_nsrevipbl}->{$rulename}
1165 || $pms->{uridnsbl_active_rules_arevipbl}->{$rulename}
1166 || $pms->{uridnsbl_active_rules_nsrhsbl}->{$rulename}
1167 || $pms->{uridnsbl_active_rules_fullnsrhsbl}->{$rulename}
1168 || $pms->{uridnsbl_active_rules_rhsbl}->{$rulename}
1169 || $pms->{uridnsbl_active_rules_rhsbl_ipsonly}->{$rulename}
1170 || $pms->{uridnsbl_active_rules_rhsbl_domsonly}->{$rulename})
1171 {
1172 # TODO: this needs to handle multiple domain hits per rule
1173 $pms->clear_test_state();
1174 my $uris = join (' ', keys %{$pms->{uridnsbl_hits}->{$rulename}});
1175 $pms->test_log ("URIs: $uris");
1176 $pms->got_hit ($rulename, "");
1177
1178 # note that this rule has completed (since it got at least 1 hit)
1179 $pms->register_async_rule_finish($rulename);
1180 }
1181}
1182
1183# ---------------------------------------------------------------------------
1184
1185# capability checks for "if can()":
1186#
1187110µs
# spent 15µs within Mail::SpamAssassin::Plugin::URIDNSBL::has_tflags_domains_only which was called: # once (15µs+0s) by Mail::SpamAssassin::Conf::Parser::cond_clause_can_or_has at line 595 of Mail/SpamAssassin/Conf/Parser.pm
sub has_tflags_domains_only { 1 }
1188sub has_subtest_for_ranges { 1 }
1189sub has_uridnsbl_for_a { 1 } # uridnsbl rules recognize tflags 'a' and 'ns'
1190
1191114µs1;
 
# spent 14.3ms within Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match which was called 3474 times, avg 4µs/call: # 2556 times (9.30ms+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::parsed_metadata at line 417, avg 4µs/call # 383 times (2.42ms+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains at line 838, avg 6µs/call # 383 times (2.05ms+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains at line 882, avg 5µs/call # 63 times (168µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:783] at line 777, avg 3µs/call # 44 times (167µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::parse_and_canonicalize_subtest at line 523, avg 4µs/call # 22 times (101µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::parse_and_canonicalize_subtest at line 519, avg 5µs/call # 21 times (134µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:662] at line 643, avg 6µs/call # once (9µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:609] at line 590 # once (6µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:581] at line 565
sub Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match; # opcode
# spent 118µs within Mail::SpamAssassin::Plugin::URIDNSBL::CORE:subst which was called 23 times, avg 5µs/call: # 21 times (106µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:662] at line 648, avg 5µs/call # once (6µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:581] at line 569 # once (6µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:609] at line 595
sub Mail::SpamAssassin::Plugin::URIDNSBL::CORE:subst; # opcode