    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.net.ntp;
    019
    020import java.net.DatagramPacket;
    021
    022/**
    023 * Implementation of NtpV3Packet with methods converting Java objects to/from the Network Time Protocol (NTP) data message header format described in RFC-1305.
    024 */
    025public class NtpV3Impl implements NtpV3Packet {
    026
    027    private static final int MODE_INDEX = 0;
    028    private static final int MODE_SHIFT = 0;
    029
    030    private static final int VERSION_INDEX = 0;
    031    private static final int VERSION_SHIFT = 3;
    032
    033    private static final int LI_INDEX = 0;
    034    private static final int LI_SHIFT = 6;
    035
    036    private static final int STRATUM_INDEX = 1;
    037    private static final int POLL_INDEX = 2;
    038    private static final int PRECISION_INDEX = 3;
    039
    040    private static final int ROOT_DELAY_INDEX = 4;
    041    private static final int ROOT_DISPERSION_INDEX = 8;
    042    private static final int REFERENCE_ID_INDEX = 12;
    043
    044    private static final int REFERENCE_TIMESTAMP_INDEX = 16;
    045    private static final int ORIGINATE_TIMESTAMP_INDEX = 24;
    046    private static final int RECEIVE_TIMESTAMP_INDEX = 32;
    047    private static final int TRANSMIT_TIMESTAMP_INDEX = 40;
    048
    049//    private static final int KEY_IDENTIFIER_INDEX = 48;
    050//    private static final int MESSAGE_DIGEST = 54; /* len 16 bytes */
    051
    052    /**
    053     * Convert byte to unsigned integer. Java only has signed types, so we have to do more work to get unsigned ops.
    054     *
    055     * @param b input byte
    056     * @return unsigned int value of byte
    057     */
    058    protected static final int ui(final byte b) {
    059        final int i = b & 0xFF;
    060        return i;
    061    }
    062
    063    /**
    064     * Convert byte to unsigned long. Java only has signed types, so we have to do more work to get unsigned ops
    065     *
    066     * @param b input byte
    067     * @return unsigned long value of byte
    068     */
    069    protected static final long ul(final byte b) {
    070        final long i = b & 0xFF;
    071        return i;
    072    }
    073
    074    private final byte[] buf = new byte[48];
    075
    076    private volatile DatagramPacket dp;
    077
    078    /** Creates a new instance of NtpV3Impl */
    079    public NtpV3Impl() {
    080    }
    081
    082    /**
    083     * Compares this object against the specified object. The result is {@code true} if and only if the argument is not <code>null</code> and is a
    084     * <code>NtpV3Impl</code> object that contains the same values as this object.
    085     *
    086     * @param obj the object to compare with.
    087     * @return {@code true} if the objects are the same; {@code false} otherwise.
    088     * @since 3.4
    089     */
    090    @Override
    091    public boolean equals(final Object obj) {
    092        if (this == obj) {
    093            return true;
    094        }
    095        if (obj == null || getClass() != obj.getClass()) {
    096            return false;
    097        }
    098        final NtpV3Impl other = (NtpV3Impl) obj;
    099        return java.util.Arrays.equals(buf, other.buf);
    100    }
    101
    102    /**
    103     * Returns the datagram packet with the NTP details already filled in.
    104     *
    105     * @return a datagram packet.
    106     */
    107    @Override
    108    public synchronized DatagramPacket getDatagramPacket() {
    109        if (dp == null) {
    110            dp = new DatagramPacket(buf, buf.length);
    111            dp.setPort(NTP_PORT);
    112        }
    113        return dp;
    114    }
    115
    116    /**
    117     * @return 4 bytes as 32-bit int
    118     */
    119    private int getInt(final int index) {
    120        final int i = ui(buf[index]) << 24 | ui(buf[index + 1]) << 16 | ui(buf[index + 2]) << 8 | ui(buf[index + 3]);
    121
    122        return i;
    123    }
    124
    125    /**
    126     * 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
    127     * condition (clock not synchronized)
    128     *
    129     * @return leap indicator as defined in RFC-1305.
    130     */
    131    @Override
    132    public int getLeapIndicator() {
    133        return ui(buf[LI_INDEX]) >> LI_SHIFT & 0x3;
    134    }
    135
    136    /**
    137     * Get Long value represented by bits starting at specified index.
    138     *
    139     * @return 8 bytes as 64-bit long
    140     */
    141    private long getLong(final int index) {
    142        final long i = ul(buf[index]) << 56 | ul(buf[index + 1]) << 48 | ul(buf[index + 2]) << 40 | ul(buf[index + 3]) << 32 | ul(buf[index + 4]) << 24
    143                | ul(buf[index + 5]) << 16 | ul(buf[index + 6]) << 8 | ul(buf[index + 7]);
    144        return i;
    145    }
    146
    147    /**
    148     * Returns mode as defined in RFC-1305 which is a 3-bit integer whose value is indicated by the MODE_xxx parameters.
    149     *
    150     * @return mode as defined in RFC-1305.
    151     */
    152    @Override
    153    public int getMode() {
    154        return ui(buf[MODE_INDEX]) >> MODE_SHIFT & 0x7;
    155    }
    156
    157    /**
    158     * Return human-readable name of message mode type as described in RFC 1305.
    159     *
    160     * @return mode name as string.
    161     */
    162    @Override
    163    public String getModeName() {
    164        return NtpUtils.getModeName(getMode());
    165    }
    166
    167    /**
    168     * Returns the {@code originate} time as defined in RFC-1305.
    169     *
    170     * @return the {@code originate} time. Never returns null.
    171     */
    172    @Override
    173    public TimeStamp getOriginateTimeStamp() {
    174        return getTimestamp(ORIGINATE_TIMESTAMP_INDEX);
    175    }
    176
    177    /**
    178     * Returns poll interval as defined in RFC-1305, which is an eight-bit signed integer indicating the maximum interval between successive messages, in
    179     * 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
    180     * NTP_MINPOLL to NTP_MAXPOLL inclusive.
    181     *
    182     * @return poll interval as defined in RFC-1305.
    183     */
    184    @Override
    185    public int getPoll() {
    186        return buf[POLL_INDEX];
    187    }
    188
    189    /**
    190     * 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.
    191     *
    192     * @return precision as defined in RFC-1305.
    193     */
    194    @Override
    195    public int getPrecision() {
    196        return buf[PRECISION_INDEX];
    197    }
    198
    199    /**
    200     * Returns {@code receive} timestamp as defined in RFC-1305.
    201     *
    202     * @return the {@code receive} time. Never returns null.
    203     */
    204    @Override
    205    public TimeStamp getReceiveTimeStamp() {
    206        return getTimestamp(RECEIVE_TIMESTAMP_INDEX);
    207    }
    208
    209    /**
    210     * Returns the reference id as defined in RFC-1305, which is a 32-bit integer whose value is dependent on several criteria.
    211     *
    212     * @return the reference id as defined in RFC-1305.
    213     */
    214    @Override
    215    public int getReferenceId() {
    216        return getInt(REFERENCE_ID_INDEX);
    217    }
    218
    219    /**
    220     * 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
    221     * empty string, clock type string, IP address, or a hex string.
    222     *
    223     * @return the reference id string.
    224     */
    225    @Override
    226    public String getReferenceIdString() {
    227        final int version = getVersion();
    228        final int stratum = getStratum();
    229        if (version == VERSION_3 || version == VERSION_4) {
    230            if (stratum == 0 || stratum == 1) {
    231                return idAsString(); // 4-character ASCII string (e.g. GPS, USNO)
    232            }
    233            // in NTPv4 servers this is latest transmit timestamp of ref source
    234            if (version == VERSION_4) {
    235                return idAsHex();
    236            }
    237        }
    238
    239        // Stratum 2 and higher this is a four-octet IPv4 address
    240        // of the primary reference host.
    241        if (stratum >= 2) {
    242            return idAsIPAddress();
    243        }
    244        return idAsHex();
    245    }
    246
    247    /**
    248     * Returns the reference time as defined in RFC-1305.
    249     *
    250     * @return the reference time as <code>TimeStamp</code> object. Never returns null.
    251     */
    252    @Override
    253    public TimeStamp getReferenceTimeStamp() {
    254        return getTimestamp(REFERENCE_TIMESTAMP_INDEX);
    255    }
    256
    257    /**
    258     * 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
    259     * negative values, depending on clock precision and skew.
    260     *
    261     * @return root delay as defined in RFC-1305.
    262     */
    263    @Override
    264    public int getRootDelay() {
    265        return getInt(ROOT_DELAY_INDEX);
    266    }
    267
    268    /**
    269     * 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
    270     * positive and negative values, depending on clock precision and skew.
    271     *
    272     * @return root delay in milliseconds
    273     */
    274    @Override
    275    public double getRootDelayInMillisDouble() {
    276        final double l = getRootDelay();
    277        return l / 65.536;
    278    }
    279
    280    /**
    281     * Returns root dispersion as defined in RFC-1305.
    282     *
    283     * @return root dispersion.
    284     */
    285    @Override
    286    public int getRootDispersion() {
    287        return getInt(ROOT_DISPERSION_INDEX);
    288    }
    289
    290    /**
    291     * Returns root dispersion (as defined in RFC-1305) in milliseconds.
    292     *
    293     * @return root dispersion in milliseconds
    294     */
    295    @Override
    296    public long getRootDispersionInMillis() {
    297        final long l = getRootDispersion();
    298        return l * 1000 / 65536L;
    299    }
    300
    301    /**
    302     * Returns root dispersion (as defined in RFC-1305) in milliseconds as double precision value.
    303     *
    304     * @return root dispersion in milliseconds
    305     */
    306    @Override
    307    public double getRootDispersionInMillisDouble() {
    308        final double l = getRootDispersion();
    309        return l / 65.536;
    310    }
    311
    312    /**
    313     * 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
    314     * ref clock, and all others a secondary reference (via NTP).
    315     *
    316     * @return Stratum level as defined in RFC-1305.
    317     */
    318    @Override
    319    public int getStratum() {
    320        return ui(buf[STRATUM_INDEX]);
    321    }
    322
    323    /**
    324     * Get NTP Timestamp at specified starting index.
    325     *
    326     * @param index index into data array
    327     * @return TimeStamp object for 64 bits starting at index
    328     */
    329    private TimeStamp getTimestamp(final int index) {
    330        return new TimeStamp(getLong(index));
    331    }
    332
    333    /**
    334     * Returns the {@code transmit} timestamp as defined in RFC-1305.
    335     *
    336     * @return the {@code transmit} timestamp as defined in RFC-1305. Never returns a null object.
    337     */
    338    @Override
    339    public TimeStamp getTransmitTimeStamp() {
    340        return getTimestamp(TRANSMIT_TIMESTAMP_INDEX);
    341    }
    342
    343    /**
    344     * Return type of time packet. The values (e.g. NTP, TIME, ICMP, ...) correspond to the protocol used to obtain the timing information.
    345     *
    346     * @return packet type string identifier which in this case is "NTP".
    347     */
    348    @Override
    349    public String getType() {
    350        return "NTP";
    351    }
    352
    353    /**
    354     * Returns NTP version number as defined in RFC-1305.
    355     *
    356     * @return NTP version number.
    357     */
    358    @Override
    359    public int getVersion() {
    360        return ui(buf[VERSION_INDEX]) >> VERSION_SHIFT & 0x7;
    361    }
    362
    363    /**
    364     * Computes a hash code for this object. The result is the exclusive OR of the values of this object stored as a byte array.
    365     *
    366     * @return a hash code value for this object.
    367     * @since 3.4
    368     */
    369    @Override
    370    public int hashCode() {
    371        return java.util.Arrays.hashCode(buf);
    372    }
    373
    374    private String idAsHex() {
    375        return Integer.toHexString(getReferenceId());
    376    }
    377
    378    /**
    379     * Returns Reference id as dotted IP address.
    380     *
    381     * @return refId as IP address string.
    382     */
    383    private String idAsIPAddress() {
    384        return ui(buf[REFERENCE_ID_INDEX]) + "." + ui(buf[REFERENCE_ID_INDEX + 1]) + "." + ui(buf[REFERENCE_ID_INDEX + 2]) + "."
    385                + ui(buf[REFERENCE_ID_INDEX + 3]);
    386    }
    387
    388    private String idAsString() {
    389        final StringBuilder id = new StringBuilder();
    390        for (int i = 0; i <= 3; i++) {
    391            final char c = (char) buf[REFERENCE_ID_INDEX + i];
    392            if (c == 0) { // 0-terminated string
    393                break;
    394            }
    395            id.append(c);
    396        }
    397        return id.toString();
    398    }
    399
    400    /**
    401     * Set the contents of this object from source datagram packet.
    402     *
    403     * @param srcDp source DatagramPacket to copy contents from, never null.
    404     * @throws IllegalArgumentException if srcDp is null or byte length is less than minimum length of 48 bytes
    405     */
    406    @Override
    407    public void setDatagramPacket(final DatagramPacket srcDp) {
    408        if (srcDp == null || srcDp.getLength() < buf.length) {
    409            throw new IllegalArgumentException();
    410        }
    411        final byte[] incomingBuf = srcDp.getData();
    412        int len = srcDp.getLength();
    413        if (len > buf.length) {
    414            len = buf.length;
    415        }
    416        System.arraycopy(incomingBuf, 0, buf, 0, len);
    417        final DatagramPacket dp = getDatagramPacket();
    418        dp.setAddress(srcDp.getAddress());
    419        final int port = srcDp.getPort();
    420        dp.setPort(port > 0 ? port : NTP_PORT);
    421        dp.setData(buf);
    422    }
    423
    424    /**
    425     * Set integer value at index position.
    426     *
    427     * @param idx   index position
    428     * @param value 32-bit int value
    429     */
    430    private void setInt(final int idx, int value) {
    431        for (int i = 3; i >= 0; i--) {
    432            buf[idx + i] = (byte) (value & 0xff);
    433            value >>>= 8; // shift right one-byte
    434        }
    435    }
    436
    437    /**
    438     * Set leap indicator as defined in RFC-1305.
    439     *
    440     * @param li leap indicator.
    441     */
    442    @Override
    443    public void setLeapIndicator(final int li) {
    444        buf[LI_INDEX] = (byte) (buf[LI_INDEX] & 0x3F | (li & 0x3) << LI_SHIFT);
    445    }
    446
    447    /**
    448     * Set mode as defined in RFC-1305.
    449     *
    450     * @param mode the mode to set
    451     */
    452    @Override
    453    public void setMode(final int mode) {
    454        buf[MODE_INDEX] = (byte) (buf[MODE_INDEX] & 0xF8 | mode & 0x7);
    455    }
    456
    457    /**
    458     * Set originate timestamp given NTP TimeStamp object. If <code>ts</code> is null then zero time is used.
    459     *
    460     * @param ts NTP timestamp
    461     */
    462    @Override
    463    public void setOriginateTimeStamp(final TimeStamp ts) {
    464        setTimestamp(ORIGINATE_TIMESTAMP_INDEX, ts);
    465    }
    466
    467    /**
    468     * Set poll interval as defined in RFC-1305.
    469     *
    470     * @param poll poll interval.
    471     */
    472    @Override
    473    public void setPoll(final int poll) {
    474        buf[POLL_INDEX] = (byte) (poll & 0xFF);
    475    }
    476
    477    /**
    478     * Set precision as defined in RFC-1305.
    479     *
    480     * @param precision the precision to set
    481     * @since 3.4
    482     */
    483    @Override
    484    public void setPrecision(final int precision) {
    485        buf[PRECISION_INDEX] = (byte) (precision & 0xFF);
    486    }
    487
    488    /**
    489     * Set receive timestamp given NTP TimeStamp object. If <code>ts</code> is null then zero time is used.
    490     *
    491     * @param ts timestamp
    492     */
    493    @Override
    494    public void setReceiveTimeStamp(final TimeStamp ts) {
    495        setTimestamp(RECEIVE_TIMESTAMP_INDEX, ts);
    496    }
    497
    498    /**
    499     * Set reference clock identifier field with 32-bit unsigned integer value. See RFC-1305 for description.
    500     *
    501     * @param refId reference clock identifier.
    502     */
    503    @Override
    504    public void setReferenceId(final int refId) {
    505        setInt(REFERENCE_ID_INDEX, refId);
    506    }
    507
    508    /**
    509     * Set Reference time with NTP timestamp. If <code>ts</code> is null then zero time is used.
    510     *
    511     * @param ts NTP timestamp
    512     */
    513    @Override
    514    public void setReferenceTime(final TimeStamp ts) {
    515        setTimestamp(REFERENCE_TIMESTAMP_INDEX, ts);
    516    }
    517
    518    /**
    519     * Set root delay as defined in RFC-1305.
    520     *
    521     * @param delay root delay
    522     * @since 3.4
    523     */
    524    @Override
    525    public void setRootDelay(final int delay) {
    526        setInt(ROOT_DELAY_INDEX, delay);
    527    }
    528
    529    /**
    530     * Set root dispersion as defined in RFC-1305.
    531     *
    532     * @param dispersion root dispersion
    533     * @since 3.4
    534     */
    535    @Override
    536    public void setRootDispersion(final int dispersion) {
    537        setInt(ROOT_DISPERSION_INDEX, dispersion);
    538    }
    539
    540    /**
    541     * Set stratum level as defined in RFC-1305.
    542     *
    543     * @param stratum stratum level.
    544     */
    545    @Override
    546    public void setStratum(final int stratum) {
    547        buf[STRATUM_INDEX] = (byte) (stratum & 0xFF);
    548    }
    549
    550    /**
    551     * Sets the NTP timestamp at the given array index.
    552     *
    553     * @param index index into the byte array.
    554     * @param t     TimeStamp.
    555     */
    556    private void setTimestamp(final int index, final TimeStamp t) {
    557        long ntpTime = t == null ? 0 : t.ntpValue();
    558        // copy 64-bits from Long value into 8 x 8-bit bytes of array
    559        // one byte at a time shifting 8-bits for each position.
    560        for (int i = 7; i >= 0; i--) {
    561            buf[index + i] = (byte) (ntpTime & 0xFF);
    562            ntpTime >>>= 8; // shift to next byte
    563        }
    564        // buf[index] |= 0x80; // only set if 1900 baseline....
    565    }
    566
    567    /**
    568     * Set transmit time with NTP timestamp. If <code>ts</code> is null then zero time is used.
    569     *
    570     * @param ts NTP timestamp
    571     */
    572    @Override
    573    public void setTransmitTime(final TimeStamp ts) {
    574        setTimestamp(TRANSMIT_TIMESTAMP_INDEX, ts);
    575    }
    576
    577    /**
    578     * Set NTP version as defined in RFC-1305.
    579     *
    580     * @param version NTP version.
    581     */
    582    @Override
    583    public void setVersion(final int version) {
    584        buf[VERSION_INDEX] = (byte) (buf[VERSION_INDEX] & 0xC7 | (version & 0x7) << VERSION_SHIFT);
    585    }
    586
    587    /**
    588     * Returns details of NTP packet as a string.
    589     *
    590     * @return details of NTP packet as a string.
    591     */
    592    @Override
    593    public String toString() {
    594        return "[" + "version:" + getVersion() + ", mode:" + getMode() + ", poll:" + getPoll() + ", precision:" + getPrecision() + ", delay:" + getRootDelay()
    595                + ", dispersion(ms):" + getRootDispersionInMillisDouble() + ", id:" + getReferenceIdString() + ", xmitTime:"
    596                + getTransmitTimeStamp().toDateString() + " ]";
    597    }
    598
    599}