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