PageRenderTime 24ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/TimeSync/Ntp.php

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