#!/usr/bin/perl -wT

# $Id: check_wins.pl,v 1.2 2003/08/20 08:31:49 tonvoon Exp $

# $Log: check_wins.pl,v $
# Revision 1.2  2003/08/20 08:31:49  tonvoon
# Changed netsaint to nagios in use lib
#
# Revision 1.1  2003/02/09 14:16:28  sghosh
# more contribs
#

require 5.004;
use strict ;
use lib utils.pm ;
use Plugin;
use Plugin::Parameter qw(:DEFAULT _check_hostname);

use vars qw($opt_H $opt_D $opt_W $opt_T $debug @my_dcs $opt_t $PROGNAME);

use utils qw(%ERRORS &usage);

use constant SAMBA_DEBUG_LVL	=> 2 ;
use constant MY_DCS 		=> ('dc1','dc2') ;

my $W_opt = new Plugin::Parameter(-name => "wins", -flags => [ 'W', 'wins' ],
				  -optional => "no", -valueoptional => "no", -type => "STRING",
				  -checker => \&_check_hostname,
				  -description => "Hostname or address of the WINS (Either Samba/nmbd or MS product)");
my $D_opt = new Plugin::Parameter(-name => "domain", -flags => [ 'D', 'domain' ],
				  -optional => "no", -valueoptional => "no", -type => "STRING",
				  -checker => sub { my ($opt, $parameter, $plugin) = @_;
						    if (defined $$opt) {
						      unless ($$opt =~ m/^(\w+)$/) {
							$plugin->usage();
							usage("$PROGNAME UNKNOWN: Invalid MS D name: $$opt\n");
						      }
						      $$opt = $1;
						    }
						  },
				  -description => "MS Domain name to find the Domain controllers of.");
my $C_opt = new Plugin::Parameter(-name => "controllers", -flags => [ 'C', 'controllers' ],
				  -optional => "no", -valueoptional => "no", -type => "STRING",
				  -binding => \@my_dcs,
				  -checker => sub { my ($opt, $parameter, $plugin) = @_;
						    # Just untaint the array
						    for (my $i = 0; $i < @{$opt}; $i++) {
						      $opt->[$i] =~ m/^(.*)$/;
						      $opt->[$i] = $1;
						    }
						  },
				  -description => "__name(s)__ of domain controller that __must__ be found in the response to a domain controller name query.");
my $d_opt = new Plugin::Parameter(-name => "debug", -flags => [ 'd', 'debug' ],
				  -optional => "yes", -valueoptional => "yes", -type => "NONE",
				  -description => "Generate debugging output");
my $plugin = new Plugin(-revision => '$Revision: 1.2 $',
			-copyright => "2001 Karl DeBisschop/S Hopcroft",
			-shortcomment => "Perl Check WINS plugin for Nagios.",
			-longcomment => "Returns OK if the addresses of domain controllers are found in the list of domain controllers returned in the WINS response to a 'domain controllers query'\n\nWhy would you want to do this ?\n\nMS File server clients insist on connecting to file servers using NetBIOS names. If they __don't__ resolve NetBIOS names with a WINS (NBNS) then they'll either fail to logon and  connect to shares or they will broadcast requsts for names. Both problems are eliminated by a healthy WINS. Also, you may have a MS domain spanning a  number of WAN connected sites, with domain controllers far away from powerful local domain controllers. In this case, you want your local clients to have their logon requests validated by the local controllers.\n\nThe plugin works by\n\tasking the WINS to resolve the addresses of the domain controllers (supplied by -C or from the constant MY_DCS)\n\tasking the WINS to return the list of addresses of the domain controllers able to validate requests for the domain whose name is given by -D\n\treturning Ok\tif all controller addresses are in that list (of addresses of domain controllers) or\n\treturning WARNING\tif not all the controller addresses are in the list or\n\treturning CRITICAL\tif there is no reply from the WINS or the list contains none of the contoller addresses",
			-parameterlists => [ [ $W_opt, $D_opt, $C_opt, $t_opt, $d_opt ], $h_opts, $V_opts ]);

my $NMBLOOKUP_PATH		= '/usr/bin/nmblookup' ;
my $NMBLOOKUP			= sub { return `$NMBLOOKUP_PATH $_[2] -R -U $_[0] $_[1]` } ;
my $NMBLOOKUP_CMD		= $NMBLOOKUP_PATH  . ' -R -U' ;

delete @ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

$plugin->init();

my $wins = $1 if $opt_W =~ m#((?:^\w+$)|(?:\d+(?:\.\d+){3,3}$))# ;
unless ($wins) {
  $plugin->usage();
  usage("$PROGNAME UNKNOWN: Invalid WINS hostname or address: $opt_W\n");
}

my ($netbios_name, @dcs_of_domain, @dc_addresses) ;
my (@addr_dcs_of_domain, @found_dcs, %address2dc) ;
my (@dc_query) ;

