From eef5661008a8e1c55304dd33ec3ecf7c71d3e0a3 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 31 May 2013 16:51:58 +0000 Subject: [PATCH] * NetDB: - Fix debug netDb.alwaysQuery setting so it works again, and always sends to that router first (move from FNDF to IterativeSearchJob) - Don't reuse DLM reply key/tag - FloodfillPeerSelector: Note unused kbucket param in javadocs - Debug logging for encrypted DSRM/DSM --- .../src/net/i2p/crypto/SessionKeyManager.java | 1 + .../crypto/TransientSessionKeyManager.java | 2 + .../i2p/data/i2np/DatabaseLookupMessage.java | 7 +++- .../HandleDatabaseLookupMessageJob.java | 21 ++++++---- .../FloodfillNetworkDatabaseFacade.java | 16 -------- .../kademlia/FloodfillPeerSelector.java | 11 +++++- .../kademlia/FloodfillVerifyStoreJob.java | 2 + .../kademlia/IterativeSearchJob.java | 38 ++++++++++++++++--- 8 files changed, 68 insertions(+), 30 deletions(-) diff --git a/core/java/src/net/i2p/crypto/SessionKeyManager.java b/core/java/src/net/i2p/crypto/SessionKeyManager.java index 53ad0c02c..1b2e4326f 100644 --- a/core/java/src/net/i2p/crypto/SessionKeyManager.java +++ b/core/java/src/net/i2p/crypto/SessionKeyManager.java @@ -159,6 +159,7 @@ public class SessionKeyManager { * Accept the given tags and associate them with the given key for decryption, * with specified expiration. * + * @param sessionTags modifiable; NOT copied * @param expire time from now * @since 0.9.7 */ diff --git a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java index dc9bdd67d..a9e3a2d0c 100644 --- a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java +++ b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java @@ -511,6 +511,8 @@ public class TransientSessionKeyManager extends SessionKeyManager { * Accept the given tags and associate them with the given key for decryption * * @param sessionTags modifiable; NOT copied + * @param expire time from now + * @since 0.9.7 */ @Override public void tagsReceived(SessionKey key, Set sessionTags, long expire) { diff --git a/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java b/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java index d8ac61363..1127ced4f 100644 --- a/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java +++ b/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java @@ -54,7 +54,12 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { private static final byte FLAG_TUNNEL = 0x01; private static final byte FLAG_ENCRYPT = 0x02; - private static final String MIN_ENCRYPTION_VERSION = "0.9.8"; + /** + * It's not supported until 0.9.7, but as of + * 0.9.6 we can specify the bit in the flags without + * the receiver rejecting the whole message as invalid. + */ + private static final String MIN_ENCRYPTION_VERSION = "0.9.7"; public DatabaseLookupMessage(I2PAppContext context) { this(context, false); diff --git a/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java b/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java index 22bdb8889..ed4b32e27 100644 --- a/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java +++ b/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java @@ -41,6 +41,7 @@ import net.i2p.util.Log; public class HandleDatabaseLookupMessageJob extends JobImpl { private final Log _log; private final DatabaseLookupMessage _message; + private boolean _replyKeyConsumed; private final static int MAX_ROUTERS_RETURNED = 3; private final static int CLOSENESS_THRESHOLD = 8; // FNDF.MAX_TO_FLOOD + 1 @@ -294,13 +295,19 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { getContext().tunnelDispatcher().dispatch(m); } else { // if we aren't the gateway, forward it on - SessionKey replyKey = _message.getReplyKey(); - if (replyKey != null) { - // encrypt the reply - message = MessageWrapper.wrap(getContext(), message, replyKey, _message.getReplyTag()); - if (message == null) { - _log.error("Encryption error"); - return; + if (!_replyKeyConsumed) { + // if we send a followup DSM w/ our RI, don't reuse key + SessionKey replyKey = _message.getReplyKey(); + if (replyKey != null) { + // encrypt the reply + if (_log.shouldLog(Log.INFO)) + _log.info("Sending encrypted reply to " + toPeer + ' ' + replyKey + ' ' + _message.getReplyTag()); + message = MessageWrapper.wrap(getContext(), message, replyKey, _message.getReplyTag()); + if (message == null) { + _log.error("Encryption error"); + return; + } + _replyKeyConsumed = true; } } TunnelGatewayMessage m = new TunnelGatewayMessage(getContext()); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java index 8496e9a7f..26e8d7b4d 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java @@ -28,8 +28,6 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad public static final char CAPABILITY_FLOODFILL = 'f'; private final Map _activeFloodQueries; private boolean _floodfillEnabled; - /** for testing, see isFloodfill() below */ - private static String _alwaysQuery; private final Set _verifiesInProgress; private FloodThrottler _floodThrottler; private LookupThrottler _lookupThrottler; @@ -49,7 +47,6 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad super(context); _activeFloodQueries = new HashMap(); _verifiesInProgress = new ConcurrentHashSet(8); - _alwaysQuery = _context.getProperty("netDb.alwaysQuery"); _context.statManager().createRequiredRateStat("netDb.successTime", "Time for successful lookup (ms)", "NetworkDatabase", new long[] { 60*60*1000l, 24*60*60*1000l }); _context.statManager().createRateStat("netDb.failedTime", "How long a failed search takes", "NetworkDatabase", new long[] { 60*60*1000l, 24*60*60*1000l }); @@ -273,19 +270,6 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad */ public static boolean isFloodfill(RouterInfo peer) { if (peer == null) return false; - // For testing or local networks... we will - // pretend that the specified router is floodfill. - // Must be set at startup since it's static. - // In that router, set netDb.floodfillOnly=false. - // Warning - experts only! - if (_alwaysQuery != null) { - Hash aq = new Hash(); - try { - aq.fromBase64(_alwaysQuery); - if (aq.equals(peer.getIdentity().getHash())) - return true; - } catch (DataFormatException dfe) {} - } String caps = peer.getCapabilities(); return caps.indexOf(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL) >= 0; } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java index 23a34c32f..7567e79b3 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java @@ -37,7 +37,10 @@ import net.i2p.util.Log; * */ class FloodfillPeerSelector extends PeerSelector { - public FloodfillPeerSelector(RouterContext ctx) { super(ctx); } + + public FloodfillPeerSelector(RouterContext ctx) { + super(ctx); + } /** * Pick out peers with the floodfill capacity set, returning them first, but then @@ -96,6 +99,7 @@ class FloodfillPeerSelector extends PeerSelector { } /** + * @param kbuckets now unused * @return all floodfills not banlisted forever. * List will not include our own hash. * List is not sorted and not shuffled. @@ -106,6 +110,7 @@ class FloodfillPeerSelector extends PeerSelector { } /** + * @param kbuckets now unused * @param toIgnore can be null * @return all floodfills not banlisted forever. * List MAY INCLUDE our own hash. @@ -140,6 +145,8 @@ class FloodfillPeerSelector extends PeerSelector { * @return floodfills closest to the key that are not banlisted forever * @param key the ROUTING key (NOT the original key) * @param maxNumRouters max to return + * @param kbuckets now unused + * * Sorted by closest to the key if > maxNumRouters, otherwise not * The list is in 3 groups - sorted by routing key within each group. * Group 1: No store or lookup failure in a long time, and @@ -166,6 +173,7 @@ class FloodfillPeerSelector extends PeerSelector { * List will not include our own hash * @param key the ROUTING key (NOT the original key) * @param toIgnore can be null + * @param kbuckets now unused */ List selectFloodfillParticipants(Hash key, int howMany, Set toIgnore, KBucketSet kbuckets) { if (toIgnore == null) { @@ -183,6 +191,7 @@ class FloodfillPeerSelector extends PeerSelector { * List MAY CONTAIN our own hash unless included in toIgnore * @param key the ROUTING key (NOT the original key) * @param toIgnore can be null + * @param kbuckets now unused */ private List selectFloodfillParticipantsIncludingUs(Hash key, int howMany, Set toIgnore, KBucketSet kbuckets) { List ffs = selectFloodfillParticipants(toIgnore, kbuckets); 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 79e1d13d0..bb1dfd06c 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java @@ -113,6 +113,8 @@ class FloodfillVerifyStoreJob extends JobImpl { } if (DatabaseLookupMessage.supportsEncryptedReplies(peer)) { MessageWrapper.OneTimeSession sess = MessageWrapper.generateSession(getContext()); + if (_log.shouldLog(Log.INFO)) + _log.info("Requesting encrypted reply from " + _target + ' ' + sess.key + ' ' + sess.tag); lookup.setReplySession(sess.key, sess.tag); } Hash fromKey; 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 c2d7bfb5c..51040b259 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java @@ -10,6 +10,7 @@ import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; +import net.i2p.data.Base64; import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.data.RouterInfo; @@ -61,6 +62,8 @@ class IterativeSearchJob extends FloodSearchJob { private final Hash _rkey; /** this is a marker to register with the MessageRegistry, it is never sent */ private OutNetMessage _out; + /** testing */ + private static Hash _alwaysQueryHash; private static final int MAX_NON_FF = 3; /** Max number of peers to query */ @@ -113,6 +116,19 @@ class IterativeSearchJob extends FloodSearchJob { floodfillPeers = new ArrayList(TOTAL_SEARCH_LIMIT); } + // For testing or local networks... we will + // pretend that the specified router is floodfill, and always closest-to-the-key. + // May be set after startup but can't be changed or unset later. + // Warning - experts only! + String alwaysQuery = getContext().getProperty("netDb.alwaysQuery"); + if (alwaysQuery != null) { + if (_alwaysQueryHash == null) { + byte[] b = Base64.decode(alwaysQuery); + if (b != null && b.length == Hash.HASH_LENGTH) + _alwaysQueryHash = Hash.create(b); + } + } + if (floodfillPeers.isEmpty()) { // ask anybody, they may not return the answer but they will return a few ff peers we can go look up, // so this situation should be temporary @@ -184,11 +200,21 @@ class IterativeSearchJob extends FloodSearchJob { // coming via newPeerToTry() if (done + pend >= TOTAL_SEARCH_LIMIT) return; - if (_toTry.isEmpty()) - return; - Iterator iter = _toTry.iterator(); - peer = iter.next(); - iter.remove(); + if (_alwaysQueryHash != null && + !_unheardFrom.contains(_alwaysQueryHash) && + !_failedPeers.contains(_alwaysQueryHash)) { + // For testing or local networks... we will + // pretend that the specified router is floodfill, and always closest-to-the-key. + // May be set after startup but can't be changed or unset later. + // Warning - experts only! + peer = _alwaysQueryHash; + } else { + if (_toTry.isEmpty()) + return; + Iterator iter = _toTry.iterator(); + peer = iter.next(); + iter.remove(); + } _unheardFrom.add(peer); } sendQuery(peer); @@ -237,6 +263,8 @@ class IterativeSearchJob extends FloodSearchJob { // request encrypted reply if (DatabaseLookupMessage.supportsEncryptedReplies(ri)) { MessageWrapper.OneTimeSession sess = MessageWrapper.generateSession(getContext()); + if (_log.shouldLog(Log.INFO)) + _log.info("Requesting encrypted reply from " + peer + ' ' + sess.key + ' ' + sess.tag); dlm.setReplySession(sess.key, sess.tag); } outMsg = MessageWrapper.wrap(getContext(), dlm, ri);