forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p' (head cbca70618d2083a5fcdead2390e9d30060080e74)
to branch 'i2p.i2p.zzz.test' (head 1affab2e83613f326d269370de6e5aed40ecae52)
This commit is contained in:
@@ -91,7 +91,7 @@ public class Blocklist {
|
||||
static final String BLOCKLIST_FILE_DEFAULT = "blocklist.txt";
|
||||
|
||||
public void startup() {
|
||||
if (! Boolean.valueOf(_context.getProperty(PROP_BLOCKLIST_ENABLED, "true")).booleanValue())
|
||||
if (! _context.getBooleanPropertyDefaultTrue(PROP_BLOCKLIST_ENABLED))
|
||||
return;
|
||||
String file = _context.getProperty(PROP_BLOCKLIST_FILE, BLOCKLIST_FILE_DEFAULT);
|
||||
// Maybe someday we'll read in multiple files and merge them
|
||||
@@ -849,6 +849,7 @@ public class Blocklist {
|
||||
return Translate.getString(key, _context, BUNDLE_NAME);
|
||||
}
|
||||
|
||||
/****
|
||||
public static void main(String args[]) {
|
||||
Blocklist b = new Blocklist();
|
||||
if ( (args != null) && (args.length == 1) )
|
||||
@@ -861,4 +862,5 @@ public class Blocklist {
|
||||
System.out.println("Testing " + tests[i] + " returns " + b.isBlocklisted(tests[i]));
|
||||
}
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
||||
@@ -20,12 +20,8 @@ public class ClientTunnelSettings {
|
||||
private TunnelPoolSettings _outboundSettings;
|
||||
|
||||
public ClientTunnelSettings() {
|
||||
_inboundSettings = new TunnelPoolSettings();
|
||||
_inboundSettings.setIsInbound(true);
|
||||
_inboundSettings.setIsExploratory(false);
|
||||
_outboundSettings = new TunnelPoolSettings();
|
||||
_outboundSettings.setIsInbound(false);
|
||||
_outboundSettings.setIsExploratory(false);
|
||||
_inboundSettings = new TunnelPoolSettings(false, true);
|
||||
_outboundSettings = new TunnelPoolSettings(false, false);
|
||||
}
|
||||
|
||||
public TunnelPoolSettings getInboundSettings() { return _inboundSettings; }
|
||||
|
||||
@@ -348,7 +348,7 @@ public class InNetMessagePool implements Service {
|
||||
_dispatchThreaded = DEFAULT_DISPATCH_THREADED;
|
||||
String threadedStr = _context.getProperty(PROP_DISPATCH_THREADED);
|
||||
if (threadedStr != null) {
|
||||
_dispatchThreaded = Boolean.valueOf(threadedStr).booleanValue();
|
||||
_dispatchThreaded = Boolean.parseBoolean(threadedStr);
|
||||
}
|
||||
if (_dispatchThreaded) {
|
||||
_context.statManager().createRateStat("pool.dispatchDataTime", "How long a tunnel dispatch takes", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
|
||||
@@ -80,7 +80,7 @@ public class MessageHistory {
|
||||
String getFilename() { return _historyFile; }
|
||||
|
||||
private void updateSettings() {
|
||||
_doLog = Boolean.valueOf(_context.getProperty(PROP_KEEP_MESSAGE_HISTORY)).booleanValue();
|
||||
_doLog = _context.getBooleanProperty(PROP_KEEP_MESSAGE_HISTORY);
|
||||
_historyFile = _context.getProperty(PROP_MESSAGE_HISTORY_FILENAME, DEFAULT_MESSAGE_HISTORY_FILENAME);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ import net.i2p.util.FortunaRandomSource;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SimpleByteCache;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
@@ -176,7 +177,7 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
|
||||
// Do we copy all the data files to the new directory? default false
|
||||
String migrate = System.getProperty("i2p.dir.migrate");
|
||||
boolean migrateFiles = Boolean.valueOf(migrate).booleanValue();
|
||||
boolean migrateFiles = Boolean.parseBoolean(migrate);
|
||||
String userDir = WorkingDir.getWorkingDir(envProps, migrateFiles);
|
||||
|
||||
// Use the router.config file specified in the router.configLocation property
|
||||
@@ -196,7 +197,7 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
envProps.putAll(_config);
|
||||
|
||||
// This doesn't work, guess it has to be in the static block above?
|
||||
// if (Boolean.valueOf(envProps.getProperty("router.disableIPv6")).booleanValue())
|
||||
// if (Boolean.parseBoolean(envProps.getProperty("router.disableIPv6")))
|
||||
// System.setProperty("java.net.preferIPv4Stack", "true");
|
||||
|
||||
if (envProps.getProperty("i2p.dir.config") == null)
|
||||
@@ -630,7 +631,7 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
return true;
|
||||
String h = _context.getProperty(PROP_HIDDEN_HIDDEN);
|
||||
if (h != null)
|
||||
return Boolean.valueOf(h).booleanValue();
|
||||
return Boolean.parseBoolean(h);
|
||||
return _context.commSystem().isInBadCountry();
|
||||
}
|
||||
|
||||
@@ -976,44 +977,24 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
* Save the current config options (returning true if save was
|
||||
* successful, false otherwise)
|
||||
*
|
||||
* Note that unlike DataHelper.storeProps(),
|
||||
* this does escape the \r or \n that are unescaped in DataHelper.loadProps().
|
||||
* Note that the escaping of \r or \n was probably a mistake and should be taken out.
|
||||
*
|
||||
* Synchronized with file read in getConfig()
|
||||
*/
|
||||
public boolean saveConfig() {
|
||||
synchronized(_configFileLock) {
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new SecureFileOutputStream(_configFilename);
|
||||
StringBuilder buf = new StringBuilder(8*1024);
|
||||
buf.append("# NOTE: This I2P config file must use UTF-8 encoding\n");
|
||||
TreeSet ordered = new TreeSet(_config.keySet());
|
||||
for (Iterator iter = ordered.iterator() ; iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.get(key);
|
||||
// Escape line breaks before saving.
|
||||
// Remember: "\" needs escaping both for regex and string.
|
||||
// NOOO - see comments in DataHelper
|
||||
//val = val.replaceAll("\\r","\\\\r");
|
||||
//val = val.replaceAll("\\n","\\\\n");
|
||||
buf.append(key).append('=').append(val).append('\n');
|
||||
}
|
||||
fos.write(buf.toString().getBytes("UTF-8"));
|
||||
} catch (IOException ioe) {
|
||||
try {
|
||||
Properties ordered = new OrderedProperties();
|
||||
synchronized(_configFileLock) {
|
||||
ordered.putAll(_config);
|
||||
DataHelper.storeProps(ordered, new File(_configFilename));
|
||||
}
|
||||
} catch (Exception ioe) {
|
||||
// warning, _log will be null when called from constructor
|
||||
if (_log != null)
|
||||
_log.error("Error saving the config to " + _configFilename, ioe);
|
||||
else
|
||||
System.err.println("Error saving the config to " + _configFilename + ": " + ioe);
|
||||
return false;
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,10 +18,10 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 23;
|
||||
public final static long BUILD = 13;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "-rc-test";
|
||||
public final static String EXTRA = "";
|
||||
public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA;
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Router version: " + FULL_VERSION);
|
||||
|
||||
@@ -21,8 +21,8 @@ public class TunnelPoolSettings {
|
||||
private int _length;
|
||||
private int _lengthVariance;
|
||||
private int _lengthOverride;
|
||||
private boolean _isInbound;
|
||||
private boolean _isExploratory;
|
||||
private final boolean _isInbound;
|
||||
private final boolean _isExploratory;
|
||||
private boolean _allowZeroHop;
|
||||
private int _IPRestriction;
|
||||
private final Properties _unknownOptions;
|
||||
@@ -54,7 +54,9 @@ public class TunnelPoolSettings {
|
||||
public static final boolean DEFAULT_ALLOW_ZERO_HOP = true;
|
||||
public static final int DEFAULT_IP_RESTRICTION = 2; // class B (/16)
|
||||
|
||||
public TunnelPoolSettings() {
|
||||
public TunnelPoolSettings(boolean isExploratory, boolean isInbound) {
|
||||
_isExploratory = isExploratory;
|
||||
_isInbound = isInbound;
|
||||
_quantity = DEFAULT_QUANTITY;
|
||||
_backupQuantity = DEFAULT_BACKUP_QUANTITY;
|
||||
// _rebuildPeriod = DEFAULT_REBUILD_PERIOD;
|
||||
@@ -130,11 +132,9 @@ public class TunnelPoolSettings {
|
||||
|
||||
/** is this an inbound tunnel? */
|
||||
public boolean isInbound() { return _isInbound; }
|
||||
public void setIsInbound(boolean isInbound) { _isInbound = isInbound; }
|
||||
|
||||
/** is this an exploratory tunnel (or a client tunnel) */
|
||||
public boolean isExploratory() { return _isExploratory; }
|
||||
public void setIsExploratory(boolean isExploratory) { _isExploratory = isExploratory; }
|
||||
|
||||
// Duration is hardcoded
|
||||
//public int getDuration() { return _duration; }
|
||||
@@ -237,7 +237,7 @@ public class TunnelPoolSettings {
|
||||
|
||||
private static final boolean getBoolean(String str, boolean defaultValue) {
|
||||
if (str == null) return defaultValue;
|
||||
boolean v = Boolean.valueOf(str).booleanValue() ||
|
||||
boolean v = Boolean.parseBoolean(str) ||
|
||||
(str != null && "YES".equals(str.toUpperCase(Locale.US)));
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -308,6 +308,11 @@ class ClientConnectionRunner {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error writing out the disconnect message: " + ime);
|
||||
}
|
||||
// give it a little time to get sent out...
|
||||
// even better would be to have stopRunning() flush it?
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
} catch (InterruptedException ie) {}
|
||||
stopRunning();
|
||||
}
|
||||
|
||||
|
||||
@@ -326,7 +326,7 @@ class ClientManager {
|
||||
if (destHash == null) return true;
|
||||
ClientConnectionRunner runner = getRunner(destHash);
|
||||
if (runner == null) return true;
|
||||
return !Boolean.valueOf(runner.getConfig().getOptions().getProperty(ClientManagerFacade.PROP_CLIENT_ONLY)).booleanValue();
|
||||
return !Boolean.parseBoolean(runner.getConfig().getOptions().getProperty(ClientManagerFacade.PROP_CLIENT_ONLY));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -169,7 +169,7 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
|
||||
}
|
||||
|
||||
// Auth, since 0.8.2
|
||||
if (_enforceAuth && Boolean.valueOf(_context.getProperty("i2cp.auth")).booleanValue()) {
|
||||
if (_enforceAuth && _context.getBooleanProperty("i2cp.auth")) {
|
||||
String configUser = _context.getProperty("i2cp.username");
|
||||
String configPW = _context.getProperty("i2cp.password");
|
||||
if (configUser != null && configPW != null) {
|
||||
|
||||
@@ -452,7 +452,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
// Per-message flag == false overrides session option which is default true
|
||||
String allow = _clientMessage.getSenderConfig().getOptions().getProperty(BUNDLE_REPLY_LEASESET);
|
||||
boolean allowLeaseBundle = SendMessageOptions.getSendLeaseSet(sendFlags) &&
|
||||
(allow == null || Boolean.valueOf(allow).booleanValue());
|
||||
(allow == null || Boolean.parseBoolean(allow));
|
||||
if (allowLeaseBundle) {
|
||||
// If we want an ack, bundle a leaseSet...
|
||||
//replyLeaseSet = getReplyLeaseSet(wantACK);
|
||||
|
||||
@@ -25,7 +25,7 @@ class FloodfillMonitorJob extends JobImpl {
|
||||
private static final int REQUEUE_DELAY = 60*60*1000;
|
||||
private static final long MIN_UPTIME = 2*60*60*1000;
|
||||
private static final long MIN_CHANGE_DELAY = 6*60*60*1000;
|
||||
private static final int MIN_FF = 300;
|
||||
private static final int MIN_FF = 360;
|
||||
private static final int MAX_FF = 999999;
|
||||
private static final String PROP_FLOODFILL_PARTICIPANT = "router.floodfillParticipant";
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class HarvesterJob extends JobImpl {
|
||||
public static final String PROP_ENABLED = "netDb.shouldHarvest";
|
||||
|
||||
private boolean harvestDirectly() {
|
||||
return Boolean.valueOf(getContext().getProperty("netDb.harvestDirectly", "false")).booleanValue();
|
||||
return getContext().getBooleanProperty("netDb.harvestDirectly");
|
||||
}
|
||||
|
||||
public HarvesterJob(RouterContext context, KademliaNetworkDatabaseFacade facade) {
|
||||
|
||||
@@ -220,7 +220,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
||||
}
|
||||
String enforce = _context.getProperty(PROP_ENFORCE_NETID);
|
||||
if (enforce != null)
|
||||
_enforceNetId = Boolean.valueOf(enforce).booleanValue();
|
||||
_enforceNetId = Boolean.parseBoolean(enforce);
|
||||
else
|
||||
_enforceNetId = DEFAULT_ENFORCE_NETID;
|
||||
_ds.restart();
|
||||
@@ -246,7 +246,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
||||
String dbDir = _context.getProperty(PROP_DB_DIR, DEFAULT_DB_DIR);
|
||||
String enforce = _context.getProperty(PROP_ENFORCE_NETID);
|
||||
if (enforce != null)
|
||||
_enforceNetId = Boolean.valueOf(enforce).booleanValue();
|
||||
_enforceNetId = Boolean.parseBoolean(enforce);
|
||||
else
|
||||
_enforceNetId = DEFAULT_ENFORCE_NETID;
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ class SearchJob extends JobImpl {
|
||||
// Returning false essentially enables kademlia as a backup to floodfill for search responses.
|
||||
if (FloodfillNetworkDatabaseFacade.floodfillEnabled(ctx))
|
||||
return false;
|
||||
return Boolean.valueOf(ctx.getProperty("netDb.floodfillOnly", DEFAULT_FLOODFILL_ONLY + "")).booleanValue();
|
||||
return ctx.getProperty("netDb.floodfillOnly", DEFAULT_FLOODFILL_ONLY);
|
||||
}
|
||||
|
||||
/***
|
||||
|
||||
@@ -527,7 +527,7 @@ public class Reseeder {
|
||||
|
||||
/******
|
||||
public static void main(String args[]) {
|
||||
if ( (args != null) && (args.length == 1) && (!Boolean.valueOf(args[0]).booleanValue()) ) {
|
||||
if ( (args != null) && (args.length == 1) && (!Boolean.parseBoolean(args[0])) ) {
|
||||
System.out.println("Not reseeding, as requested");
|
||||
return; // not reseeding on request
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class BootCommSystemJob extends JobImpl {
|
||||
|
||||
private void startupDb() {
|
||||
Job bootDb = new BootNetworkDbJob(getContext());
|
||||
boolean useTrusted = Boolean.valueOf(getContext().getProperty(PROP_USE_TRUSTED_LINKS)).booleanValue();
|
||||
boolean useTrusted = getContext().getBooleanProperty(PROP_USE_TRUSTED_LINKS);
|
||||
if (useTrusted) {
|
||||
_log.debug("Using trusted links...");
|
||||
getContext().jobQueue().addJob(new BuildTrustedLinksJob(getContext(), bootDb));
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package net.i2p.router.tasks;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.util.EventLog;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@@ -12,30 +15,47 @@ import net.i2p.util.Log;
|
||||
*/
|
||||
public class OOMListener implements I2PThread.OOMEventListener {
|
||||
private final RouterContext _context;
|
||||
private final AtomicBoolean _wasCalled = new AtomicBoolean();
|
||||
|
||||
public OOMListener(RouterContext ctx) {
|
||||
_context = ctx;
|
||||
}
|
||||
|
||||
public void outOfMemory(OutOfMemoryError oom) {
|
||||
// boost priority to help us shut down
|
||||
Thread.currentThread().setPriority(Thread.MAX_PRIORITY - 1);
|
||||
Router.clearCaches();
|
||||
Log log = _context.logManager().getLog(Router.class);
|
||||
log.log(Log.CRIT, "Thread ran out of memory, shutting down I2P", oom);
|
||||
// prevent multiple parallel shutdowns (when you OOM, you OOM a lot...)
|
||||
if (_context.router().isFinalShutdownInProgress())
|
||||
return;
|
||||
for (int i = 0; i < 5; i++) { // try this 5 times, in case it OOMs
|
||||
try {
|
||||
log.log(Log.CRIT, "free mem: " + Runtime.getRuntime().freeMemory() +
|
||||
" total mem: " + Runtime.getRuntime().totalMemory());
|
||||
break; // w00t
|
||||
} catch (OutOfMemoryError oome) {
|
||||
// gobble
|
||||
}
|
||||
}
|
||||
log.log(Log.CRIT, "To prevent future shutdowns, increase wrapper.java.maxmemory in $I2P/wrapper.config");
|
||||
try {
|
||||
// prevent multiple parallel shutdowns (when you OOM, you OOM a lot...)
|
||||
if (_context.router().isFinalShutdownInProgress())
|
||||
return;
|
||||
} catch (OutOfMemoryError oome) {}
|
||||
try {
|
||||
// Only do this once
|
||||
if (_wasCalled.getAndSet(true))
|
||||
return;
|
||||
} catch (OutOfMemoryError oome) {}
|
||||
|
||||
try {
|
||||
// boost priority to help us shut down
|
||||
// this may or may not do anything...
|
||||
Thread.currentThread().setPriority(Thread.MAX_PRIORITY - 1);
|
||||
} catch (OutOfMemoryError oome) {}
|
||||
try {
|
||||
Router.clearCaches();
|
||||
} catch (OutOfMemoryError oome) {}
|
||||
Log log = null;
|
||||
try {
|
||||
log = _context.logManager().getLog(Router.class);
|
||||
log.log(Log.CRIT, "Thread ran out of memory, shutting down I2P", oom);
|
||||
log.log(Log.CRIT, "free mem: " + Runtime.getRuntime().freeMemory() +
|
||||
" total mem: " + Runtime.getRuntime().totalMemory());
|
||||
if (_context.hasWrapper())
|
||||
log.log(Log.CRIT, "To prevent future shutdowns, increase wrapper.java.maxmemory in $I2P/wrapper.config");
|
||||
} catch (OutOfMemoryError oome) {}
|
||||
try {
|
||||
ThreadDump.dump(_context, 1);
|
||||
} catch (OutOfMemoryError oome) {}
|
||||
try {
|
||||
_context.router().eventLog().addEvent(EventLog.OOM);
|
||||
} catch (OutOfMemoryError oome) {}
|
||||
_context.router().shutdown(Router.EXIT_OOM);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.i2p.router.tasks;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterClock;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.util.EventLog;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@@ -16,6 +17,7 @@ public class Restarter implements Runnable {
|
||||
}
|
||||
|
||||
public void run() {
|
||||
_context.router().eventLog().addEvent(EventLog.SOFT_RESTART);
|
||||
Log log = _context.logManager().getLog(Router.class);
|
||||
log.error("Stopping the router for a restart...");
|
||||
log.logAlways(Log.WARN, "Stopping the client manager");
|
||||
|
||||
@@ -64,7 +64,7 @@ public class RouterWatchdog implements Runnable {
|
||||
|
||||
private boolean shutdownOnHang() {
|
||||
// prop default false
|
||||
if (!Boolean.valueOf(_context.getProperty("watchdog.haltOnHang")).booleanValue())
|
||||
if (!_context.getBooleanProperty("watchdog.haltOnHang"))
|
||||
return false;
|
||||
|
||||
// Client manager starts complaining after 10 minutes, and we run every minute,
|
||||
@@ -113,16 +113,7 @@ public class RouterWatchdog implements Runnable {
|
||||
// This works on linux...
|
||||
// It won't on windows, and we can't call i2prouter.bat either, it does something
|
||||
// completely different...
|
||||
if (_context.hasWrapper() && !SystemVersion.isWindows()) {
|
||||
ShellCommand sc = new ShellCommand();
|
||||
File i2pr = new File(_context.getBaseDir(), "i2prouter");
|
||||
String[] args = new String[2];
|
||||
args[0] = i2pr.getAbsolutePath();
|
||||
args[1] = "dump";
|
||||
boolean success = sc.executeSilentAndWaitTimed(args, 10);
|
||||
if (success)
|
||||
_log.log(Log.CRIT, "Threads dumped to wrapper log");
|
||||
}
|
||||
ThreadDump.dump(_context, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
router/java/src/net/i2p/router/tasks/ThreadDump.java
Normal file
43
router/java/src/net/i2p/router/tasks/ThreadDump.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package net.i2p.router.tasks;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.ShellCommand;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
* Only works with wrapper on non-windows platforms
|
||||
*
|
||||
* @since 0.9.3 moved from RouterWatchdog
|
||||
*/
|
||||
abstract class ThreadDump {
|
||||
|
||||
/**
|
||||
* Signal the wrapper to asynchronously dump threads to wrapper.log.
|
||||
* It waits for the signal to complete (which should be fast)
|
||||
* but does not wait for the dump itself.
|
||||
*
|
||||
* @param secondsToWait if <= 0, don't wait
|
||||
* @return success, false if windows or no wrapper, true if secondsToWait <= 0,
|
||||
false if timed out, dump result otherwise
|
||||
*/
|
||||
public static boolean dump(I2PAppContext context, int secondsToWait) {
|
||||
if (SystemVersion.isWindows() || !context.hasWrapper())
|
||||
return false;
|
||||
ShellCommand sc = new ShellCommand();
|
||||
File i2pr = new File(context.getBaseDir(), "i2prouter");
|
||||
String[] args = new String[2];
|
||||
args[0] = i2pr.getAbsolutePath();
|
||||
args[1] = "dump";
|
||||
boolean success = sc.executeSilentAndWaitTimed(args, secondsToWait);
|
||||
if (secondsToWait <= 0)
|
||||
success = true;
|
||||
if (success) {
|
||||
Log log = context.logManager().getLog(ThreadDump.class);
|
||||
log.log(Log.CRIT, "Threads dumped to wrapper log");
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public class RouterTimestamper extends Timestamper {
|
||||
// so the property must be set at startup.
|
||||
// We still need to be instantiated since the router calls clock().getTimestamper().waitForInitialization()
|
||||
String disabled = ctx.getProperty(PROP_DISABLED, DEFAULT_DISABLED);
|
||||
if (Boolean.valueOf(disabled).booleanValue()) {
|
||||
if (Boolean.parseBoolean(disabled)) {
|
||||
_initialized = true;
|
||||
return;
|
||||
}
|
||||
@@ -321,7 +321,7 @@ public class RouterTimestamper extends Timestamper {
|
||||
_context.getProperty(PROP_QUERY_FREQUENCY, DEFAULT_QUERY_FREQUENCY));
|
||||
|
||||
String disabled = _context.getProperty(PROP_DISABLED, DEFAULT_DISABLED);
|
||||
_disabled = Boolean.valueOf(disabled).booleanValue();
|
||||
_disabled = Boolean.parseBoolean(disabled);
|
||||
|
||||
_concurringServers = Math.min(4, Math.max(1,
|
||||
_context.getProperty(PROP_CONCURRING_SERVERS, DEFAULT_CONCURRING_SERVERS)));
|
||||
|
||||
@@ -331,7 +331,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("old: " + ohost + " config: " + name + " auto: " + enabled + " status: " + status);
|
||||
if (enabled.equals("always") ||
|
||||
(Boolean.valueOf(enabled).booleanValue() && status == STATUS_OK)) {
|
||||
(Boolean.parseBoolean(enabled) && status == STATUS_OK)) {
|
||||
String nhost = UDPAddr.getOption(UDPAddress.PROP_HOST);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("old: " + ohost + " config: " + name + " new: " + nhost);
|
||||
@@ -354,7 +354,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
|
||||
changed = true;
|
||||
} else if (ohost == null || ohost.length() <= 0) {
|
||||
return;
|
||||
} else if (Boolean.valueOf(enabled).booleanValue() && status != STATUS_OK) {
|
||||
} else if (Boolean.parseBoolean(enabled) && status != STATUS_OK) {
|
||||
// UDP transitioned to not-OK, turn off NTCP address
|
||||
// This will commonly happen at startup if we were initially OK
|
||||
// because UPnP was successful, but a subsequent SSU Peer Test determines
|
||||
|
||||
@@ -91,7 +91,7 @@ class GeoIP {
|
||||
*/
|
||||
/******
|
||||
public void lookup() {
|
||||
if (! Boolean.valueOf(_context.getProperty(PROP_GEOIP_ENABLED, "true")).booleanValue()) {
|
||||
if (! _context.getBooleanPropertyDefaultTrue(PROP_GEOIP_ENABLED)) {
|
||||
_pendingSearch.clear();
|
||||
return;
|
||||
}
|
||||
@@ -105,7 +105,7 @@ class GeoIP {
|
||||
* Results will be added to the table and available via get() after completion.
|
||||
*/
|
||||
public void blockingLookup() {
|
||||
if (! Boolean.valueOf(_context.getProperty(PROP_GEOIP_ENABLED, "true")).booleanValue()) {
|
||||
if (! _context.getBooleanPropertyDefaultTrue(PROP_GEOIP_ENABLED)) {
|
||||
_pendingSearch.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -362,7 +362,7 @@ public class TransportManager implements TransportEventListener {
|
||||
}
|
||||
// Use UDP port for NTCP too - see comment in NTCPTransport.getRequestedPort() for why this is here
|
||||
if (t.getStyle().equals(NTCPTransport.STYLE) && port <= 0 &&
|
||||
Boolean.valueOf(_context.getProperty(CommSystemFacadeImpl.PROP_I2NP_NTCP_AUTO_PORT)).booleanValue()) {
|
||||
_context.getBooleanProperty(CommSystemFacadeImpl.PROP_I2NP_NTCP_AUTO_PORT)) {
|
||||
Transport udp = getTransport(UDPTransport.STYLE);
|
||||
if (udp != null)
|
||||
port = t.getRequestedPort();
|
||||
@@ -545,7 +545,7 @@ public class TransportManager implements TransportEventListener {
|
||||
" ").append(_("The number of pending sends which exceed congestion window")).append("<br>\n" +
|
||||
"<b id=\"def.ssthresh\">SST</b>: ").append(_("The slow start threshold")).append("<br>\n" +
|
||||
"<b id=\"def.rtt\">RTT</b>: ").append(_("The round trip time in milliseconds")).append("<br>\n" +
|
||||
"<b id=\"def.dev\">").append(_("Dev")).append("</b>: ").append(_("The standard deviation of the round trip time in milliseconds")).append("<br>\n" +
|
||||
//"<b id=\"def.dev\">").append(_("Dev")).append("</b>: ").append(_("The standard deviation of the round trip time in milliseconds")).append("<br>\n" +
|
||||
"<b id=\"def.rto\">RTO</b>: ").append(_("The retransmit timeout in milliseconds")).append("<br>\n" +
|
||||
"<b id=\"def.mtu\">MTU</b>: ").append(_("Current maximum send packet size / estimated maximum receive packet size (bytes)")).append("<br>\n" +
|
||||
"<b id=\"def.send\">").append(_("TX")).append("</b>: ").append(_("The total number of packets sent to the peer")).append("<br>\n" +
|
||||
|
||||
@@ -78,7 +78,7 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
|
||||
private volatile boolean thinksWeAreDoubleNatted = false;
|
||||
|
||||
/** List of ports we want to forward */
|
||||
private Set<ForwardPort> portsToForward;
|
||||
private final Set<ForwardPort> portsToForward;
|
||||
/** List of ports we have actually forwarded */
|
||||
private final Set<ForwardPort> portsForwarded;
|
||||
/** Callback to call when a forward fails or succeeds */
|
||||
@@ -88,13 +88,14 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
|
||||
super();
|
||||
_context = context;
|
||||
_log = _context.logManager().getLog(UPnP.class);
|
||||
portsToForward = new HashSet<ForwardPort>();
|
||||
portsForwarded = new HashSet<ForwardPort>();
|
||||
addDeviceChangeListener(this);
|
||||
}
|
||||
|
||||
public boolean runPlugin() {
|
||||
public synchronized boolean runPlugin() {
|
||||
synchronized(lock) {
|
||||
portsToForward = null;
|
||||
portsToForward.clear();
|
||||
}
|
||||
return super.start();
|
||||
}
|
||||
@@ -102,9 +103,9 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
|
||||
/**
|
||||
* WARNING - Blocking up to 2 seconds
|
||||
*/
|
||||
public void terminate() {
|
||||
public synchronized void terminate() {
|
||||
synchronized(lock) {
|
||||
portsToForward = null;
|
||||
portsToForward.clear();
|
||||
}
|
||||
// this gets spun off in a thread...
|
||||
unregisterPortMappings();
|
||||
@@ -221,9 +222,10 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
|
||||
private void registerPortMappings() {
|
||||
Set ports;
|
||||
synchronized(lock) {
|
||||
ports = portsToForward;
|
||||
ports = new HashSet(portsForwarded);
|
||||
}
|
||||
if(ports == null) return;
|
||||
if (ports.isEmpty())
|
||||
return;
|
||||
registerPorts(ports);
|
||||
}
|
||||
|
||||
@@ -281,6 +283,8 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
|
||||
synchronized(lock) {
|
||||
ports = new HashSet(portsForwarded);
|
||||
}
|
||||
if (ports.isEmpty())
|
||||
return;
|
||||
this.unregisterPorts(ports);
|
||||
}
|
||||
|
||||
@@ -528,17 +532,15 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
|
||||
if(upstreamMaxBitRate > 0)
|
||||
sb.append("<br>").append(_("UPnP reports the maximum upstream bit rate is {0}bits/sec", DataHelper.formatSize2(upstreamMaxBitRate)));
|
||||
synchronized(lock) {
|
||||
if(portsToForward != null) {
|
||||
for(ForwardPort port : portsToForward) {
|
||||
sb.append("<br>");
|
||||
if(portsForwarded.contains(port))
|
||||
// {0} is TCP or UDP
|
||||
// {1,number,#####} prevents 12345 from being output as 12,345 in the English locale.
|
||||
// If you want the digit separator in your locale, translate as {1}.
|
||||
sb.append(_("{0} port {1,number,#####} was successfully forwarded by UPnP.", protoToString(port.protocol), port.portNumber));
|
||||
else
|
||||
sb.append(_("{0} port {1,number,#####} was not forwarded by UPnP.", protoToString(port.protocol), port.portNumber));
|
||||
}
|
||||
for(ForwardPort port : portsToForward) {
|
||||
sb.append("<br>");
|
||||
if(portsForwarded.contains(port))
|
||||
// {0} is TCP or UDP
|
||||
// {1,number,#####} prevents 12345 from being output as 12,345 in the English locale.
|
||||
// If you want the digit separator in your locale, translate as {1}.
|
||||
sb.append(_("{0} port {1,number,#####} was successfully forwarded by UPnP.", protoToString(port.protocol), port.portNumber));
|
||||
else
|
||||
sb.append(_("{0} port {1,number,#####} was not forwarded by UPnP.", protoToString(port.protocol), port.portNumber));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,13 +728,13 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
|
||||
_log.error("ForwardPortCallback changed from "+forwardCallback+" to "+cb+" - using new value, but this is very strange!");
|
||||
}
|
||||
forwardCallback = cb;
|
||||
if(portsToForward == null || portsToForward.isEmpty()) {
|
||||
portsToForward = ports;
|
||||
if (portsToForward.isEmpty()) {
|
||||
portsToForward.addAll(ports);
|
||||
portsToForwardNow = ports;
|
||||
portsToDumpNow = null;
|
||||
} else if(ports.isEmpty()) {
|
||||
portsToDumpNow = portsToForward;
|
||||
portsToForward = ports;
|
||||
portsToForward.clear();
|
||||
portsToForwardNow = null;
|
||||
} else {
|
||||
// Some ports to keep, some ports to dump
|
||||
@@ -760,7 +762,8 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
|
||||
portsToDumpNow.add(port);
|
||||
}
|
||||
}
|
||||
portsToForward = ports;
|
||||
portsToForward.clear();
|
||||
portsToForward.addAll(ports);
|
||||
}
|
||||
if(_router == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@@ -768,9 +771,9 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
|
||||
return; // When one is found, we will do the forwards
|
||||
}
|
||||
}
|
||||
if(portsToDumpNow != null)
|
||||
if(portsToDumpNow != null && !portsToDumpNow.isEmpty())
|
||||
unregisterPorts(portsToDumpNow);
|
||||
if(portsToForwardNow != null)
|
||||
if(portsToForwardNow != null && !portsToForwardNow.isEmpty())
|
||||
registerPorts(portsToForwardNow);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,12 @@ package net.i2p.router.transport;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Addresses;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.Translate;
|
||||
|
||||
@@ -59,15 +61,42 @@ class UPnPManager {
|
||||
_upnpCallback = new UPnPCallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking, may take a while
|
||||
*/
|
||||
public synchronized void start() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("UPnP Start");
|
||||
if (!_isRunning)
|
||||
_isRunning = _upnp.runPlugin();
|
||||
if (!_isRunning)
|
||||
_log.error("UPnP start failed - port conflict?");
|
||||
if (!_isRunning) {
|
||||
long b = _context.clock().now();
|
||||
try {
|
||||
_isRunning = _upnp.runPlugin();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("UPnP runPlugin took " + (_context.clock().now() - b));
|
||||
} catch (Exception e) {
|
||||
// NPE in UPnP (ticket #728), can't let it bring us down
|
||||
_log.error("UPnP error, please report", e);
|
||||
}
|
||||
}
|
||||
if (!_isRunning) {
|
||||
// Do we have a non-loopback, non-broadcast address?
|
||||
// If not, that's why it failed (HTTPServer won't start)
|
||||
Set<String> addrs = Addresses.getAddresses(true, false);
|
||||
addrs.remove("0.0.0.0");
|
||||
for (Iterator<String> iter = addrs.iterator(); iter.hasNext(); ) {
|
||||
if (iter.next().startsWith("127."))
|
||||
iter.remove();
|
||||
}
|
||||
if (addrs.isEmpty())
|
||||
_log.logAlways(Log.WARN, "UPnP start failed - no network connection?");
|
||||
else
|
||||
_log.error("UPnP start failed - port conflict?");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking, may take a while
|
||||
*/
|
||||
public synchronized void stop() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("UPnP Stop");
|
||||
@@ -86,8 +115,17 @@ class UPnPManager {
|
||||
public void update(Map<String, Integer> ports) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("UPnP Update with " + ports.size() + " ports");
|
||||
if (!_isRunning)
|
||||
return;
|
||||
|
||||
//synchronized(this) {
|
||||
// TODO
|
||||
// called too often and may block for too long
|
||||
// may not have started if net was disconnected previously
|
||||
//if (!_isRunning && !ports.isEmpty())
|
||||
// start();
|
||||
if (!_isRunning)
|
||||
return;
|
||||
//}
|
||||
|
||||
Set<ForwardPort> forwards = new HashSet(ports.size());
|
||||
for (Map.Entry<String, Integer> entry : ports.entrySet()) {
|
||||
String style = entry.getKey();
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.RouterIdentity;
|
||||
import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.transport.FIFOBandwidthLimiter;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
@@ -499,7 +500,8 @@ class EventPumper implements Runnable {
|
||||
return;
|
||||
}
|
||||
// BUGFIX for firewalls. --Sponge
|
||||
chan.socket().setKeepAlive(true);
|
||||
if (_context.commSystem().getReachabilityStatus() != CommSystemFacade.STATUS_OK)
|
||||
chan.socket().setKeepAlive(true);
|
||||
|
||||
SelectionKey ckey = chan.register(_selector, SelectionKey.OP_READ);
|
||||
new NTCPConnection(_context, _transport, chan, ckey);
|
||||
@@ -519,7 +521,8 @@ class EventPumper implements Runnable {
|
||||
_log.debug("processing connect for " + con + ": connected? " + connected);
|
||||
if (connected) {
|
||||
// BUGFIX for firewalls. --Sponge
|
||||
chan.socket().setKeepAlive(true);
|
||||
if (_context.commSystem().getReachabilityStatus() != CommSystemFacade.STATUS_OK)
|
||||
chan.socket().setKeepAlive(true);
|
||||
con.setKey(key);
|
||||
con.outboundConnected();
|
||||
_context.statManager().addRateData("ntcp.connectSuccessful", 1);
|
||||
|
||||
@@ -990,9 +990,10 @@ class NTCPConnection {
|
||||
if (clearMessage) {
|
||||
// see synchronization comments in prepareNextWriteFast()
|
||||
synchronized (_outbound) {
|
||||
if (_currentOutbound != null)
|
||||
if (_currentOutbound != null) {
|
||||
msg = _currentOutbound;
|
||||
_currentOutbound = null;
|
||||
_currentOutbound = null;
|
||||
}
|
||||
}
|
||||
if (msg != null) {
|
||||
_lastSendTime = System.currentTimeMillis();
|
||||
@@ -1065,11 +1066,11 @@ class NTCPConnection {
|
||||
//_sendBps15s = (0.955f)*_sendBps15s + (0.045f)*((float)sent*1000f)/(float)time;
|
||||
//_recvBps15s = (0.955f)*_recvBps15s + (0.045f)*((float)recv*1000)/(float)time;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Rates updated to "
|
||||
+ _sendBps + '/' + _recvBps + "Bps in/out "
|
||||
//+ _sendBps15s + "/" + _recvBps15s + "Bps in/out 15s after "
|
||||
+ sent + '/' + recv + " in " + DataHelper.formatDuration(time));
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Rates updated to "
|
||||
// + _sendBps + '/' + _recvBps + "Bps in/out "
|
||||
// //+ _sendBps15s + "/" + _recvBps15s + "Bps in/out 15s after "
|
||||
// + sent + '/' + recv + " in " + DataHelper.formatDuration(time));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
@@ -68,6 +69,13 @@ public class NTCPTransport extends TransportImpl {
|
||||
private long _lastBadSkew;
|
||||
private static final long[] RATES = { 10*60*1000 };
|
||||
|
||||
/**
|
||||
* To prevent trouble. To be raised to 1024 in 0.9.4.
|
||||
*
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private static final int MIN_PEER_PORT = 500;
|
||||
|
||||
// Opera doesn't have the char, TODO check UA
|
||||
//private static final String THINSP = " / ";
|
||||
private static final String THINSP = " / ";
|
||||
@@ -140,7 +148,7 @@ public class NTCPTransport extends TransportImpl {
|
||||
_context.statManager().createRateStat("ntcp.writeError", "", "ntcp", RATES);
|
||||
_establishing = new ConcurrentHashSet(16);
|
||||
_conLock = new Object();
|
||||
_conByIdent = new HashMap(64);
|
||||
_conByIdent = new ConcurrentHashMap(64);
|
||||
|
||||
_finisher = new NTCPSendFinisher(ctx, this);
|
||||
|
||||
@@ -160,7 +168,7 @@ public class NTCPTransport extends TransportImpl {
|
||||
_context.statManager().addRateData("ntcp.inboundEstablished", 1);
|
||||
markReachable(con.getRemotePeer().calculateHash(), true);
|
||||
//_context.shitlist().unshitlistRouter(con.getRemotePeer().calculateHash());
|
||||
NTCPConnection old = null;
|
||||
NTCPConnection old;
|
||||
synchronized (_conLock) {
|
||||
old = _conByIdent.put(con.getRemotePeer().calculateHash(), con);
|
||||
}
|
||||
@@ -263,6 +271,7 @@ public class NTCPTransport extends TransportImpl {
|
||||
public void afterSend(OutNetMessage msg, boolean sendSuccessful, boolean allowRequeue, long msToSend) {
|
||||
super.afterSend(msg, sendSuccessful, allowRequeue, msToSend);
|
||||
}
|
||||
|
||||
public TransportBid bid(RouterInfo toAddress, long dataSize) {
|
||||
if (!isAlive())
|
||||
return null;
|
||||
@@ -299,7 +308,7 @@ public class NTCPTransport extends TransportImpl {
|
||||
return null;
|
||||
}
|
||||
byte[] ip = addr.getIP();
|
||||
if ( (addr.getPort() <= 0) || (ip == null) ) {
|
||||
if ( (addr.getPort() < MIN_PEER_PORT) || (ip == null) ) {
|
||||
_context.statManager().addRateData("ntcp.connectFailedInvalidPort", 1);
|
||||
markUnreachable(peer);
|
||||
//_context.shitlist().shitlistRouter(toAddress.getIdentity().calculateHash(), "Invalid NTCP address", STYLE);
|
||||
@@ -354,26 +363,23 @@ public class NTCPTransport extends TransportImpl {
|
||||
|
||||
@Override
|
||||
public boolean isEstablished(Hash dest) {
|
||||
synchronized (_conLock) {
|
||||
NTCPConnection con = _conByIdent.get(dest);
|
||||
return (con != null) && con.isEstablished() && !con.isClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBacklogged(Hash dest) {
|
||||
synchronized (_conLock) {
|
||||
NTCPConnection con = _conByIdent.get(dest);
|
||||
return (con != null) && con.isEstablished() && con.tooBacklogged();
|
||||
}
|
||||
}
|
||||
|
||||
void removeCon(NTCPConnection con) {
|
||||
NTCPConnection removed = null;
|
||||
synchronized (_conLock) {
|
||||
RouterIdentity ident = con.getRemotePeer();
|
||||
if (ident != null)
|
||||
RouterIdentity ident = con.getRemotePeer();
|
||||
if (ident != null) {
|
||||
synchronized (_conLock) {
|
||||
removed = _conByIdent.remove(ident.calculateHash());
|
||||
}
|
||||
}
|
||||
if ( (removed != null) && (removed != con) ) {// multiple cons, close 'em both
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@@ -388,19 +394,17 @@ public class NTCPTransport extends TransportImpl {
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public int countActivePeers() { synchronized (_conLock) { return _conByIdent.size(); } }
|
||||
public int countActivePeers() { return _conByIdent.size(); }
|
||||
|
||||
/**
|
||||
* How many peers are we actively sending messages to (this minute)
|
||||
*/
|
||||
@Override
|
||||
public int countActiveSendPeers() {
|
||||
int active = 0;
|
||||
synchronized (_conLock) {
|
||||
for (Iterator iter = _conByIdent.values().iterator(); iter.hasNext(); ) {
|
||||
NTCPConnection con = (NTCPConnection)iter.next();
|
||||
for (NTCPConnection con : _conByIdent.values()) {
|
||||
if ( (con.getTimeSinceSend() <= 60*1000) || (con.getTimeSinceReceive() <= 60*1000) )
|
||||
active++;
|
||||
}
|
||||
}
|
||||
return active;
|
||||
}
|
||||
@@ -416,16 +420,9 @@ public class NTCPTransport extends TransportImpl {
|
||||
*/
|
||||
@Override
|
||||
public Vector<Long> getClockSkews() {
|
||||
|
||||
Vector<NTCPConnection> peers = new Vector();
|
||||
Vector<Long> skews = new Vector();
|
||||
|
||||
synchronized (_conLock) {
|
||||
peers.addAll(_conByIdent.values());
|
||||
}
|
||||
|
||||
for (Iterator<NTCPConnection> iter = peers.iterator(); iter.hasNext(); ) {
|
||||
NTCPConnection con = iter.next();
|
||||
for (NTCPConnection con : _conByIdent.values()) {
|
||||
if (con.isEstablished())
|
||||
skews.addElement(Long.valueOf(con.getClockSkew()));
|
||||
}
|
||||
@@ -551,11 +548,14 @@ public class NTCPTransport extends TransportImpl {
|
||||
ServerSocketChannel chan = ServerSocketChannel.open();
|
||||
chan.configureBlocking(false);
|
||||
|
||||
int port = _myAddress.getPort();
|
||||
if (port > 0 && port < 1024)
|
||||
_log.logAlways(Log.WARN, "Specified NTCP port is " + port + ", ports lower than 1024 not recommended");
|
||||
InetSocketAddress addr = null;
|
||||
if(bindToAddr==null) {
|
||||
addr = new InetSocketAddress(_myAddress.getPort());
|
||||
addr = new InetSocketAddress(port);
|
||||
} else {
|
||||
addr = new InetSocketAddress(bindToAddr, _myAddress.getPort());
|
||||
addr = new InetSocketAddress(bindToAddr, port);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Binding only to " + bindToAddr);
|
||||
}
|
||||
@@ -603,6 +603,7 @@ public class NTCPTransport extends TransportImpl {
|
||||
void establishing(NTCPConnection con) {
|
||||
_establishing.add(con);
|
||||
}
|
||||
|
||||
/**
|
||||
* called in the EventPumper no more than once a second or so, closing
|
||||
* any unconnected/unestablished connections
|
||||
@@ -694,12 +695,10 @@ public class NTCPTransport extends TransportImpl {
|
||||
@Override
|
||||
public short getReachabilityStatus() {
|
||||
if (isAlive() && _myAddress != null) {
|
||||
synchronized (_conLock) {
|
||||
for (NTCPConnection con : _conByIdent.values()) {
|
||||
if (con.isInbound())
|
||||
return CommSystemFacade.STATUS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CommSystemFacade.STATUS_UNKNOWN;
|
||||
}
|
||||
@@ -727,17 +726,17 @@ public class NTCPTransport extends TransportImpl {
|
||||
// will this work?
|
||||
replaceAddress(null);
|
||||
}
|
||||
|
||||
public static final String STYLE = "NTCP";
|
||||
|
||||
public void renderStatusHTML(java.io.Writer out, int sortFlags) throws IOException {}
|
||||
|
||||
@Override
|
||||
public void renderStatusHTML(java.io.Writer out, String urlBase, int sortFlags) throws IOException {
|
||||
TreeSet peers = new TreeSet(getComparator(sortFlags));
|
||||
synchronized (_conLock) {
|
||||
peers.addAll(_conByIdent.values());
|
||||
}
|
||||
long offsetTotal = 0;
|
||||
peers.addAll(_conByIdent.values());
|
||||
|
||||
long offsetTotal = 0;
|
||||
float bpsSend = 0;
|
||||
float bpsRecv = 0;
|
||||
long totalUptime = 0;
|
||||
@@ -838,6 +837,7 @@ public class NTCPTransport extends TransportImpl {
|
||||
}
|
||||
|
||||
private static final NumberFormat _rateFmt = new DecimalFormat("#,##0.00");
|
||||
|
||||
private static String formatRate(float rate) {
|
||||
synchronized (_rateFmt) { return _rateFmt.format(rate); }
|
||||
}
|
||||
@@ -858,14 +858,12 @@ public class NTCPTransport extends TransportImpl {
|
||||
public static final AlphaComparator instance() { return _instance; }
|
||||
}
|
||||
|
||||
private static class PeerComparator implements Comparator {
|
||||
public int compare(Object lhs, Object rhs) {
|
||||
if ( (lhs == null) || (rhs == null) || !(lhs instanceof NTCPConnection) || !(rhs instanceof NTCPConnection))
|
||||
throw new IllegalArgumentException("rhs = " + rhs + " lhs = " + lhs);
|
||||
return compare((NTCPConnection)lhs, (NTCPConnection)rhs);
|
||||
}
|
||||
protected int compare(NTCPConnection l, NTCPConnection r) {
|
||||
private static class PeerComparator implements Comparator<NTCPConnection> {
|
||||
public int compare(NTCPConnection l, NTCPConnection r) {
|
||||
if (l == null || r == null)
|
||||
throw new IllegalArgumentException();
|
||||
// base64 retains binary ordering
|
||||
// UM, no it doesn't, but close enough
|
||||
return l.getRemotePeer().calculateHash().toBase64().compareTo(r.getRemotePeer().calculateHash().toBase64());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,12 @@ class Writer {
|
||||
}
|
||||
|
||||
private class Runner implements Runnable {
|
||||
private boolean _stop;
|
||||
public Runner() { _stop = false; }
|
||||
private volatile boolean _stop;
|
||||
|
||||
public Runner() {}
|
||||
|
||||
public void stop() { _stop = true; }
|
||||
|
||||
public void run() {
|
||||
if (_log.shouldLog(Log.INFO)) _log.info("Starting writer");
|
||||
NTCPConnection con = null;
|
||||
|
||||
@@ -28,7 +28,7 @@ class ACKSender implements Runnable {
|
||||
private static final long POISON_PS = -9999999999l;
|
||||
|
||||
/** how frequently do we want to send ACKs to a peer? */
|
||||
static final int ACK_FREQUENCY = 500;
|
||||
static final int ACK_FREQUENCY = 350;
|
||||
|
||||
public ACKSender(RouterContext ctx, UDPTransport transport) {
|
||||
_context = ctx;
|
||||
@@ -73,12 +73,12 @@ class ACKSender implements Runnable {
|
||||
_peersToACK.clear();
|
||||
}
|
||||
|
||||
private long ackFrequency(long timeSinceACK, long rtt) {
|
||||
private static long ackFrequency(long timeSinceACK, long rtt) {
|
||||
// if we are actively pumping lots of data to them, we can depend upon
|
||||
// the unsentACKThreshold to figure out when to send an ACK instead of
|
||||
// using the timer, so we can set the timeout/frequency higher
|
||||
if (timeSinceACK < 2*1000)
|
||||
return Math.max(rtt/2, 500);
|
||||
return Math.max(rtt/2, ACK_FREQUENCY);
|
||||
else
|
||||
return ACK_FREQUENCY;
|
||||
}
|
||||
@@ -162,15 +162,15 @@ class ACKSender implements Runnable {
|
||||
}
|
||||
|
||||
if (!ackBitfields.isEmpty()) {
|
||||
_context.statManager().addRateData("udp.sendACKCount", ackBitfields.size(), 0);
|
||||
_context.statManager().addRateData("udp.sendACKCount", ackBitfields.size());
|
||||
if (remaining > 0)
|
||||
_context.statManager().addRateData("udp.sendACKRemaining", remaining, 0);
|
||||
_context.statManager().addRateData("udp.sendACKRemaining", remaining);
|
||||
// set above before the break
|
||||
//now = _context.clock().now();
|
||||
if (lastSend < 0)
|
||||
lastSend = now - 1;
|
||||
_context.statManager().addRateData("udp.ackFrequency", now-lastSend, now-wanted);
|
||||
//_context.statManager().getStatLog().addData(peer.getRemoteHostId().toString(), "udp.peer.sendACKCount", ackBitfields.size(), 0);
|
||||
//_context.statManager().getStatLog().addData(peer.getRemoteHostId().toString(), "udp.peer.sendACKCount", ackBitfields.size());
|
||||
UDPPacket ack = _builder.buildACK(peer, ackBitfields);
|
||||
ack.markType(1);
|
||||
ack.setFragmentCount(-1);
|
||||
@@ -193,7 +193,7 @@ class ACKSender implements Runnable {
|
||||
ackPeer(peer);
|
||||
}
|
||||
} else {
|
||||
_context.statManager().addRateData("udp.abortACK", 1, 0);
|
||||
_context.statManager().addRateData("udp.abortACK", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.data.RouterIdentity;
|
||||
@@ -21,7 +22,6 @@ import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.router.OutNetMessage;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
|
||||
import static net.i2p.router.transport.udp.InboundEstablishState.InboundState.*;
|
||||
import static net.i2p.router.transport.udp.OutboundEstablishState.OutboundState.*;
|
||||
@@ -86,7 +86,9 @@ class EstablishmentManager {
|
||||
private int _activity;
|
||||
|
||||
/** max outbound in progress - max inbound is half of this */
|
||||
private static final int DEFAULT_MAX_CONCURRENT_ESTABLISH = 30;
|
||||
private final int DEFAULT_MAX_CONCURRENT_ESTABLISH;
|
||||
private static final int DEFAULT_LOW_MAX_CONCURRENT_ESTABLISH = 20;
|
||||
private static final int DEFAULT_HIGH_MAX_CONCURRENT_ESTABLISH = 150;
|
||||
private static final String PROP_MAX_CONCURRENT_ESTABLISH = "i2np.udp.maxConcurrentEstablish";
|
||||
|
||||
/** max pending outbound connections (waiting because we are at MAX_CONCURRENT_ESTABLISH) */
|
||||
@@ -132,6 +134,9 @@ class EstablishmentManager {
|
||||
_outboundByClaimedAddress = new ConcurrentHashMap();
|
||||
_outboundByHash = new ConcurrentHashMap();
|
||||
_activityLock = new Object();
|
||||
DEFAULT_MAX_CONCURRENT_ESTABLISH = Math.max(DEFAULT_LOW_MAX_CONCURRENT_ESTABLISH,
|
||||
Math.min(DEFAULT_HIGH_MAX_CONCURRENT_ESTABLISH,
|
||||
ctx.bandwidthLimiter().getOutboundKBytesPerSecond() / 2));
|
||||
_context.statManager().createRateStat("udp.inboundEstablishTime", "How long it takes for a new inbound session to be established", "udp", UDPTransport.RATES);
|
||||
_context.statManager().createRateStat("udp.outboundEstablishTime", "How long it takes for a new outbound session to be established", "udp", UDPTransport.RATES);
|
||||
//_context.statManager().createRateStat("udp.inboundEstablishFailedState", "What state a failed inbound establishment request fails in", "udp", UDPTransport.RATES);
|
||||
@@ -142,6 +147,7 @@ class EstablishmentManager {
|
||||
_context.statManager().createRateStat("udp.establishDropped", "Dropped an inbound establish message", "udp", UDPTransport.RATES);
|
||||
_context.statManager().createRateStat("udp.establishRejected", "How many pending outbound connections are there when we refuse to add any more?", "udp", UDPTransport.RATES);
|
||||
_context.statManager().createRateStat("udp.establishOverflow", "How many messages were queued up on a pending connection when it was too much?", "udp", UDPTransport.RATES);
|
||||
_context.statManager().createRateStat("udp.establishBadIP", "Received IP or port was bad", "udp", UDPTransport.RATES);
|
||||
// following are for PeerState
|
||||
_context.statManager().createRateStat("udp.congestionOccurred", "How large the cwin was when congestion occurred (duration == sendBps)", "udp", UDPTransport.RATES);
|
||||
_context.statManager().createRateStat("udp.congestedRTO", "retransmission timeout after congestion (duration == rtt dev)", "udp", UDPTransport.RATES);
|
||||
@@ -247,6 +253,7 @@ class EstablishmentManager {
|
||||
_transport.failed(msg, "Remote peer's IP isn't valid");
|
||||
_transport.markUnreachable(toHash);
|
||||
//_context.shitlist().shitlistRouter(msg.getTarget().getIdentity().calculateHash(), "Invalid SSU address", UDPTransport.STYLE);
|
||||
_context.statManager().addRateData("udp.establishBadIP", 1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -413,9 +420,9 @@ class EstablishmentManager {
|
||||
*
|
||||
*/
|
||||
void receiveSessionRequest(RemoteHostId from, UDPPacketReader reader) {
|
||||
if (!_transport.isValid(from.getIP())) {
|
||||
if (from.getPort() < UDPTransport.MIN_PEER_PORT || !_transport.isValid(from.getIP())) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Receive session request from invalid IP: " + from);
|
||||
_log.warn("Receive session request from invalid: " + from);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -435,6 +442,7 @@ class EstablishmentManager {
|
||||
if (_context.blocklist().isBlocklisted(from.getIP())) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Receive session request from blocklisted IP: " + from);
|
||||
_context.statManager().addRateData("udp.establishBadIP", 1);
|
||||
return; // drop the packet
|
||||
}
|
||||
if (!_transport.allowConnection())
|
||||
@@ -450,14 +458,9 @@ class EstablishmentManager {
|
||||
}
|
||||
|
||||
if (isNew) {
|
||||
// we don't expect inbound connections when hidden, but it could happen
|
||||
// Don't offer if we are approaching max connections. While Relay Intros do not
|
||||
// count as connections, we have to keep the connection to this peer up longer if
|
||||
// we are offering introductions.
|
||||
// Don't offer to relay to privileged ports.
|
||||
if ((!_context.router().isHidden()) && (!_transport.introducersRequired()) && _transport.haveCapacity() &&
|
||||
state.getSentPort() >= 1024 &&
|
||||
!((FloodfillNetworkDatabaseFacade)_context.netDb()).floodfillEnabled()) {
|
||||
// TODO if already we have their RI, only offer if they need it (no 'C' cap)
|
||||
if (_transport.canIntroduce() && state.getSentPort() >= 1024) {
|
||||
// ensure > 0
|
||||
long tag = 1 + _context.random().nextLong(MAX_TAG_VALUE);
|
||||
state.setSentRelayTag(tag);
|
||||
@@ -573,7 +576,14 @@ class EstablishmentManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (_outboundStates.size() < getMaxConcurrentEstablish() && !_queuedOutbound.isEmpty()) {
|
||||
// in theory shouldn't need locking, but
|
||||
// getting IllegalStateExceptions on old Java 5,
|
||||
// which hoses this state.
|
||||
synchronized(_queuedOutbound) {
|
||||
locked_admitQueued();
|
||||
}
|
||||
}
|
||||
//remaining = _queuedOutbound.size();
|
||||
|
||||
//if (admitted > 0)
|
||||
@@ -600,6 +610,7 @@ class EstablishmentManager {
|
||||
// ok, active shrunk, lets let some queued in.
|
||||
|
||||
Map.Entry<RemoteHostId, List<OutNetMessage>> entry = iter.next();
|
||||
// java 5 IllegalStateException here
|
||||
iter.remove();
|
||||
RemoteHostId to = entry.getKey();
|
||||
List<OutNetMessage> allQueued = entry.getValue();
|
||||
@@ -709,7 +720,7 @@ class EstablishmentManager {
|
||||
private void sendInboundComplete(PeerState peer) {
|
||||
// SimpleTimer.getInstance().addEvent(new PublishToNewInbound(peer), 10*1000);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Completing to the peer after confirm: " + peer);
|
||||
_log.info("Completing to the peer after IB confirm: " + peer);
|
||||
DeliveryStatusMessage dsm = new DeliveryStatusMessage(_context);
|
||||
dsm.setArrival(Router.NETWORK_ID); // overloaded, sure, but future versions can check this
|
||||
// This causes huge values in the inNetPool.droppedDeliveryStatusDelay stat
|
||||
@@ -803,29 +814,10 @@ class EstablishmentManager {
|
||||
/** the relay tag is a 4-byte field in the protocol */
|
||||
public static final long MAX_TAG_VALUE = 0xFFFFFFFFl;
|
||||
|
||||
/**
|
||||
* This may be called more than once
|
||||
*/
|
||||
private void sendCreated(InboundEstablishState state) {
|
||||
long now = _context.clock().now();
|
||||
// This is usually handled in receiveSessionRequest() above, except, I guess,
|
||||
// if the session isn't new and we are going through again.
|
||||
// Don't offer if we are approaching max connections (see comments above)
|
||||
// Also don't offer if we are floodfill, as this extends the max idle time
|
||||
// and we will have lots of incoming conns
|
||||
if ((!_context.router().isHidden()) && (!_transport.introducersRequired()) && _transport.haveCapacity() &&
|
||||
!((FloodfillNetworkDatabaseFacade)_context.netDb()).floodfillEnabled()) {
|
||||
// offer to relay
|
||||
// (perhaps we should check our bw usage and/or how many peers we are
|
||||
// already offering introducing?)
|
||||
if (state.getSentRelayTag() == 0) {
|
||||
// ensure > 0
|
||||
state.setSentRelayTag(1 + _context.random().nextLong(MAX_TAG_VALUE));
|
||||
} else {
|
||||
// don't change it, since we've already prepared our sig
|
||||
}
|
||||
} else {
|
||||
// don't offer to relay
|
||||
state.setSentRelayTag(0);
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Send created to: " + state);
|
||||
|
||||
@@ -901,15 +893,14 @@ class EstablishmentManager {
|
||||
byte ip[] = new byte[sz];
|
||||
reader.getRelayResponseReader().readCharlieIP(ip, 0);
|
||||
int port = reader.getRelayResponseReader().readCharliePort();
|
||||
if ((!isValid(ip, port)) || (!isValid(bob.getIP(), bob.getPort()))) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Bad relay resp from " + bob + " for " + Addresses.toString(ip, port));
|
||||
_context.statManager().addRateData("udp.relayBadIP", 1);
|
||||
return;
|
||||
}
|
||||
InetAddress addr = null;
|
||||
try {
|
||||
if (!_transport.isValid(ip))
|
||||
throw new UnknownHostException("non-public IP");
|
||||
// let's not relay to a privileged port, sounds like trouble
|
||||
if (port < 1024 || port > 65535)
|
||||
throw new UnknownHostException("bad port " + port);
|
||||
if (Arrays.equals(ip, _transport.getExternalIP()))
|
||||
throw new UnknownHostException("relay myself");
|
||||
addr = InetAddress.getByAddress(ip);
|
||||
} catch (UnknownHostException uhe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@@ -943,7 +934,20 @@ class EstablishmentManager {
|
||||
}
|
||||
notifyActivity();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Are IP and port valid?
|
||||
* Refuse anybody in the same /16
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private boolean isValid(byte[] ip, int port) {
|
||||
return port >= 1024 &&
|
||||
port <= 65535 &&
|
||||
_transport.isValid(ip) &&
|
||||
(!DataHelper.eq(ip, 0, _transport.getExternalIP(), 0, 2)) &&
|
||||
(!_context.blocklist().isBlocklisted(ip));
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that while a SessionConfirmed could in theory be fragmented,
|
||||
* in practice a RouterIdentity is 387 bytes and a single fragment is 512 bytes max,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package net.i2p.router.transport.udp;
|
||||
|
||||
import net.i2p.util.ObjectCounter;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
/**
|
||||
* Count IPs
|
||||
*
|
||||
* @since 0.9.3
|
||||
*/
|
||||
class IPThrottler {
|
||||
private ObjectCounter<Integer> _counter;
|
||||
private final int _max;
|
||||
|
||||
public IPThrottler(int max, long time) {
|
||||
_max = max;
|
||||
_counter = new ObjectCounter();
|
||||
SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(), time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments before checking
|
||||
* @return true if ip.length != 4
|
||||
*/
|
||||
public boolean shouldThrottle(byte[] ip) {
|
||||
if (ip.length != 4)
|
||||
return true;
|
||||
return _counter.increment(toInt(ip)) > _max;
|
||||
}
|
||||
|
||||
private static Integer toInt(byte ip[]) {
|
||||
int rv = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
rv |= (ip[i] & 0xff) << ((3-i)*8);
|
||||
return Integer.valueOf(rv);
|
||||
}
|
||||
|
||||
private class Cleaner implements SimpleTimer.TimedEvent {
|
||||
public void timeReached() {
|
||||
_counter.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package net.i2p.router.transport.udp;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -12,6 +11,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.SessionKey;
|
||||
@@ -43,10 +43,10 @@ class IntroductionManager {
|
||||
private static final int MAX_INBOUND = 20;
|
||||
|
||||
/**
|
||||
* TODO this should be enforced in EstablishmentManager, it isn't now.
|
||||
* This is enforced in EstablishmentManager
|
||||
* @since 0.8.11
|
||||
*/
|
||||
private static final int MAX_OUTBOUND = 100;
|
||||
public static final int MAX_OUTBOUND = 100;
|
||||
|
||||
/** Max one per target in this time */
|
||||
private static final long PUNCH_CLEAN_TIME = 5*1000;
|
||||
@@ -64,6 +64,7 @@ class IntroductionManager {
|
||||
ctx.statManager().createRateStat("udp.receiveRelayIntro", "How often we get a relayed request for us to talk to someone?", "udp", UDPTransport.RATES);
|
||||
ctx.statManager().createRateStat("udp.receiveRelayRequest", "How often we receive a good request to relay to someone else?", "udp", UDPTransport.RATES);
|
||||
ctx.statManager().createRateStat("udp.receiveRelayRequestBadTag", "Received relay requests with bad/expired tag", "udp", UDPTransport.RATES);
|
||||
ctx.statManager().createRateStat("udp.relayBadIP", "Received IP or port was bad", "udp", UDPTransport.RATES);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
@@ -160,7 +161,7 @@ class IntroductionManager {
|
||||
}
|
||||
byte[] ip = cur.getRemoteIP();
|
||||
int port = cur.getRemotePort();
|
||||
if (ip == null || !TransportImpl.isPubliclyRoutable(ip) || port < 1024 || port > 65535)
|
||||
if (!isValid(ip, port))
|
||||
continue;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Picking introducer: " + cur);
|
||||
@@ -185,14 +186,15 @@ class IntroductionManager {
|
||||
*/
|
||||
public void pingIntroducers() {
|
||||
// Try to keep the connection up for two hours after we made anybody an introducer
|
||||
long pingCutoff = _context.clock().now() - (105 * 60 * 1000);
|
||||
long inactivityCutoff = _context.clock().now() - UDPTransport.MIN_EXPIRE_TIMEOUT;
|
||||
long now = _context.clock().now();
|
||||
long pingCutoff = now - (105 * 60 * 1000);
|
||||
long inactivityCutoff = now - UDPTransport.MIN_EXPIRE_TIMEOUT;
|
||||
for (PeerState cur : _inbound) {
|
||||
if (cur.getIntroducerTime() > pingCutoff &&
|
||||
cur.getLastSendTime() < inactivityCutoff) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Pinging introducer: " + cur);
|
||||
cur.setLastSendTime(_context.clock().now());
|
||||
cur.setLastSendTime(now);
|
||||
_transport.send(_builder.buildPing(cur));
|
||||
}
|
||||
}
|
||||
@@ -207,6 +209,14 @@ class IntroductionManager {
|
||||
int introducerCount() {
|
||||
return _inbound.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of peers we have volunteered to introduce
|
||||
* @since 0.9.3
|
||||
*/
|
||||
int introducedCount() {
|
||||
return _outbound.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* We are Charlie and we got this from Bob.
|
||||
@@ -218,7 +228,7 @@ class IntroductionManager {
|
||||
void receiveRelayIntro(RemoteHostId bob, UDPPacketReader reader) {
|
||||
if (_context.router().isHidden())
|
||||
return;
|
||||
_context.statManager().addRateData("udp.receiveRelayIntro", 1, 0);
|
||||
_context.statManager().addRateData("udp.receiveRelayIntro", 1);
|
||||
|
||||
if (!_transport.allowConnection()) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@@ -230,23 +240,25 @@ class IntroductionManager {
|
||||
byte ip[] = new byte[ipSize];
|
||||
reader.getRelayIntroReader().readIP(ip, 0);
|
||||
int port = reader.getRelayIntroReader().readPort();
|
||||
|
||||
if ((!isValid(ip, port)) || (!isValid(bob.getIP(), bob.getPort()))) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Bad relay intro from " + bob + " for " + Addresses.toString(ip, port));
|
||||
_context.statManager().addRateData("udp.relayBadIP", 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Receive relay intro from " + bob + " for " + Addresses.toString(ip, port));
|
||||
|
||||
InetAddress to = null;
|
||||
try {
|
||||
if (!_transport.isValid(ip))
|
||||
throw new UnknownHostException("non-public IP");
|
||||
// let's not punch to a privileged port, sounds like trouble
|
||||
if (port < 1024 || port > 65535)
|
||||
throw new UnknownHostException("bad port " + port);
|
||||
if (Arrays.equals(ip, _transport.getExternalIP()))
|
||||
throw new UnknownHostException("punch myself");
|
||||
to = InetAddress.getByAddress(ip);
|
||||
} catch (UnknownHostException uhe) {
|
||||
// shitlist Bob?
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("IP for alice to hole punch to is invalid", uhe);
|
||||
_context.statManager().addRateData("udp.relayBadIP", 1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -312,7 +324,23 @@ class IntroductionManager {
|
||||
void receiveRelayRequest(RemoteHostId alice, UDPPacketReader reader) {
|
||||
if (_context.router().isHidden())
|
||||
return;
|
||||
long tag = reader.getRelayRequestReader().readTag();
|
||||
UDPPacketReader.RelayRequestReader rrReader = reader.getRelayRequestReader();
|
||||
long tag = rrReader.readTag();
|
||||
int ipSize = rrReader.readIPSize();
|
||||
int port = rrReader.readPort();
|
||||
|
||||
// ip/port inside message should be 0:0, as it's unimplemented on send -
|
||||
// see PacketBuilder.buildRelayRequest()
|
||||
if (!isValid(alice.getIP(), alice.getPort()) || ipSize != 0 || port != 0) {
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
byte ip[] = new byte[ipSize];
|
||||
rrReader.readIP(ip, 0);
|
||||
_log.warn("Bad relay req from " + alice + " for " + Addresses.toString(ip, port));
|
||||
}
|
||||
_context.statManager().addRateData("udp.relayBadIP", 1);
|
||||
return;
|
||||
}
|
||||
|
||||
PeerState charlie = get(tag);
|
||||
if (charlie == null) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
@@ -337,4 +365,17 @@ class IntroductionManager {
|
||||
// send alice back charlie's info
|
||||
_transport.send(_builder.buildRelayResponse(alice, charlie, reader.getRelayRequestReader().readNonce(), aliceIntroKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Are IP and port valid?
|
||||
* Refuse anybody in the same /16
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private boolean isValid(byte[] ip, int port) {
|
||||
return port >= 1024 &&
|
||||
port <= 65535 &&
|
||||
_transport.isValid(ip) &&
|
||||
(!DataHelper.eq(ip, 0, _transport.getExternalIP(), 0, 2)) &&
|
||||
(!_context.blocklist().isBlocklisted(ip));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,10 +61,10 @@ class MessageReceiver {
|
||||
//_cache = ByteCache.getInstance(64, I2NPMessage.MAX_SIZE);
|
||||
_context.statManager().createRateStat("udp.inboundExpired", "How many messages were expired before reception?", "udp", UDPTransport.RATES);
|
||||
//_context.statManager().createRateStat("udp.inboundRemaining", "How many messages were remaining when a message is pulled off the complete queue?", "udp", UDPTransport.RATES);
|
||||
_context.statManager().createRateStat("udp.inboundReady", "How many messages were ready when a message is added to the complete queue?", "udp", UDPTransport.RATES);
|
||||
//_context.statManager().createRateStat("udp.inboundReady", "How many messages were ready when a message is added to the complete queue?", "udp", UDPTransport.RATES);
|
||||
//_context.statManager().createRateStat("udp.inboundReadTime", "How long it takes to parse in the completed fragments into a message?", "udp", UDPTransport.RATES);
|
||||
//_context.statManager().createRateStat("udp.inboundReceiveProcessTime", "How long it takes to add the message to the transport?", "udp", UDPTransport.RATES);
|
||||
_context.statManager().createRateStat("udp.inboundLag", "How long the oldest ready message has been sitting on the queue (period is the queue size)?", "udp", UDPTransport.RATES);
|
||||
//_context.statManager().createRateStat("udp.inboundLag", "How long the oldest ready message has been sitting on the queue (period is the queue size)?", "udp", UDPTransport.RATES);
|
||||
|
||||
_alive = true;
|
||||
}
|
||||
|
||||
@@ -369,7 +369,7 @@ class OutboundEstablishState {
|
||||
off += 4;
|
||||
DataHelper.toLong(signed, off, 4, _receivedSignedOnTime);
|
||||
boolean valid = _context.dsa().verifySignature(_receivedSignature, signed, _remotePeer.getSigningPublicKey());
|
||||
if (!valid || _log.shouldLog(Log.DEBUG)) {
|
||||
if (_log.shouldLog(Log.DEBUG) || (_log.shouldLog(Log.WARN) && !valid)) {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
buf.append("Signed sessionCreated:");
|
||||
buf.append(" Alice: ").append(Addresses.toString(_aliceIP, _alicePort));
|
||||
|
||||
@@ -42,10 +42,20 @@ class OutboundMessageState implements CDPQEntry {
|
||||
private long _seqNum;
|
||||
|
||||
public static final int MAX_MSG_SIZE = 32 * 1024;
|
||||
/** is this enough for a high-bandwidth router? */
|
||||
private static final int MAX_ENTRIES = 64;
|
||||
/** would two caches, one for small and one for large messages, be better? */
|
||||
private static final ByteCache _cache = ByteCache.getInstance(MAX_ENTRIES, MAX_MSG_SIZE);
|
||||
private static final int CACHE4_BYTES = MAX_MSG_SIZE;
|
||||
private static final int CACHE3_BYTES = CACHE4_BYTES / 4;
|
||||
private static final int CACHE2_BYTES = CACHE3_BYTES / 4;
|
||||
private static final int CACHE1_BYTES = CACHE2_BYTES / 4;
|
||||
|
||||
private static final int CACHE1_MAX = 256;
|
||||
private static final int CACHE2_MAX = CACHE1_MAX / 4;
|
||||
private static final int CACHE3_MAX = CACHE2_MAX / 4;
|
||||
private static final int CACHE4_MAX = CACHE3_MAX / 4;
|
||||
|
||||
private static final ByteCache _cache1 = ByteCache.getInstance(CACHE1_MAX, CACHE1_BYTES);
|
||||
private static final ByteCache _cache2 = ByteCache.getInstance(CACHE2_MAX, CACHE2_BYTES);
|
||||
private static final ByteCache _cache3 = ByteCache.getInstance(CACHE3_MAX, CACHE3_BYTES);
|
||||
private static final ByteCache _cache4 = ByteCache.getInstance(CACHE4_MAX, CACHE4_BYTES);
|
||||
|
||||
private static final long EXPIRATION = 10*1000;
|
||||
|
||||
@@ -72,6 +82,7 @@ class OutboundMessageState implements CDPQEntry {
|
||||
* Called from UDPTransport
|
||||
* TODO make two constructors, remove this, and make more things final
|
||||
* @return success
|
||||
* @throws IAE if too big
|
||||
*/
|
||||
public boolean initialize(I2NPMessage msg, PeerState peer) {
|
||||
if (msg == null)
|
||||
@@ -91,6 +102,7 @@ class OutboundMessageState implements CDPQEntry {
|
||||
* Called from OutboundMessageFragments
|
||||
* TODO make two constructors, remove this, and make more things final
|
||||
* @return success
|
||||
* @throws IAE if too big
|
||||
*/
|
||||
public boolean initialize(OutNetMessage m, I2NPMessage msg) {
|
||||
if ( (m == null) || (msg == null) )
|
||||
@@ -110,19 +122,13 @@ class OutboundMessageState implements CDPQEntry {
|
||||
* Called from OutboundMessageFragments
|
||||
* @param m null if msg is "injected"
|
||||
* @return success
|
||||
* @throws IAE if too big
|
||||
*/
|
||||
private boolean initialize(OutNetMessage m, I2NPMessage msg, PeerState peer) {
|
||||
_message = m;
|
||||
_peer = peer;
|
||||
if (_messageBuf != null) {
|
||||
_cache.release(_messageBuf);
|
||||
_messageBuf = null;
|
||||
}
|
||||
|
||||
_messageBuf = _cache.acquire();
|
||||
int size = msg.getRawMessageSize();
|
||||
if (size > _messageBuf.getData().length)
|
||||
throw new IllegalArgumentException("Size too large! " + size + " in " + msg);
|
||||
acquireBuf(size);
|
||||
try {
|
||||
int len = msg.toRawByteArray(_messageBuf.getData());
|
||||
_messageBuf.setValid(len);
|
||||
@@ -137,13 +143,49 @@ class OutboundMessageState implements CDPQEntry {
|
||||
// _log.debug("Raw byte array for " + _messageId + ": " + Base64.encode(_messageBuf.getData(), 0, len));
|
||||
return true;
|
||||
} catch (IllegalStateException ise) {
|
||||
_cache.release(_messageBuf);
|
||||
_messageBuf = null;
|
||||
_released = true;
|
||||
releaseBuf();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IAE if too big
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private void acquireBuf(int size) {
|
||||
if (_messageBuf != null)
|
||||
releaseBuf();
|
||||
if (size <= CACHE1_BYTES)
|
||||
_messageBuf = _cache1.acquire();
|
||||
else if (size <= CACHE2_BYTES)
|
||||
_messageBuf = _cache2.acquire();
|
||||
else if (size <= CACHE3_BYTES)
|
||||
_messageBuf = _cache3.acquire();
|
||||
else if (size <= CACHE4_BYTES)
|
||||
_messageBuf = _cache4.acquire();
|
||||
else
|
||||
throw new IllegalArgumentException("Size too large! " + size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.3
|
||||
*/
|
||||
private void releaseBuf() {
|
||||
if (_messageBuf == null)
|
||||
return;
|
||||
int size = _messageBuf.getData().length;
|
||||
if (size == CACHE1_BYTES)
|
||||
_cache1.release(_messageBuf);
|
||||
else if (size == CACHE2_BYTES)
|
||||
_cache2.release(_messageBuf);
|
||||
else if (size == CACHE3_BYTES)
|
||||
_cache3.release(_messageBuf);
|
||||
else if (size == CACHE4_BYTES)
|
||||
_cache4.release(_messageBuf);
|
||||
_messageBuf = null;
|
||||
_released = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is synchronized with writeFragment(),
|
||||
* so we do not release (probably due to an ack) while we are retransmitting.
|
||||
@@ -151,8 +193,7 @@ class OutboundMessageState implements CDPQEntry {
|
||||
*/
|
||||
public synchronized void releaseResources() {
|
||||
if (_messageBuf != null && !_released) {
|
||||
_cache.release(_messageBuf);
|
||||
_released = true;
|
||||
releaseBuf();
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_releasedBy = new Exception ("Released on " + new Date() + " by:");
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package net.i2p.router.transport.udp;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.LHMCache;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@@ -30,6 +33,8 @@ class PacketHandler {
|
||||
private final IntroductionManager _introManager;
|
||||
private volatile boolean _keepReading;
|
||||
private final Handler[] _handlers;
|
||||
private final Map<RemoteHostId, Object> _failCache;
|
||||
private static final Object DUMMY = new Object();
|
||||
|
||||
private static final int MIN_NUM_HANDLERS = 1; // unless < 32MB
|
||||
private static final int MAX_NUM_HANDLERS = 1;
|
||||
@@ -46,6 +51,7 @@ class PacketHandler {
|
||||
_inbound = inbound;
|
||||
_testManager = testManager;
|
||||
_introManager = introManager;
|
||||
_failCache = new LHMCache(24);
|
||||
|
||||
long maxMemory = Runtime.getRuntime().maxMemory();
|
||||
if (maxMemory == Long.MAX_VALUE)
|
||||
@@ -143,8 +149,8 @@ class PacketHandler {
|
||||
if (packet == null) break; // keepReading is probably false, or bind failed...
|
||||
|
||||
packet.received();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Received: " + packet);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Received: " + packet);
|
||||
_state = 4;
|
||||
long queueTime = packet.getLifetime();
|
||||
long handleStart = _context.clock().now();
|
||||
@@ -294,7 +300,7 @@ class PacketHandler {
|
||||
receivePacket(reader, packet, est, false);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Validation with existing con failed, and validation as reestablish failed too. DROP");
|
||||
_log.warn("Validation with existing con failed, and validation as reestablish failed too. DROP " + packet);
|
||||
_context.statManager().addRateData("udp.droppedInvalidReestablish", packet.getLifetime(), packet.getExpiration());
|
||||
}
|
||||
return;
|
||||
@@ -327,8 +333,70 @@ class PacketHandler {
|
||||
if (!isValid) {
|
||||
// Note that the vast majority of these are NOT corrupted packets, but
|
||||
// packets for which we don't have the PeerState (i.e. SessionKey)
|
||||
// Case 1: 48 byte destroy packet, we already closed
|
||||
// Case 2: 369 byte session created packet, re-tx of one that failed validation
|
||||
// (peer probably doesn't know his correct external port, esp. on <= 0.9.1
|
||||
|
||||
// Case 3:
|
||||
// For peers that change ports, look for an existing session with the same IP
|
||||
// If we find it, and the packet validates with its mac key, tell the transport
|
||||
// to change the port, and handle the packet.
|
||||
// All this since 0.9.3.
|
||||
RemoteHostId remoteHost = packet.getRemoteHost();
|
||||
boolean alreadyFailed;
|
||||
synchronized(_failCache) {
|
||||
alreadyFailed = _failCache.get(remoteHost) != null;
|
||||
}
|
||||
if (!alreadyFailed) {
|
||||
// this is slow, that's why we cache it above.
|
||||
List<PeerState> peers = _transport.getPeerStatesByIP(remoteHost);
|
||||
if (!peers.isEmpty()) {
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
buf.append("Established peers with this IP: ");
|
||||
boolean foundSamePort = false;
|
||||
PeerState state = null;
|
||||
int newPort = remoteHost.getPort();
|
||||
for (PeerState ps : peers) {
|
||||
boolean valid = false;
|
||||
long now = _context.clock().now();
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
buf.append(ps.getRemoteHostId().toString())
|
||||
.append(" last sent: ").append(now - ps.getLastSendTime())
|
||||
.append(" last rcvd: ").append(now - ps.getLastReceiveTime());
|
||||
if (ps.getRemotePort() == newPort) {
|
||||
foundSamePort = true;
|
||||
} else if (packet.validate(ps.getCurrentMACKey())) {
|
||||
packet.decrypt(ps.getCurrentCipherKey());
|
||||
reader.initialize(packet);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
buf.append(" VALID type ").append(reader.readPayloadType()).append("; ");
|
||||
valid = true;
|
||||
if (state == null)
|
||||
state = ps;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
buf.append(" INVALID; ");
|
||||
}
|
||||
}
|
||||
if (state != null && !foundSamePort) {
|
||||
_transport.changePeerPort(state, newPort);
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
buf.append(" CHANGED PORT TO ").append(newPort).append(" AND HANDLED");
|
||||
_log.warn(buf.toString());
|
||||
}
|
||||
handlePacket(reader, packet, state, null, null, true);
|
||||
return;
|
||||
}
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(buf.toString());
|
||||
}
|
||||
synchronized(_failCache) {
|
||||
_failCache.put(remoteHost, DUMMY);
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Cannot validate rcvd pkt (path): " + packet);
|
||||
_log.warn("Cannot validate rcvd pkt (path) wasCached? " + alreadyFailed + ": " + packet);
|
||||
|
||||
_context.statManager().addRateData("udp.droppedInvalidEstablish", packet.getLifetime(), packet.getExpiration());
|
||||
switch (peerType) {
|
||||
case INBOUND_FALLBACK:
|
||||
|
||||
@@ -72,6 +72,7 @@ class PeerState {
|
||||
* A positive number means our clock is ahead of theirs.
|
||||
*/
|
||||
private long _clockSkew;
|
||||
private final Object _clockSkewLock = new Object();
|
||||
|
||||
/** what is the current receive second, for congestion control? */
|
||||
private long _currentReceiveSecond;
|
||||
@@ -79,6 +80,8 @@ class PeerState {
|
||||
private long _lastSendTime;
|
||||
/** when did we last send them a message that was ACKed */
|
||||
private long _lastSendFullyTime;
|
||||
/** when did we last send them a ping? */
|
||||
private long _lastPingTime;
|
||||
/** when did we last receive a packet from them? */
|
||||
private long _lastReceiveTime;
|
||||
/** how many consecutive messages have we sent and not received an ACK to */
|
||||
@@ -137,11 +140,11 @@ class PeerState {
|
||||
/** what IP is the peer sending and receiving packets on? */
|
||||
private final byte[] _remoteIP;
|
||||
/** cached IP address */
|
||||
private transient InetAddress _remoteIPAddress;
|
||||
private volatile InetAddress _remoteIPAddress;
|
||||
/** what port is the peer sending and receiving packets on? */
|
||||
private final int _remotePort;
|
||||
private volatile int _remotePort;
|
||||
/** cached RemoteHostId, used to find the peerState by remote info */
|
||||
private final RemoteHostId _remoteHostId;
|
||||
private volatile RemoteHostId _remoteHostId;
|
||||
|
||||
/** if we need to contact them, do we need to talk to an introducer? */
|
||||
//private boolean _remoteRequiresIntroduction;
|
||||
@@ -284,9 +287,12 @@ class PeerState {
|
||||
*/
|
||||
public static final int LARGE_MTU = 1484;
|
||||
|
||||
/** 600 */
|
||||
private static final int MIN_RTO = 100 + ACKSender.ACK_FREQUENCY;
|
||||
private static final int INIT_RTO = 4*1000;
|
||||
private static final int INIT_RTO = 3*1000;
|
||||
public static final int INIT_RTT = INIT_RTO / 2;
|
||||
private static final int MAX_RTO = 15*1000;
|
||||
private static final int CLOCK_SKEW_FUDGE = (ACKSender.ACK_FREQUENCY * 2) / 3;
|
||||
|
||||
public PeerState(RouterContext ctx, UDPTransport transport,
|
||||
byte[] remoteIP, int remotePort, Hash remotePeer, boolean isInbound) {
|
||||
@@ -313,7 +319,7 @@ class PeerState {
|
||||
//_mtuLastChecked = -1;
|
||||
_lastACKSend = -1;
|
||||
_rto = INIT_RTO;
|
||||
_rtt = INIT_RTO / 2;
|
||||
_rtt = INIT_RTT;
|
||||
_rttDeviation = _rtt;
|
||||
_inboundMessages = new HashMap(8);
|
||||
_outboundMessages = new ArrayList(32);
|
||||
@@ -325,6 +331,17 @@ class PeerState {
|
||||
_remoteHostId = new RemoteHostId(remoteIP, remotePort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Caller should sync; UDPTransport must remove and add to peersByRemoteHost map
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public void changePort(int newPort) {
|
||||
if (newPort != _remotePort) {
|
||||
_remoteHostId = new RemoteHostId(_remoteIP, newPort);
|
||||
_remotePort = newPort;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The peer are we talking to. This should be set as soon as this
|
||||
* state is created if we are initiating a connection, but if we are
|
||||
@@ -342,9 +359,12 @@ class PeerState {
|
||||
* connection is established.
|
||||
*/
|
||||
public SessionKey getCurrentCipherKey() { return _currentCipherKey; }
|
||||
|
||||
/**
|
||||
* The pending AES key for verifying packets if we are rekeying the
|
||||
* connection, or null if we are not in the process of rekeying.
|
||||
*
|
||||
* @return null always, rekeying unimplemented
|
||||
*/
|
||||
public SessionKey getNextMACKey() { return _nextMACKey; }
|
||||
|
||||
@@ -352,6 +372,8 @@ class PeerState {
|
||||
* The pending AES key for encrypting/decrypting packets if we are
|
||||
* rekeying the connection, or null if we are not in the process
|
||||
* of rekeying.
|
||||
*
|
||||
* @return null always, rekeying unimplemented
|
||||
*/
|
||||
public SessionKey getNextCipherKey() { return _nextCipherKey; }
|
||||
|
||||
@@ -384,13 +406,20 @@ class PeerState {
|
||||
public long getLastReceiveTime() { return _lastReceiveTime; }
|
||||
/** how many seconds have we sent packets without any ACKs received? */
|
||||
public int getConsecutiveFailedSends() { return _consecutiveFailedSends; }
|
||||
/** have we received a packet with the ECN bit set in the current second? */
|
||||
|
||||
/**
|
||||
* have we received a packet with the ECN bit set in the current second?
|
||||
* @return false always
|
||||
* @deprecated unused, ECNs are never sent, always returns false
|
||||
*/
|
||||
public boolean getCurrentSecondECNReceived() { return _currentSecondECNReceived; }
|
||||
|
||||
/**
|
||||
* have all of the packets received in the current second requested that
|
||||
* the previous second's ACKs be sent?
|
||||
*/
|
||||
//public boolean getRemoteWantsPreviousACKs() { return _remoteWantsPreviousACKs; }
|
||||
|
||||
/** how many bytes should we send to the peer in a second */
|
||||
public int getSendWindowBytes() { return _sendWindowBytes; }
|
||||
/** how many bytes can we send to the peer in the current second */
|
||||
@@ -504,7 +533,12 @@ class PeerState {
|
||||
* A positive number means our clock is ahead of theirs.
|
||||
*/
|
||||
public void adjustClockSkew(long skew) {
|
||||
_clockSkew = (long) (0.9*_clockSkew + 0.1*(skew - (_rtt / 2)));
|
||||
// the real one-way delay is much less than RTT / 2, due to ack delays,
|
||||
// so add a fudge factor
|
||||
double adj = 0.1 * (skew + CLOCK_SKEW_FUDGE - (_rtt / 2));
|
||||
synchronized(_clockSkewLock) {
|
||||
_clockSkew = (long) (0.9*_clockSkew + adj);
|
||||
}
|
||||
}
|
||||
|
||||
/** what is the current receive second, for congestion control? */
|
||||
@@ -513,6 +547,21 @@ class PeerState {
|
||||
public void setLastSendTime(long when) { _lastSendTime = when; }
|
||||
/** when did we last receive a packet from them? */
|
||||
public void setLastReceiveTime(long when) { _lastReceiveTime = when; }
|
||||
|
||||
/**
|
||||
* Note ping sent. Does not update last send time.
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public void setLastPingTime(long when) { _lastPingTime = when; }
|
||||
|
||||
/**
|
||||
* Latest of last sent, last ACK, last ping
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public long getLastSendOrPingTime() {
|
||||
return Math.max(Math.max(_lastSendTime, _lastACKSend), _lastPingTime);
|
||||
}
|
||||
|
||||
/** return the smoothed send transfer rate */
|
||||
public int getSendBps() { return _sendBps; }
|
||||
public int getReceiveBps() { return _receiveBps; }
|
||||
@@ -714,7 +763,8 @@ class PeerState {
|
||||
/**
|
||||
* either they told us to back off, or we had to resend to get
|
||||
* the data through.
|
||||
*
|
||||
* Caller should synch on this
|
||||
* @return true if window shrunk, but nobody uses the return value
|
||||
*/
|
||||
private boolean congestionOccurred() {
|
||||
long now = _context.clock().now();
|
||||
@@ -722,8 +772,6 @@ class PeerState {
|
||||
return false; // only shrink once every few seconds
|
||||
_lastCongestionOccurred = now;
|
||||
|
||||
_context.statManager().addRateData("udp.congestionOccurred", _sendWindowBytes, _sendBps);
|
||||
|
||||
int congestionAt = _sendWindowBytes;
|
||||
//if (true)
|
||||
// _sendWindowBytes -= 10000;
|
||||
@@ -779,7 +827,10 @@ class PeerState {
|
||||
return randomResends;
|
||||
}
|
||||
|
||||
/** the ack was sent */
|
||||
/**
|
||||
* The ack was sent.
|
||||
* Side effect - sets _lastACKSend
|
||||
*/
|
||||
public void removeACKMessage(Long messageId) {
|
||||
boolean removed = _currentACKs.remove(messageId);
|
||||
if (removed) {
|
||||
@@ -800,15 +851,15 @@ class PeerState {
|
||||
/**
|
||||
* The max number of acks we save to send as duplicates
|
||||
*/
|
||||
private static final int MAX_RESEND_ACKS = 16;
|
||||
private static final int MAX_RESEND_ACKS = 64;
|
||||
/**
|
||||
* The max number of duplicate acks sent in each ack-only messge.
|
||||
* Doesn't really matter, we have plenty of room...
|
||||
* @since 0.7.13
|
||||
*/
|
||||
private static final int MAX_RESEND_ACKS_LARGE = MAX_RESEND_ACKS;
|
||||
private static final int MAX_RESEND_ACKS_LARGE = MAX_RESEND_ACKS / 3;
|
||||
/** for small MTU */
|
||||
private static final int MAX_RESEND_ACKS_SMALL = MAX_RESEND_ACKS;
|
||||
private static final int MAX_RESEND_ACKS_SMALL = MAX_RESEND_ACKS / 5;
|
||||
|
||||
/**
|
||||
* grab a list of ACKBitfield instances, some of which may fully
|
||||
@@ -827,11 +878,11 @@ class PeerState {
|
||||
* See above. Only called by ACKSender with alwaysIncludeRetransmissions = false.
|
||||
* So this is only for ACK-only packets, so all the size limiting is useless.
|
||||
* FIXME.
|
||||
* Side effect - sets _lastACKSend if rv is non-empty
|
||||
*
|
||||
* @return non-null, possibly empty
|
||||
*/
|
||||
public List<ACKBitfield> retrieveACKBitfields(boolean alwaysIncludeRetransmissions) {
|
||||
List<ACKBitfield> rv = new ArrayList(MAX_RESEND_ACKS);
|
||||
int bytesRemaining = countMaxACKData();
|
||||
|
||||
// Limit the overhead of all the resent acks when using small MTU
|
||||
@@ -843,8 +894,10 @@ class PeerState {
|
||||
maxResendAcks = MAX_RESEND_ACKS_SMALL;
|
||||
else
|
||||
maxResendAcks = MAX_RESEND_ACKS_LARGE;
|
||||
List<Long> randomResends = new ArrayList(_currentACKsResend);
|
||||
List<ACKBitfield> rv = new ArrayList(maxResendAcks);
|
||||
|
||||
// save to add to currentACKsResend later so we don't include twice
|
||||
List<Long> currentACKsRemoved = new ArrayList(_currentACKs.size());
|
||||
// As explained above, we include the acks in any order
|
||||
// since we are unlikely to get backed up -
|
||||
// just take them using the Set iterator.
|
||||
@@ -854,12 +907,13 @@ class PeerState {
|
||||
iter.remove();
|
||||
long id = val.longValue();
|
||||
rv.add(new FullACKBitfield(id));
|
||||
_currentACKsResend.offer(val);
|
||||
currentACKsRemoved.add(val);
|
||||
bytesRemaining -= 4;
|
||||
}
|
||||
if (_currentACKs.isEmpty())
|
||||
_wantACKSendSince = -1;
|
||||
if (alwaysIncludeRetransmissions || !rv.isEmpty()) {
|
||||
List<Long> randomResends = new ArrayList(_currentACKsResend);
|
||||
// now repeat by putting in some old ACKs
|
||||
// randomly selected from the Resend queue.
|
||||
// Maybe we should only resend each one a certain number of times...
|
||||
@@ -880,6 +934,9 @@ class PeerState {
|
||||
bytesRemaining -= 4;
|
||||
//}
|
||||
}
|
||||
for (Long val : currentACKsRemoved) {
|
||||
_currentACKsResend.offer(val);
|
||||
}
|
||||
}
|
||||
// trim down the resends
|
||||
while (_currentACKsResend.size() > MAX_RESEND_ACKS)
|
||||
@@ -907,7 +964,8 @@ class PeerState {
|
||||
}
|
||||
}
|
||||
|
||||
_lastACKSend = _context.clock().now();
|
||||
if (!rv.isEmpty())
|
||||
_lastACKSend = _context.clock().now();
|
||||
//if (rv == null)
|
||||
// rv = Collections.EMPTY_LIST;
|
||||
if (partialIncluded > 0)
|
||||
@@ -975,8 +1033,11 @@ class PeerState {
|
||||
public String toString() { return "Full ACK of " + _msgId; }
|
||||
}
|
||||
|
||||
/** we sent a message which was ACKed containing the given # of bytes */
|
||||
public void messageACKed(int bytesACKed, long lifetime, int numSends) {
|
||||
/**
|
||||
* We sent a message which was ACKed containing the given # of bytes.
|
||||
* Caller should synch on this
|
||||
*/
|
||||
private void locked_messageACKed(int bytesACKed, long lifetime, int numSends) {
|
||||
_concurrentMessagesActive--;
|
||||
if (_concurrentMessagesActive < 0)
|
||||
_concurrentMessagesActive = 0;
|
||||
@@ -990,15 +1051,15 @@ class PeerState {
|
||||
if (_sendWindowBytes <= _slowStartThreshold) {
|
||||
_sendWindowBytes += bytesACKed;
|
||||
} else {
|
||||
if (false) {
|
||||
_sendWindowBytes += 16; // why 16?
|
||||
} else {
|
||||
//if (false) {
|
||||
// _sendWindowBytes += 16; // why 16?
|
||||
//} else {
|
||||
float prob = ((float)bytesACKed) / ((float)(_sendWindowBytes<<1));
|
||||
float v = _context.random().nextFloat();
|
||||
if (v < 0) v = 0-v;
|
||||
if (v <= prob)
|
||||
_sendWindowBytes += bytesACKed; //512; // bytesACKed;
|
||||
}
|
||||
//}
|
||||
}
|
||||
} else {
|
||||
int allow = _concurrentMessagesAllowed - 1;
|
||||
@@ -1011,21 +1072,31 @@ class PeerState {
|
||||
_lastReceiveTime = _context.clock().now();
|
||||
_lastSendFullyTime = _lastReceiveTime;
|
||||
|
||||
if (true) {
|
||||
//if (true) {
|
||||
if (_sendWindowBytesRemaining + bytesACKed <= _sendWindowBytes)
|
||||
_sendWindowBytesRemaining += bytesACKed;
|
||||
else
|
||||
_sendWindowBytesRemaining = _sendWindowBytes;
|
||||
}
|
||||
//}
|
||||
|
||||
_messagesSent++;
|
||||
if (numSends < 2) {
|
||||
synchronized (this) {
|
||||
// caller synchs
|
||||
//synchronized (this) {
|
||||
recalculateTimeouts(lifetime);
|
||||
adjustMTU();
|
||||
}
|
||||
//}
|
||||
}
|
||||
else if (_log.shouldLog(Log.INFO))
|
||||
}
|
||||
|
||||
/**
|
||||
* We sent a message which was ACKed containing the given # of bytes.
|
||||
*/
|
||||
private void messageACKed(int bytesACKed, long lifetime, int numSends) {
|
||||
synchronized(this) {
|
||||
locked_messageACKed(bytesACKed, lifetime, numSends);
|
||||
}
|
||||
if (numSends >= 2 && _log.shouldLog(Log.INFO))
|
||||
_log.info("acked after numSends=" + numSends + " w/ lifetime=" + lifetime + " and size=" + bytesACKed);
|
||||
|
||||
_context.statManager().addRateData("udp.sendBps", _sendBps, lifetime);
|
||||
@@ -1104,9 +1175,10 @@ class PeerState {
|
||||
_packetsRetransmitted = packets;
|
||||
}
|
||||
*****/
|
||||
congestionOccurred();
|
||||
_context.statManager().addRateData("udp.congestionOccurred", _sendWindowBytes, _sendBps);
|
||||
_context.statManager().addRateData("udp.congestedRTO", _rto, _rttDeviation);
|
||||
synchronized (this) {
|
||||
congestionOccurred();
|
||||
adjustMTU();
|
||||
}
|
||||
//_rto *= 2;
|
||||
@@ -1168,10 +1240,14 @@ class PeerState {
|
||||
}
|
||||
|
||||
/**
|
||||
* we received a backoff request, so cut our send window
|
||||
* We received a backoff request, so cut our send window.
|
||||
* NOTE: ECN sending is unimplemented, this is never called.
|
||||
*/
|
||||
public void ECNReceived() {
|
||||
congestionOccurred();
|
||||
synchronized(this) {
|
||||
congestionOccurred();
|
||||
}
|
||||
_context.statManager().addRateData("udp.congestionOccurred", _sendWindowBytes, _sendBps);
|
||||
_currentSecondECNReceived = true;
|
||||
_lastReceiveTime = _context.clock().now();
|
||||
}
|
||||
@@ -1182,17 +1258,33 @@ class PeerState {
|
||||
|
||||
/** when did we last send an ACK to the peer? */
|
||||
public long getLastACKSend() { return _lastACKSend; }
|
||||
|
||||
/** @deprecated unused */
|
||||
public void setLastACKSend(long when) { _lastACKSend = when; }
|
||||
|
||||
public long getWantedACKSendSince() { return _wantACKSendSince; }
|
||||
|
||||
/**
|
||||
* Are we out of room to send all the current unsent acks in a single packet?
|
||||
* This is a huge threshold (134 for small MTU and 255 for large MTU)
|
||||
* that is rarely if ever exceeded in practice.
|
||||
* So just use a fixed threshold of half the resend acks, so that if the
|
||||
* packet is lost the acks have a decent chance of getting retransmitted.
|
||||
* Used only by ACKSender.
|
||||
*/
|
||||
public boolean unsentACKThresholdReached() {
|
||||
int threshold = countMaxACKData() / 4;
|
||||
return _currentACKs.size() >= threshold;
|
||||
//int threshold = countMaxACKData() / 4;
|
||||
//return _currentACKs.size() >= threshold;
|
||||
return _currentACKs.size() >= MAX_RESEND_ACKS / 2;
|
||||
}
|
||||
|
||||
/** @return MTU - 83 */
|
||||
/**
|
||||
* @return how many bytes available for acks in an ack-only packet, == MTU - 83
|
||||
* Max of 1020
|
||||
*/
|
||||
private int countMaxACKData() {
|
||||
return _mtu
|
||||
return Math.min(PacketBuilder.ABSOLUTE_MAX_ACKS * 4,
|
||||
_mtu
|
||||
- PacketBuilder.IP_HEADER_SIZE
|
||||
- PacketBuilder.UDP_HEADER_SIZE
|
||||
- UDPPacket.IV_SIZE
|
||||
@@ -1201,7 +1293,7 @@ class PeerState {
|
||||
- 4 // timestamp
|
||||
- 1 // data flag
|
||||
- 1 // # ACKs
|
||||
- 16; // padding safety
|
||||
- 16); // padding safety
|
||||
}
|
||||
|
||||
private int minRTO() {
|
||||
@@ -1213,6 +1305,7 @@ class PeerState {
|
||||
// return MAX_RTO;
|
||||
}
|
||||
|
||||
/** @return non-null */
|
||||
RemoteHostId getRemoteHostId() { return _remoteHostId; }
|
||||
|
||||
/**
|
||||
@@ -1770,6 +1863,7 @@ class PeerState {
|
||||
/**
|
||||
* Transfer the basic activity/state from the old peer to the current peer
|
||||
*
|
||||
* @param oldPeer non-null
|
||||
*/
|
||||
public void loadFrom(PeerState oldPeer) {
|
||||
_rto = oldPeer._rto;
|
||||
@@ -1875,6 +1969,9 @@ class PeerState {
|
||||
buf.append(" consecFail: ").append(_consecutiveFailedSends);
|
||||
buf.append(" recv OK/Dup: ").append(_packetsReceived).append('/').append(_packetsReceivedDuplicate);
|
||||
buf.append(" send OK/Dup: ").append(_packetsTransmitted).append('/').append(_packetsRetransmitted);
|
||||
buf.append(" IBM: ").append(_inboundMessages.size());
|
||||
buf.append(" OBQ: ").append(_outboundQueue.size());
|
||||
buf.append(" OBL: ").append(_outboundMessages.size());
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,16 @@ import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.router.RouterContext;
|
||||
import static net.i2p.router.transport.udp.PeerTestState.Role.*;
|
||||
import net.i2p.util.Addresses;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
/**
|
||||
* Entry points are runTest() to start a new test as Alice,
|
||||
* and receiveTest() for all received test packets.
|
||||
*
|
||||
* From udp.html on the website:
|
||||
|
||||
<p>The automation of collaborative reachability testing for peers is
|
||||
@@ -30,12 +34,27 @@ quite simple:</p>
|
||||
|
||||
<pre>
|
||||
Alice Bob Charlie
|
||||
|
||||
runTest()
|
||||
sendTestToBob() receiveFromAliceAsBob()
|
||||
PeerTest ------------------->
|
||||
|
||||
sendTestToCharlie() receiveFromBobAsCharlie()
|
||||
PeerTest-------------------->
|
||||
|
||||
receiveFromCharlieAsBob()
|
||||
<-------------------PeerTest
|
||||
|
||||
receiveTestReply()
|
||||
<-------------------PeerTest
|
||||
|
||||
receiveTestReply()
|
||||
<------------------------------------------PeerTest
|
||||
|
||||
receiveFromAliceAsCharlie()
|
||||
PeerTest------------------------------------------>
|
||||
|
||||
receiveTestReply()
|
||||
<------------------------------------------PeerTest
|
||||
</pre>
|
||||
|
||||
@@ -104,9 +123,27 @@ class PeerTestManager {
|
||||
private boolean _currentTestComplete;
|
||||
/** as Alice */
|
||||
private final Queue<Long> _recentTests;
|
||||
private final IPThrottler _throttle;
|
||||
|
||||
private static final int MAX_RELAYED_PER_TEST_ALICE = 9;
|
||||
private static final int MAX_RELAYED_PER_TEST_BOB = 6;
|
||||
private static final int MAX_RELAYED_PER_TEST_CHARLIE = 6;
|
||||
|
||||
/** longest we will keep track of a Charlie nonce for */
|
||||
private static final int MAX_CHARLIE_LIFETIME = 10*1000;
|
||||
private static final int MAX_CHARLIE_LIFETIME = 15*1000;
|
||||
|
||||
/** as Bob/Charlie */
|
||||
private static final int MAX_ACTIVE_TESTS = 20;
|
||||
private static final int MAX_RECENT_TESTS = 40;
|
||||
|
||||
/** for the throttler */
|
||||
private static final int MAX_PER_IP = 12;
|
||||
private static final long THROTTLE_CLEAN_TIME = 10*60*1000;
|
||||
|
||||
/** initial - ContinueTest adds backoff */
|
||||
private static final int RESEND_TIMEOUT = 4*1000;
|
||||
private static final int MAX_TEST_TIME = 30*1000;
|
||||
private static final long MAX_NONCE = (1l << 32) - 1l;
|
||||
|
||||
/**
|
||||
* Have seen peer tests (as Alice) get stuck (_currentTest != null)
|
||||
@@ -120,15 +157,12 @@ class PeerTestManager {
|
||||
_activeTests = new ConcurrentHashMap();
|
||||
_recentTests = new LinkedBlockingQueue();
|
||||
_packetBuilder = new PacketBuilder(context, transport);
|
||||
_throttle = new IPThrottler(MAX_PER_IP, THROTTLE_CLEAN_TIME);
|
||||
_context.statManager().createRateStat("udp.statusKnownCharlie", "How often the bob we pick passes us to a charlie we already have a session with?", "udp", UDPTransport.RATES);
|
||||
_context.statManager().createRateStat("udp.receiveTestReply", "How often we get a reply to our peer test?", "udp", UDPTransport.RATES);
|
||||
_context.statManager().createRateStat("udp.receiveTest", "How often we get a packet requesting us to participate in a peer test?", "udp", UDPTransport.RATES);
|
||||
_context.statManager().createRateStat("udp.testBadIP", "Received IP or port was bad", "udp", UDPTransport.RATES);
|
||||
}
|
||||
|
||||
private static final int RESEND_TIMEOUT = 5*1000;
|
||||
private static final int MAX_TEST_TIME = 30*1000;
|
||||
private static final long MAX_NONCE = (1l << 32) - 1l;
|
||||
//public void runTest(InetAddress bobIP, int bobPort, SessionKey bobIntroKey) {
|
||||
|
||||
/**
|
||||
* The next few methods are for when we are Alice
|
||||
@@ -136,42 +170,60 @@ class PeerTestManager {
|
||||
public synchronized void runTest(InetAddress bobIP, int bobPort, SessionKey bobCipherKey, SessionKey bobMACKey) {
|
||||
if (_currentTest != null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("We are already running a test with bob = " + _currentTest.getBobIP() + ", aborting test with bob = " + bobIP);
|
||||
_log.warn("We are already running a test: " + _currentTest + ", aborting test with bob = " + bobIP);
|
||||
return;
|
||||
}
|
||||
PeerTestState test = new PeerTestState();
|
||||
test.setNonce(_context.random().nextLong(MAX_NONCE));
|
||||
if (DataHelper.eq(bobIP.getAddress(), 0, _transport.getExternalIP(), 0, 2)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Not running test with Bob too close to us " + bobIP);
|
||||
return;
|
||||
}
|
||||
PeerTestState test = new PeerTestState(ALICE,
|
||||
_context.random().nextLong(MAX_NONCE),
|
||||
_context.clock().now());
|
||||
test.setBobIP(bobIP);
|
||||
test.setBobPort(bobPort);
|
||||
test.setBobCipherKey(bobCipherKey);
|
||||
test.setBobMACKey(bobMACKey);
|
||||
test.setBeginTime(_context.clock().now());
|
||||
test.setLastSendTime(test.getBeginTime());
|
||||
test.setOurRole(PeerTestState.ALICE);
|
||||
_currentTest = test;
|
||||
_currentTestComplete = false;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Running test with bob = " + bobIP + ":" + bobPort + " " + test.getNonce());
|
||||
while (_recentTests.size() > 16)
|
||||
_log.debug("Start new test: " + test);
|
||||
while (_recentTests.size() > MAX_RECENT_TESTS)
|
||||
_recentTests.poll();
|
||||
_recentTests.offer(Long.valueOf(test.getNonce()));
|
||||
|
||||
test.incrementPacketsRelayed();
|
||||
sendTestToBob();
|
||||
|
||||
_context.simpleScheduler().addEvent(new ContinueTest(), RESEND_TIMEOUT);
|
||||
_context.simpleScheduler().addEvent(new ContinueTest(test.getNonce()), RESEND_TIMEOUT);
|
||||
}
|
||||
|
||||
private class ContinueTest implements SimpleTimer.TimedEvent {
|
||||
private final long _nonce;
|
||||
|
||||
public ContinueTest(long nonce) {
|
||||
_nonce = nonce;
|
||||
}
|
||||
|
||||
public void timeReached() {
|
||||
synchronized (PeerTestManager.this) {
|
||||
PeerTestState state = _currentTest;
|
||||
if (state == null) {
|
||||
// already completed
|
||||
if (state == null || state.getNonce() != _nonce) {
|
||||
// already completed, possibly on to the next test
|
||||
return;
|
||||
} else if (expired()) {
|
||||
testComplete(true);
|
||||
} else if (_context.clock().now() - state.getLastSendTime() >= RESEND_TIMEOUT) {
|
||||
int sent = state.incrementPacketsRelayed();
|
||||
if (sent > MAX_RELAYED_PER_TEST_ALICE) {
|
||||
testComplete(false);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Sent too many packets: " + state);
|
||||
return;
|
||||
}
|
||||
if (state.getReceiveBobTime() <= 0) {
|
||||
// no message from Bob yet, send it again
|
||||
sendTestToBob();
|
||||
@@ -184,7 +236,8 @@ class PeerTestManager {
|
||||
// second message from Charlie yet
|
||||
sendTestToCharlie();
|
||||
}
|
||||
_context.simpleScheduler().addEvent(ContinueTest.this, RESEND_TIMEOUT);
|
||||
// retx at 4, 10, 17, 25 elapsed time
|
||||
_context.simpleScheduler().addEvent(ContinueTest.this, RESEND_TIMEOUT + (sent*1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,19 +257,20 @@ class PeerTestManager {
|
||||
PeerTestState test = _currentTest;
|
||||
if (!expired()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Sending test to bob: " + test.getBobIP() + ":" + test.getBobPort());
|
||||
_log.debug("Sending test to Bob: " + test);
|
||||
_transport.send(_packetBuilder.buildPeerTestFromAlice(test.getBobIP(), test.getBobPort(), test.getBobCipherKey(), test.getBobMACKey(), //_bobIntroKey,
|
||||
test.getNonce(), _transport.getIntroKey()));
|
||||
} else {
|
||||
_currentTest = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** call from a synchronized method */
|
||||
private void sendTestToCharlie() {
|
||||
PeerTestState test = _currentTest;
|
||||
if (!expired()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Sending test to charlie: " + test.getCharlieIP() + ":" + test.getCharliePort());
|
||||
_log.debug("Sending test to Charlie: " + test);
|
||||
_transport.send(_packetBuilder.buildPeerTestFromAlice(test.getCharlieIP(), test.getCharliePort(), test.getCharlieIntroKey(),
|
||||
test.getNonce(), _transport.getIntroKey()));
|
||||
} else {
|
||||
@@ -240,7 +294,7 @@ class PeerTestManager {
|
||||
* test. We are Alice.
|
||||
*/
|
||||
private synchronized void receiveTestReply(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo) {
|
||||
_context.statManager().addRateData("udp.receiveTestReply", 1, 0);
|
||||
_context.statManager().addRateData("udp.receiveTestReply", 1);
|
||||
PeerTestState test = _currentTest;
|
||||
if (expired())
|
||||
return;
|
||||
@@ -270,16 +324,20 @@ class PeerTestManager {
|
||||
InetAddress addr = InetAddress.getByAddress(ip);
|
||||
test.setAliceIP(addr);
|
||||
test.setReceiveBobTime(_context.clock().now());
|
||||
test.setAlicePort(testInfo.readPort());
|
||||
int testPort = testInfo.readPort();
|
||||
if (testPort == 0)
|
||||
throw new UnknownHostException("port 0");
|
||||
test.setAlicePort(testPort);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Receive test reply from bob @ " + from + " via our " + test.getAlicePort() + "/" + test.getAlicePortFromCharlie());
|
||||
_log.debug("Receive test reply from Bob: " + test);
|
||||
if (test.getAlicePortFromCharlie() > 0)
|
||||
testComplete(false);
|
||||
} catch (UnknownHostException uhe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Unable to get our IP (length " + ipSize +
|
||||
") from bob's reply: " + from + ", " + testInfo, uhe);
|
||||
_context.statManager().addRateData("udp.testBadIP", 1);
|
||||
}
|
||||
} else {
|
||||
// The reply is from Charlie
|
||||
@@ -294,7 +352,7 @@ class PeerTestManager {
|
||||
+ _currentTest + ", charlie: " + from + ")");
|
||||
// why are we doing this instead of calling testComplete() ?
|
||||
_currentTestComplete = true;
|
||||
_context.statManager().addRateData("udp.statusKnownCharlie", 1, 0);
|
||||
_context.statManager().addRateData("udp.statusKnownCharlie", 1);
|
||||
honorStatus(CommSystemFacade.STATUS_UNKNOWN);
|
||||
_currentTest = null;
|
||||
return;
|
||||
@@ -302,26 +360,29 @@ class PeerTestManager {
|
||||
|
||||
if (test.getReceiveCharlieTime() > 0) {
|
||||
// this is our second charlie, yay!
|
||||
test.setAlicePortFromCharlie(testInfo.readPort());
|
||||
byte ip[] = new byte[testInfo.readIPSize()];
|
||||
testInfo.readIP(ip, 0);
|
||||
try {
|
||||
int testPort = testInfo.readPort();
|
||||
if (testPort == 0)
|
||||
throw new UnknownHostException("port 0");
|
||||
test.setAlicePortFromCharlie(testPort);
|
||||
byte ip[] = new byte[testInfo.readIPSize()];
|
||||
testInfo.readIP(ip, 0);
|
||||
InetAddress addr = InetAddress.getByAddress(ip);
|
||||
test.setAliceIPFromCharlie(addr);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Receive test reply from charlie @ " + test.getCharlieIP() + " via our "
|
||||
+ test.getAlicePort() + "/" + test.getAlicePortFromCharlie());
|
||||
_log.debug("Receive test reply from Charlie: " + test);
|
||||
if (test.getReceiveBobTime() > 0)
|
||||
testComplete(true);
|
||||
} catch (UnknownHostException uhe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Charlie @ " + from + " said we were an invalid IP address: " + uhe.getMessage(), uhe);
|
||||
_context.statManager().addRateData("udp.testBadIP", 1);
|
||||
}
|
||||
} else {
|
||||
if (test.getPacketsRelayed() > MAX_RELAYED_PER_TEST) {
|
||||
if (test.incrementPacketsRelayed() > MAX_RELAYED_PER_TEST_ALICE) {
|
||||
testComplete(false);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received too many packets on the test: " + test);
|
||||
_log.warn("Sent too many packets on the test: " + test);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -338,11 +399,12 @@ class PeerTestManager {
|
||||
test.setCharlieIP(InetAddress.getByAddress(from.getIP()));
|
||||
test.setCharliePort(from.getPort());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Receive test from charlie @ " + from);
|
||||
_log.debug("Receive test from Charlie: " + test);
|
||||
sendTestToCharlie();
|
||||
} catch (UnknownHostException uhe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Charlie's IP is b0rked: " + from + ": " + testInfo);
|
||||
_context.statManager().addRateData("udp.testBadIP", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -409,44 +471,119 @@ class PeerTestManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for all incoming packets. Most of the source and dest validation is here.
|
||||
*
|
||||
* Receive a test message of some sort from the given peer, queueing up any packet
|
||||
* that should be sent in response, or if its a reply to our own current testing,
|
||||
* adjusting our test state.
|
||||
*
|
||||
* We could be Alice, Bob, or Charlie.
|
||||
*/
|
||||
public void receiveTest(RemoteHostId from, UDPPacketReader reader) {
|
||||
_context.statManager().addRateData("udp.receiveTest", 1, 0);
|
||||
_context.statManager().addRateData("udp.receiveTest", 1);
|
||||
byte[] fromIP = from.getIP();
|
||||
int fromPort = from.getPort();
|
||||
if (fromPort < 1024 || fromPort > 65535 ||
|
||||
(!_transport.isValid(fromIP)) ||
|
||||
DataHelper.eq(fromIP, 0, _transport.getExternalIP(), 0, 2) ||
|
||||
_context.blocklist().isBlocklisted(fromIP)) {
|
||||
// spoof check, and don't respond to privileged ports
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Invalid PeerTest address: " + Addresses.toString(fromIP, fromPort));
|
||||
_context.statManager().addRateData("udp.testBadIP", 1);
|
||||
return;
|
||||
}
|
||||
UDPPacketReader.PeerTestReader testInfo = reader.getPeerTestReader();
|
||||
byte testIP[] = null;
|
||||
int testPort = testInfo.readPort();
|
||||
|
||||
if (testInfo.readIPSize() > 0) {
|
||||
testIP = new byte[testInfo.readIPSize()];
|
||||
testInfo.readIP(testIP, 0);
|
||||
}
|
||||
|
||||
if ((testPort > 0 && (testPort < 1024 || testPort > 65535)) ||
|
||||
(testIP != null &&
|
||||
((!_transport.isValid(testIP)) ||
|
||||
_context.blocklist().isBlocklisted(testIP)))) {
|
||||
// spoof check, and don't respond to privileged ports
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Invalid address in PeerTest: " + Addresses.toString(testIP, testPort));
|
||||
_context.statManager().addRateData("udp.testBadIP", 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// The from IP/port and message's IP/port are now validated.
|
||||
// EXCEPT that either the message's IP could be empty or the message's port could be 0.
|
||||
// Both of those cases should be checked in receiveXfromY() as appropriate.
|
||||
// Also, IP could be us, check is below.
|
||||
|
||||
long nonce = testInfo.readNonce();
|
||||
PeerTestState test = _currentTest;
|
||||
if ( (test != null) && (test.getNonce() == nonce) ) {
|
||||
// we are Alice
|
||||
// we are Alice, we initiated the test
|
||||
receiveTestReply(from, testInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
// we are Bob or Charlie
|
||||
// we are Bob or Charlie, we are helping Alice
|
||||
|
||||
if ( (testInfo.readIPSize() > 0) && (testPort > 0) ) {
|
||||
testIP = new byte[testInfo.readIPSize()];
|
||||
testInfo.readIP(testIP, 0);
|
||||
if (_throttle.shouldThrottle(fromIP)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("PeerTest throttle from " + Addresses.toString(fromIP, fromPort));
|
||||
return;
|
||||
}
|
||||
|
||||
// use the same counter for both from and to IPs
|
||||
if (testIP != null && _throttle.shouldThrottle(testIP)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("PeerTest throttle to " + Addresses.toString(testIP, testPort));
|
||||
return;
|
||||
}
|
||||
|
||||
Long lNonce = Long.valueOf(nonce);
|
||||
PeerTestState state = _activeTests.get(lNonce);
|
||||
|
||||
if (testIP != null && DataHelper.eq(testIP, 0, _transport.getExternalIP(), 0, 2)) {
|
||||
// spoof check - have to do this after receiveTestReply(), since
|
||||
// the field should be us there.
|
||||
// Let's also eliminate anybody in the same /16
|
||||
if (_recentTests.contains(lNonce)) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Got delayed reply on nonce " + nonce +
|
||||
" from: " + Addresses.toString(fromIP, fromPort));
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Nearby address in PeerTest: " + Addresses.toString(testIP, testPort) +
|
||||
" from: " + Addresses.toString(fromIP, fromPort) +
|
||||
" state? " + state);
|
||||
_context.statManager().addRateData("udp.testBadIP", 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PeerTestState state = _activeTests.get(Long.valueOf(nonce));
|
||||
|
||||
if (state == null) {
|
||||
// NEW TEST
|
||||
if ( (testIP == null) || (testPort <= 0) ) {
|
||||
// we are bob, since we haven't seen this nonce before AND its coming from alice
|
||||
if (_activeTests.size() >= MAX_ACTIVE_TESTS) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Too many active tests, droppping from Alice " + Addresses.toString(fromIP, fromPort));
|
||||
return;
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("test IP/port are blank coming from " + from + ", assuming we are Bob and they are alice");
|
||||
receiveFromAliceAsBob(from, testInfo, nonce, null);
|
||||
} else {
|
||||
if (_recentTests.contains(Long.valueOf(nonce))) {
|
||||
if (_recentTests.contains(lNonce)) {
|
||||
// ignore the packet, as its a holdover from a recently completed locally
|
||||
// initiated test
|
||||
} else {
|
||||
if (_activeTests.size() >= MAX_ACTIVE_TESTS) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Too many active tests, droppping from Bob " + Addresses.toString(fromIP, fromPort));
|
||||
return;
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("We are charlie, as the testIP/port is " + Addresses.toString(testIP, testPort) + " and the state is unknown for " + nonce);
|
||||
// we are charlie, since alice never sends us her IP and port, only bob does (and,
|
||||
@@ -455,20 +592,21 @@ class PeerTestManager {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (state.getOurRole() == PeerTestState.BOB) {
|
||||
if (DataHelper.eq(from.getIP(), state.getAliceIP().getAddress()) &&
|
||||
(from.getPort() == state.getAlicePort()) ) {
|
||||
// EXISTING TEST
|
||||
if (state.getOurRole() == BOB) {
|
||||
if (DataHelper.eq(fromIP, state.getAliceIP().getAddress()) &&
|
||||
(fromPort == state.getAlicePort()) ) {
|
||||
receiveFromAliceAsBob(from, testInfo, nonce, state);
|
||||
} else if (DataHelper.eq(from.getIP(), state.getCharlieIP().getAddress()) &&
|
||||
(from.getPort() == state.getCharliePort()) ) {
|
||||
} else if (DataHelper.eq(fromIP, state.getCharlieIP().getAddress()) &&
|
||||
(fromPort == state.getCharliePort()) ) {
|
||||
receiveFromCharlieAsBob(from, state);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received from a fourth party as bob! alice: " + state.getAliceIP() + ", charlie: " + state.getCharlieIP() + ", dave: " + from);
|
||||
}
|
||||
} else if (state.getOurRole() == PeerTestState.CHARLIE) {
|
||||
} else if (state.getOurRole() == CHARLIE) {
|
||||
if ( (testIP == null) || (testPort <= 0) ) {
|
||||
receiveFromAliceAsCharlie(from, testInfo, nonce);
|
||||
receiveFromAliceAsCharlie(from, testInfo, nonce, state);
|
||||
} else {
|
||||
receiveFromBobAsCharlie(from, testInfo, nonce, state);
|
||||
}
|
||||
@@ -478,29 +616,34 @@ class PeerTestManager {
|
||||
|
||||
// Below here are methods for when we are Bob or Charlie
|
||||
|
||||
private static final int MAX_RELAYED_PER_TEST = 5;
|
||||
|
||||
/**
|
||||
* The packet's IP/port does not match the IP/port included in the message,
|
||||
* so we must be Charlie receiving a PeerTest from Bob.
|
||||
*
|
||||
* @param state null if new
|
||||
*/
|
||||
private void receiveFromBobAsCharlie(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo, long nonce, PeerTestState state) {
|
||||
long now = _context.clock().now();
|
||||
boolean isNew = false;
|
||||
if (state == null) {
|
||||
isNew = true;
|
||||
state = new PeerTestState();
|
||||
state.setOurRole(PeerTestState.CHARLIE);
|
||||
state = new PeerTestState(CHARLIE, nonce, now);
|
||||
} else {
|
||||
if (state.getReceiveBobTime() > now - (RESEND_TIMEOUT / 2)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Too soon, not retransmitting: " + state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Receive test as charlie nonce " + nonce);
|
||||
|
||||
|
||||
// TODO should only do most of this if isNew
|
||||
int sz = testInfo.readIPSize();
|
||||
byte aliceIPData[] = new byte[sz];
|
||||
try {
|
||||
testInfo.readIP(aliceIPData, 0);
|
||||
int alicePort = testInfo.readPort();
|
||||
if (alicePort == 0)
|
||||
throw new UnknownHostException("port 0");
|
||||
InetAddress aliceIP = InetAddress.getByAddress(aliceIPData);
|
||||
InetAddress bobIP = InetAddress.getByAddress(from.getIP());
|
||||
SessionKey aliceIntroKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
|
||||
@@ -509,12 +652,10 @@ class PeerTestManager {
|
||||
state.setAliceIP(aliceIP);
|
||||
state.setAlicePort(alicePort);
|
||||
state.setAliceIntroKey(aliceIntroKey);
|
||||
state.setNonce(nonce);
|
||||
state.setBobIP(bobIP);
|
||||
state.setBobPort(from.getPort());
|
||||
state.setLastSendTime(_context.clock().now());
|
||||
state.setOurRole(PeerTestState.CHARLIE);
|
||||
state.setReceiveBobTime(_context.clock().now());
|
||||
state.setLastSendTime(now);
|
||||
state.setReceiveBobTime(now);
|
||||
|
||||
PeerState bob = _transport.getPeerState(from);
|
||||
if (bob == null) {
|
||||
@@ -526,16 +667,15 @@ class PeerTestManager {
|
||||
state.setBobMACKey(bob.getCurrentMACKey());
|
||||
}
|
||||
|
||||
state.incrementPacketsRelayed();
|
||||
if (state.getPacketsRelayed() > MAX_RELAYED_PER_TEST) {
|
||||
// we send two packets below, but increment just once
|
||||
if (state.incrementPacketsRelayed() > MAX_RELAYED_PER_TEST_CHARLIE) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Receive from bob (" + from + ") as charlie with alice @ " + aliceIP + ":" + alicePort
|
||||
+ ", but we've already relayed too many packets to that test, so we're dropping it");
|
||||
_log.warn("Too many, not retransmitting: " + state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Receive from bob (" + from + ") as charlie, sending back to bob and sending to alice @ " + aliceIP + ":" + alicePort);
|
||||
_log.debug("Receive from Bob: " + state);
|
||||
|
||||
if (isNew) {
|
||||
_activeTests.put(Long.valueOf(nonce), state);
|
||||
@@ -550,6 +690,7 @@ class PeerTestManager {
|
||||
} catch (UnknownHostException uhe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to build the aliceIP from " + from + ", ip size: " + sz + " ip val: " + Base64.encode(aliceIPData), uhe);
|
||||
_context.statManager().addRateData("udp.testBadIP", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,20 +698,20 @@ class PeerTestManager {
|
||||
* The PeerTest message came from the peer referenced in the message (or there wasn't
|
||||
* any info in the message), plus we are not acting as Charlie (so we've got to be Bob).
|
||||
*
|
||||
* testInfo IP/port ignored
|
||||
* @param state null if new
|
||||
*/
|
||||
private void receiveFromAliceAsBob(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo, long nonce, PeerTestState state) {
|
||||
// we are Bob, so pick a (potentially) Charlie and send Charlie Alice's info
|
||||
PeerState charlie = null;
|
||||
PeerState charlie;
|
||||
RouterInfo charlieInfo = null;
|
||||
if (state == null) { // pick a new charlie
|
||||
charlie = _transport.pickTestPeer(from);
|
||||
if (charlie != null)
|
||||
charlieInfo = _context.netDb().lookupRouterInfoLocally(charlie.getRemotePeer());
|
||||
} else {
|
||||
charlie = _transport.getPeerState(new RemoteHostId(state.getCharlieIP().getAddress(), state.getCharliePort()));
|
||||
if (charlie != null)
|
||||
charlieInfo = _context.netDb().lookupRouterInfoLocally(charlie.getRemotePeer());
|
||||
}
|
||||
if (charlie != null)
|
||||
charlieInfo = _context.netDb().lookupRouterInfoLocally(charlie.getRemotePeer());
|
||||
|
||||
if ( (charlie == null) || (charlieInfo == null) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@@ -578,6 +719,7 @@ class PeerTestManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO should only do most of this if isNew
|
||||
InetAddress aliceIP = null;
|
||||
SessionKey aliceIntroKey = null;
|
||||
try {
|
||||
@@ -597,28 +739,30 @@ class PeerTestManager {
|
||||
//UDPPacket packet = _packetBuilder.buildPeerTestToAlice(aliceIP, from.getPort(), aliceIntroKey, charlieIntroKey, nonce);
|
||||
//_transport.send(packet);
|
||||
|
||||
long now = _context.clock().now();
|
||||
boolean isNew = false;
|
||||
if (state == null) {
|
||||
isNew = true;
|
||||
state = new PeerTestState();
|
||||
state.setBeginTime(_context.clock().now());
|
||||
state = new PeerTestState(BOB, nonce, now);
|
||||
} else {
|
||||
if (state.getReceiveAliceTime() > now - (RESEND_TIMEOUT / 2)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Too soon, not retransmitting: " + state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
state.setAliceIP(aliceIP);
|
||||
state.setAlicePort(from.getPort());
|
||||
state.setAliceIntroKey(aliceIntroKey);
|
||||
state.setNonce(nonce);
|
||||
state.setCharlieIP(charlie.getRemoteIPAddress());
|
||||
state.setCharliePort(charlie.getRemotePort());
|
||||
state.setCharlieIntroKey(charlieIntroKey);
|
||||
state.setLastSendTime(_context.clock().now());
|
||||
state.setOurRole(PeerTestState.BOB);
|
||||
state.setReceiveAliceTime(_context.clock().now());
|
||||
state.setLastSendTime(now);
|
||||
state.setReceiveAliceTime(now);
|
||||
|
||||
state.incrementPacketsRelayed();
|
||||
if (state.getPacketsRelayed() > MAX_RELAYED_PER_TEST) {
|
||||
if (state.incrementPacketsRelayed() > MAX_RELAYED_PER_TEST_BOB) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Receive from alice (" + aliceIP + ":" + from.getPort()
|
||||
+ ") as bob, but we've already relayed too many packets to that test, so we're dropping it");
|
||||
_log.warn("Too many, not retransmitting: " + state);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -634,13 +778,13 @@ class PeerTestManager {
|
||||
charlie.getCurrentMACKey());
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Receive from alice as bob for " + nonce + ", picking charlie @ " + charlie.getRemoteIPAddress() + ":"
|
||||
+ charlie.getRemotePort() + " for alice @ " + aliceIP + ":" + from.getPort());
|
||||
_log.debug("Receive from Alice: " + state);
|
||||
|
||||
_transport.send(packet);
|
||||
} catch (UnknownHostException uhe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to build the aliceIP from " + from, uhe);
|
||||
_context.statManager().addRateData("udp.testBadIP", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -648,23 +792,30 @@ class PeerTestManager {
|
||||
* The PeerTest message came from one of the Charlies picked for an existing test, so send Alice the
|
||||
* packet verifying participation.
|
||||
*
|
||||
* testInfo IP/port ignored
|
||||
* @param state non-null
|
||||
*/
|
||||
private void receiveFromCharlieAsBob(RemoteHostId from, PeerTestState state) {
|
||||
state.setReceiveCharlieTime(_context.clock().now());
|
||||
|
||||
state.incrementPacketsRelayed();
|
||||
if (state.getPacketsRelayed() > MAX_RELAYED_PER_TEST) {
|
||||
long now = _context.clock().now();
|
||||
if (state.getReceiveCharlieTime() > now - (RESEND_TIMEOUT / 2)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received from charlie (" + from + ") as bob (" + state + "), but we've already relayed too many, so drop it");
|
||||
_log.warn("Too soon, not retransmitting: " + state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.incrementPacketsRelayed() > MAX_RELAYED_PER_TEST_BOB) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Too many, not retransmitting: " + state);
|
||||
return;
|
||||
}
|
||||
state.setReceiveCharlieTime(now);
|
||||
|
||||
UDPPacket packet = _packetBuilder.buildPeerTestToAlice(state.getAliceIP(), state.getAlicePort(),
|
||||
state.getAliceIntroKey(), state.getCharlieIntroKey(),
|
||||
state.getNonce());
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Receive from charlie @ " + from + " as bob, sending alice back the ok @ " + state.getAliceIP() + ":" + state.getAlicePort());
|
||||
_log.debug("Receive from Charlie, sending Alice back the OK: " + state);
|
||||
|
||||
_transport.send(packet);
|
||||
}
|
||||
@@ -672,32 +823,52 @@ class PeerTestManager {
|
||||
/**
|
||||
* We are charlie, so send Alice her PeerTest message
|
||||
*
|
||||
* testInfo IP/port ignored
|
||||
* @param state non-null
|
||||
*/
|
||||
private void receiveFromAliceAsCharlie(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo, long nonce) {
|
||||
private void receiveFromAliceAsCharlie(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo,
|
||||
long nonce, PeerTestState state) {
|
||||
long now = _context.clock().now();
|
||||
if (state.getReceiveAliceTime() > now - (RESEND_TIMEOUT / 2)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Too soon, not retransmitting: " + state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.incrementPacketsRelayed() > MAX_RELAYED_PER_TEST_CHARLIE) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Too many, not retransmitting: " + state);
|
||||
return;
|
||||
}
|
||||
state.setReceiveAliceTime(now);
|
||||
|
||||
try {
|
||||
InetAddress aliceIP = InetAddress.getByAddress(from.getIP());
|
||||
SessionKey aliceIntroKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
|
||||
testInfo.readIntroKey(aliceIntroKey.getData(), 0);
|
||||
UDPPacket packet = _packetBuilder.buildPeerTestToAlice(aliceIP, from.getPort(), aliceIntroKey, _transport.getIntroKey(), nonce);
|
||||
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Receive from alice as charlie, w/ alice @ " + aliceIP + ":" + from.getPort() + " and nonce " + nonce);
|
||||
_log.debug("Receive from Alice: " + state);
|
||||
|
||||
_transport.send(packet);
|
||||
} catch (UnknownHostException uhe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to build the aliceIP from " + from, uhe);
|
||||
_context.statManager().addRateData("udp.testBadIP", 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* forget about charlie's nonce after 60s.
|
||||
* forget about charlie's nonce after a short while.
|
||||
*/
|
||||
private class RemoveTest implements SimpleTimer.TimedEvent {
|
||||
private long _nonce;
|
||||
private final long _nonce;
|
||||
|
||||
public RemoveTest(long nonce) {
|
||||
_nonce = nonce;
|
||||
}
|
||||
|
||||
public void timeReached() {
|
||||
_activeTests.remove(Long.valueOf(_nonce));
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package net.i2p.router.transport.udp;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.i2p.data.SessionKey;
|
||||
|
||||
/**
|
||||
*
|
||||
* Track the state of a peer test.
|
||||
* Used only by PeerTestManager.
|
||||
*/
|
||||
class PeerTestState {
|
||||
private long _testNonce;
|
||||
private short _ourRole;
|
||||
private final long _testNonce;
|
||||
private final Role _ourRole;
|
||||
private InetAddress _aliceIP;
|
||||
private int _alicePort;
|
||||
private InetAddress _bobIP;
|
||||
@@ -22,22 +24,26 @@ class PeerTestState {
|
||||
private SessionKey _charlieIntroKey;
|
||||
private SessionKey _bobCipherKey;
|
||||
private SessionKey _bobMACKey;
|
||||
private long _beginTime;
|
||||
private final long _beginTime;
|
||||
private long _lastSendTime;
|
||||
private long _receiveAliceTime;
|
||||
private long _receiveBobTime;
|
||||
private long _receiveCharlieTime;
|
||||
private int _packetsRelayed;
|
||||
private final AtomicInteger _packetsRelayed = new AtomicInteger();
|
||||
|
||||
public static final short ALICE = 1;
|
||||
public static final short BOB = 2;
|
||||
public static final short CHARLIE = 3;
|
||||
public enum Role {ALICE, BOB, CHARLIE};
|
||||
|
||||
public PeerTestState(Role role, long nonce, long now) {
|
||||
_ourRole = role;
|
||||
_testNonce = nonce;
|
||||
_beginTime = now;
|
||||
}
|
||||
|
||||
public long getNonce() { return _testNonce; }
|
||||
public void setNonce(long nonce) { _testNonce = nonce; }
|
||||
|
||||
/** Are we Alice, bob, or Charlie. */
|
||||
public short getOurRole() { return _ourRole; }
|
||||
public void setOurRole(short role) { _ourRole = role; }
|
||||
public Role getOurRole() { return _ourRole; }
|
||||
|
||||
/**
|
||||
* If we are Alice, this will contain the IP that Bob says we
|
||||
* can be reached at - the IP Charlie says we can be reached
|
||||
@@ -79,47 +85,49 @@ class PeerTestState {
|
||||
|
||||
/** when did this test begin? */
|
||||
public long getBeginTime() { return _beginTime; }
|
||||
public void setBeginTime(long when) { _beginTime = when; }
|
||||
|
||||
/** when did we last send out a packet? */
|
||||
public long getLastSendTime() { return _lastSendTime; }
|
||||
public void setLastSendTime(long when) { _lastSendTime = when; }
|
||||
/** when did we last hear from alice? */
|
||||
|
||||
/**
|
||||
* when did we last hear from alice?
|
||||
*/
|
||||
public long getReceiveAliceTime() { return _receiveAliceTime; }
|
||||
public void setReceiveAliceTime(long when) { _receiveAliceTime = when; }
|
||||
|
||||
/** when did we last hear from bob? */
|
||||
public long getReceiveBobTime() { return _receiveBobTime; }
|
||||
public void setReceiveBobTime(long when) { _receiveBobTime = when; }
|
||||
|
||||
/** when did we last hear from charlie? */
|
||||
public long getReceiveCharlieTime() { return _receiveCharlieTime; }
|
||||
public void setReceiveCharlieTime(long when) { _receiveCharlieTime = when; }
|
||||
|
||||
public int getPacketsRelayed() { return _packetsRelayed; }
|
||||
public void incrementPacketsRelayed() { ++_packetsRelayed; }
|
||||
/** @return new value */
|
||||
public int incrementPacketsRelayed() { return _packetsRelayed.incrementAndGet(); }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(512);
|
||||
buf.append("Role: ");
|
||||
if (_ourRole == ALICE) buf.append("Alice");
|
||||
else if (_ourRole == BOB) buf.append("Bob");
|
||||
else if (_ourRole == CHARLIE) buf.append("Charlie");
|
||||
else buf.append("unkown!");
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
buf.append("PeerTest ").append(_testNonce)
|
||||
.append(" as ").append(_ourRole.toString());
|
||||
if (_aliceIP != null)
|
||||
buf.append(" alice: ").append(_aliceIP).append(':').append(_alicePort);
|
||||
buf.append("; Alice: ").append(_aliceIP).append(':').append(_alicePort);
|
||||
if (_aliceIPFromCharlie != null)
|
||||
buf.append(" (fromCharlie ").append(_aliceIPFromCharlie).append(':').append(_alicePortFromCharlie).append(')');
|
||||
if (_bobIP != null)
|
||||
buf.append(" bob: ").append(_bobIP).append(':').append(_bobPort);
|
||||
buf.append("; Bob: ").append(_bobIP).append(':').append(_bobPort);
|
||||
if (_charlieIP != null)
|
||||
buf.append(" charlie: ").append(_charlieIP).append(':').append(_charliePort);
|
||||
buf.append(" last send after ").append(_lastSendTime - _beginTime).append("ms");
|
||||
buf.append(" Charlie: ").append(_charlieIP).append(':').append(_charliePort);
|
||||
buf.append("; last send after ").append(_lastSendTime - _beginTime);
|
||||
if (_receiveAliceTime > 0)
|
||||
buf.append(" receive from alice after ").append(_receiveAliceTime - _beginTime).append("ms");
|
||||
buf.append("; rcvd from Alice after ").append(_receiveAliceTime - _beginTime);
|
||||
if (_receiveBobTime > 0)
|
||||
buf.append(" receive from bob after ").append(_receiveBobTime - _beginTime).append("ms");
|
||||
buf.append("; rcvd from Bob after ").append(_receiveBobTime - _beginTime);
|
||||
if (_receiveCharlieTime > 0)
|
||||
buf.append(" receive from charlie after ").append(_receiveCharlieTime - _beginTime).append("ms");
|
||||
buf.append(" packets relayed: ").append(_packetsRelayed);
|
||||
buf.append("; rcvd from Charlie after ").append(_receiveCharlieTime - _beginTime);
|
||||
buf.append("; pkts relayed: ").append(_packetsRelayed.get());
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,8 @@ class UDPEndpoint {
|
||||
private DatagramSocket getSocket() {
|
||||
DatagramSocket socket = null;
|
||||
int port = _listenPort;
|
||||
if (port > 0 && port < 1024)
|
||||
_log.logAlways(Log.WARN, "Specified UDP port is " + port + ", ports lower than 1024 not recommended");
|
||||
|
||||
for (int i = 0; i < MAX_PORT_RETRIES; i++) {
|
||||
if (port <= 0) {
|
||||
@@ -113,7 +115,7 @@ class UDPEndpoint {
|
||||
break;
|
||||
} catch (SocketException se) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Binding to port " + port + " failed: " + se);
|
||||
_log.warn("Binding to port " + port + " failed", se);
|
||||
}
|
||||
port = -1;
|
||||
}
|
||||
@@ -157,6 +159,7 @@ class UDPEndpoint {
|
||||
* @since 0.9.2
|
||||
*/
|
||||
public void clearOutbound() {
|
||||
_sender.clear();
|
||||
if (_sender != null)
|
||||
_sender.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.router.OutNetMessage;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import net.i2p.router.transport.Transport;
|
||||
import net.i2p.router.transport.TransportBid;
|
||||
import net.i2p.router.transport.TransportImpl;
|
||||
@@ -51,6 +52,7 @@ import net.i2p.util.Translate;
|
||||
public class UDPTransport extends TransportImpl implements TimedWeightedPriorityMessageQueue.FailedListener {
|
||||
private final Log _log;
|
||||
private UDPEndpoint _endpoint;
|
||||
private final Object _addDropLock = new Object();
|
||||
/** Peer (Hash) to PeerState */
|
||||
private final Map<Hash, PeerState> _peersByIdent;
|
||||
/** RemoteHostId to PeerState */
|
||||
@@ -110,6 +112,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
*/
|
||||
public static final int DEFAULT_INTERNAL_PORT = 8887;
|
||||
|
||||
/**
|
||||
* To prevent trouble. To be raised to 1024 in 0.9.4.
|
||||
*
|
||||
* @since 0.9.3
|
||||
*/
|
||||
static final int MIN_PEER_PORT = 500;
|
||||
|
||||
/** Limits on port told to us by others,
|
||||
* We should have an exception if it matches the existing low port.
|
||||
*/
|
||||
@@ -404,7 +413,11 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
*/
|
||||
SessionKey getIntroKey() { return _introKey; }
|
||||
|
||||
public int getLocalPort() { return _externalListenPort; }
|
||||
/** @deprecated unused */
|
||||
public int getLocalPort() {
|
||||
return _endpoint != null ? _endpoint.getListenPort() : -1;
|
||||
}
|
||||
|
||||
public InetAddress getLocalAddress() { return _externalListenHost; }
|
||||
public int getExternalPort() { return _externalListenPort; }
|
||||
|
||||
@@ -708,7 +721,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
private boolean getIsPortFixed() {
|
||||
String prop = _context.getProperty(PROP_FIXED_PORT);
|
||||
if (prop != null)
|
||||
return Boolean.valueOf(prop).booleanValue();
|
||||
return Boolean.parseBoolean(prop);
|
||||
int status = getReachabilityStatus();
|
||||
return status != CommSystemFacade.STATUS_REJECT_UNSOLICITED;
|
||||
}
|
||||
@@ -720,6 +733,23 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
PeerState getPeerState(RemoteHostId hostInfo) {
|
||||
return _peersByRemoteHost.get(hostInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the states for all peers at the given remote host, ignoring port.
|
||||
* Used for a last-chance search for a peer that changed port, by PacketHandler.
|
||||
* @since 0.9.3
|
||||
*/
|
||||
List<PeerState> getPeerStatesByIP(RemoteHostId hostInfo) {
|
||||
List<PeerState> rv = new ArrayList(4);
|
||||
byte[] ip = hostInfo.getIP();
|
||||
if (ip != null) {
|
||||
for (PeerState ps : _peersByIdent.values()) {
|
||||
if (DataHelper.eq(ip, ps.getRemoteIP()))
|
||||
rv.add(ps);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the state for the peer with the given ident, or null
|
||||
@@ -729,6 +759,24 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
return _peersByIdent.get(remotePeer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and add to peersByRemoteHost map
|
||||
* @since 0.9.3
|
||||
*/
|
||||
public void changePeerPort(PeerState peer, int newPort) {
|
||||
int oldPort;
|
||||
synchronized (_addDropLock) {
|
||||
oldPort = peer.getRemotePort();
|
||||
if (oldPort != newPort) {
|
||||
_peersByRemoteHost.remove(peer.getRemoteHostId());
|
||||
peer.changePort(newPort);
|
||||
_peersByRemoteHost.put(peer.getRemoteHostId(), peer);
|
||||
}
|
||||
}
|
||||
if (_log.shouldLog(Log.WARN) && oldPort != newPort)
|
||||
_log.warn("Changed port from " + oldPort + " to " + newPort + " for " + peer);
|
||||
}
|
||||
|
||||
/**
|
||||
* For IntroductionManager
|
||||
* @return may be null if not started
|
||||
@@ -799,47 +847,69 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
boolean addRemotePeerState(PeerState peer) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Add remote peer state: " + peer);
|
||||
synchronized(_addDropLock) {
|
||||
return locked_addRemotePeerState(peer);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean locked_addRemotePeerState(PeerState peer) {
|
||||
Hash remotePeer = peer.getRemotePeer();
|
||||
long oldEstablishedOn = -1;
|
||||
PeerState oldPeer = null;
|
||||
if (remotePeer != null) {
|
||||
oldPeer = _peersByIdent.put(remotePeer, peer);
|
||||
if ( (oldPeer != null) && (oldPeer != peer) ) {
|
||||
// transfer over the old state/inbound message fragments/etc
|
||||
peer.loadFrom(oldPeer);
|
||||
oldEstablishedOn = oldPeer.getKeyEstablishedTime();
|
||||
}
|
||||
oldPeer = _peersByIdent.put(remotePeer, peer);
|
||||
if ( (oldPeer != null) && (oldPeer != peer) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Peer already connected (PBID): old=" + oldPeer + " new=" + peer);
|
||||
// transfer over the old state/inbound message fragments/etc
|
||||
peer.loadFrom(oldPeer);
|
||||
oldEstablishedOn = oldPeer.getKeyEstablishedTime();
|
||||
}
|
||||
}
|
||||
|
||||
RemoteHostId remoteId = peer.getRemoteHostId();
|
||||
if (oldPeer != null) {
|
||||
oldPeer.dropOutbound();
|
||||
_introManager.remove(oldPeer);
|
||||
_expireEvent.remove(oldPeer);
|
||||
RemoteHostId oldID = oldPeer.getRemoteHostId();
|
||||
if (!remoteId.equals(oldID)) {
|
||||
// leak fix, remove old address
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(remotePeer + " changed address FROM " + oldID + " TO " + remoteId);
|
||||
PeerState oldPeer2 = _peersByRemoteHost.remove(oldID);
|
||||
// different ones in the two maps? shouldn't happen
|
||||
if (oldPeer2 != oldPeer) {
|
||||
oldPeer2.dropOutbound();
|
||||
_introManager.remove(oldPeer2);
|
||||
_expireEvent.remove(oldPeer2);
|
||||
}
|
||||
}
|
||||
}
|
||||
oldPeer = null;
|
||||
|
||||
RemoteHostId remoteId = peer.getRemoteHostId();
|
||||
if (remoteId == null) return false;
|
||||
|
||||
// Should always be direct... except maybe for hidden mode?
|
||||
// or do we always know the IP by now?
|
||||
if (remoteId.getIP() == null && _log.shouldLog(Log.WARN))
|
||||
_log.warn("Add indirect: " + peer);
|
||||
|
||||
oldPeer = _peersByRemoteHost.put(remoteId, peer);
|
||||
if ( (oldPeer != null) && (oldPeer != peer) ) {
|
||||
// don't do this twice
|
||||
PeerState oldPeer2 = _peersByRemoteHost.put(remoteId, peer);
|
||||
if (oldPeer2 != null && oldPeer2 != peer && oldPeer2 != oldPeer) {
|
||||
// this shouldn't happen, should have been removed above
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Peer already connected (PBRH): old=" + oldPeer2 + " new=" + peer);
|
||||
// transfer over the old state/inbound message fragments/etc
|
||||
peer.loadFrom(oldPeer);
|
||||
oldEstablishedOn = oldPeer.getKeyEstablishedTime();
|
||||
peer.loadFrom(oldPeer2);
|
||||
oldEstablishedOn = oldPeer2.getKeyEstablishedTime();
|
||||
oldPeer2.dropOutbound();
|
||||
_introManager.remove(oldPeer2);
|
||||
_expireEvent.remove(oldPeer2);
|
||||
}
|
||||
|
||||
if (oldPeer != null) {
|
||||
oldPeer.dropOutbound();
|
||||
_introManager.remove(oldPeer);
|
||||
_expireEvent.remove(oldPeer);
|
||||
}
|
||||
|
||||
if ( (oldPeer != null) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("Peer already connected: old=" + oldPeer + " new=" + peer, new Exception("dup"));
|
||||
|
||||
if (_log.shouldLog(Log.WARN) && _peersByIdent.size() != _peersByRemoteHost.size())
|
||||
_log.warn("Size Mismatch after add: " + peer
|
||||
+ " byIDsz = " + _peersByIdent.size()
|
||||
+ " byHostsz = " + _peersByRemoteHost.size());
|
||||
|
||||
_activeThrottle.unchoke(peer.getRemotePeer());
|
||||
markReachable(peer.getRemotePeer(), peer.isInbound());
|
||||
@@ -996,15 +1066,20 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
*/
|
||||
_log.info(buf.toString(), new Exception("Dropped by"));
|
||||
}
|
||||
|
||||
synchronized(_addDropLock) {
|
||||
locked_dropPeer(peer, shouldShitlist, why);
|
||||
}
|
||||
if (needsRebuild())
|
||||
rebuildExternalAddress();
|
||||
}
|
||||
|
||||
private void locked_dropPeer(PeerState peer, boolean shouldShitlist, String why) {
|
||||
peer.dropOutbound();
|
||||
peer.expireInboundMessages();
|
||||
_introManager.remove(peer);
|
||||
_fragments.dropPeer(peer);
|
||||
|
||||
PeerState altByIdent = null;
|
||||
PeerState altByHost = null;
|
||||
|
||||
if (peer.getRemotePeer() != null) {
|
||||
dropPeerCapacities(peer);
|
||||
|
||||
@@ -1018,9 +1093,14 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
|
||||
RemoteHostId remoteId = peer.getRemoteHostId();
|
||||
if (remoteId != null) {
|
||||
altByHost = _peersByRemoteHost.remove(remoteId);
|
||||
}
|
||||
PeerState altByHost = _peersByRemoteHost.remove(remoteId);
|
||||
|
||||
if (altByIdent != altByHost && _log.shouldLog(Log.WARN))
|
||||
_log.warn("Mismatch on remove, RHID = " + remoteId
|
||||
+ " byID = " + altByIdent
|
||||
+ " byHost = " + altByHost
|
||||
+ " byIDsz = " + _peersByIdent.size()
|
||||
+ " byHostsz = " + _peersByRemoteHost.size());
|
||||
|
||||
// unchoke 'em, but just because we'll never talk again...
|
||||
_activeThrottle.unchoke(peer.getRemotePeer());
|
||||
@@ -1029,12 +1109,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
// _flooder.removePeer(peer);
|
||||
_expireEvent.remove(peer);
|
||||
|
||||
if (needsRebuild())
|
||||
rebuildExternalAddress();
|
||||
|
||||
// deal with races to make sure we drop the peers fully
|
||||
if ( (altByIdent != null) && (peer != altByIdent) ) dropPeer(altByIdent, shouldShitlist, "recurse");
|
||||
if ( (altByHost != null) && (peer != altByHost) ) dropPeer(altByHost, shouldShitlist, "recurse");
|
||||
if ( (altByIdent != null) && (peer != altByIdent) ) locked_dropPeer(altByIdent, shouldShitlist, "recurse");
|
||||
if ( (altByHost != null) && (peer != altByHost) ) locked_dropPeer(altByHost, shouldShitlist, "recurse");
|
||||
}
|
||||
|
||||
private boolean needsRebuild() {
|
||||
@@ -1145,20 +1222,33 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a session destroy message to everybody
|
||||
* BLOCKING if OB queue is full.
|
||||
* Send a session destroy message to everybody.
|
||||
* BLOCKING for at least 1 sec per 1K peers, more if BW is very low or if OB queue is full.
|
||||
*
|
||||
* @since 0.8.9
|
||||
*/
|
||||
private void destroyAll() {
|
||||
_endpoint.clearOutbound();
|
||||
int howMany = _peersByIdent.size();
|
||||
// use no more than 1/4 of configured bandwidth
|
||||
final int burst = 8;
|
||||
int pps = Math.max(48, (_context.bandwidthLimiter().getOutboundKBytesPerSecond() * 1000 / 4) / 48);
|
||||
int burstps = pps / burst;
|
||||
// max of 1000 pps
|
||||
int toSleep = Math.max(8, (1000 / burstps));
|
||||
int count = 0;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Sending destroy to : " + howMany + " peers");
|
||||
for (PeerState peer : _peersByIdent.values()) {
|
||||
sendDestroy(peer);
|
||||
// 1000 per second * 48 bytes = 400 KBps
|
||||
if ((++count) % burst == 0) {
|
||||
try {
|
||||
Thread.sleep(toSleep);
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
int toSleep = Math.min(howMany / 3, 750);
|
||||
toSleep = Math.min(howMany / 3, 750);
|
||||
if (toSleep > 0) {
|
||||
try {
|
||||
Thread.sleep(toSleep);
|
||||
@@ -1205,7 +1295,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
if (addr.getOption("ihost0") == null) {
|
||||
byte[] ip = addr.getIP();
|
||||
int port = addr.getPort();
|
||||
if (ip == null || port <= 0 ||
|
||||
if (ip == null || port < MIN_PEER_PORT ||
|
||||
(!isValid(ip)) ||
|
||||
Arrays.equals(ip, getExternalIP())) {
|
||||
markUnreachable(to);
|
||||
@@ -1503,7 +1593,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
* it's usually false positives.
|
||||
******************
|
||||
String forceIntroducers = _context.getProperty(PROP_FORCE_INTRODUCERS);
|
||||
if ( (forceIntroducers != null) && (Boolean.valueOf(forceIntroducers).booleanValue()) ) {
|
||||
if ( (forceIntroducers != null) && (Boolean.parseBoolean(forceIntroducers)) ) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Force introducers specified");
|
||||
return true;
|
||||
@@ -1526,6 +1616,24 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For EstablishmentManager
|
||||
* @since 0.9.3
|
||||
*/
|
||||
boolean canIntroduce() {
|
||||
// we don't expect inbound connections when hidden, but it could happen
|
||||
// Don't offer if we are approaching max connections. While Relay Intros do not
|
||||
// count as connections, we have to keep the connection to this peer up longer if
|
||||
// we are offering introductions.
|
||||
return
|
||||
(!_context.router().isHidden()) &&
|
||||
(!introducersRequired()) &&
|
||||
haveCapacity() &&
|
||||
(!((FloodfillNetworkDatabaseFacade)_context.netDb()).floodfillEnabled()) &&
|
||||
_introManager.introducedCount() < IntroductionManager.MAX_OUTBOUND &&
|
||||
_introManager.introducedCount() < getMaxConnections() / 4;
|
||||
}
|
||||
|
||||
/** default true */
|
||||
private boolean allowDirectUDP() {
|
||||
return _context.getBooleanPropertyDefaultTrue(PROP_ALLOW_DIRECT);
|
||||
@@ -1688,7 +1796,6 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
|
||||
public boolean allowConnection() {
|
||||
|
||||
return _peersByIdent.size() < getMaxConnections();
|
||||
}
|
||||
|
||||
@@ -1698,20 +1805,17 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
*/
|
||||
@Override
|
||||
public Vector<Long> getClockSkews() {
|
||||
|
||||
Vector<Long> skews = new Vector();
|
||||
Vector<PeerState> peers = new Vector();
|
||||
|
||||
peers.addAll(_peersByIdent.values());
|
||||
|
||||
// If our clock is way off, we may not have many (or any) successful connections,
|
||||
// so try hard in that case to return good data
|
||||
boolean includeEverybody = _context.router().getUptime() < 10*60*1000 || peers.size() < 10;
|
||||
boolean includeEverybody = _context.router().getUptime() < 10*60*1000 || _peersByIdent.size() < 10;
|
||||
long now = _context.clock().now();
|
||||
for (Iterator<PeerState> iter = peers.iterator(); iter.hasNext(); ) {
|
||||
PeerState peer = iter.next();
|
||||
if ((!includeEverybody) && now - peer.getLastReceiveTime() > 15*60*1000)
|
||||
for (PeerState peer : _peersByIdent.values()) {
|
||||
if ((!includeEverybody) && now - peer.getLastReceiveTime() > 5*60*1000)
|
||||
continue; // skip old peers
|
||||
if (peer.getRTT() > PeerState.INIT_RTT - 250)
|
||||
continue; // Big RTT makes for a poor calculation
|
||||
skews.addElement(Long.valueOf(peer.getClockSkew() / 1000));
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -1744,7 +1848,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
private static final int FLAG_CWND= 6;
|
||||
private static final int FLAG_SSTHRESH = 7;
|
||||
private static final int FLAG_RTT = 8;
|
||||
private static final int FLAG_DEV = 9;
|
||||
//private static final int FLAG_DEV = 9;
|
||||
private static final int FLAG_RTO = 10;
|
||||
private static final int FLAG_MTU = 11;
|
||||
private static final int FLAG_SEND = 12;
|
||||
@@ -1784,9 +1888,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
case FLAG_RTT:
|
||||
rv = RTTComparator.instance();
|
||||
break;
|
||||
case FLAG_DEV:
|
||||
rv = DevComparator.instance();
|
||||
break;
|
||||
//case FLAG_DEV:
|
||||
// rv = DevComparator.instance();
|
||||
// break;
|
||||
case FLAG_RTO:
|
||||
rv = RTOComparator.instance();
|
||||
break;
|
||||
@@ -1927,6 +2031,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
private static class DevComparator extends PeerComparator {
|
||||
private static final DevComparator _instance = new DevComparator();
|
||||
public static final DevComparator instance() { return _instance; }
|
||||
@@ -1939,6 +2045,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
return (int)rv;
|
||||
}
|
||||
}
|
||||
****/
|
||||
|
||||
/** */
|
||||
private static class RTOComparator extends PeerComparator {
|
||||
private static final RTOComparator _instance = new RTOComparator();
|
||||
public static final RTOComparator instance() { return _instance; }
|
||||
@@ -2088,8 +2197,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
buf.append("</th>\n");
|
||||
buf.append("<th class=\"smallhead\" nowrap><a href=\"#def.rtt\">RTT</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _("Sort by round trip time"), FLAG_RTT);
|
||||
buf.append("</th><th class=\"smallhead\" nowrap><a href=\"#def.dev\">").append(_("Dev")).append("</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _("Sort by round trip time deviation"), FLAG_DEV);
|
||||
//buf.append("</th><th class=\"smallhead\" nowrap><a href=\"#def.dev\">").append(_("Dev")).append("</a><br>");
|
||||
//appendSortLinks(buf, urlBase, sortFlags, _("Sort by round trip time deviation"), FLAG_DEV);
|
||||
buf.append("</th><th class=\"smallhead\" nowrap><a href=\"#def.rto\">RTO</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _("Sort by retransmission timeout"), FLAG_RTO);
|
||||
buf.append("</th>\n");
|
||||
@@ -2212,9 +2321,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
buf.append(DataHelper.formatDuration2(rtt));
|
||||
buf.append("</td>");
|
||||
|
||||
buf.append("<td class=\"cells\" align=\"right\">");
|
||||
buf.append(DataHelper.formatDuration2(peer.getRTTDeviation()));
|
||||
buf.append("</td>");
|
||||
//buf.append("<td class=\"cells\" align=\"right\">");
|
||||
//buf.append(DataHelper.formatDuration2(peer.getRTTDeviation()));
|
||||
//buf.append("</td>");
|
||||
|
||||
buf.append("<td class=\"cells\" align=\"right\">");
|
||||
buf.append(DataHelper.formatDuration2(rto));
|
||||
@@ -2291,7 +2400,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
buf.append("</b></td><td> </td>\n" +
|
||||
"<td align=\"center\"><b>");
|
||||
buf.append(numPeers > 0 ? DataHelper.formatDuration2(rttTotal/numPeers) : '0');
|
||||
buf.append("</b></td><td> </td><td align=\"center\"><b>");
|
||||
//buf.append("</b></td><td> </td><td align=\"center\"><b>");
|
||||
buf.append("</b></td><td align=\"center\"><b>");
|
||||
buf.append(numPeers > 0 ? DataHelper.formatDuration2(rtoTotal/numPeers) : '0');
|
||||
buf.append("</b></td><td align=\"center\"><b>").append(_mtu).append("</b></td><td align=\"center\"><b>");
|
||||
buf.append(sendTotal).append("</b></td><td align=\"center\"><b>").append(recvTotal).append("</b></td>\n" +
|
||||
@@ -2365,9 +2475,18 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
|
||||
private class ExpirePeerEvent extends SimpleTimer2.TimedEvent {
|
||||
// TODO why have separate Set, just use _peersByIdent.values()
|
||||
private final Set<PeerState> _expirePeers;
|
||||
private final List<PeerState> _expireBuffer;
|
||||
private volatile boolean _alive;
|
||||
private int _runCount;
|
||||
// we've seen firewalls change ports after 40 seconds
|
||||
private static final long PING_FIREWALL_TIME = 30*1000;
|
||||
private static final long PING_FIREWALL_CUTOFF = PING_FIREWALL_TIME / 2;
|
||||
// ping 1/4 of the peers every loop
|
||||
private static final int SLICES = 4;
|
||||
private static final long SHORT_LOOP_TIME = PING_FIREWALL_CUTOFF / (SLICES + 1);
|
||||
private static final long LONG_LOOP_TIME = 25*1000;
|
||||
|
||||
public ExpirePeerEvent() {
|
||||
super(_context.simpleTimer2());
|
||||
@@ -2381,10 +2500,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
_expireTimeout = Math.min(_expireTimeout + 15*1000, EXPIRE_TIMEOUT);
|
||||
else
|
||||
_expireTimeout = Math.max(_expireTimeout - 45*1000, MIN_EXPIRE_TIMEOUT);
|
||||
long shortInactivityCutoff = _context.clock().now() - _expireTimeout;
|
||||
long longInactivityCutoff = _context.clock().now() - EXPIRE_TIMEOUT;
|
||||
long pingCutoff = _context.clock().now() - (2 * 60*60*1000);
|
||||
long now = _context.clock().now();
|
||||
long shortInactivityCutoff = now - _expireTimeout;
|
||||
long longInactivityCutoff = now - EXPIRE_TIMEOUT;
|
||||
long pingCutoff = now - (2 * 60*60*1000);
|
||||
long pingFirewallCutoff = now - PING_FIREWALL_CUTOFF;
|
||||
boolean shouldPingFirewall = _reachabilityStatus != CommSystemFacade.STATUS_OK;
|
||||
boolean pingOneOnly = shouldPingFirewall && _externalListenPort == _endpoint.getListenPort();
|
||||
boolean shortLoop = shouldPingFirewall;
|
||||
_expireBuffer.clear();
|
||||
_runCount++;
|
||||
|
||||
for (Iterator<PeerState> iter = _expirePeers.iterator(); iter.hasNext(); ) {
|
||||
PeerState peer = iter.next();
|
||||
@@ -2397,7 +2522,25 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
if ( (peer.getLastReceiveTime() < inactivityCutoff) && (peer.getLastSendTime() < inactivityCutoff) ) {
|
||||
_expireBuffer.add(peer);
|
||||
iter.remove();
|
||||
}
|
||||
} else if (shouldPingFirewall &&
|
||||
((_runCount ^ peer.hashCode()) & (SLICES - 1)) == 0 &&
|
||||
peer.getLastSendOrPingTime() < pingFirewallCutoff &&
|
||||
peer.getLastReceiveTime() < pingFirewallCutoff) {
|
||||
// ping if firewall is mapping the port to keep port the same...
|
||||
// if the port changes we are screwed
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Pinging for firewall: " + peer);
|
||||
// don't update or idle time won't be right and peer won't get dropped
|
||||
// TODO if both sides are firewalled should only one ping
|
||||
// or else session will stay open forever?
|
||||
//peer.setLastSendTime(now);
|
||||
send(_destroyBuilder.buildPing(peer));
|
||||
peer.setLastPingTime(now);
|
||||
// If external port is different, it may be changing the port for every
|
||||
// session, so ping all of them. Otherwise only one.
|
||||
if (pingOneOnly)
|
||||
shouldPingFirewall = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (PeerState peer : _expireBuffer) {
|
||||
@@ -2407,18 +2550,21 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
_expireBuffer.clear();
|
||||
|
||||
if (_alive)
|
||||
schedule(30*1000);
|
||||
schedule(shortLoop ? SHORT_LOOP_TIME : LONG_LOOP_TIME);
|
||||
}
|
||||
|
||||
public void add(PeerState peer) {
|
||||
_expirePeers.add(peer);
|
||||
}
|
||||
|
||||
public void remove(PeerState peer) {
|
||||
_expirePeers.remove(peer);
|
||||
}
|
||||
|
||||
public void setIsAlive(boolean isAlive) {
|
||||
_alive = isAlive;
|
||||
if (isAlive) {
|
||||
reschedule(30*1000);
|
||||
reschedule(LONG_LOOP_TIME);
|
||||
} else {
|
||||
cancel();
|
||||
_expirePeers.clear();
|
||||
@@ -2507,8 +2653,14 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
if (peerInfo == null)
|
||||
continue;
|
||||
RouterAddress addr = peerInfo.getTargetAddress(STYLE);
|
||||
if (addr != null)
|
||||
return peer;
|
||||
if (addr == null)
|
||||
continue;
|
||||
byte[] ip = addr.getIP();
|
||||
if (ip == null)
|
||||
continue;
|
||||
if (DataHelper.eq(ip, 0, getExternalIP(), 0, 2))
|
||||
continue;
|
||||
return peer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -392,6 +392,7 @@ class BuildExecutor implements Runnable {
|
||||
pools.clear();
|
||||
} catch (RuntimeException e) {
|
||||
_log.log(Log.CRIT, "B0rked in the tunnel builder", e);
|
||||
try { Thread.sleep(LOOP_TIME); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -460,7 +460,7 @@ public abstract class TunnelPeerSelector {
|
||||
else
|
||||
val = ctx.getProperty(PROP_OUTBOUND_CLIENT_EXCLUDE_UNREACHABLE, DEFAULT_OUTBOUND_CLIENT_EXCLUDE_UNREACHABLE);
|
||||
|
||||
boolean rv = (val != null ? Boolean.valueOf(val).booleanValue() : def);
|
||||
boolean rv = (val != null ? Boolean.parseBoolean(val) : def);
|
||||
//System.err.println("Filter unreachable? " + rv + " (inbound? " + isInbound + ", exploratory? " + isExploratory);
|
||||
return rv;
|
||||
}
|
||||
@@ -490,7 +490,7 @@ public abstract class TunnelPeerSelector {
|
||||
else
|
||||
val = ctx.getProperty(PROP_OUTBOUND_CLIENT_EXCLUDE_SLOW);
|
||||
|
||||
boolean rv = (val != null ? Boolean.valueOf(val).booleanValue() : def);
|
||||
boolean rv = (val != null ? Boolean.parseBoolean(val) : def);
|
||||
//System.err.println("Filter unreachable? " + rv + " (inbound? " + isInbound + ", exploratory? " + isExploratory);
|
||||
return rv;
|
||||
}
|
||||
@@ -519,7 +519,7 @@ public abstract class TunnelPeerSelector {
|
||||
else
|
||||
val = ctx.getProperty(PROP_OUTBOUND_CLIENT_EXCLUDE_UPTIME);
|
||||
|
||||
boolean rv = (val != null ? Boolean.valueOf(val).booleanValue() : def);
|
||||
boolean rv = (val != null ? Boolean.parseBoolean(val) : def);
|
||||
//System.err.println("Filter unreachable? " + rv + " (inbound? " + isInbound + ", exploratory? " + isExploratory);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public class TunnelPool {
|
||||
* Destination (i.e. for servers or clients w/ persistent key,
|
||||
* or restarting close-on-idle clients)
|
||||
*/
|
||||
void startup() {
|
||||
synchronized void startup() {
|
||||
synchronized (_inProgress) {
|
||||
_inProgress.clear();
|
||||
}
|
||||
@@ -102,7 +102,7 @@ public class TunnelPool {
|
||||
new long[] { 5*60*1000l });
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
synchronized void shutdown() {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(toString() + ": Shutdown called");
|
||||
_alive = false;
|
||||
|
||||
@@ -37,8 +37,8 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
private final Map<Hash, TunnelPool> _clientInboundPools;
|
||||
/** Hash (destination) to TunnelPool */
|
||||
private final Map<Hash, TunnelPool> _clientOutboundPools;
|
||||
private TunnelPool _inboundExploratory;
|
||||
private TunnelPool _outboundExploratory;
|
||||
private final TunnelPool _inboundExploratory;
|
||||
private final TunnelPool _outboundExploratory;
|
||||
private final BuildExecutor _executor;
|
||||
private final BuildHandler _handler;
|
||||
private final TunnelPeerSelector _clientPeerSelector;
|
||||
@@ -62,10 +62,15 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
_clientInboundPools = new ConcurrentHashMap(4);
|
||||
_clientOutboundPools = new ConcurrentHashMap(4);
|
||||
_clientPeerSelector = new ClientPeerSelector(ctx);
|
||||
|
||||
ExploratoryPeerSelector selector = new ExploratoryPeerSelector(_context);
|
||||
TunnelPoolSettings inboundSettings = new TunnelPoolSettings(true, true);
|
||||
_inboundExploratory = new TunnelPool(_context, this, inboundSettings, selector);
|
||||
TunnelPoolSettings outboundSettings = new TunnelPoolSettings(true, false);
|
||||
_outboundExploratory = new TunnelPool(_context, this, outboundSettings, selector);
|
||||
|
||||
// threads will be started in startup()
|
||||
_executor = new BuildExecutor(ctx, this);
|
||||
I2PThread execThread = new I2PThread(_executor, "BuildExecutor", true);
|
||||
execThread.start();
|
||||
_handler = new BuildHandler(ctx, this, _executor);
|
||||
int numHandlerThreads;
|
||||
int share = TunnelDispatcher.getShareBandwidth(ctx);
|
||||
@@ -76,10 +81,6 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
else
|
||||
numHandlerThreads = 1;
|
||||
_numHandlerThreads = ctx.getProperty("router.buildHandlerThreads", numHandlerThreads);
|
||||
for (int i = 1; i <= _numHandlerThreads; i++) {
|
||||
I2PThread hThread = new I2PThread(_handler, "BuildHandler " + i + '/' + numHandlerThreads, true);
|
||||
hThread.start();
|
||||
}
|
||||
|
||||
// The following are for TestJob
|
||||
ctx.statManager().createRequiredRateStat("tunnel.testFailedTime", "Time for tunnel test failure (ms)", "Tunnels",
|
||||
@@ -104,9 +105,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
* @return null if none
|
||||
*/
|
||||
public TunnelInfo selectInboundTunnel() {
|
||||
TunnelPool pool = _inboundExploratory;
|
||||
if (pool == null) return null;
|
||||
TunnelInfo info = pool.selectTunnel();
|
||||
TunnelInfo info = _inboundExploratory.selectTunnel();
|
||||
if (info == null) {
|
||||
_inboundExploratory.buildFallback();
|
||||
// still can be null, but probably not
|
||||
@@ -139,13 +138,11 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
* @return null if none
|
||||
*/
|
||||
public TunnelInfo selectOutboundTunnel() {
|
||||
TunnelPool pool = _outboundExploratory;
|
||||
if (pool == null) return null;
|
||||
TunnelInfo info = pool.selectTunnel();
|
||||
TunnelInfo info = _outboundExploratory.selectTunnel();
|
||||
if (info == null) {
|
||||
pool.buildFallback();
|
||||
_outboundExploratory.buildFallback();
|
||||
// still can be null, but probably not
|
||||
info = pool.selectTunnel();
|
||||
info = _outboundExploratory.selectTunnel();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
@@ -176,9 +173,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
* @since 0.8.10
|
||||
*/
|
||||
public TunnelInfo selectInboundExploratoryTunnel(Hash closestTo) {
|
||||
TunnelPool pool = _inboundExploratory;
|
||||
if (pool == null) return null;
|
||||
TunnelInfo info = pool.selectTunnel();
|
||||
TunnelInfo info = _inboundExploratory.selectTunnel();
|
||||
if (info == null) {
|
||||
_inboundExploratory.buildFallback();
|
||||
// still can be null, but probably not
|
||||
@@ -222,13 +217,11 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
* @since 0.8.10
|
||||
*/
|
||||
public TunnelInfo selectOutboundExploratoryTunnel(Hash closestTo) {
|
||||
TunnelPool pool = _outboundExploratory;
|
||||
if (pool == null) return null;
|
||||
TunnelInfo info = pool.selectTunnel();
|
||||
TunnelInfo info = _outboundExploratory.selectTunnel();
|
||||
if (info == null) {
|
||||
pool.buildFallback();
|
||||
_outboundExploratory.buildFallback();
|
||||
// still can be null, but probably not
|
||||
info = pool.selectTunnel(closestTo);
|
||||
info = _outboundExploratory.selectTunnel(closestTo);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
@@ -261,14 +254,10 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
if (info != null)
|
||||
return info;
|
||||
}
|
||||
if (_inboundExploratory != null) {
|
||||
info = _inboundExploratory.getTunnel(id);
|
||||
if (info != null) return info;
|
||||
}
|
||||
if (_outboundExploratory != null) {
|
||||
info = _outboundExploratory.getTunnel(id);
|
||||
if (info != null) return info;
|
||||
}
|
||||
info = _inboundExploratory.getTunnel(id);
|
||||
if (info != null) return info;
|
||||
info = _outboundExploratory.getTunnel(id);
|
||||
if (info != null) return info;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -282,9 +271,6 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
|
||||
/** @return number of outbound exploratory tunnels */
|
||||
public int getOutboundTunnelCount() {
|
||||
if (_outboundExploratory == null)
|
||||
return 0;
|
||||
else
|
||||
return _outboundExploratory.size();
|
||||
}
|
||||
|
||||
@@ -507,22 +493,15 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
public synchronized void startup() {
|
||||
_isShutdown = false;
|
||||
if (!_executor.isRunning()) {
|
||||
I2PThread t = new I2PThread(_executor, "BuildExecutor");
|
||||
t.setDaemon(true);
|
||||
I2PThread t = new I2PThread(_executor, "BuildExecutor", true);
|
||||
t.start();
|
||||
for (int i = 1; i <= _numHandlerThreads; i++) {
|
||||
I2PThread hThread = new I2PThread(_handler, "BuildHandler " + i + '/' + _numHandlerThreads, true);
|
||||
hThread.start();
|
||||
}
|
||||
}
|
||||
ExploratoryPeerSelector selector = new ExploratoryPeerSelector(_context);
|
||||
|
||||
TunnelPoolSettings inboundSettings = new TunnelPoolSettings();
|
||||
inboundSettings.setIsExploratory(true);
|
||||
inboundSettings.setIsInbound(true);
|
||||
_inboundExploratory = new TunnelPool(_context, this, inboundSettings, selector);
|
||||
_inboundExploratory.startup();
|
||||
|
||||
TunnelPoolSettings outboundSettings = new TunnelPoolSettings();
|
||||
outboundSettings.setIsExploratory(true);
|
||||
outboundSettings.setIsInbound(false);
|
||||
_outboundExploratory = new TunnelPool(_context, this, outboundSettings, selector);
|
||||
_context.simpleScheduler().addEvent(new DelayedStartup(_outboundExploratory), 3*1000);
|
||||
|
||||
// try to build up longer tunnels
|
||||
@@ -554,9 +533,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
}
|
||||
|
||||
private void shutdownExploratory() {
|
||||
if (_inboundExploratory != null)
|
||||
_inboundExploratory.shutdown();
|
||||
if (_outboundExploratory != null)
|
||||
_outboundExploratory.shutdown();
|
||||
}
|
||||
|
||||
@@ -564,10 +541,8 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
public void listPools(List<TunnelPool> out) {
|
||||
out.addAll(_clientInboundPools.values());
|
||||
out.addAll(_clientOutboundPools.values());
|
||||
if (_inboundExploratory != null)
|
||||
out.add(_inboundExploratory);
|
||||
if (_outboundExploratory != null)
|
||||
out.add(_outboundExploratory);
|
||||
out.add(_inboundExploratory);
|
||||
out.add(_outboundExploratory);
|
||||
}
|
||||
void tunnelFailed() { _executor.repoll(); }
|
||||
BuildExecutor getExecutor() { return _executor; }
|
||||
@@ -639,12 +614,18 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
return new HashMap(_clientOutboundPools);
|
||||
}
|
||||
|
||||
/** for TunnelRenderer in router console */
|
||||
/**
|
||||
* For TunnelRenderer in router console
|
||||
* @return non-null
|
||||
*/
|
||||
public TunnelPool getInboundExploratoryPool() {
|
||||
return _inboundExploratory;
|
||||
}
|
||||
|
||||
/** for TunnelRenderer in router console */
|
||||
/**
|
||||
* For TunnelRenderer in router console
|
||||
* @return non-null
|
||||
*/
|
||||
public TunnelPool getOutboundExploratoryPool() {
|
||||
return _outboundExploratory;
|
||||
}
|
||||
@@ -658,13 +639,11 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
* @since 0.8.13
|
||||
*/
|
||||
public void fail(Hash peer) {
|
||||
if (_outboundExploratory != null)
|
||||
failTunnelsWithFirstHop(_outboundExploratory, peer);
|
||||
failTunnelsWithFirstHop(_outboundExploratory, peer);
|
||||
for (TunnelPool pool : _clientOutboundPools.values()) {
|
||||
failTunnelsWithFirstHop(pool, peer);
|
||||
}
|
||||
if (_inboundExploratory != null)
|
||||
failTunnelsWithLastHop(_inboundExploratory, peer);
|
||||
failTunnelsWithLastHop(_inboundExploratory, peer);
|
||||
for (TunnelPool pool : _clientInboundPools.values()) {
|
||||
failTunnelsWithLastHop(pool, peer);
|
||||
}
|
||||
|
||||
@@ -37,9 +37,11 @@ public class EventLog {
|
||||
public static final String CHANGE_PORT = "changePort";
|
||||
public static final String CLOCK_SHIFT = "clockShift";
|
||||
public static final String CRASHED = "crashed";
|
||||
public static final String CRITICAL = "critical";
|
||||
public static final String INSTALLED = "installed";
|
||||
public static final String INSTALL_FAILED = "intallFailed";
|
||||
public static final String NEW_IDENT = "newIdent";
|
||||
public static final String OOM = "oom";
|
||||
public static final String REKEYED = "rekeyed";
|
||||
public static final String SOFT_RESTART = "softRestart";
|
||||
public static final String STARTED = "started";
|
||||
@@ -48,12 +50,11 @@ public class EventLog {
|
||||
public static final String WATCHDOG = "watchdog";
|
||||
|
||||
/**
|
||||
* @param file must be absolute
|
||||
* @throws IllegalArgumentException if not absolute
|
||||
* @param file should be absolute
|
||||
*/
|
||||
public EventLog(I2PAppContext ctx, File file) {
|
||||
if (!file.isAbsolute())
|
||||
throw new IllegalArgumentException();
|
||||
//if (!file.isAbsolute())
|
||||
// throw new IllegalArgumentException();
|
||||
_context = ctx;
|
||||
_file = file;
|
||||
_cache = new HashMap(4);
|
||||
@@ -128,7 +129,7 @@ public class EventLog {
|
||||
continue;
|
||||
Long ltime = Long.valueOf(time);
|
||||
String info = s.length > 2 ? s[2] : "";
|
||||
rv.put(time, info);
|
||||
rv.put(ltime, info);
|
||||
} catch (IndexOutOfBoundsException ioobe) {
|
||||
} catch (NumberFormatException nfe) {
|
||||
}
|
||||
|
||||
@@ -11,20 +11,24 @@ import java.util.Locale;
|
||||
*/
|
||||
public abstract class RFC822Date {
|
||||
|
||||
// SimpleDateFormat is not thread-safe, methods must be synchronized
|
||||
|
||||
private static final SimpleDateFormat OUTPUT_FORMAT = new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US);
|
||||
|
||||
/**
|
||||
* http://jimyjoshi.com/blog/2007/08/rfc822dateparsinginjava.html
|
||||
* Apparently public domain
|
||||
* Probably don't need all of these...
|
||||
*/
|
||||
private static final SimpleDateFormat rfc822DateFormats[] = new SimpleDateFormat[] {
|
||||
OUTPUT_FORMAT,
|
||||
new SimpleDateFormat("EEE, d MMM yy HH:mm:ss z", Locale.US),
|
||||
new SimpleDateFormat("EEE, d MMM yy HH:mm z", Locale.US),
|
||||
new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US),
|
||||
new SimpleDateFormat("EEE, d MMM yyyy HH:mm z", Locale.US),
|
||||
new SimpleDateFormat("d MMM yy HH:mm z", Locale.US),
|
||||
new SimpleDateFormat("d MMM yy HH:mm:ss z", Locale.US),
|
||||
new SimpleDateFormat("d MMM yyyy HH:mm z", Locale.US),
|
||||
new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US)
|
||||
new SimpleDateFormat("d MMM yyyy HH:mm z", Locale.US)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -33,7 +37,7 @@ public abstract class RFC822Date {
|
||||
* @param s non-null
|
||||
* @return -1 on failure
|
||||
*/
|
||||
public static long parse822Date(String s) {
|
||||
public synchronized static long parse822Date(String s) {
|
||||
for (int i = 0; i < rfc822DateFormats.length; i++) {
|
||||
try {
|
||||
Date date = rfc822DateFormats[i].parse(s);
|
||||
@@ -44,8 +48,12 @@ public abstract class RFC822Date {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** @since 0.8.2 */
|
||||
public static String to822Date(long t) {
|
||||
return (new SimpleDateFormat("d MMM yyyy HH:mm:ss z", Locale.US)).format(new Date(t));
|
||||
/**
|
||||
* Format is "d MMM yyyy HH:mm:ss z"
|
||||
*
|
||||
* @since 0.8.2
|
||||
*/
|
||||
public synchronized static String to822Date(long t) {
|
||||
return OUTPUT_FORMAT.format(new Date(t));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user