From d1dd9ab517580f2227f3e99103e9fff4bab9a78b Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Tue, 3 Nov 2020 16:39:35 +0000 Subject: [PATCH] i2ptunnel: Add checks for offline expiration in alternate destination Improve logging for expiration checks --- .../net/i2p/i2ptunnel/I2PTunnelServer.java | 22 +++- .../net/i2p/i2ptunnel/TunnelController.java | 108 ++++++++++++------ 2 files changed, 95 insertions(+), 35 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java index 57f7c96a9f..5f5794790a 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java @@ -281,7 +281,27 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { FileInputStream privData = null; try { privData = new FileInputStream(altFile); - return sMgr.addSubsession(privData, props); + I2PSession rv = sMgr.addSubsession(privData, props); + if (rv.isOffline()) { + long exp = rv.getOfflineExpiration(); + long remaining = exp - getTunnel().getContext().clock().now(); + // if expires before the LS expires... + if (remaining <= 10*60*1000) { + String msg; + if (remaining > 0) + msg = "Offline signature for tunnel alternate destination expires " + DataHelper.formatTime(exp); + else + msg = "Offline signature for tunnel alternate destination expired " + DataHelper.formatTime(exp); + _log.log(Log.CRIT, msg); + throw new IllegalArgumentException(msg); + } + if (remaining < 60*24*60*60*1000L) { + String msg = "Offline signature for tunnel alternate destination expires in " + DataHelper.formatDuration(remaining); + _log.logAlways(Log.WARN, msg); + l.log("WARNING: " + msg); + } + } + return rv; } catch (IOException ioe) { _log.error("Failed to add subssession", ioe); return null; diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 1e776aa0f6..dafc5ba845 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -488,13 +488,11 @@ public class TunnelController implements Logging { } acquire(); changeState(TunnelState.RUNNING); - if ((!isClient() || getPersistentClientKey()) && getIsOfflineKeys()) { + if ((!isClient() || getPersistentClientKey()) && getIsOfflineKeysAnySession()) { File f = getPrivateKeyFile(); - long time = f.lastModified(); - if (time > 0) { - _pkfc = new PKFChecker(f, time); - _pkfc.schedule(5*60*1000L); - } + File f2 = getAlternatePrivateKeyFile(); + _pkfc = new PKFChecker(f, f2); + _pkfc.schedule(5*60*1000L); } } @@ -1185,7 +1183,7 @@ public class TunnelController implements Logging { /** * Returns false if not running. - * @return true if offline keys or not running + * @return true if the primary session has offline keys * @since 0.9.40 */ public boolean getIsOfflineKeys() { @@ -1195,6 +1193,24 @@ public class TunnelController implements Logging { return false; } + /** + * Returns false if not running. + * @return true if ANY session or subsession has offline keys + * @since 0.9.48 + */ + private boolean getIsOfflineKeysAnySession() { + List<I2PSession> sessions = _tunnel.getSessions(); + for (I2PSession sess : sessions) { + if (sess.isOffline()) + return true; + for (I2PSession sub : sess.getSubsessions()) { + if (sub.isOffline()) + return true; + } + } + return false; + } + // TODO synch public boolean getIsRunning() { return _state == TunnelState.RUNNING; } public boolean getIsStarting() { return _state == TunnelState.START_ON_LOAD || _state == TunnelState.STARTING; } @@ -1376,75 +1392,99 @@ public class TunnelController implements Logging { * @since 0.9.48 */ private class PKFChecker extends SimpleTimer2.TimedEvent { - private final File f; - private final long stamp; + private final List<File> files; + private final List<Long> stamps; private boolean wasRun; - /** caller must schedule */ - public PKFChecker(File f, long stamp) { + /** + * caller must schedule + * @param f2 may be null + */ + public PKFChecker(File f, File f2) { super(SimpleTimer2.getInstance()); - this.f = f; - this.stamp = stamp; + files = new ArrayList<File>(2); + stamps = new ArrayList<Long>(2); + files.add(f); + stamps.add(Long.valueOf(f.lastModified())); + if (f2 != null) { + files.add(f2); + stamps.add(Long.valueOf(f2.lastModified())); + } } public void timeReached() { if (!getIsRunning() && !getIsStarting()) return; List<I2PSession> sessions = _tunnel.getSessions(); - if (!sessions.isEmpty()) { - I2PSession sess = sessions.get(0); - long delay; - if (sess.isOffline()) { + if (getIsOfflineKeysAnySession()) { + I2PAppContext ctx = _tunnel.getContext(); + long now = ctx.clock().now(); + long delay = 2*24*60*60*1000L; + for (int i = 0; i < files.size(); i++) { + File f = files.get(i); + long stamp = stamps.get(i).longValue(); + if (_log.shouldDebug()) + _log.debug("PKFC checking: " + f + " stamp: " + DataHelper.formatTime(stamp)); + if (stamp <= 0) + continue; if (f.lastModified() > stamp) { - String msg = "Private key file with offline signature updated, restarting tunnel"; + String msg = "Private key file " + f + " with offline signature updated, restarting tunnel"; _log.logAlways(Log.WARN, msg); _tunnel.log(msg); restartTunnel(); return; } + if (sessions.isEmpty()) + continue; + I2PSession sess = sessions.get(0); + if (i > 0) { + List<I2PSession> subs = sess.getSubsessions(); + if (subs.isEmpty()) + continue; + sess = subs.get(0); + } + if (!sess.isOffline()) + continue; long exp = sess.getOfflineExpiration(); - I2PAppContext ctx = _tunnel.getContext(); - long now = ctx.clock().now(); long remaining = exp - now; - if (remaining > 10*365*24*60*60*1000L) { - // don't bother - return; - } - if (remaining <= 10*60*1000) { + if (remaining <= 11*60*1000) { // can't sign another LS String msg; if (remaining > 0) - msg = "Offline signature for tunnel expires " + DataHelper.formatTime(exp); + msg = "Offline signature in private key file " + f + " for tunnel expires " + DataHelper.formatTime(exp) + ", stopping the tunnel!"; else - msg = "Offline signature for tunnel expired " + DataHelper.formatTime(exp); + msg = "Offline signature in private key file " + f + " for tunnel expired " + DataHelper.formatTime(exp) + ", stopping the tunnel!"; _log.log(Log.CRIT, msg); _tunnel.log(msg); stopTunnel(); return; } + long d; if (remaining < 24*60*60*1000L) { - delay = Math.min(60*60*1000L, remaining - (9*60*1000)); + d = Math.min(60*60*1000L, remaining - (11*60*1000)); } else if (remaining < 7*24*60*60*1000L) { - delay = 6*60*60*1000L; + d = 6*60*60*1000L; if (!wasRun) { - delay += ctx.random().nextLong(4 * delay); + d += ctx.random().nextLong(4 * delay); wasRun = true; } } else { - delay = 24*60*60*1000L; + d = 24*60*60*1000L; if (!wasRun) { delay += ctx.random().nextLong(delay); wasRun = true; } } if (remaining < 30*24*60*60*1000L) { - String msg = "Offline signature for tunnel expires in " + DataHelper.formatDuration(remaining); + String msg = "Offline signature in private key file " + f + " for tunnel expires in " + DataHelper.formatDuration(remaining); _log.logAlways(Log.WARN, msg); _tunnel.log("WARNING: " + msg); } - } else { - delay = 24*60*60*1000L; + if (d < delay) + delay = d; } + if (_log.shouldDebug()) + _log.debug("PKFC sleeping " + DataHelper.formatDuration(delay)); schedule(delay); } } -- GitLab