Code

Fix for regex input of '|', being output causing problems with Nagios' parsing of
[nagiosplug.git] / contrib / check_ica_master_browser.pl
1 #!/usr/bin/perl -w
3 # $Id: check_ica_master_browser.pl 1099 2005-01-25 09:09:33Z stanleyhopcroft $
5 # Revision 1.1  2005/01/25 09:09:33  stanleyhopcroft
6 # New plugin - checks that ICA master browser is what it should be (important for firewalled dialup)
7 #
9 use strict ;
11 use IO::Socket;
12 use IO::Select;
13 use Getopt::Long ;
15 use lib qw(/usr/local/nagios/libexec) ;
16 use utils qw(%ERRORS &print_revision &support &usage);
17 use packet_utils qw(&pdump &tethereal) ;
19 my $PROGNAME = 'check_ica_master_browser' ;
21 # You might have to change this...
23 my $PACKET_TIMEOUT      = 1;
24                                                                                                 # Number of seconds to wait for further UDP packets
25 my $TEST_COUNT          = 2;
26                                                                                                 # Number of datagrams sent without reply 
27 my $BUFFER_SIZE         = 1500;
28                                                                                                 # buffer size used for 'recv' calls.
29 my $ICA_PORT            = 1604;
30                                                                                                 # what port ICA runs on. Unlikely to change.
32 # End user config.
34 my ($debug, $preferred_master, $bcast_addr, $ica_browser, $timeout) ;
36 Getopt::Long::Configure('bundling', 'no_ignore_case');
37 GetOptions
38         ("V|version"     => \&version,
39          "h|help"        => \&help,
40          "v|verbose"     => \$debug,
41          "B|broadcast_addr:s"   => \$bcast_addr,
42          "I|ica_browser:s"      => \$ica_browser,
43          "P|preferred_master:s" => \$preferred_master,
44          "T|Packet_timeout:i"   => \$timeout,
45 ) ;
48 my $broadcast_addr = $1 if $bcast_addr and $bcast_addr =~ m#(\d+\.\d+\.\d+\.\d+)# ;
49 usage("Invalid broadcast address: $bcast_addr")
50         if $bcast_addr and not defined($broadcast_addr)  ;
52 usage("You must provide either the name of an ICA browser or the broadcast address of the subnet containing them\n")
53         unless ($ica_browser or $broadcast_addr) ;
55 usage("You must provide the name or address of a preferred ICA master browser\n")
56         unless ($preferred_master) ;
58 my $preferred_master_n = $preferred_master =~ m#(\d+\.\d+\.\d+\.\d+)#
59         ? $preferred_master
60         : inet_ntoa(scalar gethostbyname($preferred_master)) ;
62 my $Timeout = $timeout || $PACKET_TIMEOUT ;
64                                                                                                 # Definitions of query strings. Change at your own risk :)
65                                                                                                 # this info was gathered with tcpdump whilst trying to use an ICA client,
66                                                                                                 # so I'm not 100% sure of what each value is.
68 my $bcast_helo = &tethereal(<<'End_of_Tethereal_trace', '1e') ;
69 0020  ff ff 04 d6 06 44 00 26 4a 76 1e 00 01 30 02 fd   .....D.&Jv...0..
70 0030  a8 e3 00 02 f5 95 9f f5 30 07 00 00 00 00 00 00   ........0.......
71 0040  00 00 00 00 00 00 01 00                           ........
72 End_of_Tethereal_trace
74 my $direct_helo = &tethereal(<<'End_of_Tethereal_trace', '20') ;
75 0020  64 17 05 0f 06 44 00 28 ab b5 20 00 01 30 02 fd   d....D.(.. ..0..
76 0030  a8 e3 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
77 0040  00 00 00 00 00 00 00 00 00 00                     ........
78 End_of_Tethereal_trace
80 my $Udp =  IO::Socket::INET->new( Proto => 'udp' )
81         || die "Socket failure: $!";
83                                                                                                 # select is here to allow us to set timeouts on the connections.  Otherwise they 
84                                                                                                 # just 'stop' until a server appears.
86 my $select =  IO::Select->new($Udp)
87         || die "Select failure: $!";
89 $Udp->sockopt(SO_BROADCAST, 1 );
91 my ($remote_host, $buff, $destination, $raddr, $rport, $rhost, @remote_response);
92 my ($query_message, $send_addr, $this_test) ;
94 $buff = '';
95 $this_test = 0;
97                                                                                                 # If there is no response to the first helo packet it will be resent
98                                                                                                 # up to $TEST_COUNT (see at the top).
100 $query_message = $broadcast_addr ? $bcast_helo : $direct_helo ;
101 $destination   = $broadcast_addr ? $broadcast_addr: $ica_browser ;
102 $send_addr = sockaddr_in($ICA_PORT, inet_aton($destination) ) ;
104 while ( ++$this_test <= $TEST_COUNT && !$buff ) {
106         print "Sending helo datagram. datagram number: ", $this_test, "\n"
107                 if $debug ;
109         print "Querying $destination for master browser\n"
110                 if  $debug  ;
111         &pdump($query_message)
112                 if $debug ;
113         $Udp->send($query_message, 0, $send_addr ); 
114         if ( $select->can_read($Timeout) ) {
115                 $remote_host = $Udp->recv($buff, $BUFFER_SIZE, 0 );
116         }
118         last
119                 if $buff ;
120         sleep 1 ;
122
124                                                                                                 # Ok we've looped several times, looking for a response. If we don't have one 
125                                                                                                 # yet, we simply mark the whole lot as being unavailable.
127 unless ( $buff ) {
128         print "Failed. No response to helo datagram (master browser query) from $destination.\n" ;
129         exit $ERRORS{CRITICAL} ;
132 ($rport, $raddr) = sockaddr_in( $remote_host );
133 $rhost = gethostbyaddr( $raddr, AF_INET );
134 my @tmpbuf = unpack('C*', $buff );
135 if ( $debug ) {
136         print "$rhost:$rport responded with: ",length($buff), " bytes\n";
137         &pdump($buff) ;
140                                                                                                 # Now we have a response, then we need to figure out the master browser, and 
141                                                                                                 # query it for published applications...
143 my $master_browser = join '.', @tmpbuf[32..35] ;
144 my ($master_browser_a) = gethostbyaddr(inet_aton($master_browser), AF_INET) =~ /^(\w+?)\./ ;
145      
146                                                                                                 # Ok should probably error check this, because it's remotely possible
147                                                                                                 # that a server response might be completely wrong...
148       
149 print "Master browser = $master_browser_a/$master_browser\n"
150         if  $debug ;
152 $send_addr = sockaddr_in($ICA_PORT, inet_aton($master_browser));
154 my $subject_clause = $bcast_addr ? "of the \"$destination\" subnet" : "known to ICA server \"$destination\"" ;
156 if ( $master_browser eq  $preferred_master_n ) {
157         print "Preferred master browser \"$preferred_master\" __is__ the master browser (\"$master_browser_a/$master_browser\") $subject_clause.\n" ;
158         exit $ERRORS{OK} ;
159 } else {
160         print "\"\u$preferred_master\" is __not__ the master browser (\"$master_browser_a/$master_browser\") $subject_clause: remote clients (dialup) may not find Published applications from Master Browser.\n" ;
161         exit $ERRORS{CRITICAL} ;
164 close $Udp;
167 sub print_usage () {
168         print "Usage: $PROGNAME (-B <broadcast_address>| -I <citrix_server>) - P <preferred_master_browser>" ;
171 sub print_help () {
172         print_revision($PROGNAME,'$Revision: 1099 $ ');
173         print "Copyright (c) 2002 Ed Rolison/Tom De Blende/S Hopcroft
175 Perl Check Citrix Master Browser plugin for Nagios.
177 Returns OK if the Citrix master browser is that given by the -P option.
179 The plugin works by
180   If the -B option is specified, sends a broadcast helo to find the address of the Citrix master browser in the specified subnet.
181     return critical if there is no reply;
182   Else if the -I option is specified 
183     send a direct helo to the specified server until there is a response (containing the address of the Citrix master browser)
186   return Critical if the response does not contain the address of the 'preferred master browser' (-P option).
187   return OK
189   How ICA Clients Use the Master ICA Browser.
191 Citrix ICA Clients must locate the master browser to get the address of a server or published application.
193 The Citrix ICA Client can locate the master browser by sending out broadcast packets, or,
194 if the address of a Citrix server is specified in the Citrix ICA Client or in an ICA file,
195 the ICA Client locates the master browser by sending directed packets to the specified address.
196 The ICA Client requests the address of the ICA master browser from the Citrix server.
198 ";
199         print_usage();
200         print '
201 -B, --broadcast_address:STRING
202    The broadcast address that should contain Citrix master browser. This option takes precedence over -I.
203 -I, --ica_browser:STRING
204    Optional name or address of an ICA server that could be the master browser (used when broadcast not possible).
205 -P, --preferred_master:STRING
206    Name or address of the ICA server that _should_ be the master browser.
207    Required.
208 -T, --packet-timeout:INTEGER
209    Time to wait for UDP packets (default 1 sec).
210 -v, --verbose
211    Debugging output.
212 -h, --help
213    This stuff.
215 ';
216         support();
219 sub version () {
220         print_revision($PROGNAME,'$Revision: 1099 $ ');
221         exit $ERRORS{'OK'};
224 sub help () {
225         print_help();
226         exit $ERRORS{'OK'};