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}