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 */
026public class NtpV3Impl implements NtpV3Packet
027{
028
029    private static final int MODE_INDEX = 0;
030    private static final int MODE_SHIFT = 0;
031
032    private static final int VERSION_INDEX = 0;
033    private static final int VERSION_SHIFT = 3;
034
035    private static final int LI_INDEX = 0;
036    private static final int LI_SHIFT = 6;
037
038    private static final int STRATUM_INDEX = 1;
039    private static final int POLL_INDEX = 2;
040    private static final int PRECISION_INDEX = 3;
041
042    private static final int ROOT_DELAY_INDEX = 4;
043    private static final int ROOT_DISPERSION_INDEX = 8;
044    private static final int REFERENCE_ID_INDEX = 12;
045
046    private static final int REFERENCE_TIMESTAMP_INDEX = 16;
047    private static final int ORIGINATE_TIMESTAMP_INDEX = 24;
048    private static final int RECEIVE_TIMESTAMP_INDEX = 32;
049    private static final int TRANSMIT_TIMESTAMP_INDEX = 40;
050
051//    private static final int KEY_IDENTIFIER_INDEX = 48;
052//    private static final int MESSAGE_DIGEST = 54; /* len 16 bytes */
053
054    private final byte[] buf = new byte[48];
055
056    private volatile DatagramPacket dp;
057
058    /** Creates a new instance of NtpV3Impl */
059    public NtpV3Impl()
060    {
061    }
062
063    /**
064     * Returns mode as defined in RFC-1305 which is a 3-bit integer
065     * whose value is indicated by the MODE_xxx parameters.
066     *
067     * @return mode as defined in RFC-1305.
068     */
069    @Override
070    public int getMode()
071    {
072        return (ui(buf[MODE_INDEX]) >> MODE_SHIFT) & 0x7;
073    }
074
075    /**
076     * Return human-readable name of message mode type as described in
077     * RFC 1305.
078     * @return mode name as string.
079     */
080    @Override
081    public String getModeName()
082    {
083        return NtpUtils.getModeName(getMode());
084    }
085
086    /**
087     * Set mode as defined in RFC-1305.
088     *
089     * @param mode the mode to set
090     */
091    @Override
092    public void setMode(final 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    @Override
107    public int getLeapIndicator()
108    {
109        return (ui(buf[LI_INDEX]) >> LI_SHIFT) & 0x3;
110    }
111
112    /**
113     * Set leap indicator as defined in RFC-1305.
114     *
115     * @param li leap indicator.
116     */
117    @Override
118    public void setLeapIndicator(final int li)
119    {
120        buf[LI_INDEX] = (byte) (buf[LI_INDEX] & 0x3F | ((li & 0x3) << LI_SHIFT));
121    }
122
123    /**
124     * Returns poll interval as defined in RFC-1305, which is an eight-bit
125     * signed integer indicating the maximum interval between successive
126     * messages, in seconds to the nearest power of two (e.g. value of six
127     * indicates an interval of 64 seconds. The values that can appear in
128     * this field range from NTP_MINPOLL to NTP_MAXPOLL inclusive.
129     *
130     * @return poll interval as defined in RFC-1305.
131     */
132    @Override
133    public int getPoll()
134    {
135        return buf[POLL_INDEX];
136    }
137
138    /**
139     * Set poll interval as defined in RFC-1305.
140     *
141     * @param poll poll interval.
142     */
143    @Override
144    public void setPoll(final int poll)
145    {
146        buf[POLL_INDEX] = (byte) (poll & 0xFF);
147    }
148
149    /**
150     * Returns precision as defined in RFC-1305 encoded as an 8-bit signed
151     * integer (seconds to nearest power of two).
152     * Values normally range from -6 to -20.
153     *
154     * @return precision as defined in RFC-1305.
155     */
156    @Override
157    public int getPrecision()
158    {
159        return buf[PRECISION_INDEX];
160    }
161
162    /**
163     * Set precision as defined in RFC-1305.
164     * @param precision the precision to set
165     * @since 3.4
166     */
167    @Override
168    public void setPrecision(final 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(final 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(final 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     * Set root delay as defined in RFC-1305.
234     *
235     * @param delay root delay
236     * @since 3.4
237     */
238    @Override
239    public void setRootDelay(final int delay)
240    {
241        setInt(ROOT_DELAY_INDEX, delay);
242    }
243
244    /**
245     * Return root delay as defined in RFC-1305 in milliseconds, which is
246     * the total roundtrip delay to the primary reference source, in
247     * seconds. Values can take positive and negative values, depending
248     * on clock precision and skew.
249     *
250     * @return root delay in milliseconds
251     */
252    @Override
253    public double getRootDelayInMillisDouble()
254    {
255        final double l = getRootDelay();
256        return l / 65.536;
257    }
258
259    /**
260     * Returns root dispersion as defined in RFC-1305.
261     * @return root dispersion.
262     */
263    @Override
264    public int getRootDispersion()
265    {
266        return getInt(ROOT_DISPERSION_INDEX);
267    }
268
269    /**
270     * Set root dispersion as defined in RFC-1305.
271     *
272     * @param dispersion root dispersion
273     * @since 3.4
274     */
275    @Override
276    public void setRootDispersion(final int dispersion)
277    {
278        setInt(ROOT_DISPERSION_INDEX, dispersion);
279    }
280
281    /**
282     * Returns root dispersion (as defined in RFC-1305) in milliseconds.
283     *
284     * @return root dispersion in milliseconds
285     */
286    @Override
287    public long getRootDispersionInMillis()
288    {
289        final long l = getRootDispersion();
290        return (l * 1000) / 65536L;
291    }
292
293    /**
294     * Returns root dispersion (as defined in RFC-1305) in milliseconds
295     * as double precision value.
296     *
297     * @return root dispersion in milliseconds
298     */
299    @Override
300    public double getRootDispersionInMillisDouble()
301    {
302        final double l = getRootDispersion();
303        return l / 65.536;
304    }
305
306    /**
307     * Set reference clock identifier field with 32-bit unsigned integer value.
308     * See RFC-1305 for description.
309     *
310     * @param refId reference clock identifier.
311     */
312    @Override
313    public void setReferenceId(final int refId)
314    {
315        setInt(REFERENCE_ID_INDEX, refId);
316    }
317
318    /**
319     * Returns the reference id as defined in RFC-1305, which is
320     * a 32-bit integer whose value is dependent on several criteria.
321     *
322     * @return the reference id as defined in RFC-1305.
323     */
324    @Override
325    public int getReferenceId()
326    {
327        return getInt(REFERENCE_ID_INDEX);
328    }
329
330    /**
331     * Returns the reference id string. String cannot be null but
332     * value is dependent on the version of the NTP spec supported
333     * and stratum level. Value can be an empty string, clock type string,
334     * IP address, or a hex string.
335     *
336     * @return the reference id string.
337     */
338    @Override
339    public String getReferenceIdString()
340    {
341        final int version = getVersion();
342        final int stratum = getStratum();
343        if (version == VERSION_3 || version == VERSION_4) {
344            if (stratum == 0 || stratum == 1) {
345                return idAsString(); // 4-character ASCII string (e.g. GPS, USNO)
346            }
347            // in NTPv4 servers this is latest transmit timestamp of ref source
348            if (version == VERSION_4) {
349                return idAsHex();
350            }
351        }
352
353        // Stratum 2 and higher this is a four-octet IPv4 address
354        // of the primary reference host.
355        if (stratum >= 2) {
356            return idAsIPAddress();
357        }
358        return idAsHex();
359    }
360
361    /**
362     * Returns Reference id as dotted IP address.
363     * @return refId as IP address string.
364     */
365    private String idAsIPAddress()
366    {
367        return ui(buf[REFERENCE_ID_INDEX]) + "." +
368                ui(buf[REFERENCE_ID_INDEX + 1]) + "." +
369                ui(buf[REFERENCE_ID_INDEX + 2]) + "." +
370                ui(buf[REFERENCE_ID_INDEX + 3]);
371    }
372
373    private String idAsString()
374    {
375        final StringBuilder id = new StringBuilder();
376        for (int i = 0; i <= 3; i++) {
377            final char c = (char) buf[REFERENCE_ID_INDEX + i];
378            if (c == 0) {  // 0-terminated string
379                break;
380            }
381            id.append(c);
382        }
383        return id.toString();
384    }
385
386    private String idAsHex()
387    {
388        return Integer.toHexString(getReferenceId());
389    }
390
391    /**
392     * Returns the transmit timestamp as defined in RFC-1305.
393     *
394     * @return the transmit timestamp as defined in RFC-1305.
395     * Never returns a null object.
396     */
397    @Override
398    public TimeStamp getTransmitTimeStamp()
399    {
400        return getTimestamp(TRANSMIT_TIMESTAMP_INDEX);
401    }
402
403    /**
404     * Set transmit time with NTP timestamp.
405     * If <code>ts</code> is null then zero time is used.
406     *
407     * @param ts NTP timestamp
408     */
409    @Override
410    public void setTransmitTime(final TimeStamp ts)
411    {
412        setTimestamp(TRANSMIT_TIMESTAMP_INDEX, ts);
413    }
414
415    /**
416     * Set originate timestamp given NTP TimeStamp object.
417     * If <code>ts</code> is null then zero time is used.
418     *
419     * @param ts NTP timestamp
420     */
421    @Override
422    public void setOriginateTimeStamp(final TimeStamp ts)
423    {
424        setTimestamp(ORIGINATE_TIMESTAMP_INDEX, ts);
425    }
426
427    /**
428     * Returns the originate time as defined in RFC-1305.
429     *
430     * @return the originate time.
431     * Never returns null.
432     */
433    @Override
434    public TimeStamp getOriginateTimeStamp()
435    {
436        return getTimestamp(ORIGINATE_TIMESTAMP_INDEX);
437    }
438
439    /**
440     * Returns the reference time as defined in RFC-1305.
441     *
442     * @return the reference time as <code>TimeStamp</code> object.
443     * Never returns null.
444     */
445    @Override
446    public TimeStamp getReferenceTimeStamp()
447    {
448        return getTimestamp(REFERENCE_TIMESTAMP_INDEX);
449    }
450
451    /**
452     * Set Reference time with NTP timestamp. If <code>ts</code> is null
453     * then zero time is used.
454     *
455     * @param ts NTP timestamp
456     */
457    @Override
458    public void setReferenceTime(final TimeStamp ts)
459    {
460        setTimestamp(REFERENCE_TIMESTAMP_INDEX, ts);
461    }
462
463    /**
464     * Returns receive timestamp as defined in RFC-1305.
465     *
466     * @return the receive time.
467     * Never returns null.
468     */
469    @Override
470    public TimeStamp getReceiveTimeStamp()
471    {
472        return getTimestamp(RECEIVE_TIMESTAMP_INDEX);
473    }
474
475    /**
476     * Set receive timestamp given NTP TimeStamp object.
477     * If <code>ts</code> is null then zero time is used.
478     *
479     * @param ts timestamp
480     */
481    @Override
482    public void setReceiveTimeStamp(final TimeStamp ts)
483    {
484        setTimestamp(RECEIVE_TIMESTAMP_INDEX, ts);
485    }
486
487    /**
488     * Return type of time packet. The values (e.g. NTP, TIME, ICMP, ...)
489     * correspond to the protocol used to obtain the timing information.
490     *
491     * @return packet type string identifier which in this case is "NTP".
492     */
493    @Override
494    public String getType()
495    {
496        return "NTP";
497    }
498
499    /**
500     * @return 4 bytes as 32-bit int
501     */
502    private int getInt(final int index)
503    {
504        final int i = ui(buf[index]) << 24 |
505                ui(buf[index + 1]) << 16 |
506                ui(buf[index + 2]) << 8 |
507                ui(buf[index + 3]);
508
509        return i;
510    }
511
512    /**
513     * Set integer value at index position.
514     *
515     * @param idx index position
516     * @param value 32-bit int value
517     */
518    private void setInt(final int idx, int value)
519    {
520        for (int i=3; i >= 0; i--) {
521            buf[idx + i] = (byte) (value & 0xff);
522            value >>>= 8; // shift right one-byte
523        }
524    }
525
526    /**
527     * Get NTP Timestamp at specified starting index.
528     *
529     * @param index index into data array
530     * @return TimeStamp object for 64 bits starting at index
531     */
532    private TimeStamp getTimestamp(final int index)
533    {
534        return new TimeStamp(getLong(index));
535    }
536
537    /**
538     * Get Long value represented by bits starting at specified index.
539     *
540     * @return 8 bytes as 64-bit long
541     */
542    private long getLong(final int index)
543    {
544        final long i = ul(buf[index]) << 56 |
545                ul(buf[index + 1]) << 48 |
546                ul(buf[index + 2]) << 40 |
547                ul(buf[index + 3]) << 32 |
548                ul(buf[index + 4]) << 24 |
549                ul(buf[index + 5]) << 16 |
550                ul(buf[index + 6]) << 8 |
551                ul(buf[index + 7]);
552        return i;
553    }
554
555    /**
556     * Sets the NTP timestamp at the given array index.
557     *
558     * @param index index into the byte array.
559     * @param t TimeStamp.
560     */
561    private void setTimestamp(final int index, final TimeStamp t)
562    {
563        long ntpTime = (t == null) ? 0 : t.ntpValue();
564        // copy 64-bits from Long value into 8 x 8-bit bytes of array
565        // one byte at a time shifting 8-bits for each position.
566        for (int i = 7; i >= 0; i--) {
567            buf[index + i] = (byte) (ntpTime & 0xFF);
568            ntpTime >>>= 8; // shift to next byte
569        }
570        // buf[index] |= 0x80;  // only set if 1900 baseline....
571    }
572
573    /**
574     * Returns the datagram packet with the NTP details already filled in.
575     *
576     * @return a datagram packet.
577     */
578    @Override
579    public synchronized DatagramPacket getDatagramPacket()
580    {
581        if (dp == null) {
582            dp = new DatagramPacket(buf, buf.length);
583            dp.setPort(NTP_PORT);
584        }
585        return dp;
586    }
587
588    /**
589     * Set the contents of this object from source datagram packet.
590     *
591     * @param srcDp source DatagramPacket to copy contents from, never null.
592     * @throws IllegalArgumentException if srcDp is null or byte length is less than minimum length of 48 bytes
593     */
594    @Override
595    public void setDatagramPacket(final DatagramPacket srcDp)
596    {
597        if (srcDp == null || srcDp.getLength() < buf.length) {
598            throw new IllegalArgumentException();
599        }
600        final byte[] incomingBuf = srcDp.getData();
601        int len = srcDp.getLength();
602        if (len > buf.length) {
603            len = buf.length;
604        }
605        System.arraycopy(incomingBuf, 0, buf, 0, len);
606        final DatagramPacket dp = getDatagramPacket();
607        dp.setAddress(srcDp.getAddress());
608        final int port = srcDp.getPort();
609        dp.setPort(port > 0 ? port : NTP_PORT);
610        dp.setData(buf);
611    }
612
613    /**
614     * Compares this object against the specified object.
615     * The result is <code>true</code> if and only if the argument is
616     * not <code>null</code> and is a <code>NtpV3Impl</code> object that
617     * contains the same values as this object.
618     *
619     * @param   obj   the object to compare with.
620     * @return  <code>true</code> if the objects are the same;
621     *          <code>false</code> otherwise.
622     * @since 3.4
623     */
624    @Override
625    public boolean equals(final Object obj)
626    {
627        if (this == obj) {
628            return true;
629        }
630        if (obj == null || getClass() != obj.getClass()) {
631            return false;
632        }
633        final NtpV3Impl other = (NtpV3Impl) obj;
634        return java.util.Arrays.equals(buf, other.buf);
635    }
636
637    /**
638     * Computes a hashcode for this object. The result is the exclusive
639     * OR of the values of this object stored as a byte array.
640     *
641     * @return  a hash code value for this object.
642     * @since 3.4
643     */
644    @Override
645    public int hashCode()
646    {
647        return java.util.Arrays.hashCode(buf);
648    }
649
650    /**
651     * Convert byte to unsigned integer.
652     * Java only has signed types so we have to do
653     * more work to get unsigned ops.
654     *
655     * @param b input byte
656     * @return unsigned int value of byte
657     */
658    protected static final int ui(final byte b)
659    {
660        final int i = b & 0xFF;
661        return i;
662    }
663
664    /**
665     * Convert byte to unsigned long.
666     * Java only has signed types so we have to do
667     * more work to get unsigned ops
668     *
669     * @param b input byte
670     * @return unsigned long value of byte
671     */
672    protected static final long ul(final byte b)
673    {
674        final long i = b & 0xFF;
675        return i;
676    }
677
678    /**
679     * Returns details of NTP packet as a string.
680     *
681     * @return details of NTP packet as a string.
682     */
683    @Override
684    public String toString()
685    {
686        return "[" +
687                "version:" + getVersion() +
688                ", mode:" + getMode() +
689                ", poll:" + getPoll() +
690                ", precision:" + getPrecision() +
691                ", delay:" + getRootDelay() +
692                ", dispersion(ms):" + getRootDispersionInMillisDouble() +
693                ", id:" + getReferenceIdString() +
694                ", xmitTime:" + getTransmitTimeStamp().toDateString() +
695                " ]";
696    }
697
698}