source: trunk/dnsbl/DNSBL.pm@ 67

Last change on this file since 67 was 67, checked in by Kris Deugau, 6 years ago

/trunk/dnsbl

Review and update copyright dates on DNSBL.pm, DNSBLweb.pm, browse.cgi,

delist-ip, dnsbl.cgi, and export-dnsbl. Also add a version requirement
on DNSBL.pm in any callers.

Update browse.cgi with limited search and some operational-sanity boundaries

instead of blindly barfing out the entire dataset, requiring code changes
to view only a subset of data.

  • Property svn:keywords set to Date Rev Author Id
File size: 19.4 KB
Line 
1# DNSBL
2# Functions for interacting with the DNSBL database
3##
4# $Id: DNSBL.pm 67 2018-01-09 23:12:13Z kdeugau $
5# Copyright 2009-2012,2014,2018 Kris Deugau <kdeugau@deepnet.cx>
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19##
20
21package DNSBL;
22
23use strict;
24use warnings;
25use Exporter;
26use DBI;
27use NetAddr::IP;
28
29use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
30
31$VERSION = 2.2;
32@ISA = qw(Exporter);
33@EXPORT_OK = qw(
34 );
35
36@EXPORT = (); # Export nothing by default.
37%EXPORT_TAGS = ( ALL => [qw(
38 )]
39 );
40
41## "constants"
42
43# w00t! somewhere along the line, by accident or intent, SA's
44# check_dnsbl_sub can now check up to 24 bits of an DNSBL return value!
45# 1 not available so we don't $self->shoot(foot)
46our %bitfields = (
47 # ip
48 ip => 2,
49 # "I'm a total spamming moron!" - per-IP only!
50 slist => 128,
51
52 # Block listings. Ordering for levels 0, 1, 2 not ideal due to evolution of code.
53 # Levels 3 and higher are more coherently ordered
54
55 # Automatically listed blocks based on IP counts
56 0 => 16,
57 1 => 8,
58 2 => 4,
59 3 => 4096,
60 4 => 32768,
61 5 => 262144,
62 6 => 2097152,
63
64 # Out-of-band
65 org0 => 32,
66 block0 => 64,
67 org1 => 256,
68 org2 => 512,
69 block1 => 1024,
70 block2 => 2048,
71 org3 => 8192,
72 block3 => 16384,
73 org4 => 65536,
74 block4 => 131072,
75 org5 => 524288,
76 block5 => 1048576,
77 org6 => 4194304,
78 block6 => 8388608,
79
80);
81
82# probably needs some tuning; even 7 hits in a /24 is a pretty small percentage
83# number of IPs in a block of the given masklength needed to have that block automatically listed
84# defaults: (overridden by entries in db:autolist)
85our %autolist = (
86 31 => 1,
87 30 => 1,
88 29 => 2,
89 28 => 3,
90 27 => 4,
91 26 => 5,
92 25 => 6,
93 24 => 7,
94 23 => 8,
95 22 => 10,
96 21 => 13,
97 20 => 16,
98 19 => 19,
99 18 => 22,
100 17 => 26,
101 16 => 30,
102 15 => 34,
103 14 => 38,
104 13 => 42,
105 12 => 46,
106 11 => 50,
107 10 => 54,
108 9 => 58,
109 8 => 62,
110 7 => 2**31,
111 6 => 2**31,
112 5 => 2**31,
113 4 => 2**31,
114 3 => 2**31,
115 2 => 2**31,
116 1 => 2**31,
117 0 => 2**31
118);
119
120# le sigh. constants for masklength iterationing
121our @howmany = (1,128,64,32,16,8,4,2,1,128,64,32,16,8,4,2,1,128,64,32,16,8,4,2,1,128,64,32,16,8,4,2);
122
123# hard max depth. There are not enough bits in a 32-bit IP in 127/8 for more than 7 sets of 3 block-level
124# flags, plus one for the IP, plus one for an "alternate" IP flag, plus reserving the least significant bit
125# as a "don't use this because Reasons"
126our $maxlvl = 6;
127
128# variables
129our $dbh;
130
131our $err;
132our $errstr = '';
133
134# basic object subs
135sub new {
136# iff we want to start taking arguments, or doing other things on instantiation
137# my $self = {};
138# bless $self, "DNSBL";
139# return $self;
140 bless {};
141}
142
143sub DESTROY {
144 my $self = shift;
145 $self->dbclose() if $dbh;
146}
147
148# JIC someone wants to close the db but not finish the script
149sub dbclose {
150 $dbh->rollback;
151 $dbh->disconnect;
152}
153
154## specific object subs:
155
156sub connect {
157 my $self = shift;
158 my $dbhost = shift;
159 my $dbname = shift;
160 my $dbuser = shift;
161 my $dbpass = shift;
162 ## want to NOT autocommit everything, it's unlikely we'll step on our own toes but...
163 $dbh = DBI->connect("DBI:Pg:host=$dbhost;dbname=$dbname", $dbuser, $dbpass, {
164 AutoCommit => 0,
165 PrintError => 1
166 })
167 or die "database inaccessible: ".$DBI::errstr;
168 my $sth = $dbh->prepare("SELECT masklen,ipcount FROM autolist");
169 $sth->execute;
170 while (my ($masklen,$ipcount) = $sth->fetchrow_array) {
171 $autolist{$masklen} = $ipcount;
172 }
173 return $dbh;
174}
175
176
177## DNSBLDB::initexport()
178# Prepare a couple of statement handles for later processing in export(). Assists in ~3x speed increase.
179my $parsth;
180my $sthmoron;
181sub initexport {
182 $parsth = $dbh->prepare("SELECT count(i.ip),b.block,b.level,b.listme AS oobblock,o.listme AS ooborg ".
183 "FROM iplist i INNER JOIN blocks b ON i.parent = b.block INNER JOIN orgs o ON b.orgid = o.orgid ".
184 "WHERE b.block >>= ? ".
185 "GROUP BY b.block,b.level,b.listme,o.listme ORDER BY b.block");
186 $sthmoron = $dbh->prepare("SELECT ip,s4list,white FROM iplist WHERE parent = ?");
187}
188
189
190## DNSBL::ipexists()
191# return report count if the IP has been reported, otherwise return undef
192sub ipexists {
193 my $self = shift;
194 my $ip = shift;
195 my $sth = $dbh->prepare("SELECT count, exclude FROM iplist WHERE ip=?");
196 $sth->execute($ip);
197 my $ret = $sth->fetchrow_arrayref();
198 return $ret;
199} # end ipexists()
200
201
202# report an IP or URI to the db
203# increments a hit counter iff the reported IP or URI exists, otherwise it adds it
204sub report {
205 my $self = shift;
206 my $rep = shift;
207 my $exclude = shift;
208 my $sth;
209 my $rows = 0;
210 if ($rep =~ /^[\d.]+$/) {
211 # weesa gonna ASS-U-ME IP addresses are sanely formatted.
212 eval {
213 $sth = $dbh->prepare("SELECT count FROM iplist WHERE ip=?");
214 $sth->execute($rep) or die "eep? ".$dbh->errstr."\n";
215 $rows = $sth->rows;
216 if ($rows == 0) {
217 $sth = $dbh->prepare("INSERT INTO iplist (ip,parent,exclude) VALUES ".
218 "(?,(SELECT block FROM blocks WHERE block >> ? ORDER BY level DESC LIMIT 1),?)");
219 $sth->execute($rep,$rep,$exclude) or die "couldn't add entry for $rep: ".$dbh->errstr."\n";
220 } elsif ($rows == 1) {
221 $sth = $dbh->prepare("UPDATE iplist SET count=count+1,".
222 " exclude=".($exclude ? "'y'" : "'n'"). " WHERE ip=?");
223 $sth->execute($rep) or die "couldn't update listing for $rep: ".$dbh->errstr."\n";
224 } else {
225 die "db corrupt: found $rows matches on $rep\n";
226 }
227 $sth = $dbh->prepare("SELECT block FROM blocks WHERE block >> ?");
228 $sth->execute($rep);
229 my $updsth = $dbh->prepare("UPDATE blocks SET ipcount=(SELECT count(*) FROM iplist WHERE ip << ? AND exclude='n') WHERE block=?");
230 while (my ($block) = $sth->fetchrow_array) {
231 $updsth->execute($block,$block);
232 }
233 $dbh->commit;
234 };
235 if ($@) {
236 my $msg = $@;
237 return "failed adding $rep: $msg";
238 }
239 } else {
240 return;
241 }
242 return $rows;
243} # end report()
244
245
246# add a new org
247# return the orgid
248# if the org exists, return the orgid anyway
249sub addorg {
250 my $self = shift;
251 my $orgname = shift;
252 my $listme = shift || 'n';
253 my $ret = $self->orgexists($orgname);
254 return $ret if $ret;
255 my $sth = $dbh->prepare("INSERT INTO orgs (orgname,listme) VALUES (?,?)");
256 $sth->execute($orgname,$listme) or die "couldn't add org $orgname: ".$dbh->errstr."\n";
257 $dbh->commit;
258 $sth = $dbh->prepare("SELECT orgid FROM orgs WHERE orgname=?");
259 $sth->execute($orgname);
260 my ($orgid) = $sth->fetchrow_array();
261 return $orgid;
262} # end addorg
263
264
265# checks for existence - nb, exact match! No way to really handle anything else. :/
266sub orgexists {
267 my $self = shift;
268 my $org = shift;
269 my $sth = $dbh->prepare("SELECT orgid FROM orgs WHERE orgname=?");
270 $sth->execute($org);
271 my ($ret) = $sth->fetchrow_array();
272 return $ret;
273} # end orgexists();
274
275
276# take an arbitrary IP range and an IP, and return the CIDR block (if any) the IP is in.
277sub range2cidr {
278 my $self = shift;
279 my $rstart = shift;
280 my $rend = shift;
281 my $ip = shift;
282
283 $rstart = new NetAddr::IP $rstart;
284 $rend = new NetAddr::IP $rend;
285 # Basic algoithm: Set the mask on the IP, and see if both $rstart and $rend
286 # are within the range defined by that IP/mask. Continue making the mask
287 # larger until success.
288
289 my $mask;
290 for ($mask = 32; $mask > 0; $mask--) {
291 my $ip = NetAddr::IP->new("$ip/$mask");
292 if (NetAddr::IP->new($ip->network->addr) >= $rstart &&
293 NetAddr::IP->new($ip->broadcast->addr) <= $rend) {
294 next;
295 } else {
296 $mask++;
297 last;
298 }
299 }
300 my $realnet = NetAddr::IP->new("$ip/$mask")->network;
301
302 return "$realnet";
303} # end range2cidr()
304
305
306# add a block. requires the orgid
307##fixme needs error handling
308sub addblock {
309 my $self = shift;
310 my $blockin = shift;
311 my $orgid = shift;
312 my $level = shift;
313 my $exclude = shift;
314 my $comment = shift;
315 $blockin =~ s/^\s+//;
316 $blockin =~ s/\s+$//;
317 my $block = new NetAddr::IP "$blockin"; # need this to clean up messes like ranges. sigh.
318
319 return "$blockin not a single CIDR range" if !$block;
320
321# local $dbh->{AutoCommit} = 1; # force autocommit
322
323 my $sth;
324 eval {
325 my $parent = '0/0';
326 if ($level > 0) {
327 $sth = $dbh->prepare("SELECT block FROM blocks WHERE block >> ? ORDER BY level DESC LIMIT 1");
328 $sth->execute("$block");
329 ($parent) = $sth->fetchrow_array;
330 }
331 $sth = $dbh->prepare("INSERT INTO blocks (block,orgid,level,parent,exclude,comments,ipcount) VALUES (?,?,?,?,?,?,".
332 "(SELECT count(*) FROM iplist WHERE ip << ? AND exclude='n'))");
333 $sth->execute("$block",$orgid,$level,$parent,$exclude,$comment,"$block");
334 $sth = $dbh->prepare("UPDATE iplist SET parent=? WHERE parent=? AND ip << ?");
335 $sth->execute("$block",$parent,"$block");
336 $dbh->commit;
337 };
338 if ($@) {
339 my $msg = $@;
340 eval { dbh->rollback; };
341 return "failed to add $block: $msg";
342 }
343 # nb: no need to return anything, since the CIDR block is the key
344}
345
346
347# Update a netblock entry. Supports (un)setting the exclude flag and the comment.
348# Does NOT do any magic around leftover IPs within the block
349sub updateblock {
350 my $self = shift;
351 my $blockin = shift;
352 my $orgid = shift;
353 my $level = shift;
354 my $exclude = shift;
355 my $comment = shift;
356 $blockin =~ s/^\s+//;
357 $blockin =~ s/\s+$//;
358 my $block = new NetAddr::IP "$blockin"; # need this to clean up messes like ranges. sigh.
359
360 return "$blockin not a single CIDR range" if !$block;
361
362 local $dbh->{AutoCommit} = 0;
363 local $dbh->{RaiseError} = 1;
364
365 my $sth;
366 eval {
367 my $parent = '0/0';
368 if ($level > 0) {
369 $sth = $dbh->prepare("SELECT block FROM blocks WHERE block >> ? ORDER BY level DESC LIMIT 1");
370 $sth->execute("$block");
371 ($parent) = $sth->fetchrow_array;
372 }
373 $sth = $dbh->prepare("UPDATE blocks SET exclude = ?, comments = ?, ipcount = ".
374 "(SELECT count(*) FROM iplist WHERE ip << ? AND exclude='n')".
375 " WHERE block = ?");
376 $sth->execute($exclude, $comment, "$block", "$block");
377 $sth = $dbh->prepare("UPDATE iplist SET parent=? WHERE parent=? AND ip << ?");
378 $sth->execute("$block", $parent, "$block");
379 $dbh->commit;
380 };
381 if ($@) {
382 my $msg = $@;
383 eval { dbh->rollback; };
384 return "failed to update $block: $msg";
385 }
386 # nb: no need to return anything, since the CIDR block is the key
387}
388
389
390sub blockexists {
391 my $self = shift;
392 my $block = shift;
393 my $sth = $dbh->prepare("SELECT count(*) FROM blocks WHERE block=?");
394 $sth->execute($block);
395 my ($ret) = $sth->fetchrow_array();
396 return $ret;
397}
398
399
400# returns list (block,blockcomment,orgname) for the block that contains the passed IP.
401# accepts a level argument if you don't want the top-level registrar allocation block
402sub getcontainer {
403 my $self = shift;
404 my $ip = shift;
405 my $level = shift || 0;
406 my $sth = $dbh->prepare("SELECT b.block,b.comments,o.orgname FROM blocks b INNER JOIN orgs o ".
407 "ON b.orgid=o.orgid WHERE b.block >> ? AND b.level = ?");
408 $sth->execute($ip,$level);
409 return $sth->fetchrow_array();
410} # end getcontainer()
411
412
413# Get info about whether a block, IP or org is listed
414# Returns ?
415sub islisted {
416 my $self = shift;
417 my $entity = shift;
418
419 my $sth;
420
421 if ($entity =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
422 # looking for IP
423
424 $sth = $dbh->prepare("SELECT ip,s4list,exclude FROM iplist WHERE ip=?");
425 $sth->execute($entity);
426 my @ret = $sth->fetchrow_array;
427 return @ret if @ret;
428
429 } elsif ($entity =~ m|^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/(\d+)$|) {
430 # block
431
432 my $masklen = $1;
433
434 $sth = $dbh->prepare("SELECT block,listme,exclude,ipcount FROM blocks WHERE block = ?");
435 $sth->execute($entity);
436 my ($block, $listme, $exclude, $bcount) = $sth->fetchrow_array;
437
438 return if !$block;
439
440 my @ret = ( ($bcount >= $autolist{$masklen}), $listme, $exclude);
441 return @ret;
442
443 } else {
444 # org
445
446 $sth = $dbh->prepare("SELECT orgid,listme FROM orgs WHERE orgname=?");
447 $sth->execute($entity);
448 my ($orgid,$listme) = $sth->fetchrow_array;
449 return $listme if $orgid;
450
451 }
452
453 return undef;
454
455} # end islisted()
456
457
458# whee! Recursion is Fun!
459# Call ourself to dig down through the layers of blocks from registar-allocation
460# (level 0) to final block (level n, not to exceed $maxlvl)
461# Take a reference to a hash, and stuff it full of blacklisting goodness.
462# Optionally accept a level, block-container, and OOB block and org arguments for
463# the container to check and return
464# Returns no value directly
465# Calls itself to walk down the tree of containers
466sub export {
467 my $self = shift;
468 my $listhosts = shift;
469
470# Export data as CIDR netblocks or classful (A/B/C) blocks
471# Assume classful as it's more compatible with different DNS servers
472 my $mode = shift || 'class';
473
474# Assume we're checking the whole enchilada if we don't get told where to look.
475 my $level = shift || 0;
476 my $container = shift || '0.0.0.0/0';
477 my $bitmask = shift || 0;
478
479 if ($level == 0) {
480 $errstr = '';
481 }
482
483 return if ($errstr =~ /no connection to the server/);
484 if ($level > $maxlvl) {
485 warn "getting too deep, breaking off! ($container, $level)\n";
486 return;
487 }
488
489# fiddle $container into a sane state.
490 if ($container =~ m|^\d+\.\d+\.\d+/\d+$|) {
491 $container =~ s|/(\d+)$|.0/$1|;
492 } elsif ($container =~ m|^\d+\.\d+/\d+$|) {
493 $container =~ s|/(\d+)$|.0.0/$1|;
494 } elsif ($container =~ m|^\d+/(\d+)$|) {
495 $container =~ s|/(\d+)$|.0.0.0/$1|;
496 }
497
498
499 # catch database-went-away errors
500 local $dbh->{RaiseError} = 1;
501 eval {
502
503
504 my $sth = $dbh->prepare("SELECT count(*) FROM blocks WHERE parent = ?");
505 $sth->execute($container);
506 my ($nblocks) = $sth->fetchrow_array();
507
508 # need this for a bunch of things, may as well do it here
509 my ($masklen) = ($container =~ m|/(\d+)$|);
510
511# Update the bitmask variable with the current block info as needed.
512# Much faster than retrieving this data later (~3x faster!).
513 my $listme;
514 my $listorg;
515 my $bcount;
516 my $bexclude;
517 if ($container ne '0.0.0.0/0') {
518 $sth = $dbh->prepare("SELECT b.ipcount,b.listme,b.exclude,o.listme ".
519 "FROM blocks b INNER JOIN orgs o ON b.orgid=o.orgid ".
520 "WHERE b.block = ?");
521 $sth->execute($container);
522 ($bcount,$listme,$bexclude,$listorg) = $sth->fetchrow_array();
523 $bitmask |= $bitfields{$level-1} if $bcount >= $autolist{$masklen};
524 $bitmask |= $bitfields{"block".($level-1)} if $listme;
525 $bitmask |= $bitfields{"org".($level-1)} if $listorg;
526 }
527
528# hm. can't seem to move this prepare elsewhere. :(
529 if ($nblocks > 0) {
530 my $sthsubblocks = $dbh->prepare("SELECT block,exclude FROM blocks ".
531 "WHERE level = ? AND parent = ?");
532 $sthsubblocks->execute($level, $container);
533 while (my ($cidr, $exclude) = $sthsubblocks->fetchrow_array()) {
534 if ($exclude) {
535 $listhosts->{$cidr} = -1;
536 } else { # don't check subtrees of an excluded block; rbldnsd doesn't support deep flip-flopping like that
537 $self->export($listhosts,$mode,$level+1,$cidr,$bitmask)
538 or die $errstr;
539 }
540 }
541 } # avoid checking content of subs if we don't have any
542
543 # don't check all 4.2 billion IPs individually if we're looking at all teh Intarwebs
544 return if $container eq '0.0.0.0/0';
545
546##fixme: need a way to dig out orphan IPs at all levels - IPs not found in a
547# subblock of the current container when the current container *has* subblocks
548# NB: this may be better handled as an out-of-band data-integrity-checker
549
550 # decrement level here so the right bitfield setting gets picked. this segment
551 # is inherently off-by-one from the block-recursion loop, and I can't see a
552 # better way to work around that. >:(
553 $level--;
554
555 if ($mode eq 'cidr') {
556 $listhosts->{$container} |= $bitmask if $bitmask && ($listme || $listorg || ($bcount >= $autolist{$masklen}));
557 } else {
558 # if $cidr->masklen is <= 24, iterate on /24 boundaries for bulk sublisting
559 # if $cidr->masklen is <= 16, iterate on /16 boundaries for bulk sublisting
560 # if $cidr->masklen is <= 8, iterate on /8 boundaries for bulk sublisting
561
562 if ($bitmask) {
563 my @blocksubs;
564 if ($masklen <= 30 && $masklen > 24) {
565 my ($net,$octet) = ($container =~ m|^(\d+\.\d+\.\d+\.)(\d+)/|);
566 for (my $entry = $octet; $entry < ($octet + $howmany[$masklen]); $entry++) {
567 my $host = "$net$entry";
568 $listhosts->{$host} = 0 if !defined($listhosts->{$host});
569 $listhosts->{$host} |= $bitmask;
570 }
571 } elsif ($masklen <= 24 && $masklen > 16) {
572 my ($net,$octet) = ($container =~ m|^(\d+\.\d+\.)(\d+)\.\d+/|);
573 for (my $entry = $octet; $entry < ($octet + $howmany[$masklen]); $entry++) {
574 my $twofour = "$net$entry.*";
575 $listhosts->{$twofour} |= $bitmask;
576 }
577 } elsif ($masklen <= 16 && $masklen > 8) {
578 my ($net,$octet) = ($container =~ m|^(\d+\.)(\d+)\.\d+\.\d+/|);
579 for (my $entry = $octet; $entry < ($octet + $howmany[$masklen]); $entry++) {
580 my $sixteen = "$net$entry.*";
581 $listhosts->{$sixteen} |= $bitmask;
582 }
583 } elsif ($masklen <= 8) {
584 my ($octet) = ($container =~ m|^(\d+)\.\d+\.\d+\.\d+/|);
585 for (my $entry = $octet; $entry < ($octet + $howmany[$masklen]); $entry++) {
586 my $eight = "$entry.*";
587 $listhosts->{$eight} |= $bitmask;
588 }
589 }
590
591 } # generate autolist entries for ips/octets not (yet) seen in reports
592
593 } # cidr vs classful mode
594
595 $sthmoron->execute($container);
596 while (my ($ip,$moron,$exclude) = $sthmoron->fetchrow_array()) {
597 if ($moron) {
598 $listhosts->{$ip} = $bitfields{slist};
599 } elsif ($exclude) {
600 $listhosts->{$ip} = -1;
601 } else {
602 $listhosts->{$ip} |= $bitmask;
603 $listhosts->{$ip} |= $bitfields{ip};
604 }
605 }
606
607
608 }; # db-went-away-catching eval
609 if ($@) {
610 $errstr = $@;
611 warn "export truncated: $errstr\n";
612 return;
613 }
614
615
616# get IPs which for reasons unknown are apparently allocated directly from the
617# parent registry (and so do not have containing netblocks in this system) O_o
618# select * from iplist where not (select count(*) from blocks where ip << block) > 0;
619
620 return 1;
621} # end export()
622
623
624sub export_alt {
625 my $self = shift;
626 my $listhosts = shift;
627 my $level = shift || 0;
628 my $container = shift || '0.0.0.0/0';
629 my $oobblock = shift || 0;
630 my $ooborg = shift || 0;
631
632#print "\nDEBUG: called with $level, $container, $oobblock, $ooborg\n";
633# if $level > 2 or $container =~ /^64\.76\./;
634# my %listhosts;
635
636# $level = 0 if !$level;
637 if ($level > 3) {
638 warn "getting too deep, breaking off!\n";
639 return;
640 }
641
642 my $sth = $dbh->prepare("select ip,s4list from iplist order by ip");
643 my $bsth = $dbh->prepare("select b.block,b.listme,b.level,o.listme ".
644 "from blocks b inner join orgs o on b.orgid=o.orgid ".
645 "where b.block >> ?");
646 while (my ($ip,$s4list) = $sth->fetchrow_array) {
647 $bsth->execute($ip);
648 while (my ($block,$blisted,$blevel,$olisted) = $bsth->fetchrow_array) {
649 $listhosts->{$ip} |= 0;
650 }
651 }
652
653} # end export_alt()
654
655
656## DNSBL::autolist_block()
657# check if a block should be autolisted
658sub autolist_block {
659 my $self = shift;
660 my $block = shift;
661
662 my $cidr = new NetAddr::IP "$block";
663 my $sth = $dbh->prepare("SELECT ipcount FROM blocks WHERE block = ?");
664 $sth->execute("$cidr");
665 my ($count) = $sth->fetchrow_array;
666
667 return 1 if $count >= $autolist{$cidr->masklen};
668 return 0;
669} # end autolist_block()
670
671
672# make Perl happy
6731;
Note: See TracBrowser for help on using the repository browser.