();
if (opts != null) {
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java
index 5eadd9d8c8..9e22d9b3c6 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java
@@ -97,7 +97,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
public boolean ownDest = false;
/** the I2CP port, non-null */
- public String port = System.getProperty(I2PClient.PROP_TCP_PORT, "7654");
+ public String port = System.getProperty(I2PClient.PROP_TCP_PORT, Integer.toString(I2PClient.DEFAULT_LISTEN_PORT));
/** the I2CP host, non-null */
public String host = System.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
/** the listen-on host. Sadly the listen-on port does not have a field. */
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
index fa61d40427..102c37cd53 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
@@ -409,7 +409,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
Log _log = tunnel.getContext().logManager().getLog(I2PTunnelClientBase.class);
Properties props = new Properties();
props.putAll(tunnel.getClientOptions());
- int portNum = 7654;
+ int portNum = I2PClient.DEFAULT_LISTEN_PORT;
if (tunnel.port != null) {
try {
portNum = Integer.parseInt(tunnel.port);
@@ -471,7 +471,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
// try to make this error sensible as it will happen...
String portNum = getTunnel().port;
if (portNum == null)
- portNum = "7654";
+ portNum = Integer.toString(I2PClient.DEFAULT_LISTEN_PORT);
String msg;
if (getTunnel().getContext().isRouterContext())
msg = "Unable to build tunnels for the client";
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
index 6f881bafef..c3e325c251 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
@@ -210,7 +210,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
private I2PSocketManager createManager(InputStream privData) {
Properties props = new Properties();
props.putAll(getTunnel().getClientOptions());
- int portNum = 7654;
+ int portNum = I2PClient.DEFAULT_LISTEN_PORT;
if (getTunnel().port != null) {
try {
portNum = Integer.parseInt(getTunnel().port);
@@ -305,7 +305,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
// try to make this error sensible as it will happen...
String portNum = getTunnel().port;
if (portNum == null)
- portNum = "7654";
+ portNum = Integer.toString(I2PClient.DEFAULT_LISTEN_PORT);
String msg;
if (getTunnel().getContext().isRouterContext())
msg = "Unable to build tunnels for the server at " + remoteHost.getHostAddress() + ':' + remotePort;
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
index 84a3858576..ef436d2908 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
@@ -733,10 +733,10 @@ public class TunnelController implements Logging {
int portNum = Integer.parseInt(port);
_tunnel.port = String.valueOf(portNum);
} catch (NumberFormatException nfe) {
- _tunnel.port = "7654";
+ _tunnel.port = Integer.toString(I2PClient.DEFAULT_LISTEN_PORT);
}
} else {
- _tunnel.port = "7654";
+ _tunnel.port = Integer.toString(I2PClient.DEFAULT_LISTEN_PORT);
}
}
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java
index 13062b2b33..f0a154e512 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java
@@ -836,7 +836,7 @@ public class TunnelConfig {
if ( (_i2cpPort != null) && (_i2cpPort.trim().length() > 0) ) {
config.setProperty(TunnelController.PROP_I2CP_PORT, _i2cpPort);
} else {
- config.setProperty(TunnelController.PROP_I2CP_PORT, "7654");
+ config.setProperty(TunnelController.PROP_I2CP_PORT, Integer.toString(I2PClient.DEFAULT_LISTEN_PORT));
}
}
if (_privKeyFile != null)
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
index d89f05b379..ab8ccc06ff 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
@@ -14,6 +14,7 @@ import java.util.List;
import java.util.Set;
import net.i2p.I2PException;
+import net.i2p.client.I2PClient;
import net.i2p.crypto.SigType;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
@@ -446,7 +447,7 @@ public class EditBean extends IndexBean {
if (tun != null)
return tun.getI2CPPort();
else
- return "7654";
+ return Integer.toString(I2PClient.DEFAULT_LISTEN_PORT);
}
public String getCustomOptions(int tunnel) {
diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java
index 9893f4cfa7..2f619e9390 100644
--- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java
+++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java
@@ -275,7 +275,7 @@ public class I2PSocketManagerFactory {
}
private static int getPort() {
- int i2cpPort = 7654;
+ int i2cpPort = I2PClient.DEFAULT_LISTEN_PORT;
String i2cpPortStr = System.getProperty(I2PClient.PROP_TCP_PORT);
if (i2cpPortStr != null) {
try {
diff --git a/apps/routerconsole/java/bundle-messages.sh b/apps/routerconsole/java/bundle-messages.sh
index 57652909f2..cdeaedf978 100755
--- a/apps/routerconsole/java/bundle-messages.sh
+++ b/apps/routerconsole/java/bundle-messages.sh
@@ -54,7 +54,8 @@ ROUTERFILES="\
"
# add ../java/ so the refs will work in the po file
-JPATHS="../java/src ../jsp/WEB-INF ../java/strings $ROUTERFILES"
+# do not scan 3rd-party code in java/src/com or java/src/edu
+JPATHS="../java/src/net ../jsp/WEB-INF ../java/strings $ROUTERFILES"
for i in ../locale/messages_*.po
do
# get language
diff --git a/apps/routerconsole/java/src/edu/internet2/ndt/OsfwWorker.java b/apps/routerconsole/java/src/edu/internet2/ndt/OsfwWorker.java
index c9d1f11bf0..2eb4bbf98a 100644
--- a/apps/routerconsole/java/src/edu/internet2/ndt/OsfwWorker.java
+++ b/apps/routerconsole/java/src/edu/internet2/ndt/OsfwWorker.java
@@ -7,7 +7,7 @@ import java.net.Socket;
/**
* OsfwWorker creates a thread that listens for a message from the server. It
* functions to check if the server has sent a message that is valid and
- * sufficient to determine if the server->client direction has a fire-wall.
+ * sufficient to determine if the server->client direction has a fire-wall.
*
*
* As part of the simple firewall test, the Server must try to connect to the
@@ -49,7 +49,7 @@ public class OsfwWorker implements Runnable {
* @param iParamTestTime
* Test time duration to wait for message from server
* @param _localParam
- * Applet object used to set the result of the S->C firewall test
+ * Applet object used to set the result of the S->C firewall test
*/
OsfwWorker(ServerSocket srvSocketParam, int iParamTestTime,
Tcpbw100 _localParam) {
@@ -76,7 +76,7 @@ public class OsfwWorker implements Runnable {
/**
* run() method of this SFW Worker thread. This thread listens on the socket
* from the server for a given time period, and checks to see if the server
- * has sent a message that is valid and sufficient to determine if the S->C
+ * has sent a message that is valid and sufficient to determine if the S->C
* direction has a fire-wall.
* */
public void run() {
diff --git a/apps/routerconsole/java/src/edu/internet2/ndt/Tcpbw100.java b/apps/routerconsole/java/src/edu/internet2/ndt/Tcpbw100.java
index 6812fa6e2c..d8ba60dc7b 100644
--- a/apps/routerconsole/java/src/edu/internet2/ndt/Tcpbw100.java
+++ b/apps/routerconsole/java/src/edu/internet2/ndt/Tcpbw100.java
@@ -563,38 +563,38 @@ public class Tcpbw100 extends JApplet implements ActionListener {
}
/**
- * Get Client->Server fire-wall test results.
+ * Get Client->Server fire-wall test results.
*
- * @return integer indicating C->S test results
+ * @return integer indicating C->S test results
* */
public int getC2sSFWTestResults() {
return this._iC2sSFWResult;
}
/**
- * Set Client->Server fire-wall test results.
+ * Set Client->Server fire-wall test results.
*
* @param iParamC2SRes
- * integer indicating C->S test results
+ * integer indicating C->S test results
* */
public void setC2sSFWTestResults(int iParamC2SRes) {
this._iC2sSFWResult = iParamC2SRes;
}
/**
- * Get Server->Client fire-wall test results.
+ * Get Server->Client fire-wall test results.
*
- * @return integer indicating C->S test results
+ * @return integer indicating C->S test results
* */
public int getS2cSFWTestResults() {
return this._iS2cSFWResult;
}
/**
- * Set server->Client fire-wall test results.
+ * Set server->Client fire-wall test results.
*
* @param iParamS2CRes
- * integer indicating C->S test results
+ * integer indicating C->S test results
* */
public void setS2cSFWTestResults(int iParamS2CRes) {
this._iS2cSFWResult = iParamS2CRes;
@@ -958,8 +958,8 @@ public class Tcpbw100 extends JApplet implements ActionListener {
_chkboxDefaultTest.setSelected(true);
// 3. configure number of tests
SpinnerNumberModel model = new SpinnerNumberModel();
- model.setMinimum(new Integer(0));
- model.setValue(new Integer(1));
+ model.setMinimum(Integer.valueOf(0));
+ model.setValue(Integer.valueOf(1));
_spinnerTestCount.setModel(model);
_spinnerTestCount.setPreferredSize(new Dimension(60, 20));
_cmboboxDelay = new JComboBox();
@@ -4548,7 +4548,7 @@ public class Tcpbw100 extends JApplet implements ActionListener {
try {
t.interrupt();
} catch (RuntimeException re) {
- _log.warn("TG", re);
+ _log.debug("TG", re);
}
try {
Thread.sleep(20);
@@ -4559,7 +4559,7 @@ public class Tcpbw100 extends JApplet implements ActionListener {
try {
t.stop();
} catch (RuntimeException re) {
- _log.warn("TG", re);
+ _log.debug("TG", re);
}
}
}
@@ -4571,7 +4571,7 @@ public class Tcpbw100 extends JApplet implements ActionListener {
thread_group.destroy();
break;
}catch( Throwable e ){
- _log.warn("TG", e);
+ _log.debug("TG", e);
}
}
diff --git a/apps/routerconsole/java/src/net/i2p/router/sybil/Analysis.java b/apps/routerconsole/java/src/net/i2p/router/sybil/Analysis.java
new file mode 100644
index 0000000000..31ad8bf90b
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/sybil/Analysis.java
@@ -0,0 +1,757 @@
+package net.i2p.router.sybil;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.i2p.app.ClientAppManager;
+import net.i2p.app.ClientAppState;
+import static net.i2p.app.ClientAppState.*;
+import net.i2p.data.DataHelper;
+import net.i2p.data.Destination;
+import net.i2p.data.Hash;
+import net.i2p.data.LeaseSet;
+import net.i2p.data.router.RouterAddress;
+import net.i2p.data.router.RouterInfo;
+import net.i2p.data.router.RouterKeyGenerator;
+import net.i2p.router.JobImpl;
+import net.i2p.router.RouterContext;
+import net.i2p.router.TunnelPoolSettings;
+import net.i2p.router.app.RouterApp;
+import net.i2p.router.crypto.FamilyKeyCrypto;
+import net.i2p.router.peermanager.DBHistory;
+import net.i2p.router.peermanager.PeerProfile;
+import net.i2p.router.tunnel.pool.TunnelPool;
+import net.i2p.router.util.HashDistance;
+import net.i2p.router.web.Messages;
+import net.i2p.stat.Rate;
+import net.i2p.stat.RateAverages;
+import net.i2p.stat.RateStat;
+import net.i2p.util.Addresses;
+import net.i2p.util.Log;
+import net.i2p.util.ObjectCounter;
+
+/**
+ *
+ * @since 0.9.38 split out from SybilRenderer
+ *
+ */
+public class Analysis extends JobImpl implements RouterApp {
+
+ private final RouterContext _context;
+ private final Log _log;
+ private final ClientAppManager _cmgr;
+ private final PersistSybil _persister;
+ private volatile ClientAppState _state = UNINITIALIZED;
+ private final DecimalFormat fmt = new DecimalFormat("#0.00");
+ private boolean _wasRun;
+
+ /**
+ * The name we register with the ClientAppManager
+ */
+ public static final String APP_NAME = "sybil";
+ public static final String PROP_FREQUENCY = "router.sybilFrequency";
+ private static final long MIN_FREQUENCY = 60*60*1000L;
+ private static final long MIN_UPTIME = 75*60*1000L;
+
+ public static final int PAIRMAX = 20;
+ public static final int MAX = 10;
+ // multiplied by size - 1, will also get POINTS24 added
+ private static final double POINTS32 = 5.0;
+ // multiplied by size - 1, will also get POINTS16 added
+ private static final double POINTS24 = 4.0;
+ // multiplied by size - 1
+ private static final double POINTS16 = 0.25;
+ private static final double POINTS_US32 = 25.0;
+ private static final double POINTS_US24 = 20.0;
+ private static final double POINTS_US16 = 10.0;
+ private static final double POINTS_FAMILY = -10.0;
+ private static final double POINTS_BAD_OUR_FAMILY = 100.0;
+ private static final double POINTS_OUR_FAMILY = -100.0;
+ public static final double MIN_CLOSE = 242.0;
+ private static final double PAIR_DISTANCE_FACTOR = 2.0;
+ private static final double OUR_KEY_FACTOR = 4.0;
+ private static final double VERSION_FACTOR = 1.0;
+ private static final double POINTS_BAD_VERSION = 50.0;
+ private static final double POINTS_UNREACHABLE = 4.0;
+ private static final double POINTS_NEW = 4.0;
+ private static final double POINTS_BANLIST = 25.0;
+
+ /** Get via getInstance() */
+ private Analysis(RouterContext ctx, ClientAppManager mgr, String[] args) {
+ super(ctx);
+ _context = ctx;
+ _log = ctx.logManager().getLog(Analysis.class);
+ _cmgr = mgr;
+ _persister = new PersistSybil(ctx);
+ }
+
+ /**
+ * @return non-null, creates new if not already registered
+ */
+ public synchronized static Analysis getInstance(RouterContext ctx) {
+ ClientAppManager cmgr = ctx.clientAppManager();
+ if (cmgr == null)
+ return null;
+ Analysis rv = (Analysis) cmgr.getRegisteredApp(APP_NAME);
+ if (rv == null) {
+ rv = new Analysis(ctx, cmgr, null);
+ rv.startup();
+ }
+ return rv;
+ }
+
+ public PersistSybil getPersister() { return _persister; }
+
+ /////// begin Job methods
+
+ public void runJob() {
+ long now = _context.clock().now();
+ _log.info("Running analysis");
+ Map points = backgroundAnalysis();
+ if (!points.isEmpty()) {
+ try {
+ _log.info("Storing analysis");
+ _persister.store(now, points);
+ _log.info("Store complete");
+ } catch (IOException ioe) {
+ _log.error("Failed to store analysis", ioe);
+ }
+ }
+ schedule();
+ }
+
+ /////// end Job methods
+ /////// begin ClientApp methods
+
+ /**
+ * ClientApp interface
+ */
+ public synchronized void startup() {
+ changeState(STARTING);
+ changeState(RUNNING);
+ _cmgr.register(this);
+ schedule();
+ }
+
+ /**
+ * ClientApp interface
+ * @param args ignored
+ */
+ public synchronized void shutdown(String[] args) {
+ if (_state == STOPPED)
+ return;
+ changeState(STOPPING);
+ changeState(STOPPED);
+ }
+
+ public ClientAppState getState() {
+ return _state;
+ }
+
+ public String getName() {
+ return APP_NAME;
+ }
+
+ public String getDisplayName() {
+ return "Sybil Analyzer";
+ }
+
+ /////// end ClientApp methods
+
+ private synchronized void changeState(ClientAppState state) {
+ _state = state;
+ if (_cmgr != null)
+ _cmgr.notify(this, state, null, null);
+ }
+
+ public synchronized void schedule() {
+ long freq = _context.getProperty(PROP_FREQUENCY, 0L);
+ if (freq > 0) {
+ List previous = _persister.load();
+ long now = _context.clock().now() + 15*1000;
+ if (freq < MIN_FREQUENCY)
+ freq = MIN_FREQUENCY;
+ long when;
+ if (_wasRun) {
+ when = now + freq;
+ } else if (!previous.isEmpty()) {
+ when = Math.max(previous.get(0).longValue() + freq, now);
+ } else {
+ when = now;
+ }
+ long up = _context.router().getUptime();
+ when = Math.max(when, now + MIN_UPTIME - up);
+ getTiming().setStartAfter(when);
+ _context.jobQueue().addJob(this);
+ } else {
+ _context.jobQueue().removeJob(this);
+ }
+ }
+
+ private static class RouterInfoRoutingKeyComparator implements Comparator, Serializable {
+ private final Hash _us;
+ /** @param us ROUTING KEY */
+ public RouterInfoRoutingKeyComparator(Hash us) {
+ _us = us;
+ }
+ public int compare(RouterInfo l, RouterInfo r) {
+ return HashDistance.getDistance(_us, l.getHash()).compareTo(HashDistance.getDistance(_us, r.getHash()));
+ }
+ }
+
+ /**
+ * Merge points1 into points2.
+ * points1 is unmodified.
+ */
+/****
+ private void mergePoints(Map points1, Map points2) {
+ for (Map.Entry e : points1.entrySet()) {
+ Hash h = e.getKey();
+ Points p1 = e.getValue();
+ Points p2 = points2.get(h);
+ if (p2 != null) {
+ p2.points += p1.points;
+ p2.reasons.addAll(p1.reasons);
+ } else {
+ points2.put(h, p1);
+ }
+ }
+ }
+****/
+
+ /** */
+ private void addPoints(Map points, Hash h, double d, String reason) {
+ Points dd = points.get(h);
+ if (dd != null) {
+ dd.addPoints(d, reason);
+ } else {
+ points.put(h, new Points(d, reason));
+ }
+ }
+
+ /**
+ * All the floodfills, not including us
+ * @since 0.9.38 split out from renderRouterInfoHTML
+ */
+ public List getFloodfills(Hash us) {
+ Set ffs = _context.peerManager().getPeersByCapability('f');
+ List ris = new ArrayList(ffs.size());
+ for (Hash ff : ffs) {
+ if (ff.equals(us))
+ continue;
+ RouterInfo ri = _context.netDb().lookupRouterInfoLocally(ff);
+ if (ri != null)
+ ris.add(ri);
+ }
+ return ris;
+ }
+
+ public double getAvgMinDist(List ris) {
+ double tot = 0;
+ int count = 200;
+ byte[] b = new byte[32];
+ for (int i = 0; i < count; i++) {
+ _context.random().nextBytes(b);
+ Hash h = new Hash(b);
+ double d = closestDistance(h, ris);
+ tot += d;
+ }
+ double avgMinDist = tot / count;
+ return avgMinDist;
+ }
+
+ /**
+ * Analyze threats. No output.
+ * Return separate maps for each cause instead?
+ * @since 0.9.38
+ */
+ public synchronized Map backgroundAnalysis() {
+ _wasRun = true;
+ Map points = new HashMap(64);
+ Hash us = _context.routerHash();
+ if (us == null)
+ return points;
+ List ris = getFloodfills(us);
+ if (ris.isEmpty())
+ return points;
+
+ double avgMinDist = getAvgMinDist(ris);
+
+ // IP analysis
+ calculateIPGroupsFamily(ris, points);
+ List ri32 = new ArrayList(4);
+ List ri24 = new ArrayList(4);
+ List ri16 = new ArrayList(4);
+ calculateIPGroupsUs(ris, points, ri32, ri24, ri16);
+ calculateIPGroups32(ris, points);
+ calculateIPGroups24(ris, points);
+ calculateIPGroups16(ris, points);
+
+ // Pairwise distance analysis
+ List pairs = new ArrayList(PAIRMAX);
+ calculatePairDistance(ris, points, pairs);
+
+ // Distance to our router analysis
+ // closest to our routing key today
+ Hash ourRKey = _context.router().getRouterInfo().getRoutingKey();
+ calculateRouterInfo(ourRKey, "our rkey", ris, points);
+ // closest to our routing key tomorrow
+ RouterKeyGenerator rkgen = _context.routerKeyGenerator();
+ Hash nkey = rkgen.getNextRoutingKey(us);
+ calculateRouterInfo(nkey, "our rkey (tomorrow)", ris, points);
+ // closest to us
+ calculateRouterInfo(us, "our router", ris, points);
+
+ // Distance to our published destinations analysis
+ Map clientInboundPools = _context.tunnelManager().getInboundClientPools();
+ List destinations = new ArrayList(clientInboundPools.keySet());
+ for (Hash client : destinations) {
+ boolean isLocal = _context.clientManager().isLocal(client);
+ if (!isLocal)
+ continue;
+ if (! _context.clientManager().shouldPublishLeaseSet(client))
+ continue;
+ LeaseSet ls = _context.netDb().lookupLeaseSetLocally(client);
+ if (ls == null)
+ continue;
+ Hash rkey = ls.getRoutingKey();
+ TunnelPool in = clientInboundPools.get(client);
+ String name = (in != null) ? DataHelper.escapeHTML(in.getSettings().getDestinationNickname()) : client.toBase64().substring(0,4);
+ // closest to routing key today
+ calculateRouterInfo(rkey, name, ris, points);
+ // closest to routing key tomorrow
+ nkey = rkgen.getNextRoutingKey(ls.getHash());
+ calculateRouterInfo(nkey, name + " (tomorrow)", ris, points);
+ }
+
+ // Profile analysis
+ addProfilePoints(ris, points);
+ addVersionPoints(ris, points);
+ return points;
+ }
+
+ /**
+ * @param pairs out parameter, sorted
+ * @return average distance
+ * @since 0.9.38 split out from renderPairDistance()
+ */
+ public double calculatePairDistance(List ris, Map points,
+ List pairs) {
+ int sz = ris.size();
+ double total = 0;
+ for (int i = 0; i < sz; i++) {
+ RouterInfo info1 = ris.get(i);
+ for (int j = i + 1; j < sz; j++) {
+ RouterInfo info2 = ris.get(j);
+ BigInteger dist = HashDistance.getDistance(info1.getHash(), info2.getHash());
+ if (pairs.isEmpty()) {
+ pairs.add(new Pair(info1, info2, dist));
+ } else if (pairs.size() < PAIRMAX) {
+ pairs.add(new Pair(info1, info2, dist));
+ Collections.sort(pairs);
+ } else if (dist.compareTo(pairs.get(PAIRMAX - 1).dist) < 0) {
+ pairs.set(PAIRMAX - 1, new Pair(info1, info2, dist));
+ Collections.sort(pairs);
+ }
+ total += biLog2(dist);
+ }
+ }
+
+ double avg = total / (sz * sz / 2d);
+ for (Pair p : pairs) {
+ double distance = biLog2(p.dist);
+ double point = MIN_CLOSE - distance;
+ if (point < 0)
+ break; // sorted;
+ point *= PAIR_DISTANCE_FACTOR;
+ String b2 = p.r2.getHash().toBase64();
+ addPoints(points, p.r1.getHash(), point, "Very close (" + fmt.format(distance) +
+ ") to other floodfill " + b2 + "");
+ String b1 = p.r1.getHash().toBase64();
+ addPoints(points, p.r2.getHash(), point, "Very close (" + fmt.format(distance) +
+ ") to other floodfill " + b1 + "");
+ }
+ return avg;
+ }
+
+ private static final BigInteger BI_MAX = (new BigInteger("2")).pow(256);
+
+ private static double closestDistance(Hash h, List ris) {
+ BigInteger min = BI_MAX;
+ for (RouterInfo info : ris) {
+ BigInteger dist = HashDistance.getDistance(h, info.getHash());
+ if (dist.compareTo(min) < 0)
+ min = dist;
+ }
+ return biLog2(min);
+ }
+
+ /** v4 only */
+ private static byte[] getIP(RouterInfo ri) {
+ for (RouterAddress ra : ri.getAddresses()) {
+ byte[] rv = ra.getIP();
+ if (rv != null && rv.length == 4)
+ return rv;
+ }
+ return null;
+ }
+
+ /**
+ * @param ri32 out parameter
+ * @param ri24 out parameter
+ * @param ri16 out parameter
+ * @since 0.9.38 split out from renderIPGroupsUs()
+ */
+ public void calculateIPGroupsUs(List ris, Map points,
+ List ri32, List ri24, List ri16) {
+ RouterInfo us = _context.router().getRouterInfo();
+ byte[] ourIP = getIP(us);
+ if (ourIP == null) {
+ String last = _context.getProperty("i2np.lastIP");
+ if (last == null)
+ return;
+ ourIP = Addresses.getIP(last);
+ if (ourIP == null)
+ return;
+ }
+ String reason32 = "Same IP as us";
+ String reason24 = "Same /24 IP as us";
+ String reason16 = "Same /16 IP as us";
+ for (RouterInfo info : ris) {
+ byte[] ip = getIP(info);
+ if (ip == null)
+ continue;
+ if (ip[0] == ourIP[0] && ip[1] == ourIP[1]) {
+ if (ip[2] == ourIP[2]) {
+ if (ip[3] == ourIP[3]) {
+ addPoints(points, info.getHash(), POINTS_US32, reason32);
+ ri32.add(info);
+ } else {
+ addPoints(points, info.getHash(), POINTS_US24, reason24);
+ ri24.add(info);
+ }
+ } else {
+ addPoints(points, info.getHash(), POINTS_US16, reason16);
+ ri16.add(info);
+ }
+ }
+ }
+ }
+
+ /**
+ * @since 0.9.38 split out from renderIPGroups32()
+ */
+ public Map> calculateIPGroups32(List ris, Map points) {
+ ObjectCounter oc = new ObjectCounter();
+ for (RouterInfo info : ris) {
+ byte[] ip = getIP(info);
+ if (ip == null)
+ continue;
+ Integer x = Integer.valueOf((int) DataHelper.fromLong(ip, 0, 4));
+ oc.increment(x);
+ }
+ Map> rv = new HashMap>();
+ for (Integer ii : oc.objects()) {
+ int count = oc.count(ii);
+ if (count >= 2)
+ rv.put(ii, new ArrayList(4));
+ }
+ for (Map.Entry> e : rv.entrySet()) {
+ Integer ii = e.getKey();
+ int count = oc.count(ii);
+ double point = POINTS32 * (count - 1);
+ int i = ii.intValue();
+ int i0 = (i >> 24) & 0xff;
+ int i1 = (i >> 16) & 0xff;
+ int i2 = (i >> 8) & 0xff;
+ int i3 = i & 0xff;
+ String reason = "Same IP with " +
+ (count - 1) + " other" + (( count > 2) ? "s" : "") + "";
+ for (RouterInfo info : ris) {
+ byte[] ip = getIP(info);
+ if (ip == null)
+ continue;
+ if ((ip[0] & 0xff) != i0)
+ continue;
+ if ((ip[1] & 0xff) != i1)
+ continue;
+ if ((ip[2] & 0xff) != i2)
+ continue;
+ if ((ip[3] & 0xff) != i3)
+ continue;
+ e.getValue().add(info);
+ addPoints(points, info.getHash(), point, reason);
+ }
+ }
+ return rv;
+ }
+
+ /**
+ * @since 0.9.38 split out from renderIPGroups24()
+ */
+ public Map> calculateIPGroups24(List ris, Map points) {
+ ObjectCounter oc = new ObjectCounter();
+ for (RouterInfo info : ris) {
+ byte[] ip = getIP(info);
+ if (ip == null)
+ continue;
+ Integer x = Integer.valueOf((int) DataHelper.fromLong(ip, 0, 3));
+ oc.increment(x);
+ }
+ Map> rv = new HashMap>();
+ for (Integer ii : oc.objects()) {
+ int count = oc.count(ii);
+ if (count >= 2)
+ rv.put(ii, new ArrayList(4));
+ }
+ for (Map.Entry> e : rv.entrySet()) {
+ Integer ii = e.getKey();
+ int count = oc.count(ii);
+ double point = POINTS24 * (count - 1);
+ int i = ii.intValue();
+ int i0 = i >> 16;
+ int i1 = (i >> 8) & 0xff;
+ int i2 = i & 0xff;
+ String reason = "Same /24 IP with " +
+ (count - 1) + " other" + (( count > 2) ? "s" : "") + "";
+ for (RouterInfo info : ris) {
+ byte[] ip = getIP(info);
+ if (ip == null)
+ continue;
+ if ((ip[0] & 0xff) != i0)
+ continue;
+ if ((ip[1] & 0xff) != i1)
+ continue;
+ if ((ip[2] & 0xff) != i2)
+ continue;
+ e.getValue().add(info);
+ addPoints(points, info.getHash(), point, reason);
+ }
+ }
+ return rv;
+ }
+
+ /**
+ * @since 0.9.38 split out from renderIPGroups16()
+ */
+ public Map> calculateIPGroups16(List ris, Map points) {
+ ObjectCounter oc = new ObjectCounter();
+ for (RouterInfo info : ris) {
+ byte[] ip = getIP(info);
+ if (ip == null)
+ continue;
+ Integer x = Integer.valueOf((int) DataHelper.fromLong(ip, 0, 2));
+ oc.increment(x);
+ }
+ Map> rv = new HashMap>();
+ for (Integer ii : oc.objects()) {
+ int count = oc.count(ii);
+ if (count >= 4)
+ rv.put(ii, new ArrayList(8));
+ }
+ for (Map.Entry> e : rv.entrySet()) {
+ Integer ii = e.getKey();
+ int count = oc.count(ii);
+ double point = POINTS16 * (count - 1);
+ int i = ii.intValue();
+ int i0 = i >> 8;
+ int i1 = i & 0xff;
+ String reason = "Same /16 IP with " +
+ (count - 1) + " other" + (( count > 2) ? "s" : "") + "";
+ for (RouterInfo info : ris) {
+ byte[] ip = getIP(info);
+ if (ip == null)
+ continue;
+ if ((ip[0] & 0xff) != i0)
+ continue;
+ if ((ip[1] & 0xff) != i1)
+ continue;
+ e.getValue().add(info);
+ addPoints(points, info.getHash(), point, reason);
+ }
+ }
+ return rv;
+ }
+
+ /**
+ * @since 0.9.38 split out from renderIPGroupsFamily()
+ */
+ public Map> calculateIPGroupsFamily(List ris, Map points) {
+ ObjectCounter oc = new ObjectCounter();
+ for (RouterInfo info : ris) {
+ String fam = info.getOption("family");
+ if (fam == null)
+ continue;
+ oc.increment(fam);
+ }
+ List foo = new ArrayList(oc.objects());
+ Map> rv = new HashMap>(foo.size());
+ FamilyKeyCrypto fkc = _context.router().getFamilyKeyCrypto();
+ String ourFamily = fkc != null ? fkc.getOurFamilyName() : null;
+ for (String s : foo) {
+ int count = oc.count(s);
+ List list = new ArrayList(count);
+ rv.put(s, list);
+ String ss = DataHelper.escapeHTML(s);
+ for (RouterInfo info : ris) {
+ String fam = info.getOption("family");
+ if (fam == null)
+ continue;
+ if (!fam.equals(s))
+ continue;
+ list.add(info);
+ double point = POINTS_FAMILY;
+ if (fkc != null && s.equals(ourFamily)) {
+ if (fkc.verifyOurFamily(info))
+ addPoints(points, info.getHash(), POINTS_OUR_FAMILY, "Our family \"" + ss + "\" with " + (count - 1) + " other" + (( count > 2) ? "s" : ""));
+ else
+ addPoints(points, info.getHash(), POINTS_BAD_OUR_FAMILY, "Spoofed our family \"" + ss + "\" with " + (count - 1) + " other" + (( count > 2) ? "s" : ""));
+ } else if (count > 1) {
+ addPoints(points, info.getHash(), point, "In family \"" + ss + "\" with " + (count - 1) + " other" + (( count > 2) ? "s" : ""));
+ } else {
+ addPoints(points, info.getHash(), point, "In family \"" + ss + '"');
+ }
+ }
+ }
+ return rv;
+ }
+
+ private static final long DAY = 24*60*60*1000L;
+
+ public void addProfilePoints(List ris, Map points) {
+ long now = _context.clock().now();
+ RateAverages ra = RateAverages.getTemp();
+ for (RouterInfo info : ris) {
+ Hash h = info.getHash();
+ if (_context.banlist().isBanlisted(h))
+ addPoints(points, h, POINTS_BANLIST, "Banlisted");
+ PeerProfile prof = _context.profileOrganizer().getProfileNonblocking(h);
+ if (prof != null) {
+ long heard = prof.getFirstHeardAbout();
+ if (heard > 0) {
+ long age = Math.max(now - heard, 1);
+ if (age < 2 * DAY) {
+ // (POINTS_NEW / 48) for every hour under 48, max POINTS_NEW
+ double point = Math.min(POINTS_NEW, (2 * DAY - age) / (2 * DAY / POINTS_NEW));
+ addPoints(points, h, point,
+ "First heard about: " + _t("{0} ago", DataHelper.formatDuration2(age)));
+ }
+ }
+ DBHistory dbh = prof.getDBHistory();
+ if (dbh != null) {
+ RateStat rs = dbh.getFailedLookupRate();
+ if (rs != null) {
+ Rate r = rs.getRate(24*60*60*1000);
+ if (r != null) {
+ r.computeAverages(ra, false);
+ if (ra.getTotalEventCount() > 0) {
+ double avg = 100 * ra.getAverage();
+ if (avg > 40)
+ addPoints(points, h, (avg - 40) / 6.0, "Lookup fail rate " + ((int) avg) + '%');
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void addVersionPoints(List ris, Map points) {
+ RouterInfo us = _context.router().getRouterInfo();
+ if (us == null) return;
+ String ourVer = us.getVersion();
+ if (!ourVer.startsWith("0.9.")) return;
+ ourVer = ourVer.substring(4);
+ int dot = ourVer.indexOf('.');
+ if (dot > 0)
+ ourVer = ourVer.substring(0, dot);
+ int minor;
+ try {
+ minor = Integer.parseInt(ourVer);
+ } catch (NumberFormatException nfe) { return; }
+ for (RouterInfo info : ris) {
+ Hash h = info.getHash();
+ String caps = info.getCapabilities();
+ if (!caps.contains("R"))
+ addPoints(points, h, POINTS_UNREACHABLE, "Unreachable: " + DataHelper.escapeHTML(caps));
+ String hisFullVer = info.getVersion();
+ if (!hisFullVer.startsWith("0.9.")) {
+ addPoints(points, h, POINTS_BAD_VERSION, "Strange version " + DataHelper.escapeHTML(hisFullVer));
+ continue;
+ }
+ String hisVer = hisFullVer.substring(4);
+ dot = hisVer.indexOf('.');
+ if (dot > 0)
+ hisVer = hisVer.substring(0, dot);
+ int hisMinor;
+ try {
+ hisMinor = Integer.parseInt(hisVer);
+ } catch (NumberFormatException nfe) { continue; }
+ int howOld = minor - hisMinor;
+ if (howOld < 3)
+ continue;
+ addPoints(points, h, howOld * VERSION_FACTOR, howOld + " versions behind: " + DataHelper.escapeHTML(hisFullVer));
+ }
+ }
+
+ /**
+ * @param usName HTML escaped
+ * @param ris will be re-sorted in place
+ * @since 0.9.38 split out from renderRouterInfoHTML()
+ */
+ public void calculateRouterInfo(Hash us, String usName,
+ List ris, Map points) {
+ Collections.sort(ris, new RouterInfoRoutingKeyComparator(us));
+ int count = Math.min(MAX, ris.size());
+ for (int i = 0; i < count; i++) {
+ RouterInfo ri = ris.get(i);
+ BigInteger bidist = HashDistance.getDistance(us, ri.getHash());
+ double dist = biLog2(bidist);
+ double point = MIN_CLOSE - dist;
+ if (point <= 0)
+ break;
+ point *= OUR_KEY_FACTOR;
+ addPoints(points, ri.getHash(), point, "Very close (" + fmt.format(dist) + ") to our key " + usName + ": " + us.toBase64());
+ }
+ }
+
+ /**
+ * For debugging
+ * http://forums.sun.com/thread.jspa?threadID=597652
+ * @since 0.7.14
+ */
+ private static double biLog2(BigInteger a) {
+ return Util.biLog2(a);
+ }
+
+ /**
+ * translate a string with a parameter
+ * This is a lot more expensive than _t(s), so use sparingly.
+ *
+ * @param s string to be translated containing {0}
+ * The {0} will be replaced by the parameter.
+ * Single quotes must be doubled, i.e. ' -> '' in the string.
+ * @param o parameter, not translated.
+ * To translate parameter also, use _t("foo {0} bar", _t("baz"))
+ * Do not double the single quotes in the parameter.
+ * Use autoboxing to call with ints, longs, floats, etc.
+ */
+ private String _t(String s, Object o) {
+ return Messages.getString(s, o, _context);
+ }
+}
diff --git a/apps/routerconsole/java/src/net/i2p/router/sybil/Pair.java b/apps/routerconsole/java/src/net/i2p/router/sybil/Pair.java
new file mode 100644
index 0000000000..adfe81470c
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/sybil/Pair.java
@@ -0,0 +1,24 @@
+package net.i2p.router.sybil;
+
+import java.math.BigInteger;
+
+import net.i2p.data.router.RouterInfo;
+
+/**
+ * A pair of routers and the distance between them.
+ *
+ * @since 0.9.38 moved from SybilRenderer
+ */
+public class Pair implements Comparable {
+ public final RouterInfo r1, r2;
+ public final BigInteger dist;
+
+ public Pair(RouterInfo ri1, RouterInfo ri2, BigInteger distance) {
+ r1 = ri1; r2 = ri2; dist = distance;
+ }
+
+ public int compareTo(Pair p) {
+ return this.dist.compareTo(p.dist);
+ }
+}
+
diff --git a/apps/routerconsole/java/src/net/i2p/router/sybil/PersistSybil.java b/apps/routerconsole/java/src/net/i2p/router/sybil/PersistSybil.java
new file mode 100644
index 0000000000..23ad7f3b05
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/sybil/PersistSybil.java
@@ -0,0 +1,217 @@
+package net.i2p.router.sybil;
+
+import java.io.BufferedReader;
+import java.util.Collections;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+import net.i2p.I2PAppContext;
+import net.i2p.data.Base64;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
+import net.i2p.data.Hash;
+import net.i2p.router.web.helpers.SybilRenderer;
+import net.i2p.util.Log;
+import net.i2p.util.FileSuffixFilter;
+import net.i2p.util.SecureDirectory;
+import net.i2p.util.SecureFileOutputStream;
+
+/**
+ * Store and retrieve analysis files from disk.
+ * Each file is named with a timestamp.
+ *
+ * @since 0.9.38
+ */
+public class PersistSybil {
+
+ private final I2PAppContext _context;
+ private final Log _log;
+
+ private static final String DIR = "sybil-analysis/results";
+ private static final String PFX = "sybil-";
+ private static final String SFX = ".txt.gz";
+
+ /** access via Analysis.getPersister() */
+ PersistSybil(I2PAppContext ctx) {
+ _context = ctx;
+ _log = ctx.logManager().getLog(PersistSybil.class);
+ }
+
+ /**
+ * Store each entry.
+ *
+ * @param entries each one should be "entry" at the root
+ */
+ public synchronized void store(long date, Map entries) throws IOException {
+ File dir = new SecureDirectory(_context.getConfigDir(), DIR);
+ if (!dir.exists())
+ dir.mkdirs();
+ File file = new File(dir, PFX + date + SFX);
+ StringBuilder buf = new StringBuilder(128);
+ Writer out = null;
+ try {
+ out = new OutputStreamWriter(new GZIPOutputStream(new SecureFileOutputStream(file)));
+ for (Map.Entry entry : entries.entrySet()) {
+ Hash h = entry.getKey();
+ Points p = entry.getValue();
+ buf.append(h.toBase64()).append(':');
+ p.toString(buf);
+ buf.append('\n');
+ out.write(buf.toString());
+ buf.setLength(0);
+ }
+ } finally {
+ if (out != null) try { out.close(); } catch (IOException ioe) {}
+ }
+ }
+
+ /**
+ * The list of stored analysis sets, as a time stamp.
+ *
+ * @return non-null, sorted by updated date, newest first
+ */
+ public synchronized List load() {
+ File dir = new File(_context.getConfigDir(), DIR);
+ List rv = new ArrayList();
+ File[] files = dir.listFiles(new FileSuffixFilter(PFX, SFX));
+ if (files == null)
+ return rv;
+ for (File file : files) {
+ try {
+ String name = file.getName();
+ long d = Long.parseLong(name.substring(PFX.length(), name.length() - SFX.length()));
+ rv.add(Long.valueOf(d));
+ } catch (NumberFormatException nfe) {}
+ }
+ Collections.sort(rv, Collections.reverseOrder());
+ return rv;
+ }
+
+ /**
+ * Load the analysis for a certain date.
+ *
+ * @return non-null, unsorted
+ */
+ public synchronized Map load(long date) throws IOException {
+ File dir = new File(_context.getConfigDir(), DIR);
+ File file = new File(dir, PFX + date + SFX);
+ Map rv = new HashMap();
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(file))));
+ String line;
+ while ((line = in.readLine()) != null) {
+ int colon = line.indexOf(':');
+ if (colon != 44)
+ continue;
+ if (line.length() < 46)
+ continue;
+ Hash h = new Hash();
+ try {
+ h.fromBase64(line.substring(0, 44));
+ } catch (DataFormatException dfe) {
+ continue;
+ }
+ Points p = Points.fromString(line.substring(45));
+ if (p == null)
+ continue;
+ rv.put(h, p);
+ }
+ } finally {
+ if (in != null) try { in.close(); } catch (IOException ioe) {}
+ }
+ return rv;
+ }
+
+ /**
+ * Load all the analysis for a certain hash.
+ *
+ * @return non-null, unsorted
+ */
+ public synchronized Map load(Hash h) throws IOException {
+ String bh = h.toBase64() + ':';
+ File dir = new File(_context.getConfigDir(), DIR);
+ Map rv = new HashMap();
+ List dates = load();
+ for (Long date : dates) {
+ File file = new File(dir, PFX + date + SFX);
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(file))));
+ String line;
+ while ((line = in.readLine()) != null) {
+ if (!line.startsWith(bh))
+ continue;
+ if (line.length() < 46)
+ continue;
+ Points p = Points.fromString(line.substring(45));
+ if (p == null)
+ continue;
+ rv.put(date, p);
+ }
+ } finally {
+ if (in != null) try { in.close(); } catch (IOException ioe) {}
+ }
+ }
+ return rv;
+ }
+
+ /**
+ * Delete the file for a particular date
+ *
+ * @return success
+ */
+ public synchronized boolean delete(long date) {
+ File dir = new File(_context.getConfigDir(), DIR);
+ File file = new File(dir, PFX + date + SFX);
+ return file.delete();
+ }
+
+/****
+ public static void main(String[] args) {
+ I2PAppContext ctx = I2PAppContext.getGlobalContext();
+ PersistSybil ps = new PersistSybil(ctx);
+ byte[] b = new byte[32];
+ ctx.random().nextBytes(b);
+ Hash h = new Hash(b);
+ String rsn = "Test reason";
+ Points p = new Points(1.234, rsn);
+ rsn = "Test reason2";
+ p.addPoints(2.345, rsn);
+ Map map = new HashMap();
+ map.put(h, p);
+ b = new byte[32];
+ ctx.random().nextBytes(b);
+ h = new Hash(b);
+ map.put(h, p);
+ try {
+ long now = System.currentTimeMillis();
+ System.out.println("storing entries: " + map.size());
+ ps.store(System.currentTimeMillis(), map);
+ List dates = ps.load();
+ System.out.println("Found sets: " + dates.size());
+ map = ps.load(Long.valueOf(now));
+ System.out.println("loaded entries: " + map.size());
+ for (Map.Entry e : map.entrySet()) {
+ System.out.println(e.getKey().toString() + ": " + e.getValue());
+ }
+ } catch (IOException ioe) {
+ System.out.println("store error from " + args[0]);
+ ioe.printStackTrace();
+ }
+ }
+****/
+}
diff --git a/apps/routerconsole/java/src/net/i2p/router/sybil/Points.java b/apps/routerconsole/java/src/net/i2p/router/sybil/Points.java
new file mode 100644
index 0000000000..b1f7d65ca1
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/sybil/Points.java
@@ -0,0 +1,111 @@
+package net.i2p.router.sybil;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.i2p.data.DataHelper;
+
+/**
+ * A total score and a List of reason Strings
+ *
+ * @since 0.9.38 moved from SybilRenderer
+ */
+public class Points implements Comparable {
+ private double points;
+ private final List reasons;
+
+ /**
+ * @since 0.9.38
+ */
+ private Points() {
+ reasons = new ArrayList(4);
+ }
+
+ /**
+ * @param reason may not contain '%'
+ */
+ public Points(double d, String reason) {
+ this();
+ addPoints(d, reason);
+ }
+
+ /**
+ * @since 0.9.38
+ */
+ public double getPoints() {
+ return points;
+ }
+
+ /**
+ * @since 0.9.38
+ */
+ public List getReasons() {
+ return reasons;
+ }
+
+ /**
+ * @param reason may not contain '%'
+ * @since 0.9.38
+ */
+ public void addPoints(double d, String reason) {
+ points += d;
+ DecimalFormat format = new DecimalFormat("#0.00");
+ String rsn = format.format(d) + ": " + reason;
+ reasons.add(rsn);
+ }
+
+ public int compareTo(Points r) {
+ return Double.compare(points, r.points);
+ }
+
+ /**
+ * @since 0.9.38
+ */
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder(128);
+ toString(buf);
+ return buf.toString();
+ }
+
+ /**
+ * For persistence.
+ * Total points and reasons, '%' separated, no newline.
+ * The separation character is chosen to not conflict with
+ * decimal point in various locales, or chars in reasons, including HTML links,
+ * or special chars in Pattern.
+ *
+ * @since 0.9.38
+ */
+ public void toString(StringBuilder buf) {
+ buf.append(points);
+ for (String r : reasons) {
+ buf.append('%').append(r);
+ }
+ }
+
+ /**
+ * For persistence.
+ * @return null on failure
+ * @since 0.9.38
+ */
+ public static Points fromString(String s) {
+ String[] ss = DataHelper.split(s, "%");
+ if (ss.length < 2)
+ return null;
+ double d;
+ try {
+ d = Double.parseDouble(ss[0]);
+ } catch (NumberFormatException nfe) {
+ return null;
+ }
+ Points rv = new Points();
+ for (int i = 1; i < ss.length; i++) {
+ rv.reasons.add(ss[i]);
+ }
+ rv.points = d;
+ return rv;
+ }
+}
+
diff --git a/apps/routerconsole/java/src/net/i2p/router/sybil/Util.java b/apps/routerconsole/java/src/net/i2p/router/sybil/Util.java
new file mode 100644
index 0000000000..d18203764d
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/sybil/Util.java
@@ -0,0 +1,28 @@
+package net.i2p.router.sybil;
+
+import java.math.BigInteger;
+
+/**
+ * For NetDbRenderer and Sybil
+ * http://forums.sun.com/thread.jspa?threadID=597652
+ * @since 0.9.38 moved from NetDbRenderer
+ */
+public class Util {
+
+ /**
+ * For debugging
+ * http://forums.sun.com/thread.jspa?threadID=597652
+ * @since 0.7.14
+ */
+ public static double biLog2(BigInteger a) {
+ int b = a.bitLength() - 1;
+ double c = 0;
+ double d = 0.5;
+ for (int i = b; i >= 0; --i) {
+ if (a.testBit(i))
+ c += d;
+ d /= 2;
+ }
+ return b + c;
+ }
+}
diff --git a/apps/routerconsole/java/src/net/i2p/router/sybil/package.html b/apps/routerconsole/java/src/net/i2p/router/sybil/package.html
new file mode 100644
index 0000000000..a1219ceb5c
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/sybil/package.html
@@ -0,0 +1,9 @@
+
+
+Classes to run offline Sybil analysis, and to
+store and load the results.
+
+
+Since 0.9.38.
+
+
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java
index 857d16cb57..55e42b471d 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java
@@ -228,8 +228,6 @@ public class ConfigServiceHandler extends FormHandler {
@Override
protected void processForm() {
- if (_action == null) return;
-
if (_t("Shutdown gracefully").equals(_action)) {
if (_context.hasWrapper())
registerWrapperNotifier(Router.EXIT_GRACEFUL, false);
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java
index 85b043104c..a1e9c0261d 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java
@@ -157,8 +157,6 @@ public class ConfigUpdateHandler extends FormHandler {
@Override
protected void processForm() {
- if (_action == null)
- return;
if (_action.equals(_t("Check for updates"))) {
ConsoleUpdateManager mgr = UpdateHandler.updateManager(_context);
if (mgr == null) {
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java
index 7681196748..7be5a8e42e 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java
@@ -1,5 +1,6 @@
package net.i2p.router.web;
+import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -36,6 +37,7 @@ public abstract class FormHandler {
private final List _notices;
private boolean _processed;
private boolean _valid;
+ protected Writer _out;
public FormHandler() {
_errors = new ArrayList();
@@ -110,6 +112,11 @@ public abstract class FormHandler {
*/
public void storeMethod(String val) { _method = val; }
+ /**
+ * @since 0.9.38
+ */
+ public void storeWriter(Writer out) { _out = out; }
+
/**
* The old nonces from the session
* @since 0.9.4
@@ -120,11 +127,12 @@ public abstract class FormHandler {
}
/**
- * Override this to perform the final processing (in turn, adding formNotice
+ * Implement this to perform the final processing (in turn, adding formNotice
* and formError messages, etc)
*
+ * Will only be called if _action is non-null and the nonce is valid.
*/
- protected void processForm() {}
+ protected abstract void processForm();
/**
* Add an error message to display
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
index 7410e7140d..69ee850d27 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
@@ -34,6 +34,7 @@ import net.i2p.jetty.I2PLogger;
import net.i2p.router.RouterContext;
import net.i2p.router.app.RouterApp;
import net.i2p.router.news.NewsManager;
+import net.i2p.router.sybil.Analysis;
import net.i2p.router.update.ConsoleUpdateManager;
import net.i2p.util.Addresses;
import net.i2p.util.FileSuffixFilter;
@@ -342,9 +343,10 @@ public class RouterConsoleRunner implements RouterApp {
boolean noJava7 = !SystemVersion.isJava7();
boolean noPack200 = (PluginStarter.pluginsEnabled(_context) || !NewsHelper.isUpdateDisabled(_context)) &&
!FileUtil.isPack200Supported();
- boolean openARM = SystemVersion.isARM() && SystemVersion.isOpenJDK();
- boolean isJava11 = SystemVersion.isJava11();
- if (noJava7 || noPack200 || openARM || isJava11) {
+ boolean openARM = SystemVersion.isARM() && SystemVersion.isOpenJDK() && !SystemVersion.isJava9();
+ boolean isZero = SystemVersion.isZeroVM();
+ boolean isJava11 = false; // SystemVersion.isJava11();
+ if (noJava7 || noPack200 || openARM || isZero || isJava11) {
String s = "Java version: " + System.getProperty("java.version") +
" OS: " + System.getProperty("os.name") + ' ' +
System.getProperty("os.arch") + ' ' +
@@ -363,15 +365,20 @@ public class RouterConsoleRunner implements RouterApp {
System.out.println("Warning: " + s);
}
if (openARM) {
- s = "OpenJDK is not recommended for ARM. Use Oracle Java 8";
+ s = "OpenJDK 7/8 are not recommended for ARM. Use OpenJDK 9 (or higher) or Oracle Java 8 (or higher)";
log.logAlways(net.i2p.util.Log.WARN, s);
System.out.println("Warning: " + s);
}
- if (isJava11) {
- s = "Java 11+ support is beta, and not recommended for general use";
+ if (isZero) {
+ s = "OpenJDK Zero is a very slow interpreter-only JVM. Not recommended for use with I2P. Please use a faster JVM if possible.";
log.logAlways(net.i2p.util.Log.WARN, s);
System.out.println("Warning: " + s);
}
+ //if (isJava11) {
+ // s = "Java 11+ support is beta, and not recommended for general use";
+ // log.logAlways(net.i2p.util.Log.WARN, s);
+ // System.out.println("Warning: " + s);
+ //}
}
}
@@ -865,6 +872,13 @@ public class RouterConsoleRunner implements RouterApp {
if (_mgr == null)
_context.addShutdownTask(new ServerShutdown());
ConfigServiceHandler.registerSignalHandler(_context);
+
+ if (_mgr != null &&
+ _context.getBooleanProperty(HelperBase.PROP_ADVANCED) &&
+ _context.getProperty(Analysis.PROP_FREQUENCY, 0L) > 0) {
+ // registers and starts itself
+ Analysis.getInstance(_context);
+ }
}
/**
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigHomeHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigHomeHandler.java
index 3a71f11ea3..eb0c7f74db 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigHomeHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigHomeHandler.java
@@ -18,7 +18,6 @@ public class ConfigHomeHandler extends FormHandler {
@Override
protected void processForm() {
- if (_action == null) return;
String group = getJettyString("group");
boolean deleting = _action.equals(_t("Delete selected"));
boolean adding = _action.equals(_t("Add item"));
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigKeyringHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigKeyringHandler.java
index 6cfa3885a7..c731770cdc 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigKeyringHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigKeyringHandler.java
@@ -15,7 +15,6 @@ public class ConfigKeyringHandler extends FormHandler {
@Override
protected void processForm() {
- if (_action == null) return;
boolean adding = _action.equals(_t("Add key"));
if (adding || _action.equals(_t("Delete key"))) {
if (_peer == null)
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigSummaryHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigSummaryHandler.java
index bfe2fb87a9..073a55fea7 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigSummaryHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigSummaryHandler.java
@@ -20,7 +20,6 @@ public class ConfigSummaryHandler extends FormHandler {
@Override
protected void processForm() {
- if (_action == null) return;
String group = getJettyString("group");
boolean deleting = _action.equals(_t("Delete selected"));
boolean adding = _action.equals(_t("Add item"));
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigUIHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigUIHelper.java
index fac2989e08..5aef38ee57 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigUIHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigUIHelper.java
@@ -36,7 +36,7 @@ public class ConfigUIHelper extends HelperBase {
buf.append(CHECKED);
buf.append("value=\"").append(theme).append("\" id=\"").append(theme).append("\">" +
"
" +
"").append(_t(theme)).append("
" +
"\n");
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/EventLogHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/EventLogHelper.java
index e7c0e43124..2f67328c24 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/EventLogHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/EventLogHelper.java
@@ -1,7 +1,6 @@
package net.i2p.router.web.helpers;
import java.io.IOException;
-import java.io.Writer;
import java.text.Collator;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
@@ -24,7 +23,6 @@ import net.i2p.util.SystemVersion;
* /events.jsp
*/
public class EventLogHelper extends FormHandler {
- protected Writer _out;
private long _from, _age;
//private long _to = Long.MAX_VALUE;
private String _event = ALL;
@@ -63,6 +61,8 @@ public class EventLogHelper extends FormHandler {
_xevents = new HashMap(1 + (_events.length / 2));
}
+ protected void processForm() {}
+
/** set the defaults after we have a context */
@Override
public void setContextId(String contextId) {
@@ -72,8 +72,6 @@ public class EventLogHelper extends FormHandler {
}
}
- public void storeWriter(Writer out) { _out = out; }
-
public void setFrom(String s) {
try {
_age = Long.parseLong(s);
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/GraphHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/GraphHelper.java
index 848e8d1134..69dd7fe325 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/GraphHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/GraphHelper.java
@@ -2,7 +2,6 @@ package net.i2p.router.web.helpers;
import java.io.IOException;
import java.io.Serializable;
-import java.io.Writer;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
@@ -25,7 +24,6 @@ import net.i2p.stat.Rate;
* /graphs.jsp, including form, and /graph.jsp
*/
public class GraphHelper extends FormHandler {
- protected Writer _out;
private int _periodCount;
private boolean _showEvents;
private int _width;
@@ -74,12 +72,6 @@ public class GraphHelper extends FormHandler {
return "";
}
- /**
- * This was a HelperBase but now it's a FormHandler
- * @since 0.8.2
- */
- public void storeWriter(Writer out) { _out = out; }
-
public void setPeriodCount(String str) {
setC(str);
}
@@ -403,10 +395,10 @@ public class GraphHelper extends FormHandler {
for (int i = 0; i < times.length; i++) {
_out.write("