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