|
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 } |