/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004, 2005, 2006], Hyperic, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. This program is distributed * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.util.ntp; /** * Java representation of an NTP response as described in RFC 2030 * * http://www.faqs.org/rfcs/rfc2030.html * * Packet format: * 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |LI | VN |Mode | Stratum | Poll | Precision | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Root Delay | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Root Dispersion | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Reference Identifier | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * | Reference Timestamp (64) | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * | Originate Timestamp (64) | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * | Receive Timestamp (64) | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * | Transmit Timestamp (64) | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Key Identifier (optional) (32) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * | | * | Message Digest (optional) (128) | * | | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ public class NtpResponse { protected byte leapIndicator; protected byte version; protected byte mode; protected short stratum; protected byte interval; protected byte precision; protected double rootDelay; protected double rootDispersion; protected byte[] referenceIdentifier = new byte[4]; protected double referenceTimestamp; protected double originateTimestamp; protected double receiveTimestamp; protected double transmitTimestamp; // These are not actually in the NTP response, but calculated // when the repsonse is returned protected double roundTripDelay; protected double localClockOffset; // Private constructor for decodeResponse private NtpResponse() {} /** * Generate the NTP request */ public static byte[] getRequestBytes() { // Initialize packet with all fields set to 0 byte[] packet = new byte[48]; packet[0] = 27; // version = 3, mode = 3 // Set transmit time encode(packet, 40, NtpClient.now()); return packet; } /** * Decode the NTP server response */ public static NtpResponse decodeResponse(double ts, byte[] data) { NtpResponse response = new NtpResponse(); response.leapIndicator = (byte)((data[0] >> 6) & 0x3); response.version = (byte)((data[0] >> 3) & 0x7); response.mode = (byte)(data[0] & 0x7); response.stratum = unsignedByteToShort(data[1]); response.interval = data[2]; response.precision = data[3]; response.rootDelay = (data[4] * 256.0) + unsignedByteToShort(data[5]) + (unsignedByteToShort(data[6]) / 256.0) + (unsignedByteToShort(data[7]) / 65536.0); response.rootDispersion = (unsignedByteToShort(data[8]) * 256.0) + unsignedByteToShort(data[9]) + (unsignedByteToShort(data[10]) / 256.0) + (unsignedByteToShort(data[11]) / 65536.0); response.referenceIdentifier[0] = data[12]; response.referenceIdentifier[1] = data[13]; response.referenceIdentifier[2] = data[14]; response.referenceIdentifier[3] = data[15]; response.referenceTimestamp = decode(data, 16); response.originateTimestamp = decode(data, 24); response.receiveTimestamp = decode(data, 32); response.transmitTimestamp = decode(data, 40); response.roundTripDelay = (ts - response.originateTimestamp) - (response.transmitTimestamp - response.receiveTimestamp); response.localClockOffset = ((response.receiveTimestamp - response.originateTimestamp) + (response.transmitTimestamp - ts)) / 2; return response; } // Java's byte type is signed, while NTP uses unsigned private static short unsignedByteToShort(byte b) { if ((b & 0x80) == 0x80) return (short) (128 + (b & 0x7f)); else return (short) b; } // Decode byte[8] -> double private static double decode(byte[] array, int start) { double ts = 0; for (int i=0; i<8; i++) { ts += unsignedByteToShort(array[start+i]) * Math.pow(2, (3-i)*8); } return ts; } // Encode double -> byte[8] private static void encode(byte[] array, int start, double timestamp) { for (int i=0; i<8; i++) { double base = Math.pow(2, (3-i)*8); array[start+i] = (byte)(timestamp/base); timestamp = timestamp - (double) (unsignedByteToShort(array[start+i]) * base); } } public double getLocalClockOffset() { return Math.abs(this.localClockOffset); } public double getRoundTripDelay() { return this.roundTripDelay; } public double getRootDelay() { return this.rootDelay; } public double getRootDispersion() { return this.rootDispersion; } }