From 94c96b09e99d4e91ef468e274695c3ba0c87bb69 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 17 Sep 2019 11:17:14 +0000 Subject: [PATCH] Router: Implement expiration for BlindData entries --- .../localServer/LocalHTTPServer.java | 9 +++-- core/java/src/net/i2p/client/I2PSession.java | 3 +- .../net/i2p/client/impl/I2PSessionImpl.java | 5 ++- core/java/src/net/i2p/data/BlindData.java | 32 +++++++++++++++-- .../i2p/data/i2cp/BlindingInfoMessage.java | 22 +++++++----- .../client/ClientMessageEventListener.java | 12 +++++-- .../net/i2p/router/client/LookupDestJob.java | 5 +++ .../router/networkdb/kademlia/BlindCache.java | 35 ++++++++++++++++--- 8 files changed, 99 insertions(+), 24 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java index a123701a6..669fe837a 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java @@ -63,7 +63,7 @@ public abstract class LocalHTTPServer { "Add to addressbook failed - bad parameters"; private final static String ERR_B32 = - "HTTP/1.1 409 Bad\r\n"+ + "HTTP/1.1 400 Bad\r\n"+ "Content-Type: text/plain\r\n"+ "Connection: close\r\n"+ "Proxy-Connection: close\r\n"+ @@ -271,8 +271,13 @@ public abstract class LocalHTTPServer { SigningPublicKey spk = bd.getUnblindedPubKey(); SigType bt = bd.getBlindedSigType(); bd = new BlindData(context, spk, bt, secret, authType, privateKey); + long now = context.clock().now(); + bd.setDate(now); + long exp = now + ((bd.getAuthRequired() || bd.getSecretRequired()) ? 365*24*60*60*1000L + : 90*24*68*60*1000L); + bd.setExpiration(exp); I2PSession sess = sockMgr.getSession(); - sess.sendBlindingInfo(bd, 365*60*60*1000); + sess.sendBlindingInfo(bd); writeRedirectPage(out, success, host, "FIXME", url); return; } catch (IllegalArgumentException iae) { diff --git a/core/java/src/net/i2p/client/I2PSession.java b/core/java/src/net/i2p/client/I2PSession.java index 0b0303daa..9a5826d98 100644 --- a/core/java/src/net/i2p/client/I2PSession.java +++ b/core/java/src/net/i2p/client/I2PSession.java @@ -439,10 +439,9 @@ public interface I2PSession { /** * - * @param expiration ms from now, 0 means forever * @since 0.9.43 */ - public void sendBlindingInfo(BlindData bd, int expiration) throws I2PSessionException; + public void sendBlindingInfo(BlindData bd) throws I2PSessionException; /** * Listen on specified protocol and port. diff --git a/core/java/src/net/i2p/client/impl/I2PSessionImpl.java b/core/java/src/net/i2p/client/impl/I2PSessionImpl.java index bb9168d80..ee4e060f8 100644 --- a/core/java/src/net/i2p/client/impl/I2PSessionImpl.java +++ b/core/java/src/net/i2p/client/impl/I2PSessionImpl.java @@ -1860,10 +1860,9 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2 /** * - * @param expiration ms from now, 0 means forever * @since 0.9.43 */ - public void sendBlindingInfo(BlindData bd, int expiration) throws I2PSessionException { + public void sendBlindingInfo(BlindData bd) throws I2PSessionException { if (!_routerSupportsBlindingInfo) throw new I2PSessionException("Router does not support BlindingInfo"); if (_log.shouldInfo()) @@ -1871,7 +1870,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2 SessionId id = _sessionId; if (id == null) id = DUMMY_SESSION; - sendMessage_unchecked(new BlindingInfoMessage(bd, id, expiration)); + sendMessage_unchecked(new BlindingInfoMessage(bd, id)); } protected void updateActivity() { diff --git a/core/java/src/net/i2p/data/BlindData.java b/core/java/src/net/i2p/data/BlindData.java index 7ac2332b1..ea17c6f8c 100644 --- a/core/java/src/net/i2p/data/BlindData.java +++ b/core/java/src/net/i2p/data/BlindData.java @@ -28,6 +28,7 @@ public class BlindData { private boolean _secretRequired; private boolean _authRequired; private long _date; + private long _expiration; private String _b32; /** @@ -257,6 +258,7 @@ public class BlindData { } /** + * Creation date. Absolute timestamp. * @since 0.9.41 */ public void setDate(long date) { @@ -264,6 +266,9 @@ public class BlindData { } /** + * Creation date. Absolute timestamp. + * Returns zero if not specified. + * * @return creation date or as overridden by setDate() * @since 0.9.41 */ @@ -271,14 +276,33 @@ public class BlindData { return _date; } + /** + * Expiration date. Absolute timestamp. + * @since 0.9.43 + */ + public void setExpiration(long date) { + _expiration = date; + } + + /** + * Expiration date. Absolute timestamp. + * Returns zero if not specified. + * + * @return expiration date or as overridden by setExpiration() + * @since 0.9.43 + */ + public long getExpiration() { + return _expiration; + } + @Override public synchronized String toString() { calculate(); StringBuilder buf = new StringBuilder(1024); buf.append("[BlindData: "); buf.append("\n\tSigningPublicKey: ").append(_clearSPK); - buf.append("\n\tAlpha : ").append(_alpha); - buf.append("\n\tAlpha valid for : ").append((new Date(_date)).toString()); + buf.append("\n\tAlpha : ").append(getAlpha()); + buf.append("\n\tAlpha valid for : ").append((new Date(_routingKeyGenMod)).toString()); buf.append("\n\tBlindedPublicKey: ").append(_blindSPK); buf.append("\n\tBlinded Hash : ").append(_blindHash); if (_secret != null) @@ -305,6 +329,10 @@ public class BlindData { buf.append("\n\t + secret : ").append(Blinding.encode(_clearSPK, true, _authRequired)); if (!(_authRequired || _secretRequired)) buf.append("\n\t + auth,secret : ").append(Blinding.encode(_clearSPK, true, true)); + if (_date > 0) + buf.append("\n\tCreated : ").append((new Date(_date)).toString()); + if (_expiration > 0) + buf.append("\n\tExpires : ").append((new Date(_expiration)).toString()); buf.append(']'); return buf.toString(); } diff --git a/core/java/src/net/i2p/data/i2cp/BlindingInfoMessage.java b/core/java/src/net/i2p/data/i2cp/BlindingInfoMessage.java index 2a11800aa..b400a6df3 100644 --- a/core/java/src/net/i2p/data/i2cp/BlindingInfoMessage.java +++ b/core/java/src/net/i2p/data/i2cp/BlindingInfoMessage.java @@ -60,9 +60,8 @@ public class BlindingInfoMessage extends I2CPMessageImpl { * * @param expiration ms from now or 0 for forever */ - public BlindingInfoMessage(BlindData bd, - SessionId id, int expiration) { - this(id, expiration, bd.getAuthType(), bd.getBlindedSigType(), bd.getAuthPrivKey(), bd.getSecret()); + public BlindingInfoMessage(BlindData bd, SessionId id) { + this(id, bd.getExpiration(), bd.getAuthType(), bd.getBlindedSigType(), bd.getAuthPrivKey(), bd.getSecret()); Destination dest = bd.getDestination(); if (dest != null) { _dest = dest; @@ -157,13 +156,13 @@ public class BlindingInfoMessage extends I2CPMessageImpl { _endpointType = TYPE_KEY; } - private BlindingInfoMessage(SessionId id, int expiration, int authType, SigType blindType, + private BlindingInfoMessage(SessionId id, long expiration, int authType, SigType blindType, PrivateKey privKey, String secret) { if (id == null || blindType == null) throw new IllegalArgumentException(); if (authType != BlindData.AUTH_NONE && authType != BlindData.AUTH_DH && authType != BlindData.AUTH_PSK) - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Bad auth type"); if (authType == BlindData.AUTH_NONE && privKey != null) throw new IllegalArgumentException("no key required"); if (authType != BlindData.AUTH_NONE && privKey == null) @@ -171,8 +170,9 @@ public class BlindingInfoMessage extends I2CPMessageImpl { _sessionId = id; _authType = authType; _blindType = blindType; - if (expiration > 0) - _expiration = expiration + I2PAppContext.getGlobalContext().clock().now(); + _expiration = expiration; + if (expiration > 0 && expiration < Integer.MAX_VALUE) + _expiration += I2PAppContext.getGlobalContext().clock().now(); _privkey = privKey; _secret = secret; } @@ -262,6 +262,10 @@ public class BlindingInfoMessage extends I2CPMessageImpl { _blindData = new BlindData(I2PAppContext.getGlobalContext(), _dest, _blindType, _secret, _authType, _privkey); else if (_endpointType == TYPE_KEY) _blindData = new BlindData(I2PAppContext.getGlobalContext(), _pubkey, _blindType, _secret, _authType, _privkey); + if (_blindData != null) { + _blindData.setDate(I2PAppContext.getGlobalContext().clock().now()); + _blindData.setExpiration(_expiration); + } // HASH and HOST not supported by router yet return _blindData; } @@ -278,7 +282,7 @@ public class BlindingInfoMessage extends I2CPMessageImpl { _blindType = SigType.getByCode(bt); if (_blindType == null) throw new I2CPMessageException("unsupported sig type " + bt); - _expiration = DataHelper.readLong(in, 4); + _expiration = DataHelper.readLong(in, 4) * 1000; if (_endpointType == TYPE_HASH) { _hash = Hash.create(in); } else if (_endpointType == TYPE_HOST) { @@ -338,7 +342,7 @@ public class BlindingInfoMessage extends I2CPMessageImpl { os.write(flags); os.write((byte) _endpointType); DataHelper.writeLong(os, 2, _blindType.getCode()); - DataHelper.writeLong(os, 4, _expiration); + DataHelper.writeLong(os, 4, _expiration / 1000); if (_endpointType == TYPE_HASH) { _hash.writeBytes(os); } else if (_endpointType == TYPE_HOST) { diff --git a/router/java/src/net/i2p/router/client/ClientMessageEventListener.java b/router/java/src/net/i2p/router/client/ClientMessageEventListener.java index d7ba34d5e..1ac851b33 100644 --- a/router/java/src/net/i2p/router/client/ClientMessageEventListener.java +++ b/router/java/src/net/i2p/router/client/ClientMessageEventListener.java @@ -839,8 +839,16 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi if (_log.shouldWarn()) _log.warn("Updated: " + bd); } else { - if (_log.shouldWarn()) - _log.warn("No change: " + obd); + long oexp = obd.getExpiration(); + long nexp = bd.getExpiration(); + if (nexp > oexp) { + obd.setExpiration(nexp); + if (_log.shouldWarn()) + _log.warn("Updated expiration: " + obd); + } else { + if (_log.shouldWarn()) + _log.warn("No change: " + obd); + } } } } diff --git a/router/java/src/net/i2p/router/client/LookupDestJob.java b/router/java/src/net/i2p/router/client/LookupDestJob.java index 5cac60efc..bb0c16495 100644 --- a/router/java/src/net/i2p/router/client/LookupDestJob.java +++ b/router/java/src/net/i2p/router/client/LookupDestJob.java @@ -105,6 +105,11 @@ class LookupDestJob extends JobImpl { bd = bd2; } } else { + long now = getContext().clock().now(); + bd.setDate(now); + long exp = now + ((bd.getAuthRequired() || bd.getSecretRequired()) ? 365*24*60*60*1000L + : 90*24*68*60*1000L); + bd.setExpiration(exp); getContext().netDb().setBlindData(bd); } h = bd.getBlindedHash(); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java b/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java index 97f494288..0952efeca 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java @@ -275,13 +275,17 @@ class BlindCache { /** * Load from file. * Format: - * sigtype,bsigtype,b64 pubkey,[b64 secret],[b64 dest] + * sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest] + * + * If timestamp is positive, it's a creation date; + * if negative, it's a negative expiration date. */ private synchronized void load() { File file = new File(_context.getConfigDir(), PERSIST_FILE); if (!file.exists()) return; Log log = _context.logManager().getLog(BlindCache.class); + long now = _context.clock().now(); int count = 0; BufferedReader br = null; try { @@ -292,7 +296,14 @@ class BlindCache { if (line.startsWith("#")) continue; try { - storeInCache(fromPersistentString(line)); + BlindData bd = fromPersistentString(line); + long exp = bd.getExpiration(); + if (exp > 0 && exp < now) { + if (log.shouldInfo()) + log.info("Skipping expired entry " + bd); + continue; + } + storeInCache(bd); count++; } catch (IllegalArgumentException iae) { if (log.shouldLog(Log.WARN)) @@ -341,6 +352,9 @@ class BlindCache { /** * Format: * sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest] + * + * If timestamp is positive, it's a creation date; + * if negative, it's a negative expiration date. */ private BlindData fromPersistentString(String line) throws DataFormatException { String[] ss = DataHelper.split(line, ",", 8); @@ -389,13 +403,21 @@ class BlindCache { } else { rv = new BlindData(_context, spk, st2, secret, auth, privkey); } - rv.setDate(time); + if (time >= 0) { + rv.setDate(time); + } else { + rv.setDate(_context.clock().now()); + rv.setExpiration(0 - time); + } return rv; } /** * Format: * sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest] + * + * If timestamp is positive, it's a creation date; + * if negative, it's a negative expiration date. */ private static String toPersistentString(BlindData bd) { StringBuilder buf = new StringBuilder(1024); @@ -403,7 +425,12 @@ class BlindCache { buf.append(spk.getType().getCode()).append(','); buf.append(bd.getBlindedSigType().getCode()).append(','); buf.append(bd.getAuthType()).append(','); - buf.append(bd.getDate()).append(','); + long time = bd.getExpiration(); + if (time > 0) + time = 0 - time; + else + time = bd.getDate(); + buf.append(time).append(','); buf.append(spk.toBase64()).append(','); String secret = bd.getSecret(); if (secret != null && secret.length() > 0)