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