web/Zend/TimeSync/Ntp.php
changeset 0 4eba9c11703f
equal deleted inserted replaced
-1:000000000000 0:4eba9c11703f
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category  Zend
       
    16  * @package   Zend_TimeSync
       
    17  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    18  * @license   http://framework.zend.com/license/new-bsd     New BSD License
       
    19  * @version   $Id: Ntp.php 21480 2010-03-13 22:09:26Z thomas $
       
    20  */
       
    21 
       
    22 /**
       
    23  * Zend_TimeSync_Protocol
       
    24  */
       
    25 require_once 'Zend/TimeSync/Protocol.php';
       
    26 
       
    27 /**
       
    28  * NTP Protocol handling class
       
    29  *
       
    30  * @category  Zend
       
    31  * @package   Zend_TimeSync
       
    32  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    33  * @license   http://framework.zend.com/license/new-bsd     New BSD License
       
    34  */
       
    35 class Zend_TimeSync_Ntp extends Zend_TimeSync_Protocol
       
    36 {
       
    37     /**
       
    38      * NTP port number (123) assigned by the Internet Assigned Numbers Authority
       
    39      *
       
    40      * @var integer
       
    41      */
       
    42     protected $_port = 123;
       
    43 
       
    44     /**
       
    45      * NTP class constructor, sets the timeserver and port number
       
    46      *
       
    47      * @param string  $timeserver Adress of the timeserver to connect to
       
    48      * @param integer $port       (Optional) Port for this timeserver
       
    49      */
       
    50     public function __construct($timeserver, $port = 123)
       
    51     {
       
    52         $this->_timeserver = 'udp://' . $timeserver;
       
    53         if ($port !== null) {
       
    54             $this->_port = $port;
       
    55         }
       
    56     }
       
    57 
       
    58     /**
       
    59      * Prepare local timestamp for transmission in our request packet
       
    60      *
       
    61      * NTP timestamps are represented as a 64-bit fixed-point number, in
       
    62      * seconds relative to 0000 UT on 1 January 1900.  The integer part is
       
    63      * in the first 32 bits and the fraction part in the last 32 bits
       
    64      *
       
    65      * @return string
       
    66      */
       
    67     protected function _prepare()
       
    68     {
       
    69         $frac   = microtime();
       
    70         $fracba = ($frac & 0xff000000) >> 24;
       
    71         $fracbb = ($frac & 0x00ff0000) >> 16;
       
    72         $fracbc = ($frac & 0x0000ff00) >> 8;
       
    73         $fracbd = ($frac & 0x000000ff);
       
    74 
       
    75         $sec   = (time() + 2208988800);
       
    76         $secba = ($sec & 0xff000000) >> 24;
       
    77         $secbb = ($sec & 0x00ff0000) >> 16;
       
    78         $secbc = ($sec & 0x0000ff00) >> 8;
       
    79         $secbd = ($sec & 0x000000ff);
       
    80 
       
    81         // Flags
       
    82         $nul       = chr(0x00);
       
    83         $nulbyte   = $nul . $nul . $nul . $nul;
       
    84         $ntppacket = chr(0xd9) . $nul . chr(0x0a) . chr(0xfa);
       
    85 
       
    86         /*
       
    87          * Root delay
       
    88          *
       
    89          * Indicates the total roundtrip delay to the primary reference
       
    90          * source at the root of the synchronization subnet, in seconds
       
    91          */
       
    92         $ntppacket .= $nul . $nul . chr(0x1c) . chr(0x9b);
       
    93 
       
    94         /*
       
    95          * Clock Dispersion
       
    96          *
       
    97          * Indicates the maximum error relative to the primary reference source at the
       
    98          * root of the synchronization subnet, in seconds
       
    99          */
       
   100         $ntppacket .= $nul . chr(0x08) . chr(0xd7) . chr(0xff);
       
   101 
       
   102         /*
       
   103          * ReferenceClockID
       
   104          *
       
   105          * Identifying the particular reference clock
       
   106          */
       
   107         $ntppacket .= $nulbyte;
       
   108 
       
   109         /*
       
   110          * The local time, in timestamp format, at the peer when its latest NTP message
       
   111          * was sent. Contanis an integer and a fractional part
       
   112          */
       
   113         $ntppacket .= chr($secba)  . chr($secbb)  . chr($secbc)  . chr($secbd);
       
   114         $ntppacket .= chr($fracba) . chr($fracbb) . chr($fracbc) . chr($fracbd);
       
   115 
       
   116         /*
       
   117          * The local time, in timestamp format, at the peer. Contains an integer
       
   118          * and a fractional part.
       
   119          */
       
   120         $ntppacket .= $nulbyte;
       
   121         $ntppacket .= $nulbyte;
       
   122 
       
   123         /*
       
   124          * This is the local time, in timestamp format, when the latest NTP message from
       
   125          * the peer arrived. Contanis an integer and a fractional part.
       
   126          */
       
   127         $ntppacket .= $nulbyte;
       
   128         $ntppacket .= $nulbyte;
       
   129 
       
   130         /*
       
   131          * The local time, in timestamp format, at which the
       
   132          * NTP message departed the sender. Contanis an integer
       
   133          * and a fractional part.
       
   134          */
       
   135         $ntppacket .= chr($secba)  . chr($secbb)  . chr($secbc)  . chr($secbd);
       
   136         $ntppacket .= chr($fracba) . chr($fracbb) . chr($fracbc) . chr($fracbd);
       
   137 
       
   138         return $ntppacket;
       
   139     }
       
   140 
       
   141     /**
       
   142      * Calculates a 32bit integer
       
   143      *
       
   144      * @param string $input
       
   145      * @return integer
       
   146      */
       
   147     protected function _getInteger($input)
       
   148     {
       
   149         $f1  = str_pad(ord($input[0]), 2, '0', STR_PAD_LEFT);
       
   150         $f1 .= str_pad(ord($input[1]), 2, '0', STR_PAD_LEFT);
       
   151         $f1 .= str_pad(ord($input[2]), 2, '0', STR_PAD_LEFT);
       
   152         $f1 .= str_pad(ord($input[3]), 2, '0', STR_PAD_LEFT);
       
   153         return (int) $f1;
       
   154     }
       
   155 
       
   156     /**
       
   157      * Calculates a 32bit signed fixed point number
       
   158      *
       
   159      * @param string $input
       
   160      * @return float
       
   161      */
       
   162     protected function _getFloat($input)
       
   163     {
       
   164         $f1  = str_pad(ord($input[0]), 2, '0', STR_PAD_LEFT);
       
   165         $f1 .= str_pad(ord($input[1]), 2, '0', STR_PAD_LEFT);
       
   166         $f1 .= str_pad(ord($input[2]), 2, '0', STR_PAD_LEFT);
       
   167         $f1 .= str_pad(ord($input[3]), 2, '0', STR_PAD_LEFT);
       
   168         $f2  = $f1 >> 17;
       
   169         $f3  = ($f1 & 0x0001FFFF);
       
   170         $f1  = $f2 . '.' . $f3;
       
   171         return (float) $f1;
       
   172     }
       
   173 
       
   174     /**
       
   175      * Calculates a 64bit timestamp
       
   176      *
       
   177      * @param string $input
       
   178      * @return float
       
   179      */
       
   180     protected function _getTimestamp($input)
       
   181     {
       
   182         $f1  = (ord($input[0]) * pow(256, 3));
       
   183         $f1 += (ord($input[1]) * pow(256, 2));
       
   184         $f1 += (ord($input[2]) * pow(256, 1));
       
   185         $f1 += (ord($input[3]));
       
   186         $f1 -= 2208988800;
       
   187 
       
   188         $f2  = (ord($input[4]) * pow(256, 3));
       
   189         $f2 += (ord($input[5]) * pow(256, 2));
       
   190         $f2 += (ord($input[6]) * pow(256, 1));
       
   191         $f2 += (ord($input[7]));
       
   192 
       
   193         return (float) ($f1 . "." . $f2);
       
   194     }
       
   195 
       
   196     /**
       
   197      * Reads the data returned from the timeserver
       
   198      *
       
   199      * This will return an array with binary data listing:
       
   200      *
       
   201      * @return array
       
   202      * @throws Zend_TimeSync_Exception When timeserver can not be connected
       
   203      */
       
   204     protected function _read()
       
   205     {
       
   206         $flags = ord(fread($this->_socket, 1));
       
   207         $info  = stream_get_meta_data($this->_socket);
       
   208 
       
   209         if ($info['timed_out'] === true) {
       
   210             fclose($this->_socket);
       
   211             throw new Zend_TimeSync_Exception('could not connect to ' .
       
   212                 "'$this->_timeserver' on port '$this->_port', reason: 'server timed out'");
       
   213         }
       
   214 
       
   215         $result = array(
       
   216             'flags'          => $flags,
       
   217             'stratum'        => ord(fread($this->_socket, 1)),
       
   218             'poll'           => ord(fread($this->_socket, 1)),
       
   219             'precision'      => ord(fread($this->_socket, 1)),
       
   220             'rootdelay'      => $this->_getFloat(fread($this->_socket, 4)),
       
   221             'rootdispersion' => $this->_getFloat(fread($this->_socket, 4)),
       
   222             'referenceid'    => fread($this->_socket, 4),
       
   223             'referencestamp' => $this->_getTimestamp(fread($this->_socket, 8)),
       
   224             'originatestamp' => $this->_getTimestamp(fread($this->_socket, 8)),
       
   225             'receivestamp'   => $this->_getTimestamp(fread($this->_socket, 8)),
       
   226             'transmitstamp'  => $this->_getTimestamp(fread($this->_socket, 8)),
       
   227             'clientreceived' => microtime(true)
       
   228         );
       
   229 
       
   230         $this->_disconnect();
       
   231         return $result;
       
   232     }
       
   233 
       
   234     /**
       
   235      * Sends the NTP packet to the server
       
   236      *
       
   237      * @param  string $data Data to send to the timeserver
       
   238      * @return void
       
   239      */
       
   240     protected function _write($data)
       
   241     {
       
   242         $this->_connect();
       
   243 
       
   244         fwrite($this->_socket, $data);
       
   245         stream_set_timeout($this->_socket, Zend_TimeSync::$options['timeout']);
       
   246     }
       
   247 
       
   248     /**
       
   249      * Extracts the binary data returned from the timeserver
       
   250      *
       
   251      * @param  string|array $binary Data returned from the timeserver
       
   252      * @return integer Difference in seconds
       
   253      */
       
   254     protected function _extract($binary)
       
   255     {
       
   256         /*
       
   257          * Leap Indicator bit 1100 0000
       
   258          *
       
   259          * Code warning of impending leap-second to be inserted at the end of
       
   260          * the last day of the current month.
       
   261          */
       
   262         $leap = ($binary['flags'] & 0xc0) >> 6;
       
   263         switch($leap) {
       
   264             case 0:
       
   265                 $this->_info['leap'] = '0 - no warning';
       
   266                 break;
       
   267 
       
   268             case 1:
       
   269                 $this->_info['leap'] = '1 - last minute has 61 seconds';
       
   270                 break;
       
   271 
       
   272             case 2:
       
   273                 $this->_info['leap'] = '2 - last minute has 59 seconds';
       
   274                 break;
       
   275 
       
   276             default:
       
   277                 $this->_info['leap'] = '3 - not syncronised';
       
   278                 break;
       
   279         }
       
   280 
       
   281         /*
       
   282          * Version Number bit 0011 1000
       
   283          *
       
   284          * This should be 3 (RFC 1305)
       
   285          */
       
   286         $this->_info['version'] = ($binary['flags'] & 0x38) >> 3;
       
   287 
       
   288         /*
       
   289          * Mode bit 0000 0111
       
   290          *
       
   291          * Except in broadcast mode, an NTP association is formed when two peers
       
   292          * exchange messages and one or both of them create and maintain an
       
   293          * instantiation of the protocol machine, called an association.
       
   294          */
       
   295         $mode = ($binary['flags'] & 0x07);
       
   296         switch($mode) {
       
   297             case 1:
       
   298                 $this->_info['mode'] = 'symetric active';
       
   299                 break;
       
   300 
       
   301             case 2:
       
   302                 $this->_info['mode'] = 'symetric passive';
       
   303                 break;
       
   304 
       
   305             case 3:
       
   306                 $this->_info['mode'] = 'client';
       
   307                 break;
       
   308 
       
   309             case 4:
       
   310                 $this->_info['mode'] = 'server';
       
   311                 break;
       
   312 
       
   313             case 5:
       
   314                 $this->_info['mode'] = 'broadcast';
       
   315                 break;
       
   316 
       
   317             default:
       
   318                 $this->_info['mode'] = 'reserved';
       
   319                 break;
       
   320         }
       
   321 
       
   322         $ntpserviceid = 'Unknown Stratum ' . $binary['stratum'] . ' Service';
       
   323 
       
   324         /*
       
   325          * Reference Clock Identifier
       
   326          *
       
   327          * Identifies the particular reference clock.
       
   328          */
       
   329         $refid = strtoupper($binary['referenceid']);
       
   330         switch($binary['stratum']) {
       
   331             case 0:
       
   332                 if (substr($refid, 0, 3) === 'DCN') {
       
   333                     $ntpserviceid = 'DCN routing protocol';
       
   334                 } else if (substr($refid, 0, 4) === 'NIST') {
       
   335                     $ntpserviceid = 'NIST public modem';
       
   336                 } else if (substr($refid, 0, 3) === 'TSP') {
       
   337                     $ntpserviceid = 'TSP time protocol';
       
   338                 } else if (substr($refid, 0, 3) === 'DTS') {
       
   339                     $ntpserviceid = 'Digital Time Service';
       
   340                 }
       
   341                 break;
       
   342 
       
   343             case 1:
       
   344                 if (substr($refid, 0, 4) === 'ATOM') {
       
   345                     $ntpserviceid = 'Atomic Clock (calibrated)';
       
   346                 } else if (substr($refid, 0, 3) === 'VLF') {
       
   347                     $ntpserviceid = 'VLF radio';
       
   348                 } else if ($refid === 'CALLSIGN') {
       
   349                     $ntpserviceid = 'Generic radio';
       
   350                 } else if (substr($refid, 0, 4) === 'LORC') {
       
   351                     $ntpserviceid = 'LORAN-C radionavigation';
       
   352                 } else if (substr($refid, 0, 4) === 'GOES') {
       
   353                     $ntpserviceid = 'GOES UHF environment satellite';
       
   354                 } else if (substr($refid, 0, 3) === 'GPS') {
       
   355                     $ntpserviceid = 'GPS UHF satellite positioning';
       
   356                 }
       
   357                 break;
       
   358 
       
   359             default:
       
   360                 $ntpserviceid  = ord(substr($binary['referenceid'], 0, 1));
       
   361                 $ntpserviceid .= '.';
       
   362                 $ntpserviceid .= ord(substr($binary['referenceid'], 1, 1));
       
   363                 $ntpserviceid .= '.';
       
   364                 $ntpserviceid .= ord(substr($binary['referenceid'], 2, 1));
       
   365                 $ntpserviceid .= '.';
       
   366                 $ntpserviceid .= ord(substr($binary['referenceid'], 3, 1));
       
   367                 break;
       
   368         }
       
   369 
       
   370         $this->_info['ntpid'] = $ntpserviceid;
       
   371 
       
   372         /*
       
   373          * Stratum
       
   374          *
       
   375          * Indicates the stratum level of the local clock
       
   376          */
       
   377         switch($binary['stratum']) {
       
   378             case 0:
       
   379                 $this->_info['stratum'] = 'undefined';
       
   380                 break;
       
   381 
       
   382             case 1:
       
   383                 $this->_info['stratum'] = 'primary reference';
       
   384                 break;
       
   385 
       
   386             default:
       
   387                 $this->_info['stratum'] = 'secondary reference';
       
   388                 break;
       
   389         }
       
   390 
       
   391         /*
       
   392          * Indicates the total roundtrip delay to the primary reference source at the
       
   393          * root of the synchronization subnet, in seconds.
       
   394          *
       
   395          * Both positive and negative values, depending on clock precision and skew, are
       
   396          * possible.
       
   397          */
       
   398         $this->_info['rootdelay'] = $binary['rootdelay'];
       
   399 
       
   400         /*
       
   401          * Indicates the maximum error relative to the primary reference source at the
       
   402          * root of the synchronization subnet, in seconds.
       
   403          *
       
   404          * Only positive values greater than zero are possible.
       
   405          */
       
   406         $this->_info['rootdispersion'] = $binary['rootdispersion'];
       
   407 
       
   408         /*
       
   409          * The roundtrip delay of the peer clock relative to the local clock
       
   410          * over the network path between them, in seconds.
       
   411          *
       
   412          * Note that this variable can take on both positive and negative values,
       
   413          * depending on clock precision and skew-error accumulation.
       
   414          */
       
   415         $this->_info['roundtrip']  = $binary['receivestamp'];
       
   416         $this->_info['roundtrip'] -= $binary['originatestamp'];
       
   417         $this->_info['roundtrip'] -= $binary['transmitstamp'];
       
   418         $this->_info['roundtrip'] += $binary['clientreceived'];
       
   419         $this->_info['roundtrip'] /= 2;
       
   420 
       
   421         // The offset of the peer clock relative to the local clock, in seconds.
       
   422         $this->_info['offset']  = $binary['receivestamp'];
       
   423         $this->_info['offset'] -= $binary['originatestamp'];
       
   424         $this->_info['offset'] += $binary['transmitstamp'];
       
   425         $this->_info['offset'] -= $binary['clientreceived'];
       
   426         $this->_info['offset'] /= 2;
       
   427         $time = (time() - $this->_info['offset']);
       
   428 
       
   429         return $time;
       
   430     }
       
   431 }