| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.net.ntp; |
| |
| import java.net.DatagramPacket; |
| import java.util.Arrays; |
| |
| /** |
| * Implements {@link NtpV3Packet} to convert Java objects to and from the Network Time Protocol (NTP) data message header format described in RFC-1305. |
| */ |
| public class NtpV3Impl implements NtpV3Packet { |
| |
| private static final int MODE_INDEX = 0; |
| private static final int MODE_SHIFT = 0; |
| |
| private static final int VERSION_INDEX = 0; |
| private static final int VERSION_SHIFT = 3; |
| |
| private static final int LI_INDEX = 0; |
| private static final int LI_SHIFT = 6; |
| |
| private static final int STRATUM_INDEX = 1; |
| private static final int POLL_INDEX = 2; |
| private static final int PRECISION_INDEX = 3; |
| |
| private static final int ROOT_DELAY_INDEX = 4; |
| private static final int ROOT_DISPERSION_INDEX = 8; |
| private static final int REFERENCE_ID_INDEX = 12; |
| |
| private static final int REFERENCE_TIMESTAMP_INDEX = 16; |
| private static final int ORIGINATE_TIMESTAMP_INDEX = 24; |
| private static final int RECEIVE_TIMESTAMP_INDEX = 32; |
| private static final int TRANSMIT_TIMESTAMP_INDEX = 40; |
| |
| // private static final int KEY_IDENTIFIER_INDEX = 48; |
| // private static final int MESSAGE_DIGEST = 54; /* len 16 bytes */ |
| |
| /** |
| * Convert byte to unsigned integer. Java only has signed types, so we have to do more work to get unsigned ops. |
| * |
| * @param b input byte |
| * @return unsigned int value of byte |
| */ |
| protected static final int ui(final byte b) { |
| return b & 0xFF; |
| } |
| |
| /** |
| * Convert byte to unsigned long. Java only has signed types, so we have to do more work to get unsigned ops |
| * |
| * @param b input byte |
| * @return unsigned long value of byte |
| */ |
| protected static final long ul(final byte b) { |
| return b & 0xFF; |
| } |
| |
| private final byte[] buf = new byte[48]; |
| |
| private volatile DatagramPacket dp; |
| |
| /** Creates a new instance of NtpV3Impl */ |
| public NtpV3Impl() { |
| } |
| |
| /** |
| * 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 |
| * {@code NtpV3Impl} object that contains the same values as this object. |
| * |
| * @param obj the object to compare with. |
| * @return {@code true} if the objects are the same; {@code false} otherwise. |
| * @since 3.4 |
| */ |
| @Override |
| public boolean equals(final Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null || getClass() != obj.getClass()) { |
| return false; |
| } |
| final NtpV3Impl other = (NtpV3Impl) obj; |
| return Arrays.equals(buf, other.buf); |
| } |
| |
| /** |
| * Gets the datagram packet with the NTP details already filled in. |
| * |
| * @return a datagram packet. |
| */ |
| @Override |
| public synchronized DatagramPacket getDatagramPacket() { |
| if (dp == null) { |
| dp = new DatagramPacket(buf, buf.length); |
| dp.setPort(NTP_PORT); |
| } |
| return dp; |
| } |
| |
| /** |
| * Gets 4 bytes as 32-bit int. |
| * |
| * @return 4 bytes as 32-bit int |
| */ |
| private int getInt(final int index) { |
| return ui(buf[index]) << 24 | ui(buf[index + 1]) << 16 | ui(buf[index + 2]) << 8 | ui(buf[index + 3]); |
| } |
| |
| /** |
| * 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 |
| * condition (clock not synchronized) |
| * |
| * @return leap indicator as defined in RFC-1305. |
| */ |
| @Override |
| public int getLeapIndicator() { |
| return ui(buf[LI_INDEX]) >> LI_SHIFT & 0x3; |
| } |
| |
| /** |
| * Gets Long value represented by bits starting at specified index. |
| * |
| * @return 8 bytes as 64-bit long |
| */ |
| private long getLong(final int index) { |
| 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 |
| | ul(buf[index + 5]) << 16 | ul(buf[index + 6]) << 8 | ul(buf[index + 7]); |
| } |
| |
| /** |
| * Gets the mode as defined in RFC-1305 which is a 3-bit integer whose value is indicated by the MODE_xxx parameters. |
| * |
| * @return mode as defined in RFC-1305. |
| */ |
| @Override |
| public int getMode() { |
| return ui(buf[MODE_INDEX]) >> MODE_SHIFT & 0x7; |
| } |
| |
| /** |
| * Gets the human-readable name of message mode type as described in RFC 1305. |
| * |
| * @return mode name as string. |
| */ |
| @Override |
| public String getModeName() { |
| return NtpUtils.getModeName(getMode()); |
| } |
| |
| /** |
| * Gets the {@code originate} time as defined in RFC-1305. |
| * |
| * @return the {@code originate} time. Never returns null. |
| */ |
| @Override |
| public TimeStamp getOriginateTimeStamp() { |
| return getTimestamp(ORIGINATE_TIMESTAMP_INDEX); |
| } |
| |
| /** |
| * Gets the poll interval as defined in RFC-1305, which is an eight-bit signed integer indicating the maximum interval between successive messages, in |
| * 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 |
| * NTP_MINPOLL to NTP_MAXPOLL inclusive. |
| * |
| * @return poll interval as defined in RFC-1305. |
| */ |
| @Override |
| public int getPoll() { |
| return buf[POLL_INDEX]; |
| } |
| |
| /** |
| * 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. |
| * |
| * @return precision as defined in RFC-1305. |
| */ |
| @Override |
| public int getPrecision() { |
| return buf[PRECISION_INDEX]; |
| } |
| |
| /** |
| * Gets the {@code receive} timestamp as defined in RFC-1305. |
| * |
| * @return the {@code receive} time. Never returns null. |
| */ |
| @Override |
| public TimeStamp getReceiveTimeStamp() { |
| return getTimestamp(RECEIVE_TIMESTAMP_INDEX); |
| } |
| |
| /** |
| * Gets the reference id as defined in RFC-1305, which is a 32-bit integer whose value is dependent on several criteria. |
| * |
| * @return the reference id as defined in RFC-1305. |
| */ |
| @Override |
| public int getReferenceId() { |
| return getInt(REFERENCE_ID_INDEX); |
| } |
| |
| /** |
| * 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 |
| * empty string, clock type string, IP address, or a hexadecimal string. |
| * |
| * @return the reference id string. |
| */ |
| @Override |
| public String getReferenceIdString() { |
| final int version = getVersion(); |
| final int stratum = getStratum(); |
| if (version == VERSION_3 || version == VERSION_4) { |
| if (stratum == 0 || stratum == 1) { |
| return idAsString(); // 4-character ASCII string (e.g. GPS, USNO) |
| } |
| // in NTPv4 servers this is latest transmit timestamp of ref source |
| if (version == VERSION_4) { |
| return idAsHex(); |
| } |
| } |
| |
| // Stratum 2 and higher this is a four-octet IPv4 address |
| // of the primary reference host. |
| if (stratum >= 2) { |
| return idAsIPAddress(); |
| } |
| return idAsHex(); |
| } |
| |
| /** |
| * Gets the reference time as defined in RFC-1305. |
| * |
| * @return the reference time as {@code TimeStamp} object. Never returns null. |
| */ |
| @Override |
| public TimeStamp getReferenceTimeStamp() { |
| return getTimestamp(REFERENCE_TIMESTAMP_INDEX); |
| } |
| |
| /** |
| * 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 |
| * negative values, depending on clock precision and skew. |
| * |
| * @return root delay as defined in RFC-1305. |
| */ |
| @Override |
| public int getRootDelay() { |
| return getInt(ROOT_DELAY_INDEX); |
| } |
| |
| /** |
| * 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 |
| * take positive and negative values, depending on clock precision and skew. |
| * |
| * @return root delay in milliseconds |
| */ |
| @Override |
| public double getRootDelayInMillisDouble() { |
| final double l = getRootDelay(); |
| return l / 65.536; |
| } |
| |
| /** |
| * Gets the root dispersion as defined in RFC-1305. |
| * |
| * @return root dispersion. |
| */ |
| @Override |
| public int getRootDispersion() { |
| return getInt(ROOT_DISPERSION_INDEX); |
| } |
| |
| /** |
| * Gets the root dispersion (as defined in RFC-1305) in milliseconds. |
| * |
| * @return root dispersion in milliseconds |
| */ |
| @Override |
| public long getRootDispersionInMillis() { |
| final long l = getRootDispersion(); |
| return l * 1000 / 65536L; |
| } |
| |
| /** |
| * Gets the root dispersion (as defined in RFC-1305) in milliseconds as double precision value. |
| * |
| * @return root dispersion in milliseconds |
| */ |
| @Override |
| public double getRootDispersionInMillisDouble() { |
| final double l = getRootDispersion(); |
| return l / 65.536; |
| } |
| |
| /** |
| * 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 |
| * ref clock, and all others a secondary reference (via NTP). |
| * |
| * @return Stratum level as defined in RFC-1305. |
| */ |
| @Override |
| public int getStratum() { |
| return ui(buf[STRATUM_INDEX]); |
| } |
| |
| /** |
| * Gets the NTP Timestamp at specified starting index. |
| * |
| * @param index index into data array |
| * @return TimeStamp object for 64 bits starting at index |
| */ |
| private TimeStamp getTimestamp(final int index) { |
| return new TimeStamp(getLong(index)); |
| } |
| |
| /** |
| * Gets the {@code transmit} timestamp as defined in RFC-1305. |
| * |
| * @return the {@code transmit} timestamp as defined in RFC-1305. Never returns a null object. |
| */ |
| @Override |
| public TimeStamp getTransmitTimeStamp() { |
| return getTimestamp(TRANSMIT_TIMESTAMP_INDEX); |
| } |
| |
| /** |
| * Gets the type of time packet. The values (e.g. NTP, TIME, ICMP, ...) correspond to the protocol used to obtain the timing information. |
| * |
| * @return packet type string identifier which in this case is "NTP". |
| */ |
| @Override |
| public String getType() { |
| return "NTP"; |
| } |
| |
| /** |
| * Gets the NTP version number as defined in RFC-1305. |
| * |
| * @return NTP version number. |
| */ |
| @Override |
| public int getVersion() { |
| return ui(buf[VERSION_INDEX]) >> VERSION_SHIFT & 0x7; |
| } |
| |
| /** |
| * Computes a hash code for this object. The result is the exclusive OR of the values of this object stored as a byte array. |
| * |
| * @return a hash code value for this object. |
| * @since 3.4 |
| */ |
| @Override |
| public int hashCode() { |
| return Arrays.hashCode(buf); |
| } |
| |
| private String idAsHex() { |
| return Integer.toHexString(getReferenceId()); |
| } |
| |
| /** |
| * Returns Reference id as dotted IP address. |
| * |
| * @return refId as IP address string. |
| */ |
| private String idAsIPAddress() { |
| return ui(buf[REFERENCE_ID_INDEX]) + "." + ui(buf[REFERENCE_ID_INDEX + 1]) + "." + ui(buf[REFERENCE_ID_INDEX + 2]) + "." |
| + ui(buf[REFERENCE_ID_INDEX + 3]); |
| } |
| |
| private String idAsString() { |
| final StringBuilder id = new StringBuilder(); |
| for (int i = 0; i <= 3; i++) { |
| final char c = (char) buf[REFERENCE_ID_INDEX + i]; |
| if (c == 0) { // 0-terminated string |
| break; |
| } |
| id.append(c); |
| } |
| return id.toString(); |
| } |
| |
| /** |
| * Sets the contents of this object from source datagram packet. |
| * |
| * @param srcDp source DatagramPacket to copy contents from, never null. |
| * @throws IllegalArgumentException if srcDp is null or byte length is less than minimum length of 48 bytes |
| */ |
| @Override |
| public void setDatagramPacket(final DatagramPacket srcDp) { |
| if (srcDp == null || srcDp.getLength() < buf.length) { |
| throw new IllegalArgumentException(); |
| } |
| final byte[] incomingBuf = srcDp.getData(); |
| int len = srcDp.getLength(); |
| if (len > buf.length) { |
| len = buf.length; |
| } |
| System.arraycopy(incomingBuf, 0, buf, 0, len); |
| final DatagramPacket dp = getDatagramPacket(); |
| dp.setAddress(srcDp.getAddress()); |
| final int port = srcDp.getPort(); |
| dp.setPort(port > 0 ? port : NTP_PORT); |
| dp.setData(buf); |
| } |
| |
| /** |
| * Sets integer value at index position. |
| * |
| * @param idx index position |
| * @param value 32-bit int value |
| */ |
| private void setInt(final int idx, int value) { |
| for (int i = 3; i >= 0; i--) { |
| buf[idx + i] = (byte) (value & 0xff); |
| value >>>= 8; // shift right one-byte |
| } |
| } |
| |
| /** |
| * Sets leap indicator as defined in RFC-1305. |
| * |
| * @param li leap indicator. |
| */ |
| @Override |
| public void setLeapIndicator(final int li) { |
| buf[LI_INDEX] = (byte) (buf[LI_INDEX] & 0x3F | (li & 0x3) << LI_SHIFT); |
| } |
| |
| /** |
| * Sets mode as defined in RFC-1305. |
| * |
| * @param mode the mode to set |
| */ |
| @Override |
| public void setMode(final int mode) { |
| buf[MODE_INDEX] = (byte) (buf[MODE_INDEX] & 0xF8 | mode & 0x7); |
| } |
| |
| /** |
| * Sets originate timestamp given NTP TimeStamp object. If {@code ts} is null then zero time is used. |
| * |
| * @param ts NTP timestamp |
| */ |
| @Override |
| public void setOriginateTimeStamp(final TimeStamp ts) { |
| setTimestamp(ORIGINATE_TIMESTAMP_INDEX, ts); |
| } |
| |
| /** |
| * Sets poll interval as defined in RFC-1305. |
| * |
| * @param poll poll interval. |
| */ |
| @Override |
| public void setPoll(final int poll) { |
| buf[POLL_INDEX] = (byte) (poll & 0xFF); |
| } |
| |
| /** |
| * Sets precision as defined in RFC-1305. |
| * |
| * @param precision the precision to set |
| * @since 3.4 |
| */ |
| @Override |
| public void setPrecision(final int precision) { |
| buf[PRECISION_INDEX] = (byte) (precision & 0xFF); |
| } |
| |
| /** |
| * Sets receive timestamp given NTP TimeStamp object. If {@code ts} is null then zero time is used. |
| * |
| * @param ts timestamp |
| */ |
| @Override |
| public void setReceiveTimeStamp(final TimeStamp ts) { |
| setTimestamp(RECEIVE_TIMESTAMP_INDEX, ts); |
| } |
| |
| /** |
| * Sets reference clock identifier field with 32-bit unsigned integer value. See RFC-1305 for description. |
| * |
| * @param refId reference clock identifier. |
| */ |
| @Override |
| public void setReferenceId(final int refId) { |
| setInt(REFERENCE_ID_INDEX, refId); |
| } |
| |
| /** |
| * Sets Reference time with NTP timestamp. If {@code ts} is null then zero time is used. |
| * |
| * @param ts NTP timestamp |
| */ |
| @Override |
| public void setReferenceTime(final TimeStamp ts) { |
| setTimestamp(REFERENCE_TIMESTAMP_INDEX, ts); |
| } |
| |
| /** |
| * Sets root delay as defined in RFC-1305. |
| * |
| * @param delay root delay |
| * @since 3.4 |
| */ |
| @Override |
| public void setRootDelay(final int delay) { |
| setInt(ROOT_DELAY_INDEX, delay); |
| } |
| |
| /** |
| * Sets root dispersion as defined in RFC-1305. |
| * |
| * @param dispersion root dispersion |
| * @since 3.4 |
| */ |
| @Override |
| public void setRootDispersion(final int dispersion) { |
| setInt(ROOT_DISPERSION_INDEX, dispersion); |
| } |
| |
| /** |
| * Sets stratum level as defined in RFC-1305. |
| * |
| * @param stratum stratum level. |
| */ |
| @Override |
| public void setStratum(final int stratum) { |
| buf[STRATUM_INDEX] = (byte) (stratum & 0xFF); |
| } |
| |
| /** |
| * Sets the NTP timestamp at the given array index. |
| * |
| * @param index index into the byte array. |
| * @param t TimeStamp. |
| */ |
| private void setTimestamp(final int index, final TimeStamp t) { |
| long ntpTime = t == null ? 0 : t.ntpValue(); |
| // copy 64-bits from Long value into 8 x 8-bit bytes of array |
| // one byte at a time shifting 8-bits for each position. |
| for (int i = 7; i >= 0; i--) { |
| buf[index + i] = (byte) (ntpTime & 0xFF); |
| ntpTime >>>= 8; // shift to next byte |
| } |
| // buf[index] |= 0x80; // only set if 1900 baseline.... |
| } |
| |
| /** |
| * Sets transmit time with NTP timestamp. If {@code ts} is null then zero time is used. |
| * |
| * @param ts NTP timestamp |
| */ |
| @Override |
| public void setTransmitTime(final TimeStamp ts) { |
| setTimestamp(TRANSMIT_TIMESTAMP_INDEX, ts); |
| } |
| |
| /** |
| * Sets NTP version as defined in RFC-1305. |
| * |
| * @param version NTP version. |
| */ |
| @Override |
| public void setVersion(final int version) { |
| buf[VERSION_INDEX] = (byte) (buf[VERSION_INDEX] & 0xC7 | (version & 0x7) << VERSION_SHIFT); |
| } |
| |
| /** |
| * Returns details of NTP packet as a string. |
| * |
| * @return details of NTP packet as a string. |
| */ |
| @Override |
| public String toString() { |
| return "[version:" + getVersion() + ", mode:" + getMode() + ", poll:" + getPoll() + ", precision:" + getPrecision() + ", delay:" + getRootDelay() |
| + ", dispersion(ms):" + getRootDispersionInMillisDouble() + ", id:" + getReferenceIdString() + ", xmitTime:" |
| + getTransmitTimeStamp().toDateString() + " ]"; |
| } |
| |
| } |