blob: 23ba5c28bede0b5bd7dbe0a5d9f8da5167d0aec7 [file] [log] [blame]
package org.apache.commons.net.ntp;
/*
* 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
*
* http://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.
*/
import java.net.DatagramPacket;
/***
* Implementation of NtpV3Packet with methods converting Java objects to/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 */
private final byte[] buf = new byte[48];
private volatile DatagramPacket dp;
/** Creates a new instance of NtpV3Impl */
public NtpV3Impl()
{
}
/***
* Returns 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;
}
/***
* Return 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());
}
/***
* Set mode as defined in RFC-1305.
*
* @param mode the mode to set
*/
@Override
public void setMode(int mode)
{
buf[MODE_INDEX] = (byte) (buf[MODE_INDEX] & 0xF8 | mode & 0x7);
}
/***
* 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 condition (clock not synchronized)
*
* @return leap indicator as defined in RFC-1305.
*/
@Override
public int getLeapIndicator()
{
return (ui(buf[LI_INDEX]) >> LI_SHIFT) & 0x3;
}
/***
* Set leap indicator as defined in RFC-1305.
*
* @param li leap indicator.
*/
@Override
public void setLeapIndicator(int li)
{
buf[LI_INDEX] = (byte) (buf[LI_INDEX] & 0x3F | ((li & 0x3) << LI_SHIFT));
}
/***
* Returns 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];
}
/***
* Set poll interval as defined in RFC-1305.
*
* @param poll poll interval.
*/
@Override
public void setPoll(int poll)
{
buf[POLL_INDEX] = (byte) (poll & 0xFF);
}
/***
* 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.
*
* @return precision as defined in RFC-1305.
*/
@Override
public int getPrecision()
{
return buf[PRECISION_INDEX];
}
/***
* Set precision as defined in RFC-1305.
* @param precision the precision to set
* @since 3.4
*/
@Override
public void setPrecision(int precision)
{
buf[PRECISION_INDEX] = (byte) (precision & 0xFF);
}
/***
* Returns NTP version number as defined in RFC-1305.
*
* @return NTP version number.
*/
@Override
public int getVersion()
{
return (ui(buf[VERSION_INDEX]) >> VERSION_SHIFT) & 0x7;
}
/***
* Set NTP version as defined in RFC-1305.
*
* @param version NTP version.
*/
@Override
public void setVersion(int version)
{
buf[VERSION_INDEX] = (byte) (buf[VERSION_INDEX] & 0xC7 | ((version & 0x7) << VERSION_SHIFT));
}
/***
* 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 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]);
}
/***
* Set stratum level as defined in RFC-1305.
*
* @param stratum stratum level.
*/
@Override
public void setStratum(int stratum)
{
buf[STRATUM_INDEX] = (byte) (stratum & 0xFF);
}
/***
* 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
* 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);
}
/***
* Set root delay as defined in RFC-1305.
*
* @param delay root delay
* @since 3.4
*/
@Override
public void setRootDelay(int delay)
{
setInt(ROOT_DELAY_INDEX, delay);
}
/**
* 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 positive and negative values, depending
* on clock precision and skew.
*
* @return root delay in milliseconds
*/
@Override
public double getRootDelayInMillisDouble()
{
double l = getRootDelay();
return l / 65.536;
}
/***
* Returns root dispersion as defined in RFC-1305.
* @return root dispersion.
*/
@Override
public int getRootDispersion()
{
return getInt(ROOT_DISPERSION_INDEX);
}
/***
* Set root dispersion as defined in RFC-1305.
*
* @param dispersion root dispersion
* @since 3.4
*/
@Override
public void setRootDispersion(int dispersion)
{
setInt(ROOT_DISPERSION_INDEX, dispersion);
}
/***
* Returns root dispersion (as defined in RFC-1305) in milliseconds.
*
* @return root dispersion in milliseconds
*/
@Override
public long getRootDispersionInMillis()
{
long l = getRootDispersion();
return (l * 1000) / 65536L;
}
/***
* Returns root dispersion (as defined in RFC-1305) in milliseconds
* as double precision value.
*
* @return root dispersion in milliseconds
*/
@Override
public double getRootDispersionInMillisDouble()
{
double l = getRootDispersion();
return l / 65.536;
}
/***
* Set reference clock identifier field with 32-bit unsigned integer value.
* See RFC-1305 for description.
*
* @param refId reference clock identifier.
*/
@Override
public void setReferenceId(int refId)
{
setInt(REFERENCE_ID_INDEX, refId);
}
/***
* Returns 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);
}
/***
* 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 empty string, clock type string,
* IP address, or a hex string.
*
* @return the reference id string.
*/
@Override
public String getReferenceIdString()
{
int version = getVersion();
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();
}
/***
* 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()
{
StringBuilder id = new StringBuilder();
for (int i = 0; i <= 3; i++) {
char c = (char) buf[REFERENCE_ID_INDEX + i];
if (c == 0) { // 0-terminated string
break;
}
id.append(c);
}
return id.toString();
}
private String idAsHex()
{
return Integer.toHexString(getReferenceId());
}
/***
* Returns the transmit timestamp as defined in RFC-1305.
*
* @return the transmit timestamp as defined in RFC-1305.
* Never returns a null object.
*/
@Override
public TimeStamp getTransmitTimeStamp()
{
return getTimestamp(TRANSMIT_TIMESTAMP_INDEX);
}
/***
* Set transmit time with NTP timestamp.
* If <code>ts</code> is null then zero time is used.
*
* @param ts NTP timestamp
*/
@Override
public void setTransmitTime(TimeStamp ts)
{
setTimestamp(TRANSMIT_TIMESTAMP_INDEX, ts);
}
/***
* Set originate timestamp given NTP TimeStamp object.
* If <code>ts</code> is null then zero time is used.
*
* @param ts NTP timestamp
*/
@Override
public void setOriginateTimeStamp(TimeStamp ts)
{
setTimestamp(ORIGINATE_TIMESTAMP_INDEX, ts);
}
/***
* Returns the originate time as defined in RFC-1305.
*
* @return the originate time.
* Never returns null.
*/
@Override
public TimeStamp getOriginateTimeStamp()
{
return getTimestamp(ORIGINATE_TIMESTAMP_INDEX);
}
/***
* Returns the reference time as defined in RFC-1305.
*
* @return the reference time as <code>TimeStamp</code> object.
* Never returns null.
*/
@Override
public TimeStamp getReferenceTimeStamp()
{
return getTimestamp(REFERENCE_TIMESTAMP_INDEX);
}
/***
* Set Reference time with NTP timestamp. If <code>ts</code> is null
* then zero time is used.
*
* @param ts NTP timestamp
*/
@Override
public void setReferenceTime(TimeStamp ts)
{
setTimestamp(REFERENCE_TIMESTAMP_INDEX, ts);
}
/***
* Returns receive timestamp as defined in RFC-1305.
*
* @return the receive time.
* Never returns null.
*/
@Override
public TimeStamp getReceiveTimeStamp()
{
return getTimestamp(RECEIVE_TIMESTAMP_INDEX);
}
/***
* Set receive timestamp given NTP TimeStamp object.
* If <code>ts</code> is null then zero time is used.
*
* @param ts timestamp
*/
@Override
public void setReceiveTimeStamp(TimeStamp ts)
{
setTimestamp(RECEIVE_TIMESTAMP_INDEX, ts);
}
/***
* Return 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";
}
/***
* @return 4 bytes as 32-bit int
*/
private int getInt(int index)
{
int i = ui(buf[index]) << 24 |
ui(buf[index + 1]) << 16 |
ui(buf[index + 2]) << 8 |
ui(buf[index + 3]);
return i;
}
/***
* Set integer value at index position.
*
* @param idx index position
* @param value 32-bit int value
*/
private void setInt(int idx, int value)
{
for (int i=3; i >= 0; i--) {
buf[idx + i] = (byte) (value & 0xff);
value >>>= 8; // shift right one-byte
}
}
/**
* Get NTP Timestamp at specified starting index.
*
* @param index index into data array
* @return TimeStamp object for 64 bits starting at index
*/
private TimeStamp getTimestamp(int index)
{
return new TimeStamp(getLong(index));
}
/***
* Get Long value represented by bits starting at specified index.
*
* @return 8 bytes as 64-bit long
*/
private long getLong(int index)
{
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 |
ul(buf[index + 5]) << 16 |
ul(buf[index + 6]) << 8 |
ul(buf[index + 7]);
return i;
}
/***
* Sets the NTP timestamp at the given array index.
*
* @param index index into the byte array.
* @param t TimeStamp.
*/
private void setTimestamp(int index, 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....
}
/***
* Returns 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;
}
/***
* Set 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(DatagramPacket srcDp)
{
if (srcDp == null || srcDp.getLength() < buf.length) {
throw new IllegalArgumentException();
}
byte[] incomingBuf = srcDp.getData();
int len = srcDp.getLength();
if (len > buf.length) {
len = buf.length;
}
System.arraycopy(incomingBuf, 0, buf, 0, len);
DatagramPacket dp = getDatagramPacket();
dp.setAddress(srcDp.getAddress());
int port = srcDp.getPort();
dp.setPort(port > 0 ? port : NTP_PORT);
dp.setData(buf);
}
/***
* 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 <code>NtpV3Impl</code> object that
* contains the same values as this object.
*
* @param obj the object to compare with.
* @return <code>true</code> if the objects are the same;
* <code>false</code> otherwise.
* @since 3.4
*/
@Override
public boolean equals(Object obj)
{
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
NtpV3Impl other = (NtpV3Impl) obj;
return java.util.Arrays.equals(buf, other.buf);
}
/***
* Computes a hashcode 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 java.util.Arrays.hashCode(buf);
}
/***
* 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(byte b)
{
int i = b & 0xFF;
return i;
}
/***
* 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(byte b)
{
long i = b & 0xFF;
return i;
}
/***
* 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() +
" ]";
}
}