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