1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.net.ntp;
19
20 import java.net.DatagramPacket;
21 import java.util.Arrays;
22
23 /**
24 * Implements {@link NtpV3Packet} to convert Java objects to and from the Network Time Protocol (NTP) data message header format described in RFC-1305.
25 */
26 public class NtpV3Impl implements NtpV3Packet {
27
28 private static final int MODE_INDEX = 0;
29 private static final int MODE_SHIFT = 0;
30
31 private static final int VERSION_INDEX = 0;
32 private static final int VERSION_SHIFT = 3;
33
34 private static final int LI_INDEX = 0;
35 private static final int LI_SHIFT = 6;
36
37 private static final int STRATUM_INDEX = 1;
38 private static final int POLL_INDEX = 2;
39 private static final int PRECISION_INDEX = 3;
40
41 private static final int ROOT_DELAY_INDEX = 4;
42 private static final int ROOT_DISPERSION_INDEX = 8;
43 private static final int REFERENCE_ID_INDEX = 12;
44
45 private static final int REFERENCE_TIMESTAMP_INDEX = 16;
46 private static final int ORIGINATE_TIMESTAMP_INDEX = 24;
47 private static final int RECEIVE_TIMESTAMP_INDEX = 32;
48 private static final int TRANSMIT_TIMESTAMP_INDEX = 40;
49
50 // private static final int KEY_IDENTIFIER_INDEX = 48;
51 // private static final int MESSAGE_DIGEST = 54; /* len 16 bytes */
52
53 /**
54 * Convert byte to unsigned integer. Java only has signed types, so we have to do more work to get unsigned ops.
55 *
56 * @param b input byte
57 * @return unsigned int value of byte
58 */
59 protected static final int ui(final byte b) {
60 return b & 0xFF;
61 }
62
63 /**
64 * Convert byte to unsigned long. Java only has signed types, so we have to do more work to get unsigned ops
65 *
66 * @param b input byte
67 * @return unsigned long value of byte
68 */
69 protected static final long ul(final byte b) {
70 return b & 0xFF;
71 }
72
73 private final byte[] buf = new byte[48];
74
75 private volatile DatagramPacket dp;
76
77 /** Creates a new instance of NtpV3Impl */
78 public NtpV3Impl() {
79 }
80
81 /**
82 * Compares this object against the specified object. The result is {@code true} if and only if the argument is not {@code null} and is a
83 * {@code NtpV3Impl} object that contains the same values as this object.
84 *
85 * @param obj the object to compare with.
86 * @return {@code true} if the objects are the same; {@code false} otherwise.
87 * @since 3.4
88 */
89 @Override
90 public boolean equals(final Object obj) {
91 if (this == obj) {
92 return true;
93 }
94 if (obj == null || getClass() != obj.getClass()) {
95 return false;
96 }
97 final NtpV3Impl other = (NtpV3Impl) obj;
98 return Arrays.equals(buf, other.buf);
99 }
100
101 /**
102 * Gets the datagram packet with the NTP details already filled in.
103 *
104 * @return a datagram packet.
105 */
106 @Override
107 public synchronized DatagramPacket getDatagramPacket() {
108 if (dp == null) {
109 dp = new DatagramPacket(buf, buf.length);
110 dp.setPort(NTP_PORT);
111 }
112 return dp;
113 }
114
115 /**
116 * Gets 4 bytes as 32-bit int.
117 *
118 * @return 4 bytes as 32-bit int
119 */
120 private int getInt(final int index) {
121 return ui(buf[index]) << 24 | ui(buf[index + 1]) << 16 | ui(buf[index + 2]) << 8 | ui(buf[index + 3]);
122 }
123
124 /**
125 * Gets leap indicator as defined in RFC-1305 which is a two-bit code: 0=no warning 1=last minute has 61 seconds 2=last minute has 59 seconds 3=alarm
126 * condition (clock not synchronized)
127 *
128 * @return leap indicator as defined in RFC-1305.
129 */
130 @Override
131 public int getLeapIndicator() {
132 return ui(buf[LI_INDEX]) >> LI_SHIFT & 0x3;
133 }
134
135 /**
136 * Gets Long value represented by bits starting at specified index.
137 *
138 * @return 8 bytes as 64-bit long
139 */
140 private long getLong(final int index) {
141 return ul(buf[index]) << 56 | ul(buf[index + 1]) << 48 | ul(buf[index + 2]) << 40 | ul(buf[index + 3]) << 32 | ul(buf[index + 4]) << 24
142 | ul(buf[index + 5]) << 16 | ul(buf[index + 6]) << 8 | ul(buf[index + 7]);
143 }
144
145 /**
146 * Gets the mode as defined in RFC-1305 which is a 3-bit integer whose value is indicated by the MODE_xxx parameters.
147 *
148 * @return mode as defined in RFC-1305.
149 */
150 @Override
151 public int getMode() {
152 return ui(buf[MODE_INDEX]) >> MODE_SHIFT & 0x7;
153 }
154
155 /**
156 * Gets the human-readable name of message mode type as described in RFC 1305.
157 *
158 * @return mode name as string.
159 */
160 @Override
161 public String getModeName() {
162 return NtpUtils.getModeName(getMode());
163 }
164
165 /**
166 * Gets the {@code originate} time as defined in RFC-1305.
167 *
168 * @return the {@code originate} time. Never returns null.
169 */
170 @Override
171 public TimeStamp getOriginateTimeStamp() {
172 return getTimestamp(ORIGINATE_TIMESTAMP_INDEX);
173 }
174
175 /**
176 * Gets the poll interval as defined in RFC-1305, which is an eight-bit signed integer indicating the maximum interval between successive messages, in
177 * seconds to the nearest power of two (e.g. value of six indicates an interval of 64 seconds). The values that can appear in this field range from
178 * NTP_MINPOLL to NTP_MAXPOLL inclusive.
179 *
180 * @return poll interval as defined in RFC-1305.
181 */
182 @Override
183 public int getPoll() {
184 return buf[POLL_INDEX];
185 }
186
187 /**
188 * Gets the precision as defined in RFC-1305 encoded as an 8-bit signed integer (seconds to the nearest power of two). Values normally range from -6 to -20.
189 *
190 * @return precision as defined in RFC-1305.
191 */
192 @Override
193 public int getPrecision() {
194 return buf[PRECISION_INDEX];
195 }
196
197 /**
198 * Gets the {@code receive} timestamp as defined in RFC-1305.
199 *
200 * @return the {@code receive} time. Never returns null.
201 */
202 @Override
203 public TimeStamp getReceiveTimeStamp() {
204 return getTimestamp(RECEIVE_TIMESTAMP_INDEX);
205 }
206
207 /**
208 * Gets the reference id as defined in RFC-1305, which is a 32-bit integer whose value is dependent on several criteria.
209 *
210 * @return the reference id as defined in RFC-1305.
211 */
212 @Override
213 public int getReferenceId() {
214 return getInt(REFERENCE_ID_INDEX);
215 }
216
217 /**
218 * Gets the reference id string. String cannot be null but value is dependent on the version of the NTP spec supported and stratum level. Value can be an
219 * empty string, clock type string, IP address, or a hexadecimal string.
220 *
221 * @return the reference id string.
222 */
223 @Override
224 public String getReferenceIdString() {
225 final int version = getVersion();
226 final int stratum = getStratum();
227 if (version == VERSION_3 || version == VERSION_4) {
228 if (stratum == 0 || stratum == 1) {
229 return idAsString(); // 4-character ASCII string (e.g. GPS, USNO)
230 }
231 // in NTPv4 servers this is latest transmit timestamp of ref source
232 if (version == VERSION_4) {
233 return idAsHex();
234 }
235 }
236
237 // Stratum 2 and higher this is a four-octet IPv4 address
238 // of the primary reference host.
239 if (stratum >= 2) {
240 return idAsIPAddress();
241 }
242 return idAsHex();
243 }
244
245 /**
246 * Gets the reference time as defined in RFC-1305.
247 *
248 * @return the reference time as {@code TimeStamp} object. Never returns null.
249 */
250 @Override
251 public TimeStamp getReferenceTimeStamp() {
252 return getTimestamp(REFERENCE_TIMESTAMP_INDEX);
253 }
254
255 /**
256 * Gets the root delay as defined in RFC-1305, which is the total roundtrip delay to the primary reference source, in seconds. Values can take positive and
257 * negative values, depending on clock precision and skew.
258 *
259 * @return root delay as defined in RFC-1305.
260 */
261 @Override
262 public int getRootDelay() {
263 return getInt(ROOT_DELAY_INDEX);
264 }
265
266 /**
267 * Gets the root delay as defined in RFC-1305 in milliseconds, which is the total roundtrip delay to the primary reference source, in seconds. Values can
268 * take positive and negative values, depending on clock precision and skew.
269 *
270 * @return root delay in milliseconds
271 */
272 @Override
273 public double getRootDelayInMillisDouble() {
274 final double l = getRootDelay();
275 return l / 65.536;
276 }
277
278 /**
279 * Gets the root dispersion as defined in RFC-1305.
280 *
281 * @return root dispersion.
282 */
283 @Override
284 public int getRootDispersion() {
285 return getInt(ROOT_DISPERSION_INDEX);
286 }
287
288 /**
289 * Gets the root dispersion (as defined in RFC-1305) in milliseconds.
290 *
291 * @return root dispersion in milliseconds
292 */
293 @Override
294 public long getRootDispersionInMillis() {
295 final long l = getRootDispersion();
296 return l * 1000 / 65536L;
297 }
298
299 /**
300 * Gets the root dispersion (as defined in RFC-1305) in milliseconds as double precision value.
301 *
302 * @return root dispersion in milliseconds
303 */
304 @Override
305 public double getRootDispersionInMillisDouble() {
306 final double l = getRootDispersion();
307 return l / 65.536;
308 }
309
310 /**
311 * Gets the Stratum as defined in RFC-1305, which indicates the stratum level of the local clock, with values defined as follows: 0=unspecified, 1=primary
312 * ref clock, and all others a secondary reference (via NTP).
313 *
314 * @return Stratum level as defined in RFC-1305.
315 */
316 @Override
317 public int getStratum() {
318 return ui(buf[STRATUM_INDEX]);
319 }
320
321 /**
322 * Gets the NTP Timestamp at specified starting index.
323 *
324 * @param index index into data array
325 * @return TimeStamp object for 64 bits starting at index
326 */
327 private TimeStamp getTimestamp(final int index) {
328 return new TimeStamp(getLong(index));
329 }
330
331 /**
332 * Gets the {@code transmit} timestamp as defined in RFC-1305.
333 *
334 * @return the {@code transmit} timestamp as defined in RFC-1305. Never returns a null object.
335 */
336 @Override
337 public TimeStamp getTransmitTimeStamp() {
338 return getTimestamp(TRANSMIT_TIMESTAMP_INDEX);
339 }
340
341 /**
342 * Gets the type of time packet. The values (e.g. NTP, TIME, ICMP, ...) correspond to the protocol used to obtain the timing information.
343 *
344 * @return packet type string identifier which in this case is "NTP".
345 */
346 @Override
347 public String getType() {
348 return "NTP";
349 }
350
351 /**
352 * Gets the NTP version number as defined in RFC-1305.
353 *
354 * @return NTP version number.
355 */
356 @Override
357 public int getVersion() {
358 return ui(buf[VERSION_INDEX]) >> VERSION_SHIFT & 0x7;
359 }
360
361 /**
362 * Computes a hash code for this object. The result is the exclusive OR of the values of this object stored as a byte array.
363 *
364 * @return a hash code value for this object.
365 * @since 3.4
366 */
367 @Override
368 public int hashCode() {
369 return Arrays.hashCode(buf);
370 }
371
372 private String idAsHex() {
373 return Integer.toHexString(getReferenceId());
374 }
375
376 /**
377 * Returns Reference id as dotted IP address.
378 *
379 * @return refId as IP address string.
380 */
381 private String idAsIPAddress() {
382 return ui(buf[REFERENCE_ID_INDEX]) + "." + ui(buf[REFERENCE_ID_INDEX + 1]) + "." + ui(buf[REFERENCE_ID_INDEX + 2]) + "."
383 + ui(buf[REFERENCE_ID_INDEX + 3]);
384 }
385
386 private String idAsString() {
387 final StringBuilder id = new StringBuilder();
388 for (int i = 0; i <= 3; i++) {
389 final char c = (char) buf[REFERENCE_ID_INDEX + i];
390 if (c == 0) { // 0-terminated string
391 break;
392 }
393 id.append(c);
394 }
395 return id.toString();
396 }
397
398 /**
399 * Sets the contents of this object from source datagram packet.
400 *
401 * @param srcDp source DatagramPacket to copy contents from, never null.
402 * @throws IllegalArgumentException if srcDp is null or byte length is less than minimum length of 48 bytes
403 */
404 @Override
405 public void setDatagramPacket(final DatagramPacket srcDp) {
406 if (srcDp == null || srcDp.getLength() < buf.length) {
407 throw new IllegalArgumentException();
408 }
409 final byte[] incomingBuf = srcDp.getData();
410 int len = srcDp.getLength();
411 if (len > buf.length) {
412 len = buf.length;
413 }
414 System.arraycopy(incomingBuf, 0, buf, 0, len);
415 final DatagramPacket dp = getDatagramPacket();
416 dp.setAddress(srcDp.getAddress());
417 final int port = srcDp.getPort();
418 dp.setPort(port > 0 ? port : NTP_PORT);
419 dp.setData(buf);
420 }
421
422 /**
423 * Sets integer value at index position.
424 *
425 * @param idx index position
426 * @param value 32-bit int value
427 */
428 private void setInt(final int idx, int value) {
429 for (int i = 3; i >= 0; i--) {
430 buf[idx + i] = (byte) (value & 0xff);
431 value >>>= 8; // shift right one-byte
432 }
433 }
434
435 /**
436 * Sets leap indicator as defined in RFC-1305.
437 *
438 * @param li leap indicator.
439 */
440 @Override
441 public void setLeapIndicator(final int li) {
442 buf[LI_INDEX] = (byte) (buf[LI_INDEX] & 0x3F | (li & 0x3) << LI_SHIFT);
443 }
444
445 /**
446 * Sets mode as defined in RFC-1305.
447 *
448 * @param mode the mode to set
449 */
450 @Override
451 public void setMode(final int mode) {
452 buf[MODE_INDEX] = (byte) (buf[MODE_INDEX] & 0xF8 | mode & 0x7);
453 }
454
455 /**
456 * Sets originate timestamp given NTP TimeStamp object. If {@code ts} is null then zero time is used.
457 *
458 * @param ts NTP timestamp
459 */
460 @Override
461 public void setOriginateTimeStamp(final TimeStamp ts) {
462 setTimestamp(ORIGINATE_TIMESTAMP_INDEX, ts);
463 }
464
465 /**
466 * Sets poll interval as defined in RFC-1305.
467 *
468 * @param poll poll interval.
469 */
470 @Override
471 public void setPoll(final int poll) {
472 buf[POLL_INDEX] = (byte) (poll & 0xFF);
473 }
474
475 /**
476 * Sets precision as defined in RFC-1305.
477 *
478 * @param precision the precision to set
479 * @since 3.4
480 */
481 @Override
482 public void setPrecision(final int precision) {
483 buf[PRECISION_INDEX] = (byte) (precision & 0xFF);
484 }
485
486 /**
487 * Sets receive timestamp given NTP TimeStamp object. If {@code ts} is null then zero time is used.
488 *
489 * @param ts timestamp
490 */
491 @Override
492 public void setReceiveTimeStamp(final TimeStamp ts) {
493 setTimestamp(RECEIVE_TIMESTAMP_INDEX, ts);
494 }
495
496 /**
497 * Sets reference clock identifier field with 32-bit unsigned integer value. See RFC-1305 for description.
498 *
499 * @param refId reference clock identifier.
500 */
501 @Override
502 public void setReferenceId(final int refId) {
503 setInt(REFERENCE_ID_INDEX, refId);
504 }
505
506 /**
507 * Sets Reference time with NTP timestamp. If {@code ts} is null then zero time is used.
508 *
509 * @param ts NTP timestamp
510 */
511 @Override
512 public void setReferenceTime(final TimeStamp ts) {
513 setTimestamp(REFERENCE_TIMESTAMP_INDEX, ts);
514 }
515
516 /**
517 * Sets root delay as defined in RFC-1305.
518 *
519 * @param delay root delay
520 * @since 3.4
521 */
522 @Override
523 public void setRootDelay(final int delay) {
524 setInt(ROOT_DELAY_INDEX, delay);
525 }
526
527 /**
528 * Sets root dispersion as defined in RFC-1305.
529 *
530 * @param dispersion root dispersion
531 * @since 3.4
532 */
533 @Override
534 public void setRootDispersion(final int dispersion) {
535 setInt(ROOT_DISPERSION_INDEX, dispersion);
536 }
537
538 /**
539 * Sets stratum level as defined in RFC-1305.
540 *
541 * @param stratum stratum level.
542 */
543 @Override
544 public void setStratum(final int stratum) {
545 buf[STRATUM_INDEX] = (byte) (stratum & 0xFF);
546 }
547
548 /**
549 * Sets the NTP timestamp at the given array index.
550 *
551 * @param index index into the byte array.
552 * @param t TimeStamp.
553 */
554 private void setTimestamp(final int index, final TimeStamp t) {
555 long ntpTime = t == null ? 0 : t.ntpValue();
556 // copy 64-bits from Long value into 8 x 8-bit bytes of array
557 // one byte at a time shifting 8-bits for each position.
558 for (int i = 7; i >= 0; i--) {
559 buf[index + i] = (byte) (ntpTime & 0xFF);
560 ntpTime >>>= 8; // shift to next byte
561 }
562 // buf[index] |= 0x80; // only set if 1900 baseline....
563 }
564
565 /**
566 * Sets transmit time with NTP timestamp. If {@code ts} is null then zero time is used.
567 *
568 * @param ts NTP timestamp
569 */
570 @Override
571 public void setTransmitTime(final TimeStamp ts) {
572 setTimestamp(TRANSMIT_TIMESTAMP_INDEX, ts);
573 }
574
575 /**
576 * Sets NTP version as defined in RFC-1305.
577 *
578 * @param version NTP version.
579 */
580 @Override
581 public void setVersion(final int version) {
582 buf[VERSION_INDEX] = (byte) (buf[VERSION_INDEX] & 0xC7 | (version & 0x7) << VERSION_SHIFT);
583 }
584
585 /**
586 * Returns details of NTP packet as a string.
587 *
588 * @return details of NTP packet as a string.
589 */
590 @Override
591 public String toString() {
592 return "[version:" + getVersion() + ", mode:" + getMode() + ", poll:" + getPoll() + ", precision:" + getPrecision() + ", delay:" + getRootDelay()
593 + ", dispersion(ms):" + getRootDispersionInMillisDouble() + ", id:" + getReferenceIdString() + ", xmitTime:"
594 + getTransmitTimeStamp().toDateString() + " ]";
595 }
596
597 }