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