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