diff options
Diffstat (limited to 'nagios-plugins-contrib-24.20190301~bpo9+1/check_rbl/check_rbl-1.4.4/check_rbl')
-rw-r--r-- | nagios-plugins-contrib-24.20190301~bpo9+1/check_rbl/check_rbl-1.4.4/check_rbl | 642 |
1 files changed, 642 insertions, 0 deletions
diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/check_rbl/check_rbl-1.4.4/check_rbl b/nagios-plugins-contrib-24.20190301~bpo9+1/check_rbl/check_rbl-1.4.4/check_rbl new file mode 100644 index 0000000..730a937 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/check_rbl/check_rbl-1.4.4/check_rbl @@ -0,0 +1,642 @@ +#!perl + +# nagios: -epn + +package main; + +# check_rbl is a Nagios plugin to check if an SMTP server is black- or +# white- listed +# +# See the INSTALL file for installation instructions +# +# Copyright (c) 2014, Matteo Corti <matteo@corti.li> +# Copyright (c) 2007, ETH Zurich. +# Copyright (c) 2010, Elan Ruusamae <glen@delfi.ee>. +# +# This module is free software; you can redistribute it and/or modify it +# under the terms of GNU general public license (gpl) version 3. +# See the LICENSE file for details. + +use strict; +use warnings; + +our $VERSION = '1.4.4'; + +use Data::Validate::Domain qw(is_hostname); +use Data::Validate::IP qw(is_ipv4 is_ipv6); +use IO::Select; +use Net::DNS; +use Net::IP qw(ip_expand_address); +use Readonly; +use English qw(-no_match_vars); + +my $plugin_module = load_module( 'Monitoring::Plugin', 'Nagios::Plugin' ); +my $plugin_threshold_module = + load_module( 'Monitoring::Plugin::Threshold', 'Nagios::Plugin::Threshold' ); +my $plugin_getopt_module = + load_module( 'Monitoring::Plugin::Getopt', 'Nagios::Plugin::Getopt' ); + +# Check which version of the monitoring plugins is available + +sub load_module { + + my @names = @_; + my $loaded_module; + + for my $name (@names) { + + my $file = $name; + + # requires need either a bare word or a file name + $file =~ s{::}{/}gsxm; + $file .= '.pm'; + + eval { ## no critic (ErrorHandling::RequireCheckingReturnValueOfEval) + require $file; + $name->import(); + }; + if ( !$EVAL_ERROR ) { + $loaded_module = $name; + last; + } + } + + if ( !$loaded_module ) { + #<<< + print 'CHECK_RBL: plugin not found: ' . join( ', ', @names ) . "\n"; ## no critic (RequireCheckedSyscall) + #>>> + + exit 2; + } + + return $loaded_module; + +} + +Readonly our $DEFAULT_TIMEOUT => 15; +Readonly our $DEFAULT_RETRIES => 4; +Readonly our $DEFAULT_WORKERS => 20; +Readonly our $DEFAULT_QUERY_TIMEOUT => 15; +Readonly our $DEFAULT_APPEND_STRING => q{}; + +# IMPORTANT: Nagios plugins could be executed using embedded perl in this case +# the main routine would be executed as a subroutine and all the +# declared subroutines would therefore be inner subroutines +# This will cause all the global lexical variables not to stay shared +# in the subroutines! +# +# All variables are therefore declared as package variables... +# + +## no critic (ProhibitPackageVars) +our ( @listed, @timeouts, $options, $plugin, $threshold, $timeouts_string, ); + +############################################################################## +# Usage : debug("some message string") +# Purpose : write a message if the debugging option was specified +# Returns : n/a +# Arguments : message : message string +# Throws : n/a +# Comments : n/a +# See also : n/a +sub debug { + + # arguments + my $message = shift; + + if ( !defined $message ) { + $plugin->nagios_exit( $plugin_module->UNKNOWN, + q{Internal error: not enough parameters for 'debug'} ); + } + + if ( $options && $options->debug() ) { + ## no critic (RequireCheckedSyscall) + print "[DBG] $message\n"; + } + + return; + +} + +############################################################################## +# Usage : verbose("some message string", $optional_verbosity_level); +# Purpose : write a message if the verbosity level is high enough +# Returns : n/a +# Arguments : message : message string +# level : options verbosity level +# Throws : n/a +# Comments : n/a +# See also : n/a +sub verbose { + + # arguments + my $message = shift; + my $level = shift; + + if ( !defined $message ) { + $plugin->nagios_exit( $plugin_module->UNKNOWN, + q{Internal error: not enough parameters for 'verbose'} ); + } + + if ( !defined $level ) { + $level = 0; + } + + if ( $level < $options->verbose ) { + if ( !print $message ) { + $plugin->nagios_exit( $plugin_module->UNKNOWN, + 'Error: cannot write to STDOUT' ); + } + } + + return; + +} + +# the script is declared as a package so that it can be unit tested +# but it should not be used as a module +if ( !caller ) { + run(); +} + +############################################################################## +# Usage : my $res = init_dns_resolver( $retries ) +# Purpose : Initializes a new DNS resolver +# Arguments : retries : number of retries +# Returns : The newly created resolver +# See also : Perl Net::DNS +sub init_dns_resolver { + + my $retries = shift; + + my $res = Net::DNS::Resolver->new(); + if ( $res->can('force_v4') ) { + $res->force_v4(1); + } + + if ($retries) { + $res->retry($retries); + } + + return $res; +} + +############################################################################## +# Usage : mdns(\@addresses, $callback) +# Purpose : Perform multiple DNS lookups in parallel +# Returns : n/a +# See also : Perl Net::DNS module mresolv in examples +# +# Resolves all IPs in C<@addresses> in parallel. +# If answer is found C<$callback> is called with arguments as: $name, $host. +# +# Author: Elan Ruusamae <glen@delfi.ee>, (c) 1999-2010 +sub mdns { + + my ( $data, $callback ) = @_; + + # number of requests to have outstanding at any time + my $workers = $options ? $options->workers() : 1; + + # timeout per query (seconds) + my $timeout = $options ? $options->get('query-timeout') : $DEFAULT_TIMEOUT; + my $res = init_dns_resolver( $options ? $options->retry() : 0 ); + + my $sel = IO::Select->new(); + my $eof = 0; + + my @addrs = @{$data}; + + my %addrs; + while (1) { + + #---------------------------------------------------------------------- + # Read names until we've filled our quota of outstanding requests. + #---------------------------------------------------------------------- + + while ( !$eof && $sel->count() < $workers ) { + + my $name = shift @addrs; + + if ( !defined $name ) { + debug('reading...EOF.'); + $eof = 1; + last; + } + + debug("reading...$name"); + + my $sock = $res->bgsend($name); + + if ( !defined $sock ) { + verbose 'DNS query error: ' . $res->errorstring; + verbose "Skipping $name"; + } + else { + + # we store in a hash the query we made, as parsing it back from + # response gives different ip for ips with multiple hosts + $addrs{$sock} = $name; + $sel->add($sock); + debug( "name = $name, outstanding = " . $sel->count() ); + + } + + } + + #---------------------------------------------------------------------- + # Wait for any replies. Remove any replies from the outstanding pool. + #---------------------------------------------------------------------- + + my @ready; + my $timed_out = 1; + + debug('waiting for replies'); + + @ready = $sel->can_read($timeout); + + while (@ready) { + + $timed_out = 0; + + debug( 'replies received: ' . scalar @ready ); + + foreach my $sock (@ready) { + + debug('handling a reply'); + + my $addr = $addrs{$sock}; + delete $addrs{$sock}; + $sel->remove($sock); + + my $ans = $res->bgread($sock); + + my $host; + + if ($ans) { + + foreach my $rr ( $ans->answer ) { + + debug('Processing answer'); + + ## no critic(ProhibitDeepNests) + if ( !( $rr->type eq 'A' ) ) { + next; + } + + $host = $rr->address; + + debug("host = $host"); + + # take just the first answer + last; + } + } + else { + + debug( 'no answer: ' . $res->errorstring() ); + + } + + if ( defined $host ) { + + debug("callback( $addr, $host )"); + + } + else { + + debug("callback( $addr, <undefined> )"); + + } + + &{$callback}( $addr, $host ); + } + + @ready = $sel->can_read(0); + + } + + #---------------------------------------------------------------------- + # If we timed out waiting for replies, remove all entries from the + # outstanding pool. + #---------------------------------------------------------------------- + + if ($timed_out) { + + debug('timeout: clearing the outstanding pool.'); + + foreach my $sock ( $sel->handles() ) { + my $addr = $addrs{$sock}; + delete $addrs{$sock}; + $sel->remove($sock); + + # callback for hosts that timed out + &{$callback}( $addr, q{} ); + } + } + + debug( 'outstanding = ' . $sel->count() . ", eof = $eof" ); + + #---------------------------------------------------------------------- + # We're done if there are no outstanding queries and we've read EOF. + #---------------------------------------------------------------------- + + last if ( $sel->count() == 0 ) && $eof; + } + + return; + +} + +############################################################################## +# Usage : validate( $hostname ); +# Purpose : check if an IP address or host name is valid +# Returns : the IP address corresponding to $hostname +# Arguments : n/a +# Throws : an UNKNOWN error if the argument is not valid +# Comments : n/a +# See also : n/a +sub validate { + + my $hostname = shift; + my $ip = $hostname; + + debug("validate($hostname, $ip)"); + + if ( !is_ipv4($hostname) && !is_ipv6($hostname) ) { + + if ( is_hostname($hostname) ) { + + mdns( + [$hostname], + sub { + my ( $addr, $host ) = @_; + $ip = $host; + } + ); + + if ( !$ip ) { + $plugin->nagios_exit( $plugin_module->UNKNOWN, + 'Cannot resolve ' . $hostname ); + } + + } + + if ( !$ip ) { + $plugin->nagios_exit( $plugin_module->UNKNOWN, + 'Cannot resolve ' . $hostname ); + } + + } + + if ( is_ipv6($ip) ) { + $ip = Net::IP::ip_expand_address( $ip, 6 ); + } + + return $ip; + +} + +############################################################################## +# Usage : run(); +# Purpose : main method +# Returns : n/a +# Arguments : n/a +# Throws : n/a +# Comments : n/a +# See also : n/a +sub run { + + ################################################################################ + # Initialization + + $plugin = $plugin_module->new( shortname => 'CHECK_RBL' ); + + my $time = time; + + ######################## + # Command line arguments + + $options = $plugin_getopt_module->new( + usage => 'Usage: %s [OPTIONS]', + version => $VERSION, + url => 'http://matteocorti.github.io/check_rbl/', + blurb => 'Check SMTP black- or white- listing status', + ); + + $options->arg( + spec => 'critical|c=i', + help => 'Number of blacklisting servers for a critical warning', + required => 0, + default => 1, + ); + + $options->arg( + spec => 'warning|w=i', + help => 'Number of blacklisting servers for a warning', + required => 0, + default => 1, + ); + + $options->arg( + spec => 'debug|d', + help => 'Prints debugging information', + required => 0, + default => 0, + ); + + $options->arg( + spec => 'server|s=s@', + help => 'RBL server', + required => 1, + ); + + $options->arg( + spec => 'host|H=s', + help => 'SMTP server to check', + required => 1, + ); + + $options->arg( + spec => 'retry|r=i', + help => 'Number of times to try a DNS query (default is 4) ', + required => 0, + default => $DEFAULT_RETRIES, + ); + + $options->arg( + spec => 'workers=i', + help => 'Number of parallel checks', + required => 0, + default => $DEFAULT_WORKERS, + ); + + $options->arg( + spec => 'whitelistings|wl', + help => 'Check whitelistings instead of blacklistings', + required => 0, + default => 0, + ); + + $options->arg( + spec => 'query-timeout=i', + help => 'Timeout of the RBL queries', + required => 0, + default => $DEFAULT_QUERY_TIMEOUT, + ); + + $options->arg( + spec => 'append|a=s', + help => 'Append string at the end of the plugin output', + required => 0, + default => $DEFAULT_APPEND_STRING, + ); + + $options->getopts(); + + ############### + # Sanity checks + + if ( $options->critical < $options->warning ) { + $plugin->nagios_exit( $plugin_module->UNKNOWN, + 'critical has to be greater or equal warning' ); + } + + my $ip = validate( $options->host ); + + my @servers = @{ $options->server }; + + verbose 'Using ' . $options->timeout . " as global script timeout\n"; + alarm $options->timeout; + + ################ + # Set the limits + + # see https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT + $threshold = $plugin_threshold_module->set_thresholds( + warning => $options->warning - 1, + critical => $options->critical - 1, + ); + + ################################################################################ + + my $nservers = scalar @servers; + + verbose 'Checking ' . $options->host . " ($ip) on $nservers server(s)\n"; + + # build address lists + my @addrs; + foreach my $server (@servers) { + my $local_ip = $ip; + if ( is_ipv6($local_ip) ) { + $local_ip = reverse $local_ip; + $local_ip =~ s/://gmxs; + $local_ip =~ s/(.)/$1\./gmxs; + $local_ip = $local_ip . $server; + } + else { + $local_ip =~ +s/(\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3})/$4.$3.$2.$1.$server/mxs; + } + push @addrs, $local_ip; + } + + mdns( + \@addrs, + sub { + my ( $addr, $host ) = @_; + + if ( defined $host ) { + + debug("callback( $addr, $host )"); + + } + else { + + debug("callback( $addr, <undefined> )"); + + } + + # extract RBL we checked + $addr =~ s/^(?:[a-f\d][.]){32}//mxs; + $addr =~ s/^(?:\d+[.]){4}//mxs; + if ( defined $host ) { + if ( $host eq q{} ) { + push @timeouts, $addr; + } + else { + verbose "listed in $addr as $host\n"; + if ( !$options->get('whitelistings') ) { + push @listed, $addr; + } + } + } + else { + verbose "not listed in $addr\n"; + if ( $options->get('whitelistings') ) { + push @listed, $addr; + } + } + } + ); + + my $total = scalar @listed; + + my $status; + my $appendstring = $options->append; + if ( $options->get('whitelistings') ) { + + $status = + $options->host + . " NOT WHITELISTED on $total " + . ( ( $total == 1 ) ? 'server' : 'servers' ) + . " of $nservers"; + } + else { + $status = + $options->host + . " BLACKLISTED on $total " + . ( ( $total == 1 ) ? 'server' : 'servers' ) + . " of $nservers"; + + } + + # append timeout info, but do not account these in status + if (@timeouts) { + $timeouts_string = scalar @timeouts; + $status = + " ($timeouts_string server" + . ( ( $timeouts_string > 1 ) ? 's' : q{} ) + . ' timed out: ' + . join( ', ', @timeouts ) . ')'; + } + + if ( $total > 0 ) { + $status .= " (@listed)"; + } + + $plugin->add_perfdata( + label => 'servers', + value => $total, + uom => q{}, + threshold => $threshold, + ); + + $plugin->add_perfdata( + label => 'time', + value => time - $time, + uom => q{s}, + ); + + # append string defined in append argument to status output + if ( $appendstring ne q{} ) { + $status .= " $appendstring"; + } + + $plugin->nagios_exit( $threshold->get_status($total), $status ); + + return; + +} + +1; |