    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}