PageRenderTime 60ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/TimeSync/Ntp.php

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