1 --- check_rbl-1.1.0/check_rbl 2010-04-06 20:26:18.598337785 +0300
2 +++ check_rbl-1.2.0/check_rbl 2010-04-06 20:40:42.858468257 +0300
4 # check_rbl is a Nagios plugin to check if an SMTP server is blacklisted
6 # See the INSTALL file for installation instructions
8 # Copyright (c) 2007, ETH Zurich.
9 +# Copyright (c) 2010, Elan Ruusamäe <glen@delfi.ee>.
11 # This module is free software; you can redistribute it and/or modify it
12 # under the terms of GNU general public license (gpl) version 3.
18 -use English '-no_match_vars';
19 use Nagios::Plugin::Threshold;
21 use Nagios::Plugin::Getopt;
23 -use Parallel::Iterator qw(iterate);
27 Readonly my $DEFAULT_RETRIES => 4;
28 Readonly my $DEFAULT_WORKERS => 20;
41 ##############################################################################
42 -# Usage : my $ip = lookup( $hostname );
43 -# Purpose : DNS lookup
44 -# Returns : $ip if found; undef if not found
45 -# Arguments : $hostname : the FQDN to resolve
51 - my ($hostname) = @_;
55 - my $res = Net::DNS::Resolver->new;
57 - $res->retry( $OPTIONS->retry() );
59 - my $query = $res->search($hostname);
62 - foreach my $rr ( $query->answer ) {
63 - if ( $rr->type eq 'A' ) {
64 - return $rr->address;
72 - return; # dead code to make perlcritic happy
76 -##############################################################################
77 # Usage : verbose("some message string", $optional_verbosity_level);
78 # Purpose : write a message if the verbosity level is high enough
84 -##############################################################################
85 -# Usage : check_server( $ip, $server )
86 -# Purpose : checks if $ip is blacklisted by $server
88 -# Arguments : $ip : host IP
89 -# $server : RBL server
91 -# Comments : if blacklisted pushed $server onto @blacklisted
97 - my $lookup_ip = $IP;
98 +=item B<mdns(\@addresses, $callback)>
101 -s/(\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3})/$4.$3.$2.$1.$server/mxs;
103 - verbose " -> $lookup_ip\n";
105 - if ( lookup($lookup_ip) ) {
107 - verbose "LISTED: $lookup_ip\n";
111 +Resolves all IPs in C<@addresses> in parallel.
112 +If answer is found C<$callback> is called with arguments as: $name, $host.
114 - verbose "OK: $lookup_ip\n";
115 +Perform multiple DNS lookups in parallel. Based on Perl Net-DNS/mresolv.
116 +Elan Ruusamäe <glen@delfi.ee>, (c) 1999-2010
122 + my $callback = shift;
124 + # number of requests to have outstanding at any time
125 + my $opt_n = $OPTIONS->workers;
126 + # timeout per query (seconds)
128 + my $opt_d = $OPTIONS->verbose;
130 + my $sel = IO::Select->new;
133 + my @addrs = @$data;
138 + #----------------------------------------------------------------------
139 + # Read names until we've filled our quota of outstanding requests.
140 + #----------------------------------------------------------------------
142 + while (!$eof && $sel->count < $opt_n) {
143 + print "DEBUG: reading..." if $opt_d;
144 + $name = shift @addrs;
145 + unless (defined $name) {
146 + print "EOF.\n" if $opt_d;
150 + print "NAME: $name\n" if $opt_d;
151 + $sock = $res->bgsend($name);
153 + # we store in a hash the query we made, as parsing it back from
154 + # response gives different ip for ips with multiple hosts
155 + $addrs{$sock} = $name;
157 + print "name = $name, outstanding = ", $sel->count, "\n" if $opt_d;
160 + #----------------------------------------------------------------------
161 + # Wait for any replies. Remove any replies from the outstanding pool.
162 + #----------------------------------------------------------------------
167 + print "DEBUG: waiting for replies\n" if $opt_d;
169 + for (@ready = $sel->can_read($opt_t);
171 + @ready = $sel->can_read(0)) {
175 + print "DEBUG: replies received: ", scalar @ready, "\n" if $opt_d;
177 + foreach $sock (@ready) {
178 + print "DEBUG: handling a reply\n" if $opt_d;
179 + my $addr = $addrs{$sock};
180 + delete $addrs{$sock};
181 + $sel->remove($sock);
183 + my $ans = $res->bgread($sock);
186 + foreach my $rr ($ans->answer) {
187 + next unless $rr->type eq 'A';
188 + $host = $rr->address;
189 + # take just first answer
193 + print "DEBUG: no answer: ". $res->errorstring. "\n" if $opt_d;
195 + &$callback($addr, $host);
199 + #----------------------------------------------------------------------
200 + # If we timed out waiting for replies, remove all entries from the
201 + # outstanding pool.
202 + #----------------------------------------------------------------------
205 + print "DEBUG: timeout: clearing the outstanding pool.\n" if $opt_d;
207 + foreach $sock ($sel->handles) {
208 + my $addr = $addrs{$sock};
209 + delete $addrs{$sock};
210 + $sel->remove($sock);
211 + # callback for hosts that timed out
212 + &$callback($addr, '');
216 + print "DEBUG: outstanding = ", $sel->count, ", eof = $eof\n" if $opt_d;
218 + #----------------------------------------------------------------------
219 + # We're done if there are no outstanding queries and we've read EOF.
220 + #----------------------------------------------------------------------
222 + last if ($sel->count == 0) && $eof;
226 ##############################################################################
228 'critical has to be greater or equal warning' );
231 +$res = new Net::DNS::Resolver;
232 +$res->force_v4(1) if $res->can('force_v4');
233 +$res->retry($OPTIONS->retry());
235 $IP = $OPTIONS->host;
236 -if ( $IP =~ m/[[:lower:]]/mxs ) {
237 - $IP = lookup( $OPTIONS->host );
238 +if ($IP =~ m/[[:lower:]]/mxs) {
239 + mdns([ $OPTIONS->host ], sub {
240 + my ($addr, $host) = @_;
246 @@ -259,28 +314,37 @@
248 verbose 'Checking ' . $OPTIONS->host . " ($IP) on $nservers server(s)\n";
251 - { workers => $OPTIONS->workers },
254 - while ( my $server = pop @servers ) {
261 -while ( my ( $server, $result ) = $iter->() ) {
263 - push @BLACKLISTED, $server;
265 +# build address lists
267 +foreach my $server (@servers) {
268 + (my $ip = $IP) =~ s/(\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3})/$4.$3.$2.$1.$server/x;
273 + my ($addr, $host) = @_;
274 + # extract RBL we checked
275 + $addr =~ s/^(?:\d+\.){4}//;
276 + if (defined $host) {
278 + push @TIMEOUTS, $addr;
280 + verbose "listed in $addr as $host\n";
281 + push @BLACKLISTED, $addr;
286 my $total = scalar @BLACKLISTED;
289 $OPTIONS->host. " BLACKLISTED on $total " . ( ( $total == 1 ) ? 'server' : 'servers' ) . " of $nservers";
291 +# append timeout info, but do not account these in status
293 + $status .= sprintf(" (%d servers timeout: %s)", scalar @TIMEOUTS, join(', ', @TIMEOUTS));
297 $status .= " (@BLACKLISTED)";