File indexing completed on 2025-01-19 05:21:35
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_TimeSync 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * Zend_TimeSync_Protocol 0024 */ 0025 // require_once 'Zend/TimeSync/Protocol.php'; 0026 0027 /** 0028 * NTP Protocol handling class 0029 * 0030 * @category Zend 0031 * @package Zend_TimeSync 0032 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0033 * @license http://framework.zend.com/license/new-bsd New BSD License 0034 */ 0035 class Zend_TimeSync_Ntp extends Zend_TimeSync_Protocol 0036 { 0037 /** 0038 * NTP port number (123) assigned by the Internet Assigned Numbers Authority 0039 * 0040 * @var integer 0041 */ 0042 protected $_port = 123; 0043 0044 /** 0045 * NTP class constructor, sets the timeserver and port number 0046 * 0047 * @param string $timeserver Adress of the timeserver to connect to 0048 * @param integer $port (Optional) Port for this timeserver 0049 */ 0050 public function __construct($timeserver, $port = 123) 0051 { 0052 $this->_timeserver = 'udp://' . $timeserver; 0053 if ($port !== null) { 0054 $this->_port = $port; 0055 } 0056 } 0057 0058 /** 0059 * Prepare local timestamp for transmission in our request packet 0060 * 0061 * NTP timestamps are represented as a 64-bit fixed-point number, in 0062 * seconds relative to 0000 UT on 1 January 1900. The integer part is 0063 * in the first 32 bits and the fraction part in the last 32 bits 0064 * 0065 * @return string 0066 */ 0067 protected function _prepare() 0068 { 0069 $frac = microtime(); 0070 $fracba = ($frac & 0xff000000) >> 24; 0071 $fracbb = ($frac & 0x00ff0000) >> 16; 0072 $fracbc = ($frac & 0x0000ff00) >> 8; 0073 $fracbd = ($frac & 0x000000ff); 0074 0075 $sec = (time() + 2208988800); 0076 $secba = ($sec & 0xff000000) >> 24; 0077 $secbb = ($sec & 0x00ff0000) >> 16; 0078 $secbc = ($sec & 0x0000ff00) >> 8; 0079 $secbd = ($sec & 0x000000ff); 0080 0081 // Flags 0082 $nul = chr(0x00); 0083 $nulbyte = $nul . $nul . $nul . $nul; 0084 $ntppacket = chr(0xd9) . $nul . chr(0x0a) . chr(0xfa); 0085 0086 /* 0087 * Root delay 0088 * 0089 * Indicates the total roundtrip delay to the primary reference 0090 * source at the root of the synchronization subnet, in seconds 0091 */ 0092 $ntppacket .= $nul . $nul . chr(0x1c) . chr(0x9b); 0093 0094 /* 0095 * Clock Dispersion 0096 * 0097 * Indicates the maximum error relative to the primary reference source at the 0098 * root of the synchronization subnet, in seconds 0099 */ 0100 $ntppacket .= $nul . chr(0x08) . chr(0xd7) . chr(0xff); 0101 0102 /* 0103 * ReferenceClockID 0104 * 0105 * Identifying the particular reference clock 0106 */ 0107 $ntppacket .= $nulbyte; 0108 0109 /* 0110 * The local time, in timestamp format, at the peer when its latest NTP message 0111 * was sent. Contanis an integer and a fractional part 0112 */ 0113 $ntppacket .= chr($secba) . chr($secbb) . chr($secbc) . chr($secbd); 0114 $ntppacket .= chr($fracba) . chr($fracbb) . chr($fracbc) . chr($fracbd); 0115 0116 /* 0117 * The local time, in timestamp format, at the peer. Contains an integer 0118 * and a fractional part. 0119 */ 0120 $ntppacket .= $nulbyte; 0121 $ntppacket .= $nulbyte; 0122 0123 /* 0124 * This is the local time, in timestamp format, when the latest NTP message from 0125 * the peer arrived. Contanis an integer and a fractional part. 0126 */ 0127 $ntppacket .= $nulbyte; 0128 $ntppacket .= $nulbyte; 0129 0130 /* 0131 * The local time, in timestamp format, at which the 0132 * NTP message departed the sender. Contanis an integer 0133 * and a fractional part. 0134 */ 0135 $ntppacket .= chr($secba) . chr($secbb) . chr($secbc) . chr($secbd); 0136 $ntppacket .= chr($fracba) . chr($fracbb) . chr($fracbc) . chr($fracbd); 0137 0138 return $ntppacket; 0139 } 0140 0141 /** 0142 * Calculates a 32bit integer 0143 * 0144 * @param string $input 0145 * @return integer 0146 */ 0147 protected function _getInteger($input) 0148 { 0149 $f1 = str_pad(ord($input[0]), 2, '0', STR_PAD_LEFT); 0150 $f1 .= str_pad(ord($input[1]), 2, '0', STR_PAD_LEFT); 0151 $f1 .= str_pad(ord($input[2]), 2, '0', STR_PAD_LEFT); 0152 $f1 .= str_pad(ord($input[3]), 2, '0', STR_PAD_LEFT); 0153 return (int) $f1; 0154 } 0155 0156 /** 0157 * Calculates a 32bit signed fixed point number 0158 * 0159 * @param string $input 0160 * @return float 0161 */ 0162 protected function _getFloat($input) 0163 { 0164 $f1 = str_pad(ord($input[0]), 2, '0', STR_PAD_LEFT); 0165 $f1 .= str_pad(ord($input[1]), 2, '0', STR_PAD_LEFT); 0166 $f1 .= str_pad(ord($input[2]), 2, '0', STR_PAD_LEFT); 0167 $f1 .= str_pad(ord($input[3]), 2, '0', STR_PAD_LEFT); 0168 $f2 = $f1 >> 17; 0169 $f3 = ($f1 & 0x0001FFFF); 0170 $f1 = $f2 . '.' . $f3; 0171 return (float) $f1; 0172 } 0173 0174 /** 0175 * Calculates a 64bit timestamp 0176 * 0177 * @param string $input 0178 * @return float 0179 */ 0180 protected function _getTimestamp($input) 0181 { 0182 $f1 = (ord($input[0]) * pow(256, 3)); 0183 $f1 += (ord($input[1]) * pow(256, 2)); 0184 $f1 += (ord($input[2]) * pow(256, 1)); 0185 $f1 += (ord($input[3])); 0186 $f1 -= 2208988800; 0187 0188 $f2 = (ord($input[4]) * pow(256, 3)); 0189 $f2 += (ord($input[5]) * pow(256, 2)); 0190 $f2 += (ord($input[6]) * pow(256, 1)); 0191 $f2 += (ord($input[7])); 0192 0193 return (float) ($f1 . "." . $f2); 0194 } 0195 0196 /** 0197 * Reads the data returned from the timeserver 0198 * 0199 * This will return an array with binary data listing: 0200 * 0201 * @return array 0202 * @throws Zend_TimeSync_Exception When timeserver can not be connected 0203 */ 0204 protected function _read() 0205 { 0206 $flags = ord(fread($this->_socket, 1)); 0207 $info = stream_get_meta_data($this->_socket); 0208 0209 if ($info['timed_out'] === true) { 0210 fclose($this->_socket); 0211 throw new Zend_TimeSync_Exception('could not connect to ' . 0212 "'$this->_timeserver' on port '$this->_port', reason: 'server timed out'"); 0213 } 0214 0215 $result = array( 0216 'flags' => $flags, 0217 'stratum' => ord(fread($this->_socket, 1)), 0218 'poll' => ord(fread($this->_socket, 1)), 0219 'precision' => ord(fread($this->_socket, 1)), 0220 'rootdelay' => $this->_getFloat(fread($this->_socket, 4)), 0221 'rootdispersion' => $this->_getFloat(fread($this->_socket, 4)), 0222 'referenceid' => fread($this->_socket, 4), 0223 'referencestamp' => $this->_getTimestamp(fread($this->_socket, 8)), 0224 'originatestamp' => $this->_getTimestamp(fread($this->_socket, 8)), 0225 'receivestamp' => $this->_getTimestamp(fread($this->_socket, 8)), 0226 'transmitstamp' => $this->_getTimestamp(fread($this->_socket, 8)), 0227 'clientreceived' => microtime(true) 0228 ); 0229 0230 $this->_disconnect(); 0231 return $result; 0232 } 0233 0234 /** 0235 * Sends the NTP packet to the server 0236 * 0237 * @param string $data Data to send to the timeserver 0238 * @return void 0239 */ 0240 protected function _write($data) 0241 { 0242 $this->_connect(); 0243 0244 fwrite($this->_socket, $data); 0245 stream_set_timeout($this->_socket, Zend_TimeSync::$options['timeout']); 0246 } 0247 0248 /** 0249 * Extracts the binary data returned from the timeserver 0250 * 0251 * @param string|array $binary Data returned from the timeserver 0252 * @return integer Difference in seconds 0253 */ 0254 protected function _extract($binary) 0255 { 0256 /* 0257 * Leap Indicator bit 1100 0000 0258 * 0259 * Code warning of impending leap-second to be inserted at the end of 0260 * the last day of the current month. 0261 */ 0262 $leap = ($binary['flags'] & 0xc0) >> 6; 0263 switch($leap) { 0264 case 0: 0265 $this->_info['leap'] = '0 - no warning'; 0266 break; 0267 0268 case 1: 0269 $this->_info['leap'] = '1 - last minute has 61 seconds'; 0270 break; 0271 0272 case 2: 0273 $this->_info['leap'] = '2 - last minute has 59 seconds'; 0274 break; 0275 0276 default: 0277 $this->_info['leap'] = '3 - not syncronised'; 0278 break; 0279 } 0280 0281 /* 0282 * Version Number bit 0011 1000 0283 * 0284 * This should be 3 (RFC 1305) 0285 */ 0286 $this->_info['version'] = ($binary['flags'] & 0x38) >> 3; 0287 0288 /* 0289 * Mode bit 0000 0111 0290 * 0291 * Except in broadcast mode, an NTP association is formed when two peers 0292 * exchange messages and one or both of them create and maintain an 0293 * instantiation of the protocol machine, called an association. 0294 */ 0295 $mode = ($binary['flags'] & 0x07); 0296 switch($mode) { 0297 case 1: 0298 $this->_info['mode'] = 'symetric active'; 0299 break; 0300 0301 case 2: 0302 $this->_info['mode'] = 'symetric passive'; 0303 break; 0304 0305 case 3: 0306 $this->_info['mode'] = 'client'; 0307 break; 0308 0309 case 4: 0310 $this->_info['mode'] = 'server'; 0311 break; 0312 0313 case 5: 0314 $this->_info['mode'] = 'broadcast'; 0315 break; 0316 0317 default: 0318 $this->_info['mode'] = 'reserved'; 0319 break; 0320 } 0321 0322 $ntpserviceid = 'Unknown Stratum ' . $binary['stratum'] . ' Service'; 0323 0324 /* 0325 * Reference Clock Identifier 0326 * 0327 * Identifies the particular reference clock. 0328 */ 0329 $refid = strtoupper($binary['referenceid']); 0330 switch($binary['stratum']) { 0331 case 0: 0332 if (substr($refid, 0, 3) === 'DCN') { 0333 $ntpserviceid = 'DCN routing protocol'; 0334 } else if (substr($refid, 0, 4) === 'NIST') { 0335 $ntpserviceid = 'NIST public modem'; 0336 } else if (substr($refid, 0, 3) === 'TSP') { 0337 $ntpserviceid = 'TSP time protocol'; 0338 } else if (substr($refid, 0, 3) === 'DTS') { 0339 $ntpserviceid = 'Digital Time Service'; 0340 } 0341 break; 0342 0343 case 1: 0344 if (substr($refid, 0, 4) === 'ATOM') { 0345 $ntpserviceid = 'Atomic Clock (calibrated)'; 0346 } else if (substr($refid, 0, 3) === 'VLF') { 0347 $ntpserviceid = 'VLF radio'; 0348 } else if ($refid === 'CALLSIGN') { 0349 $ntpserviceid = 'Generic radio'; 0350 } else if (substr($refid, 0, 4) === 'LORC') { 0351 $ntpserviceid = 'LORAN-C radionavigation'; 0352 } else if (substr($refid, 0, 4) === 'GOES') { 0353 $ntpserviceid = 'GOES UHF environment satellite'; 0354 } else if (substr($refid, 0, 3) === 'GPS') { 0355 $ntpserviceid = 'GPS UHF satellite positioning'; 0356 } 0357 break; 0358 0359 default: 0360 $ntpserviceid = ord(substr($binary['referenceid'], 0, 1)); 0361 $ntpserviceid .= '.'; 0362 $ntpserviceid .= ord(substr($binary['referenceid'], 1, 1)); 0363 $ntpserviceid .= '.'; 0364 $ntpserviceid .= ord(substr($binary['referenceid'], 2, 1)); 0365 $ntpserviceid .= '.'; 0366 $ntpserviceid .= ord(substr($binary['referenceid'], 3, 1)); 0367 break; 0368 } 0369 0370 $this->_info['ntpid'] = $ntpserviceid; 0371 0372 /* 0373 * Stratum 0374 * 0375 * Indicates the stratum level of the local clock 0376 */ 0377 switch($binary['stratum']) { 0378 case 0: 0379 $this->_info['stratum'] = 'undefined'; 0380 break; 0381 0382 case 1: 0383 $this->_info['stratum'] = 'primary reference'; 0384 break; 0385 0386 default: 0387 $this->_info['stratum'] = 'secondary reference'; 0388 break; 0389 } 0390 0391 /* 0392 * Indicates the total roundtrip delay to the primary reference source at the 0393 * root of the synchronization subnet, in seconds. 0394 * 0395 * Both positive and negative values, depending on clock precision and skew, are 0396 * possible. 0397 */ 0398 $this->_info['rootdelay'] = $binary['rootdelay']; 0399 0400 /* 0401 * Indicates the maximum error relative to the primary reference source at the 0402 * root of the synchronization subnet, in seconds. 0403 * 0404 * Only positive values greater than zero are possible. 0405 */ 0406 $this->_info['rootdispersion'] = $binary['rootdispersion']; 0407 0408 /* 0409 * The roundtrip delay of the peer clock relative to the local clock 0410 * over the network path between them, in seconds. 0411 * 0412 * Note that this variable can take on both positive and negative values, 0413 * depending on clock precision and skew-error accumulation. 0414 */ 0415 $this->_info['roundtrip'] = $binary['receivestamp']; 0416 $this->_info['roundtrip'] -= $binary['originatestamp']; 0417 $this->_info['roundtrip'] -= $binary['transmitstamp']; 0418 $this->_info['roundtrip'] += $binary['clientreceived']; 0419 $this->_info['roundtrip'] /= 2; 0420 0421 // The offset of the peer clock relative to the local clock, in seconds. 0422 $this->_info['offset'] = $binary['receivestamp']; 0423 $this->_info['offset'] -= $binary['originatestamp']; 0424 $this->_info['offset'] += $binary['transmitstamp']; 0425 $this->_info['offset'] -= $binary['clientreceived']; 0426 $this->_info['offset'] /= 2; 0427 $time = (time() - $this->_info['offset']); 0428 0429 return $time; 0430 } 0431 }