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