6 # Revision 1.1 2005/01/25 09:05:53 stanleyhopcroft
7 # New plugin to check Citrix Metaframe XP "Program Neighbourhood"
9 # Revision 1.1 2005-01-25 16:50:30+11 anwsmh
17 use utils
qw($TIMEOUT %ERRORS &print_revision &support);
21 my $PROGNAME = 'check_program_neigbourhood' ;
22 my ($debug, $xml_debug, $pn_server, $pub_apps, $app_servers, $server_farm, $usage) ;
24 Getopt::Long::Configure('bundling', 'no_ignore_case') ;
26 ("V|version" => \&version,
27 "A|published_app:s" => \$pub_apps,
30 "F|server_farm=s" => \$server_farm,
31 "P|pn_server=s" => \$pn_server,
32 "S|app_server=s" => \$app_servers,
33 "v|verbose" => \$debug,
34 "x|xml_debug" => \$xml_debug,
38 print "Name or IP Address of _one_ Program Neighbourhood server is required.\n" ;
40 exit $ERRORS{UNKNOWN} ;
43 $pub_apps ||= 'Word 2003' ;
44 $pub_apps =~ s/["']//g ;
45 my @pub_apps = split /,\s*/, $pub_apps ;
47 my @app_servers = split /,\s*/, $app_servers ;
50 print "IP Address of _each_ Application server in the Metaframe Citrix XP server farm is required.\n" ;
52 exit $ERRORS{UNKNOWN} ;
55 my @non_ip_addresses = grep ! /\d+\.\d+\.\d+\.\d+/, @app_servers ;
57 scalar(@non_ip_addresses) && do {
58 print qq(Application servers must be specified by IP Address (not name): "@non_ip_addresses".\n) ;
60 exit $ERRORS{UNKNOWN} ;
64 print "Name of Citrix Metaframe XP server farm is required.\n" ;
66 exit $ERRORS{UNKNOWN} ;
72 my $xml_p = new XML::Parser(Handlers => {Start => \&handle_start,
73 End => sub { pop @tag_stack },
74 Char => \&handle_char}) ;
76 # values required by Metaframe XP that don't appear to matter too much
78 my $client_host = 'Nagios server (http://www.Nagios.ORG)' ;
79 my $user_name = 'nagios' ;
80 my $domain = 'Nagios_Uber_Alles' ;
82 # end values required by Metaframe XP
84 my $nilpotent_req = <<'EOR' ;
85 <?xml version="1.0" encoding="ISO-8859-1"?>
86 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd"><NFuseProtocol version="1.1">
88 <ServerAddress addresstype="dns-port" />
89 </RequestProtocolInfo>
93 my $server_farm_req = <<'EOR' ;
94 <?xml version="1.0" encoding="ISO-8859-1"?>
95 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
96 <NFuseProtocol version="1.1">
97 <RequestServerFarmData>
99 </RequestServerFarmData>
103 my $spec_server_farm_req = <<EOR ;
104 <?xml version="1.0" encoding="ISO-8859-1"?>
105 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
106 <NFuseProtocol version="1.1">
109 <UnspecifiedName>$server_farm*</UnspecifiedName>
111 <ClientName>$client_host</ClientName>
112 <ClientAddress addresstype="dns-port" />
113 <ServerAddress addresstype="dns-port" />
116 <UserName>$user_name</UserName>
117 <Domain>$domain</Domain>
123 my $app_req = <<EOR ;
124 <?xml version="1.0" encoding="ISO-8859-1"?>
125 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
126 <NFuseProtocol version="1.1">
129 <UnspecifiedName>PUBLISHED_APP_ENCODED</UnspecifiedName>
131 <ClientName>Nagios_Service_Check</ClientName>
132 <ClientAddress addresstype="dns-port"/>
133 <ServerAddress addresstype="dns-port" />
136 <UserName>$PROGNAME</UserName>
137 <Domain>$domain</Domain>
143 my $ua = LWP
::UserAgent
->new ;
144 my $req = HTTP
::Request
->new('POST', "http://$pn_server/scripts/WPnBr.dll") ;
145 $req->content_type('text/xml') ;
149 my @pubapp_encoded = map { my $x = $_ ; $x =~ s/(\W)/'&#' . ord($1) . ';'/eg; $x } @pub_apps ;
151 my $error_tag_cr = sub { ! exists($xml_tag{ErrorId
}) } ;
154 # { Content => url, Ok => ok_condition, Seq => \d+ }
156 { Content
=> $nilpotent_req, Ok
=> $error_tag_cr, Seq
=> 0 },
157 { Content
=> $server_farm_req, Ok
=> sub {
158 ! exists($xml_tag{ErrorId
}) &&
159 exists( $xml_tag{ServerFarmName
}) &&
160 defined($xml_tag{ServerFarmName
}) &&
161 $xml_tag{ServerFarmName
} eq $server_farm
163 { Content
=> $nilpotent_req, Ok
=> $error_tag_cr, Seq
=> 4 },
164 { Content
=> $spec_server_farm_req, Ok
=> sub {
165 ! exists($xml_tag{ErrorId
}) &&
166 exists( $xml_tag{ServerAddress
}) &&
167 defined($xml_tag{ServerAddress
}) &&
168 $xml_tag{ServerAddress
} =~ /\d+\.\d+\.\d+\.\d+:\d+/
170 { Content
=> $nilpotent_req, Ok
=> $error_tag_cr, Seq
=> 8 },
171 { Content
=> $app_req, Ok
=> sub {
172 ! exists($xml_tag{ErrorId
}) &&
173 exists( $xml_tag{ServerAddress
}) &&
174 defined($xml_tag{ServerAddress
}) &&
175 (($svr) = split(/:/, $xml_tag{ServerAddress
})) &&
177 scalar(grep $_ eq $svr, @app_servers)
183 foreach my $pub_app (@pub_apps) {
185 my $pubapp_enc = shift @pubapp_encoded ;
186 my $app_req_tmp = $app_reqs[5]{Content
} ;
187 $app_reqs[5]{Content
} =~ s/PUBLISHED_APP_ENCODED/$pubapp_enc/ ;
189 foreach (@app_reqs) {
191 $req->content($_->{Content
}) ;
193 $debug && print STDERR
"App: $pub_app Seq: $_->{Seq}\n", $req->as_string, "\n" ;
195 my $resp = $ua->request($req) ;
197 $debug && print STDERR
"App: $pub_app Seq: ", $_->{Seq
} + 1, "\n", $resp->as_string, "\n" ;
199 $resp->is_error && do {
200 my $err = $resp->as_string ;
202 &outahere
(qq(Failed
. HTTP error finding
$pub_app at seq
$_->{Seq
}: "$err")) ;
204 my $xml = $resp->content ;
207 ($xml_disp = $xml) =~ s/\n//g ;
208 $xml_disp =~ s/ \s+/ /g ;
210 &outahere
($resp->as_string)
213 my ($xml_ok, $whine) = &valid_xml
($xml_p, $xml) ;
215 $xml_ok || &outahere
(qq(Failed
. Bad XML finding
$pub_app at
eq $_->{Seq
} in "$xml_disp".)) ;
217 &{$_->{Ok
}} || &outahere
(qq(Failed
. \"\
&\
$_->{Ok
}\" false finding
$pub_app at seq
$_->{Seq
} in "$xml_disp".)) ;
219 # Ugly but alternative is $_->{Ok}->().
220 # eval $_->{Ok} where $_->{Ok} is an
221 # expression returning a bool is possible. but
222 # sub { } prevent recompilation.
226 $app_reqs[5]{Content
} = $app_req_tmp ;
228 $app_location .= qq("$pub_app" => $svr, ) ;
232 substr($app_location, -2, 2) = '' ;
233 print qq(Ok
. Citrix XML service located all published apps
$app_location.\n) ;
237 print "Citrix XML service $_[0]\n" ;
238 exit $ERRORS{CRITICAL
} ;
242 my ($p, $input) = @_ ;
251 return (0, qq(XML
::Parser
->parse failed
: Bad XML
in "$input".!))
255 print STDERR
pack('A4 A30 A40', ' ', $_, qq(-> "$xml_tag{$_}")), "\n"
256 foreach (keys %xml_tag)
259 return (1, 'valid xml')
265 push @tag_stack, $_[1] ;
267 $xml_debug && print STDERR
pack('A8 A30 A40', ' ', 'handle_start - tag', " -> '$_[1]'"), "\n" ;
268 $xml_debug && print STDERR
pack('A8 A30 A60', ' ', 'handle_start - @tag_stack', " -> (@tag_stack)"), "\n" ;
274 !($text =~ /\S/ || $text =~ /^[ \t]$/) && return ;
278 my $tag = $tag_stack[-1] ;
280 $xml_debug && print STDERR
pack('A8 A30 A30', ' ', 'handle_char - tag', " -> '$tag'"), "\n" ;
281 $xml_debug && print STDERR
pack('A8 A30 A40', ' ', 'handle_char - text', " -> '$text'"), "\n" ;
283 $xml_tag{$tag} .= $text ;
291 #12345678901234567890123456789012345678901234567890123456789012345678901234567890
293 print_revision
($PROGNAME,'$Revision$ ');
295 my $help = <<EOHELP ;
296 Copyright (c) 2004 Karl DeBisschop/S Hopcroft
298 $PROGNAME -P <pn_server> -S <svr1,svr2,..> -A <app1,app2,..>
299 -F <Farm> [-v -x -h -V]
301 Check the Citrix Metaframe XP service by completing an HTTP dialogue with a Program
302 Neigbourhood server (pn_server) that returns an ICA server in the named Server farm
303 hosting the specified applications (an ICA server in a farm which runs some MS app).
317 #12345678901234567890123456789012345678901234567890123456789012345678901234567890
319 my $usage = <<EOUSAGE ;
321 [-P | --pn_server] The name or address of the Citrix Metaframe XP
322 Program Neigbourhood server (required).
323 [-A | --pub_apps] The name or names of an application published by the
324 server farm (default 'Word 2003').
325 [-F | --server_farm] The name of a Citrix Metaframe XP server farm. (required)
326 [-S | --app_servers] The _IP addresses_ of _all_ of the Farms ICA servers expected
327 to host the published application.
328 Enter as a comma separated string eg 'Srv1, Svr2, ..,Srvn'.
329 Since the PN servers round-robin the app servers to the clients,
330 _all_ the server farm addresses must be specified or the check
331 will fail (required).
348 print_revision
($PROGNAME,'$Revision$ ');
359 This is the set of requests and responses transmitted between a Citrix Metaframe XP Program Neigbourhood (PN) client and a PN server.
361 This dialogue was captured by and reconstructed from tcpdump.
363 Citrix are not well known for documenting their protocols although the DTD may be informative. Note that the pair(s) 0 and 1, 4 and 5, ...
364 do not appear to do anything.
367 POST /scripts/WPnBr.dll HTTP/1.1
368 Content-type: text/xml
371 Connection: Keep-Alive
374 <?xml version="1.0" encoding="ISO-8859-1"?>
375 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
376 <NFuseProtocol version="1.1"><RequestProtocolInfo><ServerAddress addresstype="dns-port" /></RequestProtocolInfo></NFuseProtocol>
378 HTTP/1.1 100 Continue
379 Server: Citrix Web PN Server
380 Date: Thu, 30 Sep 2004 00:12:40 GMT
385 Server: Citrix Web PN Server
386 Date: Thu, 30 Sep 2004 00:12:40 GMT
387 Content-type: text/xml
391 <?xml version="1.0" encoding="ISO-8859-1" ?>
392 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
393 <NFuseProtocol version="1.1">
394 <ResponseProtocolInfo>
395 <ServerAddress addresstype="no-change"></ServerAddress>
396 </ResponseProtocolInfo>
400 POST /scripts/WPnBr.dll HTTP/1.1
401 Content-type: text/xml
404 Connection: Keep-Alive
407 <?xml version="1.0" encoding="ISO-8859-1"?>
408 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
409 <NFuseProtocol version="1.1"><RequestServerFarmData><Nil /></RequestServerFarmData></NFuseProtocol>
411 HTTP/1.1 100 Continue
412 Server: Citrix Web PN Server
413 Date: Thu, 30 Sep 2004 00:12:40 GMT
418 Server: Citrix Web PN Server
419 Date: Thu, 30 Sep 2004 00:12:40 GMT
420 Content-type: text/xml
424 <?xml version="1.0" encoding="ISO-8859-1" ?>
425 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
426 <NFuseProtocol version="1.1">
427 <ResponseServerFarmData>
429 <ServerFarmName>FOOFARM01</ServerFarmName>
431 </ResponseServerFarmData>
435 POST /scripts/WPnBr.dll HTTP/1.1
436 Content-type: text/xml
439 Connection: Keep-Alive
442 <?xml version="1.0" encoding="ISO-8859-1"?>
443 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
444 <NFuseProtocol version="1.1"><RequestProtocolInfo><ServerAddress addresstype="dns-port" /></RequestProtocolInfo></NFuseProtocol>
446 HTTP/1.1 100 Continue
447 Server: Citrix Web PN Server
448 Date: Thu, 30 Sep 2004 00:12:55 GMT
453 Server: Citrix Web PN Server
454 Date: Thu, 30 Sep 2004 00:12:55 GMT
455 Content-type: text/xml
459 <?xml version="1.0" encoding="ISO-8859-1" ?>
460 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
461 <NFuseProtocol version="1.1">
462 <ResponseProtocolInfo>
463 <ServerAddress addresstype="no-change"></ServerAddress>
464 </ResponseProtocolInfo>
468 POST /scripts/WPnBr.dll HTTP/1.1
469 Content-type: text/xml
472 Connection: Keep-Alive
475 <?xml version="1.0" encoding="ISO-8859-1"?>
476 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
477 <NFuseProtocol version="1.1">
478 <RequestAddress><Name>i
479 <UnspecifiedName>FOOFARM01*</UnspecifiedName>
480 </Name><ClientName>WS09535</ClientName>
481 <ClientAddress addresstype="dns-port" />
482 <ServerAddress addresstype="dns-port" />
485 <UserName>foo-user</UserName>
486 <Domain>some-domain</Domain>
488 </RequestAddress></NFuseProtocol>
490 HTTP/1.1 100 Continue
491 Server: Citrix Web PN Server
492 Date: Thu, 30 Sep 2004 00:12:56 GMT
497 Server: Citrix Web PN Server
498 Date: Thu, 30 Sep 2004 00:12:56 GMT
499 Content-type: text/xml
503 <?xml version="1.0" encoding="ISO-8859-1" ?>
504 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
505 <NFuseProtocol version="1.1">
507 <ServerAddress addresstype="dot-port">10.1.2.2:1494</ServerAddress>
508 <ServerType>win32</ServerType>
509 <ConnectionType>tcp</ConnectionType>
510 <ClientType>ica30</ClientType>
511 <TicketTag>10.1.2.2</TicketTag>
512 <SSLRelayAddress addresstype="dns-port">ica_svr01.some.domain:443</SSLRelayAddress>
517 POST /scripts/WPnBr.dll HTTP/1.1
518 Content-type: text/xml
521 Connection: Keep-Alive
524 <?xml version="1.0" encoding="ISO-8859-1"?>
525 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
526 <NFuseProtocol version="1.1"><RequestProtocolInfo><ServerAddress addresstype="dns-port" /></RequestProtocolInfo></NFuseProtocol>
528 HTTP/1.1 100 Continue
529 Server: Citrix Web PN Server
530 Date: Thu, 30 Sep 2004 00:13:29 GMT
535 Server: Citrix Web PN Server
536 Date: Thu, 30 Sep 2004 00:13:29 GMT
537 Content-type: text/xml
541 <?xml version="1.0" encoding="ISO-8859-1" ?>
542 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
543 <NFuseProtocol version="1.1">
544 <ResponseProtocolInfo>
545 <ServerAddress addresstype="no-change"></ServerAddress>
546 </ResponseProtocolInfo>
550 POST /scripts/WPnBr.dll HTTP/1.1
551 Content-type: text/xml
554 Connection: Keep-Alive
557 <?xml version="1.0" encoding="ISO-8859-1"?>
558 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
559 <NFuseProtocol version="1.1">
562 <UnspecifiedName>EXCEL#32;2003</UnspecifiedName>
564 <ClientName>WS09535</ClientName>
565 <ClientAddress addresstype="dns-port" />
566 <ServerAddress addresstype="dns-port" />
568 <Credentials><UserName>foo-user</UserName>
569 <Domain>some-domain</Domain>
574 HTTP/1.1 100 Continue
575 Server: Citrix Web PN Server
576 Date: Thu, 30 Sep 2004 00:13:29 GMT
581 Server: Citrix Web PN Server
582 Date: Thu, 30 Sep 2004 00:13:29 GMT
583 Content-type: text/xml
587 <?xml version="1.0" encoding="ISO-8859-1" ?>
588 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
589 <NFuseProtocol version="1.1">
591 <ServerAddress addresstype="dot-port">10.1.2.14:1494</ServerAddress>
592 <ServerType>win32</ServerType>
593 <ConnectionType>tcp</ConnectionType>
594 <ClientType>ica30</ClientType>
595 <TicketTag>10.1.2.14</TicketTag>
596 <SSLRelayAddress addresstype="dns-port">ica_svr02.some.domain:443</SSLRelayAddress>
600 ** One sees this XML on an error (there may well be other error XML also, but I haven't seen it) **
602 <?xml version="1.0" encoding="ISO-8859-1" ?>
603 <!DOCTYPE NFuseProtocol SYSTEM "NFuse.dtd">
604 <NFuseProtocol version="1.1">
606 <ErrorId>unspecified</ErrorId>
607 <BrowserError>0x0000000E</BrowserError>
617 # You never know when you may be embedded ...