forked from I2P_Developers/i2p.i2p
* 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
This commit is contained in:
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user