Changeset d6638f3 for router


Ignore:
Timestamp:
May 12, 2016 1:48:44 PM (4 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
86a35f1
Parents:
f9bd495
Message:

NTP:
Don't put random data in zeroed fields
Increase random data in originate timestamp from 1 byte to 2 bytes
Verify originate timestamp to prevent injection
Verify received packet size
Log tweaks, javadocs, cleanups

Location:
router/java/src/net/i2p/router/time
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • router/java/src/net/i2p/router/time/NtpClient.java

    rf9bd495 rd6638f3  
    3737import java.util.Collections;
    3838
     39import net.i2p.data.DataHelper;
     40import net.i2p.util.HexDump;
     41import net.i2p.util.Log;
    3942
    4043/**
     
    5457class NtpClient {
    5558    /** difference between the unix epoch and jan 1 1900 (NTP uses that) */
    56     private final static double SECONDS_1900_TO_EPOCH = 2208988800.0;
     59    public final static double SECONDS_1900_TO_EPOCH = 2208988800.0;
    5760    private final static int NTP_PORT = 123;
    5861    private static final int DEFAULT_TIMEOUT = 10*1000;
     62    private static final int OFF_ORIGTIME = 24;
     63    private static final int OFF_TXTIME = 40;
     64    private static final int MIN_PKT_LEN = 48;
    5965
    6066    /**
     
    6470     * @throws IllegalArgumentException if none of the servers are reachable
    6571     */
     72/****
    6673    public static long currentTime(String serverNames[]) {
    6774        if (serverNames == null)
     
    7885        throw new IllegalArgumentException("No reachable NTP servers specified");
    7986    }
     87****/
    8088   
    8189    /**
    8290     * Query the ntp servers, returning the current time from first one we find
    8391     * Hack to return time and stratum
     92     *
     93     * @param log may be null
    8494     * @return time in rv[0] and stratum in rv[1]
    8595     * @throws IllegalArgumentException if none of the servers are reachable
    8696     * @since 0.7.12
    8797     */
    88     public static long[] currentTimeAndStratum(String serverNames[], int perServerTimeout) {
     98    public static long[] currentTimeAndStratum(String serverNames[], int perServerTimeout, Log log) {
    8999        if (serverNames == null)
    90100            throw new IllegalArgumentException("No NTP servers specified");
     
    94104        Collections.shuffle(names);
    95105        for (int i = 0; i < names.size(); i++) {
    96             long[] rv = currentTimeAndStratum(names.get(i), perServerTimeout);
     106            long[] rv = currentTimeAndStratum(names.get(i), perServerTimeout, log);
    97107            if (rv != null && rv[0] > 0)
    98108                return rv;
     
    106116     * @return milliseconds since january 1, 1970 (UTC), or -1 on error
    107117     */
     118/****
    108119    public static long currentTime(String serverName) {
    109120         long[] la = currentTimeAndStratum(serverName, DEFAULT_TIMEOUT);
     
    112123         return -1;
    113124    }
     125****/
    114126
    115127    /**
    116128     * Hack to return time and stratum
     129     *
     130     * @param log may be null
    117131     * @return time in rv[0] and stratum in rv[1], or null for error
    118132     * @since 0.7.12
    119133     */
    120     private static long[] currentTimeAndStratum(String serverName, int timeout) {
     134    private static long[] currentTimeAndStratum(String serverName, int timeout, Log log) {
    121135        DatagramSocket socket = null;
    122136        try {
     
    126140            byte[] buf = new NtpMessage().toByteArray();
    127141            DatagramPacket packet = new DatagramPacket(buf, buf.length, address, NTP_PORT);
     142            byte[] txtime = new byte[8];
    128143
    129144            // Set the transmit timestamp *just* before sending the packet
    130145            // ToDo: Does this actually improve performance or not?
    131             NtpMessage.encodeTimestamp(packet.getData(), 40,
     146            NtpMessage.encodeTimestamp(packet.getData(), OFF_TXTIME,
    132147                                       (System.currentTimeMillis()/1000.0)
    133148                                       + SECONDS_1900_TO_EPOCH);
    134149
    135150            socket.send(packet);
     151            // save for check
     152            System.arraycopy(packet.getData(), OFF_TXTIME, txtime, 0, 8);
     153            if (log != null && log.shouldDebug())
     154                log.debug("Sent:\n" + HexDump.dump(buf));
    136155
    137156            // Get response
     
    143162            double destinationTimestamp = (System.currentTimeMillis()/1000.0) + SECONDS_1900_TO_EPOCH;
    144163
     164            if (packet.getLength() < MIN_PKT_LEN) {
     165                if (log != null && log.shouldWarn())
     166                    log.warn("Short packet length " + packet.getLength());
     167                return null;
     168            }
     169
    145170            // Process response
    146171            NtpMessage msg = new NtpMessage(packet.getData());
    147172
    148             //double roundTripDelay = (destinationTimestamp-msg.originateTimestamp) -
    149             //                        (msg.receiveTimestamp-msg.transmitTimestamp);
    150             double localClockOffset = ((msg.receiveTimestamp - msg.originateTimestamp) +
    151                                        (msg.transmitTimestamp - destinationTimestamp)) / 2;
     173            if (log != null && log.shouldDebug())
     174                log.debug("Received from: " + packet.getAddress().getHostAddress() +
     175                          '\n' + msg + '\n' + HexDump.dump(packet.getData()));
    152176
    153177            // Stratum must be between 1 (atomic) and 15 (maximum defined value)
    154178            // Anything else is right out, treat such responses like errors
    155179            if ((msg.stratum < 1) || (msg.stratum > 15)) {
    156                 //System.out.println("Response from NTP server of unacceptable stratum " + msg.stratum + ", failing.");
     180                if (log != null && log.shouldWarn())
     181                    log.warn("Response from NTP server of unacceptable stratum " + msg.stratum + ", failing.");
    157182                return null;
    158183            }
     184
     185            if (!DataHelper.eq(txtime, 0, packet.getData(), OFF_ORIGTIME, 8)) {
     186                if (log != null && log.shouldWarn())
     187                    log.warn("Origin time mismatch sent:\n" + HexDump.dump(txtime) +
     188                             "rcvd:\n" + HexDump.dump(packet.getData(), OFF_ORIGTIME, 8));
     189                return null;
     190            }
     191
     192
     193            double localClockOffset = ((msg.receiveTimestamp - msg.originateTimestamp) +
     194                                       (msg.transmitTimestamp - destinationTimestamp)) / 2;
    159195           
    160196            long[] rv = new long[2];
    161197            rv[0] = (long)(System.currentTimeMillis() + localClockOffset*1000);
    162198            rv[1] = msg.stratum;
    163             //System.out.println("host: " + address.getHostAddress() + " rtt: " + roundTripDelay + " offset: " + localClockOffset + " seconds");
     199            if (log != null && log.shouldInfo()) {
     200                double roundTripDelay = (destinationTimestamp-msg.originateTimestamp) -
     201                                        (msg.receiveTimestamp-msg.transmitTimestamp);
     202                log.info("host: " + packet.getAddress().getHostAddress() + " rtt: " +
     203                         roundTripDelay + " offset: " + localClockOffset + " seconds");
     204            }
    164205            return rv;
    165206        } catch (IOException ioe) {
    166             //ioe.printStackTrace();
     207            if (log != null && log.shouldWarn())
     208                log.warn("NTP failure from " + serverName, ioe);
    167209            return null;
    168210        } finally {
     
    172214    }
    173215   
    174 /****
    175216    public static void main(String[] args) throws IOException {
    176217        // Process command-line args
    177218        if(args.length <= 0) {
    178             printUsage();
    179             return;
    180             // args = new String[] { "ntp1.sth.netnod.se", "ntp2.sth.netnod.se" };
     219           args = new String[] { "pool.ntp.org" };
    181220        }
    182221
    183         long now = currentTime(args);
    184         System.out.println("Current time: " + new java.util.Date(now));
    185     }
    186    
    187     static void printUsage() {
     222        Log log = new Log(NtpClient.class);
     223        long[] rv = currentTimeAndStratum(args, DEFAULT_TIMEOUT, log);
     224        System.out.println("Current time: " + new java.util.Date(rv[0]) + " (stratum " + rv[1] + ')');
     225    }
     226   
     227/****
     228    private static void printUsage() {
    188229        System.out.println(
    189230        "NtpClient - an NTP client for Java.\n" +
  • router/java/src/net/i2p/router/time/NtpMessage.java

    rf9bd495 rd6638f3  
    3232import java.text.DecimalFormat;
    3333import java.text.SimpleDateFormat;
     34import java.util.Arrays;
    3435import java.util.Date;
    3536
     
    118119     * multicast mode, the server sets this field to 5 (broadcast).
    119120     */
    120     public byte mode = 0;
     121    public final byte mode;
    121122   
    122123   
     
    234235     * in seconds since 00:00 1-Jan-1900.
    235236     */
    236     public double transmitTimestamp = 0;
     237    public final double transmitTimestamp;
    237238   
    238239   
     
    240241    /**
    241242     * Constructs a new NtpMessage from an array of bytes.
     243     *
     244     * @param array 48 bytes minimum
    242245     */
    243246    public NtpMessage(byte[] array) {
     
    281284        // appropriate default values.
    282285        this.mode = 3;
    283         this.transmitTimestamp = (System.currentTimeMillis()/1000.0) + 2208988800.0;
     286        this.transmitTimestamp = (System.currentTimeMillis()/1000.0) + NtpClient.SECONDS_1900_TO_EPOCH;
    284287    }
    285288   
     
    288291    /**
    289292     * This method constructs the data bytes of a raw NTP packet.
     293     *
     294     * @return 48 bytes
    290295     */
    291296    public byte[] toByteArray() {
     
    369374     * and return it as a double, according to the NTP 64-bit timestamp
    370375     * format.
     376     *
     377     * @param array 8 bytes starting at pointer
     378     * @param pointer the offset
     379     * @return the time since 1900 (NOT Java time)
    371380     */
    372381    public static double decodeTimestamp(byte[] array, int pointer) {
     
    384393    /**
    385394     * Encodes a timestamp in the specified position in the message
     395     *
     396     * @param array output 8 bytes starting at pointer
     397     * @param pointer the offset
     398     * @param timestamp the time to encode (since 1900, NOT Java time)
    386399     */
    387400    public static void encodeTimestamp(byte[] array, int pointer, double timestamp) {
     401        if (timestamp == 0.0) {
     402            // don't put in random data
     403            Arrays.fill(array, pointer, pointer + 8, (byte) 0);
     404            return;
     405        }
     406
    388407        // Converts a double into a 64-bit fixed point
    389         for(int i=0; i<8; i++) {
     408        // 6 bytes of real data
     409        for(int i=0; i<7; i++) {
    390410            // 2^24, 2^16, 2^8, .. 2^-32
    391411            double base = Math.pow(2, (3-i)*8);
     
    402422        // bitstring, both to avoid systematic roundoff errors and as
    403423        // a means of loop detection and replay detection.
    404         array[7+pointer] = (byte) (RandomSource.getInstance().nextInt());
     424        // 2 bytes of random data
     425        RandomSource.getInstance().nextBytes(array, pointer + 6, 2);
    405426    }
    406427   
     
    416437        // timestamp is relative to 1900, utc is used by Java and is relative
    417438        // to 1970
    418         double utc = timestamp - (2208988800.0);
     439        double utc = timestamp - NtpClient.SECONDS_1900_TO_EPOCH;
    419440       
    420441        // milliseconds
     
    426447        // fraction
    427448        double fraction = timestamp - ((long) timestamp);
    428         String fractionSting = new DecimalFormat(".000000").format(fraction);
     449        String fractionSting = new DecimalFormat(".000000000").format(fraction);
    429450       
    430451        return date + fractionSting;
  • router/java/src/net/i2p/router/time/RouterTimestamper.java

    rf9bd495 rd6638f3  
    274274            //    try { Thread.sleep(2*1000); } catch (InterruptedException ie) {}
    275275            //}
    276             long[] timeAndStratum = NtpClient.currentTimeAndStratum(serverList, perServerTimeout);
     276            long[] timeAndStratum = NtpClient.currentTimeAndStratum(serverList, perServerTimeout, _log);
    277277            now = timeAndStratum[0];
    278278            stratum = (int) timeAndStratum[1];
Note: See TracChangeset for help on using the changeset viewer.