# tsitc> /usr/local/samba/bin/nmblookup -R -U wins ipa01
# Sending queries to 10.0.100.29
# 10.0.100.16 ipa01<00>

$plugin->start_timeout($opt_t, "Timed out waiting for response from WINS servers");

@dc_query = $debug ?
  map { $NMBLOOKUP->($wins, "$_#20", '-d ' . SAMBA_DEBUG_LVL) } @my_dcs :
  map { ( $NMBLOOKUP->($wins, "$_#20", '') )[1] } @my_dcs ;

$plugin->stop_timeout();

chomp @dc_query ;
if ( scalar grep(/name_query failed/, @dc_query) ) {
  print qq($PROGNAME CRITICAL: Failed. WINS "$wins" failed to resolve), scalar @my_dcs > 1 ? ' at least one of ' : ' ', qq("@my_dcs", the domain controller(s) of "$opt_D". Got "@dc_query"\n) ;
  exit $ERRORS{"CRITICAL"} ;
}

# the results of looking up the DCs (by their name) in the WINS 

# 10.0.100.16 ipa01<20>
# 10.0.100.1 ipa02<20>
# 10.0.100.104 ipa03<20>

@dc_addresses = () ;
foreach (@dc_query) {
  next unless /^(\S+)\s+(\S+?)<\S+?>$/ ;
  $address2dc{$1} = $2 ;
  push @dc_addresses, $1 ;
}

$netbios_name = "$opt_D#1C"  ;

$plugin->start_timeout($opt_t, "Timed out wating for response from WINS servers");

@dcs_of_domain = $NMBLOOKUP->($wins, $netbios_name, defined $debug ? '-d ' . SAMBA_DEBUG_LVL : '') ;

$plugin->stop_timeout();

shift @dcs_of_domain ;
chomp @dcs_of_domain ;
@addr_dcs_of_domain = map /^(\S+)/, @dcs_of_domain ;

# tsitc> /usr/local/samba/bin/nmblookup -R -U wins ipaustralia#1c
# Sending queries to 10.0.100.29
# 10.0.100.114 ipaustralia<1c>
# 168.168.102.129 ipaustralia<1c>
# 192.168.101.221 ipaustralia<1c>
# 10.0.100.61 ipaustralia<1c>
# 192.168.108.129 ipaustralia<1c>
# 192.168.102.128 ipaustralia<1c>
# 10.0.4.126 ipaustralia<1c>
# 192.168.106.214 ipaustralia<1c>
# 10.0.3.165 ipaustralia<1c>
# 192.168.105.214 ipaustralia<1c>
# 10.0.6.145 ipaustralia<1c>
# 192.168.104.128 ipaustralia<1c>
# 10.0.4.59 ipaustralia<1c>
# 10.9.99.99 ipaustralia<1c>
# 10.99.99.99 ipaustralia<1c>
# 10.9.99.254 ipaustralia<1c>
# 10.0.3.15 ipaustralia<1c>
# 192.168.102.129 ipaustralia<1c>
# 192.168.103.129 ipaustralia<1c>
# 192.168.105.129 ipaustralia<1c>
# 192.168.106.129 ipaustralia<1c>
# 192.168.101.129 ipaustralia<1c>
# 192.168.104.129 ipaustralia<1c>
# 10.0.3.123 ipaustralia<1c>
# 10.0.100.67 ipaustralia<1c>
# tsitc> 

my %x ;
@found_dcs = grep { ! $x{$_}++ } @address2dc{ grep exists $address2dc{$_}, @addr_dcs_of_domain} ;

if ( &set_eq( \@found_dcs, [ values %address2dc ] ) ) {
  print $debug ? qq($PROGNAME OK: WINS named "$wins" resolved addresses of "@my_dcs" as "@dc_query" and controllers of domain "$opt_D" as "@dcs_of_domain"\n) :
		 qq($PROGNAME OK: Found controllers named "@my_dcs" in response to "$opt_D#1C" name query from WINS named "$wins".\n) ;
  exit $ERRORS{"OK"} ;
} elsif ( scalar @found_dcs == 0 ) {
  print qq($PROGNAME CRITICAL: Failed. Found __no__ controllers named "@my_dcs" in response to "$opt_D#1C" query from WINS named "$wins". Got "@dcs_of_domain"\n) ;
  exit $ERRORS{"CRITICAL"} ;
} elsif ( scalar @found_dcs < scalar keys %address2dc ) {
  print qq($PROGNAME WARNING: Not all domain controllers found in response to "$opt_D#1C" query from WINS named "$wins". Expected "@my_dcs", got "@found_dcs"\n) ;
  exit $ERRORS{"WARNING"} ;
}

sub set_eq {

  return 0 unless scalar @{$_[0]} == scalar @{$_[1]} ;
  foreach my $a ( @{$_[0]} ) {
    return 0 unless scalar grep { $a eq $_ } @{$_[1]} ;
  } 
  return 1 ;

}
