#!/usr/bin/env perl

=head1 NAME

multi-ping - Multi-protocol ping wrapper

=head1 SYNOPSIS

  multi-ping [--loop|--forever] [--sleep=N]  hostname1 hostname2 .. hostnameN

=cut

=head1 DESCRIPTION

This wrapper script will invoke one of 'ping' or 'ping6', as appropriate,
to test the connectivity of a remote host and your route to it.

=cut

=head1 AUTHOR

 Steve
 --
 http://www.steve.org.uk/

=cut

=head1 LICENSE

Copyright (c) 2013-2014 by Steve Kemp.  All rights reserved.

This script is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.

The LICENSE file contains the full text of the license.

=cut


use strict;
use warnings;

use Getopt::Long;
use Pod::Usage;


#
#  Check the dependencies.
#
checkSystem();

#
#  Parse the command-line.
#
my %config = parsedOptions();


#
#  We could parse arguments here, for the moment we will hard-wire
# a timeout of five seconds.
#
my $timeout = 5;


#
# If we didn't get any arguments then we should complain.
#
if ( scalar @ARGV < 1 )
{
    print "Usage: multi-ping hostname1 hostname2 .. hostnameN\n";
    exit(1);
}


#
# Process each hostname specified upon the command-line.
#
# Looping if applicable.
#
do
{
    foreach my $host (@ARGV)
    {
        pingHost($host);
    }

    sleep( $config{ 'sleep' } );

} until ( !$config{ 'loop' } );




=begin doc

Given a hostname lookup both the AAAA & A records.  For each result
perform the appropriate ping request.

=end doc

=cut

sub pingHost
{
    my ($hostname) = (@_);

    #
    #  If we've been given an URI then we'll remove the leading-scheme
    # and use the hostname only.
    #
    if ( $hostname =~ m!^([a-z]+:\/\/)([^/]+)/?!i )
    {
        $hostname = $2;

        #
        #  Port might be specified too, drop that if present.
        #
        if ( $hostname =~ /^(.*):([0-9]+)$/ )
        {
            $hostname = $1;
        }
    }

    #
    #  Lookup the IP for the name specified
    #
    my $res = Net::DNS::Resolver->new;

    #
    #  The two types we'll lookup.
    #
    #  NOTE: This shouldn't be required but my laptop resolver seems to
    # struggle with lookups of the type "ANY".  Sigh.
    #
    #
    foreach my $type (qw! A AAAA !)
    {
        my $query = $res->query( $hostname, $type );

        if ($query)
        {
            foreach my $rr ( $query->answer )
            {
                my $ping_binary =
                  $rr->type eq "A"    ? "ping" :
                  $rr->type eq "AAAA" ? "ping6" :
                                        "";
                if ($ping_binary)
                {
                    my $result = system(
                        "$ping_binary -c1 -w$timeout -W$timeout $hostname >/dev/null 2>/dev/null"
                    );

                    print "Host $hostname - " . $rr->address() .
                      ( ( $result == 0 ) ? " - alive" : " - FAILED"  ) . "\n";
                }
            }
        }
        else
        {
            print "WARNING: Failed to resolve $hostname [$type]\n";
        }
    }
}



=begin doc

Test that we have the required perl dependencies present.

=end doc

=cut

sub checkSystem
{
    my $eval = "use Net::DNS;";

    ## no critic (Eval)
    eval($eval);
    ## use critic

    #
    #  If we don't have Net::DNS we're out of luck.
    #
    if ($@)
    {
        print "The required Net::DNS module is missing.  Aborting.\n";
        exit(1);
    }

}



=begin doc

Parse the options and return suitable values.

=end doc

=cut

sub parsedOptions
{
    my %vars;

    #
    # Defaults
    #
    $vars{ 'loop' }  = 0;
    $vars{ 'sleep' } = 1;

    exit
      if (
           !GetOptions( "help"    => \$vars{ 'help' },
                        "verbose" => \$vars{ 'verbose' },
                        "forever" => \$vars{ 'loop' },
                        "loop"    => \$vars{ 'loop' },
                        "sleep=i" => \$vars{ 'sleep' },
                      ) );

    pod2usage(1) if ( $vars{ 'help' } );

    return (%vars);

}
