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