propagate from branch 'i2p.i2p' (head a89e68077114f79902a76649affe78e8ab709787)

to branch 'i2p.i2p.729' (head 11fde36281469d5ac8757bca1d7f93f679e0cb74)
This commit is contained in:
zab2
2013-06-02 12:41:02 +00:00
111 changed files with 801 additions and 739 deletions

View File

@@ -17,8 +17,12 @@ import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.RouterInfo;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.TunnelId;
//import net.i2p.util.Log;
import net.i2p.util.VersionComparator;
/**
* Defines the message a router sends to another router to search for a
@@ -34,6 +38,8 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl {
private TunnelId _replyTunnel;
/** this must be kept as a list to preserve the order and not break the checksum */
private List<Hash> _dontIncludePeers;
private SessionKey _replyKey;
private SessionTag _replyTag;
//private static volatile long _currentLookupPeriod = 0;
//private static volatile int _currentLookupCount = 0;
@@ -45,6 +51,16 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl {
Have to prevent a huge alloc on rcv of a malicious msg though */
private static final int MAX_NUM_PEERS = 512;
private static final byte FLAG_TUNNEL = 0x01;
private static final byte FLAG_ENCRYPT = 0x02;
/**
* 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);
}
@@ -144,6 +160,49 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl {
_replyTunnel = replyTunnel;
}
/**
* Does this router support encrypted replies?
*
* @param to null OK
* @since 0.9.7
*/
public static boolean supportsEncryptedReplies(RouterInfo to) {
if (to == null)
return false;
String v = to.getOption("router.version");
return v != null &&
VersionComparator.comp(v, MIN_ENCRYPTION_VERSION) >= 0;
}
/**
* The included session key or null if unset
*
* @since 0.9.7
*/
public SessionKey getReplyKey() { return _replyKey; }
/**
* The included session tag or null if unset
*
* @since 0.9.7
*/
public SessionTag getReplyTag() { return _replyTag; }
/**
* Only worthwhile if sending reply via tunnel
*
* @throws IllegalStateException if key or tag previously set, to protect saved checksum
* @param encryptKey non-null
* @param encryptTag non-null
* @since 0.9.7
*/
public void setReplySession(SessionKey encryptKey, SessionTag encryptTag) {
if (_replyKey != null || _replyTag != null)
throw new IllegalStateException();
_replyKey = encryptKey;
_replyTag = encryptTag;
}
/**
* Set of peers that a lookup reply should NOT include.
* WARNING - returns a copy.
@@ -224,7 +283,8 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl {
// as of 0.9.6, ignore other 7 bits of the flag byte
// TODO store the whole flag byte
boolean tunnelSpecified = (data[curIndex] & 0x01) != 0;
boolean tunnelSpecified = (data[curIndex] & FLAG_TUNNEL) != 0;
boolean replyKeySpecified = (data[curIndex] & FLAG_ENCRYPT) != 0;
curIndex++;
if (tunnelSpecified) {
@@ -246,6 +306,15 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl {
peers.add(p);
}
_dontIncludePeers = peers;
if (replyKeySpecified) {
byte[] rk = new byte[SessionKey.KEYSIZE_BYTES];
System.arraycopy(data, curIndex, rk, 0, SessionKey.KEYSIZE_BYTES);
_replyKey = new SessionKey(rk);
curIndex += SessionKey.KEYSIZE_BYTES;
byte[] rt = new byte[SessionTag.BYTE_LENGTH];
System.arraycopy(data, curIndex, rt, 0, SessionTag.BYTE_LENGTH);
_replyTag = new SessionTag(rt);
}
}
@@ -258,6 +327,8 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl {
totalLength += 2; // numPeers
if (_dontIncludePeers != null)
totalLength += Hash.HASH_LENGTH * _dontIncludePeers.size();
if (_replyKey != null)
totalLength += SessionKey.KEYSIZE_BYTES + SessionTag.BYTE_LENGTH;
return totalLength;
}
@@ -269,12 +340,17 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl {
curIndex += Hash.HASH_LENGTH;
System.arraycopy(_fromHash.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
// TODO allow specification of the other 7 bits of the flag byte
// Generate the flag byte
if (_replyTunnel != null) {
out[curIndex++] = 0x01;
byte flag = FLAG_TUNNEL;
if (_replyKey != null)
flag |= FLAG_ENCRYPT;
out[curIndex++] = flag;
byte id[] = DataHelper.toLong(4, _replyTunnel.getTunnelId());
System.arraycopy(id, 0, out, curIndex, 4);
curIndex += 4;
} else if (_replyKey != null) {
out[curIndex++] = FLAG_ENCRYPT;
} else {
out[curIndex++] = 0x00;
}
@@ -293,6 +369,12 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl {
curIndex += Hash.HASH_LENGTH;
}
}
if (_replyKey != null) {
System.arraycopy(_replyKey.getData(), 0, out, curIndex, SessionKey.KEYSIZE_BYTES);
curIndex += SessionKey.KEYSIZE_BYTES;
System.arraycopy(_replyTag.getData(), 0, out, curIndex, SessionTag.BYTE_LENGTH);
curIndex += SessionTag.BYTE_LENGTH;
}
return curIndex;
}
@@ -326,6 +408,10 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl {
buf.append("\n\tSearch Key: ").append(_key);
buf.append("\n\tFrom: ").append(_fromHash);
buf.append("\n\tReply Tunnel: ").append(_replyTunnel);
if (_replyKey != null)
buf.append("\n\tReply Key: ").append(_replyKey);
if (_replyTag != null)
buf.append("\n\tReply Tag: ").append(_replyTag);
buf.append("\n\tDont Include Peers: ");
if (_dontIncludePeers != null)
buf.append(_dontIncludePeers.size());

View File

@@ -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 = 0;
public final static long BUILD = 4;
/** for example "-test" */
public final static String EXTRA = "";

View File

@@ -8,6 +8,7 @@ package net.i2p.router.networkdb;
*
*/
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -16,6 +17,7 @@ import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
import net.i2p.data.RouterIdentity;
import net.i2p.data.RouterInfo;
import net.i2p.data.SessionKey;
import net.i2p.data.TunnelId;
import net.i2p.data.i2np.DatabaseLookupMessage;
import net.i2p.data.i2np.DatabaseSearchReplyMessage;
@@ -27,6 +29,7 @@ import net.i2p.router.JobImpl;
import net.i2p.router.OutNetMessage;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.networkdb.kademlia.MessageWrapper;
import net.i2p.router.message.SendMessageDirectJob;
import net.i2p.util.Log;
@@ -36,8 +39,10 @@ import net.i2p.util.Log;
* Unused directly - see kademlia/ for extension
*/
public class HandleDatabaseLookupMessageJob extends JobImpl {
private Log _log;
private DatabaseLookupMessage _message;
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
private final static int REPLY_TIMEOUT = 60*1000;
@@ -149,8 +154,7 @@ public class HandleDatabaseLookupMessageJob extends JobImpl {
if ( (info.getIdentity().isHidden()) || (isUnreachable(info) && !publishUnreachable()) ) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Not answering a query for a netDb peer who isn't reachable");
Set<Hash> us = new HashSet<Hash>(1);
us.add(getContext().routerHash());
Set<Hash> us = Collections.singleton(getContext().routerHash());
sendClosest(_message.getSearchKey(), us, fromKey, _message.getReplyTunnel());
//} else if (info.isHidden()) {
// // Don't return hidden nodes
@@ -203,9 +207,11 @@ public class HandleDatabaseLookupMessageJob extends JobImpl {
*/
private Set<Hash> getNearestRouters() {
Set<Hash> dontInclude = _message.getDontIncludePeers();
Hash us = getContext().routerHash();
if (dontInclude == null)
dontInclude = new HashSet(1);
dontInclude.add(getContext().routerHash());
dontInclude = Collections.singleton(us);
else
dontInclude.add(us);
// Honor flag to exclude all floodfills
//if (dontInclude.contains(Hash.FAKE_HASH)) {
// This is handled in FloodfillPeerSelector
@@ -289,6 +295,21 @@ public class HandleDatabaseLookupMessageJob extends JobImpl {
getContext().tunnelDispatcher().dispatch(m);
} else {
// if we aren't the gateway, forward it on
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());
m.setMessage(message);
m.setMessageExpiration(message.getMessageExpiration());

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

@@ -111,6 +111,12 @@ class FloodfillVerifyStoreJob extends JobImpl {
_facade.verifyFinished(_key);
return;
}
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;
if (_isRouterInfo)
fromKey = null;

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;
@@ -24,6 +25,7 @@ import net.i2p.router.RouterContext;
import net.i2p.router.TunnelInfo;
import net.i2p.router.util.RandomIterator;
import net.i2p.util.Log;
import net.i2p.util.VersionComparator;
/**
* A traditional Kademlia search that continues to search
@@ -60,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 */
@@ -112,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
@@ -183,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);
@@ -233,6 +260,13 @@ class IterativeSearchJob extends FloodSearchJob {
// if we have the ff RI, garlic encrypt it
RouterInfo ri = getContext().netDb().lookupRouterInfoLocally(peer);
if (ri != null) {
// 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);
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": Encrypted DLM for " + _key + " to " + peer);

View File

@@ -17,14 +17,16 @@ import net.i2p.data.i2np.I2NPMessage;
import net.i2p.router.RouterContext;
import net.i2p.router.message.GarlicMessageBuilder;
import net.i2p.router.message.PayloadGarlicConfig;
import net.i2p.router.util.RemovableSingletonSet;
/**
* Method and class for garlic encrypting outbound netdb traffic,
* including management of the ElGamal/AES tags
* and sending keys and tags for others to encrypt inbound netdb traffic,
* including management of the ElGamal/AES tags.
*
* @since 0.7.10
*/
class MessageWrapper {
public class MessageWrapper {
//private static final Log _log = RouterContext.getGlobalContext().logManager().getLog(MessageWrapper.class);
@@ -142,4 +144,61 @@ class MessageWrapper {
key, sentKey, null);
return msg;
}
/**
* A single key and tag, for receiving a single message.
*
* @since 0.9.7
*/
public static class OneTimeSession {
public final SessionKey key;
public final SessionTag tag;
public OneTimeSession(SessionKey key, SessionTag tag) {
this.key = key; this.tag = tag;
}
}
/**
* Create a single key and tag, for receiving a single encrypted message,
* and register it with the router's session key manager, to expire in two minutes.
* The recipient can then send us an AES-encrypted message,
* avoiding ElGamal.
*
* @since 0.9.7
*/
public static OneTimeSession generateSession(RouterContext ctx) {
SessionKey key = ctx.keyGenerator().generateSessionKey();
SessionTag tag = new SessionTag(true);
Set<SessionTag> tags = new RemovableSingletonSet(tag);
ctx.sessionKeyManager().tagsReceived(key, tags, 2*60*1000);
return new OneTimeSession(key, tag);
}
/**
* Garlic wrap a message from nobody, destined for an unknown router,
* to hide the contents from the IBGW.
* Uses a supplied session key and session tag for AES encryption,
* avoiding ElGamal.
*
* @param encryptKey non-null
* @param encryptTag non-null
* @return null on encrypt failure
* @since 0.9.7
*/
public static GarlicMessage wrap(RouterContext ctx, I2NPMessage m, SessionKey encryptKey, SessionTag encryptTag) {
DeliveryInstructions instructions = new DeliveryInstructions();
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_LOCAL);
PayloadGarlicConfig payload = new PayloadGarlicConfig();
payload.setCertificate(Certificate.NULL_CERT);
payload.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE));
payload.setPayload(m);
payload.setDeliveryInstructions(instructions);
payload.setExpiration(m.getMessageExpiration());
GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, payload, null, null,
null, encryptKey, encryptTag);
return msg;
}
}

View File

@@ -489,7 +489,7 @@ class StoreJob extends JobImpl {
String v = ri.getOption("router.version");
if (v == null)
return false;
return (new VersionComparator()).compare(v, MIN_ENCRYPTION_VERSION) >= 0;
return VersionComparator.comp(v, MIN_ENCRYPTION_VERSION) >= 0;
}
/**

View File

@@ -202,7 +202,6 @@ abstract class BuildRequestor {
private static final boolean SEND_VARIABLE = true;
/** 5 (~2600 bytes) fits nicely in 3 tunnel messages */
private static final int SHORT_RECORDS = 5;
private static final VersionComparator _versionComparator = new VersionComparator();
private static final List<Integer> SHORT_ORDER = new ArrayList(SHORT_RECORDS);
static {
for (int i = 0; i < SHORT_RECORDS; i++)
@@ -217,7 +216,7 @@ abstract class BuildRequestor {
String v = ri.getOption("router.version");
if (v == null)
return false;
return _versionComparator.compare(v, MIN_VARIABLE_VERSION) >= 0;
return VersionComparator.comp(v, MIN_VARIABLE_VERSION) >= 0;
}
/**

View File

@@ -1,6 +1,5 @@
package net.i2p.router.tunnel.pool;
import java.util.HashSet;
import java.util.Set;
import net.i2p.crypto.SessionKeyManager;
@@ -19,6 +18,7 @@ import net.i2p.router.RouterContext;
import net.i2p.router.TunnelInfo;
import net.i2p.router.message.GarlicMessageBuilder;
import net.i2p.router.message.PayloadGarlicConfig;
import net.i2p.router.util.RemovableSingletonSet;
import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
import net.i2p.util.Log;
@@ -140,9 +140,7 @@ class TestJob extends JobImpl {
scheduleRetest();
return;
}
// can't be a singleton, the SKM modifies it
Set encryptTags = new HashSet(1);
encryptTags.add(encryptTag);
Set<SessionTag> encryptTags = new RemovableSingletonSet(encryptTag);
// Register the single tag with the appropriate SKM
if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) {
SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_pool.getSettings().getDestination());

View File

@@ -212,7 +212,7 @@ public abstract class TunnelPeerSelector {
if (caps != null)
peers.addAll(caps);
}
if (filterSlow(ctx, isInbound, isExploratory)) {
if (filterSlow(isInbound, isExploratory)) {
// NOTE: filterSlow always returns true
Log log = ctx.logManager().getLog(TunnelPeerSelector.class);
char excl[] = getExcludeCaps(ctx);
@@ -345,7 +345,6 @@ public abstract class TunnelPeerSelector {
/** 0.7.8 and earlier had major message corruption bugs */
private static final String MIN_VERSION = "0.7.9";
private static final VersionComparator _versionComparator = new VersionComparator();
private static boolean shouldExclude(RouterContext ctx, Log log, RouterInfo peer, char excl[]) {
String cap = peer.getCapabilities();
@@ -371,7 +370,7 @@ public abstract class TunnelPeerSelector {
// minimum version check
String v = peer.getOption("router.version");
if (v == null || _versionComparator.compare(v, MIN_VERSION) < 0)
if (v == null || VersionComparator.comp(v, MIN_VERSION) < 0)
return true;
// uptime is always spoofed to 90m, so just remove all this
@@ -435,34 +434,28 @@ public abstract class TunnelPeerSelector {
private static final String PROP_INBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE = "router.inboundExploratoryExcludeUnreachable";
private static final String PROP_INBOUND_CLIENT_EXCLUDE_UNREACHABLE = "router.inboundClientExcludeUnreachable";
private static final String DEFAULT_OUTBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE = "false";
private static final String DEFAULT_OUTBOUND_CLIENT_EXCLUDE_UNREACHABLE = "false";
private static final boolean DEFAULT_OUTBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE = false;
private static final boolean DEFAULT_OUTBOUND_CLIENT_EXCLUDE_UNREACHABLE = false;
// see comments at getExclude() above
private static final String DEFAULT_INBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE = "true";
private static final String DEFAULT_INBOUND_CLIENT_EXCLUDE_UNREACHABLE = "true";
private static final boolean DEFAULT_INBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE = true;
private static final boolean DEFAULT_INBOUND_CLIENT_EXCLUDE_UNREACHABLE = true;
/**
* do we want to skip peers who haven't been up for long?
* @return true for inbound, false for outbound, unless configured otherwise
*/
protected boolean filterUnreachable(boolean isInbound, boolean isExploratory) {
boolean def = false;
String val = null;
if (isExploratory)
if (isExploratory) {
if (isInbound)
val = ctx.getProperty(PROP_INBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE, DEFAULT_INBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE);
return ctx.getProperty(PROP_INBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE, DEFAULT_INBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE);
else
val = ctx.getProperty(PROP_OUTBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE, DEFAULT_OUTBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE);
else
return ctx.getProperty(PROP_OUTBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE, DEFAULT_OUTBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE);
} else {
if (isInbound)
val = ctx.getProperty(PROP_INBOUND_CLIENT_EXCLUDE_UNREACHABLE, DEFAULT_INBOUND_CLIENT_EXCLUDE_UNREACHABLE);
return ctx.getProperty(PROP_INBOUND_CLIENT_EXCLUDE_UNREACHABLE, DEFAULT_INBOUND_CLIENT_EXCLUDE_UNREACHABLE);
else
val = ctx.getProperty(PROP_OUTBOUND_CLIENT_EXCLUDE_UNREACHABLE, DEFAULT_OUTBOUND_CLIENT_EXCLUDE_UNREACHABLE);
boolean rv = (val != null ? Boolean.parseBoolean(val) : def);
//System.err.println("Filter unreachable? " + rv + " (inbound? " + isInbound + ", exploratory? " + isExploratory);
return rv;
return ctx.getProperty(PROP_OUTBOUND_CLIENT_EXCLUDE_UNREACHABLE, DEFAULT_OUTBOUND_CLIENT_EXCLUDE_UNREACHABLE);
}
}
@@ -475,56 +468,50 @@ public abstract class TunnelPeerSelector {
* do we want to skip peers that are slow?
* @return true unless configured otherwise
*/
protected boolean filterSlow(RouterContext ctx, boolean isInbound, boolean isExploratory) {
boolean def = true;
String val = null;
if (isExploratory)
protected boolean filterSlow(boolean isInbound, boolean isExploratory) {
if (isExploratory) {
if (isInbound)
val = ctx.getProperty(PROP_INBOUND_EXPLORATORY_EXCLUDE_SLOW);
return ctx.getProperty(PROP_INBOUND_EXPLORATORY_EXCLUDE_SLOW, true);
else
val = ctx.getProperty(PROP_OUTBOUND_EXPLORATORY_EXCLUDE_SLOW);
else
return ctx.getProperty(PROP_OUTBOUND_EXPLORATORY_EXCLUDE_SLOW, true);
} else {
if (isInbound)
val = ctx.getProperty(PROP_INBOUND_CLIENT_EXCLUDE_SLOW);
return ctx.getProperty(PROP_INBOUND_CLIENT_EXCLUDE_SLOW, true);
else
val = ctx.getProperty(PROP_OUTBOUND_CLIENT_EXCLUDE_SLOW);
boolean rv = (val != null ? Boolean.parseBoolean(val) : def);
//System.err.println("Filter unreachable? " + rv + " (inbound? " + isInbound + ", exploratory? " + isExploratory);
return rv;
return ctx.getProperty(PROP_OUTBOUND_CLIENT_EXCLUDE_SLOW, true);
}
}
/****
private static final String PROP_OUTBOUND_EXPLORATORY_EXCLUDE_UPTIME = "router.outboundExploratoryExcludeUptime";
private static final String PROP_OUTBOUND_CLIENT_EXCLUDE_UPTIME = "router.outboundClientExcludeUptime";
private static final String PROP_INBOUND_EXPLORATORY_EXCLUDE_UPTIME = "router.inboundExploratoryExcludeUptime";
private static final String PROP_INBOUND_CLIENT_EXCLUDE_UPTIME = "router.inboundClientExcludeUptime";
****/
/**
* do we want to skip peers who haven't been up for long?
* @return true unless configured otherwise
*/
protected boolean filterUptime(RouterContext ctx, boolean isInbound, boolean isExploratory) {
boolean def = true;
String val = null;
if (isExploratory)
/****
protected boolean filterUptime(boolean isInbound, boolean isExploratory) {
if (isExploratory) {
if (isInbound)
val = ctx.getProperty(PROP_INBOUND_EXPLORATORY_EXCLUDE_UPTIME);
return ctx.getProperty(PROP_INBOUND_EXPLORATORY_EXCLUDE_UPTIME, true);
else
val = ctx.getProperty(PROP_OUTBOUND_EXPLORATORY_EXCLUDE_UPTIME);
else
return ctx.getProperty(PROP_OUTBOUND_EXPLORATORY_EXCLUDE_UPTIME, true);
} else {
if (isInbound)
val = ctx.getProperty(PROP_INBOUND_CLIENT_EXCLUDE_UPTIME);
return ctx.getProperty(PROP_INBOUND_CLIENT_EXCLUDE_UPTIME, true);
else
val = ctx.getProperty(PROP_OUTBOUND_CLIENT_EXCLUDE_UPTIME);
boolean rv = (val != null ? Boolean.parseBoolean(val) : def);
//System.err.println("Filter unreachable? " + rv + " (inbound? " + isInbound + ", exploratory? " + isExploratory);
return rv;
return ctx.getProperty(PROP_OUTBOUND_CLIENT_EXCLUDE_UPTIME, true);
}
}
****/
/** see HashComparator */
protected void orderPeers(List rv, Hash hash) {
if (rv.size() > 1)
Collections.sort(rv, new HashComparator(hash));
}

View File

@@ -1114,8 +1114,7 @@ public class TunnelPool {
return null;
}
} else {
peers = new ArrayList(1);
peers.add(_context.routerHash());
peers = Collections.singletonList(_context.routerHash());
}
PooledTunnelCreatorConfig cfg = new PooledTunnelCreatorConfig(_context, peers.size(), settings.isInbound(), settings.getDestination());

View File

@@ -0,0 +1,78 @@
package net.i2p.router.util;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* Like Collections.singleton() but item is removable,
* clear() is supported, and the iterator supports remove().
* Item may not be null. add() and addAll() unsupported.
* Unsynchronized.
*
* @since 0.9.7
*/
public class RemovableSingletonSet<E> extends AbstractSet<E> {
private E _elem;
public RemovableSingletonSet(E element) {
if (element == null)
throw new NullPointerException();
_elem = element;
}
@Override
public void clear() {
_elem = null;
}
@Override
public boolean contains(Object o) {
return o != null && o.equals(_elem);
}
@Override
public boolean isEmpty() {
return _elem == null;
}
@Override
public boolean remove(Object o) {
boolean rv = o.equals(_elem);
if (rv)
_elem = null;
return rv;
}
public int size() {
return _elem != null ? 1 : 0;
}
public Iterator<E> iterator() {
return new RSSIterator();
}
private class RSSIterator implements Iterator<E> {
boolean done;
public boolean hasNext() {
return _elem != null && !done;
}
public E next() {
if (!hasNext())
throw new NoSuchElementException();
done = true;
return _elem;
}
public void remove() {
if (_elem == null || !done)
throw new IllegalStateException();
_elem = null;
}
}
}