aboutsummaryrefslogtreecommitdiff
path: root/contrib/check_wins.pl
blob: 9ea7c45dd9286dd1798155817985fea6fab02a1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#!/usr/bin/perl -w

# $Id$

# $Log$
# 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
#

use strict ;

use Getopt::Long ;
use vars qw($opt_H $opt_D $opt_W $opt_T $debug @my_dcs);

use lib '/usr/local/nagios/libexec/' ;
use utils qw($TIMEOUT %ERRORS &print_revision &support &usage);

my $PROGNAME = 'check_wins' ;

use constant SAMBA_DEBUG_LVL	=> 2 ;
use constant MY_DCS 		=> ('dc1','dc2') ;
# use constant MY_DCS 		=> qw(ipa01 ipa02 ipa03) ;

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

sub print_help ();
sub print_usage ();
sub help ();
sub version ();

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

$SIG{"ALRM"} = sub { die "Alarm clock restart" } ;

Getopt::Long::Configure('bundling', 'no_ignore_case');
GetOptions
        ("V|version"     => \&version,
         "h|help"        => \&help,
         "d|debug"       => \$debug,
	 "C|controllers:s" => \@my_dcs,
         "T|timeout:i"	 => \$opt_T,
         "W|wins=s"	 => \$opt_W,
         "D|domain=s"    => \$opt_D);


($opt_D) || usage("MS Domain name not specified\n");
my $domain = $1 if $opt_D =~ m#(\w+)# ;                # NetBIOS names allow __any__ characters (more than \w)
($domain) || usage("Invalid MS D name: $opt_D\n");

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

usage("You must provide the names of your domain controllers by updating MY_DCS in the text or use -C dc_name1 -C dc_name2 ..\n")
  unless (@my_dcs or MY_DCS) ;

@my_dcs = MY_DCS	unless defined $my_dcs[0] ;
$TIMEOUT = $opt_T	if defined $opt_T ;

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>

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

if ($@ and $@ =~ /Alarm clock restart/) {
  print qq(Failed. Timeout while waiting for response from  (one of) "$NMBLOOKUP_CMD $wins @my_dcs"\n) ;
  exit $ERRORS{"CRITICAL"} ;
}

if ($@ and $@ !~ /Alarm clock restart/) {
  print qq(Failed. "$@" in response to "NMBLOOKUP_CMD $wins @my_dcs"\n) ;
  exit $ERRORS{"UNKNOWN"} ;
}

chomp @dc_query ;
if ( scalar grep(/name_query failed/, @dc_query) ) {
  print qq(Failed. WINS "$wins" failed to resolve), scalar @my_dcs > 1 ? ' at least one of ' : ' ', qq("@my_dcs", the domain controller(s) of "$domain". 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 = "$domain#1C"  ;

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

} ;

if ($@ and $@ =~ /Alarm clock restart/) {
  print qq(Failed. Timeout while waiting for response from "$NMBLOOKUP_CMD $wins $netbios_name"\n) ;
  exit $ERRORS{"CRITICAL"} ;
} 

if ($@ and $@ !~ /Alarm clock restart/) {
  print qq(Failed. "$@" in response to "$NMBLOOKUP_CMD $wins $netbios_name"\n) ;
  exit $ERRORS{"UNKNOWN"} ;
}

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} ;
# @found_dcs = grep { defined $_} @address2dc{ grep exists $address2dc{$_}, @addr_dcs_of_domain} ;
								# Gotcha.
								# 'exists' is necessary to prevent autovivificatiton
								# of keys in %address2dc

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

}


sub print_usage () {
	print "Usage: $PROGNAME -W <wins> -D <domain>\n";
}

sub print_help () {
	print_revision($PROGNAME,'$Revision$ ');
	print "Copyright (c) 2001 Karl DeBisschop/S Hopcroft

Perl Check WINS plugin for NetSaint.

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' 

Why would you want to do this ?

MS 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.

The plugin works by
  asking the WINS to resolve the addresses of the domain controllers (supplied by -C or from the constant MY_DCS)
  asking 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
  returning Ok		if all controller addresses are in that list (of addresses of domain controllers) or
  returning WARNING	if not all the controller addresses are in the list or
  returning CRITICAL	if there is no reply from the WINS or the list contains none of the contoller addresses

";
	print_usage();
	print '
-W, --wins=STRING
   Hostname or address of the WINS (Either Samba/nmbd or MS product)
-D, --domain=STRING
   MS Domain name to find the Domain controllers of.
-C, --controllers:STRING
   Optional __name(s)__ of domain controller that __must__ be found in the response to a domain controller name query.
   If not defined, then use the constant value MY_DCS. You must use either -C or make sure that MY_DCS contains the names 
   of __your__ domain controllers.
-T, --timeout:INTEGER
-d, --debug
   Debugging output.
-h, --help
   This stuff.

';
	support();
}

sub version () {
	print_revision($PROGNAME,'$Revision$ ');
	exit $ERRORS{'OK'};
}

sub help () {
	print_help();
	exit $ERRORS{'OK'};
}