- 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
This commit is contained in:
zzz
2013-05-31 16:51:58 +00:00
parent 4b9a7323ad
commit eef5661008
8 changed files with 68 additions and 30 deletions

View File

@@ -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
*/

View File

@@ -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<SessionTag> sessionTags, long expire) {

View File

@@ -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);

View File

@@ -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());

View File

@@ -28,8 +28,6 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad
public static final char CAPABILITY_FLOODFILL = 'f';
private final Map<Hash, FloodSearchJob> _activeFloodQueries;
private boolean _floodfillEnabled;
/** for testing, see isFloodfill() below */
private static String _alwaysQuery;
private final Set<Hash> _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;
}

View File

@@ -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<Hash> selectFloodfillParticipants(Hash key, int howMany, Set<Hash> 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<Hash> selectFloodfillParticipantsIncludingUs(Hash key, int howMany, Set<Hash> toIgnore, KBucketSet kbuckets) {
List<Hash> ffs = selectFloodfillParticipants(toIgnore, kbuckets);

View File

@@ -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;

View File

@@ -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<Hash> 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<Hash> 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);