001package org.apache.commons.net.ntp;
002/*
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements.  See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License.  You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019import java.net.DatagramPacket;
020
021/***
022 * Implementation of NtpV3Packet with methods converting Java objects to/from
023 * the Network Time Protocol (NTP) data message header format described in RFC-1305.
024 *
025 * @author Naz Irizarry, MITRE Corp
026 * @author Jason Mathews, MITRE Corp
027 *
028 * @version $Revision: 1230358 $ $Date: 2012-01-12 01:51:02 +0000 (Thu, 12 Jan 2012) $
029 */
030public class NtpV3Impl implements NtpV3Packet
031{
032
033    private static final int MODE_INDEX = 0;
034    private static final int MODE_SHIFT = 0;
035
036    private static final int VERSION_INDEX = 0;
037    private static final int VERSION_SHIFT = 3;
038
039    private static final int LI_INDEX = 0;
040    private static final int LI_SHIFT = 6;
041
042    private static final int STRATUM_INDEX = 1;
043    private static final int POLL_INDEX = 2;
044    private static final int PRECISION_INDEX = 3;
045
046    private static final int ROOT_DELAY_INDEX = 4;
047    private static final int ROOT_DISPERSION_INDEX = 8;
048    private static final int REFERENCE_ID_INDEX = 12;
049
050    private static final int REFERENCE_TIMESTAMP_INDEX = 16;
051    private static final int ORIGINATE_TIMESTAMP_INDEX = 24;
052    private static final int RECEIVE_TIMESTAMP_INDEX = 32;
053    private static final int TRANSMIT_TIMESTAMP_INDEX = 40;
054
055//    private static final int KEY_IDENTIFIER_INDEX = 48;
056//    private static final int MESSAGE_DIGEST = 54; /* len 16 bytes */
057
058    private final byte[] buf = new byte[48];
059
060    private volatile DatagramPacket dp;
061
062    /** Creates a new instance of NtpV3Impl */
063    public NtpV3Impl()
064    {
065    }
066
067    /***
068     * Returns mode as defined in RFC-1305 which is a 3-bit integer
069     * whose value is indicated by the MODE_xxx parameters.
070     *
071     * @return mode as defined in RFC-1305.
072     */
073    public int getMode()
074    {
075        return (ui(buf[MODE_INDEX]) >> MODE_SHIFT) & 0x7;
076    }
077
078    /***
079     * Return human-readable name of message mode type as described in
080     * RFC 1305.
081     * @return mode name as string.
082     */
083    public String getModeName()
084    {
085        return NtpUtils.getModeName(getMode());
086    }
087
088    /***
089     * Set mode as defined in RFC-1305.
090     * @param mode
091     */
092    public void setMode(int mode)
093    {
094        buf[MODE_INDEX] = (byte) (buf[MODE_INDEX] & 0xF8 | mode & 0x7);
095    }
096
097    /***
098     * Returns leap indicator as defined in RFC-1305 which is a two-bit code:
099     *  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
312        // Stratum 2 and higher this is a four-octet IPv4 address
313        // of the primary reference host.
314        if (stratum >= 2) {
315            return idAsIPAddress();
316        }
317        return idAsHex();
318    }
319
320    /***
321     * Returns Reference id as dotted IP address.
322     * @return refId as IP address string.
323     */
324    private String idAsIPAddress()
325    {
326        return ui(buf[REFERENCE_ID_INDEX]) + "." +
327                ui(buf[REFERENCE_ID_INDEX + 1]) + "." +
328                ui(buf[REFERENCE_ID_INDEX + 2]) + "." +
329                ui(buf[REFERENCE_ID_INDEX + 3]);
330    }
331
332    private String idAsString()
333    {
334        StringBuilder id = new StringBuilder();
335        for (int i = 0; i <= 3; i++) {
336            char c = (char) buf[REFERENCE_ID_INDEX + i];
337            if (c == 0) {  // 0-terminated string
338                break;
339            }
340            id.append(c);
341        }
342        return id.toString();
343    }
344
345    private String idAsHex()
346    {
347        return Integer.toHexString(getReferenceId());
348    }
349
350    /***
351     * Returns the transmit timestamp as defined in RFC-1305.
352     *
353     * @return the transmit timestamp as defined in RFC-1305.
354     * Never returns a null object.
355     */
356    public TimeStamp getTransmitTimeStamp()
357    {
358        return getTimestamp(TRANSMIT_TIMESTAMP_INDEX);
359    }
360
361    /***
362     * Set transmit time with NTP timestamp.
363     * If <code>ts</code> is null then zero time is used.
364     *
365     * @param ts NTP timestamp
366     */
367    public void setTransmitTime(TimeStamp ts)
368    {
369        setTimestamp(TRANSMIT_TIMESTAMP_INDEX, ts);
370    }
371
372    /***
373     * Set originate timestamp given NTP TimeStamp object.
374     * If <code>ts</code> is null then zero time is used.
375     *
376     * @param ts NTP timestamp
377     */
378    public void setOriginateTimeStamp(TimeStamp ts)
379    {
380        setTimestamp(ORIGINATE_TIMESTAMP_INDEX, ts);
381    }
382
383    /***
384     * Returns the originate time as defined in RFC-1305.
385     *
386     * @return the originate time.
387     * Never returns null.
388     */
389    public TimeStamp getOriginateTimeStamp()
390    {
391        return getTimestamp(ORIGINATE_TIMESTAMP_INDEX);
392    }
393
394    /***
395     * Returns the reference time as defined in RFC-1305.
396     *
397     * @return the reference time as <code>TimeStamp</code> object.
398     * Never returns null.
399     */
400    public TimeStamp getReferenceTimeStamp()
401    {
402        return getTimestamp(REFERENCE_TIMESTAMP_INDEX);
403    }
404
405    /***
406     * Set Reference time with NTP timestamp. If <code>ts</code> is null
407     * then zero time is used.
408     *
409     * @param ts NTP timestamp
410     */
411    public void setReferenceTime(TimeStamp ts)
412    {
413        setTimestamp(REFERENCE_TIMESTAMP_INDEX, ts);
414    }
415
416    /***
417     * Returns receive timestamp as defined in RFC-1305.
418     *
419     * @return the receive time.
420     * Never returns null.
421     */
422    public TimeStamp getReceiveTimeStamp()
423    {
424        return getTimestamp(RECEIVE_TIMESTAMP_INDEX);
425    }
426
427    /***
428     * Set receive timestamp given NTP TimeStamp object.
429     * If <code>ts</code> is null then zero time is used.
430     *
431     * @param ts timestamp
432     */
433    public void setReceiveTimeStamp(TimeStamp ts)
434    {
435        setTimestamp(RECEIVE_TIMESTAMP_INDEX, ts);
436    }
437
438    /***
439     * Return type of time packet. The values (e.g. NTP, TIME, ICMP, ...)
440     * correspond to the protocol used to obtain the timing information.
441     *
442     * @return packet type string identifier which in this case is "NTP".
443     */
444    public String getType()
445    {
446        return "NTP";
447    }
448
449    /***
450     * @return 4 bytes as 32-bit int
451     */
452    private int getInt(int index)
453    {
454        int i = ui(buf[index]) << 24 |
455                ui(buf[index + 1]) << 16 |
456                ui(buf[index + 2]) << 8 |
457                ui(buf[index + 3]);
458
459        return i;
460    }
461
462    /***
463     * Get NTP Timestamp at specified starting index.
464     *
465     * @param index index into data array
466     * @return TimeStamp object for 64 bits starting at index
467     */
468    private TimeStamp getTimestamp(int index)
469    {
470        return new TimeStamp(getLong(index));
471    }
472
473    /***
474     * Get Long value represented by bits starting at specified index.
475     *
476     * @return 8 bytes as 64-bit long
477     */
478    private long getLong(int index)
479    {
480        long i = ul(buf[index]) << 56 |
481                ul(buf[index + 1]) << 48 |
482                ul(buf[index + 2]) << 40 |
483                ul(buf[index + 3]) << 32 |
484                ul(buf[index + 4]) << 24 |
485                ul(buf[index + 5]) << 16 |
486                ul(buf[index + 6]) << 8 |
487                ul(buf[index + 7]);
488        return i;
489    }
490
491    /***
492     * Sets the NTP timestamp at the given array index.
493     *
494     * @param index index into the byte array.
495     * @param t TimeStamp.
496     */
497    private void setTimestamp(int index, TimeStamp t)
498    {
499        long ntpTime = (t == null) ? 0 : t.ntpValue();
500        // copy 64-bits from Long value into 8 x 8-bit bytes of array
501        // one byte at a time shifting 8-bits for each position.
502        for (int i = 7; i >= 0; i--) {
503            buf[index + i] = (byte) (ntpTime & 0xFF);
504            ntpTime >>>= 8; // shift to next byte
505        }
506        // buf[index] |= 0x80;  // only set if 1900 baseline....
507    }
508
509    /***
510     * Returns the datagram packet with the NTP details already filled in.
511     *
512     * @return a datagram packet.
513     */
514    public synchronized DatagramPacket getDatagramPacket()
515    {
516        if (dp == null) {
517            dp = new DatagramPacket(buf, buf.length);
518            dp.setPort(NTP_PORT);
519        }
520        return dp;
521    }
522
523    /***
524     * Set the contents of this object from source datagram packet.
525     *
526     * @param srcDp source DatagramPacket to copy contents from.
527     */
528    public void setDatagramPacket(DatagramPacket srcDp)
529    {
530        byte[] incomingBuf = srcDp.getData();
531        int len = srcDp.getLength();
532        if (len > buf.length) {
533            len = buf.length;
534        }
535
536        System.arraycopy(incomingBuf, 0, buf, 0, len);
537    }
538
539    /***
540     * Convert byte to unsigned integer.
541     * Java only has signed types so we have to do
542     * more work to get unsigned ops.
543     *
544     * @param b
545     * @return unsigned int value of byte
546     */
547    protected final static int ui(byte b)
548    {
549        int i = b & 0xFF;
550        return i;
551    }
552
553    /***
554     * Convert byte to unsigned long.
555     * Java only has signed types so we have to do
556     * more work to get unsigned ops
557     *
558     * @param b
559     * @return unsigned long value of byte
560     */
561    protected final static long ul(byte b)
562    {
563        long i = b & 0xFF;
564        return i;
565    }
566
567    /***
568     * Returns details of NTP packet as a string.
569     *
570     * @return details of NTP packet as a string.
571     */
572    @Override
573    public String toString()
574    {
575        return "[" +
576                "version:" + getVersion() +
577                ", mode:" + getMode() +
578                ", poll:" + getPoll() +
579                ", precision:" + getPrecision() +
580                ", delay:" + getRootDelay() +
581                ", dispersion(ms):" + getRootDispersionInMillisDouble() +
582                ", id:" + getReferenceIdString() +
583                ", xmitTime:" + getTransmitTimeStamp().toDateString() +
584                " ]";
585    }
586
587}