View Javadoc

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