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