#!/usr/bin/perl # rbl.monitor - check RBL blacklists for an IP address. Uses asynch I/O # to send all the requests simultaneously # Copyright (c) 2007, 2008 by Ed Ravin . License is GNU. # Available to the public courtesy of Public Access Networks http://panix.com my $usage="\ Usage: rbl.monitor [options] hostname [...] Options [and default values]: --listfile [preset list, see script] --rbllist --timeout [60 seconds] --debug [off] "; use strict; use Net::DNS; use IO::Select; use Getopt::Long; my %opt; GetOptions(\%opt, "listfile=s", "rbllist=s", "timeout=i", "debug", ) or die $usage; my $listfile= $opt{listfile} || ""; my $rbllist= $opt{rbllist} || ""; my $selecttimeout = 5; my $timeout= ($opt{timeout} || 60) + ($selecttimeout * 2); my $debug= $opt{debug} || 0; # Default RBLs to check - just a few of the lists most likely to block mail # Sites with specific needs should customize via the command line my @rbls2check=( "bl.spamcop.net", "relays.mail-abuse.org", "zen.spamhaus.org", "dnsbl.sorbs.net", "dnsbl-1.uceprotect.net", ); if ($listfile) { open(LIST, "< $listfile") || die "$0: cannot open list file \"$listfile\": $!\n"; @rbls2check= grep !/^\s*#/, ; @rbls2check= grep !/^\s*$/, @rbls2check; map {chomp} @rbls2check; close LIST; die "$0: no RBL names found in \"$listfile\"\n" unless @rbls2check; } if ($rbllist) { @rbls2check= split(',', $rbllist); } print "*** checking these RBLs:\n " . join("\n ", @rbls2check) . "\n" if $debug; my (@summary, @detail); my @sockets; my $res = Net::DNS::Resolver->new; my $sel = IO::Select->new(); my $starttime= time; my %revip2host; # gethostbyname is non-reentrant, so parse the hostnames to test up front foreach my $host (@ARGV) { my $hostdata= gethostbyname($host); if (!defined($hostdata)) { push @summary, $host; push @detail, "$host: bad hostname"; next; } my $revip= join(".", reverse(unpack("C4", $hostdata))); $revip2host{$revip}= $host; } # start all the queries foreach my $revip (keys %revip2host) { foreach my $rbl (@rbls2check) { my $dnssock= $res->bgsend(join(".", $revip, $rbl)); die "$0: Net::DNS::Resolver::bgsend returns undef - too many open files?\n" unless defined($dnssock); push @sockets, $dnssock; $sel->add($dnssock); } } MAINLOOP: while ($sel->handles > 0) { my @ready = $sel->can_read($selecttimeout); if ( (time - $starttime) > $timeout) { # waited too long? push @detail, "TIMEOUT: " . scalar($sel->handles) . " responses still pending"; last MAINLOOP; } foreach my $sock (@ready) { my ($authority, $ipaddress, $revip, $forwardip, $host); my $packet = $res->bgread($sock); foreach my $rr ($packet->answer) { if ($rr->type eq "A") { $ipaddress= $rr->address; $authority= $rr->name; my $q= \$packet->question; my @qquads= split('\.',${$$q}{qname}); splice(@qquads, 4); $revip= join('.', @qquads); $forwardip= join('.', reverse(@qquads)); $host= $revip2host{$revip} || $forwardip; push @summary, $host unless grep /^$host$/, @summary; push @detail, "$host: $authority: " . $rr->address; } } $sel->remove($sock); } } print join(" ", (sort @summary)) if (@summary); print "\n"; print join("\n", (sort @detail)), "\n" if @detail; exit 1 if @summary; exit 0;