#!/usr/bin/perl #Copyright (c) 2008, Zane C. Bowers #All rights reserved. # #Redistribution and use in source and binary forms, with or without modification, #are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # #THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. #IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, #INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, #BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, #DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF #LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR #OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF #THE POSSIBILITY OF SUCH DAMAGE. use strict; use warnings; use Getopt::Std; use IO::Socket; use IO::Interface; use Net::CIDR::Lite; #use Net::DHCPClient; #print version sub main::VERSION_MESSAGE { print "netident 0.1.0\n"; }; #exit after printing help or version $Getopt::Std::STANDARD_HELP_VERSION="TRUE"; #print help sub main::HELP_MESSAGE { print "-f The file to use.\n". "-d The directory with test files in it to run.\n". "-F The directory to use that contains the flag files.\n". "-h Print this\n.". "-v Print the version.\n"; }; sub runline{ my $check=$_[0]; my $expect=$_[1]; my %args=%{$_[2]}; my $returned=undef; if ($check eq "pingmac"){ $returned=pingmac(\%args); }; if ($check eq "defgateway"){ $returned=defgateway(\%args); }; if ($check eq "cidr"){ $returned=cidr(\%args); }; if ($returned eq $expect){ return 1; }; return 0; }; #parse the name line of the file sub parseName{ #$_[0]=the first line of a my $name=$_[0]; chomp($name); my $check=$name; if($check =~ s/\///g){ print "The name contained a /, which is a illegal character.\n"; exit 1; }; return($name); }; #parse and run a file sub runfile{ #$_[0]=the file open("thefile", $_[0])||die("Could not open '".$_[0]."'!"); my @rawdata=; close("thefile"); my $name=parseName($rawdata[0]); my $int=1; #starts at one since line 0 has been hit already while(defined($rawdata[$int])){ my $line=$rawdata[$int]; chomp($line); my @linesplit=split(/\|/, $line); my $check=$linesplit[0]; #the check to run my $expect=$linesplit[1]; #if true or false should be expected for a test my %args=(); #puts a hash of arguements together my $argInt=1; #starting at 2 as it is the next in the line while(defined($linesplit[$argInt])){ my @argsplit=split(/=/, $linesplit[$argInt]); $args{$argsplit[0]}=""; my $argsAssembleInt=1; while(defined $argsplit[$argsAssembleInt]){ #Adds = onto the arg if it is more than one. #This is placed here add = back into it and then only after the first one. if($argsAssembleInt > 1){ $args{$argsplit[0]}=$args{$argsplit[0]}."="; }; $args{$argsplit[0]}=$argsplit[$argsAssembleInt]; $argsAssembleInt++; }; $argInt++; }; my $lineReturn=runline($check, $expect, \%args); if(!$lineReturn){ return (0, $name); }; $int++; }; return (1, $name); }; #pings a ip address and checks the mac sub pingmac{ # my $subargs = { %{$_[0]} }; my %args= %{$_[0]}; system("ping -c 1 ".$args{ip}." > /dev/null"); if ( $? == 0 ){ my $arpline=`arp $args{ip}`; my @a=split(/ at /, $arpline); my @b=split(/ on /, $a[1]); if ($b[0] eq $args{mac}){ return "1"; print "it works\n"; }; }; return "0"; }; #do a default gateway test sub defgateway{ my %args= %{$_[0]}; #gets it and breaks it down to a string my @raw=`route get default`; my @gateway=grep(/gateway:/, @raw); $gateway[0] =~ s/ //g; $gateway[0] =~ s/gateway://g; chomp($gateway[0]); if($args{ip} eq $gateway[0]){ return 1; }; return "0"; }; #do a default gateway test sub cidr{ my %args= %{$_[0]}; my $cidr = Net::CIDR::Lite->new; $cidr->add($args{cidr}); my $socket = IO::Socket::INET->new(Proto=>'udp'); my @iflist=$socket->if_list(); #if a interface is not specified, make sure it exists if(defined($args{if})){ my $iflistInt=0;#used for intering through @iflist while(defined($iflist[$iflistInt])){ #checks if this is the interface in question if($iflist[$iflistInt] eq $args{if}){ #gets the address my $address=$socket->if_addr($args{if}); #if the interface does not have a address, don't check it if(defined($address)){ #checks this address is with in this cidr if ($cidr->find($address)){ return 1; }; }; }; $iflistInt++; }; #if a specific IP is defined and it reaches this point, it means it was now found return "0"; }; #if a interface is not specified, make sure it exists my $iflistInt=0;#used for intering through @iflist while(defined($iflist[$iflistInt])){ #gets the address my $address=$socket->if_addr($iflist[$iflistInt]); #if the interface does not have a address, don't check it if(defined($address)){ #checks this address is with in this cidr if ($cidr->find($address)){ return 1; }; }; $iflistInt++; }; return "0"; }; #holds the config my %config; #get the ops my %opts; getopts("d:f:F:hv", \%opts); if (defined($opts{h})){ &main::VERSION_MESSAGE; &main::HELP_MESSAGE; exit; }; if (defined($opts{v})){ &main::VERSION_MESSAGE; exit; }; #sets the file it will use as the config if(defined($opts{f})){ if (defined($opts{d})){ print "Can't be defined with -d and -f.\n"; }; $config{file}=$opts{f}; }else{ #sets the directory it reads the configs from if(!defined($opts{d})){ $config{dir}="/usr/local/etc/netident/"; if(!-e $config{dir}){ print $config{dir}." does not exist.\n"; exit 1; }; }else{ $config{dir}=$opts{d}; if(!-e $config{dir}){ print $config{dir}." does not exist.\n"; exit 1; }; }; }; #get the flags dir if(defined($opts{F})){ $config{flagdir}=$opts{F}; }else{ $config{flagdir}='/var/db/netident'; }; #make sure the flag dir exists if (!-d $config{flagdir}){ if(!mkdir($config{flagdir})){ print "Could not create the flag directory, '".$config{flagdir}."'.\n"; exit 1; }; }; if(defined($config{file})){ my ($returned, $name)=runfile($config{file}); #create the flag file if true #remove it if false and it exists if($returned){ if(open("FLAGFILE", '>', $config{flagdir}."/".$name)){ print FLAGFILE ""; close("FLAGFILE"); exit 0; }else{ print "Could not not create flag file '".$config{flagdir}."/".$name."'.\n"; exit 1; }; }else{ if(-e $config{flagdir}."/".$name){ if(!unlink($config{flagdir}."/".$name)){ print "Could not not remove flag file '".$config{flagdir}."/".$name."'.\n"; exit 1; }; }; exit 0; }; }; if (defined($config{dir})){ #reads the directory and get the list of test file if(!opendir(TESTDIR, $config{dir})){ print "Could not open directory ".$config{dir}."'\n"; exit 1; }; my @testfiles=readdir(TESTDIR); closedir(TESTDIR); @testfiles=grep(!/^\./, @testfiles); #tests all the files my $testfilesInt=0; while(defined($testfiles[$testfilesInt])){ #tests the file my ($returned, $name)=runfile($config{dir}."/".$testfiles[$testfilesInt]); #create the flag file if true #remove it if false and it exists if($returned){ if(open("FLAGFILE", '>', $config{flagdir}."/".$name)){ print FLAGFILE ""; close("FLAGFILE"); }else{ print "Could not not create flag file '".$config{flagdir}."/".$name."'.\n"; }; }else{ if(-e $config{flagdir}."/".$name){ if(!unlink($config{flagdir}."/".$name)){ print "Could not not remove flag file '".$config{flagdir}."/".$name."'.\n"; }; }; }; $testfilesInt++; }; }; #----------------------------------------------------------- # POD documentation section #----------------------------------------------------------- =pod =head1 NAME netident - A tool for helping identify what network a machine is on. =head1 SYNOPSIS netident [B<-F> ] B<-f> netident [B<-F> ] B<-d> =head1 FLAGS =item -F The directory in which the flags are created or removed from. The default is "/var/db/netident/". =item -f The file that is used for the test. This option can't be in conjunction with -d. =item -d This is a directory that contains the test files. The default is "/usr/local/etc/netident". =head1 TEST FILE The first line contains the name of the test file. This can not contain a "/". Every line after that is a test. A test has multiple sections and "|" is used as a delimiter. The first check, followed by the expected boolean return, and the rest are arguements. The boolean expected return are 0 or 1 only. 0 means false and 1 means true. The arguements are in the form of variable=value. The following example is for a test file that generates the flag "example". It checks for a IP of "192.168.0.1" with a MAC of "00:11:22:33:44:55". After that it checks to see if the default gateway is "192.168.0.1". test pingmac|1|ip=192.168.0.1|mac=00:11:22:33:44:55 defgateway|1|ip=192.168.0.1 =head1 TESTS =item pingmac This test pings a IP to make sure it is in the ARP table and then checks to see if the MAC maches. The arguement for the IP is "ip". The arguement for the MAC is "mac". =item defgateway This checks the routing table for the default route and compares it to passed variable. The arguement "ip" is used for the default gateway. =item cidr This checks if a specific interface or any of them have a address that matches a given CIDR. The arguement "cidr" is CIDR to be matched. The arguement "if" is optional arguement for the interface. =head1 FLAGS These are created in =head1 CHANGELOG =item 0.1.0 The initial release. =head1 AUTHOR Zane C. Bowers =head1 COPYRIGHT Copyright (c) 2008, Zame C. Bowers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS` OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =head1 SCRIPT CATEGORIES Networking =head1 OSNAMES unix =head1 README netident - A tool for helping identify what network a machine is on. =cut #----------------------------------------------------------- # End of POD documentation #-----------------------------------------------------------