forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p.zzz.test' (head 128a31611abc6a88e58133f3bf6a577fe6dd5b1c)
to branch 'i2p.i2p.zzz.test4' (head fa9a871892517271eb2531b433fe80a2a713be9c)
This commit is contained in:
@@ -9,7 +9,9 @@ import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
|
||||
/**
|
||||
* Hold the tunnel request record, managing its encryption and decryption.
|
||||
* Hold the tunnel request record, managing its ElGamal encryption and decryption.
|
||||
* Iterative AES encryption/decryption is done elsewhere.
|
||||
*
|
||||
* Cleartext:
|
||||
* <pre>
|
||||
* bytes 0-3: tunnel ID to receive messages as
|
||||
@@ -23,7 +25,13 @@ import net.i2p.data.SessionKey;
|
||||
* byte 184: flags
|
||||
* bytes 185-188: request time (in hours since the epoch)
|
||||
* bytes 189-192: next message ID
|
||||
* bytes 193-222: uninterpreted / random padding
|
||||
* bytes 193-221: uninterpreted / random padding
|
||||
* </pre>
|
||||
*
|
||||
* Encrypted:
|
||||
* <pre>
|
||||
* bytes 0-15: First 16 bytes of router hash
|
||||
* bytes 16-527: ElGamal encrypted block (discarding zero bytes at elg[0] and elg[257])
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
@@ -152,7 +160,7 @@ public class BuildRequestRecord {
|
||||
|
||||
/**
|
||||
* Encrypt the record to the specified peer. The result is formatted as: <pre>
|
||||
* bytes 0-15: SHA-256-128 of the current hop's identity (the toPeer parameter)
|
||||
* bytes 0-15: truncated SHA-256 of the current hop's identity (the toPeer parameter)
|
||||
* bytes 15-527: ElGamal-2048 encrypted block
|
||||
* </pre>
|
||||
*/
|
||||
@@ -226,7 +234,7 @@ public class BuildRequestRecord {
|
||||
* byte 184: flags
|
||||
* bytes 185-188: request time (in hours since the epoch)
|
||||
* bytes 189-192: next message ID
|
||||
* bytes 193-222: uninterpreted / random padding
|
||||
* bytes 193-221: uninterpreted / random padding
|
||||
*/
|
||||
DataHelper.toLong(buf, OFF_RECV_TUNNEL, 4, receiveTunnelId);
|
||||
System.arraycopy(peer.getData(), 0, buf, OFF_OUR_IDENT, Hash.HASH_LENGTH);
|
||||
|
||||
@@ -58,6 +58,10 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
//_context.statManager().createRateStat("i2np.readTime", "How long it takes to read an I2NP message", "I2NP", new long[] { 10*60*1000, 60*60*1000 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the whole message (including the type) and throw it away.
|
||||
* @deprecated Unused, why would you do this
|
||||
*/
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
try {
|
||||
readBytes(in, -1, new byte[1024]);
|
||||
|
||||
@@ -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 = 5;
|
||||
public final static long BUILD = 0;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.crypto.SessionKeyManager;
|
||||
import net.i2p.crypto.TransientSessionKeyManager;
|
||||
import net.i2p.data.Destination;
|
||||
@@ -76,11 +77,13 @@ public class ClientConnectionRunner {
|
||||
* This contains the last 10 MessageIds that have had their (non-ack) status
|
||||
* delivered to the client (so that we can be sure only to update when necessary)
|
||||
*/
|
||||
private final List _alreadyProcessed;
|
||||
private final List<MessageId> _alreadyProcessed;
|
||||
private ClientWriterRunner _writer;
|
||||
private Hash _destHashCache;
|
||||
/** are we, uh, dead */
|
||||
private boolean _dead;
|
||||
/** For outbound traffic. true if i2cp.messageReliability = "none"; @since 0.8.1 */
|
||||
private boolean _dontSendMSM;
|
||||
|
||||
/**
|
||||
* Create a new runner against the given socket
|
||||
@@ -91,11 +94,9 @@ public class ClientConnectionRunner {
|
||||
_log = _context.logManager().getLog(ClientConnectionRunner.class);
|
||||
_manager = manager;
|
||||
_socket = socket;
|
||||
_config = null;
|
||||
_messages = new ConcurrentHashMap();
|
||||
_alreadyProcessed = new ArrayList();
|
||||
_acceptedPending = new ConcurrentHashSet();
|
||||
_dead = false;
|
||||
}
|
||||
|
||||
private static volatile int __id = 0;
|
||||
@@ -189,6 +190,9 @@ public class ClientConnectionRunner {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("SessionEstablished called for destination " + _destHashCache.toBase64());
|
||||
_config = config;
|
||||
// This is the only option that is interpreted here, not at the tunnel manager
|
||||
if (config.getOptions() != null)
|
||||
_dontSendMSM = "none".equalsIgnoreCase(config.getOptions().getProperty(I2PClient.PROP_RELIABILITY));
|
||||
// per-destination session key manager to prevent rather easy correlation
|
||||
if (_sessionKeyManager == null)
|
||||
_sessionKeyManager = new TransientSessionKeyManager(_context);
|
||||
@@ -197,10 +201,18 @@ public class ClientConnectionRunner {
|
||||
_manager.destinationEstablished(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a notification to the client that their message (id specified) was
|
||||
* delivered (or failed delivery)
|
||||
* Note that this sends the Guaranteed status codes, even though we only support best effort.
|
||||
* Doesn't do anything if i2cp.messageReliability = "none"
|
||||
*/
|
||||
void updateMessageDeliveryStatus(MessageId id, boolean delivered) {
|
||||
if (_dead) return;
|
||||
if (_dead || _dontSendMSM)
|
||||
return;
|
||||
_context.jobQueue().addJob(new MessageDeliveryStatusUpdate(id, delivered));
|
||||
}
|
||||
|
||||
/**
|
||||
* called after a new leaseSet is granted by the client, the NetworkDb has been
|
||||
* updated. This takes care of all the LeaseRequestState stuff (including firing any jobs)
|
||||
@@ -254,7 +266,8 @@ public class ClientConnectionRunner {
|
||||
long expiration = 0;
|
||||
if (message instanceof SendMessageExpiresMessage)
|
||||
expiration = ((SendMessageExpiresMessage) message).getExpiration().getTime();
|
||||
_acceptedPending.add(id);
|
||||
if (!_dontSendMSM)
|
||||
_acceptedPending.add(id);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("** Receiving message [" + id.getMessageId() + "] with payload of size ["
|
||||
@@ -276,9 +289,11 @@ public class ClientConnectionRunner {
|
||||
/**
|
||||
* Send a notification to the client that their message (id specified) was accepted
|
||||
* for delivery (but not necessarily delivered)
|
||||
*
|
||||
* Doesn't do anything if i2cp.messageReliability = "none"
|
||||
*/
|
||||
void ackSendMessage(MessageId id, long nonce) {
|
||||
if (_dontSendMSM)
|
||||
return;
|
||||
SessionId sid = _sessionId;
|
||||
if (sid == null) return;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -517,12 +532,17 @@ public class ClientConnectionRunner {
|
||||
}
|
||||
|
||||
public String getName() { return "Update Delivery Status"; }
|
||||
|
||||
/**
|
||||
* Note that this sends the Guaranteed status codes, even though we only support best effort.
|
||||
*/
|
||||
public void runJob() {
|
||||
if (_dead) return;
|
||||
|
||||
MessageStatusMessage msg = new MessageStatusMessage();
|
||||
msg.setMessageId(_messageId.getMessageId());
|
||||
msg.setSessionId(_sessionId.getSessionId());
|
||||
// has to be >= 0, it is initialized to -1
|
||||
msg.setNonce(2);
|
||||
msg.setSize(0);
|
||||
if (_success)
|
||||
|
||||
@@ -41,8 +41,6 @@ public class ClientListenerRunner implements Runnable {
|
||||
_log = _context.logManager().getLog(ClientListenerRunner.class);
|
||||
_manager = manager;
|
||||
_port = port;
|
||||
_running = false;
|
||||
_listening = false;
|
||||
|
||||
String val = context.getProperty(BIND_ALL_INTERFACES);
|
||||
_bindAllInterfaces = Boolean.valueOf(val).booleanValue();
|
||||
|
||||
@@ -43,7 +43,6 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade {
|
||||
|
||||
public ClientManagerFacadeImpl(RouterContext context) {
|
||||
_context = context;
|
||||
_manager = null;
|
||||
_log.debug("Client manager facade created");
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ class LeaseRequestState {
|
||||
_onFailed = onFailed;
|
||||
_expiration = expiration;
|
||||
_requestedLeaseSet = requested;
|
||||
_successful = false;
|
||||
}
|
||||
|
||||
/** created lease set from client */
|
||||
|
||||
@@ -47,9 +47,6 @@ class MessageReceivedJob extends JobImpl {
|
||||
|
||||
/**
|
||||
* Deliver notification to the client that the given message is available.
|
||||
* This is synchronous and returns true if the notification was sent safely,
|
||||
* otherwise it returns false
|
||||
*
|
||||
*/
|
||||
public void messageAvailable(MessageId id, long size) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -59,6 +56,7 @@ class MessageReceivedJob extends JobImpl {
|
||||
msg.setMessageId(id.getMessageId());
|
||||
msg.setSessionId(_runner.getSessionId().getSessionId());
|
||||
msg.setSize(size);
|
||||
// has to be >= 0, it is initialized to -1
|
||||
msg.setNonce(1);
|
||||
msg.setStatus(MessageStatusMessage.STATUS_AVAILABLE);
|
||||
try {
|
||||
|
||||
@@ -44,9 +44,9 @@ class RequestLeaseSetJob extends JobImpl {
|
||||
_onCreate = onCreate;
|
||||
_onFail = onFail;
|
||||
_requestState = state;
|
||||
ctx.statManager().createRateStat("client.requestLeaseSetSuccess", "How frequently the router requests successfully a new leaseSet?", "ClientMessages", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
ctx.statManager().createRateStat("client.requestLeaseSetTimeout", "How frequently the router requests a new leaseSet but gets no reply?", "ClientMessages", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
ctx.statManager().createRateStat("client.requestLeaseSetDropped", "How frequently the router requests a new leaseSet but the client drops?", "ClientMessages", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
ctx.statManager().createRateStat("client.requestLeaseSetSuccess", "How frequently the router requests successfully a new leaseSet?", "ClientMessages", new long[] { 60*60*1000 });
|
||||
ctx.statManager().createRateStat("client.requestLeaseSetTimeout", "How frequently the router requests a new leaseSet but gets no reply?", "ClientMessages", new long[] { 60*60*1000 });
|
||||
ctx.statManager().createRateStat("client.requestLeaseSetDropped", "How frequently the router requests a new leaseSet but the client drops?", "ClientMessages", new long[] { 60*60*1000 });
|
||||
}
|
||||
|
||||
public String getName() { return "Request Lease Set"; }
|
||||
|
||||
@@ -37,17 +37,9 @@ public class GarlicConfig {
|
||||
private long _replyBlockExpiration;
|
||||
|
||||
public GarlicConfig() {
|
||||
_recipient = null;
|
||||
_recipientPublicKey = null;
|
||||
_cert = null;
|
||||
_id = -1;
|
||||
_expiration = -1;
|
||||
_cloveConfigs = new ArrayList();
|
||||
_instructions = null;
|
||||
_requestAck = false;
|
||||
_replyThroughRouter = null;
|
||||
_replyInstructions = null;
|
||||
_replyBlockCertificate = null;
|
||||
_replyBlockMessageId = -1;
|
||||
_replyBlockExpiration = -1;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ public class GarlicMessageBuilder {
|
||||
|
||||
/**
|
||||
* This was 100 since 0.6.1.10 (50 before that). It's important because:
|
||||
* <pre>
|
||||
* - Tags are 32 bytes. So it previously added 3200 bytes to an initial message.
|
||||
* - Too many tags adds a huge overhead to short-duration connections
|
||||
* (like http, datagrams, etc.)
|
||||
@@ -43,14 +44,17 @@ public class GarlicMessageBuilder {
|
||||
* - This reduces the effective maximum datagram size because the client
|
||||
* doesn't know when tags will be bundled, so the tag size must be
|
||||
* subtracted from the maximum I2NP size or transport limit.
|
||||
* </pre>
|
||||
*
|
||||
* Issues with too small a value:
|
||||
* <pre>
|
||||
* - When tags are sent, a reply leaseset (~1KB) is always bundled.
|
||||
* Maybe don't need to bundle more than every minute or so
|
||||
* rather than every time?
|
||||
* - Does the number of tags (and the threshold of 20) limit the effective
|
||||
* streaming lib window size? Should the threshold and the number of
|
||||
* sent tags be variable based on the message rate?
|
||||
* </pre>
|
||||
*
|
||||
* We have to be very careful if we implement an adaptive scheme,
|
||||
* since the key manager is per-router, not per-local-dest.
|
||||
@@ -218,6 +222,7 @@ public class GarlicMessageBuilder {
|
||||
|
||||
byte cloveSet[] = buildCloveSet(ctx, config);
|
||||
|
||||
// TODO - 128 is the minimum padded size - should it be more? less? random?
|
||||
byte encData[] = ctx.elGamalAESEngine().encrypt(cloveSet, target, encryptKey, wrappedTags, encryptTag, 128);
|
||||
msg.setData(encData);
|
||||
msg.setMessageExpiration(config.getExpiration());
|
||||
|
||||
@@ -194,10 +194,12 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
||||
|
||||
public void shutdown() {
|
||||
_initialized = false;
|
||||
_kb = null;
|
||||
// don't null out _kb, it can cause NPEs in concurrent operations
|
||||
//_kb = null;
|
||||
if (_ds != null)
|
||||
_ds.stop();
|
||||
_ds = null;
|
||||
// don't null out _ds, it can cause NPEs in concurrent operations
|
||||
//_ds = null;
|
||||
_exploreKeys.clear(); // hope this doesn't cause an explosion, it shouldn't.
|
||||
// _exploreKeys = null;
|
||||
}
|
||||
@@ -750,6 +752,10 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
||||
} else if (upLongEnough && !routerInfo.isCurrent(ROUTER_INFO_EXPIRATION_SHORT)) {
|
||||
if (routerInfo.getAddresses().isEmpty())
|
||||
return "Peer " + key.toBase64() + " published > 90m ago with no addresses";
|
||||
// This should cover the introducers case below too
|
||||
// And even better, catches the case where the router is unreachable but knows no introducers
|
||||
if (routerInfo.getCapabilities().indexOf(Router.CAPABILITY_UNREACHABLE) >= 0)
|
||||
return "Peer " + key.toBase64() + " published > 90m ago and thinks it is unreachable";
|
||||
RouterAddress ra = routerInfo.getTargetAddress("SSU");
|
||||
if (ra != null) {
|
||||
// Introducers change often, introducee will ping introducer for 2 hours
|
||||
|
||||
@@ -20,7 +20,7 @@ import net.i2p.router.message.PayloadGarlicConfig;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Method an class for garlic encrypting outbound netdb traffic,
|
||||
* Method and class for garlic encrypting outbound netdb traffic,
|
||||
* including management of the ElGamal/AES tags
|
||||
*
|
||||
* @since 0.7.10
|
||||
|
||||
@@ -40,8 +40,7 @@ public class Reseeder {
|
||||
|
||||
private static final String DEFAULT_SEED_URL =
|
||||
"http://a.netdb.i2p2.de/,http://b.netdb.i2p2.de/,http://c.netdb.i2p2.de/," +
|
||||
"http://reseed.i2p-projekt.de/,http://i2pbote.net/netDb/,http://r31453.ovh.net/static_media/netDb/," +
|
||||
"http://p2i.mine.nu/netDb/";
|
||||
"http://reseed.i2p-projekt.de/,http://i2pbote.net/netDb/,http://r31453.ovh.net/static_media/netDb/";
|
||||
private static final String PROP_INPROGRESS = "net.i2p.router.web.ReseedHandler.reseedInProgress";
|
||||
private static final String PROP_ERROR = "net.i2p.router.web.ReseedHandler.errorMessage";
|
||||
private static final String PROP_STATUS = "net.i2p.router.web.ReseedHandler.statusMessage";
|
||||
|
||||
@@ -224,6 +224,7 @@ public class PeerProfile {
|
||||
public double getIntegrationValue() { return _integrationValue; }
|
||||
/**
|
||||
* is this peer actively failing (aka not worth touching)?
|
||||
* deprecated - unused - always false
|
||||
*/
|
||||
public boolean getIsFailing() { return _isFailing; }
|
||||
|
||||
@@ -476,7 +477,9 @@ public class PeerProfile {
|
||||
private double calculateSpeed() { return _context.speedCalculator().calc(this); }
|
||||
private double calculateCapacity() { return _context.capacityCalculator().calc(this); }
|
||||
private double calculateIntegration() { return _context.integrationCalculator().calc(this); }
|
||||
/** deprecated - unused - always false */
|
||||
private boolean calculateIsFailing() { return false; }
|
||||
/** deprecated - unused - always false */
|
||||
void setIsFailing(boolean val) { _isFailing = val; }
|
||||
|
||||
@Override
|
||||
|
||||
@@ -316,6 +316,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
|
||||
String ohost = newProps.getProperty(NTCPAddress.PROP_HOST);
|
||||
String enabled = _context.getProperty(PROP_I2NP_NTCP_AUTO_IP, "true");
|
||||
String name = _context.getProperty(PROP_I2NP_NTCP_HOSTNAME);
|
||||
// hostname config trumps auto config
|
||||
if (name != null && name.length() > 0)
|
||||
enabled = "false";
|
||||
Transport udp = _manager.getTransport(UDPTransport.STYLE);
|
||||
@@ -335,6 +336,17 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
|
||||
newProps.setProperty(NTCPAddress.PROP_HOST, nhost);
|
||||
changed = true;
|
||||
}
|
||||
} else if (enabled.equalsIgnoreCase("false") &&
|
||||
name != null && name.length() > 0 &&
|
||||
!name.equals(ohost) &&
|
||||
nport != null) {
|
||||
// Host name is configured, and we have a port (either auto or configured)
|
||||
// but we probably only get here if the port is auto,
|
||||
// otherwise createNTCPAddress() would have done it already
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("old: " + ohost + " config: " + name + " new: " + name);
|
||||
newProps.setProperty(NTCPAddress.PROP_HOST, name);
|
||||
changed = true;
|
||||
} else if (ohost == null || ohost.length() <= 0) {
|
||||
return;
|
||||
} else if (enabled.equalsIgnoreCase("true") && status != STATUS_OK) {
|
||||
@@ -359,10 +371,10 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Changing NTCP cost from " + oldCost + " to " + newCost);
|
||||
} else {
|
||||
_log.warn("No change to NTCP Address");
|
||||
_log.info("No change to NTCP Address");
|
||||
}
|
||||
} else {
|
||||
_log.warn("No change to NTCP Address");
|
||||
_log.info("No change to NTCP Address");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -98,23 +98,26 @@ public class GeoIP {
|
||||
public void run() {
|
||||
if (_lock.getAndSet(true))
|
||||
return;
|
||||
// clear the negative cache every few runs, to prevent it from getting too big
|
||||
if (((++_lookupRunCount) % CLEAR) == 0)
|
||||
_notFound.clear();
|
||||
Long[] search = _pendingSearch.toArray(new Long[_pendingSearch.size()]);
|
||||
if (search.length <= 0)
|
||||
return;
|
||||
_pendingSearch.clear();
|
||||
Arrays.sort(search);
|
||||
String[] countries = readGeoIPFile(search);
|
||||
|
||||
for (int i = 0; i < countries.length; i++) {
|
||||
if (countries[i] != null)
|
||||
_IPToCountry.put(search[i], countries[i]);
|
||||
else
|
||||
_notFound.add(search[i]);
|
||||
try {
|
||||
// clear the negative cache every few runs, to prevent it from getting too big
|
||||
if (((++_lookupRunCount) % CLEAR) == 0)
|
||||
_notFound.clear();
|
||||
Long[] search = _pendingSearch.toArray(new Long[_pendingSearch.size()]);
|
||||
if (search.length <= 0)
|
||||
return;
|
||||
_pendingSearch.clear();
|
||||
Arrays.sort(search);
|
||||
String[] countries = readGeoIPFile(search);
|
||||
|
||||
for (int i = 0; i < countries.length; i++) {
|
||||
if (countries[i] != null)
|
||||
_IPToCountry.put(search[i], countries[i]);
|
||||
else
|
||||
_notFound.add(search[i]);
|
||||
}
|
||||
} finally {
|
||||
_lock.set(false);
|
||||
}
|
||||
_lock.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterAddress;
|
||||
@@ -36,7 +37,11 @@ import net.i2p.util.Translate;
|
||||
|
||||
public class TransportManager implements TransportEventListener {
|
||||
private Log _log;
|
||||
private List<Transport> _transports;
|
||||
/**
|
||||
* Converted from List to prevent concurrent modification exceptions.
|
||||
* If we want more than one transport with the same style we will have to change this.
|
||||
*/
|
||||
private Map<String, Transport> _transports;
|
||||
private RouterContext _context;
|
||||
private UPnPManager _upnpManager;
|
||||
|
||||
@@ -56,20 +61,20 @@ public class TransportManager implements TransportEventListener {
|
||||
_context.statManager().createRateStat("transport.bidFailSelf", "Could not attempt to bid on message, as it targeted ourselves", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("transport.bidFailNoTransports", "Could not attempt to bid on message, as none of the transports could attempt it", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("transport.bidFailAllTransports", "Could not attempt to bid on message, as all of the transports had failed", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_transports = new ArrayList();
|
||||
_transports = new ConcurrentHashMap(2);
|
||||
if (Boolean.valueOf(_context.getProperty(PROP_ENABLE_UPNP, "true")).booleanValue())
|
||||
_upnpManager = new UPnPManager(context, this);
|
||||
}
|
||||
|
||||
public void addTransport(Transport transport) {
|
||||
if (transport == null) return;
|
||||
_transports.add(transport);
|
||||
_transports.put(transport.getStyle(), transport);
|
||||
transport.setListener(this);
|
||||
}
|
||||
|
||||
public void removeTransport(Transport transport) {
|
||||
if (transport == null) return;
|
||||
_transports.remove(transport);
|
||||
_transports.remove(transport.getStyle());
|
||||
transport.setListener(null);
|
||||
}
|
||||
|
||||
@@ -140,11 +145,10 @@ public class TransportManager implements TransportEventListener {
|
||||
_upnpManager.start();
|
||||
configTransports();
|
||||
_log.debug("Starting up the transport manager");
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Transport t = _transports.get(i);
|
||||
for (Transport t : _transports.values()) {
|
||||
RouterAddress addr = t.startListening();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Transport " + i + " (" + t.getStyle() + ") started");
|
||||
_log.debug("Transport " + t.getStyle() + " started");
|
||||
}
|
||||
// kick UPnP - Do this to get the ports opened even before UDP registers an address
|
||||
transportAddressChanged();
|
||||
@@ -161,19 +165,14 @@ public class TransportManager implements TransportEventListener {
|
||||
public void stopListening() {
|
||||
if (_upnpManager != null)
|
||||
_upnpManager.stop();
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
_transports.get(i).stopListening();
|
||||
for (Transport t : _transports.values()) {
|
||||
t.stopListening();
|
||||
}
|
||||
_transports.clear();
|
||||
}
|
||||
|
||||
public Transport getTransport(String style) {
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Transport t = _transports.get(i);
|
||||
if(style.equals(t.getStyle()))
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
return _transports.get(style);
|
||||
}
|
||||
|
||||
int getTransportCount() { return _transports.size(); }
|
||||
@@ -189,16 +188,16 @@ public class TransportManager implements TransportEventListener {
|
||||
|
||||
public int countActivePeers() {
|
||||
int peers = 0;
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
peers += _transports.get(i).countActivePeers();
|
||||
for (Transport t : _transports.values()) {
|
||||
peers += t.countActivePeers();
|
||||
}
|
||||
return peers;
|
||||
}
|
||||
|
||||
public int countActiveSendPeers() {
|
||||
int peers = 0;
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
peers += _transports.get(i).countActiveSendPeers();
|
||||
for (Transport t : _transports.values()) {
|
||||
peers += t.countActiveSendPeers();
|
||||
}
|
||||
return peers;
|
||||
}
|
||||
@@ -210,8 +209,8 @@ public class TransportManager implements TransportEventListener {
|
||||
* @param pct percent of limit 0-100
|
||||
*/
|
||||
public boolean haveOutboundCapacity(int pct) {
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
if (_transports.get(i).haveCapacity(pct))
|
||||
for (Transport t : _transports.values()) {
|
||||
if (t.haveCapacity(pct))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -225,8 +224,8 @@ public class TransportManager implements TransportEventListener {
|
||||
public boolean haveHighOutboundCapacity() {
|
||||
if (_transports.isEmpty())
|
||||
return false;
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
if (!_transports.get(i).haveCapacity(HIGH_CAPACITY_PCT))
|
||||
for (Transport t : _transports.values()) {
|
||||
if (!t.haveCapacity(HIGH_CAPACITY_PCT))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -239,8 +238,8 @@ public class TransportManager implements TransportEventListener {
|
||||
* @param pct percent of limit 0-100
|
||||
*/
|
||||
public boolean haveInboundCapacity(int pct) {
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
if (_transports.get(i).getCurrentAddress() != null && _transports.get(i).haveCapacity(pct))
|
||||
for (Transport t : _transports.values()) {
|
||||
if (t.getCurrentAddress() != null && t.haveCapacity(pct))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -253,8 +252,8 @@ public class TransportManager implements TransportEventListener {
|
||||
*/
|
||||
public Vector getClockSkews() {
|
||||
Vector skews = new Vector();
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Vector tempSkews = _transports.get(i).getClockSkews();
|
||||
for (Transport t : _transports.values()) {
|
||||
Vector tempSkews = t.getClockSkews();
|
||||
if ((tempSkews == null) || (tempSkews.isEmpty())) continue;
|
||||
skews.addAll(tempSkews);
|
||||
}
|
||||
@@ -266,7 +265,7 @@ public class TransportManager implements TransportEventListener {
|
||||
/** @return the best status of any transport */
|
||||
public short getReachabilityStatus() {
|
||||
short rv = CommSystemFacade.STATUS_UNKNOWN;
|
||||
for (Transport t : _transports) {
|
||||
for (Transport t : _transports.values()) {
|
||||
short s = t.getReachabilityStatus();
|
||||
if (s < rv)
|
||||
rv = s;
|
||||
@@ -275,13 +274,12 @@ public class TransportManager implements TransportEventListener {
|
||||
}
|
||||
|
||||
public void recheckReachability() {
|
||||
for (int i = 0; i < _transports.size(); i++)
|
||||
_transports.get(i).recheckReachability();
|
||||
for (Transport t : _transports.values())
|
||||
t.recheckReachability();
|
||||
}
|
||||
|
||||
public boolean isBacklogged(Hash dest) {
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Transport t = _transports.get(i);
|
||||
for (Transport t : _transports.values()) {
|
||||
if (t.isBacklogged(dest))
|
||||
return true;
|
||||
}
|
||||
@@ -289,8 +287,7 @@ public class TransportManager implements TransportEventListener {
|
||||
}
|
||||
|
||||
public boolean isEstablished(Hash dest) {
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Transport t = _transports.get(i);
|
||||
for (Transport t : _transports.values()) {
|
||||
if (t.isEstablished(dest))
|
||||
return true;
|
||||
}
|
||||
@@ -303,8 +300,7 @@ public class TransportManager implements TransportEventListener {
|
||||
* This is NOT reset if the peer contacts us.
|
||||
*/
|
||||
public boolean wasUnreachable(Hash dest) {
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Transport t = _transports.get(i);
|
||||
for (Transport t : _transports.values()) {
|
||||
if (!t.wasUnreachable(dest))
|
||||
return false;
|
||||
}
|
||||
@@ -330,9 +326,9 @@ public class TransportManager implements TransportEventListener {
|
||||
public Map<String, RouterAddress> getAddresses() {
|
||||
Map<String, RouterAddress> rv = new HashMap(_transports.size());
|
||||
// do this first since SSU may force a NTCP change
|
||||
for (Transport t : _transports)
|
||||
for (Transport t : _transports.values())
|
||||
t.updateAddress();
|
||||
for (Transport t : _transports) {
|
||||
for (Transport t : _transports.values()) {
|
||||
if (t.getCurrentAddress() != null)
|
||||
rv.put(t.getStyle(), t.getCurrentAddress());
|
||||
}
|
||||
@@ -345,7 +341,7 @@ public class TransportManager implements TransportEventListener {
|
||||
*/
|
||||
private Map<String, Integer> getPorts() {
|
||||
Map<String, Integer> rv = new HashMap(_transports.size());
|
||||
for (Transport t : _transports) {
|
||||
for (Transport t : _transports.values()) {
|
||||
int port = t.getRequestedPort();
|
||||
if (t.getCurrentAddress() != null) {
|
||||
Properties opts = t.getCurrentAddress().getOptions();
|
||||
@@ -386,8 +382,7 @@ public class TransportManager implements TransportEventListener {
|
||||
|
||||
List<TransportBid> rv = new ArrayList(_transports.size());
|
||||
Set failedTransports = msg.getFailedTransports();
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Transport t = _transports.get(i);
|
||||
for (Transport t : _transports.values()) {
|
||||
if (failedTransports.contains(t.getStyle())) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Skipping transport " + t.getStyle() + " as it already failed");
|
||||
@@ -415,8 +410,7 @@ public class TransportManager implements TransportEventListener {
|
||||
Hash peer = msg.getTarget().getIdentity().calculateHash();
|
||||
Set failedTransports = msg.getFailedTransports();
|
||||
TransportBid rv = null;
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Transport t = _transports.get(i);
|
||||
for (Transport t : _transports.values()) {
|
||||
if (t.isUnreachable(peer)) {
|
||||
unreachableTransports++;
|
||||
// this keeps GetBids() from shitlisting for "no common transports"
|
||||
@@ -482,8 +476,7 @@ public class TransportManager implements TransportEventListener {
|
||||
|
||||
public List getMostRecentErrorMessages() {
|
||||
List rv = new ArrayList(16);
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Transport t = _transports.get(i);
|
||||
for (Transport t : _transports.values()) {
|
||||
rv.addAll(t.getMostRecentErrorMessages());
|
||||
}
|
||||
return rv;
|
||||
@@ -491,8 +484,7 @@ public class TransportManager implements TransportEventListener {
|
||||
|
||||
public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException {
|
||||
TreeMap transports = new TreeMap();
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Transport t = _transports.get(i);
|
||||
for (Transport t : _transports.values()) {
|
||||
transports.put(t.getStyle(), t);
|
||||
}
|
||||
for (Iterator iter = transports.values().iterator(); iter.hasNext(); ) {
|
||||
@@ -506,8 +498,7 @@ public class TransportManager implements TransportEventListener {
|
||||
|
||||
StringBuilder buf = new StringBuilder(4*1024);
|
||||
buf.append("<h3>").append(_("Router Transport Addresses")).append("</h3><pre>\n");
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Transport t = _transports.get(i);
|
||||
for (Transport t : _transports.values()) {
|
||||
if (t.getCurrentAddress() != null)
|
||||
buf.append(t.getCurrentAddress());
|
||||
else
|
||||
|
||||
@@ -29,18 +29,45 @@ import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/*
|
||||
/**
|
||||
* Handle the 4-phase establishment, which is as follows:
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* Alice contacts Bob
|
||||
* =========================================================
|
||||
*
|
||||
* Message 1 (Session Request):
|
||||
* X+(H(X) xor Bob.identHash)----------------------------->
|
||||
*
|
||||
* Message 2 (Session Created):
|
||||
* <----------------------------------------Y+E(H(X+Y)+tsB, sk, Y[239:255])
|
||||
* E(#+Alice.identity+tsA+padding+S(X+Y+Bob.identHash+tsA+tsB+padding), sk, hX_xor_Bob.identHash[16:31])--->
|
||||
*
|
||||
* Message 3 (Session Confirm A):
|
||||
* E(sz+Alice.identity+tsA+padding+S(X+Y+Bob.identHash+tsA+tsB), sk, hX_xor_Bob.identHash[16:31])--->
|
||||
*
|
||||
* Message 4 (Session Confirm B):
|
||||
* <----------------------E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev)
|
||||
*
|
||||
* Key:
|
||||
*
|
||||
* X, Y: 256 byte DH keys
|
||||
* H(): 32 byte SHA256 Hash
|
||||
* E(data, session key, IV): AES256 Encrypt
|
||||
* S(): 40 byte DSA Signature
|
||||
* tsA, tsB: timestamps (4 bytes, seconds since epoch)
|
||||
* sk: 32 byte Session key
|
||||
* sz: 2 byte size of Alice identity to follow
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* Alternately, when Bob receives a connection, it could be a
|
||||
* check connection (perhaps prompted by Bob asking for someone
|
||||
* to verify his listener). check connections are formatted per
|
||||
* {@link #isCheckInfo()}
|
||||
* isCheckInfo()
|
||||
* NOTE: Check info is unused.
|
||||
*
|
||||
*/
|
||||
public class EstablishState {
|
||||
private RouterContext _context;
|
||||
@@ -57,7 +84,9 @@ public class EstablishState {
|
||||
// alice receives (and bob sends)
|
||||
private byte _Y[];
|
||||
private transient byte _e_hXY_tsB[];
|
||||
/** Bob's Timestamp in seconds */
|
||||
private transient long _tsB;
|
||||
/** Alice's Timestamp in seconds */
|
||||
private transient long _tsA;
|
||||
private transient byte _e_bobSig[];
|
||||
|
||||
@@ -98,9 +127,6 @@ public class EstablishState {
|
||||
_log = ctx.logManager().getLog(getClass());
|
||||
_transport = transport;
|
||||
_con = con;
|
||||
_verified = false;
|
||||
_corrupt = false;
|
||||
_confirmWritten = false;
|
||||
_dh = new DHSessionKeyBuilder();
|
||||
if (_con.isInbound()) {
|
||||
_X = new byte[256];
|
||||
@@ -116,10 +142,7 @@ public class EstablishState {
|
||||
|
||||
_prevEncrypted = new byte[16];
|
||||
_curEncrypted = new byte[16];
|
||||
_curEncryptedOffset = 0;
|
||||
_curDecrypted = new byte[16];
|
||||
|
||||
_received = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +173,10 @@ public class EstablishState {
|
||||
|
||||
public boolean getFailedBySkew() { return _failedBySkew; }
|
||||
|
||||
/** we are Bob, so receive these bytes as part of an inbound connection */
|
||||
/**
|
||||
* we are Bob, so receive these bytes as part of an inbound connection
|
||||
* This method receives messages 1 and 3, and sends messages 2 and 4.
|
||||
*/
|
||||
private void receiveInbound(ByteBuffer src) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(prefix()+"Receiving inbound: prev received=" + _received + " src.remaining=" + src.remaining());
|
||||
@@ -311,7 +337,10 @@ public class EstablishState {
|
||||
}
|
||||
}
|
||||
|
||||
/** we are Alice, so receive these bytes as part of an outbound connection */
|
||||
/**
|
||||
* We are Alice, so receive these bytes as part of an outbound connection.
|
||||
* This method receives messages 2 and 4, and sends message 3.
|
||||
*/
|
||||
private void receiveOutbound(ByteBuffer src) {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(prefix()+"Receive outbound " + src + " received=" + _received);
|
||||
|
||||
@@ -498,8 +527,10 @@ public class EstablishState {
|
||||
public boolean isComplete() { return _verified; }
|
||||
|
||||
/**
|
||||
* we are establishing an outbound connection, so prepare ourselves by
|
||||
* We are Alice.
|
||||
* We are establishing an outbound connection, so prepare ourselves by
|
||||
* queueing up the write of the first part of the handshake
|
||||
* This method sends message #1 to Bob.
|
||||
*/
|
||||
public void prepareOutbound() {
|
||||
if (_received <= 0) {
|
||||
@@ -516,7 +547,9 @@ public class EstablishState {
|
||||
}
|
||||
|
||||
/**
|
||||
* make sure the signatures are correct, and if they are, update the
|
||||
* We are Bob. Verify message #3 from Alice, then send message #4 to Alice.
|
||||
*
|
||||
* Make sure the signatures are correct, and if they are, update the
|
||||
* NIOConnection with the session key / peer ident / clock skew / iv.
|
||||
* The NIOConnection itself is responsible for registering with the
|
||||
* transport
|
||||
@@ -623,6 +656,9 @@ public class EstablishState {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We are Bob. Send message #4 to Alice.
|
||||
*/
|
||||
private void sendInboundConfirm(RouterIdentity alice, long tsA) {
|
||||
// send Alice E(S(X+Y+Alice.identHash+tsA+tsB), sk, prev)
|
||||
byte toSign[] = new byte[256+256+32+4+4];
|
||||
@@ -703,6 +739,9 @@ public class EstablishState {
|
||||
* - 4 byte i2p network time as known by the remote side (seconds since the epoch)
|
||||
* - uninterpreted padding data, up to byte 223
|
||||
* - xor of the local router's identity hash and the SHA256 of bytes 32 through bytes 223
|
||||
*
|
||||
* @return should always be false since nobody ever sends a check info message
|
||||
*
|
||||
*/
|
||||
private static boolean isCheckInfo(I2PAppContext ctx, Hash us, byte first256[]) {
|
||||
Log log = ctx.logManager().getLog(EstablishState.class);
|
||||
@@ -742,6 +781,8 @@ public class EstablishState {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated unused */
|
||||
/*********
|
||||
public static void checkHost(String args[]) {
|
||||
if (args.length != 3) {
|
||||
System.err.println("Usage: EstablishState ipOrHostname portNum peerHashBase64");
|
||||
@@ -779,7 +820,9 @@ public class EstablishState {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
*******/
|
||||
|
||||
/*******
|
||||
public static void main(String args[]) {
|
||||
if (args.length == 3) {
|
||||
checkHost(args);
|
||||
@@ -943,6 +986,7 @@ public class EstablishState {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
*******/
|
||||
|
||||
/**
|
||||
* Mark a string for extraction by xgettext and translation.
|
||||
|
||||
@@ -218,6 +218,13 @@ class InboundEstablishState {
|
||||
if (_receivedIdentity == null)
|
||||
_receivedIdentity = new byte[conf.readTotalFragmentNum()][];
|
||||
int cur = conf.readCurrentFragmentNum();
|
||||
if (cur >= _receivedIdentity.length) {
|
||||
// avoid AIOOBE
|
||||
// should do more than this, but what? disconnect?
|
||||
fail();
|
||||
packetReceived();
|
||||
return;
|
||||
}
|
||||
if (_receivedIdentity[cur] == null) {
|
||||
byte fragment[] = new byte[conf.readCurrentFragmentSize()];
|
||||
conf.readFragmentData(fragment, 0);
|
||||
|
||||
@@ -96,7 +96,10 @@ class IntroductionManager {
|
||||
int sz = peers.size();
|
||||
start = start % sz;
|
||||
int found = 0;
|
||||
long inactivityCutoff = _context.clock().now() - (UDPTransport.EXPIRE_TIMEOUT / 2);
|
||||
long inactivityCutoff = _context.clock().now() - (UDPTransport.EXPIRE_TIMEOUT / 2); // 15 min
|
||||
// if not too many to choose from, be less picky
|
||||
if (sz <= howMany + 2)
|
||||
inactivityCutoff -= UDPTransport.EXPIRE_TIMEOUT / 4;
|
||||
for (int i = 0; i < sz && found < howMany; i++) {
|
||||
PeerState cur = peers.get((start + i) % sz);
|
||||
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(cur.getRemotePeer());
|
||||
@@ -119,7 +122,11 @@ class IntroductionManager {
|
||||
continue;
|
||||
}
|
||||
// Try to pick active peers...
|
||||
if (cur.getLastReceiveTime() < inactivityCutoff || cur.getLastSendTime() < inactivityCutoff) {
|
||||
// FIXME this is really strict and causes us to run out of introducers
|
||||
// We have much less introducers than we used to have because routers don't offer
|
||||
// if they are approaching max connections (see EstablishmentManager)
|
||||
// FIXED, was ||, is this OK now?
|
||||
if (cur.getLastReceiveTime() < inactivityCutoff && cur.getLastSendTime() < inactivityCutoff) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Peer is idle too long: " + cur);
|
||||
continue;
|
||||
@@ -135,6 +142,8 @@ class IntroductionManager {
|
||||
found++;
|
||||
}
|
||||
|
||||
// FIXME failsafe if found == 0, relax inactivityCutoff and try again?
|
||||
|
||||
// Try to keep the connection up for two hours after we made anybody an introducer
|
||||
long pingCutoff = _context.clock().now() - (2 * 60 * 60 * 1000);
|
||||
inactivityCutoff = _context.clock().now() - (UDPTransport.EXPIRE_TIMEOUT / 4);
|
||||
@@ -156,7 +165,7 @@ class IntroductionManager {
|
||||
* Not as elaborate as pickInbound() above.
|
||||
* Just a quick check to see how many volunteers we know,
|
||||
* which the Transport uses to see if we need more.
|
||||
* @return number of peers that have volunteerd to introduce us
|
||||
* @return number of peers that have volunteered to introduce us
|
||||
*/
|
||||
int introducerCount() {
|
||||
return _inbound.size();
|
||||
|
||||
@@ -641,6 +641,7 @@ class PacketBuilder {
|
||||
// pad here if we want. maybe randomized?
|
||||
|
||||
// pad up so we're on the encryption boundary
|
||||
// TODO: why not random data?
|
||||
if ( (off % 16) != 0)
|
||||
off += 16 - (off % 16);
|
||||
packet.getPacket().setLength(off);
|
||||
|
||||
@@ -70,9 +70,11 @@ class UDPPacket {
|
||||
// various flag fields for use in the data packets
|
||||
public static final byte DATA_FLAG_EXPLICIT_ACK = (byte)(1 << 7);
|
||||
public static final byte DATA_FLAG_ACK_BITFIELDS = (1 << 6);
|
||||
// unused
|
||||
public static final byte DATA_FLAG_ECN = (1 << 4);
|
||||
public static final byte DATA_FLAG_WANT_ACKS = (1 << 3);
|
||||
public static final byte DATA_FLAG_WANT_REPLY = (1 << 2);
|
||||
// unused
|
||||
public static final byte DATA_FLAG_EXTENDED = (1 << 1);
|
||||
|
||||
public static final byte BITFIELD_CONTINUATION = (byte)(1 << 7);
|
||||
|
||||
@@ -602,6 +602,8 @@ class UDPPacketReader {
|
||||
_log.debug("read alice port: " + rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/** unused */
|
||||
public int readChallengeSize() {
|
||||
int offset = readBodyOffset() + 4;
|
||||
offset += DataHelper.fromLong(_message, offset, 1);
|
||||
@@ -612,6 +614,8 @@ class UDPPacketReader {
|
||||
_log.debug("read challenge size: " + rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/** unused */
|
||||
public void readChallengeSize(byte target[], int targetOffset) {
|
||||
int offset = readBodyOffset() + 4;
|
||||
offset += DataHelper.fromLong(_message, offset, 1);
|
||||
@@ -672,6 +676,8 @@ class UDPPacketReader {
|
||||
offset++;
|
||||
return (int)DataHelper.fromLong(_message, offset, 2);
|
||||
}
|
||||
|
||||
/** unused */
|
||||
public int readChallengeSize() {
|
||||
int offset = readBodyOffset();
|
||||
offset += DataHelper.fromLong(_message, offset, 1);
|
||||
@@ -679,6 +685,8 @@ class UDPPacketReader {
|
||||
offset += 2;
|
||||
return (int)DataHelper.fromLong(_message, offset, 1);
|
||||
}
|
||||
|
||||
/** unused */
|
||||
public void readChallengeSize(byte target[], int targetOffset) {
|
||||
int offset = readBodyOffset();
|
||||
offset += DataHelper.fromLong(_message, offset, 1);
|
||||
@@ -753,6 +761,7 @@ class UDPPacketReader {
|
||||
|
||||
/* ------- End Reader Classes ------- */
|
||||
|
||||
/******
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
try {
|
||||
@@ -778,4 +787,5 @@ class UDPPacketReader {
|
||||
}
|
||||
|
||||
}
|
||||
*******/
|
||||
}
|
||||
|
||||
@@ -946,7 +946,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
// try to shift 'em around every 10 minutes or so
|
||||
if (_introducersSelectedOn < _context.clock().now() - 10*60*1000) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Our introducers are valid, but thy havent changed in a while, so lets rechoose");
|
||||
_log.warn("Our introducers are valid, but havent changed in a while, so lets rechoose");
|
||||
return true;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
@@ -955,7 +955,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Our introducers are not valid (" +valid + ")");
|
||||
_log.info("Need more introducers (have " +valid + " need " + PUBLIC_RELAY_COUNT + ')');
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@@ -1018,7 +1018,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
/** minimum active peers to maintain IP detection, etc. */
|
||||
private static final int MIN_PEERS = 3;
|
||||
/** minimum peers volunteering to be introducers if we need that */
|
||||
private static final int MIN_INTRODUCER_POOL = 4;
|
||||
private static final int MIN_INTRODUCER_POOL = 5;
|
||||
|
||||
public TransportBid bid(RouterInfo toAddress, long dataSize) {
|
||||
if (dataSize > OutboundMessageState.MAX_MSG_SIZE) {
|
||||
@@ -1234,7 +1234,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
_introducersSelectedOn = _context.clock().now();
|
||||
introducersIncluded = true;
|
||||
} else {
|
||||
// FIXME
|
||||
// maybe we should fail to publish an address at all in this case?
|
||||
// YES that would be better
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Need introducers but we don't know any");
|
||||
}
|
||||
@@ -1332,13 +1334,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
switch (status) {
|
||||
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
|
||||
case CommSystemFacade.STATUS_DIFFERENT:
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Require introducers, because our status is " + status);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Require introducers, because our status is " + status);
|
||||
return true;
|
||||
default:
|
||||
if (!allowDirectUDP()) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Require introducers, because we do not allow direct UDP connections");
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Require introducers, because we do not allow direct UDP connections");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -24,7 +24,7 @@ public class BuildMessageProcessor {
|
||||
|
||||
public BuildMessageProcessor(I2PAppContext ctx) {
|
||||
_filter = new DecayingHashSet(ctx, 60*1000, 32, "TunnelBMP");
|
||||
ctx.statManager().createRateStat("tunnel.buildRequestDup", "How frequently we get dup build request messages", "Tunnels", new long[] { 60*1000, 10*60*1000 });
|
||||
ctx.statManager().createRateStat("tunnel.buildRequestDup", "How frequently we get dup build request messages", "Tunnels", new long[] { 60*60*1000 });
|
||||
}
|
||||
/**
|
||||
* Decrypt the record targetting us, encrypting all of the other records with the included
|
||||
|
||||
@@ -15,17 +15,19 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
public class InboundEndpointProcessor {
|
||||
private RouterContext _context;
|
||||
private Log _log;
|
||||
private TunnelCreatorConfig _config;
|
||||
private IVValidator _validator;
|
||||
private final RouterContext _context;
|
||||
private final Log _log;
|
||||
private final TunnelCreatorConfig _config;
|
||||
private final IVValidator _validator;
|
||||
|
||||
static final boolean USE_ENCRYPTION = HopProcessor.USE_ENCRYPTION;
|
||||
private static final ByteCache _cache = ByteCache.getInstance(128, HopProcessor.IV_LENGTH);
|
||||
|
||||
/** @deprecated unused */
|
||||
public InboundEndpointProcessor(RouterContext ctx, TunnelCreatorConfig cfg) {
|
||||
this(ctx, cfg, DummyValidator.getInstance());
|
||||
}
|
||||
|
||||
public InboundEndpointProcessor(RouterContext ctx, TunnelCreatorConfig cfg, IVValidator validator) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(InboundEndpointProcessor.class);
|
||||
@@ -84,6 +86,9 @@ public class InboundEndpointProcessor {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iteratively undo the crypto that the various layers in the tunnel added.
|
||||
*/
|
||||
private void decrypt(RouterContext ctx, TunnelCreatorConfig cfg, byte iv[], byte orig[], int offset, int length) {
|
||||
Log log = ctx.logManager().getLog(OutboundGatewayProcessor.class);
|
||||
ByteArray ba = _cache.acquire();
|
||||
|
||||
@@ -14,9 +14,9 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
public class OutboundGatewayProcessor {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private TunnelCreatorConfig _config;
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
private final TunnelCreatorConfig _config;
|
||||
|
||||
static final boolean USE_ENCRYPTION = HopProcessor.USE_ENCRYPTION;
|
||||
private static final ByteCache _cache = ByteCache.getInstance(128, HopProcessor.IV_LENGTH);
|
||||
@@ -54,10 +54,8 @@ public class OutboundGatewayProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo the crypto that the various layers in the tunnel added. This is used
|
||||
* by both the outbound gateway (preemptively undoing the crypto peers will add)
|
||||
* and by the inbound endpoint.
|
||||
*
|
||||
* Iteratively undo the crypto that the various layers in the tunnel added. This is used
|
||||
* by the outbound gateway (preemptively undoing the crypto peers will add).
|
||||
*/
|
||||
private void decrypt(I2PAppContext ctx, TunnelCreatorConfig cfg, byte iv[], byte orig[], int offset, int length) {
|
||||
Log log = ctx.logManager().getLog(OutboundGatewayProcessor.class);
|
||||
@@ -73,6 +71,11 @@ public class OutboundGatewayProcessor {
|
||||
_cache.release(ba);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo the crypto for a single hop. This is used
|
||||
* by both the outbound gateway (preemptively undoing the crypto peers will add)
|
||||
* and by the inbound endpoint.
|
||||
*/
|
||||
static void decrypt(I2PAppContext ctx, byte iv[], byte orig[], int offset, int length, byte cur[], HopConfig config) {
|
||||
// update the IV for the previous (next?) hop
|
||||
ctx.aes().decryptBlock(orig, offset, config.getIVKey(), orig, offset);
|
||||
|
||||
@@ -60,7 +60,6 @@ public class TunnelGateway {
|
||||
_preprocessor = preprocessor;
|
||||
_sender = sender;
|
||||
_receiver = receiver;
|
||||
_messagesSent = 0;
|
||||
_flushFrequency = 500;
|
||||
_delayedFlush = new DelayedFlush();
|
||||
_lastFlush = _context.clock().now();
|
||||
@@ -128,8 +127,8 @@ public class TunnelGateway {
|
||||
FlushTimer.getInstance().addEvent(_delayedFlush, delayAmount);
|
||||
}
|
||||
_context.statManager().addRateData("tunnel.lockedGatewayAdd", afterAdded-beforeLock, remaining);
|
||||
long complete = System.currentTimeMillis();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
long complete = System.currentTimeMillis();
|
||||
_log.debug("Time to add the message " + msg.getUniqueId() + ": " + (complete-startAdd)
|
||||
+ " delayed? " + delayedFlush + " remaining: " + remaining
|
||||
+ " prepare: " + (beforeLock-startAdd)
|
||||
@@ -137,6 +136,7 @@ public class TunnelGateway {
|
||||
+ " preprocess: " + (afterPreprocess-afterAdded)
|
||||
+ " expire: " + (afterExpire-afterPreprocess)
|
||||
+ " queue flush: " + (complete-afterExpire));
|
||||
}
|
||||
}
|
||||
|
||||
public int getMessagesSent() { return _messagesSent; }
|
||||
@@ -202,10 +202,7 @@ public class TunnelGateway {
|
||||
_messageId = message.getUniqueId();
|
||||
_expiration = message.getMessageExpiration();
|
||||
_remaining = message.toByteArray();
|
||||
_offset = 0;
|
||||
_fragmentNumber = 0;
|
||||
_created = now;
|
||||
_messageIds = null;
|
||||
}
|
||||
/** may be null */
|
||||
public Hash getToRouter() { return _toRouter; }
|
||||
|
||||
@@ -498,6 +498,8 @@ class BuildHandler {
|
||||
long nextId = req.readNextTunnelId();
|
||||
boolean isInGW = req.readIsInboundGateway();
|
||||
boolean isOutEnd = req.readIsOutboundEndpoint();
|
||||
// time is in hours, and only for log below - what's the point?
|
||||
// tunnel-alt-creation.html specifies that this is enforced +/- 1 hour but it is not.
|
||||
long time = req.readRequestTime();
|
||||
long now = (_context.clock().now() / (60l*60l*1000l)) * (60*60*1000);
|
||||
int ourSlot = -1;
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
|
||||
@@ -14,7 +15,7 @@ import net.i2p.router.TunnelPoolSettings;
|
||||
*
|
||||
*/
|
||||
class ClientPeerSelector extends TunnelPeerSelector {
|
||||
public List selectPeers(RouterContext ctx, TunnelPoolSettings settings) {
|
||||
public List<Hash> selectPeers(RouterContext ctx, TunnelPoolSettings settings) {
|
||||
int length = getLength(ctx, settings);
|
||||
if (length < 0)
|
||||
return null;
|
||||
@@ -31,7 +32,7 @@ class ClientPeerSelector extends TunnelPeerSelector {
|
||||
ctx.profileOrganizer().selectFastPeers(length, exclude, matches, settings.getIPRestriction());
|
||||
|
||||
matches.remove(ctx.routerHash());
|
||||
ArrayList rv = new ArrayList(matches);
|
||||
ArrayList<Hash> rv = new ArrayList(matches);
|
||||
if (rv.size() > 1)
|
||||
orderPeers(rv, settings.getRandomKey());
|
||||
if (settings.isInbound())
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
import net.i2p.stat.Rate;
|
||||
@@ -17,7 +18,7 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
class ExploratoryPeerSelector extends TunnelPeerSelector {
|
||||
public List selectPeers(RouterContext ctx, TunnelPoolSettings settings) {
|
||||
public List<Hash> selectPeers(RouterContext ctx, TunnelPoolSettings settings) {
|
||||
Log l = ctx.logManager().getLog(getClass());
|
||||
int length = getLength(ctx, settings);
|
||||
if (length < 0) {
|
||||
@@ -33,7 +34,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Set exclude = getExclude(ctx, settings.isInbound(), settings.isExploratory());
|
||||
Set<Hash> exclude = getExclude(ctx, settings.isInbound(), settings.isExploratory());
|
||||
exclude.add(ctx.routerHash());
|
||||
// Don't use ff peers for exploratory tunnels to lessen exposure to netDb searches and stores
|
||||
// Hmm if they don't get explored they don't get a speed/capacity rating
|
||||
@@ -56,7 +57,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
|
||||
l.debug("profileOrganizer.selectNotFailing(" + length + ") found " + matches);
|
||||
|
||||
matches.remove(ctx.routerHash());
|
||||
ArrayList rv = new ArrayList(matches);
|
||||
ArrayList<Hash> rv = new ArrayList(matches);
|
||||
if (rv.size() > 1)
|
||||
orderPeers(rv, settings.getRandomKey());
|
||||
if (settings.isInbound())
|
||||
@@ -67,7 +68,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
|
||||
}
|
||||
|
||||
private static final int MIN_NONFAILING_PCT = 25;
|
||||
private boolean shouldPickHighCap(RouterContext ctx) {
|
||||
private static boolean shouldPickHighCap(RouterContext ctx) {
|
||||
if (Boolean.valueOf(ctx.getProperty("router.exploreHighCapacity", "false")).booleanValue())
|
||||
return true;
|
||||
// no need to explore too wildly at first
|
||||
@@ -86,9 +87,9 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
|
||||
failPct = 100 - MIN_NONFAILING_PCT;
|
||||
} else {
|
||||
failPct = getExploratoryFailPercentage(ctx);
|
||||
Log l = ctx.logManager().getLog(getClass());
|
||||
if (l.shouldLog(Log.DEBUG))
|
||||
l.debug("Normalized Fail pct: " + failPct);
|
||||
//Log l = ctx.logManager().getLog(getClass());
|
||||
//if (l.shouldLog(Log.DEBUG))
|
||||
// l.debug("Normalized Fail pct: " + failPct);
|
||||
// always try a little, this helps keep the failPct stat accurate too
|
||||
if (failPct > 100 - MIN_NONFAILING_PCT)
|
||||
failPct = 100 - MIN_NONFAILING_PCT;
|
||||
@@ -96,21 +97,23 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
|
||||
return (failPct >= ctx.random().nextInt(100));
|
||||
}
|
||||
|
||||
// We should really use the difference between the exploratory fail rate
|
||||
// and the high capacity fail rate - but we don't have a stat for high cap,
|
||||
// so use the fast (== client) fail rate, it should be close
|
||||
// if the expl. and client tunnel lengths aren't too different.
|
||||
// So calculate the difference between the exploratory fail rate
|
||||
// and the client fail rate, normalized to 100:
|
||||
// 100 * ((Efail - Cfail) / (100 - Cfail))
|
||||
// Even this isn't the "true" rate for the NonFailingPeers pool, since we
|
||||
// are often building exploratory tunnels using the HighCapacity pool.
|
||||
private int getExploratoryFailPercentage(RouterContext ctx) {
|
||||
/**
|
||||
* We should really use the difference between the exploratory fail rate
|
||||
* and the high capacity fail rate - but we don't have a stat for high cap,
|
||||
* so use the fast (== client) fail rate, it should be close
|
||||
* if the expl. and client tunnel lengths aren't too different.
|
||||
* So calculate the difference between the exploratory fail rate
|
||||
* and the client fail rate, normalized to 100:
|
||||
* 100 * ((Efail - Cfail) / (100 - Cfail))
|
||||
* Even this isn't the "true" rate for the NonFailingPeers pool, since we
|
||||
* are often building exploratory tunnels using the HighCapacity pool.
|
||||
*/
|
||||
private static int getExploratoryFailPercentage(RouterContext ctx) {
|
||||
int c = getFailPercentage(ctx, "Client");
|
||||
int e = getFailPercentage(ctx, "Exploratory");
|
||||
Log l = ctx.logManager().getLog(getClass());
|
||||
if (l.shouldLog(Log.DEBUG))
|
||||
l.debug("Client, Expl. Fail pct: " + c + ", " + e);
|
||||
//Log l = ctx.logManager().getLog(getClass());
|
||||
//if (l.shouldLog(Log.DEBUG))
|
||||
// l.debug("Client, Expl. Fail pct: " + c + ", " + e);
|
||||
if (e <= c || e <= 25) // doing very well (unlikely)
|
||||
return 0;
|
||||
if (c >= 90) // doing very badly
|
||||
@@ -118,7 +121,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
|
||||
return (100 * (e-c)) / (100-c);
|
||||
}
|
||||
|
||||
private int getFailPercentage(RouterContext ctx, String t) {
|
||||
private static int getFailPercentage(RouterContext ctx, String t) {
|
||||
String pfx = "tunnel.build" + t;
|
||||
int timeout = getEvents(ctx, pfx + "Expire", 10*60*1000);
|
||||
int reject = getEvents(ctx, pfx + "Reject", 10*60*1000);
|
||||
@@ -129,8 +132,8 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
|
||||
return (int)(100 * pct);
|
||||
}
|
||||
|
||||
// Use current + last to get more recent and smoother data
|
||||
private int getEvents(RouterContext ctx, String stat, long period) {
|
||||
/** Use current + last to get more recent and smoother data */
|
||||
private static int getEvents(RouterContext ctx, String stat, long period) {
|
||||
RateStat rs = ctx.statManager().getRate(stat);
|
||||
if (rs == null)
|
||||
return 0;
|
||||
|
||||
@@ -12,7 +12,7 @@ import net.i2p.util.Log;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PooledTunnelCreatorConfig extends TunnelCreatorConfig {
|
||||
class PooledTunnelCreatorConfig extends TunnelCreatorConfig {
|
||||
private TunnelPool _pool;
|
||||
private TestJob _testJob;
|
||||
// private Job _expireJob;
|
||||
|
||||
@@ -21,10 +21,13 @@ import net.i2p.router.TunnelPoolSettings;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import net.i2p.router.networkdb.kademlia.HashDistance;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
/**
|
||||
* Coordinate the selection of peers to go into a tunnel for one particular
|
||||
* pool.
|
||||
*
|
||||
* Todo: there's nothing non-static in here
|
||||
*/
|
||||
public abstract class TunnelPeerSelector {
|
||||
/**
|
||||
@@ -36,7 +39,7 @@ public abstract class TunnelPeerSelector {
|
||||
* to build through, and the settings reject 0 hop tunnels, this will
|
||||
* return null.
|
||||
*/
|
||||
public abstract List selectPeers(RouterContext ctx, TunnelPoolSettings settings);
|
||||
public abstract List<Hash> selectPeers(RouterContext ctx, TunnelPoolSettings settings);
|
||||
|
||||
protected int getLength(RouterContext ctx, TunnelPoolSettings settings) {
|
||||
int length = settings.getLength();
|
||||
@@ -79,6 +82,11 @@ public abstract class TunnelPeerSelector {
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging, also possibly for restricted routes?
|
||||
* Needs analysis and testing
|
||||
* @return should always be false
|
||||
*/
|
||||
protected boolean shouldSelectExplicit(TunnelPoolSettings settings) {
|
||||
if (settings.isExploratory()) return false;
|
||||
Properties opts = settings.getUnknownOptions();
|
||||
@@ -92,7 +100,12 @@ public abstract class TunnelPeerSelector {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected List selectExplicit(RouterContext ctx, TunnelPoolSettings settings, int length) {
|
||||
/**
|
||||
* For debugging, also possibly for restricted routes?
|
||||
* Needs analysis and testing
|
||||
* @return should always be false
|
||||
*/
|
||||
protected List<Hash> selectExplicit(RouterContext ctx, TunnelPoolSettings settings, int length) {
|
||||
String peers = null;
|
||||
Properties opts = settings.getUnknownOptions();
|
||||
if (opts != null)
|
||||
@@ -102,7 +115,7 @@ public abstract class TunnelPeerSelector {
|
||||
peers = I2PAppContext.getGlobalContext().getProperty("explicitPeers");
|
||||
|
||||
Log log = ctx.logManager().getLog(ClientPeerSelector.class);
|
||||
List rv = new ArrayList();
|
||||
List<Hash> rv = new ArrayList();
|
||||
StringTokenizer tok = new StringTokenizer(peers, ",");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String peerStr = tok.nextToken();
|
||||
@@ -156,7 +169,7 @@ public abstract class TunnelPeerSelector {
|
||||
/**
|
||||
* Pick peers that we want to avoid
|
||||
*/
|
||||
public Set getExclude(RouterContext ctx, boolean isInbound, boolean isExploratory) {
|
||||
public Set<Hash> getExclude(RouterContext ctx, boolean isInbound, boolean isExploratory) {
|
||||
// we may want to update this to skip 'hidden' or 'unreachable' peers, but that
|
||||
// isn't safe, since they may publish one set of routerInfo to us and another to
|
||||
// other peers. the defaults for filterUnreachable has always been to return false,
|
||||
@@ -175,11 +188,12 @@ public abstract class TunnelPeerSelector {
|
||||
//
|
||||
// Defaults changed to true for inbound only in filterUnreachable below.
|
||||
|
||||
Set peers = new HashSet(1);
|
||||
Set<Hash> peers = new HashSet(1);
|
||||
peers.addAll(ctx.profileOrganizer().selectPeersRecentlyRejecting());
|
||||
peers.addAll(ctx.tunnelManager().selectPeersInTooManyTunnels());
|
||||
// if (false && filterUnreachable(ctx, isInbound, isExploratory)) {
|
||||
if (filterUnreachable(ctx, isInbound, isExploratory)) {
|
||||
// NOTE: filterUnreachable returns true for inbound, false for outbound
|
||||
// This is the only use for getPeersByCapability? And the whole set of datastructures in PeerManager?
|
||||
List<Hash> caps = ctx.peerManager().getPeersByCapability(Router.CAPABILITY_UNREACHABLE);
|
||||
if (caps != null)
|
||||
@@ -189,6 +203,7 @@ public abstract class TunnelPeerSelector {
|
||||
peers.addAll(caps);
|
||||
}
|
||||
if (filterSlow(ctx, isInbound, isExploratory)) {
|
||||
// NOTE: filterSlow always returns true
|
||||
Log log = ctx.logManager().getLog(TunnelPeerSelector.class);
|
||||
char excl[] = getExcludeCaps(ctx);
|
||||
if (excl != null) {
|
||||
@@ -301,6 +316,7 @@ public abstract class TunnelPeerSelector {
|
||||
return peers;
|
||||
}
|
||||
|
||||
/** warning, this is also called by ProfileOrganizer.isSelectable() */
|
||||
public static boolean shouldExclude(RouterContext ctx, RouterInfo peer) {
|
||||
Log log = ctx.logManager().getLog(TunnelPeerSelector.class);
|
||||
return shouldExclude(ctx, log, peer, getExcludeCaps(ctx));
|
||||
@@ -318,6 +334,10 @@ public abstract class TunnelPeerSelector {
|
||||
}
|
||||
|
||||
private static final long DONT_EXCLUDE_PERIOD = 15*60*1000;
|
||||
/** 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();
|
||||
if (cap == null) {
|
||||
@@ -340,6 +360,13 @@ public abstract class TunnelPeerSelector {
|
||||
// otherwise, it contains flags we aren't trying to focus on,
|
||||
// so don't exclude it based on published capacity
|
||||
|
||||
// minimum version check
|
||||
String v = peer.getOption("router.version");
|
||||
if (v == null || _versionComparator.compare(v, MIN_VERSION) < 0)
|
||||
return true;
|
||||
|
||||
// uptime is always spoofed to 90m, so just remove all this
|
||||
/******
|
||||
String val = peer.getOption("stat_uptime");
|
||||
if (val != null) {
|
||||
long uptimeMs = 0;
|
||||
@@ -390,6 +417,8 @@ public abstract class TunnelPeerSelector {
|
||||
// not publishing an uptime, so exclude it
|
||||
return true;
|
||||
}
|
||||
******/
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final String PROP_OUTBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE = "router.outboundExploratoryExcludeUnreachable";
|
||||
@@ -403,6 +432,10 @@ public abstract class TunnelPeerSelector {
|
||||
private static final String DEFAULT_INBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE = "true";
|
||||
private static final String 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(RouterContext ctx, boolean isInbound, boolean isExploratory) {
|
||||
boolean def = false;
|
||||
String val = null;
|
||||
@@ -429,6 +462,10 @@ public abstract class TunnelPeerSelector {
|
||||
private static final String PROP_INBOUND_EXPLORATORY_EXCLUDE_SLOW = "router.inboundExploratoryExcludeSlow";
|
||||
private static final String PROP_INBOUND_CLIENT_EXCLUDE_SLOW = "router.inboundClientExcludeSlow";
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@@ -454,7 +491,10 @@ public abstract class TunnelPeerSelector {
|
||||
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? */
|
||||
/**
|
||||
* 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;
|
||||
|
||||
Reference in New Issue
Block a user