propagate from branch 'i2p.i2p.zzz.test' (head 128a31611abc6a88e58133f3bf6a577fe6dd5b1c)

to branch 'i2p.i2p.zzz.test4' (head fa9a871892517271eb2531b433fe80a2a713be9c)
This commit is contained in:
zzz
2010-10-05 13:06:16 +00:00
151 changed files with 17311 additions and 8368 deletions

View File

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

View File

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

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

View File

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

View File

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

View File

@@ -43,7 +43,6 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade {
public ClientManagerFacadeImpl(RouterContext context) {
_context = context;
_manager = null;
_log.debug("Client manager facade created");
}

View File

@@ -33,7 +33,6 @@ class LeaseRequestState {
_onFailed = onFailed;
_expiration = expiration;
_requestedLeaseSet = requested;
_successful = false;
}
/** created lease set from client */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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