<?php
##
## this file name is 'class.ping.php'
##
## ping object
##
## [author]
##  - http://linuxchannel.net/
##
## [changes]
##  - 2008.12.01 : new build
##
## [references]
##  - http://kr.php.net/manual/en/function.socket-create.php
##
## [usage]
##
## [example]
##
class icmp
{
  function 
__destruct()
  {
    echo 
"__destruct()\n";
  }

  public function 
make_socket($timeout = array('sec'=>0,'usec'=>500000))
  {
    if(!
$sock = @socket_create(AF_INETSOCK_RAWgetprotobyname('icmp')))
    {
        return array(-
2socket_last_error());
    }

    @
socket_set_option($sock,SOL_SOCKET,SO_RCVTIMEO,$timeout); // good idea for blocking mode
    
@socket_set_option($sock,SOL_SOCKET,SO_SNDTIMEO,$timeout);

    return 
$sock;
  }

  
// timeout in (seconds, microtime) 
  
public function ping($host$sock=NULL)
  {
    static 
$port 0;
    static 
$datasize 58// default 58 bytes + 8 = 64 bytes
    
static $timeout = array('sec'=>0'usec'=>500000);
    static 
$select_timeout 5;

    
$ident = array(ord('J'), ord('C'));
    
$seq = array(rand(0255), rand(0255));

    
## 8 bytes of ICMP header data
    
$packet '';
    
$packet .= chr(8); // type = 8 : request
    
$packet .= chr(0); // code = 0
    
$packet .= chr(0); // checksum init
    
$packet .= chr(0); // checksum init
    
$packet .= chr($ident[0]); // identifier
    
$packet .= chr($ident[1]); // identifier
    
$packet .= chr($seq[0]); // seq
    
$packet .= chr($seq[1]); // seq

    
for($i=0$i<$datasize$i++) $packet .= chr(0);

    
$chk icmp::icmpchecksum($packet);

    
$packet[2] = $chk[0]; // checksum init
    
$packet[3] = $chk[1]; // checksum init

    
if(preg_match('/[a-z]/',$host)) $host gethostbyname($host); // to ip

    
if(!$sock)
    {
        
$sock icmp::make_socket($timeout);
        if(
is_array($sock)) return $sock// error
    
}

    
$time_start microtime();
    @
socket_sendto($sock$packetstrlen($packet), 0$host$port);
    
$time_send microtime();

    
$_time time(); // idle check initial
    
$read   = array($sock);
    while(
1)
    {
        if((
time()-$_time) >= $select_timeout) { return array(-1"Timeout Response ${select_timeout}s"); break; }
        
$select = @socket_select($read$write=NULL$except=NULL$timeout['sec'], $timeout['usec']);
        if(
$select === FALSE) continue;
        if(
$select === NULL)
        {
            
//@socket_close($sock);
            
return array(-1'Select Error');
        }
        else if(
$select === 0)
        {
            
//@socket_close($sock);
            
return array(-1'Timeout');
        }
        break; 
// require
    
}

    
$recv '';
    @
socket_recvfrom($sock$recv655350$host$port);
    
$time_stop microtime();
    @
socket_close($sock);

    
$recv unpack('C*'$recv);

    
// ICMP proto = 1
    
if($recv[10] !== 1) return array(-1,"Not ICMP packet");

    
// ICMP response = 0
    
if($recv[21] !== 0) return array(-1,"Not ICMP response");
    if(
$ident[0] !== $recv[25] || $ident[1] !== $recv[26]) return array(-1,"Bad identification number");
    if(
$seq[0] !== $recv[27] || $seq[1] !== $recv[28]) return array(-1,"Bad sequence number, $seq[0]=>$recv[27]$seq[1]=>$recv[28])");

    
$ttt icmp::getms($time_start$time_send);
    
$gtt icmp::getms($time_send$time_stop);
    
$rtt icmp::getms($time_start$time_stop);

    return array(
"$rtt $ttt $gtt",'');
  }

  private function 
icmpchecksum($data)
  {
    
$bit unpack('n*'$data);
    
$sum array_sum($bit);

    if (
strlen($data) % 2)
    {
        
$temp unpack('C*'$data[strlen($data) - 1]);
        
$sum += $temp[1];
    }

    
$sum = ($sum >> 16) + ($sum 0xffff);
    
$sum += ($sum >> 16);

    return 
pack('n*', ~$sum);
  }

  private function 
getms($_start$_end)
  {
    
$end explode(' '$_end);
    
$start explode(' '$_start);

    return 
sprintf('%.3f',(($end[1]+$end[0])-($start[1]+$start[0])) * 1000);
  }

  private function 
error($errstr)
  {
    return 
error_log($errstr,0);
  }

  private function 
socket_info($socket)
  {
    
$opt = array
    (
    
'SO_DEBUG'=>SO_DEBUG,
    
'SO_ACCEPTCONN'=>SO_ACCEPTCONN,
    
'SO_BROADCAST'=>SO_BROADCAST,
    
'SO_REUSEADDR'=>SO_REUSEADDR,
    
'SO_KEEPALIVE'=>SO_KEEPALIVE,
    
'SO_LINGER'=>SO_LINGER,
    
'SO_OOBINLINE'=>SO_OOBINLINE,
    
'SO_SNDBUF'=>SO_SNDBUF,
    
'SO_RCVBUF'=>SO_RCVBUF,
    
'SO_ERROR'=>SO_ERROR,
    
'SO_TYPE'=>SO_TYPE,
    
'SO_DONTROUTE'=>SO_DONTROUTE,
    
'SO_RCVLOWAT'=>SO_RCVLOWAT,
    
'SO_RCVTIMEO'=>SO_RCVTIMEO,
    
'SO_SNDLOWAT'=>SO_SNDLOWAT,
    
'SO_SNDTIMEO'=>SO_SNDTIMEO,
    );

    foreach (
$opt as $k=>$value)
    {
        
$r socket_get_option($socketSOL_SOCKET$value);
        echo 
"$k : \n";
        
print_r($r);
        echo 
"\n";
    }
  }
// end of class
?>