    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: 1230358 $ $Date: 2012-01-12 01:51:02 +0000 (Thu, 12 Jan 2012) $
    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
    312            // Stratum 2 and higher this is a four-octet IPv4 address
    313            // of the primary reference host.
    314            if (stratum >= 2) {
    315                return idAsIPAddress();
    316            }
    317            return idAsHex();
    318        }
    319
    320        /***
    321         * Returns Reference id as dotted IP address.
    322         * @return refId as IP address string.
    323         */
    324        private String idAsIPAddress()
    325        {
    326            return ui(buf[REFERENCE_ID_INDEX]) + "." +
    327                    ui(buf[REFERENCE_ID_INDEX + 1]) + "." +
    328                    ui(buf[REFERENCE_ID_INDEX + 2]) + "." +
    329                    ui(buf[REFERENCE_ID_INDEX + 3]);
    330        }
    331
    332        private String idAsString()
    333        {
    334            StringBuilder id = new StringBuilder();
    335            for (int i = 0; i <= 3; i++) {
    336                char c = (char) buf[REFERENCE_ID_INDEX + i];
    337                if (c == 0) {  // 0-terminated string
    338                    break;
    339                }
    340                id.append(c);
    341            }
    342            return id.toString();
    343        }
    344
    345        private String idAsHex()
    346        {
    347            return Integer.toHexString(getReferenceId());
    348        }
    349
    350        /***
    351         * Returns the transmit timestamp as defined in RFC-1305.
    352         *
    353         * @return the transmit timestamp as defined in RFC-1305.
    354         * Never returns a null object.
    355         */
    356        public TimeStamp getTransmitTimeStamp()
    357        {
    358            return getTimestamp(TRANSMIT_TIMESTAMP_INDEX);
    359        }
    360
    361        /***
    362         * Set transmit time with NTP timestamp.
    363         * If <code>ts</code> is null then zero time is used.
    364         *
    365         * @param ts NTP timestamp
    366         */
    367        public void setTransmitTime(TimeStamp ts)
    368        {
    369            setTimestamp(TRANSMIT_TIMESTAMP_INDEX, ts);
    370        }
    371
    372        /***
    373         * Set originate timestamp given NTP TimeStamp object.
    374         * If <code>ts</code> is null then zero time is used.
    375         *
    376         * @param ts NTP timestamp
    377         */
    378        public void setOriginateTimeStamp(TimeStamp ts)
    379        {
    380            setTimestamp(ORIGINATE_TIMESTAMP_INDEX, ts);
    381        }
    382
    383        /***
    384         * Returns the originate time as defined in RFC-1305.
    385         *
    386         * @return the originate time.
    387         * Never returns null.
    388         */
    389        public TimeStamp getOriginateTimeStamp()
    390        {
    391            return getTimestamp(ORIGINATE_TIMESTAMP_INDEX);
    392        }
    393
    394        /***
    395         * Returns the reference time as defined in RFC-1305.
    396         *
    397         * @return the reference time as <code>TimeStamp</code> object.
    398         * Never returns null.
    399         */
    400        public TimeStamp getReferenceTimeStamp()
    401        {
    402            return getTimestamp(REFERENCE_TIMESTAMP_INDEX);
    403        }
    404
    405        /***
    406         * Set Reference time with NTP timestamp. If <code>ts</code> is null
    407         * then zero time is used.
    408         *
    409         * @param ts NTP timestamp
    410         */
    411        public void setReferenceTime(TimeStamp ts)
    412        {
    413            setTimestamp(REFERENCE_TIMESTAMP_INDEX, ts);
    414        }
    415
    416        /***
    417         * Returns receive timestamp as defined in RFC-1305.
    418         *
    419         * @return the receive time.
    420         * Never returns null.
    421         */
    422        public TimeStamp getReceiveTimeStamp()
    423        {
    424            return getTimestamp(RECEIVE_TIMESTAMP_INDEX);
    425        }
    426
    427        /***
    428         * Set receive timestamp given NTP TimeStamp object.
    429         * If <code>ts</code> is null then zero time is used.
    430         *
    431         * @param ts timestamp
    432         */
    433        public void setReceiveTimeStamp(TimeStamp ts)
    434        {
    435            setTimestamp(RECEIVE_TIMESTAMP_INDEX, ts);
    436        }
    437
    438        /***
    439         * Return type of time packet. The values (e.g. NTP, TIME, ICMP, ...)
    440         * correspond to the protocol used to obtain the timing information.
    441         *
    442         * @return packet type string identifier which in this case is "NTP".
    443         */
    444        public String getType()
    445        {
    446            return "NTP";
    447        }
    448
    449        /***
    450         * @return 4 bytes as 32-bit int
    451         */
    452        private int getInt(int index)
    453        {
    454            int i = ui(buf[index]) << 24 |
    455                    ui(buf[index + 1]) << 16 |
    456                    ui(buf[index + 2]) << 8 |
    457                    ui(buf[index + 3]);
    458
    459            return i;
    460        }
    461
    462        /***
    463         * Get NTP Timestamp at specified starting index.
    464         *
    465         * @param index index into data array
    466         * @return TimeStamp object for 64 bits starting at index
    467         */
    468        private TimeStamp getTimestamp(int index)
    469        {
    470            return new TimeStamp(getLong(index));
    471        }
    472
    473        /***
    474         * Get Long value represented by bits starting at specified index.
    475         *
    476         * @return 8 bytes as 64-bit long
    477         */
    478        private long getLong(int index)
    479        {
    480            long i = ul(buf[index]) << 56 |
    481                    ul(buf[index + 1]) << 48 |
    482                    ul(buf[index + 2]) << 40 |
    483                    ul(buf[index + 3]) << 32 |
    484                    ul(buf[index + 4]) << 24 |
    485                    ul(buf[index + 5]) << 16 |
    486                    ul(buf[index + 6]) << 8 |
    487                    ul(buf[index + 7]);
    488            return i;
    489        }
    490
    491        /***
    492         * Sets the NTP timestamp at the given array index.
    493         *
    494         * @param index index into the byte array.
    495         * @param t TimeStamp.
    496         */
    497        private void setTimestamp(int index, TimeStamp t)
    498        {
    499            long ntpTime = (t == null) ? 0 : t.ntpValue();
    500            // copy 64-bits from Long value into 8 x 8-bit bytes of array
    501            // one byte at a time shifting 8-bits for each position.
    502            for (int i = 7; i >= 0; i--) {
    503                buf[index + i] = (byte) (ntpTime & 0xFF);
    504                ntpTime >>>= 8; // shift to next byte
    505            }
    506            // buf[index] |= 0x80;  // only set if 1900 baseline....
    507        }
    508
    509        /***
    510         * Returns the datagram packet with the NTP details already filled in.
    511         *
    512         * @return a datagram packet.
    513         */
    514        public synchronized DatagramPacket getDatagramPacket()
    515        {
    516            if (dp == null) {
    517                dp = new DatagramPacket(buf, buf.length);
    518                dp.setPort(NTP_PORT);
    519            }
    520            return dp;
    521        }
    522
    523        /***
    524         * Set the contents of this object from source datagram packet.
    525         *
    526         * @param srcDp source DatagramPacket to copy contents from.
    527         */
    528        public void setDatagramPacket(DatagramPacket srcDp)
    529        {
    530            byte[] incomingBuf = srcDp.getData();
    531            int len = srcDp.getLength();
    532            if (len > buf.length) {
    533                len = buf.length;
    534            }
    535
    536            System.arraycopy(incomingBuf, 0, buf, 0, len);
    537        }
    538
    539        /***
    540         * Convert byte to unsigned integer.
    541         * Java only has signed types so we have to do
    542         * more work to get unsigned ops.
    543         *
    544         * @param b
    545         * @return unsigned int value of byte
    546         */
    547        protected final static int ui(byte b)
    548        {
    549            int i = b & 0xFF;
    550            return i;
    551        }
    552
    553        /***
    554         * Convert byte to unsigned long.
    555         * Java only has signed types so we have to do
    556         * more work to get unsigned ops
    557         *
    558         * @param b
    559         * @return unsigned long value of byte
    560         */
    561        protected final static long ul(byte b)
    562        {
    563            long i = b & 0xFF;
    564            return i;
    565        }
    566
    567        /***
    568         * Returns details of NTP packet as a string.
    569         *
    570         * @return details of NTP packet as a string.
    571         */
    572        @Override
    573        public String toString()
    574        {
    575            return "[" +
    576                    "version:" + getVersion() +
    577                    ", mode:" + getMode() +
    578                    ", poll:" + getPoll() +
    579                    ", precision:" + getPrecision() +
    580                    ", delay:" + getRootDelay() +
    581                    ", dispersion(ms):" + getRootDispersionInMillisDouble() +
    582                    ", id:" + getReferenceIdString() +
    583                    ", xmitTime:" + getTransmitTimeStamp().toDateString() +
    584                    " ]";
    585        }
    586
    587    }