From 266a20d55e627ae6241a7c88cd2c5861bd6f1cea Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 14 Oct 2014 13:57:02 +0000 Subject: [PATCH] I2NP: Implement DatabaseLookupMessage search type field, to replace all-zeros hash, and ease implementation for separate LS and RI databases, as documented in i2np spec. --- history.txt | 3 + .../i2p/data/i2np/DatabaseLookupMessage.java | 76 +++++++++++++++++++ .../src/net/i2p/router/RouterVersion.java | 2 +- .../HandleDatabaseLookupMessageJob.java | 34 ++++++--- .../router/networkdb/kademlia/ExploreJob.java | 2 + .../kademlia/FloodfillVerifyStoreJob.java | 1 + .../kademlia/IterativeSearchJob.java | 1 + .../networkdb/kademlia/SingleSearchJob.java | 4 + 8 files changed, 112 insertions(+), 11 deletions(-) diff --git a/history.txt b/history.txt index 568143b36..c88f78b35 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,6 @@ +2014-10-14 zzz + * I2NP: Implement DatabaseLookupMessage search type field + 2014-10-13 zzz * i2ptunnel: Set default sig type to ECDSA-P256 for client types Standard, IRC, and Socks IRC, if non-shared. diff --git a/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java b/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java index 790e37622..c1e27e3dc 100644 --- a/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java +++ b/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java @@ -40,6 +40,7 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { private List _dontIncludePeers; private SessionKey _replyKey; private SessionTag _replyTag; + private Type _type; //private static volatile long _currentLookupPeriod = 0; //private static volatile int _currentLookupCount = 0; @@ -52,7 +53,26 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { private static final int MAX_NUM_PEERS = 512; private static final byte FLAG_TUNNEL = 0x01; + // any flags below here will confuse routers 0.9.5 or lower private static final byte FLAG_ENCRYPT = 0x02; + private static final byte FLAG_TYPE_MASK = 0x0c; + private static final byte FLAG_TYPE_ANY = 0; + private static final byte FLAG_TYPE_LS = 0x04; + private static final byte FLAG_TYPE_RI = 0x08; + private static final byte FLAG_TYPE_EXPL = 0x0c; + + /** @since 0.9.16 */ + public enum Type { + /** default - LS or RI */ + ANY, + /** lease set only */ + LS, + /** router info only */ + RI, + /** exploratory - return closest non-floodfill router infos */ + EXPL + } + /** * It's not supported until 0.9.7, but as of @@ -86,6 +106,7 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { // + " messages so far)", new Exception("Flood cause")); // } //} + _type = Type.ANY; } /** @@ -130,6 +151,30 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { _key = key; } + /** + * Defines the type of data being searched for. + * Default ANY. + * + * @return non-null + * @since 0.9.16 + */ + public Type getSearchType() { return _type; } + + /** + * Defines the type of data being searched for. + * Default ANY. + * Must be ANY for queried routers 0.9.5 or lower, but there are few if + * any floodfills that old left, so not even worth checking. + * + * @param type non-null + * @since 0.9.16 + */ + public void setSearchType(Type type) { + if (type == null) + throw new IllegalArgumentException(); + _type = type; + } + /** * Contains the router who requested this lookup * @@ -285,6 +330,21 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { // TODO store the whole flag byte boolean tunnelSpecified = (data[curIndex] & FLAG_TUNNEL) != 0; boolean replyKeySpecified = (data[curIndex] & FLAG_ENCRYPT) != 0; + switch (data[curIndex] & FLAG_TYPE_MASK) { + case FLAG_TYPE_LS: + _type = Type.LS; + break; + case FLAG_TYPE_RI: + _type = Type.RI; + break; + case FLAG_TYPE_EXPL: + _type = Type.EXPL; + break; + case FLAG_TYPE_ANY: + default: + _type = Type.ANY; + break; + } curIndex++; if (tunnelSpecified) { @@ -348,6 +408,21 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { byte flag = FLAG_TUNNEL; if (_replyKey != null) flag |= FLAG_ENCRYPT; + switch (_type) { + case LS: + flag |= FLAG_TYPE_LS; + break; + case RI: + flag |= FLAG_TYPE_RI; + break; + case EXPL: + flag |= FLAG_TYPE_EXPL; + break; + case ANY: + default: + // flag is 0 + break; + } out[curIndex++] = flag; byte id[] = DataHelper.toLong(4, _replyTunnel.getTunnelId()); System.arraycopy(id, 0, out, curIndex, 4); @@ -410,6 +485,7 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { public String toString() { StringBuilder buf = new StringBuilder(256); buf.append("[DatabaseLookupMessage: "); + buf.append("\n\tSearch Type: ").append(_type); buf.append("\n\tSearch Key: ").append(_key); if (_replyKey != null) buf.append("\n\tReply GW: "); diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index f2522cd4e..7ca1a3ec2 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 9; + public final static long BUILD = 10; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java b/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java index 9931444aa..1b9dc0403 100644 --- a/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java +++ b/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java @@ -9,6 +9,7 @@ package net.i2p.router.networkdb; */ import java.util.Collections; +import java.util.HashSet; import java.util.Set; import net.i2p.data.DatabaseEntry; @@ -82,9 +83,11 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { return; } + DatabaseLookupMessage.Type lookupType = _message.getSearchType(); // only lookup once, then cast to correct type DatabaseEntry dbe = getContext().netDb().lookupLocally(_message.getSearchKey()); - if (dbe != null && dbe.getType() == DatabaseEntry.KEY_TYPE_LEASESET) { + if (dbe != null && dbe.getType() == DatabaseEntry.KEY_TYPE_LEASESET && + (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, // to avoid anonymity vulnerabilities. @@ -131,7 +134,7 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { if (_log.shouldLog(Log.INFO)) _log.info("We have local LS " + _message.getSearchKey() + ", NOT answering query, out of our keyspace"); getContext().statManager().addRateData("netDb.lookupsMatchedLocalNotClosest", 1); - Set routerHashSet = getNearestRouters(); + Set routerHashSet = getNearestRouters(lookupType); sendClosest(_message.getSearchKey(), routerHashSet, fromKey, _message.getReplyTunnel()); } } else { @@ -144,10 +147,11 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { ", NOT answering query - local? " + isLocal + " shouldPublish? " + shouldPublishLocal + " RAP? " + ls.getReceivedAsPublished() + " RAR? " + ls.getReceivedAsReply()); getContext().statManager().addRateData("netDb.lookupsMatchedRemoteNotClosest", 1); - Set routerHashSet = getNearestRouters(); + Set routerHashSet = getNearestRouters(lookupType); sendClosest(_message.getSearchKey(), routerHashSet, fromKey, _message.getReplyTunnel()); } - } else if (dbe != null && dbe.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { + } else if (dbe != null && dbe.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO && + lookupType != DatabaseLookupMessage.Type.LS) { RouterInfo info = (RouterInfo) dbe; if (info.isCurrent(EXPIRE_DELAY)) { if ( (info.getIdentity().isHidden()) || (isUnreachable(info) && !publishUnreachable()) ) { @@ -172,7 +176,7 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { } } else { // expired locally - return closest peer hashes - Set routerHashSet = getNearestRouters(); + Set routerHashSet = getNearestRouters(lookupType); // ERR: see above // // Remove hidden nodes from set.. @@ -190,7 +194,7 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { } } else { // not found locally - return closest peer hashes - Set routerHashSet = getNearestRouters(); + Set routerHashSet = getNearestRouters(lookupType); if (_log.shouldLog(Log.DEBUG)) _log.debug("We do not have key " + _message.getSearchKey() + " locally. sending back " + routerHashSet.size() + " peers to " + fromKey); @@ -204,13 +208,23 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { * Will not include us. * Side effect - adds us to the message's dontInclude set. */ - private Set getNearestRouters() { + private Set getNearestRouters(DatabaseLookupMessage.Type lookupType) { + // convert the new EXPL type flag to the old-style FAKE_HASH + // to pass to findNearestRouters() Set dontInclude = _message.getDontIncludePeers(); Hash us = getContext().routerHash(); - if (dontInclude == null) - dontInclude = Collections.singleton(us); - else + if (dontInclude == null && lookupType == DatabaseLookupMessage.Type.EXPL) { + dontInclude = new HashSet(2); dontInclude.add(us); + dontInclude.add(Hash.FAKE_HASH); + } else if (dontInclude == null) { + dontInclude = Collections.singleton(us); + } else if (lookupType == DatabaseLookupMessage.Type.EXPL) { + dontInclude.add(us); + dontInclude.add(Hash.FAKE_HASH); + } else { + dontInclude.add(us); + } // Honor flag to exclude all floodfills //if (dontInclude.contains(Hash.FAKE_HASH)) { // This is handled in FloodfillPeerSelector diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/ExploreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/ExploreJob.java index aff802b77..c386d10b8 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/ExploreJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/ExploreJob.java @@ -99,6 +99,8 @@ class ExploreJob extends SearchJob { if (dontIncludePeers.add(Hash.FAKE_HASH)) available--; } + // supported as of 0.9.16. TODO remove fake hash above + msg.setSearchType(DatabaseLookupMessage.Type.EXPL); KBucketSet ks = _facade.getKBuckets(); Hash rkey = getContext().routingKeyGenerator().getRoutingKey(getState().getTarget()); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java index 085c0c921..891b7ecb0 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java @@ -215,6 +215,7 @@ class FloodfillVerifyStoreJob extends JobImpl { m.setReplyTunnel(replyTunnelInfo.getReceiveTunnelId(0)); m.setFrom(replyTunnelInfo.getPeer(0)); m.setSearchKey(_key); + m.setSearchType(_isRouterInfo ? DatabaseLookupMessage.Type.RI : DatabaseLookupMessage.Type.LS); return m; } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java index 490a45a21..a1329967c 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java @@ -307,6 +307,7 @@ class IterativeSearchJob extends FloodSearchJob { dlm.setMessageExpiration(getContext().clock().now() + SINGLE_SEARCH_MSG_TIME); dlm.setReplyTunnel(replyTunnel.getReceiveTunnelId(0)); dlm.setSearchKey(_key); + dlm.setSearchType(_isLease ? DatabaseLookupMessage.Type.LS : DatabaseLookupMessage.Type.RI); if (_log.shouldLog(Log.INFO)) { int tries; diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/SingleSearchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/SingleSearchJob.java index 1c7995089..47449313e 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/SingleSearchJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/SingleSearchJob.java @@ -19,6 +19,9 @@ class SingleSearchJob extends FloodOnlySearchJob { private static final int TIMEOUT = 8*1000; + /** + * @param key for Router Info ONLY + */ public SingleSearchJob(RouterContext ctx, Hash key, Hash to) { // warning, null FloodfillNetworkDatabaseFacade ... // define our own failed() and success() below so _facade isn't used. @@ -46,6 +49,7 @@ class SingleSearchJob extends FloodOnlySearchJob { dlm.setMessageExpiration(getContext().clock().now()+5*1000); dlm.setReplyTunnel(replyTunnel.getReceiveTunnelId(0)); dlm.setSearchKey(_key); + dlm.setSearchType(DatabaseLookupMessage.Type.RI); if (_log.shouldLog(Log.INFO)) _log.info(getJobId() + ": Single search for " + _key + " to " + _to);