001    package org.apache.commons.net.ntp;
002    /*
003     * Licensed to the Apache Software Foundation (ASF) under one or more
004     * contributor license agreements.  See the NOTICE file distributed with
005     * this work for additional information regarding copyright ownership.
006     * The ASF licenses this file to You under the Apache License, Version 2.0
007     * (the "License"); you may not use this file except in compliance with
008     * the License.  You may obtain a copy of the License at
009     *
010     *      http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    import java.net.DatagramPacket;
020    
021    /***
022     * Implementation of NtpV3Packet with methods converting Java objects to/from
023     * the Network Time Protocol (NTP) data message header format described in RFC-1305.
024     *
025     * @author Naz Irizarry, MITRE Corp
026     * @author Jason Mathews, MITRE Corp
027     *
028     * @version $Revision: 1299238 $
029     */
030    public class NtpV3Impl implements NtpV3Packet
031    {
032    
033        private static final int MODE_INDEX = 0;
034        private static final int MODE_SHIFT = 0;
035    
036        private static final int VERSION_INDEX = 0;
037        private static final int VERSION_SHIFT = 3;
038    
039        private static final int LI_INDEX = 0;
040        private static final int LI_SHIFT = 6;
041    
042        private static final int STRATUM_INDEX = 1;
043        private static final int POLL_INDEX = 2;
044        private static final int PRECISION_INDEX = 3;
045    
046        private static final int ROOT_DELAY_INDEX = 4;
047        private static final int ROOT_DISPERSION_INDEX = 8;
048        private static final int REFERENCE_ID_INDEX = 12;
049    
050        private static final int REFERENCE_TIMESTAMP_INDEX = 16;
051        private static final int ORIGINATE_TIMESTAMP_INDEX = 24;
052        private static final int RECEIVE_TIMESTAMP_INDEX = 32;
053        private static final int TRANSMIT_TIMESTAMP_INDEX = 40;
054    
055    //    private static final int KEY_IDENTIFIER_INDEX = 48;
056    //    private static final int MESSAGE_DIGEST = 54; /* len 16 bytes */
057    
058        private final byte[] buf = new byte[48];
059    
060        private volatile DatagramPacket dp;
061    
062        /** Creates a new instance of NtpV3Impl */
063        public NtpV3Impl()
064        {
065        }
066    
067        /***
068         * Returns mode as defined in RFC-1305 which is a 3-bit integer
069         * whose value is indicated by the MODE_xxx parameters.
070         *
071         * @return mode as defined in RFC-1305.
072         */
073        public int getMode()
074        {
075            return (ui(buf[MODE_INDEX]) >> MODE_SHIFT) & 0x7;
076        }
077    
078        /***
079         * Return human-readable name of message mode type as described in
080         * RFC 1305.
081         * @return mode name as string.
082         */
083        public String getModeName()
084        {
085            return NtpUtils.getModeName(getMode());
086        }
087    
088        /***
089         * Set mode as defined in RFC-1305.
090         * @param mode
091         */
092        public void setMode(int mode)
093        {
094            buf[MODE_INDEX] = (byte) (buf[MODE_INDEX] & 0xF8 | mode & 0x7);
095        }
096    
097        /***
098         * Returns leap indicator as defined in RFC-1305 which is a two-bit code:
099         *  0=no warning
100         *  1=last minute has 61 seconds
101         *  2=last minute has 59 seconds
102         *  3=alarm condition (clock not synchronized)
103         *
104         * @return leap indicator as defined in RFC-1305.
105         */
106        public int getLeapIndicator()
107        {
108            return (ui(buf[LI_INDEX]) >> LI_SHIFT) & 0x3;
109        }
110    
111        /***
112         * Set leap indicator as defined in RFC-1305.
113         * @param li leap indicator.
114         */
115        public void setLeapIndicator(int li)
116        {
117            buf[LI_INDEX] = (byte) (buf[LI_INDEX] & 0x3F | ((li & 0x3) << LI_SHIFT));
118        }
119    
120        /***
121         * Returns poll interval as defined in RFC-1305, which is an eight-bit
122         * signed integer indicating the maximum interval between successive
123         * messages, in seconds to the nearest power of two (e.g. value of six
124         * indicates an interval of 64 seconds. The values that can appear in
125         * this field range from NTP_MINPOLL to NTP_MAXPOLL inclusive.
126         *
127         * @return poll interval as defined in RFC-1305.
128         */
129        public int getPoll()
130        {
131            return buf[POLL_INDEX];
132        }
133    
134        /***
135         * Set poll interval as defined in RFC-1305.
136         *
137         * @param poll poll interval.
138         */
139        public void setPoll(int poll)
140        {
141            buf[POLL_INDEX] = (byte) (poll & 0xFF);
142        }
143    
144        /***
145         * Returns precision as defined in RFC-1305 encoded as an 8-bit signed
146         * integer (seconds to nearest power of two).
147         * Values normally range from -6 to -20.
148         *
149         * @return precision as defined in RFC-1305.
150         */
151        public int getPrecision()
152        {
153            return buf[PRECISION_INDEX];
154        }
155    
156        /***
157         * Set precision as defined in RFC-1305.
158         * @param precision
159         */
160        public void setPrecision(int precision)
161        {
162            buf[PRECISION_INDEX] = (byte) (precision & 0xFF);
163        }
164    
165        /***
166         * Returns NTP version number as defined in RFC-1305.
167         *
168         * @return NTP version number.
169         */
170        public int getVersion()
171        {
172            return (ui(buf[VERSION_INDEX]) >> VERSION_SHIFT) & 0x7;
173        }
174    
175        /***
176         * Set NTP version as defined in RFC-1305.
177         *
178         * @param version NTP version.
179         */
180        public void setVersion(int version)
181        {
182            buf[VERSION_INDEX] = (byte) (buf[VERSION_INDEX] & 0xC7 | ((version & 0x7) << VERSION_SHIFT));
183        }
184    
185        /***
186         * Returns Stratum as defined in RFC-1305, which indicates the stratum level
187         * of the local clock, with values defined as follows: 0=unspecified,
188         * 1=primary ref clock, and all others a secondary reference (via NTP).
189         *
190         * @return Stratum level as defined in RFC-1305.
191         */
192        public int getStratum()
193        {
194            return ui(buf[STRATUM_INDEX]);
195        }
196    
197        /***
198         * Set stratum level as defined in RFC-1305.
199         *
200         * @param stratum stratum level.
201         */
202        public void setStratum(int stratum)
203        {
204            buf[STRATUM_INDEX] = (byte) (stratum & 0xFF);
205        }
206    
207        /***
208         * Return root delay as defined in RFC-1305, which is the total roundtrip delay
209         * to the primary reference source, in seconds. Values can take positive and
210         * negative values, depending on clock precision and skew.
211         *
212         * @return root delay as defined in RFC-1305.
213         */
214        public int getRootDelay()
215        {
216            return getInt(ROOT_DELAY_INDEX);
217        }
218    
219        /***
220         * Return root delay as defined in RFC-1305 in milliseconds, which is
221         * the total roundtrip delay to the primary reference source, in
222         * seconds. Values can take positive and negative values, depending
223         * on clock precision and skew.
224         *
225         * @return root delay in milliseconds
226         */
227        public double getRootDelayInMillisDouble()
228        {
229            double l = getRootDelay();
230            return l / 65.536;
231        }
232    
233        /***
234         * Returns root dispersion as defined in RFC-1305.
235         * @return root dispersion.
236         */
237        public int getRootDispersion()
238        {
239            return getInt(ROOT_DISPERSION_INDEX);
240        }
241    
242        /***
243         * Returns root dispersion (as defined in RFC-1305) in milliseconds.
244         *
245         * @return root dispersion in milliseconds
246         */
247        public long getRootDispersionInMillis()
248        {
249            long l = getRootDispersion();
250            return (l * 1000) / 65536L;
251        }
252    
253        /***
254         * Returns root dispersion (as defined in RFC-1305) in milliseconds
255         * as double precision value.
256         *
257         * @return root dispersion in milliseconds
258         */
259        public double getRootDispersionInMillisDouble()
260        {
261            double l = getRootDispersion();
262            return l / 65.536;
263        }
264    
265        /***
266         * Set reference clock identifier field with 32-bit unsigned integer value.
267         * See RFC-1305 for description.
268         *
269         * @param refId reference clock identifier.
270         */
271        public void setReferenceId(int refId)
272        {
273            for (int i = 3; i >= 0; i--) {
274                buf[REFERENCE_ID_INDEX + i] = (byte) (refId & 0xff);
275                refId >>>= 8; // shift right one-byte
276            }
277        }
278    
279        /***
280         * Returns the reference id as defined in RFC-1305, which is
281         * a 32-bit integer whose value is dependent on several criteria.
282         *
283         * @return the reference id as defined in RFC-1305.
284         */
285        public int getReferenceId()
286        {
287            return getInt(REFERENCE_ID_INDEX);
288        }
289    
290        /***
291         * Returns the reference id string. String cannot be null but
292         * value is dependent on the version of the NTP spec supported
293         * and stratum level. Value can be an empty string, clock type string,
294         * IP address, or a hex string.
295         *
296         * @return the reference id string.
297         */
298        public String getReferenceIdString()
299        {
300            int version = getVersion();
301            int stratum = getStratum();
302            if (version == VERSION_3 || version == VERSION_4) {
303                if (stratum == 0 || stratum == 1) {
304                    return idAsString(); // 4-character ASCII string (e.g. GPS, USNO)
305                }
306                // in NTPv4 servers this is latest transmit timestamp of ref source
307                if (version == VERSION_4) {
308                    return idAsHex();
309                }
310            }
311    
312            // Stratum 2 and higher this is a four-octet IPv4 address
313            // of the primary reference host.
314            if (stratum >= 2) {
315                return idAsIPAddress();
316            }
317            return idAsHex();
318        }
319    
320        /***
321         * Returns Reference id as dotted IP address.
322         * @return refId as IP address string.
323         */
324        private String idAsIPAddress()
325        {
326            return ui(buf[REFERENCE_ID_INDEX]) + "." +
327                    ui(buf[REFERENCE_ID_INDEX + 1]) + "." +
328                    ui(buf[REFERENCE_ID_INDEX + 2]) + "." +
329                    ui(buf[REFERENCE_ID_INDEX + 3]);
330        }
331    
332        private String idAsString()
333        {
334            StringBuilder id = new StringBuilder();
335            for (int i = 0; i <= 3; i++) {
336                char c = (char) buf[REFERENCE_ID_INDEX + i];
337                if (c == 0) {  // 0-terminated string
338                    break;
339                }
340                id.append(c);
341            }
342            return id.toString();
343        }
344    
345        private String idAsHex()
346        {
347            return Integer.toHexString(getReferenceId());
348        }
349    
350        /***
351         * Returns the transmit timestamp as defined in RFC-1305.
352         *
353         * @return the transmit timestamp as defined in RFC-1305.
354         * Never returns a null object.
355         */
356        public TimeStamp getTransmitTimeStamp()
357        {
358            return getTimestamp(TRANSMIT_TIMESTAMP_INDEX);
359        }
360    
361        /***
362         * Set transmit time with NTP timestamp.
363         * If <code>ts</code> is null then zero time is used.
364         *
365         * @param ts NTP timestamp
366         */
367        public void setTransmitTime(TimeStamp ts)
368        {
369            setTimestamp(TRANSMIT_TIMESTAMP_INDEX, ts);
370        }
371    
372        /***
373         * Set originate timestamp given NTP TimeStamp object.
374         * If <code>ts</code> is null then zero time is used.
375         *
376         * @param ts NTP timestamp
377         */
378        public void setOriginateTimeStamp(TimeStamp ts)
379        {
380            setTimestamp(ORIGINATE_TIMESTAMP_INDEX, ts);
381        }
382    
383        /***
384         * Returns the originate time as defined in RFC-1305.
385         *
386         * @return the originate time.
387         * Never returns null.
388         */
389        public TimeStamp getOriginateTimeStamp()
390        {
391            return getTimestamp(ORIGINATE_TIMESTAMP_INDEX);
392        }
393    
394        /***
395         * Returns the reference time as defined in RFC-1305.
396         *
397         * @return the reference time as <code>TimeStamp</code> object.
398         * Never returns null.
399         */
400        public TimeStamp getReferenceTimeStamp()
401        {
402            return getTimestamp(REFERENCE_TIMESTAMP_INDEX);
403        }
404    
405        /***
406         * Set Reference time with NTP timestamp. If <code>ts</code> is null
407         * then zero time is used.
408         *
409         * @param ts NTP timestamp
410         */
411        public void setReferenceTime(TimeStamp ts)
412        {
413            setTimestamp(REFERENCE_TIMESTAMP_INDEX, ts);
414        }
415    
416        /***
417         * Returns receive timestamp as defined in RFC-1305.
418         *
419         * @return the receive time.
420         * Never returns null.
421         */
422        public TimeStamp getReceiveTimeStamp()
423        {
424            return getTimestamp(RECEIVE_TIMESTAMP_INDEX);
425        }
426    
427        /***
428         * Set receive timestamp given NTP TimeStamp object.
429         * If <code>ts</code> is null then zero time is used.
430         *
431         * @param ts timestamp
432         */
433        public void setReceiveTimeStamp(TimeStamp ts)
434        {
435            setTimestamp(RECEIVE_TIMESTAMP_INDEX, ts);
436        }
437    
438        /***
439         * Return type of time packet. The values (e.g. NTP, TIME, ICMP, ...)
440         * correspond to the protocol used to obtain the timing information.
441         *
442         * @return packet type string identifier which in this case is "NTP".
443         */
444        public String getType()
445        {
446            return "NTP";
447        }
448    
449        /***
450         * @return 4 bytes as 32-bit int
451         */
452        private int getInt(int index)
453        {
454            int i = ui(buf[index]) << 24 |
455                    ui(buf[index + 1]) << 16 |
456                    ui(buf[index + 2]) << 8 |
457                    ui(buf[index + 3]);
458    
459            return i;
460        }
461    
462        /***
463         * Get NTP Timestamp at specified starting index.
464         *
465         * @param index index into data array
466         * @return TimeStamp object for 64 bits starting at index
467         */
468        private TimeStamp getTimestamp(int index)
469        {
470            return new TimeStamp(getLong(index));
471        }
472    
473        /***
474         * Get Long value represented by bits starting at specified index.
475         *
476         * @return 8 bytes as 64-bit long
477         */
478        private long getLong(int index)
479        {
480            long i = ul(buf[index]) << 56 |
481                    ul(buf[index + 1]) << 48 |
482                    ul(buf[index + 2]) << 40 |
483                    ul(buf[index + 3]) << 32 |
484                    ul(buf[index + 4]) << 24 |
485                    ul(buf[index + 5]) << 16 |
486                    ul(buf[index + 6]) << 8 |
487                    ul(buf[index + 7]);
488            return i;
489        }
490    
491        /***
492         * Sets the NTP timestamp at the given array index.
493         *
494         * @param index index into the byte array.
495         * @param t TimeStamp.
496         */
497        private void setTimestamp(int index, TimeStamp t)
498        {
499            long ntpTime = (t == null) ? 0 : t.ntpValue();
500            // copy 64-bits from Long value into 8 x 8-bit bytes of array
501            // one byte at a time shifting 8-bits for each position.
502            for (int i = 7; i >= 0; i--) {
503                buf[index + i] = (byte) (ntpTime & 0xFF);
504                ntpTime >>>= 8; // shift to next byte
505            }
506            // buf[index] |= 0x80;  // only set if 1900 baseline....
507        }
508    
509        /***
510         * Returns the datagram packet with the NTP details already filled in.
511         *
512         * @return a datagram packet.
513         */
514        public synchronized DatagramPacket getDatagramPacket()
515        {
516            if (dp == null) {
517                dp = new DatagramPacket(buf, buf.length);
518                dp.setPort(NTP_PORT);
519            }
520            return dp;
521        }
522    
523        /***
524         * Set the contents of this object from source datagram packet.
525         *
526         * @param srcDp source DatagramPacket to copy contents from.
527         */
528        public void setDatagramPacket(DatagramPacket srcDp)
529        {
530            byte[] incomingBuf = srcDp.getData();
531            int len = srcDp.getLength();
532            if (len > buf.length) {
533                len = buf.length;
534            }
535    
536            System.arraycopy(incomingBuf, 0, buf, 0, len);
537        }
538    
539        /***
540         * Convert byte to unsigned integer.
541         * Java only has signed types so we have to do
542         * more work to get unsigned ops.
543         *
544         * @param b
545         * @return unsigned int value of byte
546         */
547        protected final static int ui(byte b)
548        {
549            int i = b & 0xFF;
550            return i;
551        }
552    
553        /***
554         * Convert byte to unsigned long.
555         * Java only has signed types so we have to do
556         * more work to get unsigned ops
557         *
558         * @param b
559         * @return unsigned long value of byte
560         */
561        protected final static long ul(byte b)
562        {
563            long i = b & 0xFF;
564            return i;
565        }
566    
567        /***
568         * Returns details of NTP packet as a string.
569         *
570         * @return details of NTP packet as a string.
571         */
572        @Override
573        public String toString()
574        {
575            return "[" +
576                    "version:" + getVersion() +
577                    ", mode:" + getMode() +
578                    ", poll:" + getPoll() +
579                    ", precision:" + getPrecision() +
580                    ", delay:" + getRootDelay() +
581                    ", dispersion(ms):" + getRootDispersionInMillisDouble() +
582                    ", id:" + getReferenceIdString() +
583                    ", xmitTime:" + getTransmitTimeStamp().toDateString() +
584                    " ]";
585        }
586    
587    }