#!/usr/bin/perl
#< Ping a /24 network and report on host status

use Getopt::Long qw(:config no_ignore_case);
use Net::DNS::Resolver;
use Net::Ping;
use warnings;
use strict;

our $O_NAMESERVER;
our $O_NETWORK;
our $O_RESOLVE = 0;
our $O_HELP = 0;
our $O_VERBOSE = 0;
our $E_SUCCESS = 0;
our $E_ERROR = 1;

sub print_verbose {
   print STDOUT "--> @_\n" if $O_VERBOSE;
}

sub print_error {
   print STDERR "Error: @_\n";
}

sub display_help {
   my $thisprog = $0;
   $thisprog =~ s#^.*/##;
   if ( $O_VERBOSE > 0 ) {
      print_verbose "thisprog -> $thisprog"
   }
   printf( "Usage: %s [-hrv] [-R <namesever>] -n <network>\n", $thisprog );
   printf( "           -r   Attempt to resolve addresses\n" );
   printf( "           -R   Specify nameserver for resolution\n" );
   printf( "           -h   Display this help message\n" );
   printf( "           -v   Run in verbose mode\n" );
}

sub process_arguments {
   GetOptions ( 'resolve'     => \$O_RESOLVE,
                'Resolver=s'  => \$O_NAMESERVER,
                'network=s'   => \$O_NETWORK,
                'help'        => \$O_HELP,
                'verbose+'    => \$O_VERBOSE ) or $O_HELP++;
   if ( $O_HELP > 0 ) {
      display_help();
      exit $E_SUCCESS;
   }
   if ( ! defined ( $O_NETWORK ) ) {
      print_error "Network (-n) must be specified";
      exit $E_ERROR;
   }
}

sub check_ip_addr {
   if ( defined( $O_NETWORK ) ) {
      my $error_counter = 0;
      my @octets = split( /\./, $O_NETWORK );
      my $num_octets = scalar( @octets );
      print_verbose "Network address has $num_octets octets";
      ++$error_counter if $num_octets != 4;
      if ( $octets[$num_octets - 1] != 0 ) {
         print_verbose "Not a /24 network address";
         ++$error_counter;
      }
      foreach my $octet ( @octets ) {
         if ( $octet eq "" || $octet < 0 || $octet > 254 ) {
            print_verbose "Octet $octet out of bounds";
            ++$error_counter;
         }
      }
      if ( $error_counter != 0 ) {
         print_error "Errors found in network address";
         exit $E_ERROR;
      }
   }
   if ( defined( $O_NAMESERVER ) ) {
      my $error_counter = 0;
      my @octets = split( /\./, $O_NAMESERVER );
      my $num_octets = scalar( @octets );
      print_verbose "Nameserver address has $num_octets octets";
      ++$error_counter if $num_octets != 4;
      foreach my $octet ( @octets ) {
         if ( $octet eq "" || $octet < 0 || $octet > 254 ) {
            print_error "Octet $octet out of bounds";
            ++$error_counter;
         }
      }
      if ( $error_counter != 0 ) {
         print_error "Errors found in nameserver address";
         exit $E_ERROR;
      }
   }
}

sub perform_test {
   my @octets = split( /\./, $O_NETWORK );
   for ( my $i = 1; $i <= 254; $i++ ) {
      my $target = sprintf( "%s.%s.%s.%s", $octets[0], $octets[1], $octets[2], $i );
      my $ping = Net::Ping->new();
      my $hostname = "";
      if ( $O_RESOLVE ) {
         my $resolver;
         if ( defined( $O_NAMESERVER ) ) {
            $resolver = Net::DNS::Resolver->new(
               nameservers => [$O_NAMESERVER],
               udp_timeout => 1,
               retry       => 1
            );
         } else {
            $resolver = Net::DNS::Resolver->new(
               udp_timeout => 1,
               retry       => 1
            );
         }
         if ( my $packet = $resolver->query( $target ) ) {
            for my $answer ( $packet->answer() ) {
               my $type = $answer->type();
               if ( $type eq "PTR" ) { 
                  $hostname = $answer->ptrdname();
               } else {
                  $hostname = "Unable to resolve host";
               }
            }
         } else {
            $hostname = "Unable to resolve host";
         }
      } 
      if ( $ping->ping( $target, 1 ) ) {
         if ( $hostname eq "" ) {
            printf( "%s is UP\n", $target );
         } else {
            printf( "%s is UP\t[%s]\n", $target, $hostname );
         }
      } else {
         if ( $hostname eq "" ) {
            printf( "%s is DOWN\n", $target );
         } else {
            printf( "%s is DOWN\t[%s]\n", $target, $hostname );
         }
      }
   }
}

process_arguments();
check_ip_addr();
perform_test();

exit $E_SUCCESS;