source: trunk/dnsbl/DNSBL.pm

Last change on this file was 104, checked in by Kris Deugau, 5 weeks ago

/trunk/dnsbl

Apply largely the same semantic change from r101 to the dnsbl/ subtool, with
some differences due to the actual export not being inside the module.

  • Property svn:keywords set to Date Rev Author Id
File size: 20.4 KB
RevLine 
[2]1# DNSBL
2# Functions for interacting with the DNSBL database
[40]3##
4# $Id: DNSBL.pm 104 2025-09-24 17:20:10Z kdeugau $
[73]5# Copyright 2009-2012,2014,2018,2025 Kris Deugau <kdeugau@deepnet.cx>
[40]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##
[2]20
21package DNSBL;
22
23use strict;
24use warnings;
25use Exporter;
[73]26
[2]27use DBI;
28use NetAddr::IP;
29
30use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
31
[73]32$VERSION = 3.0;
[2]33@ISA = qw(Exporter);
[73]34@EXPORT_OK = qw( $dbh );
[2]35
[73]36@EXPORT = qw( $dbh );
[2]37%EXPORT_TAGS = ( ALL => [qw(
38 )]
39 );
40
41## "constants"
42
[36]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!
[2]45# 1 not available so we don't $self->shoot(foot)
46our %bitfields = (
[50]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
[2]56 0 => 16,
57 1 => 8,
58 2 => 4,
[50]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
[2]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
[24]84# defaults: (overridden by entries in db:autolist)
[2]85our %autolist = (
[10]86 31 => 1,
[2]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,
[15]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,
[2]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
[48]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
[2]128# variables
129our $dbh;
130
131our $err;
[66]132our $errstr = '';
[2]133
134# basic object subs
135sub new {
[73]136 my $this = shift;
137 my $class = ref($this) || $this;
138 my %args = @_;
[2]139
[73]140 # Prepopulate a basic config. Note some of these *will* cause errors if left unset.
141 my %defconfig = (
142 dbhost => "localhost",
143 dbname => "dnsbl",
144 dbuser => "dnsbl",
145 dbpass => "spambgone",
[104]146 misc => {
147 blzone => "spamhosts.example.com",
148 altblzone => "company.dnsbl",
149 bladmin => "systems.example.com",
150 ttl => 600,
151 soa => "600 600 600 600",
152 iplisted => '$ relayed a reported spam',
153 blocklisted => 'Netblock listed on one or more criteria',
154 },
[73]155 );
156
157 my %siteconfig;
158 my $dbhost;
159 my $dbname;
160 my $dbuser;
161 my $dbpass;
162 if (defined($args{configfile})) {
163 if (-e $args{configfile} && -f $args{configfile}) {
164 my $ret = eval `cat $args{configfile}`;
165 unless ($ret) {
166 if ($@) { $errstr = "couldn't parse $args{configfile}: $@\n"; return; }
167 if (!defined($ret)) { $errstr = "couldn't load $args{configfile}: $!\n"; return; }
168 if (!$ret) { $errstr = "couldn't load $args{configfile}\n"; return; }
169 }
170 # crossload legacy variables, but prefer new %siteconfig values
171 $siteconfig{dbhost} = $dbhost if !$siteconfig{dbhost} && $dbhost;
172 $siteconfig{dbname} = $dbname if !$siteconfig{dbname} && $dbname;
173 $siteconfig{dbuser} = $dbuser if !$siteconfig{dbuser} && $dbuser;
174 $siteconfig{dbpass} = $dbpass if !$siteconfig{dbpass} && $dbpass;
175 }
176 }
177
178 # Assemble the object. Apply configuration hashes in order of precedence.
179 my $self = {
180 # Hardcoded defaults
181 %defconfig,
182 # Default config file OR caller-specified one, loaded above
183 %siteconfig,
184 # Caller-specified arguments
185 %args
186 };
187 bless $self, $class;
188
189 return $self;
190} # new()
191
[2]192sub DESTROY {
193 my $self = shift;
[25]194 $self->dbclose() if $dbh;
[2]195}
196
197# JIC someone wants to close the db but not finish the script
198sub dbclose {
199 $dbh->disconnect;
200}
201
202## specific object subs:
203
204sub connect {
[25]205 my $self = shift;
[103]206 # after jumping a HUUUGE number of PG versions, AutoCommit => 0 produced some bizarre bugs
[73]207 $dbh = DBI->connect("DBI:Pg:host=$self->{dbhost};dbname=$self->{dbname}", $self->{dbuser}, $self->{dbpass}, {
[103]208 AutoCommit => 1,
209 PrintError => 0
[2]210 })
211 or die "database inaccessible: ".$DBI::errstr;
[24]212 my $sth = $dbh->prepare("SELECT masklen,ipcount FROM autolist");
213 $sth->execute;
214 while (my ($masklen,$ipcount) = $sth->fetchrow_array) {
215 $autolist{$masklen} = $ipcount;
216 }
[95]217 $sth = $dbh->prepare("SELECT key,value FROM misc");
[92]218 $sth->execute;
219 while (my ($key,$value) = $sth->fetchrow_array) {
220 $self->{misc}{$key} = $value;
221 }
[2]222 return $dbh;
223}
224
[5]225
[23]226## DNSBLDB::initexport()
227# Prepare a couple of statement handles for later processing in export(). Assists in ~3x speed increase.
228my $parsth;
229my $sthmoron;
230sub initexport {
231 $parsth = $dbh->prepare("SELECT count(i.ip),b.block,b.level,b.listme AS oobblock,o.listme AS ooborg ".
[32]232 "FROM iplist i INNER JOIN blocks b ON i.parent = b.block INNER JOIN orgs o ON b.orgid = o.orgid ".
[23]233 "WHERE b.block >>= ? ".
234 "GROUP BY b.block,b.level,b.listme,o.listme ORDER BY b.block");
[69]235 $sthmoron = $dbh->prepare("SELECT ip,s4list,exclude FROM iplist WHERE parent = ?");
[23]236}
237
238
[5]239## DNSBL::ipexists()
240# return report count if the IP has been reported, otherwise return undef
[2]241sub ipexists {
242 my $self = shift;
243 my $ip = shift;
[66]244 my $sth = $dbh->prepare("SELECT count, exclude FROM iplist WHERE ip=?");
[2]245 $sth->execute($ip);
[66]246 my $ret = $sth->fetchrow_arrayref();
[2]247 return $ret;
[5]248} # end ipexists()
[2]249
250
251# report an IP or URI to the db
252# increments a hit counter iff the reported IP or URI exists, otherwise it adds it
253sub report {
254 my $self = shift;
255 my $rep = shift;
[70]256 my $exclude = shift || 'n';
[77]257 $exclude = 'y' if $exclude eq 'on';
[2]258 my $sth;
259 my $rows = 0;
[103]260
261 local $dbh->{AutoCommit} = 0;
262 local $dbh->{RaiseError} = 1;
263
[2]264 if ($rep =~ /^[\d.]+$/) {
265 # weesa gonna ASS-U-ME IP addresses are sanely formatted.
[32]266 eval {
267 $sth = $dbh->prepare("SELECT count FROM iplist WHERE ip=?");
268 $sth->execute($rep) or die "eep? ".$dbh->errstr."\n";
269 $rows = $sth->rows;
270 if ($rows == 0) {
[66]271 $sth = $dbh->prepare("INSERT INTO iplist (ip,parent,exclude) VALUES ".
272 "(?,(SELECT block FROM blocks WHERE block >> ? ORDER BY level DESC LIMIT 1),?)");
[77]273 $sth->execute($rep, $rep, $exclude) or die "couldn't add entry for $rep: ".$dbh->errstr."\n";
[32]274 } elsif ($rows == 1) {
[66]275 $sth = $dbh->prepare("UPDATE iplist SET count=count+1,".
[77]276 " exclude = ? WHERE ip = ?");
[71]277 $sth->execute($exclude, $rep) or die "couldn't update listing for $rep: ".$dbh->errstr."\n";
[32]278 } else {
279 die "db corrupt: found $rows matches on $rep\n";
280 }
281 $sth = $dbh->prepare("SELECT block FROM blocks WHERE block >> ?");
282 $sth->execute($rep);
[70]283 my $updsth = $dbh->prepare("UPDATE blocks SET ipcount=(".
284 "SELECT count(*) FROM iplist i JOIN blocks b ON b.block=i.parent WHERE i.ip << ? AND i.exclude='n' AND b.exclude='n'".
[77]285 ") WHERE block = ?");
[32]286 while (my ($block) = $sth->fetchrow_array) {
287 $updsth->execute($block,$block);
288 }
289 $dbh->commit;
290 };
291 if ($@) {
292 my $msg = $@;
293 return "failed adding $rep: $msg";
[2]294 }
295 } else {
296 return;
297 }
298 return $rows;
299} # end report()
300
301
302# add a new org
303# return the orgid
304# if the org exists, return the orgid anyway
305sub addorg {
306 my $self = shift;
307 my $orgname = shift;
308 my $listme = shift || 'n';
309 my $ret = $self->orgexists($orgname);
310 return $ret if $ret;
311 my $sth = $dbh->prepare("INSERT INTO orgs (orgname,listme) VALUES (?,?)");
312 $sth->execute($orgname,$listme) or die "couldn't add org $orgname: ".$dbh->errstr."\n";
313 $dbh->commit;
314 $sth = $dbh->prepare("SELECT orgid FROM orgs WHERE orgname=?");
315 $sth->execute($orgname);
316 my ($orgid) = $sth->fetchrow_array();
317 return $orgid;
318} # end addorg
319
320
321# checks for existence - nb, exact match! No way to really handle anything else. :/
322sub orgexists {
323 my $self = shift;
324 my $org = shift;
325 my $sth = $dbh->prepare("SELECT orgid FROM orgs WHERE orgname=?");
326 $sth->execute($org);
327 my ($ret) = $sth->fetchrow_array();
328 return $ret;
329} # end orgexists();
330
331
[54]332# take an arbitrary IP range and an IP, and return the CIDR block (if any) the IP is in.
333sub range2cidr {
334 my $self = shift;
335 my $rstart = shift;
336 my $rend = shift;
337 my $ip = shift;
338
339 $rstart = new NetAddr::IP $rstart;
340 $rend = new NetAddr::IP $rend;
341 # Basic algoithm: Set the mask on the IP, and see if both $rstart and $rend
342 # are within the range defined by that IP/mask. Continue making the mask
343 # larger until success.
344
345 my $mask;
346 for ($mask = 32; $mask > 0; $mask--) {
347 my $ip = NetAddr::IP->new("$ip/$mask");
348 if (NetAddr::IP->new($ip->network->addr) >= $rstart &&
349 NetAddr::IP->new($ip->broadcast->addr) <= $rend) {
350 next;
351 } else {
352 $mask++;
353 last;
354 }
355 }
356 my $realnet = NetAddr::IP->new("$ip/$mask")->network;
357
358 return "$realnet";
359} # end range2cidr()
360
361
[2]362# add a block. requires the orgid
363##fixme needs error handling
364sub addblock {
365 my $self = shift;
366 my $blockin = shift;
367 my $orgid = shift;
368 my $level = shift;
[72]369 my $exclude = shift || 'n';
[66]370 my $comment = shift;
[2]371 $blockin =~ s/^\s+//;
372 $blockin =~ s/\s+$//;
373 my $block = new NetAddr::IP "$blockin"; # need this to clean up messes like ranges. sigh.
374
[11]375 return "$blockin not a single CIDR range" if !$block;
376
[103]377 local $dbh->{AutoCommit} = 0;
378 local $dbh->{RaiseError} = 1;
[2]379
[32]380 my $sth;
381 eval {
382 my $parent = '0/0';
383 if ($level > 0) {
384 $sth = $dbh->prepare("SELECT block FROM blocks WHERE block >> ? ORDER BY level DESC LIMIT 1");
385 $sth->execute("$block");
386 ($parent) = $sth->fetchrow_array;
387 }
[103]388 $dbh->do("INSERT INTO blocks (block,orgid,level,parent,exclude,comments,ipcount) VALUES (?,?,?,?,?,?,".
389 "(SELECT count(*) FROM iplist i JOIN blocks b ON b.block=i.parent WHERE i.ip << ? AND i.exclude='n' AND b.exclude='n'))",
390 undef, "$block",$orgid,$level,$parent,$exclude,$comment,"$block");
391 $dbh->do("UPDATE iplist SET parent=? WHERE parent=? AND ip << ?", undef, "$block", $parent, "$block");
[32]392 $dbh->commit;
393 };
394 if ($@) {
395 my $msg = $@;
396 eval { dbh->rollback; };
397 return "failed to add $block: $msg";
398 }
[2]399 # nb: no need to return anything, since the CIDR block is the key
400}
401
402
[66]403# Update a netblock entry. Supports (un)setting the exclude flag and the comment.
404# Does NOT do any magic around leftover IPs within the block
405sub updateblock {
406 my $self = shift;
407 my $blockin = shift;
408 my $orgid = shift;
409 my $level = shift;
[72]410 my $exclude = shift || 'n';
[66]411 my $comment = shift;
412 $blockin =~ s/^\s+//;
413 $blockin =~ s/\s+$//;
414 my $block = new NetAddr::IP "$blockin"; # need this to clean up messes like ranges. sigh.
415
416 return "$blockin not a single CIDR range" if !$block;
417
418 local $dbh->{AutoCommit} = 0;
419 local $dbh->{RaiseError} = 1;
420
421 my $sth;
422 eval {
423 my $parent = '0/0';
424 if ($level > 0) {
425 $sth = $dbh->prepare("SELECT block FROM blocks WHERE block >> ? ORDER BY level DESC LIMIT 1");
426 $sth->execute("$block");
427 ($parent) = $sth->fetchrow_array;
428 }
429 $sth = $dbh->prepare("UPDATE blocks SET exclude = ?, comments = ?, ipcount = ".
[77]430 "(SELECT count(*) FROM iplist i JOIN blocks b ON b.block=i.parent WHERE i.ip << ? AND i.exclude='n' AND b.exclude='n')".
[66]431 " WHERE block = ?");
432 $sth->execute($exclude, $comment, "$block", "$block");
433 $sth = $dbh->prepare("UPDATE iplist SET parent=? WHERE parent=? AND ip << ?");
434 $sth->execute("$block", $parent, "$block");
435 $dbh->commit;
436 };
437 if ($@) {
438 my $msg = $@;
439 eval { dbh->rollback; };
440 return "failed to update $block: $msg";
441 }
442 # nb: no need to return anything, since the CIDR block is the key
[72]443} # updateblock()
[66]444
445
[2]446sub blockexists {
447 my $self = shift;
448 my $block = shift;
449 my $sth = $dbh->prepare("SELECT count(*) FROM blocks WHERE block=?");
450 $sth->execute($block);
451 my ($ret) = $sth->fetchrow_array();
452 return $ret;
453}
454
455
[66]456# returns list (block,blockcomment,orgname) for the block that contains the passed IP.
[2]457# accepts a level argument if you don't want the top-level registrar allocation block
458sub getcontainer {
459 my $self = shift;
460 my $ip = shift;
461 my $level = shift || 0;
[66]462 my $sth = $dbh->prepare("SELECT b.block,b.comments,o.orgname FROM blocks b INNER JOIN orgs o ".
[2]463 "ON b.orgid=o.orgid WHERE b.block >> ? AND b.level = ?");
464 $sth->execute($ip,$level);
465 return $sth->fetchrow_array();
466} # end getcontainer()
467
468
[29]469# Get info about whether a block, IP or org is listed
470# Returns ?
471sub islisted {
472 my $self = shift;
473 my $entity = shift;
474
475 my $sth;
476
477 if ($entity =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
478 # looking for IP
479
[66]480 $sth = $dbh->prepare("SELECT ip,s4list,exclude FROM iplist WHERE ip=?");
[29]481 $sth->execute($entity);
482 my @ret = $sth->fetchrow_array;
483 return @ret if @ret;
484
485 } elsif ($entity =~ m|^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/(\d+)$|) {
486 # block
487
488 my $masklen = $1;
489
[66]490 $sth = $dbh->prepare("SELECT block,listme,exclude,ipcount FROM blocks WHERE block = ?");
[29]491 $sth->execute($entity);
[66]492 my ($block, $listme, $exclude, $bcount) = $sth->fetchrow_array;
[29]493
494 return if !$block;
495
[66]496 my @ret = ( ($bcount >= $autolist{$masklen}), $listme, $exclude);
[29]497 return @ret;
498
499 } else {
500 # org
501
502 $sth = $dbh->prepare("SELECT orgid,listme FROM orgs WHERE orgname=?");
503 $sth->execute($entity);
504 my ($orgid,$listme) = $sth->fetchrow_array;
505 return $listme if $orgid;
506
507 }
508
509 return undef;
510
511} # end islisted()
512
513
[2]514# whee! Recursion is Fun!
515# Call ourself to dig down through the layers of blocks from registar-allocation
[48]516# (level 0) to final block (level n, not to exceed $maxlvl)
[2]517# Take a reference to a hash, and stuff it full of blacklisting goodness.
518# Optionally accept a level, block-container, and OOB block and org arguments for
519# the container to check and return
520# Returns no value directly
521# Calls itself to walk down the tree of containers
522sub export {
523 my $self = shift;
524 my $listhosts = shift;
525
526# Export data as CIDR netblocks or classful (A/B/C) blocks
527# Assume classful as it's more compatible with different DNS servers
528 my $mode = shift || 'class';
529
530# Assume we're checking the whole enchilada if we don't get told where to look.
531 my $level = shift || 0;
532 my $container = shift || '0.0.0.0/0';
[23]533 my $bitmask = shift || 0;
[2]534
[66]535 if ($level == 0) {
536 $errstr = '';
537 }
538
539 return if ($errstr =~ /no connection to the server/);
[48]540 if ($level > $maxlvl) {
[2]541 warn "getting too deep, breaking off! ($container, $level)\n";
542 return;
543 }
544
545# fiddle $container into a sane state.
546 if ($container =~ m|^\d+\.\d+\.\d+/\d+$|) {
547 $container =~ s|/(\d+)$|.0/$1|;
548 } elsif ($container =~ m|^\d+\.\d+/\d+$|) {
549 $container =~ s|/(\d+)$|.0.0/$1|;
550 } elsif ($container =~ m|^\d+/(\d+)$|) {
551 $container =~ s|/(\d+)$|.0.0.0/$1|;
552 }
553
[66]554
555 # catch database-went-away errors
556 local $dbh->{RaiseError} = 1;
557 eval {
558
559
[32]560 my $sth = $dbh->prepare("SELECT count(*) FROM blocks WHERE parent = ?");
[2]561 $sth->execute($container);
562 my ($nblocks) = $sth->fetchrow_array();
563
[23]564 # need this for a bunch of things, may as well do it here
565 my ($masklen) = ($container =~ m|/(\d+)$|);
566
567# Update the bitmask variable with the current block info as needed.
568# Much faster than retrieving this data later (~3x faster!).
569 my $listme;
570 my $listorg;
571 my $bcount;
[66]572 my $bexclude;
[23]573 if ($container ne '0.0.0.0/0') {
[66]574 $sth = $dbh->prepare("SELECT b.ipcount,b.listme,b.exclude,o.listme ".
[23]575 "FROM blocks b INNER JOIN orgs o ON b.orgid=o.orgid ".
576 "WHERE b.block = ?");
577 $sth->execute($container);
[66]578 ($bcount,$listme,$bexclude,$listorg) = $sth->fetchrow_array();
[77]579 $bcount = 0 if !$bcount;
[23]580 $bitmask |= $bitfields{$level-1} if $bcount >= $autolist{$masklen};
[36]581 $bitmask |= $bitfields{"block".($level-1)} if $listme;
582 $bitmask |= $bitfields{"org".($level-1)} if $listorg;
[23]583 }
584
585# hm. can't seem to move this prepare elsewhere. :(
[2]586 if ($nblocks > 0) {
[66]587 my $sthsubblocks = $dbh->prepare("SELECT block,exclude FROM blocks ".
[32]588 "WHERE level = ? AND parent = ?");
[23]589 $sthsubblocks->execute($level, $container);
[66]590 while (my ($cidr, $exclude) = $sthsubblocks->fetchrow_array()) {
591 if ($exclude) {
592 $listhosts->{$cidr} = -1;
593 } else { # don't check subtrees of an excluded block; rbldnsd doesn't support deep flip-flopping like that
594 $self->export($listhosts,$mode,$level+1,$cidr,$bitmask)
595 or die $errstr;
596 }
[2]597 }
598 } # avoid checking content of subs if we don't have any
599
600 # don't check all 4.2 billion IPs individually if we're looking at all teh Intarwebs
601 return if $container eq '0.0.0.0/0';
602
603##fixme: need a way to dig out orphan IPs at all levels - IPs not found in a
604# subblock of the current container when the current container *has* subblocks
605# NB: this may be better handled as an out-of-band data-integrity-checker
606
607 # decrement level here so the right bitfield setting gets picked. this segment
608 # is inherently off-by-one from the block-recursion loop, and I can't see a
609 # better way to work around that. >:(
610 $level--;
611
[4]612 if ($mode eq 'cidr') {
[23]613 $listhosts->{$container} |= $bitmask if $bitmask && ($listme || $listorg || ($bcount >= $autolist{$masklen}));
[2]614 } else {
615 # if $cidr->masklen is <= 24, iterate on /24 boundaries for bulk sublisting
616 # if $cidr->masklen is <= 16, iterate on /16 boundaries for bulk sublisting
617 # if $cidr->masklen is <= 8, iterate on /8 boundaries for bulk sublisting
618
[23]619 if ($bitmask) {
[2]620 my @blocksubs;
621 if ($masklen <= 30 && $masklen > 24) {
622 my ($net,$octet) = ($container =~ m|^(\d+\.\d+\.\d+\.)(\d+)/|);
623 for (my $entry = $octet; $entry < ($octet + $howmany[$masklen]); $entry++) {
624 my $host = "$net$entry";
625 $listhosts->{$host} = 0 if !defined($listhosts->{$host});
[23]626 $listhosts->{$host} |= $bitmask;
[2]627 }
628 } elsif ($masklen <= 24 && $masklen > 16) {
629 my ($net,$octet) = ($container =~ m|^(\d+\.\d+\.)(\d+)\.\d+/|);
630 for (my $entry = $octet; $entry < ($octet + $howmany[$masklen]); $entry++) {
631 my $twofour = "$net$entry.*";
[23]632 $listhosts->{$twofour} |= $bitmask;
[2]633 }
634 } elsif ($masklen <= 16 && $masklen > 8) {
635 my ($net,$octet) = ($container =~ m|^(\d+\.)(\d+)\.\d+\.\d+/|);
636 for (my $entry = $octet; $entry < ($octet + $howmany[$masklen]); $entry++) {
637 my $sixteen = "$net$entry.*";
[23]638 $listhosts->{$sixteen} |= $bitmask;
[2]639 }
640 } elsif ($masklen <= 8) {
641 my ($octet) = ($container =~ m|^(\d+)\.\d+\.\d+\.\d+/|);
642 for (my $entry = $octet; $entry < ($octet + $howmany[$masklen]); $entry++) {
643 my $eight = "$entry.*";
[23]644 $listhosts->{$eight} |= $bitmask;
[2]645 }
646 }
647
648 } # generate autolist entries for ips/octets not (yet) seen in reports
649
650 } # cidr vs classful mode
651
[23]652 $sthmoron->execute($container);
[66]653 while (my ($ip,$moron,$exclude) = $sthmoron->fetchrow_array()) {
[2]654 if ($moron) {
655 $listhosts->{$ip} = $bitfields{slist};
[66]656 } elsif ($exclude) {
657 $listhosts->{$ip} = -1;
[2]658 } else {
[66]659 $listhosts->{$ip} |= $bitmask;
[2]660 $listhosts->{$ip} |= $bitfields{ip};
661 }
662 }
663
[66]664
665 }; # db-went-away-catching eval
666 if ($@) {
667 $errstr = $@;
668 warn "export truncated: $errstr\n";
669 return;
670 }
671
672
[2]673# get IPs which for reasons unknown are apparently allocated directly from the
674# parent registry (and so do not have containing netblocks in this system) O_o
675# select * from iplist where not (select count(*) from blocks where ip << block) > 0;
676
[66]677 return 1;
[2]678} # end export()
679
680
681## DNSBL::autolist_block()
682# check if a block should be autolisted
683sub autolist_block {
684 my $self = shift;
685 my $block = shift;
686
687 my $cidr = new NetAddr::IP "$block";
[32]688 my $sth = $dbh->prepare("SELECT ipcount FROM blocks WHERE block = ?");
[2]689 $sth->execute("$cidr");
690 my ($count) = $sth->fetchrow_array;
691
692 return 1 if $count >= $autolist{$cidr->masklen};
693 return 0;
694} # end autolist_block()
695
696
697# make Perl happy
6981;
Note: See TracBrowser for help on using the repository browser.