← Index
NYTProf Performance Profile   « line view »
For /usr/local/bin/sa-learn
  Run on Sun Nov 5 02:36:06 2017
Reported on Sun Nov 5 02:56:20 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
23411609ms14.5sMail::SpamAssassin::Plugin::URIDNSBL::::parsed_metadataMail::SpamAssassin::Plugin::URIDNSBL::parsed_metadata
804311532ms5.45sMail::SpamAssassin::Plugin::URIDNSBL::::lookup_single_dnsblMail::SpamAssassin::Plugin::URIDNSBL::lookup_single_dnsbl
23411427ms8.05sMail::SpamAssassin::Plugin::URIDNSBL::::query_hosts_or_domainsMail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains
3831127.7ms1.04sMail::SpamAssassin::Plugin::URIDNSBL::::lookup_a_recordMail::SpamAssassin::Plugin::URIDNSBL::lookup_a_record
3171113.8ms870msMail::SpamAssassin::Plugin::URIDNSBL::::lookup_domain_nsMail::SpamAssassin::Plugin::URIDNSBL::lookup_domain_ns
34749112.8ms12.8msMail::SpamAssassin::Plugin::URIDNSBL::::CORE:matchMail::SpamAssassin::Plugin::URIDNSBL::CORE:match (opcode)
63113.49ms3.64msMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:783]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:783]
22211.58ms2.25msMail::SpamAssassin::Plugin::URIDNSBL::::parse_and_canonicalize_subtestMail::SpamAssassin::Plugin::URIDNSBL::parse_and_canonicalize_subtest
21111.56ms3.88msMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:662]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:662]
111153µs578µsMail::SpamAssassin::Plugin::URIDNSBL::::set_configMail::SpamAssassin::Plugin::URIDNSBL::set_config
2331123µs123µsMail::SpamAssassin::Plugin::URIDNSBL::::CORE:substMail::SpamAssassin::Plugin::URIDNSBL::CORE:subst (opcode)
111113µs314µsMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:609]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:609]
11174µs714µsMail::SpamAssassin::Plugin::URIDNSBL::::newMail::SpamAssassin::Plugin::URIDNSBL::new
11172µs87µsMail::SpamAssassin::Plugin::URIDNSBL::::__ANON__[:581]Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[:581]
11150µs50µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@295Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@295
11134µs640µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@296Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@296
11132µs222µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@307Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@307
11130µs102µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@297Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@297
11128µs38µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@299Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@299
11127µs250µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@298Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@298
11126µs32µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@301Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@301
11124µs94µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@304Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@304
11123µs57µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@300Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@300
11122µs83µsMail::SpamAssassin::Plugin::URIDNSBL::::BEGIN@302Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@302
1118µs8µ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
295274µs150µs
# spent 50µs within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@295 which was called: # once (50µs+0s) by Mail::SpamAssassin::PluginHandler::load_plugin at line 295
use Mail::SpamAssassin::Plugin;
296289µs21.25ms
# spent 640µs (34+606) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@296 which was called: # once (34µs+606µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 296
use Mail::SpamAssassin::Constants qw(:ip);
# spent 640µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@296 # spent 606µs making 1 call to Exporter::import
297261µs2173µs
# spent 102µs (30+71) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@297 which was called: # once (30µs+71µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 297
use Mail::SpamAssassin::Util;
# spent 102µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@297 # spent 71µs making 1 call to Exporter::import
298275µs2472µs
# spent 250µs (27+223) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@298 which was called: # once (27µs+223µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 298
use Mail::SpamAssassin::Logger;
# spent 250µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@298 # spent 223µs making 1 call to Exporter::import
299265µs249µs
# spent 38µs (28+10) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@299 which was called: # once (28µs+10µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 299
use strict;
# spent 38µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@299 # spent 10µs making 1 call to strict::import
300268µs290µs
# spent 57µs (23+33) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@300 which was called: # once (23µs+33µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 300
use warnings;
# spent 57µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@300 # spent 33µs making 1 call to warnings::import
301263µs238µs
# spent 32µs (26+6) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@301 which was called: # once (26µs+6µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 301
use bytes;
# spent 32µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@301 # spent 6µs making 1 call to bytes::import
302262µs2144µs
# spent 83µs (22+61) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@302 which was called: # once (22µs+61µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 302
use re 'taint';
# spent 83µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@302 # spent 61µs making 1 call to re::import
303
304296µs2165µs
# spent 94µs (24+70) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@304 which was called: # once (24µs+70µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 304
use vars qw(@ISA);
# spent 94µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@304 # spent 70µs making 1 call to vars::import
305120µs@ISA = qw(Mail::SpamAssassin::Plugin);
306
30729.51ms2412µs
# spent 222µs (32+190) within Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@307 which was called: # once (32µs+190µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 307
use constant LOG_COMPLETION_TIMES => 0;
# spent 222µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::BEGIN@307 # spent 190µs making 1 call to constant::import
308
309# constructor
310
# spent 714µs (74+640) within Mail::SpamAssassin::Plugin::URIDNSBL::new which was called: # once (74µs+640µ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;
31212µs my $samain = shift;
313
314 # some boilerplate...
31512µs $class = ref($class) || $class;
316113µs124µs my $self = $class->SUPER::new($samain);
# spent 24µs making 1 call to Mail::SpamAssassin::Plugin::new
31712µs bless ($self, $class);
318
319 # this can be effectively global, at least in each process, safely
320
321110µs $self->{finished} = { };
322
323111µs137µs $self->register_eval_rule ("check_uridnsbl");
# spent 37µs making 1 call to Mail::SpamAssassin::Plugin::register_eval_rule
324112µs1578µs $self->set_config($samain->{conf});
# spent 578µs making 1 call to Mail::SpamAssassin::Plugin::URIDNSBL::set_config
325
326116µ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 14.5s (609ms+13.9) within Mail::SpamAssassin::Plugin::URIDNSBL::parsed_metadata which was called 234 times, avg 61.9ms/call: # 234 times (609ms+13.9s) by Mail::SpamAssassin::PluginHandler::callback at line 204 of Mail/SpamAssassin/PluginHandler.pm, avg 61.9ms/call
sub parsed_metadata {
339234552µs my ($self, $opts) = @_;
340234708µs my $pms = $opts->{permsgstatus};
341234819µs my $conf = $pms->{conf};
342
343234860µs return 0 if $conf->{skip_uribl_checks};
344
3452342.71ms2349.42ms if (!$pms->is_dns_available()) {
# spent 9.42ms making 234 calls to Mail::SpamAssassin::PerMsgStatus::is_dns_available, avg 40µ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
351234673µs $self->{dns_not_available} = 0;
352 }
353
354234964µs $pms->{'uridnsbl_activerules'} = { };
355234840µs $pms->{'uridnsbl_hits'} = { };
356234836µs $pms->{'uridnsbl_seen_lookups'} = { };
357
358 # only hit DNSBLs for active rules (defined and score != 0)
359234718µs $pms->{'uridnsbl_active_rules_rhsbl'} = { };
360234920µs $pms->{'uridnsbl_active_rules_rhsbl_ipsonly'} = { };
361234850µs $pms->{'uridnsbl_active_rules_rhsbl_domsonly'} = { };
362234735µs $pms->{'uridnsbl_active_rules_nsrhsbl'} = { };
363234726µs $pms->{'uridnsbl_active_rules_fullnsrhsbl'} = { };
364234874µs $pms->{'uridnsbl_active_rules_nsrevipbl'} = { };
365234689µs $pms->{'uridnsbl_active_rules_arevipbl'} = { };
366
3674685.53ms foreach my $rulename (keys %{$conf->{uridnsbls}}) {
368538236.6ms5382329ms next unless ($conf->is_rule_active('body_evals',$rulename));
# spent 329ms making 5382 calls to Mail::SpamAssassin::Conf::is_rule_active, avg 61µs/call
369
370538247.9ms my $rulecf = $conf->{uridnsbls}->{$rulename};
371538232.9ms my $tflags = $conf->{tflags}->{$rulename};
37253828.86ms $tflags = '' if !defined $tflags;
37313806108ms my %tfl = map { ($_,1) } split(' ',$tflags);
374
375538236.9ms my $is_rhsbl = $rulecf->{is_rhsbl};
376538235.6ms if ( $is_rhsbl && $tfl{'ips_only'}) {
377 $pms->{uridnsbl_active_rules_rhsbl_ipsonly}->{$rulename} = 1;
378 } elsif ($is_rhsbl && $tfl{'domains_only'}) {
379234016.2ms $pms->{uridnsbl_active_rules_rhsbl_domsonly}->{$rulename} = 1;
380 } elsif ($is_rhsbl) {
381257417.4ms $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)
3874682.52ms if ($tfl{'a'}) { # tflag 'a' explicitly
388234728µs $pms->{uridnsbl_active_rules_arevipbl}->{$rulename} = 1;
389 }
3904681.67ms if ($tfl{'ns'} || !$tfl{'a'}) { # tflag 'ns' explicitly, or default
391234716µ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
399234727µs my $skip_domains = $conf->{uridnsbl_skip_domains};
400234471µs $skip_domains = {} if !$skip_domains;
401
402 # list of hashes to use in order
403234469µs my @uri_ordered;
404
405 # Generate the full list of html-parsed domains.
4062342.56ms2345.46s my $uris = $pms->get_uri_detail_list();
# spent 5.46s making 234 calls to Mail::SpamAssassin::PerMsgStatus::get_uri_detail_list, avg 23.3ms/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
415302431.0ms while (my($uri, $info) = each %{$uris}) {
416 # we want to skip mailto: uris
417255630.6ms25568.20ms next if ($uri =~ /^mailto:/i);
# spent 8.20ms making 2556 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 3µs/call
418
419 # no hosts/domains were found via this uri, so skip
42024468.81ms next unless ($info->{hosts});
421
42224294.19ms my $entry = 3;
423
424242925.2ms if ($info->{types}->{a}) {
42510111.66ms $entry = 5;
426
427 # determine a vs a_empty
428202210.9ms foreach my $at (@{$info->{anchor_text}}) {
42910122.49ms if (length $at) {
43010061.69ms $entry = 0;
43110061.76ms last;
432 }
433 }
434 }
435 elsif ($info->{types}->{form}) {
436 $entry = 1;
437 }
438 elsif ($info->{types}->{img}) {
4397121.21ms $entry = 2;
440 }
4416901.34ms elsif ($info->{types}->{parsed} && (keys %{$info->{types}} == 1)) {
4426901.17ms $entry = 4;
443 }
444
445 # take the usable domains and add them to the ordered list
446729854.8ms while (my($host,$domain) = each( %{$info->{hosts}} )) {
44724409.07ms if ($skip_domains->{$domain}) {
44842400µs42356µs dbg("uridnsbl: domain $domain in skip list, host $host");
# spent 356µ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.94ms $uri_ordered[$entry]->{$host} = $domain;
452 }
453 }
454 }
455
456 # at this point, @uri_ordered is an ordered array of hostname hashes
457
458234636µs my %hostlist; # keys are host names, values are their domain parts
459
460234707µs my $umd = $conf->{uridnsbl_max_domains};
4612342.90ms while (keys %hostlist < $umd && @uri_ordered) {
4627201.38ms my $array = shift @uri_ordered;
4637202.41ms next unless $array;
464
465 # run through and find the new domains in this grouping
4667003.65ms my @hosts = grep(!$hostlist{$_}, keys %{$array});
4673501.11ms next unless @hosts;
468
469 # the new hosts are all useful, just add them in
4702802.90ms if (keys(%hostlist) + @hosts <= $umd) {
4712801.12ms foreach my $host (@hosts) {
4723831.90ms $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.03ms my @hnames = keys %hostlist;
4872341.93ms15510.2ms $pms->set_tag('URIHOSTS',
# spent 10.2ms making 155 calls to Mail::SpamAssassin::PerMsgStatus::set_tag, avg 66µs/call
488 @hnames == 1 ? $hnames[0] : \@hnames) if @hnames;
489234918µs my @dnames = values %hostlist;
4902341.50ms1558.73ms $pms->set_tag('URIDOMAINS',
# spent 8.73ms 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.64ms2348.05s $self->query_hosts_or_domains($pms, \%hostlist);
# spent 8.05s making 234 calls to Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains, avg 34.4ms/call
495
4962342.89ms 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.25ms (1.58+678µs) within Mail::SpamAssassin::Plugin::URIDNSBL::parse_and_canonicalize_subtest which was called 22 times, avg 102µs/call: # 21 times (1.47ms+601µs) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:662] at line 649, avg 98µs/call # once (110µs+77µ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 {
5152255µs my($subtest) = @_;
5162235µs my $digested_subtest;
517
51822100µs local($1,$2,$3);
51922359µs22119µs if ($subtest =~ m{^ ([^/-]+) (?: ([/-]) (.+) )? \z}xs) {
# spent 119µs making 22 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 5µs/call
5202296µs my($n1,$delim,$n2) = ($1,$2,$3);
5212234µs my $any_quad_dot;
5222286µs for ($n1,$n2) {
52344655µs44154µs if (!defined $_) {
# spent 154µ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/) {
5301184µs11405µs $_ = Mail::SpamAssassin::Util::my_inet_aton($_); # quad-dot -> number
# spent 405µs making 11 calls to Mail::SpamAssassin::Util::my_inet_aton, avg 37µs/call
5311121µs $any_quad_dot = 1;
532 } else {
533 return;
534 }
535 }
53622106µs $digested_subtest = defined $n2 ? $n1.$delim.$n2
537 : $any_quad_dot ? $n1.'-'.$n1 : "$n1";
538 }
53922258µs return $digested_subtest;
540}
541
542
# spent 578µs (153+426) within Mail::SpamAssassin::Plugin::URIDNSBL::set_config which was called: # once (153µs+426µs) by Mail::SpamAssassin::Plugin::URIDNSBL::new at line 324
sub set_config {
54312µs my($self, $conf) = @_;
54415µs my @cmds;
545
54616µs push(@cmds, {
547 setting => 'skip_uribl_checks',
548 default => 0,
549 type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL,
550 });
551
55215µ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 87µs (72+15) within Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:581] which was called: # once (72µs+15µ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);
565136µs19µs if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)$/) {
56614µs my $rulename = $1;
56713µs my $zone = $2;
56813µs my $type = $3;
569119µ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 }
582112µs });
583
584 push (@cmds, {
585 setting => 'uridnssub',
586 is_priv => 1,
587
# spent 314µs (113+201) within Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:609] which was called: # once (113µs+201µs) by Mail::SpamAssassin::Conf::Parser::parse at line 438 of Mail/SpamAssassin/Conf/Parser.pm
code => sub {
58816µ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;
595120µs16µs $zone =~ s/\.\z//; # strip a redundant trailing dot
596110µs1187µs $subrule = parse_and_canonicalize_subtest($subrule);
59712µs defined $subrule or return $Mail::SpamAssassin::Conf::INVALID_VALUE;
598112µ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 }
610115µ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 }
635111µs });
636
637 push (@cmds, {
638 setting => 'urirhssub',
639 is_priv => 1,
640
# spent 3.88ms (1.56+2.32) within Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:662] which was called 21 times, avg 185µs/call: # 21 times (1.56ms+2.32ms) by Mail::SpamAssassin::Conf::Parser::parse at line 438 of Mail/SpamAssassin/Conf/Parser.pm, avg 185µs/call
code => sub {
6412198µs my ($self, $key, $value, $line) = @_;
6422193µs local($1,$2,$3,$4);
64321568µs21145µs if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(.*?)\s*$/) {
# spent 145µs making 21 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 7µs/call
6442164µs my $rulename = $1;
6452154µs my $zone = $2;
6462152µs my $type = $3;
6472151µs my $subrule = $4;
64821260µs21112µs $zone =~ s/\.\z//; # strip a redundant trailing dot
# spent 112µs making 21 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:subst, avg 5µs/call
64921143µs212.07ms $subrule = parse_and_canonicalize_subtest($subrule);
# spent 2.07ms making 21 calls to Mail::SpamAssassin::Plugin::URIDNSBL::parse_and_canonicalize_subtest, avg 98µs/call
6502136µs defined $subrule or return $Mail::SpamAssassin::Conf::INVALID_VALUE;
65121365µ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 }
663111µ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 }
688111µ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 }
716110µ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 }
74117µ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 }
76916µ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.64ms (3.49+154µ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 58µs/call: # 63 times (3.49ms+154µs) by Mail::SpamAssassin::Conf::Parser::parse at line 438 of Mail/SpamAssassin/Conf/Parser.pm, avg 58µs/call
code => sub {
77663274µs my ($self, $key, $value, $line) = @_;
77763509µs63154µs if ($value =~ /^$/) {
# spent 154µs making 63 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 2µs/call
778 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
779 }
78063838µs foreach my $domain (split(/\s+/, $value)) {
7812152.13ms $self->{uridnsbl_skip_domains}->{lc $domain} = 1;
782 }
783 }
784111µ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 }
80016µ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 }
811110µs });
812
813122µs1426µs $conf->{parser}->register_commands(\@cmds);
814}
815
816# ---------------------------------------------------------------------------
817
818
# spent 8.05s (427ms+7.62) within Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains which was called 234 times, avg 34.4ms/call: # 234 times (427ms+7.62s) by Mail::SpamAssassin::Plugin::URIDNSBL::parsed_metadata at line 494, avg 34.4ms/call
sub query_hosts_or_domains {
819234552µs my ($self, $pms, $hosthash_ref) = @_;
820234744µs my $conf = $pms->{conf};
821234647µs my $seen_lookups = $pms->{'uridnsbl_seen_lookups'};
822
823234686µs my $rhsblrules = $pms->{uridnsbl_active_rules_rhsbl};
824234539µs my $rhsbliprules = $pms->{uridnsbl_active_rules_rhsbl_ipsonly};
825234554µs my $rhsbldomrules = $pms->{uridnsbl_active_rules_rhsbl_domsonly};
826234554µs my $nsrhsblrules = $pms->{uridnsbl_active_rules_nsrhsbl};
827234610µs my $fullnsrhsblrules = $pms->{uridnsbl_active_rules_fullnsrhsbl};
828234536µs my $nsreviprules = $pms->{uridnsbl_active_rules_nsrevipbl};
829234627µs my $areviprules = $pms->{uridnsbl_active_rules_arevipbl};
830
8312347.24ms while (my($host,$domain) = each(%$hosthash_ref)) {
8323831.13ms $domain = lc $domain; # just in case
8333831.13ms $host = lc $host;
8343833.80ms3832.92ms dbg("uridnsbl: considering host=$host, domain=$domain");
# spent 2.92ms making 383 calls to Mail::SpamAssassin::Logger::dbg, avg 8µs/call
8353832.24ms my $obj = { dom => $domain };
836
837383734µs my ($is_ip, $single_dnsbl);
8383835.66ms3832.10ms if ($host =~ /^\d+\.\d+\.\d+\.\d+$/) {
# spent 2.10ms making 383 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 5µ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 {
855383859µs $single_dnsbl = 1;
856 }
857
8583831.93ms if ($single_dnsbl) {
859 # rule names which look up a domain in the basic RHSBL subset
8607665.68ms my @rhsblrules = keys %{$rhsblrules};
861
862 # and add the "domains_only" and "ips_only" subsets as appropriate
8633831.59ms if ($is_ip) {
864 push @rhsblrules, keys %{$rhsbliprules};
865 } else {
8667664.13ms push @rhsblrules, keys %{$rhsbldomrules};
867 }
868
8693832.17ms foreach my $rulename (@rhsblrules) {
870804333.7ms my $rulecf = $conf->{uridnsbls}->{$rulename};
871 $self->lookup_single_dnsbl($pms, $obj, $rulename,
872804383.9ms80435.45s $domain, $rulecf->{zone}, $rulecf->{type});
# spent 5.45s making 8043 calls to Mail::SpamAssassin::Plugin::URIDNSBL::lookup_single_dnsbl, avg 677µ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
877804373.5ms8043248ms $pms->register_async_rule_start($rulename);
# spent 248ms making 8043 calls to Mail::SpamAssassin::PerMsgStatus::register_async_rule_start, avg 31µ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
88238317.2ms3831.95ms if ($host !~ /^\d+\.\d+\.\d+\.\d+$/) {
# spent 1.95ms making 383 calls to Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match, avg 5µs/call
8833835.10ms if ( !$seen_lookups->{'NS:'.$domain} &&
884 (%$nsreviprules || %$nsrhsblrules || %$fullnsrhsblrules) ) {
8853171.34ms $seen_lookups->{'NS:'.$domain} = 1;
8863172.52ms317870ms $self->lookup_domain_ns($pms, $obj, $domain);
# spent 870ms making 317 calls to Mail::SpamAssassin::Plugin::URIDNSBL::lookup_domain_ns, avg 2.74ms/call
887 }
8883832.98ms if (%$areviprules && !$seen_lookups->{'A:'.$host}) {
8893832.40ms $seen_lookups->{'A:'.$host} = 1;
8903831.50ms my $obj = { dom => $host };
8913833.36ms3831.04s $self->lookup_a_record($pms, $obj, $host);
# spent 1.04s making 383 calls to Mail::SpamAssassin::Plugin::URIDNSBL::lookup_a_record, avg 2.71ms/call
8923836.49ms38311.5ms $pms->register_async_rule_start($_) for keys %$areviprules;
# spent 11.5ms making 383 calls to Mail::SpamAssassin::PerMsgStatus::register_async_rule_start, avg 30µs/call
893 }
894 }
895 }
896 }
897}
898
899# ---------------------------------------------------------------------------
900
901
# spent 870ms (13.8+856) within Mail::SpamAssassin::Plugin::URIDNSBL::lookup_domain_ns which was called 317 times, avg 2.74ms/call: # 317 times (13.8ms+856ms) by Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains at line 886, avg 2.74ms/call
sub lookup_domain_ns {
9023171.07ms my ($self, $pms, $obj, $dom, $rulename) = @_;
903
9043171.41ms my $key = "NS:" . $dom;
9053172.46ms 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.77ms317856ms master_deadline => $pms->{master_deadline} );
# spent 856ms making 317 calls to Mail::SpamAssassin::AsyncLoop::bgsend_and_start_lookup, avg 2.70ms/call
915
9163172.85ms 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.04s (27.7ms+1.01) within Mail::SpamAssassin::Plugin::URIDNSBL::lookup_a_record which was called 383 times, avg 2.71ms/call: # 383 times (27.7ms+1.01s) by Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains at line 891, avg 2.71ms/call
sub lookup_a_record {
9873831.08ms my ($self, $pms, $obj, $hname, $rulename) = @_;
988
9893831.58ms my $key = "A:" . $hname;
9903833.05ms 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.65ms3831.01s master_deadline => $pms->{master_deadline} );
# spent 1.01s making 383 calls to Mail::SpamAssassin::AsyncLoop::bgsend_and_start_lookup, avg 2.64ms/call
1000
10013833.73ms 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.45s (532ms+4.92) within Mail::SpamAssassin::Plugin::URIDNSBL::lookup_single_dnsbl which was called 8043 times, avg 677µs/call: # 8043 times (532ms+4.92s) by Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains at line 872, avg 677µs/call
sub lookup_single_dnsbl {
1060804369.6ms my ($self, $pms, $obj, $rulename, $lookupstr, $dnsbl, $qtype) = @_;
1061
1062804340.1ms my $key = "DNSBL:" . $lookupstr . ':' . $dnsbl;
1063804383.3ms 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) },
10718043170ms80434.92s master_deadline => $pms->{master_deadline} );
# spent 4.92s making 8043 calls to Mail::SpamAssassin::AsyncLoop::bgsend_and_start_lookup, avg 611µs/call
1072
10738043120ms 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#
118719µs
# spent 8µs within Mail::SpamAssassin::Plugin::URIDNSBL::has_tflags_domains_only which was called: # once (8µ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
1191110µs1;
 
# spent 12.8ms within Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match which was called 3474 times, avg 4µs/call: # 2556 times (8.20ms+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::parsed_metadata at line 417, avg 3µs/call # 383 times (2.10ms+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains at line 838, avg 5µs/call # 383 times (1.95ms+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::query_hosts_or_domains at line 882, avg 5µs/call # 63 times (154µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:783] at line 777, avg 2µs/call # 44 times (154µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::parse_and_canonicalize_subtest at line 523, avg 4µs/call # 22 times (119µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::parse_and_canonicalize_subtest at line 519, avg 5µs/call # 21 times (145µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:662] at line 643, avg 7µs/call # once (9µs+0s) by Mail::SpamAssassin::Plugin::URIDNSBL::__ANON__[/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/URIDNSBL.pm:581] at line 565 # 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
sub Mail::SpamAssassin::Plugin::URIDNSBL::CORE:match; # opcode
# spent 123µs within Mail::SpamAssassin::Plugin::URIDNSBL::CORE:subst which was called 23 times, avg 5µs/call: # 21 times (112µ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:609] at line 595 # 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
sub Mail::SpamAssassin::Plugin::URIDNSBL::CORE:subst; # opcode