File Coverage

blib/lib/Chooser.pm
Criterion Covered Total %
statement 21 247 8.5
branch 0 86 0.0
condition n/a
subroutine 7 16 43.7
pod 5 9 55.5
total 33 358 9.2


line stmt bran cond sub pod time code
1             package Chooser;
2              
3 1     1   20586 use warnings;
  1         4  
  1         38  
4 1     1   6 use strict;
  1         2  
  1         36  
5 1     1   6 use Exporter;
  1         8  
  1         56  
6 1     1   6 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         2  
  1         91  
7 1     1   1392 use IO::Socket::SSL;
  1         105996  
  1         9  
8 1     1   1172 use Sys::Hostname;
  1         1288  
  1         60  
9 1     1   984 use Text::NeatTemplate;
  1         3950  
  1         2532  
10              
11             our @ISA = qw(Exporter);
12             our @EXPORT = qw(choose);
13             our @EXPORT_OK = qw(choose);
14             our %EXPORT_TAGS = (DEFAULT => [qw(choose)]);
15              
16             sub argstohash{
17 0     0 0   my $argsString=$_[0];
18 0           my %vars=%{$_[1]};
  0            
19            
20 0           my @argsStringSplit=split(/\|/, $argsString);
21              
22 0           my %args=();
23              
24             #puts the hash together
25 0           my %targs;
26 0           $targs{hostname}=hostname;
27 0           $targs{pipe}='|';
28 0           $targs{newline}="\n";
29             #adds %ENV stuff
30 0           my @keys=keys(%ENV);
31 0           my $keysInt=0;
32 0           while (defined($keys[$keysInt])) {
33 0           $targs{'ENV'.$keys[$keysInt]}=$ENV{$keys[$keysInt]};
34              
35 0           $keysInt++;
36             }
37             #add the var stuff
38 0           @keys=keys(%vars);
39 0           $keysInt=0;
40 0           while (defined($keys[$keysInt])) {
41 0           $targs{'VAR'.$keys[$keysInt]}=$vars{$keys[$keysInt]};
42              
43 0           $keysInt++;
44             }
45              
46             #puts a hash of arguements together
47 0           my $argInt=0; #starting at 2 as it is the next in the line
48 0           while(defined($argsStringSplit[$argInt])){
49 0           my @argsplit=split(/=/, $argsStringSplit[$argInt], 2);
50            
51             #runs the template over it
52 0           my $tobj = Text::NeatTemplate->new();
53 0           $args{$argsplit[0]}=$tobj->fill_in(
54             data_hash=>\%targs,
55             template=>$argsplit[1],
56             );
57            
58 0           $argInt++;
59             }
60            
61 0           return %args;
62             }
63              
64             #checks if a check is good or not
65             sub checklegit{
66 0     0 0   my $check=$_[0];
67            
68 0 0         if (!defined($check)){
69 0           return undef;
70             }
71            
72 0           my @checks=("eval", "cidr", "hostregex", "defaultgateway",
73             "netidentflag", "pingmac", 'sslcert' );
74            
75 0           my $checksInt=0;
76 0           while($checks[$checksInt]){
77            
78 0 0         if ($checks[$checksInt] eq $check){
79 0           return 1
80             }
81            
82 0           $checksInt++;
83             }
84 0           return undef;
85             }
86              
87             sub runcheck{
88 0     0 0   my $check=$_[0];
89 0           my %args=%{$_[1]};
  0            
90            
91 0           my $returned=undef;
92 0           my $success=undef;
93            
94 0 0         if ($check eq "pingmac"){
95 0           ($success, $returned)=pingmac(\%args);
96             }
97            
98 0 0         if ($check eq "defgateway"){
99 0           ($success, $returned)=defgateway(\%args);
100             }
101            
102 0 0         if ($check eq "cidr"){
103 0           ($success, $returned)=cidr(\%args);
104             }
105            
106 0 0         if ($check eq "eval"){
107 0 0         if (defined($args{eval})){
108 0           ($success, $returned)=eval($args{eval});
109             }
110             }
111            
112 0 0         if ($check eq "hostregex"){
113 0           $returned=0;
114 0 0         if (hostname =~ /$args{regex}/){
115 0           $returned=1;
116             }
117 0           $success=1;
118             }
119            
120 0 0         if ($check eq 'sslcert') {
121 0           my $run=1;
122 0 0         if (!defined($args{host})) {
123 0           $run=undef;
124             }
125 0 0         if (!defined($args{subject})) {
126 0           $run=undef;
127             }
128 0 0         if (!defined($args{port})) {
129             }
130 0 0         if ($run) {
131 0           ($success, $returned)=sslcert(\%args);
132             }
133             }
134            
135 0 0         if($check eq "netidentflag"){
136 0           my $flagdir='/var/db/netident';
137            
138 0 0         if (defined($ENV{NETIDENTFLAGDIR})){
139 0           $flagdir=$ENV{NETIDENTFLAGDIR};
140             }
141            
142 0 0         if(defined{$args{flag}}){
143 0 0         if (-f $flagdir."/".$args{flag}){
144 0           $success=1;
145 0           $returned=1;
146             }else{
147 0           $returned=0;
148             }
149             }else{
150 0           $success=0;
151             }
152            
153             }
154            
155 0           return ($success, $returned);
156             }
157              
158             #do a default gateway test
159             sub defgateway{
160 0     0 1   my %args= %{$_[0]};
  0            
161            
162             #gets it and breaks it down to a string
163 0           my @raw=`route get default`;
164 0           my @gateway=grep(/gateway:/, @raw);
165 0           $gateway[0] =~ s/ //g;
166 0           $gateway[0] =~ s/gateway://g;
167 0           chomp($gateway[0]);
168              
169 0 0         if($args{ip} eq $gateway[0]){
170 0           return 1;
171             }
172              
173 0           return "0";
174             }
175              
176             #pings a ip address and checks the mac
177             sub pingmac{
178             # my $subargs = { %{$_[0]} };
179 0     0 1   my %args= %{$_[0]};
  0            
180            
181 0           system("ping -c 1 ".$args{ip}." > /dev/null");
182 0 0         if ( $? == 0 ){
183 0           my $arpline=`arp $args{ip}`;
184 0           my @a=split(/ at /, $arpline);
185 0           my @b=split(/ on /, $a[1]);
186 0 0         if ($b[0] eq $args{mac}){
187 0           return "1";
188             }
189             }
190            
191 0           return "0";
192             }
193              
194             #do a default gateway test
195             sub cidr{
196 0     0 1   my %args= %{$_[0]};
  0            
197            
198 0           my $cidr = Net::CIDR::Lite->new;
199            
200 0           $cidr->add($args{cidr});
201            
202 0           my $socket = IO::Socket::INET->new(Proto=>'udp');
203            
204 0           my @iflist=$socket->if_list();
205            
206             #if a interface is not specified, make sure it exists
207 0 0         if(defined($args{if})){
208 0           my $iflistInt=0;#used for intering through @iflist
209 0           while(defined($iflist[$iflistInt])){
210             #checks if this is the interface in question
211 0 0         if($iflist[$iflistInt] eq $args{if}){
212             #gets the address
213 0           my $address=$socket->if_addr($args{if});
214             #if the interface does not have a address, don't check it
215 0 0         if(defined($address)){
216             #checks this address is with in this cidr
217 0 0         if ($cidr->find($address)){
218 0           return 1;
219             }
220             }
221             }
222            
223 0           $iflistInt++;
224             }
225            
226             #if a specific IP is defined and it reaches this point, it means it was now found
227 0           return "0";
228             }
229              
230             #if a interface is not specified, make sure it exists
231 0           my $iflistInt=0;#used for intering through @iflist
232 0           while(defined($iflist[$iflistInt])){
233             #gets the address
234 0           my $address=$socket->if_addr($iflist[$iflistInt]);
235             #if the interface does not have a address, don't check it
236 0 0         if(defined($address)){
237             #checks this address is with in this cidr
238 0 0         if ($cidr->find($address)){
239 0           return 1;
240             }
241             }
242 0           $iflistInt++;
243             }
244              
245 0           return "0";
246             }
247              
248             #handles the the sslcert test
249             sub sslcert{
250 0     0 1   my %args=%{$_[0]};
  0            
251              
252 0           my $client=IO::Socket::SSL->new( $args{host}.':'.$args{port},
253             SSL_version=>$args{version},
254             SSL_cipher_list=>$args{cipher_list},
255             SSL_ca_file=>$args{ca_file},
256             SSL_ca_path=>$args{ca_path},
257             SSL_crl_file=>$args{crl_file},
258             SSL_verify_mode=>$args{verify_mode},
259             SSL_verifycn_name=>$args{verifycn_name},
260             SSL_verifycn_scheme=>$args{verifycn_scheme},
261             );
262              
263 0 0         if (!$client) {
264 0           return 0;
265             }
266              
267 0           my $certinfo=$client->dump_peer_certificate;
268              
269             # 0 is the subject
270             # 1 is the issuer
271 0           my @certinfoA=split(/\n/, $certinfo);
272              
273             #process the subject
274 0           my $subject=$certinfoA[0];
275 0           $subject=~s/^Subject\ Name\:\ //;
276              
277 0 0         if ($args{subject} ne $subject) {
278 0           return 0
279             }
280              
281             #process the issuer
282 0 0         if (defined($args{issuer})) {
283 0           my $issuer=$certinfoA[1];
284 0           $issuer=s/^Issuer\ \ Name\:\ //g;
285              
286 0 0         if ($args{issuer} ne $issuer) {
287 0           return 0
288             }
289             }
290              
291             #it is all good
292 0           return 1;
293             }
294              
295             #process the value
296             sub valueProcess{
297 0     0 0   my $value=$_[0];
298 0           my $returned=$_[1];
299 0           my %vars=%{$_[2]};
  0            
300            
301 0 0         if (!$value =~ /^\%/){
302 0           return $value;
303             }
304            
305             #puts the hash together
306 0           my %targs;
307 0           $targs{returned}=$returned;
308 0           $targs{value}=$value;
309 0           $targs{hostname}=hostname;
310 0           $targs{pipe}='|';
311 0           $targs{newline}="\n";
312             #adds %ENV stuff
313 0           my @keys=keys(%ENV);
314 0           my $keysInt=0;
315 0           while (defined($keys[$keysInt])) {
316 0           $targs{'ENV'.$keys[$keysInt]}=$ENV{$keys[$keysInt]};
317              
318 0           $keysInt++;
319             }
320 0           @keys=keys(%vars);
321 0           $keysInt=0;
322 0           while (defined($keys[$keysInt])) {
323 0           $targs{'VAR'.$keys[$keysInt]}=$vars{$keys[$keysInt]};
324              
325 0           $keysInt++;
326             }
327              
328             #works just like %env{whatever} in perl
329 0           my $doeval=0;
330 0 0         if ($value =~ /^\%eval\{/){
331 0           $value =~ s/\%eval\{//g;
332 0           $value =~ s/\}$//g;
333 0           $doeval=1;
334              
335 0           return eval($value);
336             }
337              
338             #runs the template over it
339 0           my $tobj = Text::NeatTemplate->new();
340 0           $value=$tobj->fill_in(
341             data_hash=>\%targs,
342             template=>$value,
343             );
344              
345             #eval it if needed
346 0 0         if ($doeval) {
347 0           return eval($value);
348             }
349            
350 0           return $value;
351             }
352              
353             =head1 NAME
354              
355             Chooser - A system for choosing a value for something. Takes a string composed of various tests, arguements, and etc and returns a value based on it.
356              
357             =head1 VERSION
358              
359             Version 2.0.0
360              
361             =cut
362              
363             our $VERSION = '2.0.0';
364              
365              
366             =head1 SYNOPSIS
367              
368             Takes a string composed of various tests, arguements, and etc and
369             returns a value based on it. See FORMATTING for more information on
370             the string.
371              
372             use Chooser;
373              
374             #The first tests if /test/ matches the hostname. If it does
375             # a value of test is set with a wieght of 42. This makes
376             #it heavier so even if another is matched, this will be returned.
377             #
378             #The second test checks to make sure that no interfaces have a
379             #CIDR of 192.168.0.0/16. If it does a value of not192168 is returned.
380             #
381             #The third tests if
382             my $string="hostregex|1|test|42|regex=test\n".
383             "cidr|0|not192168|1|cidr=192.168.0.0/16".
384             "defgateway|0|19216801|1|ip=192.168.0.1"
385              
386             my ($success, $choosen) = choose($string);
387             if(!$success){
388             print "The choosen value is '".$choosen."'\n";
389             }else{
390             print "Chooser hit a error processing...\n".$string."\n";
391             };
392             ...
393              
394             =head1 EXPORT
395              
396             chooose
397              
398             =head1 FUNCTIONS
399              
400             =head2 choose
401              
402             This function is used for running a chooser string. See FORMATING for information
403             on the string passed to it.
404              
405             If any of the lines in the string contain errors, choose returns a error.
406              
407             There are three returned values. The first return is a bolean for if it succedded or not. The
408             second is the choosen value. The third is the wieght of the returned value.
409              
410             =cut
411              
412             #parse and run a string
413             sub choose{
414 0     0 1   my $string=$_[0];
415              
416 0 0         if (!defined($string)){
417 0           return (0, undef);
418             }
419              
420 0           my @rawdata=split(/\n/, $string);
421              
422 0           my $value;
423 0           my %values=();
424              
425 0           my %vars;
426            
427 0           my $int=0;
428 0           while(defined($rawdata[$int])){
429 0           my $line=$rawdata[$int];
430 0           chomp($line);
431              
432 0 0         if ($line =~ /^\$/) {
433 0           $line=~s/^\$//;
434 0           my ($variable, $data)=split(/\=/, $line, 2);
435              
436 0 0         if (defined($data)) {
437             #puts the hash together
438 0           my %targs;
439 0           $targs{hostname}=hostname;
440 0           $targs{pipe}='|';
441 0           $targs{newline}="\n";
442             #adds %ENV stuff
443 0           my @keys=keys(%ENV);
444 0           my $keysInt=0;
445 0           while (defined($keys[$keysInt])) {
446 0           $targs{'ENV'.$keys[$keysInt]}=$ENV{$keys[$keysInt]};
447            
448 0           $keysInt++;
449             }
450 0           @keys=keys(%vars);
451 0           $keysInt=0;
452 0           while (defined($keys[$keysInt])) {
453 0           $targs{'VAR'.$keys[$keysInt]}=$vars{$keys[$keysInt]};
454            
455 0           $keysInt++;
456             }
457            
458             #runs the template over it
459 0           my $tobj = Text::NeatTemplate->new();
460 0           $vars{$variable}=$tobj->fill_in(
461             data_hash=>\%targs,
462             template=>$data,
463             );
464             }
465             }else {
466 0           my ($check, $restofline)=split(/\|/, $line, 2);
467 0           (my $expect, $restofline)=split(/\|/, $restofline, 2);
468 0           (my $value, $restofline)=split(/\|/, $restofline, 2);
469 0           (my $wieght, $restofline)=split(/\|/, $restofline, 2);
470 0           (my $argsString, $restofline)=split(/\|/, $restofline, 2);
471 0           my %args=argstohash($argsString,\%vars);
472            
473 0 0         if (!defined($wieght)){
474 0           $wieght=0
475             }
476            
477             #if the check is legit, run it
478 0 0         if(checklegit($check)){
479 0           my ($success, $returned)=runcheck($check, \%args, \%vars);
480             #makes sure the check was sucessful
481 0 0         if ($success){
482 0 0         if ($returned eq $expect){
483 0           $value=valueProcess($value, $returned, \%vars);
484 0           $values{$value}=$wieght;
485             }
486             }
487            
488             }else{
489 0           return 0;
490             }
491             }
492              
493 0           $int++;
494             }
495              
496             #finds the heaviest value
497 0           my @keys=keys(%values);
498 0           my $keysInt=0;
499 0 0         if(defined($keys[$keysInt])){
500 0           $value=$keys[$keysInt];
501 0           my $lastwieght=$values{$keys[$keysInt]};
502 0           while(defined($keys[$keysInt])){
503             #if the value is heavier or equal to the last one, use it
504 0 0         if ($values{$keys[$keysInt]} >= $lastwieght){
505 0           $value=$keys[$keysInt];
506             }
507 0           $keysInt++;
508             }
509             }
510              
511 0           return (1, $value, $values{$value});
512             }
513              
514             =head1 FORMATTING
515              
516             $variable=data
517             ||||=|=...
518              
519             '|' is used a delimiter and there is no whitespace.
520              
521             For information on the support checks, see the CHECK sections.
522              
523             The expect section is the expected turn value for a check. Unless stated other wise
524             it is going to be '0' for false and '1' for true.
525              
526             The value is the return value for if it is true. The eventual returned one is choosen
527             by the wieght. The highest number takes presdence. If equal, the last value is used.
528              
529             The wieght is the way for the returned value.
530              
531             The args are every thing after the wieght. Any thing before the first '=' is considered
532             part of the variable name. The variable name is case sensitive. Everything after the first
533             '=' is considered part of the value of the variable.
534              
535             Both the values and arg values support templating. Templating is done via Text::NeatTemplate.
536              
537             In regards to a choosen value matching /\%eval\{.*\}/, '%eval{' is removed as well as the
538             trailing '}' and it is evaled. So for example '%eval{return "44";}' would set the value to
539             '44'.
540              
541             Any line that starts with a '$' is a variable. These can be included in stuff via the
542             template system.
543              
544             =head1 CHECKS
545              
546             =head2 cidr
547              
548             This checks if a specific interface or any of them have a address that matches a given CIDR.
549              
550             =head3 args
551              
552             =head4 cidr
553              
554             The arguement "cidr" is CIDR to be matched.
555              
556             =head4 if
557              
558             The arguement "if" is optional arguement for the interface.
559              
560             =head2 defgateway
561              
562             This checks the routing table for the default route and compares it to passed variable.
563              
564             =head3 args
565              
566             =head4 ip
567              
568             The arguement "ip" is used for the default gateway.
569              
570             =head2 eval
571              
572             This runs some perl code. This requires two things being returned. The first
573             thing that needs returned is success of check. This is if the if there as a error
574             or not with the check. It needs to return true or the choose function returns with
575             an error condition. The second returned value is the value that is checked against
576             expect value.
577              
578             =head3 args
579              
580             =head4 eval
581              
582             The arguement "eval" is the arguement that contains the code used for this.
583              
584             =head2 hostregex
585              
586             This runs a regex over the hostname and turns true if it matches.
587              
588             =head3 args
589              
590             =head4 regex
591              
592             The arguement "regex" is the regex to use.
593              
594             =head2 netidentflag
595              
596             This tests to see if a flag created by netident is present. The directory used is the
597             default netident flag directory, unless the enviromental variable 'NETIDENTFLAGDIR' is
598             set.
599              
600             The arguement "flag" is used to specify the flag to look for.
601              
602             =head2 pingmac
603              
604             This test pings a IP to make sure it is in the ARP table and then checks to see if the MAC maches.
605              
606             =head3 args
607              
608             =head4 ip
609              
610             The IP to ping
611              
612             =head4 mac
613              
614             The MAC to check for.
615              
616             =head2 sslcert
617              
618             =head3 args
619              
620             To get the values to for the subject and issure, use the
621             code below and use everything after /\: /.
622              
623             use IO::Socket::SSL;
624             my $client->new($host.':'.$port);
625             print $client->dump_peer_certificate;
626              
627             The required values are listed below.
628              
629             host
630             port
631             subject
632              
633             For more information about most of these options, please
634             see the documentation for IO::Socket::SSL for the new
635             method.
636              
637             =head4 CAfile
638              
639             The CA file to use.
640              
641             =head4 CApath
642              
643             CA path to use.
644              
645             =head4 check_crl
646              
647             Check to see if it has been revoked.
648              
649             =head4 cipher_list
650              
651             The cipher list to use.
652              
653             =head4 crl_file
654              
655             The CRL file to use.
656              
657             =head4 host
658              
659             This is either the hostname or IP address to connect to.
660              
661             =head4 port
662              
663             This is the port to connect to.
664              
665             =head4 subject
666              
667             This is the subject name to check for. To get what this should be, run the
668             code below.
669              
670             =head4 verify_mode
671              
672             The verify mode to use.
673              
674             =head4 verifycn_name
675              
676             The name to use to verify the hostname.
677              
678             =head4 verifycn_scheme
679              
680             The scheme to use when verifying the hostname.
681              
682             =head4 version
683              
684             The SSL version to use.
685              
686             =head1 TEMPLATING
687              
688             Templating for choosen values and arg values is done using Text::NeatTemplate.
689              
690             =head2 TEMPLATE KEYS
691              
692             =head3 {$ENV*}
693              
694             All enviromental variables have 'ENV' appended to them in the hash ref that
695             is passed to Text::NeatTemplate.
696              
697             =head3 {$hostname}
698              
699             This is the hostname of the machine it is running on.
700              
701             =head3 {$newline}
702              
703             This inserts a "\n".
704              
705             =head3 {$returned}
706              
707             This is the returned value of a check. This is only present if a value is being
708             processed.
709              
710             =head3 {$pipe}
711              
712             This inserts a '|'.
713              
714             =head3 {$value}
715              
716             This is the raw value string. This is only present if a value is being processed.
717              
718             =head3 {$VAR*}
719              
720             This adds in any variables.
721              
722             =head1 AUTHOR
723              
724             Zane C. Bowers, C<< >>
725              
726             =head1 BUGS
727              
728             Please report any bugs or feature requests to C, or through
729             the web interface at L. I will be notified, and then you'll
730             automatically be notified of progress on your bug as I make changes.
731              
732              
733              
734              
735             =head1 SUPPORT
736              
737             You can find documentation for this module with the perldoc command.
738              
739             perldoc Chooser
740              
741              
742             You can also look for information at:
743              
744             =over 4
745              
746             =item * RT: CPAN's request tracker
747              
748             L
749              
750             =item * AnnoCPAN: Annotated CPAN documentation
751              
752             L
753              
754             =item * CPAN Ratings
755              
756             L
757              
758             =item * Search CPAN
759              
760             L
761              
762             =back
763              
764              
765              
766             =head1 COPYRIGHT & LICENSE
767              
768             Copyright 2009 Zane C. Bowers, all rights reserved.
769              
770             This program is free software; you can redistribute it and/or modify it
771             under the same terms as Perl itself.
772              
773              
774             =cut
775              
776             1; # End of Chooser