forked from I2P_Developers/i2p.i2p
Clock: Add sanity checks to detect invalid system clock
This commit is contained in:
157
core/java/src/net/i2p/time/BuildTime.java
Normal file
157
core/java/src/net/i2p/time/BuildTime.java
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package net.i2p.time;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
import java.util.jar.Attributes;
|
||||||
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.util.SystemVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the build date as set in i2p.jar,
|
||||||
|
* and reasonable min and max values for the current time,
|
||||||
|
* to be used as sanity checks.
|
||||||
|
*
|
||||||
|
* Idea taken from Chrome, which assumes any clock more than
|
||||||
|
* 2 days before or 1 year after the build date is bad.
|
||||||
|
*
|
||||||
|
* Not maintained as a public API, not for use by plugins or applications.
|
||||||
|
*
|
||||||
|
* @since 0.9.25 modded from FileDumpHelper
|
||||||
|
*/
|
||||||
|
public class BuildTime {
|
||||||
|
|
||||||
|
private static final long _buildTime;
|
||||||
|
private static final long _earliestTime;
|
||||||
|
private static final long _latestTime;
|
||||||
|
private static final long YEARS_25 = 25L*365*24*60*60*1000;
|
||||||
|
/** update this periodically */
|
||||||
|
private static final String EARLIEST = "2016-02-19 12:00:00 UTC";
|
||||||
|
|
||||||
|
static {
|
||||||
|
// this is the standard format of build.timestamp as set in the top-level build.xml
|
||||||
|
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z", Locale.US);
|
||||||
|
TimeZone utc = TimeZone.getTimeZone("GMT");
|
||||||
|
fmt.setTimeZone(utc);
|
||||||
|
long min;
|
||||||
|
try {
|
||||||
|
Date date = fmt.parse(EARLIEST);
|
||||||
|
if (date == null)
|
||||||
|
throw new RuntimeException("BuildTime FAIL");
|
||||||
|
min = date.getTime();
|
||||||
|
} catch (ParseException pe) {
|
||||||
|
System.out.println("BuildTime FAIL");
|
||||||
|
pe.printStackTrace();
|
||||||
|
throw new RuntimeException("BuildTime FAIL", pe);
|
||||||
|
}
|
||||||
|
long max = min + YEARS_25;
|
||||||
|
long build = getBuildTime(fmt, "i2p.jar");
|
||||||
|
if (build > max) {
|
||||||
|
System.out.println("Warning: Strange build time, contact packager: " + new Date(build));
|
||||||
|
build = max;
|
||||||
|
} else if (build < min) {
|
||||||
|
if (build > 0)
|
||||||
|
System.out.println("Warning: Strange build time, contact packager: " + new Date(build));
|
||||||
|
build = min;
|
||||||
|
} else {
|
||||||
|
// build time looks reasonable
|
||||||
|
// allow 24h skew on build machine
|
||||||
|
min = build - 24*60*60*1000L;
|
||||||
|
}
|
||||||
|
_earliestTime = min;
|
||||||
|
_latestTime = max;
|
||||||
|
_buildTime = build;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the build date for i2p.jar.
|
||||||
|
*
|
||||||
|
* @return the earliest possible time if actual build date is unknown
|
||||||
|
*/
|
||||||
|
public static long getBuildTime() {
|
||||||
|
return _buildTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the earliest it could possibly be right now.
|
||||||
|
* Latest of the build time minus a day, or a hardcoded time.
|
||||||
|
*
|
||||||
|
* @return the time
|
||||||
|
*/
|
||||||
|
public static long getEarliestTime() {
|
||||||
|
return _earliestTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the latest it could possibly be right now.
|
||||||
|
* Hardcoded.
|
||||||
|
*
|
||||||
|
* @return the time
|
||||||
|
*/
|
||||||
|
public static long getLatestTime() {
|
||||||
|
return _latestTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BuildTime() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Won't be available on Android or on any builds not using our build.xml.
|
||||||
|
*
|
||||||
|
* @return 0 if unknown
|
||||||
|
*/
|
||||||
|
private static long getBuildTime(SimpleDateFormat fmt, String jar) {
|
||||||
|
if (SystemVersion.isAndroid())
|
||||||
|
return 0;
|
||||||
|
File f = new File(I2PAppContext.getGlobalContext().getBaseDir(), "lib");
|
||||||
|
f = new File(f, jar);
|
||||||
|
Attributes atts = attributes(f);
|
||||||
|
if (atts == null)
|
||||||
|
return 0;
|
||||||
|
String s = atts.getValue("Build-Date");
|
||||||
|
if (s == null)
|
||||||
|
return 0;
|
||||||
|
try {
|
||||||
|
Date date = fmt.parse(s);
|
||||||
|
if (date != null) {
|
||||||
|
return date.getTime();
|
||||||
|
}
|
||||||
|
} catch (ParseException pe) {}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Attributes attributes(File f) {
|
||||||
|
InputStream in = null;
|
||||||
|
try {
|
||||||
|
in = (new URL("jar:file:" + f.getAbsolutePath() + "!/META-INF/MANIFEST.MF")).openStream();
|
||||||
|
Manifest man = new Manifest(in);
|
||||||
|
return man.getMainAttributes();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (in != null) try { in.close(); } catch (IOException e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/****
|
||||||
|
public static void main(String[] args) {
|
||||||
|
long date = getEarliestTime();
|
||||||
|
System.out.println("Earliest date: " + new Date(date));
|
||||||
|
date = getBuildTime();
|
||||||
|
System.out.println("Build date: " + new Date(date));
|
||||||
|
date = System.currentTimeMillis();
|
||||||
|
System.out.println("System time: " + new Date(date));
|
||||||
|
date = I2PAppContext.getGlobalContext().clock().now();
|
||||||
|
System.out.println("I2P time: " + new Date(date));
|
||||||
|
date = getLatestTime();
|
||||||
|
System.out.println("Latest date: " + new Date(date));
|
||||||
|
}
|
||||||
|
****/
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
package net.i2p.util;
|
package net.i2p.util;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.time.BuildTime;
|
||||||
import net.i2p.time.Timestamper;
|
import net.i2p.time.Timestamper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,6 +21,7 @@ import net.i2p.time.Timestamper;
|
|||||||
*/
|
*/
|
||||||
public class Clock implements Timestamper.UpdateListener {
|
public class Clock implements Timestamper.UpdateListener {
|
||||||
protected final I2PAppContext _context;
|
protected final I2PAppContext _context;
|
||||||
|
protected final boolean _isSystemClockBad;
|
||||||
protected long _startedOn;
|
protected long _startedOn;
|
||||||
protected boolean _statCreated;
|
protected boolean _statCreated;
|
||||||
protected volatile long _offset;
|
protected volatile long _offset;
|
||||||
@ -28,7 +31,28 @@ public class Clock implements Timestamper.UpdateListener {
|
|||||||
public Clock(I2PAppContext context) {
|
public Clock(I2PAppContext context) {
|
||||||
_context = context;
|
_context = context;
|
||||||
_listeners = new CopyOnWriteArraySet<ClockUpdateListener>();
|
_listeners = new CopyOnWriteArraySet<ClockUpdateListener>();
|
||||||
_startedOn = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
|
long min = BuildTime.getEarliestTime();
|
||||||
|
long max = BuildTime.getLatestTime();
|
||||||
|
// If the system clock is obviously bad, set our offset so our time is something "close"
|
||||||
|
// We do not call setOffset() here as it sets _alreadyChanged.
|
||||||
|
// Don't use Log here.
|
||||||
|
if (now < min) {
|
||||||
|
// positive offset
|
||||||
|
_offset = min - now;
|
||||||
|
System.out.println("ERROR: System clock is invalid: " + new Date(now));
|
||||||
|
now = min;
|
||||||
|
_isSystemClockBad = true;
|
||||||
|
} else if (now > max) {
|
||||||
|
// negative offset
|
||||||
|
_offset = max - now;
|
||||||
|
System.out.println("ERROR: System clock is invalid: " + new Date(now));
|
||||||
|
now = max;
|
||||||
|
_isSystemClockBad = true;
|
||||||
|
} else {
|
||||||
|
_isSystemClockBad = false;
|
||||||
|
}
|
||||||
|
_startedOn = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Clock getInstance() {
|
public static Clock getInstance() {
|
||||||
@ -63,6 +87,7 @@ public class Clock implements Timestamper.UpdateListener {
|
|||||||
/**
|
/**
|
||||||
* Specify how far away from the "correct" time the computer is - a positive
|
* Specify how far away from the "correct" time the computer is - a positive
|
||||||
* value means that the system time is slow, while a negative value means the system time is fast.
|
* value means that the system time is slow, while a negative value means the system time is fast.
|
||||||
|
*
|
||||||
* Warning - overridden in RouterClock
|
* Warning - overridden in RouterClock
|
||||||
*
|
*
|
||||||
* @param offsetMs the delta from System.currentTimeMillis() (NOT the delta from now())
|
* @param offsetMs the delta from System.currentTimeMillis() (NOT the delta from now())
|
||||||
@ -70,7 +95,7 @@ public class Clock implements Timestamper.UpdateListener {
|
|||||||
public synchronized void setOffset(long offsetMs, boolean force) {
|
public synchronized void setOffset(long offsetMs, boolean force) {
|
||||||
long delta = offsetMs - _offset;
|
long delta = offsetMs - _offset;
|
||||||
if (!force) {
|
if (!force) {
|
||||||
if ((offsetMs > MAX_OFFSET) || (offsetMs < 0 - MAX_OFFSET)) {
|
if (!_isSystemClockBad && (offsetMs > MAX_OFFSET || offsetMs < 0 - MAX_OFFSET)) {
|
||||||
Log log = getLog();
|
Log log = getLog();
|
||||||
if (log.shouldLog(Log.WARN))
|
if (log.shouldLog(Log.WARN))
|
||||||
log.warn("Maximum offset shift exceeded [" + offsetMs + "], NOT HONORING IT");
|
log.warn("Maximum offset shift exceeded [" + offsetMs + "], NOT HONORING IT");
|
||||||
@ -128,17 +153,27 @@ public class Clock implements Timestamper.UpdateListener {
|
|||||||
|
|
||||||
|
|
||||||
public void setNow(long realTime) {
|
public void setNow(long realTime) {
|
||||||
|
if (realTime < BuildTime.getEarliestTime() || realTime > BuildTime.getLatestTime()) {
|
||||||
|
Log log = getLog();
|
||||||
|
String msg = "Invalid time received: " + new Date(realTime);
|
||||||
|
if (log.shouldWarn())
|
||||||
|
log.warn(msg, new Exception());
|
||||||
|
else
|
||||||
|
log.logAlways(Log.WARN, msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
long diff = realTime - System.currentTimeMillis();
|
long diff = realTime - System.currentTimeMillis();
|
||||||
setOffset(diff);
|
setOffset(diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Warning - overridden in RouterClock
|
||||||
|
*
|
||||||
* @param stratum ignored
|
* @param stratum ignored
|
||||||
* @since 0.7.12
|
* @since 0.7.12
|
||||||
*/
|
*/
|
||||||
public void setNow(long realTime, int stratum) {
|
public void setNow(long realTime, int stratum) {
|
||||||
long diff = realTime - System.currentTimeMillis();
|
setNow(realTime);
|
||||||
setOffset(diff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
14
history.txt
14
history.txt
@ -1,3 +1,17 @@
|
|||||||
|
2016-02-19 zzz
|
||||||
|
* Clock: Add sanity checks to detect invalid system clock
|
||||||
|
|
||||||
|
2016-02-18 zzz
|
||||||
|
* Console: Clean up display and form handling
|
||||||
|
for specifying a fixed host name or IP on /confignet
|
||||||
|
* Crypto: Add utilities for loading CRLs from disk;
|
||||||
|
check for revocation when reading in certificates
|
||||||
|
* Transport:
|
||||||
|
- Implement mayDisconnect() for outbound connections also,
|
||||||
|
use when publishing RI directly to floodfill
|
||||||
|
- Run SSU idle disconnect check faster if floodfill or near connection limit
|
||||||
|
* NetDB: Fix bug publishing router info too often
|
||||||
|
|
||||||
2016-02-17 zzz
|
2016-02-17 zzz
|
||||||
* i2psnark: Increase max files per torrent to 2000
|
* i2psnark: Increase max files per torrent to 2000
|
||||||
* I2PTunnel: Improve layout of blacklist radio buttons
|
* I2PTunnel: Improve layout of blacklist radio buttons
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package net.i2p.router;
|
package net.i2p.router;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.router.time.RouterTimestamper;
|
import net.i2p.router.time.RouterTimestamper;
|
||||||
|
import net.i2p.time.BuildTime;
|
||||||
import net.i2p.time.Timestamper;
|
import net.i2p.time.Timestamper;
|
||||||
import net.i2p.util.Clock;
|
import net.i2p.util.Clock;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
@ -108,7 +110,7 @@ public class RouterClock extends Clock {
|
|||||||
private synchronized void setOffset(long offsetMs, boolean force, int stratum) {
|
private synchronized void setOffset(long offsetMs, boolean force, int stratum) {
|
||||||
long delta = offsetMs - _offset;
|
long delta = offsetMs - _offset;
|
||||||
if (!force) {
|
if (!force) {
|
||||||
if ((offsetMs > MAX_OFFSET) || (offsetMs < 0 - MAX_OFFSET)) {
|
if (!_isSystemClockBad && (offsetMs > MAX_OFFSET || offsetMs < 0 - MAX_OFFSET)) {
|
||||||
Log log = getLog();
|
Log log = getLog();
|
||||||
if (log.shouldLog(Log.WARN))
|
if (log.shouldLog(Log.WARN))
|
||||||
log.warn("Maximum offset shift exceeded [" + offsetMs + "], NOT HONORING IT");
|
log.warn("Maximum offset shift exceeded [" + offsetMs + "], NOT HONORING IT");
|
||||||
@ -218,6 +220,15 @@ public class RouterClock extends Clock {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setNow(long realTime, int stratum) {
|
public void setNow(long realTime, int stratum) {
|
||||||
|
if (realTime < BuildTime.getEarliestTime() || realTime > BuildTime.getLatestTime()) {
|
||||||
|
Log log = getLog();
|
||||||
|
String msg = "Invalid time received: " + new Date(realTime);
|
||||||
|
if (log.shouldWarn())
|
||||||
|
log.warn(msg, new Exception());
|
||||||
|
else
|
||||||
|
log.logAlways(Log.WARN, msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
long diff = realTime - System.currentTimeMillis();
|
long diff = realTime - System.currentTimeMillis();
|
||||||
setOffset(diff, stratum);
|
setOffset(diff, stratum);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 6;
|
public final static long BUILD = 7;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
Reference in New Issue
Block a user