#!/usr/bin/perl
use IO::Socket; 
use POSIX 'strftime'; 
use Time::HiRes qw(gettimeofday tv_interval); 
use Getopt::Long; 
use strict; 

my $USAGE = "Usage: sip_ping.pl [-v] [-t] [-s <src_host>] [-p <src_port] <hostname>"; 
my $RECV_TIMEOUT = 5; # temps d’attente d’une réponse en secondes 
my $sock = IO::Socket::INET->new(Proto => 'udp',
                                 LocalPort=>'6655',
                                 ReuseAddr=>1)
  or die "Impossible de créer la socket: $@";

# options
my ($verbose, $host, $my_ip, $my_port, $time); 
GetOptions("verbose|v" => \$verbose,
           "source-ip|s=s" => \$my_ip,
           "source-port|p=n"=> \$my_port,
           "time|t" => \$time) or die "Options invalides:\n\n$USAGE\n";

# détermine à qui envoyer un ping
my $host = shift(@ARGV) or die $USAGE; 
my $dst_addr = inet_aton($host) or die "Hôte introuvable: $host"; 
my $dst_ip = inet_ntoa($dst_addr); 
my $portaddr = sockaddr_in(5060, $dst_addr);

# détermine qui nous sommes
$my_ip = "127.0.0.1" unless defined($my_ip); 
$my_port = "6655" unless defined($my_port);

# Numéro appelant long de 32 caractères hexadécimaux aléatoires
my $callid = ""; $callid .= ('0'..'9', "a".."f")[int(rand(16))] for 1 .. 32;

# date du jour
my $date = strftime('%a, %e %B %Y %I:%M:%S %Z',localtime( ));

# id de branche - voir la rfc3261 pour plus d’infos, avec utilisation de time( ) pour l’unicité
my $branch="z9hG4bK" . time( ); 
my $packet = qq(OPTIONS sip:$dst_ip SIP/2.0 
Via: SIP/2.0/UDP $my_ip:$my_port;branch=$branch 
From: <sip:ping\@$my_ip> 
To: <sip:$host> 
Contact: <sip:ping\@$my_ip> 
Call-ID: $callid\@$my_ip 
CSeq: 102 OPTIONS 
User-Agent: sip_ping.pl 
Date: $date Allow: ACK, CANCEL 
Content-Length: 0 
);

# envoie le paquet
print "Envoi: \n\n$packet\n" if $verbose; send($sock, $packet, 0, $portaddr) == length($packet)
  or die "Envoi impossible à $host: $!"; 
my $send_time = [gettimeofday( )]; # démarre le chronomètre 
my $elapsed;

# obtient la réponse
eval {
  local $SIG{ALRM} = sub { die "temporisation de l’alarme" };
  alarm $RECV_TIMEOUT;
  $portaddr = recv($sock, $packet, 1500, 0) or die "Réception impossible : $!";
  $elapsed = tv_interval($send_time); # arrête le chrono
  alarm 0;
  1;
} or die($@);

# affiche la sortie
if ($verbose) {
  printf("Après (\%0.2f ms), l’hôte a dit: \n\n\%s\n",$elapsed*1000, $packet);
}
elsif ($time) {
  printf("%0.2f\n", $elapsed*1000);
}
else {
  print("$host est en fonctionnement\n");
}
