#!/usr/bin/env perl use strict; use Getopt::Std; use Net::Ping; use POSIX; use Socket; use Switch; use Time::HiRes qw/time/; use Term::ANSIColor; # force print before buffer is full $| = 1; # imcp requires root access die("pinger must be run as root\n") if($> != 0); my $alive = 1; my(%switches, $all, $print, $both, $time, $name, $force, $startTime, $finishTime); # handle command line options my $status = getopts('abdfrnpth', \%switches); help() if(!$status || $switches{'h'}); for (keys %switches) { switch($_) { case 'a' { $all = 1; } case 'b' { $both = 1; $name = 1; } case 'd' { $alive = 0; } case 'f' { $force = 1; } case 'p' { $print = 1; } case 'r' { $alive = 1; } case 'n' { $name = 1; } case 't' { $time = 1; } else {} } } die("Usage: pinger START_IP [END_IP]\n")if(scalar(@ARGV) < 1 || scalar(@ARGV) > 2); $startTime = time if($time); my $p = Net::Ping->new('icmp'); my(@reachable_ips,@unreachable_ips); # must be IP addresses if($ARGV[0] !~ /((?:\d{1,3}\.){3}\d{1,3})/ || (defined($ARGV[1]) && $ARGV[1] !~ /((?:\d{1,3}\.){3}\d{1,3})/)) { die("Both arguments must be IP addresses if two are specified.\n"); } my @ip1 = split(/\./, $ARGV[0]); # Set the second IP address to the first if no second argument my @ip2 = (scalar(@ARGV) == 2) ? split(/\./, $ARGV[1]) : @ip1; # and only the final octet should differ if($ip1[0] != $ip2[0] || $ip1[1] != $ip2[1] || $ip1[2] != $ip2[2]) { die("The first three octets of the IP addresses must match.\n") } # reverse IPs if 1 is larger than 2 if($ip1[3] > $ip2[3]) { my $temp = $ip2[3]; $ip2[3] = $ip1[3]; $ip1[3] = $temp; } my $baseIp = $ip1[0] . '.' . $ip1[1] . '.' . $ip1[2]; my $start = $ip1[3]; my $end = $ip2[3]; my $count = $ip2[3] - $ip1[3] + 1; for(my $i = $start, my $k = 1; $i <= $end; $i++, $k++) { progressBar($k, $count); my $ip = $baseIp . '.' . $i; my $reachable = $p->ping($ip, 1); if($name && ($reachable || $force)) { my $hostaddr = gethostbyaddr(inet_aton($ip), AF_INET); if($hostaddr ne "") { $ip = ($both) ? "$ip\t$hostaddr" : "$hostaddr"; } } if($print) { $ip = ($reachable) ? "o $ip" : "x $ip"; } if($reachable) { push(@reachable_ips, colored($ip, 'green')); } else { push(@unreachable_ips, colored($ip, 'red')); } } $p->close(); $finishTime = time if($time); print "\n\n"; my @ips; if($all) { print "All:\n"; @ips = (@reachable_ips, @unreachable_ips); @ips = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [$_, int sprintf("%03.f%03.f%03.f%03.f", split(/\.+/, $_))] } @ips; } elsif($alive) { print "Reachable:\n"; @ips = @reachable_ips; } else { print "Dead:\n"; @ips = @unreachable_ips; } my $delimeter = ($both) ? "\n" : ", "; print join($delimeter, @ips); print "\n\n"; if($time) { my $completed = $finishTime - $startTime; printf("Completed in %.2f seconds.\n", $completed); } # details from http://stackoverflow.com/questions/1782107/how-do-i-retrieve-the-terminal-width-in-perl sub findTerminalWidth { my($winsize, $row, $col, $xpixel, $ypixel); my $available = 1; require 'sys/ioctl.ph'; $available = 0 unless defined &TIOCGWINSZ; open(TTY, "+ 0) { print "] ["; for(my $i = 1; $i < $barWidth; $i++) { ($i <= $barPercent) ? print '=' : print ' '; } } print "]\r"; } sub help { print "usage:\t pinger [-d] start [finish]\n\n"; print "\t-a\t\tList both reachable and unreachable hosts\n"; print "\t-b\t\tList both hostname and ip address\n"; print "\t-d\t\tList dead addresses\n"; print "\t-f\t\tForce hostname lookup even if unreachable\n"; print "\t-n\t\tReturn list as hostnames when possible\n"; print "\t-p\t\tEnable print mode; only noticeable with -a enabled\n"; print "\t-r\t\tList reachable addresses (default)\n"; print "\t-t\t\tTime ping execution\n"; exit(1); }