001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.net.ntp; 020 021import java.text.DecimalFormat; 022import java.util.Date; 023 024import lucee.commons.i18n.DateFormatPool; 025 026public final class NtpMessage 027{ 028 /** 029 * This is a two-bit code warning of an impending leap second to be 030 * inserted/deleted in the last minute of the current day. It's values 031 * may be as follows: 032 * 033 * Value Meaning 034 * ----- ------- 035 * 0 no warning 036 * 1 last minute has 61 seconds 037 * 2 last minute has 59 seconds) 038 * 3 alarm condition (clock not synchronized) 039 */ 040 public byte leapIndicator = 0; 041 042 043 /** 044 * This value indicates the NTP/SNTP version number. The version number 045 * is 3 for Version 3 (IPv4 only) and 4 for Version 4 (IPv4, IPv6 and OSI). 046 * If necessary to distinguish between IPv4, IPv6 and OSI, the 047 * encapsulating context must be inspected. 048 */ 049 public byte version = 3; 050 051 052 /** 053 * This value indicates the mode, with values defined as follows: 054 * 055 * Mode Meaning 056 * ---- ------- 057 * 0 reserved 058 * 1 symmetric active 059 * 2 symmetric passive 060 * 3 client 061 * 4 server 062 * 5 broadcast 063 * 6 reserved for NTP control message 064 * 7 reserved for private use 065 * 066 * In unicast and anycast modes, the client sets this field to 3 (client) 067 * in the request and the server sets it to 4 (server) in the reply. In 068 * multicast mode, the server sets this field to 5 (broadcast). 069 */ 070 public byte mode = 0; 071 072 073 /** 074 * This value indicates the stratum level of the local clock, with values 075 * defined as follows: 076 * 077 * Stratum Meaning 078 * ---------------------------------------------- 079 * 0 unspecified or unavailable 080 * 1 primary reference (e.g., radio clock) 081 * 2-15 secondary reference (via NTP or SNTP) 082 * 16-255 reserved 083 */ 084 public short stratum = 0; 085 086 087 /** 088 * This value indicates the maximum interval between successive messages, 089 * in seconds to the nearest power of two. The values that can appear in 090 * this field presently range from 4 (16 s) to 14 (16284 s); however, most 091 * applications use only the sub-range 6 (64 s) to 10 (1024 s). 092 */ 093 public byte pollInterval = 0; 094 095 096 /** 097 * This value indicates the precision of the local clock, in seconds to 098 * the nearest power of two. The values that normally appear in this field\ 099 * range from -6 for mains-frequency clocks to -20 for microsecond clocks 100 * found in some workstations. 101 */ 102 public byte precision = 0; 103 104 105 /** 106 * This value indicates the total roundtrip delay to the primary reference 107 * source, in seconds. Note that this variable can take on both positive 108 * and negative values, depending on the relative time and frequency 109 * offsets. The values that normally appear in this field range from 110 * negative values of a few milliseconds to positive values of several 111 * hundred milliseconds. 112 */ 113 public double rootDelay = 0; 114 115 116 /** 117 * This value indicates the nominal error relative to the primary reference 118 * source, in seconds. The values that normally appear in this field 119 * range from 0 to several hundred milliseconds. 120 */ 121 public double rootDispersion = 0; 122 123 124 /** 125 * This is a 4-byte array identifying the particular reference source. 126 * In the case of NTP Version 3 or Version 4 stratum-0 (unspecified) or 127 * stratum-1 (primary) servers, this is a four-character ASCII string, left 128 * justified and zero padded to 32 bits. In NTP Version 3 secondary 129 * servers, this is the 32-bit IPv4 address of the reference source. In NTP 130 * Version 4 secondary servers, this is the low order 32 bits of the latest 131 * transmit timestamp of the reference source. NTP primary (stratum 1) 132 * servers should set this field to a code identifying the external 133 * reference source according to the following list. If the external 134 * reference is one of those listed, the associated code should be used. 135 * Codes for sources not listed can be contrived as appropriate. 136 * 137 * Code External Reference Source 138 * ---- ------------------------- 139 * LOCL uncalibrated local clock used as a primary reference for 140 * a subnet without external means of synchronization 141 * PPS atomic clock or other pulse-per-second source 142 * individually calibrated to national standards 143 * ACTS NIST dialup modem service 144 * USNO USNO modem service 145 * PTB PTB (Germany) modem service 146 * TDF Allouis (France) Radio 164 kHz 147 * DCF Mainflingen (Germany) Radio 77.5 kHz 148 * MSF Rugby (UK) Radio 60 kHz 149 * WWV Ft. Collins (US) Radio 2.5, 5, 10, 15, 20 MHz 150 * WWVB Boulder (US) Radio 60 kHz 151 * WWVH Kaui Hawaii (US) Radio 2.5, 5, 10, 15 MHz 152 * CHU Ottawa (Canada) Radio 3330, 7335, 14670 kHz 153 * LORC LORAN-C radionavigation system 154 * OMEG OMEGA radionavigation system 155 * GPS Global Positioning Service 156 * GOES Geostationary Orbit Environment Satellite 157 */ 158 public byte[] referenceIdentifier = {0, 0, 0, 0}; 159 160 161 /** 162 * This is the time at which the local clock was last set or corrected, in 163 * seconds since 00:00 1-Jan-1900. 164 */ 165 public double referenceTimestamp = 0; 166 167 168 /** 169 * This is the time at which the request departed the client for the 170 * server, in seconds since 00:00 1-Jan-1900. 171 */ 172 public double originateTimestamp = 0; 173 174 175 /** 176 * This is the time at which the request arrived at the server, in seconds 177 * since 00:00 1-Jan-1900. 178 */ 179 public double receiveTimestamp = 0; 180 181 182 /** 183 * This is the time at which the reply departed the server for the client, 184 * in seconds since 00:00 1-Jan-1900. 185 */ 186 public double transmitTimestamp = 0; 187 188 189 190 /** 191 * Constructs a new NtpMessage from an array of bytes. 192 * @param array 193 */ 194 public NtpMessage(byte[] array) 195 { 196 // See the packet format diagram in RFC 2030 for details 197 leapIndicator = (byte) ((array[0] >> 6) & 0x3); 198 version = (byte) ((array[0] >> 3) & 0x7); 199 mode = (byte) (array[0] & 0x7); 200 stratum = unsignedByteToShort(array[1]); 201 pollInterval = array[2]; 202 precision = array[3]; 203 204 rootDelay = (array[4] * 256.0) + 205 unsignedByteToShort(array[5]) + 206 (unsignedByteToShort(array[6]) / 256.0) + 207 (unsignedByteToShort(array[7]) / 65536.0); 208 209 rootDispersion = (unsignedByteToShort(array[8]) * 256.0) + 210 unsignedByteToShort(array[9]) + 211 (unsignedByteToShort(array[10]) / 256.0) + 212 (unsignedByteToShort(array[11]) / 65536.0); 213 214 referenceIdentifier[0] = array[12]; 215 referenceIdentifier[1] = array[13]; 216 referenceIdentifier[2] = array[14]; 217 referenceIdentifier[3] = array[15]; 218 219 referenceTimestamp = decodeTimestamp(array, 16); 220 originateTimestamp = decodeTimestamp(array, 24); 221 receiveTimestamp = decodeTimestamp(array, 32); 222 transmitTimestamp = decodeTimestamp(array, 40); 223 } 224 225 226 227 /** 228 * Constructs a new NtpMessage 229 * @param leapIndicator 230 * @param version 231 * @param mode 232 * @param stratum 233 * @param pollInterval 234 * @param precision 235 * @param rootDelay 236 * @param rootDispersion 237 * @param referenceIdentifier 238 * @param referenceTimestamp 239 * @param originateTimestamp 240 * @param receiveTimestamp 241 * @param transmitTimestamp 242 */ 243 public NtpMessage(byte leapIndicator, 244 byte version, 245 byte mode, 246 short stratum, 247 byte pollInterval, 248 byte precision, 249 double rootDelay, 250 double rootDispersion, 251 byte[] referenceIdentifier, 252 double referenceTimestamp, 253 double originateTimestamp, 254 double receiveTimestamp, 255 double transmitTimestamp) 256 { 257 // ToDo: Validity checking 258 this.leapIndicator = leapIndicator; 259 this.version = version; 260 this.mode = mode; 261 this.stratum = stratum; 262 this.pollInterval = pollInterval; 263 this.precision = precision; 264 this.rootDelay = rootDelay; 265 this.rootDispersion = rootDispersion; 266 this.referenceIdentifier = referenceIdentifier; 267 this.referenceTimestamp = referenceTimestamp; 268 this.originateTimestamp = originateTimestamp; 269 this.receiveTimestamp = receiveTimestamp; 270 this.transmitTimestamp = transmitTimestamp; 271 } 272 273 274 275 /** 276 * Constructs a new NtpMessage in client -> server mode, and sets the 277 * transmit timestamp to the current time. 278 */ 279 public NtpMessage() 280 { 281 // Note that all the other member variables are already set with 282 // appropriate default values. 283 this.mode = 3; 284 this.transmitTimestamp = (System.currentTimeMillis()/1000.0) + 2208988800.0; 285 } 286 287 288 289 /** 290 * This method constructs the data bytes of a raw NTP packet. 291 * @return 292 */ 293 public byte[] toByteArray() 294 { 295 // All bytes are automatically set to 0 296 byte[] p = new byte[48]; 297 298 p[0] = (byte) (leapIndicator << 6 | version << 3 | mode); 299 p[1] = (byte) stratum; 300 p[2] = pollInterval; 301 p[3] = precision; 302 303 // root delay is a signed 16.16-bit FP, in Java an int is 32-bits 304 int l = (int) (rootDelay * 65536.0); 305 p[4] = (byte) ((l >> 24) & 0xFF); 306 p[5] = (byte) ((l >> 16) & 0xFF); 307 p[6] = (byte) ((l >> 8) & 0xFF); 308 p[7] = (byte) (l & 0xFF); 309 310 // root dispersion is an unsigned 16.16-bit FP, in Java there are no 311 // unsigned primitive types, so we use a long which is 64-bits 312 long ul = (long) (rootDispersion * 65536.0); 313 p[8] = (byte) ((ul >> 24) & 0xFF); 314 p[9] = (byte) ((ul >> 16) & 0xFF); 315 p[10] = (byte) ((ul >> 8) & 0xFF); 316 p[11] = (byte) (ul & 0xFF); 317 318 p[12] = referenceIdentifier[0]; 319 p[13] = referenceIdentifier[1]; 320 p[14] = referenceIdentifier[2]; 321 p[15] = referenceIdentifier[3]; 322 323 encodeTimestamp(p, 16, referenceTimestamp); 324 encodeTimestamp(p, 24, originateTimestamp); 325 encodeTimestamp(p, 32, receiveTimestamp); 326 encodeTimestamp(p, 40, transmitTimestamp); 327 328 return p; 329 } 330 331 332 333 /** 334 * Returns a string representation of a NtpMessage 335 * @return 336 */ 337 public String toString() 338 { 339 String precisionStr = new DecimalFormat("0.#E0").format(Math.pow(2, precision)); 340 return "Leap indicator: " + leapIndicator + "\n" + 341 "Version: " + version + "\n" + 342 "Mode: " + mode + "\n" + 343 "Stratum: " + stratum + "\n" + 344 "Poll: " + pollInterval + "\n" + 345 "Precision: " + precision + " (" + precisionStr + " seconds)\n" + 346 "Root delay: " + new DecimalFormat("0.00").format(rootDelay*1000) + " ms\n" + 347 "Root dispersion: " + new DecimalFormat("0.00").format(rootDispersion*1000) + " ms\n" + 348 "Reference identifier: " + referenceIdentifierToString(referenceIdentifier, stratum, version) + "\n" + 349 "Reference timestamp: " + timestampToString(referenceTimestamp) + "\n" + 350 "Originate timestamp: " + timestampToString(originateTimestamp) + "\n" + 351 "Receive timestamp: " + timestampToString(receiveTimestamp) + "\n" + 352 "Transmit timestamp: " + timestampToString(transmitTimestamp); 353 } 354 355 356 357 /** 358 * Converts an unsigned byte to a short. By default, Java assumes that 359 * a byte is signed. 360 * @param b 361 * @return 362 */ 363 public static short unsignedByteToShort(byte b) 364 { 365 if((b & 0x80)==0x80) return (short) (128 + (b & 0x7f)); 366 return b; 367 } 368 369 370 371 /** 372 * Will read 8 bytes of a message beginning at <code>pointer</code> 373 * and return it as a double, according to the NTP 64-bit timestamp 374 * format. 375 * @param array 376 * @param pointer 377 * @return 378 */ 379 public static double decodeTimestamp(byte[] array, int pointer) 380 { 381 double r = 0.0; 382 383 for(int i=0; i<8; i++) 384 { 385 r += unsignedByteToShort(array[pointer+i]) * Math.pow(2, (3-i)*8); 386 } 387 388 return r; 389 } 390 391 392 393 /** 394 * Encodes a timestamp in the specified position in the message 395 * @param array 396 * @param pointer 397 * @param timestamp 398 */ 399 public static void encodeTimestamp(byte[] array, int pointer, double timestamp) 400 { 401 // Converts a double into a 64-bit fixed point 402 for(int i=0; i<8; i++) 403 { 404 // 2^24, 2^16, 2^8, .. 2^-32 405 double base = Math.pow(2, (3-i)*8); 406 407 // Capture byte value 408 array[pointer+i] = (byte) (timestamp / base); 409 410 // Subtract captured value from remaining total 411 timestamp = timestamp - (unsignedByteToShort(array[pointer+i]) * base); 412 } 413 414 // From RFC 2030: It is advisable to fill the non-significan't 415 // low order bits of the timestamp with a random, unbiased 416 // bitstring, both to avoid systematic roundoff errors and as 417 // a means of loop detection and replay detection. 418 array[7] = (byte) (Math.random()*255.0); 419 } 420 421 422 423 /** 424 * Returns a timestamp (number of seconds since 00:00 1-Jan-1900) as a 425 * formatted date/time string. 426 * @param timestamp 427 * @return 428 */ 429 public static String timestampToString(double timestamp) 430 { 431 if(timestamp==0) return "0"; 432 433 // timestamp is relative to 1900, utc is used by Java and is relative 434 // to 1970 435 double utc = timestamp - (2208988800.0); 436 437 // milliseconds 438 long ms = (long) (utc * 1000.0); 439 440 // date/time 441 String date = DateFormatPool.format("dd-MMM-yyyy HH:mm:ss",new Date(ms)); 442 443 // fraction 444 double fraction = timestamp - ((long) timestamp); 445 String fractionSting = new DecimalFormat(".000000").format(fraction); 446 447 return date + fractionSting; 448 } 449 450 451 452 /** 453 * Returns a string representation of a reference identifier according 454 * to the rules set out in RFC 2030. 455 * @param ref 456 * @param stratum 457 * @param version 458 * @return 459 */ 460 public static String referenceIdentifierToString(byte[] ref, short stratum, byte version) 461 { 462 // From the RFC 2030: 463 // In the case of NTP Version 3 or Version 4 stratum-0 (unspecified) 464 // or stratum-1 (primary) servers, this is a four-character ASCII 465 // string, left justified and zero padded to 32 bits. 466 if(stratum==0 || stratum==1) 467 { 468 return new String(ref); 469 } 470 471 // In NTP Version 3 secondary servers, this is the 32-bit IPv4 472 // address of the reference source. 473 else if(version==3) 474 { 475 return unsignedByteToShort(ref[0]) + "." + 476 unsignedByteToShort(ref[1]) + "." + 477 unsignedByteToShort(ref[2]) + "." + 478 unsignedByteToShort(ref[3]); 479 } 480 481 // In NTP Version 4 secondary servers, this is the low order 32 bits 482 // of the latest transmit timestamp of the reference source. 483 else if(version==4) 484 { 485 return "" + ((unsignedByteToShort(ref[0]) / 256.0) + 486 (unsignedByteToShort(ref[1]) / 65536.0) + 487 (unsignedByteToShort(ref[2]) / 16777216.0) + 488 (unsignedByteToShort(ref[3]) / 4294967296.0)); 489 } 490 491 return ""; 492 } 493}