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