From 026ddb3278e35df078a03ae450a45bad56423a33 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 1 Dec 2018 13:13:51 +0000 Subject: [PATCH] Router: Add preliminary support for more LS2 types (proposal 123) I2CP: Don't require privkeys for meta in CLS2 message --- core/java/src/net/i2p/data/DatabaseEntry.java | 25 +++++++++- .../i2p/data/i2cp/CreateLeaseSet2Message.java | 48 ++++++++++++------- .../i2p/data/i2np/DatabaseStoreMessage.java | 26 +++++----- .../HandleDatabaseLookupMessageJob.java | 5 +- .../networkdb/kademlia/ExpireLeasesJob.java | 4 +- .../kademlia/FloodOnlyLookupMatchJob.java | 3 +- ...andleFloodfillDatabaseStoreMessageJob.java | 3 +- .../KademliaNetworkDatabaseFacade.java | 16 +++---- .../router/networkdb/kademlia/StoreJob.java | 9 ++-- .../kademlia/TransientDataStore.java | 7 +-- 10 files changed, 84 insertions(+), 62 deletions(-) diff --git a/core/java/src/net/i2p/data/DatabaseEntry.java b/core/java/src/net/i2p/data/DatabaseEntry.java index 2fee30869..7a3fadd97 100644 --- a/core/java/src/net/i2p/data/DatabaseEntry.java +++ b/core/java/src/net/i2p/data/DatabaseEntry.java @@ -103,11 +103,34 @@ public abstract class DatabaseEntry extends DataStructureImpl { * Get the type of the data structure. * This should be faster than instanceof. * - * @return KEY_TYPE_ROUTERINFO or KEY_TYPE_LEASESET + * @return KEY_TYPE_ROUTERINFO or KEY_TYPE_LEASESET or LS2 types * @since 0.8.2 */ public abstract int getType(); + /** + * Convenience method, is the type any variant of leaseset? + * + * @return true for any type of LeaseSet, false for RouterInfo, false for others + * @since 0.9.38 + */ + public boolean isLeaseSet() { + return isLeaseSet(getType()); + } + + /** + * Convenience method, is the type any variant of leaseset? + * + * @return true for any type of LeaseSet, false for RouterInfo, false for others + * @since 0.9.38 + */ + public static boolean isLeaseSet(int type) { + return type == KEY_TYPE_LEASESET || + type == KEY_TYPE_LS2 || + type == KEY_TYPE_ENCRYPTED_LS2 || + type == KEY_TYPE_META_LS2; + } + /** * Returns the raw payload data, excluding the signature, to be signed by sign(). * diff --git a/core/java/src/net/i2p/data/i2cp/CreateLeaseSet2Message.java b/core/java/src/net/i2p/data/i2cp/CreateLeaseSet2Message.java index fe54df66b..ea2ecca8b 100644 --- a/core/java/src/net/i2p/data/i2cp/CreateLeaseSet2Message.java +++ b/core/java/src/net/i2p/data/i2cp/CreateLeaseSet2Message.java @@ -9,8 +9,10 @@ import net.i2p.crypto.EncType; import net.i2p.crypto.SigType; import net.i2p.data.DatabaseEntry; import net.i2p.data.DataFormatException; +import net.i2p.data.EncryptedLeaseSet; import net.i2p.data.LeaseSet; import net.i2p.data.LeaseSet2; +import net.i2p.data.MetaLeaseSet; import net.i2p.data.PrivateKey; import net.i2p.data.SigningPrivateKey; @@ -24,6 +26,9 @@ import net.i2p.data.SigningPrivateKey; * serialized after the LeaseSet, not before, so we can * infer the types from the LeaseSet. * + * For Meta LS: + * SigningPrivateKey and PrivateKey are not present. + * * @since 0.9.38 */ public class CreateLeaseSet2Message extends CreateLeaseSetMessage { @@ -43,26 +48,32 @@ public class CreateLeaseSet2Message extends CreateLeaseSetMessage { _leaseSet = new LeaseSet(); } else if (type == DatabaseEntry.KEY_TYPE_LS2) { _leaseSet = new LeaseSet2(); + } else if (type == DatabaseEntry.KEY_TYPE_ENCRYPTED_LS2) { + _leaseSet = new EncryptedLeaseSet(); + } else if (type == DatabaseEntry.KEY_TYPE_META_LS2) { + _leaseSet = new MetaLeaseSet(); } else if (type == -1) { throw new EOFException("EOF reading LS type"); } else { throw new I2CPMessageException("Unsupported Leaseset type: " + type); } _leaseSet.readBytes(in); - // In CLSM this is the type of the dest, but revocation is unimplemented. - // In CLS2M this is the type of the signature (which may be different than the - // type of the dest if it's an offline signature) - // and is needed by the session tag manager. - SigType stype = _leaseSet.getSignature().getType(); - if (stype == null) - throw new I2CPMessageException("Unsupported sig type"); - _signingPrivateKey = new SigningPrivateKey(stype); - _signingPrivateKey.readBytes(in); - EncType etype = _leaseSet.getEncryptionKey().getType(); - if (etype == null) - throw new I2CPMessageException("Unsupported encryption type"); - _privateKey = new PrivateKey(etype); - _privateKey.readBytes(in); + if (type != DatabaseEntry.KEY_TYPE_META_LS2) { + // In CLSM this is the type of the dest, but revocation is unimplemented. + // In CLS2M this is the type of the signature (which may be different than the + // type of the dest if it's an offline signature) + // and is needed by the session tag manager. + SigType stype = _leaseSet.getSignature().getType(); + if (stype == null) + throw new I2CPMessageException("Unsupported sig type"); + _signingPrivateKey = new SigningPrivateKey(stype); + _signingPrivateKey.readBytes(in); + EncType etype = _leaseSet.getEncryptionKey().getType(); + if (etype == null) + throw new I2CPMessageException("Unsupported encryption type"); + _privateKey = new PrivateKey(etype); + _privateKey.readBytes(in); + } } catch (DataFormatException dfe) { throw new I2CPMessageException("Error reading the CreateLeaseSetMessage", dfe); } @@ -70,7 +81,8 @@ public class CreateLeaseSet2Message extends CreateLeaseSetMessage { @Override protected byte[] doWriteMessage() throws I2CPMessageException, IOException { - if (_sessionId == null || _signingPrivateKey == null || _privateKey == null || _leaseSet == null) + if (_sessionId == null || _leaseSet == null || + (_leaseSet.getType() != DatabaseEntry.KEY_TYPE_META_LS2 && (_signingPrivateKey == null || _privateKey == null))) throw new I2CPMessageException("Unable to write out the message as there is not enough data"); int size = 4 // sessionId + 1 // type @@ -82,8 +94,10 @@ public class CreateLeaseSet2Message extends CreateLeaseSetMessage { _sessionId.writeBytes(os); os.write(_leaseSet.getType()); _leaseSet.writeBytes(os); - _signingPrivateKey.writeBytes(os); - _privateKey.writeBytes(os); + if (_leaseSet.getType() != DatabaseEntry.KEY_TYPE_META_LS2) { + _signingPrivateKey.writeBytes(os); + _privateKey.writeBytes(os); + } } catch (DataFormatException dfe) { throw new I2CPMessageException("Error writing out the message data", dfe); } diff --git a/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java b/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java index 1862b0914..54eab321d 100644 --- a/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java +++ b/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java @@ -16,9 +16,11 @@ import net.i2p.I2PAppContext; import net.i2p.data.DatabaseEntry; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; +import net.i2p.data.EncryptedLeaseSet; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.LeaseSet2; +import net.i2p.data.MetaLeaseSet; import net.i2p.data.router.RouterInfo; import net.i2p.data.TunnelId; @@ -134,17 +136,15 @@ public class DatabaseStoreMessage extends FastI2NPMessageImpl { _replyGateway = null; } - if (dbType == DatabaseEntry.KEY_TYPE_LEASESET) { - _dbEntry = new LeaseSet(); - try { - _dbEntry.readBytes(new ByteArrayInputStream(data, curIndex, data.length-curIndex)); - } catch (DataFormatException dfe) { - throw new I2NPMessageException("Error reading the leaseSet", dfe); - } catch (IOException ioe) { - throw new I2NPMessageException("Error reading the leaseSet", ioe); - } - } else if (dbType == DatabaseEntry.KEY_TYPE_LS2) { - _dbEntry = new LeaseSet2(); + if (DatabaseEntry.isLeaseSet(dbType)) { + if (dbType == DatabaseEntry.KEY_TYPE_LEASESET) + _dbEntry = new LeaseSet(); + else if (dbType == DatabaseEntry.KEY_TYPE_LS2) + _dbEntry = new LeaseSet2(); + else if (dbType == DatabaseEntry.KEY_TYPE_ENCRYPTED_LS2) + _dbEntry = new EncryptedLeaseSet(); + else + _dbEntry = new MetaLeaseSet(); try { _dbEntry.readBytes(new ByteArrayInputStream(data, curIndex, data.length-curIndex)); } catch (DataFormatException dfe) { @@ -196,7 +196,7 @@ public class DatabaseStoreMessage extends FastI2NPMessageImpl { if (_replyToken > 0) len += 4 + Hash.HASH_LENGTH; // replyTunnel+replyGateway int type = _dbEntry.getType(); - if (type == DatabaseEntry.KEY_TYPE_LEASESET) { + if (_dbEntry.isLeaseSet()) { if (_byteCache == null) { _byteCache = _dbEntry.toByteArray(); } @@ -218,7 +218,7 @@ public class DatabaseStoreMessage extends FastI2NPMessageImpl { protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException { if (_dbEntry == null) throw new I2NPMessageException("Missing entry"); int type = _dbEntry.getType(); - if (type != DatabaseEntry.KEY_TYPE_LEASESET && type != DatabaseEntry.KEY_TYPE_ROUTERINFO) + if (type != DatabaseEntry.KEY_TYPE_ROUTERINFO && !_dbEntry.isLeaseSet()) throw new I2NPMessageException("Invalid key type " + type); // Use the hash of the DatabaseEntry diff --git a/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java b/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java index cf4c3e8ca..7e1659170 100644 --- a/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java +++ b/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java @@ -95,7 +95,7 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { // only lookup once, then cast to correct type DatabaseEntry dbe = getContext().netDb().lookupLocally(_message.getSearchKey()); int type = dbe != null ? dbe.getType() : -1; - if ((type == DatabaseEntry.KEY_TYPE_LEASESET || type == DatabaseEntry.KEY_TYPE_LS2) && + if (DatabaseEntry.isLeaseSet(type) && (lookupType == DatabaseLookupMessage.Type.ANY || lookupType == DatabaseLookupMessage.Type.LS)) { LeaseSet ls = (LeaseSet) dbe; // We have to be very careful here to decide whether or not to send out the leaseSet, @@ -260,8 +260,7 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { _log.debug("Sending data matching key " + key + " to peer " + toPeer + " tunnel " + replyTunnel); DatabaseStoreMessage msg = new DatabaseStoreMessage(getContext()); - int type = data.getType(); - if (type == DatabaseEntry.KEY_TYPE_LEASESET || type == DatabaseEntry.KEY_TYPE_LS2) { + if (data.isLeaseSet()) { getContext().statManager().addRateData("netDb.lookupsMatchedLeaseSet", 1); } msg.setEntry(data); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/ExpireLeasesJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/ExpireLeasesJob.java index 8f9c29c4c..23d50e070 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/ExpireLeasesJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/ExpireLeasesJob.java @@ -61,9 +61,7 @@ class ExpireLeasesJob extends JobImpl { Set toExpire = new HashSet(128); for (Map.Entry entry : _facade.getDataStore().getMapEntries()) { DatabaseEntry obj = entry.getValue(); - int type = obj.getType(); - if (type == DatabaseEntry.KEY_TYPE_LEASESET || - type == DatabaseEntry.KEY_TYPE_LS2) { + if (obj.isLeaseSet()) { LeaseSet ls = (LeaseSet)obj; if (!ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) toExpire.add(entry.getKey()); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlyLookupMatchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlyLookupMatchJob.java index 76a356168..9edc1e65f 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlyLookupMatchJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlyLookupMatchJob.java @@ -54,8 +54,7 @@ class FloodOnlyLookupMatchJob extends JobImpl implements ReplyJob { // Should we just pass the DataStructure directly back to somebody? DatabaseEntry entry = dsm.getEntry(); int type = entry.getType(); - if (type == DatabaseEntry.KEY_TYPE_LEASESET || - type == DatabaseEntry.KEY_TYPE_LS2) { + if (DatabaseEntry.isLeaseSet(type)) { // Since HFDSMJ wants to setReceivedAsPublished(), we have to // set a flag saying this was really the result of a query, // so don't do that. diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java index de99a34a7..63adc3382 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java @@ -70,8 +70,7 @@ class HandleFloodfillDatabaseStoreMessageJob extends JobImpl { Hash key = _message.getKey(); DatabaseEntry entry = _message.getEntry(); int type = entry.getType(); - if (type == DatabaseEntry.KEY_TYPE_LEASESET || - type == DatabaseEntry.KEY_TYPE_LS2) { + if (DatabaseEntry.isLeaseSet(type)) { getContext().statManager().addRateData("netDb.storeLeaseSetHandled", 1); if (_log.shouldLog(Log.INFO)) _log.info("Handling dbStore of leaseset " + _message); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index b8a10935f..8d4d8cb62 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -438,8 +438,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad //return _ds.countLeaseSets(); int rv = 0; for (DatabaseEntry ds : _ds.getEntries()) { - int type = ds.getType(); - if ((type == DatabaseEntry.KEY_TYPE_LEASESET || type == DatabaseEntry.KEY_TYPE_LS2) && + if (ds.isLeaseSet() && ((LeaseSet)ds).getReceivedAsPublished()) rv++; } @@ -468,7 +467,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad if (rv == null) return null; int type = rv.getType(); - if (type == DatabaseEntry.KEY_TYPE_LEASESET || type == DatabaseEntry.KEY_TYPE_LS2) { + if (DatabaseEntry.isLeaseSet(type)) { LeaseSet ls = (LeaseSet)rv; if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) return rv; @@ -553,8 +552,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad if (!_initialized) return null; DatabaseEntry ds = _ds.get(key); if (ds != null) { - int type = ds.getType(); - if (type == DatabaseEntry.KEY_TYPE_LEASESET || type == DatabaseEntry.KEY_TYPE_LS2) { + if (ds.isLeaseSet()) { LeaseSet ls = (LeaseSet)ds; if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) { return ls; @@ -607,8 +605,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad if (!_initialized) return null; DatabaseEntry ds = _ds.get(key); if (ds != null) { - int type = ds.getType(); - if (type == DatabaseEntry.KEY_TYPE_LEASESET || type == DatabaseEntry.KEY_TYPE_LS2) { + if (ds.isLeaseSet()) { LeaseSet ls = (LeaseSet)ds; return ls.getDestination(); } @@ -1082,7 +1079,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad private void processStoreFailure(Hash h, DatabaseEntry entry) throws UnsupportedCryptoException { if (entry.getHash().equals(h)) { int etype = entry.getType(); - if (etype == DatabaseEntry.KEY_TYPE_LEASESET || etype == DatabaseEntry.KEY_TYPE_LS2) { + if (DatabaseEntry.isLeaseSet(etype)) { LeaseSet ls = (LeaseSet) entry; Destination d = ls.getDestination(); Certificate c = d.getCertificate(); @@ -1242,8 +1239,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad if (!_initialized) return null; Set leases = new HashSet(); for (DatabaseEntry o : getDataStore().getEntries()) { - int type = o.getType(); - if (type == DatabaseEntry.KEY_TYPE_LEASESET || type == DatabaseEntry.KEY_TYPE_LS2) + if (o.isLeaseSet()) leases.add((LeaseSet)o); } return leases; diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java index 1f98cb859..f901a3b54 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java @@ -78,8 +78,7 @@ abstract class StoreJob extends JobImpl { _timeoutMs = timeoutMs; _expiration = context.clock().now() + timeoutMs; _peerSelector = facade.getPeerSelector(); - int type = data.getType(); - if (type == DatabaseEntry.KEY_TYPE_LEASESET || type == DatabaseEntry.KEY_TYPE_LS2) { + if (data.isLeaseSet()) { _connectChecker = null; _connectMask = 0; } else { @@ -313,8 +312,7 @@ abstract class StoreJob extends JobImpl { if (type == DatabaseEntry.KEY_TYPE_ROUTERINFO) { if (responseTime > MAX_DIRECT_EXPIRATION) responseTime = MAX_DIRECT_EXPIRATION; - } else if (type == DatabaseEntry.KEY_TYPE_LEASESET || type == DatabaseEntry.KEY_TYPE_LS2) { - } else { + } else if (!DatabaseEntry.isLeaseSet(type)) { throw new IllegalArgumentException("Storing an unknown data type! " + _state.getData()); } msg.setEntry(_state.getData()); @@ -340,8 +338,7 @@ abstract class StoreJob extends JobImpl { * */ private void sendStore(DatabaseStoreMessage msg, RouterInfo peer, long expiration) { - int type = msg.getEntry().getType(); - if (type == DatabaseEntry.KEY_TYPE_LEASESET || type == DatabaseEntry.KEY_TYPE_LS2) { + if (msg.getEntry().isLeaseSet()) { getContext().statManager().addRateData("netDb.storeLeaseSetSent", 1); // if it is an encrypted leaseset... if (getContext().keyRing().get(msg.getKey()) != null) diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java index 7f483ca7f..5e4fe3988 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java @@ -99,9 +99,7 @@ class TransientDataStore implements DataStore { public int countLeaseSets() { int count = 0; for (DatabaseEntry d : _data.values()) { - int type = d.getType(); - if (type == DatabaseEntry.KEY_TYPE_LEASESET || - type == DatabaseEntry.KEY_TYPE_LS2) + if (d.isLeaseSet()) count++; } return count; @@ -151,8 +149,7 @@ class TransientDataStore implements DataStore { _log.info("New router for " + key + ": published on " + new Date(ri.getPublished())); rv = true; } - } else if (type == DatabaseEntry.KEY_TYPE_LEASESET || - type == DatabaseEntry.KEY_TYPE_LS2) { + } else if (DatabaseEntry.isLeaseSet(type)) { LeaseSet ls = (LeaseSet)data; if (old != null) { LeaseSet ols = (LeaseSet)old;