Filename | /usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/DNSEval.pm |
Statements | Executed 38 statements in 4.84ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 187µs | 452µs | new | Mail::SpamAssassin::Plugin::DNSEval::
1 | 1 | 1 | 46µs | 46µs | BEGIN@27 | Mail::SpamAssassin::Plugin::DNSEval::
1 | 1 | 1 | 31µs | 46µs | BEGIN@32 | Mail::SpamAssassin::Plugin::DNSEval::
1 | 1 | 1 | 31µs | 105µs | BEGIN@35 | Mail::SpamAssassin::Plugin::DNSEval::
1 | 1 | 1 | 31µs | 627µs | BEGIN@29 | Mail::SpamAssassin::Plugin::DNSEval::
1 | 1 | 1 | 29µs | 117µs | BEGIN@37 | Mail::SpamAssassin::Plugin::DNSEval::
1 | 1 | 1 | 28µs | 175µs | BEGIN@30 | Mail::SpamAssassin::Plugin::DNSEval::
1 | 1 | 1 | 26µs | 32µs | BEGIN@34 | Mail::SpamAssassin::Plugin::DNSEval::
1 | 1 | 1 | 26µs | 232µs | BEGIN@28 | Mail::SpamAssassin::Plugin::DNSEval::
1 | 1 | 1 | 26µs | 67µs | BEGIN@33 | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | _check_rbl_addresses | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | check_dns_sender | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | check_rbl | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | check_rbl_accreditor | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | check_rbl_backend | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | check_rbl_envfrom | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | check_rbl_from_domain | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | check_rbl_from_host | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | check_rbl_results_for | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | check_rbl_sub | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | check_rbl_txt | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | check_start | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | ip_list_uniq_and_strip_private | Mail::SpamAssassin::Plugin::DNSEval::
0 | 0 | 0 | 0s | 0s | message_accreditor_tag | Mail::SpamAssassin::Plugin::DNSEval::
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 | |||||
20 | DNSEVAL - look up URLs against DNS blocklists | ||||
21 | |||||
22 | =cut | ||||
23 | |||||
24 | |||||
25 | package Mail::SpamAssassin::Plugin::DNSEval; | ||||
26 | |||||
27 | 2 | 79µs | 1 | 46µs | # spent 46µs within Mail::SpamAssassin::Plugin::DNSEval::BEGIN@27 which was called:
# once (46µs+0s) by Mail::SpamAssassin::PluginHandler::load_plugin at line 27 # spent 46µs making 1 call to Mail::SpamAssassin::Plugin::DNSEval::BEGIN@27 |
28 | 2 | 80µs | 2 | 438µs | # spent 232µs (26+206) within Mail::SpamAssassin::Plugin::DNSEval::BEGIN@28 which was called:
# once (26µs+206µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 28 # spent 232µs making 1 call to Mail::SpamAssassin::Plugin::DNSEval::BEGIN@28
# spent 206µs making 1 call to Exporter::import |
29 | 2 | 82µs | 2 | 1.22ms | # spent 627µs (31+596) within Mail::SpamAssassin::Plugin::DNSEval::BEGIN@29 which was called:
# once (31µs+596µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 29 # spent 627µs making 1 call to Mail::SpamAssassin::Plugin::DNSEval::BEGIN@29
# spent 596µs making 1 call to Exporter::import |
30 | 2 | 62µs | 2 | 323µs | # spent 175µs (28+148) within Mail::SpamAssassin::Plugin::DNSEval::BEGIN@30 which was called:
# once (28µs+148µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 30 # spent 175µs making 1 call to Mail::SpamAssassin::Plugin::DNSEval::BEGIN@30
# spent 148µs making 1 call to Exporter::import |
31 | |||||
32 | 2 | 92µs | 2 | 61µs | # spent 46µs (31+15) within Mail::SpamAssassin::Plugin::DNSEval::BEGIN@32 which was called:
# once (31µs+15µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 32 # spent 46µs making 1 call to Mail::SpamAssassin::Plugin::DNSEval::BEGIN@32
# spent 15µs making 1 call to strict::import |
33 | 2 | 90µs | 2 | 108µs | # spent 67µs (26+41) within Mail::SpamAssassin::Plugin::DNSEval::BEGIN@33 which was called:
# once (26µs+41µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 33 # spent 67µs making 1 call to Mail::SpamAssassin::Plugin::DNSEval::BEGIN@33
# spent 41µs making 1 call to warnings::import |
34 | 2 | 89µs | 2 | 38µs | # spent 32µs (26+6) within Mail::SpamAssassin::Plugin::DNSEval::BEGIN@34 which was called:
# once (26µs+6µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 34 # spent 32µs making 1 call to Mail::SpamAssassin::Plugin::DNSEval::BEGIN@34
# spent 6µs making 1 call to bytes::import |
35 | 2 | 90µs | 2 | 180µs | # spent 105µs (31+74) within Mail::SpamAssassin::Plugin::DNSEval::BEGIN@35 which was called:
# once (31µs+74µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 35 # spent 105µs making 1 call to Mail::SpamAssassin::Plugin::DNSEval::BEGIN@35
# spent 74µs making 1 call to re::import |
36 | |||||
37 | 2 | 3.98ms | 2 | 205µs | # spent 117µs (29+88) within Mail::SpamAssassin::Plugin::DNSEval::BEGIN@37 which was called:
# once (29µs+88µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 37 # spent 117µs making 1 call to Mail::SpamAssassin::Plugin::DNSEval::BEGIN@37
# spent 88µs making 1 call to vars::import |
38 | 1 | 14µs | @ISA = qw(Mail::SpamAssassin::Plugin); | ||
39 | |||||
40 | # constructor: register the eval rule | ||||
41 | # spent 452µs (187+265) within Mail::SpamAssassin::Plugin::DNSEval::new which was called:
# once (187µs+265µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 1 of (eval 93)[Mail/SpamAssassin/PluginHandler.pm:129] | ||||
42 | 1 | 2µs | my $class = shift; | ||
43 | 1 | 2µs | my $mailsaobject = shift; | ||
44 | |||||
45 | # some boilerplate... | ||||
46 | 1 | 2µs | $class = ref($class) || $class; | ||
47 | 1 | 18µs | 1 | 28µs | my $self = $class->SUPER::new($mailsaobject); # spent 28µs making 1 call to Mail::SpamAssassin::Plugin::new |
48 | 1 | 2µs | bless ($self, $class); | ||
49 | |||||
50 | # this is done this way so that the same list can be used here and in | ||||
51 | # check_start() | ||||
52 | 1 | 19µs | $self->{'evalrules'} = [ | ||
53 | 'check_rbl_accreditor', | ||||
54 | 'check_rbl', | ||||
55 | 'check_rbl_txt', | ||||
56 | 'check_rbl_sub', | ||||
57 | 'check_rbl_results_for', | ||||
58 | 'check_rbl_from_host', | ||||
59 | 'check_rbl_from_domain', | ||||
60 | 'check_rbl_envfrom', | ||||
61 | 'check_dns_sender', | ||||
62 | ]; | ||||
63 | |||||
64 | 2 | 19µs | foreach(@{$self->{'evalrules'}}) { | ||
65 | 9 | 76µs | 9 | 237µs | $self->register_eval_rule($_); # spent 237µs making 9 calls to Mail::SpamAssassin::Plugin::register_eval_rule, avg 26µs/call |
66 | } | ||||
67 | |||||
68 | 1 | 19µs | return $self; | ||
69 | } | ||||
70 | |||||
71 | # this is necessary because PMS::run_rbl_eval_tests() calls these functions | ||||
72 | # directly as part of PMS | ||||
73 | sub check_start { | ||||
74 | my ($self, $opts) = @_; | ||||
75 | |||||
76 | foreach(@{$self->{'evalrules'}}) { | ||||
77 | $opts->{'permsgstatus'}->register_plugin_eval_glue($_); | ||||
78 | } | ||||
79 | } | ||||
80 | |||||
81 | sub ip_list_uniq_and_strip_private { | ||||
82 | my ($self, @origips) = @_; | ||||
83 | my @ips; | ||||
84 | my %seen; | ||||
85 | my $IP_PRIVATE = IP_PRIVATE; | ||||
86 | foreach my $ip (@origips) { | ||||
87 | next unless $ip; | ||||
88 | next if (exists ($seen{$ip})); $seen{$ip} = 1; | ||||
89 | next if ($ip =~ /$IP_PRIVATE/o); | ||||
90 | push(@ips, $ip); | ||||
91 | } | ||||
92 | return @ips; | ||||
93 | } | ||||
94 | |||||
95 | # check an RBL if the message contains an "accreditor assertion," | ||||
96 | # that is, the message contains the name of a service that will vouch | ||||
97 | # for their practices. | ||||
98 | # | ||||
99 | sub check_rbl_accreditor { | ||||
100 | my ($self, $pms, $rule, $set, $rbl_server, $subtest, $accreditor) = @_; | ||||
101 | |||||
102 | if (!defined $pms->{accreditor_tag}) { | ||||
103 | $self->message_accreditor_tag($pms); | ||||
104 | } | ||||
105 | if ($pms->{accreditor_tag}->{$accreditor}) { | ||||
106 | $self->check_rbl_backend($pms, $rule, $set, $rbl_server, 'A', $subtest); | ||||
107 | } | ||||
108 | return 0; | ||||
109 | } | ||||
110 | |||||
111 | # Check for an Accreditor Assertion within the message, that is, the name of | ||||
112 | # a third-party who will vouch for the sender's practices. The accreditor | ||||
113 | # can be asserted in the EnvelopeFrom like this: | ||||
114 | # | ||||
115 | # listowner@a--accreditor.mail.example.com | ||||
116 | # | ||||
117 | # or in an 'Accreditor" Header field, like this: | ||||
118 | # | ||||
119 | # Accreditor: accreditor1, parm=value; accreditor2, parm-value | ||||
120 | # | ||||
121 | # This implementation supports multiple accreditors, but ignores any | ||||
122 | # parameters in the header field. | ||||
123 | # | ||||
124 | sub message_accreditor_tag { | ||||
125 | my ($self, $pms) = @_; | ||||
126 | my %acctags; | ||||
127 | |||||
128 | if ($pms->get('EnvelopeFrom:addr') =~ /[@.]a--([a-z0-9]{3,})\./i) { | ||||
129 | (my $tag = $1) =~ tr/A-Z/a-z/; | ||||
130 | $acctags{$tag} = -1; | ||||
131 | } | ||||
132 | my $accreditor_field = $pms->get('Accreditor',undef); | ||||
133 | if (defined $accreditor_field) { | ||||
134 | my @accreditors = split(/,/, $accreditor_field); | ||||
135 | foreach my $accreditor (@accreditors) { | ||||
136 | my @terms = split(' ', $accreditor); | ||||
137 | if ($#terms >= 0) { | ||||
138 | my $tag = $terms[0]; | ||||
139 | $tag =~ tr/A-Z/a-z/; | ||||
140 | $acctags{$tag} = -1; | ||||
141 | } | ||||
142 | } | ||||
143 | } | ||||
144 | $pms->{accreditor_tag} = \%acctags; | ||||
145 | } | ||||
146 | |||||
147 | sub check_rbl_backend { | ||||
148 | my ($self, $pms, $rule, $set, $rbl_server, $type, $subtest) = @_; | ||||
149 | local ($_); | ||||
150 | |||||
151 | # First check that DNS is available, if not do not perform this check | ||||
152 | return 0 if $self->{main}->{conf}->{skip_rbl_checks}; | ||||
153 | return 0 unless $pms->is_dns_available(); | ||||
154 | $pms->load_resolver(); | ||||
155 | |||||
156 | if (($rbl_server !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) && | ||||
157 | (index($rbl_server, '.') >= 0) && | ||||
158 | ($rbl_server !~ /\.$/)) { | ||||
159 | $rbl_server .= "."; | ||||
160 | } | ||||
161 | |||||
162 | dbg("dns: checking RBL $rbl_server, set $set"); | ||||
163 | |||||
164 | # ok, make a list of all the IPs in the untrusted set | ||||
165 | my @fullips = map { $_->{ip} } @{$pms->{relays_untrusted}}; | ||||
166 | |||||
167 | # now, make a list of all the IPs in the external set, for use in | ||||
168 | # notfirsthop testing. This will often be more IPs than found | ||||
169 | # in @fullips. It includes the IPs that are trusted, but | ||||
170 | # not in internal_networks. | ||||
171 | my @fullexternal = map { | ||||
172 | (!$_->{internal}) ? ($_->{ip}) : () | ||||
173 | } @{$pms->{relays_trusted}}; | ||||
174 | push (@fullexternal, @fullips); # add untrusted set too | ||||
175 | |||||
176 | # Make sure a header significantly improves results before adding here | ||||
177 | # X-Sender-Ip: could be worth using (very low occurance for me) | ||||
178 | # X-Sender: has a very low bang-for-buck for me | ||||
179 | my $IP_ADDRESS = IP_ADDRESS; | ||||
180 | my @originating; | ||||
181 | for my $header (@{$pms->{conf}->{originating_ip_headers}}) { | ||||
182 | my $str = $pms->get($header,undef); | ||||
183 | next unless defined $str && $str ne ''; | ||||
184 | push (@originating, ($str =~ m/($IP_ADDRESS)/g)); | ||||
185 | } | ||||
186 | |||||
187 | # Let's go ahead and trim away all private ips (KLC) | ||||
188 | # also uniq the list and strip dups. (jm) | ||||
189 | my @ips = $self->ip_list_uniq_and_strip_private(@fullips); | ||||
190 | |||||
191 | # if there's no untrusted IPs, it means we trust all the open-internet | ||||
192 | # relays, so we can return right now. | ||||
193 | return 0 unless (scalar @ips + scalar @originating > 0); | ||||
194 | |||||
195 | dbg("dns: IPs found: full-external: ".join(", ", @fullexternal). | ||||
196 | " untrusted: ".join(", ", @ips). | ||||
197 | " originating: ".join(", ", @originating)); | ||||
198 | |||||
199 | my $trusted = $self->{main}->{conf}->{trusted_networks}; | ||||
200 | |||||
201 | # If name is foo-notfirsthop, check all addresses except for | ||||
202 | # the originating one. Suitable for use with dialup lists, like the PDL. | ||||
203 | # note that if there's only 1 IP in the untrusted set, do NOT pop the | ||||
204 | # list, since it'd remove that one, and a legit user is supposed to | ||||
205 | # use their SMTP server (ie. have at least 1 more hop)! | ||||
206 | # If name is foo-lastexternal, check only the Received header just before | ||||
207 | # it enters our internal networks; we can trust it and it's the one that | ||||
208 | # passed mail between networks | ||||
209 | if ($set =~ /-(notfirsthop|lastexternal)$/) | ||||
210 | { | ||||
211 | # use the external IP set, instead of the trusted set; the user may have | ||||
212 | # specified some third-party relays as trusted. Also, don't use | ||||
213 | # @originating; those headers are added by a phase of relaying through | ||||
214 | # a server like Hotmail, which is not going to be in dialup lists anyway. | ||||
215 | @ips = $self->ip_list_uniq_and_strip_private(@fullexternal); | ||||
216 | if ($1 eq "lastexternal") { | ||||
217 | @ips = (defined $ips[0]) ? ($ips[0]) : (); | ||||
218 | } else { | ||||
219 | pop @ips if (scalar @ips > 1); | ||||
220 | } | ||||
221 | } | ||||
222 | # If name is foo-firsttrusted, check only the Received header just | ||||
223 | # after it enters our trusted networks; that's the only one we can | ||||
224 | # trust the IP address from (since our relay added that header). | ||||
225 | # And if name is foo-untrusted, check any untrusted IP address. | ||||
226 | elsif ($set =~ /-(first|un)trusted$/) | ||||
227 | { | ||||
228 | my @tips; | ||||
229 | foreach my $ip (@originating) { | ||||
230 | if ($ip && !$trusted->contains_ip($ip)) { | ||||
231 | push(@tips, $ip); | ||||
232 | } | ||||
233 | } | ||||
234 | @ips = $self->ip_list_uniq_and_strip_private (@ips, @tips); | ||||
235 | if ($1 eq "first") { | ||||
236 | @ips = (defined $ips[0]) ? ($ips[0]) : (); | ||||
237 | } else { | ||||
238 | shift @ips; | ||||
239 | } | ||||
240 | } | ||||
241 | else | ||||
242 | { | ||||
243 | my @tips; | ||||
244 | foreach my $ip (@originating) { | ||||
245 | if ($ip && !$trusted->contains_ip($ip)) { | ||||
246 | push(@tips, $ip); | ||||
247 | } | ||||
248 | } | ||||
249 | |||||
250 | # add originating IPs as untrusted IPs (if they are untrusted) | ||||
251 | @ips = reverse $self->ip_list_uniq_and_strip_private (@ips, @tips); | ||||
252 | } | ||||
253 | |||||
254 | # How many IPs max you check in the received lines | ||||
255 | my $checklast=$self->{main}->{conf}->{num_check_received}; | ||||
256 | |||||
257 | if (scalar @ips > $checklast) { | ||||
258 | splice (@ips, $checklast); # remove all others | ||||
259 | } | ||||
260 | |||||
261 | my $tflags = $pms->{conf}->{tflags}->{$rule}; | ||||
262 | |||||
263 | # Trusted relays should only be checked against nice rules (dnswls) | ||||
264 | if (defined $tflags && $tflags !~ /\bnice\b/) { | ||||
265 | # remove trusted hosts from beginning | ||||
266 | while (@ips && $trusted->contains_ip($ips[0])) { shift @ips } | ||||
267 | } | ||||
268 | |||||
269 | unless (scalar @ips > 0) { | ||||
270 | dbg("dns: no untrusted IPs to check"); | ||||
271 | return 0; | ||||
272 | } | ||||
273 | |||||
274 | dbg("dns: only inspecting the following IPs: ".join(", ", @ips)); | ||||
275 | |||||
276 | eval { | ||||
277 | foreach my $ip (@ips) { | ||||
278 | my $revip = reverse_ip_address($ip); | ||||
279 | $pms->do_rbl_lookup($rule, $set, $type, | ||||
280 | $revip.'.'.$rbl_server, $subtest) if defined $revip; | ||||
281 | } | ||||
282 | }; | ||||
283 | |||||
284 | # note that results are not handled here, hits are handled directly | ||||
285 | # as DNS responses are harvested | ||||
286 | return 0; | ||||
287 | } | ||||
288 | |||||
289 | sub check_rbl { | ||||
290 | my ($self, $pms, $rule, $set, $rbl_server, $subtest) = @_; | ||||
291 | $self->check_rbl_backend($pms, $rule, $set, $rbl_server, 'A', $subtest); | ||||
292 | } | ||||
293 | |||||
294 | sub check_rbl_txt { | ||||
295 | my ($self, $pms, $rule, $set, $rbl_server, $subtest) = @_; | ||||
296 | $self->check_rbl_backend($pms, $rule, $set, $rbl_server, 'TXT', $subtest); | ||||
297 | } | ||||
298 | |||||
299 | # run for first message | ||||
300 | sub check_rbl_sub { | ||||
301 | my ($self, $pms, $rule, $set, $subtest) = @_; | ||||
302 | |||||
303 | return 0 if $self->{main}->{conf}->{skip_rbl_checks}; | ||||
304 | return 0 unless $pms->is_dns_available(); | ||||
305 | |||||
306 | $pms->register_rbl_subtest($rule, $set, $subtest); | ||||
307 | } | ||||
308 | |||||
309 | # backward compatibility | ||||
310 | sub check_rbl_results_for { | ||||
311 | #warn "dns: check_rbl_results_for() is deprecated, use check_rbl_sub()\n"; | ||||
312 | check_rbl_sub(@_); | ||||
313 | } | ||||
314 | |||||
315 | # this only checks the address host name and not the domain name because | ||||
316 | # using the domain name had much worse results for dsn.rfc-ignorant.org | ||||
317 | sub check_rbl_from_host { | ||||
318 | my ($self, $pms, $rule, $set, $rbl_server, $subtest) = @_; | ||||
319 | _check_rbl_addresses($self, $pms, $rule, $set, $rbl_server, $subtest, $_[1]->all_from_addrs_domains()); | ||||
320 | } | ||||
321 | |||||
322 | =over 4 | ||||
323 | |||||
324 | =item check_rbl_from_domain | ||||
325 | |||||
326 | This checks all the from addrs domain names as an alternate to check_rbl_from_host. As of v3.4.1, it has been improved to include a subtest for a specific octet. | ||||
327 | |||||
328 | =back | ||||
329 | |||||
330 | =cut | ||||
331 | sub check_rbl_from_domain { | ||||
332 | my ($self, $pms, $rule, $set, $rbl_server, $subtest) = @_; | ||||
333 | _check_rbl_addresses($self, $pms, $rule, $set, $rbl_server, $subtest, $_[1]->all_from_addrs_domains()); | ||||
334 | } | ||||
335 | |||||
336 | # this only checks the address host name and not the domain name because | ||||
337 | # using the domain name had much worse results for dsn.rfc-ignorant.org | ||||
338 | sub check_rbl_envfrom { | ||||
339 | my ($self, $pms, $rule, $set, $rbl_server, $subtest) = @_; | ||||
340 | _check_rbl_addresses($self, $pms, $rule, $set, $rbl_server, $subtest, $_[1]->get('EnvelopeFrom:addr',undef)); | ||||
341 | } | ||||
342 | |||||
343 | sub _check_rbl_addresses { | ||||
344 | my ($self, $pms, $rule, $set, $rbl_server, $subtest, @addresses) = @_; | ||||
345 | |||||
346 | return 0 if $self->{main}->{conf}->{skip_rbl_checks}; | ||||
347 | return 0 unless $pms->is_dns_available(); | ||||
348 | |||||
349 | my %hosts; | ||||
350 | for (@addresses) { | ||||
351 | next if !defined($_) || !/ \@ ( [^\@\s]+ )/x; | ||||
352 | my $address = $1; | ||||
353 | # strip leading & trailing dots (as seen in some e-mail addresses) | ||||
354 | $address =~ s/^\.+//; $address =~ s/\.+\z//; | ||||
355 | # squash duplicate dots to avoid an invalid DNS query with a null label | ||||
356 | $address =~ tr/.//s; | ||||
357 | $hosts{lc($address)} = 1 if $address =~ /\./; # must by a FQDN | ||||
358 | } | ||||
359 | return unless scalar keys %hosts; | ||||
360 | |||||
361 | $pms->load_resolver(); | ||||
362 | |||||
363 | if (($rbl_server !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) && | ||||
364 | (index($rbl_server, '.') >= 0) && | ||||
365 | ($rbl_server !~ /\.$/)) { | ||||
366 | $rbl_server .= "."; | ||||
367 | } | ||||
368 | dbg("dns: _check_rbl_addresses RBL $rbl_server, set $set"); | ||||
369 | |||||
370 | for my $host (keys %hosts) { | ||||
371 | dbg("dns: checking [$host] / $rule / $set / $rbl_server"); | ||||
372 | $pms->do_rbl_lookup($rule, $set, 'A', "$host.$rbl_server", $subtest); | ||||
373 | } | ||||
374 | } | ||||
375 | |||||
376 | sub check_dns_sender { | ||||
377 | my ($self, $pms, $rule) = @_; | ||||
378 | |||||
379 | my $host; | ||||
380 | for my $from ($pms->get('EnvelopeFrom:addr',undef)) { | ||||
381 | next unless defined $from; | ||||
382 | |||||
383 | $from =~ tr/././s; # bug 3366 | ||||
384 | if ($from =~ m/ \@ ( [^\@\s]+ \. [^\@\s]+ )/x ) { | ||||
385 | $host = lc($1); | ||||
386 | last; | ||||
387 | } | ||||
388 | } | ||||
389 | return 0 unless defined $host; | ||||
390 | |||||
391 | # First check that DNS is available, if not do not perform this check | ||||
392 | # TODO: need a way to skip DNS checks as a whole in configuration | ||||
393 | return 0 unless $pms->is_dns_available(); | ||||
394 | $pms->load_resolver(); | ||||
395 | |||||
396 | if ($host eq 'compiling.spamassassin.taint.org') { | ||||
397 | # only used when compiling | ||||
398 | return 0; | ||||
399 | } | ||||
400 | |||||
401 | dbg("dns: checking A and MX for host $host"); | ||||
402 | |||||
403 | $pms->do_dns_lookup($rule, 'A', $host); | ||||
404 | $pms->do_dns_lookup($rule, 'MX', $host); | ||||
405 | |||||
406 | # cache name of host for later checking | ||||
407 | $pms->{sender_host} = $host; | ||||
408 | |||||
409 | return 0; | ||||
410 | } | ||||
411 | |||||
412 | 1 | 17µs | 1; |