forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p.zzz.confsplit' (head 324249e53469a81e66f9d1c1989d9f53817868f4)
to branch 'i2p.i2p' (head cf6476e03a43a35fea6697b29f9ff43f77875100)
This commit is contained in:
@@ -4,11 +4,13 @@ import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -22,6 +24,7 @@ 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.Banlist;
|
||||
import net.i2p.router.JobImpl;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
@@ -38,6 +41,7 @@ import net.i2p.stat.RateStat;
|
||||
import net.i2p.util.Addresses;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.ObjectCounter;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -59,6 +63,11 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
*/
|
||||
public static final String APP_NAME = "sybil";
|
||||
public static final String PROP_FREQUENCY = "router.sybilFrequency";
|
||||
public static final String PROP_THRESHOLD = "router.sybilThreshold";
|
||||
public static final String PROP_BLOCK = "router.sybilEnableBlocking";
|
||||
public static final String PROP_NONFF = "router.sybilAnalyzeAll";
|
||||
public static final String PROP_BLOCKTIME = "router.sybilBlockPeriod";
|
||||
public static final String PROP_REMOVETIME = "router.sybilDeleteOld";
|
||||
private static final long MIN_FREQUENCY = 60*60*1000L;
|
||||
private static final long MIN_UPTIME = 75*60*1000L;
|
||||
|
||||
@@ -74,6 +83,7 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
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_NONFF = -5.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;
|
||||
@@ -84,6 +94,9 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
private static final double POINTS_UNREACHABLE = 4.0;
|
||||
private static final double POINTS_NEW = 4.0;
|
||||
private static final double POINTS_BANLIST = 25.0;
|
||||
private static final double DEFAULT_BLOCK_THRESHOLD = 50.0;
|
||||
private static final long DEFAULT_BLOCK_TIME = 7*24*60*60*1000L;
|
||||
public static final float MIN_BLOCK_POINTS = 12.01f;
|
||||
|
||||
/** Get via getInstance() */
|
||||
private Analysis(RouterContext ctx, ClientAppManager mgr, String[] args) {
|
||||
@@ -116,11 +129,12 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
public void runJob() {
|
||||
long now = _context.clock().now();
|
||||
_log.info("Running analysis");
|
||||
Map<Hash, Points> points = backgroundAnalysis();
|
||||
Map<Hash, Points> points = backgroundAnalysis(_context.getBooleanProperty(PROP_NONFF));
|
||||
if (!points.isEmpty()) {
|
||||
try {
|
||||
_log.info("Storing analysis");
|
||||
_persister.store(now, points);
|
||||
_persister.removeOld();
|
||||
_log.info("Store complete");
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Failed to store analysis", ioe);
|
||||
@@ -139,6 +153,7 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
changeState(STARTING);
|
||||
changeState(RUNNING);
|
||||
_cmgr.register(this);
|
||||
_persister.removeOld();
|
||||
schedule();
|
||||
}
|
||||
|
||||
@@ -255,6 +270,20 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
return ris;
|
||||
}
|
||||
|
||||
/**
|
||||
* All the routers, not including us
|
||||
* @since 0.9.41
|
||||
*/
|
||||
public List<RouterInfo> getAllRouters(Hash us) {
|
||||
Set<RouterInfo> set = _context.netDb().getRouters();
|
||||
List<RouterInfo> ris = new ArrayList<RouterInfo>(set.size());
|
||||
for (RouterInfo ri : set) {
|
||||
if (!ri.getIdentity().getHash().equals(us))
|
||||
ris.add(ri);
|
||||
}
|
||||
return ris;
|
||||
}
|
||||
|
||||
public double getAvgMinDist(List<RouterInfo> ris) {
|
||||
double tot = 0;
|
||||
int count = 200;
|
||||
@@ -272,17 +301,25 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
/**
|
||||
* Analyze threats. No output.
|
||||
* Return separate maps for each cause instead?
|
||||
* @param includeAll false for floodfills only
|
||||
* @since 0.9.38
|
||||
*/
|
||||
public synchronized Map<Hash, Points> backgroundAnalysis() {
|
||||
public synchronized Map<Hash, Points> backgroundAnalysis(boolean includeAll) {
|
||||
_wasRun = true;
|
||||
Map<Hash, Points> points = new HashMap<Hash, Points>(64);
|
||||
Hash us = _context.routerHash();
|
||||
if (us == null)
|
||||
return points;
|
||||
List<RouterInfo> ris = getFloodfills(us);
|
||||
List<RouterInfo> ris;
|
||||
if (includeAll) {
|
||||
ris = getAllRouters(us);
|
||||
} else {
|
||||
ris = getFloodfills(us);
|
||||
}
|
||||
if (ris.isEmpty())
|
||||
return points;
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Analyzing " + ris.size() + " routers, including non-floodfills? " + includeAll);
|
||||
|
||||
double avgMinDist = getAvgMinDist(ris);
|
||||
|
||||
@@ -336,9 +373,45 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
// Profile analysis
|
||||
addProfilePoints(ris, points);
|
||||
addVersionPoints(ris, points);
|
||||
if (_context.getBooleanProperty(PROP_BLOCK))
|
||||
doBlocking(points);
|
||||
return points;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocklist and Banlist if configured
|
||||
* @since 0.9.41
|
||||
*/
|
||||
private void doBlocking(Map<Hash, Points> points) {
|
||||
double threshold = DEFAULT_BLOCK_THRESHOLD;
|
||||
long now = _context.clock().now();
|
||||
long blockUntil = _context.getProperty(Analysis.PROP_BLOCKTIME, DEFAULT_BLOCK_TIME) + now;
|
||||
try {
|
||||
threshold = Double.parseDouble(_context.getProperty(PROP_THRESHOLD, Double.toString(DEFAULT_BLOCK_THRESHOLD)));
|
||||
if (threshold < MIN_BLOCK_POINTS)
|
||||
threshold = MIN_BLOCK_POINTS;
|
||||
} catch (NumberFormatException nfe) {}
|
||||
DateFormat dfmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT);
|
||||
dfmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
|
||||
String day = dfmt.format(now);
|
||||
for (Map.Entry<Hash, Points> e : points.entrySet()) {
|
||||
double p = e.getValue().getPoints();
|
||||
if (p >= threshold) {
|
||||
Hash h = e.getKey();
|
||||
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(h);
|
||||
if (ri != null) {
|
||||
for (RouterAddress ra : ri.getAddresses()) {
|
||||
byte[] ip = ra.getIP();
|
||||
if (ip != null)
|
||||
_context.blocklist().add(ip);
|
||||
}
|
||||
}
|
||||
String reason = "Sybil analysis " + day + " with " + fmt.format(p) + " threat points";
|
||||
_context.banlist().banlistRouter(h, reason, null, null, blockUntil);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pairs out parameter, sorted
|
||||
* @return average distance
|
||||
@@ -350,8 +423,14 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
double total = 0;
|
||||
for (int i = 0; i < sz; i++) {
|
||||
RouterInfo info1 = ris.get(i);
|
||||
// don't do distance calculation for non-floodfills
|
||||
if (!info1.getCapabilities().contains("f"))
|
||||
continue;
|
||||
for (int j = i + 1; j < sz; j++) {
|
||||
RouterInfo info2 = ris.get(j);
|
||||
// don't do distance calculation for non-floodfills
|
||||
if (!info2.getCapabilities().contains("f"))
|
||||
continue;
|
||||
BigInteger dist = HashDistance.getDistance(info1.getHash(), info2.getHash());
|
||||
if (pairs.isEmpty()) {
|
||||
pairs.add(new Pair(info1, info2, dist));
|
||||
@@ -634,12 +713,28 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
private static final long DAY = 24*60*60*1000L;
|
||||
|
||||
public void addProfilePoints(List<RouterInfo> ris, Map<Hash, Points> points) {
|
||||
Map<Hash, Banlist.Entry> banEntries = _context.banlist().getEntries();
|
||||
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");
|
||||
if (_context.banlist().isBanlisted(h)) {
|
||||
StringBuilder buf = new StringBuilder("Banlisted");
|
||||
Banlist.Entry entry = banEntries.get(h);
|
||||
if (entry != null) {
|
||||
if (entry.cause != null) {
|
||||
buf.append(": ");
|
||||
if (entry.causeCode != null)
|
||||
buf.append(_t(entry.cause, entry.causeCode));
|
||||
else
|
||||
buf.append(_t(entry.cause));
|
||||
}
|
||||
}
|
||||
addPoints(points, h, POINTS_BANLIST, buf.toString());
|
||||
}
|
||||
// don't do profile calcluations for non-floodfills
|
||||
if (!info.getCapabilities().contains("f"))
|
||||
continue;
|
||||
PeerProfile prof = _context.profileOrganizer().getProfileNonblocking(h);
|
||||
if (prof != null) {
|
||||
long heard = prof.getFirstHeardAbout();
|
||||
@@ -689,6 +784,8 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
String caps = info.getCapabilities();
|
||||
if (!caps.contains("R"))
|
||||
addPoints(points, h, POINTS_UNREACHABLE, "Unreachable: " + DataHelper.escapeHTML(caps));
|
||||
if (!caps.contains("f"))
|
||||
addPoints(points, h, POINTS_NONFF, "Non-floodfill");
|
||||
String hisFullVer = info.getVersion();
|
||||
if (!hisFullVer.startsWith("0.9.")) {
|
||||
addPoints(points, h, POINTS_BAD_VERSION, "Strange version " + DataHelper.escapeHTML(hisFullVer));
|
||||
@@ -720,6 +817,9 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
int count = Math.min(MAX, ris.size());
|
||||
for (int i = 0; i < count; i++) {
|
||||
RouterInfo ri = ris.get(i);
|
||||
// don't do distance calculation for non-floodfills
|
||||
if (!ri.getCapabilities().contains("f"))
|
||||
continue;
|
||||
BigInteger bidist = HashDistance.getDistance(us, ri.getHash());
|
||||
double dist = biLog2(bidist);
|
||||
double point = MIN_CLOSE - dist;
|
||||
@@ -739,6 +839,10 @@ public class Analysis extends JobImpl implements RouterApp {
|
||||
return Util.biLog2(a);
|
||||
}
|
||||
|
||||
private String _t(String s) {
|
||||
return Messages.getString(s, _context);
|
||||
}
|
||||
|
||||
/**
|
||||
* translate a string with a parameter
|
||||
* This is a lot more expensive than _t(s), so use sparingly.
|
||||
|
||||
@@ -64,6 +64,8 @@ public class PersistSybil {
|
||||
Writer out = null;
|
||||
try {
|
||||
out = new OutputStreamWriter(new GZIPOutputStream(new SecureFileOutputStream(file)));
|
||||
out.write("# Format (one per line)\n");
|
||||
out.write("# Base64 router hash:total points%points:reason%points:reason ...\n");
|
||||
for (Map.Entry<Hash, Points> entry : entries.entrySet()) {
|
||||
Hash h = entry.getKey();
|
||||
Points p = entry.getValue();
|
||||
@@ -114,6 +116,8 @@ public class PersistSybil {
|
||||
in = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(file))));
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
if (line.startsWith("#"))
|
||||
continue;
|
||||
int colon = line.indexOf(':');
|
||||
if (colon != 44)
|
||||
continue;
|
||||
@@ -169,6 +173,39 @@ public class PersistSybil {
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all files older than configured threshold
|
||||
* Inline for now, thread later if necessary
|
||||
*
|
||||
* @since 0.9.41
|
||||
*/
|
||||
public synchronized void removeOld() {
|
||||
long age = _context.getProperty(Analysis.PROP_REMOVETIME, 0L);
|
||||
if (age < 60*1000)
|
||||
return;
|
||||
long cutoff = _context.clock().now() - age;
|
||||
File dir = new File(_context.getConfigDir(), DIR);
|
||||
File[] files = dir.listFiles(new FileSuffixFilter(PFX, SFX));
|
||||
if (files == null)
|
||||
return;
|
||||
int deleted = 0;
|
||||
for (File file : files) {
|
||||
try {
|
||||
String name = file.getName();
|
||||
long d = Long.parseLong(name.substring(PFX.length(), name.length() - SFX.length()));
|
||||
if (d < cutoff) {
|
||||
if (file.delete())
|
||||
deleted++;
|
||||
else if (_log.shouldWarn())
|
||||
_log.warn("Failed to delete: " + file);
|
||||
}
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
if (deleted > 0 && _log.shouldWarn())
|
||||
_log.warn("Deleted " + deleted + " old analysis files");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete the file for a particular date
|
||||
*
|
||||
|
||||
@@ -81,7 +81,7 @@ public class Points implements Comparable<Points> {
|
||||
public void toString(StringBuilder buf) {
|
||||
buf.append(points);
|
||||
for (String r : reasons) {
|
||||
buf.append('%').append(r);
|
||||
buf.append('%').append(r.replace("%", "%"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -264,8 +264,8 @@ class NewsFetcher extends UpdateRunner {
|
||||
}
|
||||
String minJava = args.get(MIN_JAVA_VERSION_KEY);
|
||||
if (minJava != null) {
|
||||
String ourJava = System.getProperty("java.version");
|
||||
if (VersionComparator.comp(ourJava, minJava) < 0) {
|
||||
if (!SystemVersion.isJava(minJava)) {
|
||||
String ourJava = System.getProperty("java.version");
|
||||
String msg = _mgr._t("Requires Java version {0} but installed Java version is {1}", minJava, ourJava);
|
||||
_log.logAlways(Log.WARN, "Cannot update to version " + ver + ": " + msg);
|
||||
_mgr.notifyVersionConstraint(this, _currentURI, ROUTER_SIGNED, "", ver, msg);
|
||||
|
||||
@@ -409,8 +409,7 @@ class PluginUpdateRunner extends UpdateRunner {
|
||||
}
|
||||
|
||||
minVersion = PluginStarter.stripHTML(props, "min-java-version");
|
||||
if (minVersion != null &&
|
||||
VersionComparator.comp(System.getProperty("java.version"), minVersion) < 0) {
|
||||
if (minVersion != null && !SystemVersion.isJava(minVersion)) {
|
||||
to.delete();
|
||||
statusDone("<b>" + _t("This plugin requires Java version {0} or higher", minVersion) + "</b>");
|
||||
return;
|
||||
|
||||
@@ -17,6 +17,8 @@ import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
||||
import net.i2p.CoreVersion;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientApp;
|
||||
@@ -358,8 +360,7 @@ public class PluginStarter implements Runnable {
|
||||
}
|
||||
|
||||
minVersion = stripHTML(props, "min-java-version");
|
||||
if (minVersion != null &&
|
||||
VersionComparator.comp(System.getProperty("java.version"), minVersion) < 0) {
|
||||
if (minVersion != null && !SystemVersion.isJava(minVersion)) {
|
||||
String foo = "Plugin " + appName + " requires Java version " + minVersion + " or higher";
|
||||
log.error(foo);
|
||||
disablePlugin(appName);
|
||||
@@ -542,12 +543,24 @@ public class PluginStarter implements Runnable {
|
||||
* @throws Exception just about anything, caller would be wise to catch Throwable
|
||||
*/
|
||||
public static boolean stopPlugin(RouterContext ctx, String appName) throws Exception {
|
||||
Server s = RouterConsoleRunner.getConsoleServer(ctx);
|
||||
return stopPlugin(ctx, s, appName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true on success
|
||||
* @throws Exception just about anything, caller would be wise to catch Throwable
|
||||
* @since 0.9.41
|
||||
*/
|
||||
protected static boolean stopPlugin(RouterContext ctx, Server s, String appName) throws Exception {
|
||||
Log log = ctx.logManager().getLog(PluginStarter.class);
|
||||
File pluginDir = new File(ctx.getConfigDir(), PLUGIN_DIR + '/' + appName);
|
||||
if ((!pluginDir.exists()) || (!pluginDir.isDirectory())) {
|
||||
log.error("Cannot stop nonexistent plugin: " + appName);
|
||||
return false;
|
||||
}
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Stopping plugin: " + appName);
|
||||
|
||||
// stop things in clients.config
|
||||
File clientConfig = new File(pluginDir, "clients.config");
|
||||
@@ -576,13 +589,16 @@ public class PluginStarter implements Runnable {
|
||||
}
|
||||
}
|
||||
*/
|
||||
if(pluginWars.containsKey(appName)) {
|
||||
Iterator <String> wars = pluginWars.get(appName).iterator();
|
||||
while (wars.hasNext()) {
|
||||
String warName = wars.next();
|
||||
WebAppStarter.stopWebApp(ctx, warName);
|
||||
if (s != null) {
|
||||
Collection<String> wars = pluginWars.get(appName);
|
||||
if (wars != null) {
|
||||
for (String warName : wars) {
|
||||
if (log.shouldInfo())
|
||||
log.info("Stopping webapp " + warName + " in plugin " + appName);
|
||||
WebAppStarter.stopWebApp(ctx, s, warName);
|
||||
}
|
||||
wars.clear();
|
||||
}
|
||||
pluginWars.get(appName).clear();
|
||||
}
|
||||
//}
|
||||
|
||||
@@ -594,8 +610,6 @@ public class PluginStarter implements Runnable {
|
||||
if (name != null && name.length() > 0)
|
||||
NavHelper.unregisterApp(name);
|
||||
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Stopping plugin: " + appName);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -948,6 +962,14 @@ public class PluginStarter implements Runnable {
|
||||
}
|
||||
|
||||
public static boolean isPluginRunning(String pluginName, RouterContext ctx) {
|
||||
Server s = RouterConsoleRunner.getConsoleServer(ctx);
|
||||
return isPluginRunning(pluginName, ctx, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.41
|
||||
*/
|
||||
protected static boolean isPluginRunning(String pluginName, RouterContext ctx, Server s) {
|
||||
Log log = ctx.logManager().getLog(PluginStarter.class);
|
||||
|
||||
boolean isJobRunning = false;
|
||||
@@ -956,13 +978,16 @@ public class PluginStarter implements Runnable {
|
||||
// TODO have a pending indication too
|
||||
isJobRunning = true;
|
||||
}
|
||||
|
||||
boolean isWarRunning = false;
|
||||
if(pluginWars.containsKey(pluginName)) {
|
||||
Iterator <String> it = pluginWars.get(pluginName).iterator();
|
||||
while(it.hasNext() && !isWarRunning) {
|
||||
String warName = it.next();
|
||||
if(WebAppStarter.isWebAppRunning(ctx, warName)) {
|
||||
isWarRunning = true;
|
||||
if (s != null) {
|
||||
Collection<String> wars = pluginWars.get(pluginName);
|
||||
if (wars != null) {
|
||||
for (String warName : wars) {
|
||||
if (WebAppStarter.isWebAppRunning(s, warName)) {
|
||||
isWarRunning = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ package net.i2p.router.web;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@@ -11,15 +14,18 @@ import net.i2p.util.Log;
|
||||
* @since 0.7.13
|
||||
* @author zzz
|
||||
*/
|
||||
public class PluginStopper extends PluginStarter {
|
||||
class PluginStopper extends PluginStarter {
|
||||
|
||||
public PluginStopper(RouterContext ctx) {
|
||||
private final Server _server;
|
||||
|
||||
public PluginStopper(RouterContext ctx, Server server) {
|
||||
super(ctx);
|
||||
_server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
stopPlugins(_context);
|
||||
stopPlugins();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,18 +33,23 @@ public class PluginStopper extends PluginStarter {
|
||||
*
|
||||
* this shouldn't throw anything
|
||||
*/
|
||||
private static void stopPlugins(RouterContext ctx) {
|
||||
Log log = ctx.logManager().getLog(PluginStopper.class);
|
||||
private void stopPlugins() {
|
||||
Log log = _context.logManager().getLog(PluginStopper.class);
|
||||
List<String> pl = getPlugins();
|
||||
Collections.reverse(pl); // reverse the order
|
||||
for (String app : pl) {
|
||||
if (isPluginRunning(app, ctx)) {
|
||||
if (isPluginRunning(app, _context, _server)) {
|
||||
try {
|
||||
stopPlugin(ctx, app);
|
||||
if (log.shouldInfo())
|
||||
log.info("Stopping plugin: " + app);
|
||||
stopPlugin(_context, _server, app);
|
||||
} catch (Throwable e) {
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Failed to stop plugin: " + app, e);
|
||||
}
|
||||
} else {
|
||||
if (log.shouldInfo())
|
||||
log.info("Plugin not running: " + app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,9 +239,10 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
public synchronized void shutdown(String[] args) {
|
||||
if (_state == STOPPED)
|
||||
return;
|
||||
// this unregisters us with the ClientAppManager
|
||||
changeState(STOPPING);
|
||||
if (PluginStarter.pluginsEnabled(_context))
|
||||
(new I2PAppThread(new PluginStopper(_context), "PluginStopper")).start();
|
||||
(new I2PAppThread(new PluginStopper(_context, _server), "PluginStopper")).start();
|
||||
stopAllWebApps();
|
||||
try {
|
||||
_server.stop();
|
||||
@@ -293,7 +294,10 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
}
|
||||
|
||||
/**
|
||||
* To get to Jetty
|
||||
* To get to Jetty.
|
||||
* Warning, this will NOT work during shutdown, because
|
||||
* changeState(STOPPING) will unregister us first.
|
||||
*
|
||||
* @return may be null or stopped perhaps
|
||||
* @since 0.9.38
|
||||
*/
|
||||
@@ -340,13 +344,13 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
|
||||
/** @since 0.9.17 */
|
||||
private void checkJavaVersion() {
|
||||
boolean noJava7 = !SystemVersion.isJava7();
|
||||
boolean noJava8 = !SystemVersion.isJava8();
|
||||
boolean noPack200 = (PluginStarter.pluginsEnabled(_context) || !NewsHelper.isUpdateDisabled(_context)) &&
|
||||
!FileUtil.isPack200Supported();
|
||||
boolean openARM = SystemVersion.isARM() && SystemVersion.isOpenJDK() && !SystemVersion.isJava9();
|
||||
boolean isZero = SystemVersion.isZeroVM();
|
||||
boolean isJava11 = false; // SystemVersion.isJava11();
|
||||
if (noJava7 || noPack200 || openARM || isZero || isJava11) {
|
||||
if (noJava8 || noPack200 || openARM || isZero || isJava11) {
|
||||
String s = "Java version: " + System.getProperty("java.version") +
|
||||
" OS: " + System.getProperty("os.name") + ' ' +
|
||||
System.getProperty("os.arch") + ' ' +
|
||||
@@ -354,8 +358,8 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
net.i2p.util.Log log = _context.logManager().getLog(RouterConsoleRunner.class);
|
||||
log.logAlways(net.i2p.util.Log.WARN, s);
|
||||
System.out.println("Warning: " + s);
|
||||
if (noJava7) {
|
||||
s = "Java 7 is now required, please upgrade";
|
||||
if (noJava8) {
|
||||
s = "Java 8 or higher will be required in a future release, please upgrade Java";
|
||||
log.logAlways(net.i2p.util.Log.WARN, s);
|
||||
System.out.println("Warning: " + s);
|
||||
}
|
||||
@@ -1137,6 +1141,9 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
* @since 0.9.30
|
||||
*/
|
||||
private void stopAllWebApps() {
|
||||
net.i2p.util.Log log = _context.logManager().getLog(RouterConsoleRunner.class);
|
||||
if (log.shouldWarn())
|
||||
log.warn("Stop all webapps");
|
||||
Properties props = webAppProperties(_context);
|
||||
Set<String> keys = props.stringPropertyNames();
|
||||
for (String name : keys) {
|
||||
@@ -1144,10 +1151,15 @@ public class RouterConsoleRunner implements RouterApp {
|
||||
String app = name.substring(PREFIX.length(), name.lastIndexOf(ENABLED));
|
||||
if (ROUTERCONSOLE.equals(app))
|
||||
continue;
|
||||
if (WebAppStarter.isWebAppRunning(_context, app)) {
|
||||
if (_context.portMapper().isRegistered(app)) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("Stopping " + app);
|
||||
try {
|
||||
WebAppStarter.stopWebApp(_context, app);
|
||||
WebAppStarter.stopWebApp(_context, _server, app);
|
||||
} catch (Throwable t) { t.printStackTrace(); }
|
||||
} else {
|
||||
if (log.shouldWarn())
|
||||
log.info("Not Stoppping, isn't running " + app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +151,10 @@ public class WebAppStarter {
|
||||
/**
|
||||
* Stop it and remove the context.
|
||||
* Throws just about anything, caller would be wise to catch Throwable
|
||||
*
|
||||
* Warning, this will NOT work during shutdown, because
|
||||
* the console is already unregistered.
|
||||
*
|
||||
* @since public since 0.9.33, was package private
|
||||
*/
|
||||
public static void stopWebApp(RouterContext ctx, String appName) {
|
||||
@@ -171,10 +175,36 @@ public class WebAppStarter {
|
||||
} catch (IllegalStateException ise) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop it and remove the context.
|
||||
* Throws just about anything, caller would be wise to catch Throwable
|
||||
* @since 0.9.41
|
||||
*/
|
||||
static void stopWebApp(RouterContext ctx, Server s, String appName) {
|
||||
ContextHandlerCollection server = getConsoleServer(s);
|
||||
if (server == null)
|
||||
return;
|
||||
ContextHandler wac = getWebApp(server, appName);
|
||||
if (wac == null)
|
||||
return;
|
||||
ctx.portMapper().unregister(appName);
|
||||
try {
|
||||
// not graceful is default in Jetty 6?
|
||||
wac.stop();
|
||||
} catch (Exception ie) {}
|
||||
try {
|
||||
server.removeHandler(wac);
|
||||
server.mapContexts();
|
||||
} catch (IllegalStateException ise) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* As of 0.9.34, the appName will be registered with the PortMapper,
|
||||
* and PortMapper.isRegistered() will be more efficient than this.
|
||||
*
|
||||
* Warning, this will NOT work during shutdown, because
|
||||
* the console is already unregistered.
|
||||
*
|
||||
* @since public since 0.9.33; was package private
|
||||
*/
|
||||
public static boolean isWebAppRunning(I2PAppContext ctx, String appName) {
|
||||
@@ -184,11 +214,43 @@ public class WebAppStarter {
|
||||
return wac.isStarted();
|
||||
}
|
||||
|
||||
/** @since Jetty 6 */
|
||||
/**
|
||||
* @since 0.9.41
|
||||
*/
|
||||
static boolean isWebAppRunning(Server s, String appName) {
|
||||
ContextHandler wac = getWebApp(s, appName);
|
||||
if (wac == null)
|
||||
return false;
|
||||
return wac.isStarted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning, this will NOT work during shutdown, because
|
||||
* the console is already unregistered.
|
||||
*
|
||||
* @since Jetty 6
|
||||
*/
|
||||
static ContextHandler getWebApp(I2PAppContext ctx, String appName) {
|
||||
ContextHandlerCollection server = getConsoleServer(ctx);
|
||||
if (server == null)
|
||||
return null;
|
||||
return getWebApp(server, appName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.41
|
||||
*/
|
||||
static ContextHandler getWebApp(Server s, String appName) {
|
||||
ContextHandlerCollection server = getConsoleServer(s);
|
||||
if (server == null)
|
||||
return null;
|
||||
return getWebApp(server, appName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.41
|
||||
*/
|
||||
private static ContextHandler getWebApp(ContextHandlerCollection server, String appName) {
|
||||
Handler handlers[] = server.getHandlers();
|
||||
if (handlers == null)
|
||||
return null;
|
||||
@@ -205,12 +267,23 @@ public class WebAppStarter {
|
||||
|
||||
/**
|
||||
* See comments in ConfigClientsHandler
|
||||
*
|
||||
* Warning, this will NOT work during shutdown, because
|
||||
* the console is already unregistered.
|
||||
*
|
||||
* @since public since 0.9.33, was package private
|
||||
*/
|
||||
public static ContextHandlerCollection getConsoleServer(I2PAppContext ctx) {
|
||||
Server s = RouterConsoleRunner.getConsoleServer(ctx);
|
||||
if (s == null)
|
||||
return null;
|
||||
return getConsoleServer(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.41
|
||||
*/
|
||||
private static ContextHandlerCollection getConsoleServer(Server s) {
|
||||
Handler h = s.getChildHandlerByClass(ContextHandlerCollection.class);
|
||||
if (h == null)
|
||||
return null;
|
||||
|
||||
@@ -140,13 +140,16 @@ public class ConfigClientsHelper extends HelperBase {
|
||||
boolean isConsole = ca.className.equals("net.i2p.router.web.RouterConsoleRunner");
|
||||
boolean showStart;
|
||||
boolean showStop;
|
||||
boolean showEdit;
|
||||
if (isConsole) {
|
||||
showStart = false;
|
||||
showStop = false;
|
||||
showEdit = true;
|
||||
} else {
|
||||
ClientApp clientApp = _context.routerAppManager().getClientApp(ca.className, LoadClientAppsJob.parseArgs(ca.args));
|
||||
showStart = clientApp == null;
|
||||
showStop = clientApp != null && clientApp.getState() == ClientAppState.RUNNING;
|
||||
showEdit = !showStop && (clientApp == null || clientApp.getState() != ClientAppState.STARTING);
|
||||
}
|
||||
String scur = Integer.toString(cur);
|
||||
renderForm(buf, scur, ca.clientName,
|
||||
@@ -161,8 +164,9 @@ public class ConfigClientsHelper extends HelperBase {
|
||||
// edit
|
||||
allowEdit && scur.equals(_edit),
|
||||
// show edit button, show update button
|
||||
// Don't allow edit if it's running, or else we would lose the "handle" to the ClientApp to stop it.
|
||||
allowEdit && !showStop, false,
|
||||
// Don't allow edit if it's running or starting, or else we would lose the "handle" to the ClientApp to stop it.
|
||||
allowEdit && showEdit,
|
||||
false,
|
||||
// show stop button
|
||||
showStop,
|
||||
// show delete button, show start button
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
package net.i2p.router.web.helpers;
|
||||
|
||||
import net.i2p.crypto.Blinding;
|
||||
import net.i2p.crypto.EncType;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.BlindData;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.router.web.FormHandler;
|
||||
import net.i2p.util.ConvertToHash;
|
||||
|
||||
@@ -12,46 +20,183 @@ import net.i2p.util.ConvertToHash;
|
||||
public class ConfigKeyringHandler extends FormHandler {
|
||||
private String _peer;
|
||||
private String _key;
|
||||
private String _secret;
|
||||
private String[] _revokes;
|
||||
private int _mode;
|
||||
|
||||
@Override
|
||||
protected void processForm() {
|
||||
boolean adding = _action.equals(_t("Add key"));
|
||||
if (adding || _action.equals(_t("Delete key"))) {
|
||||
if (_peer == null)
|
||||
if (_action.equals(_t("Add key"))) {
|
||||
if (_peer == null) {
|
||||
addFormError(_t("You must enter a destination"));
|
||||
if (_key == null && adding)
|
||||
addFormError(_t("You must enter a key"));
|
||||
if (_peer == null || (_key == null && adding))
|
||||
return;
|
||||
Hash h = ConvertToHash.getHash(_peer);
|
||||
if (adding) {
|
||||
SessionKey sk = new SessionKey();
|
||||
try {
|
||||
sk.fromBase64(_key);
|
||||
} catch (DataFormatException dfe) {}
|
||||
if (h == null || h.getData() == null) {
|
||||
addFormError(_t("Invalid destination"));
|
||||
} else if (_context.clientManager().isLocal(h)) {
|
||||
// don't bother translating
|
||||
addFormError("Cannot add key for local destination. Enable encryption in the Hidden Services Manager.");
|
||||
} else if (sk.getData() == null) {
|
||||
addFormError(_t("Invalid key"));
|
||||
} else {
|
||||
_context.keyRing().put(h, sk);
|
||||
addFormNotice(_t("Key for {0} added to keyring", h.toBase32()));
|
||||
}
|
||||
Hash h = null;
|
||||
if (!_peer.endsWith(".b32.i2p") || _peer.length() <= 60) {
|
||||
// don't wait for several seconds for b33 lookup
|
||||
h = ConvertToHash.getHash(_peer);
|
||||
}
|
||||
|
||||
byte[] b = null;
|
||||
if (_mode == 1 || _mode == 4 || _mode == 5) {
|
||||
if (_key == null) {
|
||||
addFormError(_t("You must enter a key"));
|
||||
return;
|
||||
}
|
||||
b = Base64.decode(_key);
|
||||
if (b == null || b.length != 32) {
|
||||
addFormError(_t("Invalid key"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else { // Delete
|
||||
if (h != null && h.getData() != null) {
|
||||
if (_context.clientManager().isLocal(h)) {
|
||||
if (_mode == 1) {
|
||||
// LS1
|
||||
if (h == null || h.getData() == null) {
|
||||
addFormError(_t("Invalid destination"));
|
||||
} else if (_context.clientManager().isLocal(h)) {
|
||||
// don't bother translating
|
||||
addFormError("Cannot remove key for local destination. Disable encryption in the Hidden Services Manager.");
|
||||
} else if (_context.keyRing().remove(h) != null) {
|
||||
addFormNotice(_t("Key for {0} removed from keyring", h.toBase32()));
|
||||
addFormError("Cannot add key for local destination. Enable encryption in the Hidden Services Manager.");
|
||||
} else {
|
||||
addFormNotice(_t("Key for {0} not found in keyring", h.toBase32()));
|
||||
SessionKey sk = new SessionKey(b);
|
||||
_context.keyRing().put(h, sk);
|
||||
addFormNotice(_t("Key for {0} added to keyring", h.toBase32()));
|
||||
}
|
||||
} else {
|
||||
addFormError(_t("Invalid destination"));
|
||||
if ((_mode == 3 || _mode == 5 || _mode == 7) && _secret == null) {
|
||||
addFormError(_t("Lookup password required"));
|
||||
return;
|
||||
}
|
||||
// b33 if supplied as hostname
|
||||
BlindData bdin = null;
|
||||
try {
|
||||
bdin = Blinding.decode(_context, _peer);
|
||||
} catch (IllegalArgumentException iae) {}
|
||||
|
||||
// we need the dest or the spk, not just the desthash
|
||||
SigningPublicKey spk = null;
|
||||
Destination d = null;
|
||||
// don't cause LS fetch
|
||||
if (!_peer.endsWith(".b32.i2p"))
|
||||
d = _context.namingService().lookup(_peer);
|
||||
if (d != null) {
|
||||
spk = d.getSigningPublicKey();
|
||||
} else if (bdin != null) {
|
||||
spk = bdin.getUnblindedPubKey();
|
||||
}
|
||||
if (spk == null) {
|
||||
addFormError(_t("Requires hostname, destination, or blinded base32"));
|
||||
return;
|
||||
}
|
||||
// from BlindCache
|
||||
BlindData bdold = _context.netDb().getBlindData(spk);
|
||||
if (bdold != null && d == null)
|
||||
d = bdold.getDestination();
|
||||
if (d != null && _context.clientManager().isLocal(d)) {
|
||||
// don't bother translating
|
||||
addFormError("Cannot add key for local destination. Enable encryption in the Hidden Services Manager.");
|
||||
return;
|
||||
}
|
||||
|
||||
SigType blindType;
|
||||
if (bdin != null) {
|
||||
blindType = bdin.getBlindedSigType();
|
||||
} else if (bdold != null) {
|
||||
blindType = bdold.getBlindedSigType();
|
||||
} else {
|
||||
blindType = Blinding.getDefaultBlindedType(spk.getType());
|
||||
}
|
||||
|
||||
int atype;
|
||||
PrivateKey pk;
|
||||
if (_mode == 4 || _mode == 5) {
|
||||
atype = BlindData.AUTH_PSK;
|
||||
// use supplied pk
|
||||
pk = new PrivateKey(EncType.ECIES_X25519, b);
|
||||
} else if (_mode == 6 || _mode == 7) {
|
||||
atype = BlindData.AUTH_DH;
|
||||
// create new pk
|
||||
b = new byte[32];
|
||||
_context.random().nextBytes(b);
|
||||
pk = new PrivateKey(EncType.ECIES_X25519, b);
|
||||
} else {
|
||||
// modes 2 and 3
|
||||
atype = BlindData.AUTH_NONE;
|
||||
pk = null;
|
||||
}
|
||||
if (_mode == 2 || _mode == 4 || _mode == 6)
|
||||
_secret = null;
|
||||
if (bdin != null) {
|
||||
// more checks based on supplied b33
|
||||
if (bdin.getSecretRequired() && _secret == null) {
|
||||
addFormError(_t("Destination requires lookup password"));
|
||||
return;
|
||||
}
|
||||
if (!bdin.getSecretRequired() && _secret != null) {
|
||||
addFormError(_t("Destination does not require lookup password"));
|
||||
return;
|
||||
}
|
||||
if (bdin.getAuthRequired() && pk == null) {
|
||||
addFormError(_t("Destination requires encryption key"));
|
||||
return;
|
||||
}
|
||||
if (!bdin.getAuthRequired() && pk != null) {
|
||||
addFormError(_t("Destination does not require encryption key"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// to BlindCache
|
||||
BlindData bdout;
|
||||
if (d != null) {
|
||||
bdout = new BlindData(_context, d, blindType, _secret, atype, pk);
|
||||
} else {
|
||||
bdout = new BlindData(_context, spk, blindType, _secret, atype, pk);
|
||||
}
|
||||
if (bdold != null) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("already cached: " + bdold);
|
||||
}
|
||||
try {
|
||||
_context.netDb().setBlindData(bdout);
|
||||
addFormNotice(_t("Key for {0} added to keyring", bdout.toBase32()));
|
||||
if (_mode == 6 || _mode == 7) {
|
||||
addFormNotice(_t("Send key to server opererator.") + ' ' + pk.toPublic().toBase64());
|
||||
}
|
||||
} catch (IllegalArgumentException iae) {
|
||||
addFormError(_t("Invalid destination") + ": " + iae.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
} else if (_action.equals(_t("Delete key")) && _revokes != null) {
|
||||
// these should all be b32s or b33s
|
||||
for (String p : _revokes) {
|
||||
boolean removed = false;
|
||||
if (p.length() == 60) {
|
||||
// don't wait for several seconds for b33 lookup
|
||||
Hash h = ConvertToHash.getHash(p);
|
||||
if (h != null) {
|
||||
if (_context.clientManager().isLocal(h)) {
|
||||
// don't bother translating
|
||||
addFormError("Cannot remove key for local destination. Disable encryption in the Hidden Services Manager.");
|
||||
} else if (_context.keyRing().remove(h) != null) {
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
} else if (p.length() > 60) {
|
||||
try {
|
||||
BlindData bd = Blinding.decode(_context, p);
|
||||
if (bd != null) {
|
||||
SigningPublicKey spk = bd.getUnblindedPubKey();
|
||||
removed = _context.netDb().removeBlindData(spk);
|
||||
}
|
||||
} catch (IllegalArgumentException iae) {}
|
||||
} else {
|
||||
addFormError(_t("Invalid destination") + ": " + p);
|
||||
}
|
||||
if (removed) {
|
||||
addFormNotice(_t("Key for {0} removed from keyring", p));
|
||||
} else {
|
||||
addFormError(_t("Key for {0} not found in keyring", p));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -61,4 +206,23 @@ public class ConfigKeyringHandler extends FormHandler {
|
||||
|
||||
public void setPeer(String peer) { if (peer != null) _peer = peer.trim(); }
|
||||
public void setKey(String key) { if (key != null) _key = key.trim(); }
|
||||
|
||||
/** @since 0.9.41 */
|
||||
public void setNofilter_blindedPassword(String pw) {
|
||||
if (pw != null) {
|
||||
pw = pw.trim();
|
||||
if (pw.length() > 0)
|
||||
_secret = pw;
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 0.9.41 */
|
||||
public void setEncryptMode(String m) {
|
||||
try {
|
||||
_mode = Integer.parseInt(m);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
|
||||
/** @since 0.9.41 */
|
||||
public void setRevokeClient(String[] revokes) { _revokes = revokes; }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package net.i2p.router.web.helpers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.data.BlindData;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
import net.i2p.router.web.HelperBase;
|
||||
@@ -39,18 +45,30 @@ public class ConfigKeyringHelper extends HelperBase {
|
||||
|
||||
/**
|
||||
* @since 0.9.33 moved from PersistentKeyRing
|
||||
* @param local true for local (Enc. LS1 only), false for remote (all types)
|
||||
*/
|
||||
private void render(StringBuilder buf, boolean local) {
|
||||
buf.append("\n<table class=\"configtable\"><tr><th align=\"left\">").append(_t("Destination"))
|
||||
.append("<th align=\"left\">").append(_t("Name"))
|
||||
.append("<th align=\"left\">").append(_t("Encryption Key"))
|
||||
.append("</tr>");
|
||||
buf.append("\n<table class=\"configtable\"><tr>");
|
||||
if (!local)
|
||||
buf.append("<th align=\"left\">").append(_t("Delete"));
|
||||
buf.append("<th align=\"left\">").append(_t("Destination"))
|
||||
.append("<th align=\"left\">").append(_t("Name"));
|
||||
if (!local)
|
||||
buf.append("<th align=\"left\">").append(_t("Type"));
|
||||
buf.append("<th align=\"left\">").append(_t("Encryption Key"));
|
||||
if (!local)
|
||||
buf.append("<th align=\"left\">").append(_t("Lookup Password"));
|
||||
buf.append("</tr>");
|
||||
// Enc. LS1
|
||||
for (Map.Entry<Hash, SessionKey> e : _context.keyRing().entrySet()) {
|
||||
Hash h = e.getKey();
|
||||
if (local != _context.clientManager().isLocal(h))
|
||||
continue;
|
||||
buf.append("\n<tr><td>");
|
||||
buf.append(h.toBase32());
|
||||
String b32 = h.toBase32();
|
||||
if (!local)
|
||||
buf.append("<input value=\"").append(b32).append("\" type=\"checkbox\" name=\"revokeClient\" class=\"tickbox\"/></td><td>");
|
||||
buf.append(b32);
|
||||
buf.append("</td><td>");
|
||||
Destination dest = _context.netDb().lookupDestinationLocally(h);
|
||||
if (dest != null && local) {
|
||||
@@ -63,10 +81,75 @@ public class ConfigKeyringHelper extends HelperBase {
|
||||
buf.append(host);
|
||||
}
|
||||
buf.append("</td><td>");
|
||||
if (!local)
|
||||
buf.append(_t("Encrypted")).append(" (AES)</td><td>");
|
||||
SessionKey sk = e.getValue();
|
||||
buf.append(sk.toBase64());
|
||||
if (!local)
|
||||
buf.append("</td><td>");
|
||||
buf.append("</td>\n");
|
||||
}
|
||||
// LS2
|
||||
if (!local) {
|
||||
List<BlindData> bdata = _context.netDb().getBlindData();
|
||||
if (bdata.size() > 1)
|
||||
Collections.sort(bdata, new BDComparator());
|
||||
for (BlindData bd : bdata) {
|
||||
buf.append("\n<tr><td>");
|
||||
String b32 = bd.toBase32();
|
||||
if (!local)
|
||||
buf.append("<input value=\"").append(b32).append("\" type=\"checkbox\" name=\"revokeClient\" class=\"tickbox\"/></td><td>");
|
||||
buf.append(b32);
|
||||
buf.append("</td><td>");
|
||||
Hash h = bd.getDestHash();
|
||||
if (h != null) {
|
||||
String host = _context.namingService().reverseLookup(h);
|
||||
if (host != null)
|
||||
buf.append(host);
|
||||
}
|
||||
buf.append("</td><td>");
|
||||
int type = bd.getAuthType();
|
||||
PrivateKey pk = bd.getAuthPrivKey();
|
||||
String secret = bd.getSecret();
|
||||
String s;
|
||||
if (type == BlindData.AUTH_DH) {
|
||||
if (secret != null)
|
||||
s = _t("Encrypted with lookup password") + " (DH)";
|
||||
else
|
||||
s = _t("Encrypted") + " (DH)";
|
||||
} else if (type == BlindData.AUTH_PSK) {
|
||||
if (secret != null)
|
||||
s = _t("Encrypted with lookup password") + " (PSK)";
|
||||
else
|
||||
s = _t("Encrypted") + " (PSK)";
|
||||
} else {
|
||||
if (secret != null)
|
||||
s = _t("Blinded with lookup password");
|
||||
else
|
||||
s = _t("Blinded");
|
||||
}
|
||||
buf.append(s);
|
||||
buf.append("</td><td>");
|
||||
if (pk != null) {
|
||||
// display pubkey for DH for sharing with server
|
||||
if (type == BlindData.AUTH_DH)
|
||||
buf.append(pk.toPublic().toBase64());
|
||||
else
|
||||
buf.append(pk.toBase64());
|
||||
}
|
||||
buf.append("</td><td>");
|
||||
if (secret != null)
|
||||
buf.append(secret);
|
||||
buf.append("</td><tr>");
|
||||
}
|
||||
}
|
||||
buf.append("</table>\n");
|
||||
}
|
||||
|
||||
/** @since 0.9.41 */
|
||||
private static class BDComparator implements Comparator<BlindData>, Serializable {
|
||||
public int compare(BlindData l, BlindData r) {
|
||||
return l.toBase32().compareTo(r.toBase32());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class ConfigNavHelper extends HelperBase {
|
||||
"reseed", "advanced", "family" };
|
||||
|
||||
private static final String titles[] =
|
||||
{_x("Bandwidth"), _x("Network"), _x("UI"), _x("Summary Bar"), _x("Home Page"),
|
||||
{_x("Bandwidth"), _x("Network"), _x("UI"), _x("Sidebar"), _x("Home Page"),
|
||||
_x("Service"), _x("Update"), _x("Tunnels"),
|
||||
_x("Clients"), _x("Peers"), _x("Keyring"), _x("Logging"), _x("Stats"),
|
||||
_x("I2CP"), _x("Plugins"), _x("Web Apps"),
|
||||
|
||||
@@ -65,9 +65,9 @@ public class ConfigReseedHandler extends FormHandler {
|
||||
}
|
||||
if (!addCheckerStatus(checker)) {
|
||||
if (checker.inProgress()) {
|
||||
addFormNotice(_t("Reseed in progress, check summary bar for status"));
|
||||
addFormNotice(_t("Reseed in progress, check sidebar for status"));
|
||||
} else {
|
||||
addFormNotice(_t("Reseed complete, check summary bar for status"));
|
||||
addFormNotice(_t("Reseed complete, check sidebar for status"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import net.i2p.router.web.FormHandler;
|
||||
|
||||
|
||||
/**
|
||||
* Simple summary bar configuration.
|
||||
* Simple sidebar configuration.
|
||||
*
|
||||
* @since 0.9.1
|
||||
*/
|
||||
@@ -39,12 +39,12 @@ public class ConfigSummaryHandler extends FormHandler {
|
||||
}
|
||||
} else if (_action.equals(_t("Restore full default"))) {
|
||||
_context.router().saveConfig(SummaryHelper.PROP_SUMMARYBAR + "default", isAdvanced() ? SummaryHelper.DEFAULT_FULL_ADVANCED : SummaryHelper.DEFAULT_FULL);
|
||||
addFormNotice(_t("Full summary bar default restored.") + " " +
|
||||
_t("Summary bar will refresh shortly."));
|
||||
addFormNotice(_t("Full sidebar default restored.") + " " +
|
||||
_t("Sidebar will refresh shortly."));
|
||||
} else if (_action.equals(_t("Restore minimal default"))) {
|
||||
_context.router().saveConfig(SummaryHelper.PROP_SUMMARYBAR + "default", isAdvanced() ? SummaryHelper.DEFAULT_MINIMAL_ADVANCED : SummaryHelper.DEFAULT_MINIMAL);
|
||||
addFormNotice(_t("Minimal summary bar default restored.") + " " +
|
||||
_t("Summary bar will refresh shortly."));
|
||||
addFormNotice(_t("Minimal sidebar default restored.") + " " +
|
||||
_t("Sidebar will refresh shortly."));
|
||||
} else if (adding || deleting || saving || moving) {
|
||||
Map<Integer, String> sections = new TreeMap<Integer, String>();
|
||||
for (Object o : _settings.keySet()) {
|
||||
@@ -137,7 +137,7 @@ public class ConfigSummaryHandler extends FormHandler {
|
||||
}
|
||||
SummaryHelper.saveSummaryBarSections(_context, "default", sections);
|
||||
addFormNotice(_t("Saved order of sections.") + " " +
|
||||
_t("Summary bar will refresh shortly."));
|
||||
_t("Sidebar will refresh shortly."));
|
||||
} else {
|
||||
//addFormError(_t("Unsupported"));
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class HomeHelper extends HelperBase {
|
||||
"anoncoin.i2p" + S + _x("The Anoncoin project") + S + "http://anoncoin.i2p/" + S + I + "anoncoin_32.png" + S +
|
||||
_x("I2P Bug Reports") + S + _x("Bug tracker") + S + "http://trac.i2p2.i2p/report/1" + S + I + "bug.png" + S +
|
||||
//"colombo-bt.i2p" + S + _x("The Italian Bittorrent Resource") + S + "http://colombo-bt.i2p/" + S + I + "colomboicon.png" + S +
|
||||
_x("Dev Builds") + S + _x("Development builds of I2P") + S + "http://bobthebuilder.i2p/" + S + I + "script_gear.png" + S +
|
||||
//_x("Dev Builds") + S + _x("Development builds of I2P") + S + "http://bobthebuilder.i2p/" + S + I + "script_gear.png" + S +
|
||||
_x("Dev Forum") + S + _x("Development forum") + S + "http://zzz.i2p/" + S + I + "group_gear.png" + S +
|
||||
//_x("diftracker") + S + _x("Bittorrent tracker") + S + "http://diftracker.i2p/" + S + I + "magnet.png" + S +
|
||||
"echelon.i2p" + S + _x("I2P Applications") + S + "http://echelon.i2p/" + S + I + "box_open.png" + S +
|
||||
@@ -69,12 +69,13 @@ public class HomeHelper extends HelperBase {
|
||||
//"jisko.i2p" + S + _x("Simple and fast microblogging website") + S + "http://jisko.i2p/" + S + I + "jisko_console_icon.png" + S +
|
||||
//_x("Key Server") + S + _x("OpenPGP Keyserver") + S + "http://keys.i2p/" + S + I + "education.png" + S +
|
||||
//"killyourtv.i2p" + S + _x("Debian and Tahoe-LAFS repositories") + S + "http://killyourtv.i2p/" + S + I + "television_delete.png" + S +
|
||||
_x("MuWire") + S + _x("Easy anonymous file sharing") + S + "http://muwire.i2p/" + S + I + "muwire.png" + S +
|
||||
//_x("Open4You") + S + _x("Free eepsite hosting with PHP and MySQL") + S + "http://open4you.i2p/" + S + I + "open4you-logo.png" + S +
|
||||
//_x("Pastebin") + S + _x("Encrypted I2P Pastebin") + S + "http://zerobin.i2p/" + S + I + "paste_plain.png" + S +
|
||||
_x("Planet I2P") + S + _x("I2P News") + S + "http://planet.i2p/" + S + I + "world.png" + S +
|
||||
_x("I2P Plugins") + S + _x("Add-on directory") + S + "http://i2pwiki.i2p/index.php?title=Plugins" + S + I + "info/plugin_link.png" + S +
|
||||
//_x("Postman's Tracker") + S + _x("Bittorrent tracker") + S + "http://tracker2.postman.i2p/" + S + I + "magnet.png" + S +
|
||||
_x("PrivateBin") + S + _x("Encrypted I2P Pastebin") + S + "http://paste.crypthost.i2p/" + S + I + "paste_plain.png" + S +
|
||||
//_x("PrivateBin") + S + _x("Encrypted I2P Pastebin") + S + "http://paste.crypthost.i2p/" + S + I + "paste_plain.png" + S +
|
||||
_x("Project Website") + S + _x("I2P home page") + S + "http://i2p-projekt.i2p/" + S + I + "info_rhombus.png" + S +
|
||||
//_x("lenta news [ru]") + S + _x("Russian News Feed") + S + "http://lenta.i2p/" + S + I + "lenta_main_logo.png" + S +
|
||||
//"Salt" + S + "salt.i2p" + S + "http://salt.i2p/" + S + I + "salt_console.png" + S +
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package net.i2p.router.web.helpers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DataHelper;
|
||||
@@ -36,10 +38,11 @@ public class NetDbHelper extends FormHandler {
|
||||
{_x("Summary"), // 0
|
||||
_x("Local Router"), // 1
|
||||
_x("Router Lookup"), // 2
|
||||
// advanced below here
|
||||
_x("All Routers"), // 3
|
||||
_x("All Routers with Full Stats"), // 4
|
||||
"LeaseSet Debug", // 5
|
||||
_x("LeaseSets"), // 6
|
||||
_x("LeaseSets"), // 5
|
||||
"LeaseSet Debug", // 6
|
||||
"Sybil", // 7
|
||||
"Advanced Lookup" }; // 8
|
||||
|
||||
@@ -49,8 +52,8 @@ public class NetDbHelper extends FormHandler {
|
||||
"", // 2
|
||||
"?f=2", // 3
|
||||
"?f=1", // 4
|
||||
"?l=2", // 5
|
||||
"?l=1", // 6
|
||||
"?l=1", // 5
|
||||
"?l=2", // 6
|
||||
"?f=3", // 7
|
||||
"?f=4" }; // 8
|
||||
|
||||
@@ -221,11 +224,33 @@ public class NetDbHelper extends FormHandler {
|
||||
_postOK = "Run new analysis".equals(_action) ||
|
||||
"Review analysis".equals(_action);
|
||||
if ("Save".equals(_action)) {
|
||||
String newTime = getJettyString("runFrequency");
|
||||
if (newTime != null) {
|
||||
try {
|
||||
long ntime = Long.parseLong(newTime) * 60*60*1000;
|
||||
if (_context.router().saveConfig(Analysis.PROP_FREQUENCY, Long.toString(ntime)))
|
||||
Map<String, String> toSave = new HashMap<String, String>(4);
|
||||
String newTime = getJettyString("runFrequency");
|
||||
if (newTime != null) {
|
||||
long ntime = Long.parseLong(newTime) * 60*60*1000;
|
||||
toSave.put(Analysis.PROP_FREQUENCY, Long.toString(ntime));
|
||||
}
|
||||
String thresh = getJettyString("threshold");
|
||||
if (thresh != null && thresh.length() > 0) {
|
||||
float val = Math.max(Float.parseFloat(thresh), Analysis.MIN_BLOCK_POINTS);
|
||||
toSave.put(Analysis.PROP_THRESHOLD, Float.toString(val));
|
||||
}
|
||||
String days = getJettyString("days");
|
||||
if (days != null && days.length() > 0) {
|
||||
long val = 24*60*60*1000L * Integer.parseInt(days);
|
||||
toSave.put(Analysis.PROP_BLOCKTIME, Long.toString(val));
|
||||
}
|
||||
String age = getJettyString("deleteAge");
|
||||
if (age != null && age.length() > 0) {
|
||||
long val = 24*60*60*1000L * Integer.parseInt(age);
|
||||
toSave.put(Analysis.PROP_REMOVETIME, Long.toString(val));
|
||||
}
|
||||
String enable = getJettyString("block");
|
||||
toSave.put(Analysis.PROP_BLOCK, Boolean.toString(enable != null));
|
||||
String nonff = getJettyString("nonff");
|
||||
toSave.put(Analysis.PROP_NONFF, Boolean.toString(nonff != null));
|
||||
if (_context.router().saveConfig(toSave, null))
|
||||
addFormNotice(_t("Configuration saved successfully."));
|
||||
else
|
||||
addFormError("Error saving the configuration (applied but not saved) - please see the error logs");
|
||||
@@ -233,7 +258,6 @@ public class NetDbHelper extends FormHandler {
|
||||
} catch (NumberFormatException nfe) {
|
||||
addFormError("bad value");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +281,7 @@ public class NetDbHelper extends FormHandler {
|
||||
} else if (_full == 3) {
|
||||
if (_mode == 12 && !_postOK)
|
||||
_mode = 0;
|
||||
else if (_mode == 13 && !_postOK)
|
||||
else if ((_mode == 13 || _mode == 16) && !_postOK)
|
||||
_mode = 14;
|
||||
(new SybilRenderer(_context)).getNetDbSummary(_out, _newNonce, _mode, _date);
|
||||
} else if (_full == 4) {
|
||||
@@ -276,9 +300,9 @@ public class NetDbHelper extends FormHandler {
|
||||
*/
|
||||
private int getTab() {
|
||||
if (_debug)
|
||||
return 5;
|
||||
if (_lease)
|
||||
return 6;
|
||||
if (_lease)
|
||||
return 5;
|
||||
if (".".equals(_routerPrefix))
|
||||
return 1;
|
||||
if (_routerPrefix != null || _version != null || _country != null ||
|
||||
@@ -310,7 +334,7 @@ public class NetDbHelper extends FormHandler {
|
||||
for (int i = 0; i < titles.length; i++) {
|
||||
if (i == 2 && tab != 2)
|
||||
continue; // can't nav to lookup
|
||||
if ((i == 5 || i == 7 || i == 8) && !isAdvanced())
|
||||
if (i > 2 && i != tab && !isAdvanced())
|
||||
continue;
|
||||
if (i == tab) {
|
||||
// we are there
|
||||
|
||||
@@ -42,6 +42,7 @@ import net.i2p.router.TunnelPoolSettings;
|
||||
import net.i2p.router.util.HashDistance; // debug
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import static net.i2p.router.sybil.Util.biLog2;
|
||||
import net.i2p.router.web.HelperBase;
|
||||
import net.i2p.router.web.Messages;
|
||||
import net.i2p.router.web.WebAppStarter;
|
||||
import net.i2p.util.Log;
|
||||
@@ -765,23 +766,26 @@ class NetDbRenderer {
|
||||
start = end;
|
||||
|
||||
// transports table
|
||||
buf.append("<table id=\"netdbtransports\">\n");
|
||||
buf.append("<tr><th align=\"left\">" + _t("Transports") + "</th><th>" + _t("Count") + "</th></tr>\n");
|
||||
for (int i = 0; i < TNAMES.length; i++) {
|
||||
int num = transportCount[i];
|
||||
if (num > 0) {
|
||||
buf.append("<tr><td>").append(_t(TNAMES[i]));
|
||||
buf.append("</td><td align=\"center\">").append(num).append("</td></tr>\n");
|
||||
boolean showTransports = _context.getBooleanProperty(HelperBase.PROP_ADVANCED);
|
||||
if (showTransports) {
|
||||
buf.append("<table id=\"netdbtransports\">\n");
|
||||
buf.append("<tr><th align=\"left\">" + _t("Transports") + "</th><th>" + _t("Count") + "</th></tr>\n");
|
||||
for (int i = 0; i < TNAMES.length; i++) {
|
||||
int num = transportCount[i];
|
||||
if (num > 0) {
|
||||
buf.append("<tr><td>").append(_t(TNAMES[i]));
|
||||
buf.append("</td><td align=\"center\">").append(num).append("</td></tr>\n");
|
||||
}
|
||||
}
|
||||
buf.append("</table>\n");
|
||||
buf.append("</td><td style=\"vertical-align: top;\">");
|
||||
out.write(buf.toString());
|
||||
buf.setLength(0);
|
||||
end = System.currentTimeMillis();
|
||||
if (log.shouldWarn())
|
||||
log.warn("part 3 took " + (end - start));
|
||||
start = end;
|
||||
}
|
||||
buf.append("</table>\n");
|
||||
buf.append("</td><td style=\"vertical-align: top;\">");
|
||||
out.write(buf.toString());
|
||||
buf.setLength(0);
|
||||
end = System.currentTimeMillis();
|
||||
if (log.shouldWarn())
|
||||
log.warn("part 3 took " + (end - start));
|
||||
start = end;
|
||||
|
||||
// country table
|
||||
List<String> countryList = new ArrayList<String>(countries.objects());
|
||||
|
||||
@@ -150,6 +150,22 @@ public class PeerHelper extends HelperBase {
|
||||
out.write(buf.toString());
|
||||
} else if ("upnp".equals(_transport)) {
|
||||
// UPnP Status
|
||||
StringBuilder buf = new StringBuilder(512);
|
||||
buf.append("<h3>").append(_t("UPnP Overview"))
|
||||
.append("</h3><div id=\"upnpoverview\"><p>")
|
||||
.append(_t("UPnP is used to communicate with Internet Gateway Devices (IGDs) to detect the external IP address and forward ports."))
|
||||
.append(' ')
|
||||
.append(_t("If UPnP is not working, it may be for one of the following reasons:"))
|
||||
.append("</p><ul id=\"upnphelp\"><li>").append(_t("No UPnP-compatible device present"))
|
||||
.append("</li>\n<li>").append(_t("UPnP disabled on the device"))
|
||||
.append("</li>\n<li>").append(_t("Software firewall interference with UPnP"))
|
||||
.append("</li>\n<li>").append(_t("Bugs in the device's UPnP implementation"))
|
||||
.append("</li>\n<li>").append(_t("Multiple firewall/routers in the internet connection path"))
|
||||
.append("</li>\n<li>").append(_t("UPnP device change, reset, or address change"))
|
||||
.append("</li>\n</ul><p>")
|
||||
.append(_t("UPnP may be enabled or disabled on the Network Configuration page, but a change requires a router restart to take effect."))
|
||||
.append("</p></div>");
|
||||
out.write(buf.toString());
|
||||
_context.commSystem().renderStatusHTML(_out, _urlBase, _sortFlags);
|
||||
}
|
||||
out.flush();
|
||||
@@ -228,8 +244,9 @@ public class PeerHelper extends HelperBase {
|
||||
.append("<tr><td><b id=\"def.idle\">").append(_t("Idle")).append("</b></td><td>").append(_t("How long since a packet has been received / sent")).append("</td></tr>\n")
|
||||
.append("<tr><td><b id=\"def.rate\">").append(_t("In/Out")).append("</b></td><td>").append(_t("The smoothed inbound / outbound transfer rate (KBytes per second)")).append("</td></tr>\n")
|
||||
.append("<tr><td><b id=\"def.up\">").append(_t("Up")).append("</b></td><td>").append(_t("How long ago this connection was established")).append("</td></tr>\n")
|
||||
.append("<tr><td><b id=\"def.skew\">").append(_t("Skew")).append("</b></td><td>").append(_t("The difference between the peer's clock and your own")).append("</td></tr>\n")
|
||||
.append("<tr><td><b id=\"def.cwnd\">CWND</b></td><td>").append(_t("The congestion window, which is how many bytes can be sent without an acknowledgement")).append(" /<br>\n")
|
||||
.append("<tr><td><b id=\"def.skew\">").append(_t("Skew")).append("</b></td><td>").append(_t("The difference between the peer's clock and your own")).append("</td></tr>\n");
|
||||
if (isAdvanced()) {
|
||||
buf.append("<tr><td><b id=\"def.cwnd\">CWND</b></td><td>").append(_t("The congestion window, which is how many bytes can be sent without an acknowledgement")).append(" /<br>\n")
|
||||
.append(_t("The number of sent messages awaiting acknowledgement")).append(" /<br>\n")
|
||||
.append(_t("The maximum number of concurrent messages to send")).append(" /<br>\n")
|
||||
.append(_t("The number of pending sends which exceed congestion window")).append("</td></tr>\n")
|
||||
@@ -237,8 +254,9 @@ public class PeerHelper extends HelperBase {
|
||||
.append("<tr><td><b id=\"def.rtt\">RTT</b></td><td>").append(_t("The round trip time in milliseconds")).append("</td></tr>\n")
|
||||
//.append("<tr><td><b id=\"def.dev\">").append(_t("Dev")).append("</b></td><td>").append(_t("The standard deviation of the round trip time in milliseconds")).append("</td></tr>\n")
|
||||
.append("<tr><td><b id=\"def.rto\">RTO</b></td><td>").append(_t("The retransmit timeout in milliseconds")).append("</td></tr>\n")
|
||||
.append("<tr><td><b id=\"def.mtu\">MTU</b></td><td>").append(_t("Current maximum send packet size / estimated maximum receive packet size (bytes)")).append("</td></tr>\n")
|
||||
.append("<tr><td><b id=\"def.send\">").append(_t("TX")).append("</b></td><td>").append(_t("The total number of messages sent to the peer")).append("</td></tr>\n")
|
||||
.append("<tr><td><b id=\"def.mtu\">MTU</b></td><td>").append(_t("Current maximum send packet size / estimated maximum receive packet size (bytes)")).append("</td></tr>\n");
|
||||
}
|
||||
buf.append("<tr><td><b id=\"def.send\">").append(_t("TX")).append("</b></td><td>").append(_t("The total number of messages sent to the peer")).append("</td></tr>\n")
|
||||
.append("<tr><td><b id=\"def.recv\">").append(_t("RX")).append("</b></td><td>").append(_t("The total number of messages received from the peer")).append("</td></tr>\n")
|
||||
.append("<tr><td><b id=\"def.resent\">").append(_t("Dup TX")).append("</b></td><td>").append(_t("The total number of packets retransmitted to the peer")).append("</td></tr>\n")
|
||||
.append("<tr><td><b id=\"def.dupRecv\">").append(_t("Dup RX")).append("</b></td><td>").append(_t("The total number of duplicate packets received from the peer")).append("</td></tr>\n")
|
||||
@@ -437,7 +455,8 @@ public class PeerHelper extends HelperBase {
|
||||
buf.append("<h3 id=\"udpcon\">").append(_t("UDP connections")).append(": ").append(peers.size());
|
||||
buf.append(". ").append(_t("Limit")).append(": ").append(ut.getMaxConnections());
|
||||
//buf.append(". ").append(_t("Timeout")).append(": ").append(DataHelper.formatDuration2(_expireTimeout));
|
||||
if (isAdvanced()) {
|
||||
final boolean isAdvanced = isAdvanced();
|
||||
if (isAdvanced) {
|
||||
buf.append(". ").append(_t("Status")).append(": ").append(_t(ut.getReachabilityStatus().toStatusString()));
|
||||
}
|
||||
buf.append(".</h3>\n");
|
||||
@@ -463,21 +482,24 @@ public class PeerHelper extends HelperBase {
|
||||
buf.append("</span></th><th nowrap><span class=\"peersort\"><a href=\"#def.skew\">").append(_t("Skew")).append("</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by clock skew"), FLAG_SKEW);
|
||||
buf.append("</span></th>\n");
|
||||
buf.append("<th nowrap><a href=\"#def.cwnd\">CWND</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by congestion window"), FLAG_CWND);
|
||||
buf.append("</th><th nowrap><span class=\"peersort\"><a href=\"#def.ssthresh\">SST</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by slow start threshold"), FLAG_SSTHRESH);
|
||||
buf.append("</span></th>\n");
|
||||
buf.append("<th nowrap><span class=\"peersort\"><a href=\"#def.rtt\">RTT</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by round trip time"), FLAG_RTT);
|
||||
//buf.append("</th><th nowrap><a href=\"#def.dev\">").append(_t("Dev")).append("</a><br>");
|
||||
//appendSortLinks(buf, urlBase, sortFlags, _t("Sort by round trip time deviation"), FLAG_DEV);
|
||||
buf.append("</span></th><th nowrap><span class=\"peersort\"><a href=\"#def.rto\">RTO</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by retransmission timeout"), FLAG_RTO);
|
||||
buf.append("</span></th>\n");
|
||||
buf.append("<th nowrap><a href=\"#def.mtu\">MTU</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by outbound maximum transmit unit"), FLAG_MTU);
|
||||
buf.append("</th><th nowrap><span class=\"peersort\"><a href=\"#def.send\">").append(_t("TX")).append("</a><br>");
|
||||
if (isAdvanced) {
|
||||
buf.append("<th nowrap><a href=\"#def.cwnd\">CWND</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by congestion window"), FLAG_CWND);
|
||||
buf.append("</th><th nowrap><span class=\"peersort\"><a href=\"#def.ssthresh\">SST</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by slow start threshold"), FLAG_SSTHRESH);
|
||||
buf.append("</span></th>\n");
|
||||
buf.append("<th nowrap><span class=\"peersort\"><a href=\"#def.rtt\">RTT</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by round trip time"), FLAG_RTT);
|
||||
//buf.append("</th><th nowrap><a href=\"#def.dev\">").append(_t("Dev")).append("</a><br>");
|
||||
//appendSortLinks(buf, urlBase, sortFlags, _t("Sort by round trip time deviation"), FLAG_DEV);
|
||||
buf.append("</span></th><th nowrap><span class=\"peersort\"><a href=\"#def.rto\">RTO</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by retransmission timeout"), FLAG_RTO);
|
||||
buf.append("</span></th>\n");
|
||||
buf.append("<th nowrap><a href=\"#def.mtu\">MTU</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by outbound maximum transmit unit"), FLAG_MTU);
|
||||
buf.append("</th>");
|
||||
}
|
||||
buf.append("<th nowrap><span class=\"peersort\"><a href=\"#def.send\">").append(_t("TX")).append("</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets sent"), FLAG_SEND);
|
||||
buf.append("</span></th><th nowrap><span class=\"peersort\"><a href=\"#def.recv\">").append(_t("RX")).append("</a><br>");
|
||||
appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets received"), FLAG_RECV);
|
||||
@@ -576,46 +598,50 @@ public class PeerHelper extends HelperBase {
|
||||
|
||||
long sendWindow = peer.getSendWindowBytes();
|
||||
|
||||
buf.append("<td class=\"cells cwnd\" align=\"center\"><span class=\"right\">");
|
||||
buf.append(sendWindow/1024);
|
||||
buf.append("K");
|
||||
buf.append("</span>").append(THINSP).append("<span class=\"right\">").append(peer.getConcurrentSends());
|
||||
buf.append("</span>").append(THINSP).append("<span class=\"right\">").append(peer.getConcurrentSendWindow());
|
||||
buf.append("</span>").append(THINSP).append("<span class=\"left\">").append(peer.getConsecutiveSendRejections());
|
||||
if (peer.isBacklogged())
|
||||
buf.append(' ').append(_t("backlogged"));
|
||||
buf.append("</span></td>");
|
||||
int rtt = 0;
|
||||
int rto = 0;
|
||||
if (isAdvanced) {
|
||||
buf.append("<td class=\"cells cwnd\" align=\"center\"><span class=\"right\">");
|
||||
buf.append(sendWindow/1024);
|
||||
buf.append("K");
|
||||
buf.append("</span>").append(THINSP).append("<span class=\"right\">").append(peer.getConcurrentSends());
|
||||
buf.append("</span>").append(THINSP).append("<span class=\"right\">").append(peer.getConcurrentSendWindow());
|
||||
buf.append("</span>").append(THINSP).append("<span class=\"left\">").append(peer.getConsecutiveSendRejections());
|
||||
if (peer.isBacklogged())
|
||||
buf.append(' ').append(_t("backlogged"));
|
||||
buf.append("</span></td>");
|
||||
|
||||
buf.append("<td class=\"cells\" align=\"right\">");
|
||||
buf.append(peer.getSlowStartThreshold()/1024);
|
||||
buf.append("K</td>");
|
||||
buf.append("<td class=\"cells\" align=\"right\">");
|
||||
buf.append(peer.getSlowStartThreshold()/1024);
|
||||
buf.append("K</td>");
|
||||
|
||||
int rtt = peer.getRTT();
|
||||
int rto = peer.getRTO();
|
||||
rtt = peer.getRTT();
|
||||
rto = peer.getRTO();
|
||||
|
||||
buf.append("<td class=\"cells\" align=\"right\">");
|
||||
if (rtt > 0)
|
||||
buf.append(DataHelper.formatDuration2(rtt));
|
||||
else
|
||||
buf.append("n/a");
|
||||
buf.append("</td>");
|
||||
buf.append("<td class=\"cells\" align=\"right\">");
|
||||
if (rtt > 0)
|
||||
buf.append(DataHelper.formatDuration2(rtt));
|
||||
else
|
||||
buf.append("n/a");
|
||||
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));
|
||||
buf.append("</td>");
|
||||
buf.append("<td class=\"cells\" align=\"right\">");
|
||||
buf.append(DataHelper.formatDuration2(rto));
|
||||
buf.append("</td>");
|
||||
|
||||
buf.append("<td class=\"cells\" align=\"center\"><span class=\"right\">");
|
||||
buf.append(peer.getMTU()).append("</span>").append(THINSP);
|
||||
buf.append("<span class=\"left\">").append(peer.getReceiveMTU());
|
||||
buf.append("<td class=\"cells\" align=\"center\"><span class=\"right\">");
|
||||
buf.append(peer.getMTU()).append("</span>").append(THINSP);
|
||||
buf.append("<span class=\"left\">").append(peer.getReceiveMTU());
|
||||
|
||||
//.append('/');
|
||||
//buf.append(peer.getMTUIncreases()).append('/');
|
||||
//buf.append(peer.getMTUDecreases());
|
||||
buf.append("</span></td>");
|
||||
//.append('/');
|
||||
//buf.append(peer.getMTUIncreases()).append('/');
|
||||
//buf.append(peer.getMTUDecreases());
|
||||
buf.append("</span></td>");
|
||||
}
|
||||
|
||||
long sent = peer.getMessagesSent();
|
||||
long recv = peer.getMessagesReceived();
|
||||
@@ -681,19 +707,22 @@ public class PeerHelper extends HelperBase {
|
||||
buf.append("</b></span></td>" +
|
||||
"<td align=\"right\"><b>").append(DataHelper.formatDuration2(x));
|
||||
x = offsetTotal/numPeers;
|
||||
buf.append("</b></td><td align=\"right\"><b>").append(DataHelper.formatDuration2(x)).append("</b></td>\n" +
|
||||
"<td align=\"center\"><b>");
|
||||
buf.append(cwinTotal/(numPeers*1024) + "K");
|
||||
buf.append("</b></td><td> </td>\n" +
|
||||
"<td align=\"right\"><b>");
|
||||
if (numRTTPeers > 0)
|
||||
buf.append(DataHelper.formatDuration2(rttTotal/numRTTPeers));
|
||||
else
|
||||
buf.append("n/a");
|
||||
//buf.append("</b></td><td> </td><td align=\"center\"><b>");
|
||||
buf.append("</b></td><td align=\"right\"><b>");
|
||||
buf.append(DataHelper.formatDuration2(rtoTotal/numPeers));
|
||||
buf.append("</b></td><td align=\"center\"><b>").append(ut.getMTU(false)).append("</b></td><td align=\"right\"><b>");
|
||||
buf.append("</b></td><td align=\"right\"><b>").append(DataHelper.formatDuration2(x)).append("</b></td>\n");
|
||||
if (isAdvanced) {
|
||||
buf.append("<td align=\"center\"><b>");
|
||||
buf.append(cwinTotal/(numPeers*1024) + "K");
|
||||
buf.append("</b></td><td> </td>\n" +
|
||||
"<td align=\"right\"><b>");
|
||||
if (numRTTPeers > 0)
|
||||
buf.append(DataHelper.formatDuration2(rttTotal/numRTTPeers));
|
||||
else
|
||||
buf.append("n/a");
|
||||
//buf.append("</b></td><td> </td><td align=\"center\"><b>");
|
||||
buf.append("</b></td><td align=\"right\"><b>");
|
||||
buf.append(DataHelper.formatDuration2(rtoTotal/numPeers));
|
||||
buf.append("</b></td><td align=\"center\"><b>").append(ut.getMTU(false)).append("</b></td>");
|
||||
}
|
||||
buf.append("<td align=\"right\"><b>");
|
||||
buf.append(sendTotal).append("</b></td><td align=\"right\"><b>").append(recvTotal).append("</b></td>\n" +
|
||||
"<td align=\"right\"><b>").append(resentTotal);
|
||||
buf.append("</b></td><td align=\"right\"><b>").append(dupRecvTotal).append("</b></td></tr>\n");
|
||||
|
||||
@@ -23,7 +23,7 @@ import net.i2p.stat.RateStat;
|
||||
* Dump the stats to the web admin interface
|
||||
*/
|
||||
public class StatsGenerator {
|
||||
private RouterContext _context;
|
||||
private final RouterContext _context;
|
||||
|
||||
public StatsGenerator(RouterContext context) {
|
||||
_context = context;
|
||||
|
||||
@@ -16,6 +16,7 @@ import net.i2p.data.DataHelper;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.news.NewsEntry;
|
||||
import net.i2p.router.news.NewsManager;
|
||||
import net.i2p.router.web.ConfigUpdateHandler;
|
||||
import net.i2p.router.web.CSSHelper;
|
||||
import net.i2p.router.web.Messages;
|
||||
import net.i2p.router.web.NavHelper;
|
||||
@@ -151,22 +152,10 @@ class SummaryBarRenderer {
|
||||
|
||||
"<table id=\"sb_help\"><tr><td>" +
|
||||
|
||||
"<a href=\"/help#advancedsettings\" target=\"_top\" title=\"")
|
||||
.append(_t("A guide to some of the less-used configuration settings"))
|
||||
.append("\">")
|
||||
.append(nbsp(_t("Advanced Settings")))
|
||||
.append("</a>\n" +
|
||||
|
||||
"<a href=\"/help#changelog\" target=\"_top\" title=\"")
|
||||
"<a href=\"/viewhistory\" target=\"_top\" title=\"")
|
||||
.append(_t("Recent development changes to the router"))
|
||||
.append("\">")
|
||||
.append(nbsp(_t("Changelog")))
|
||||
.append("</a>\n" +
|
||||
|
||||
"<a href=\"/help#configurationhelp\" target=\"_top\" title=\"")
|
||||
.append(_t("An introduction to configuring your router"))
|
||||
.append("\">")
|
||||
.append(nbsp(_t("Configuration")))
|
||||
.append("</a>\n" +
|
||||
|
||||
"<a href=\"/help#faq\" target=\"_top\" title=\"")
|
||||
@@ -175,7 +164,7 @@ class SummaryBarRenderer {
|
||||
.append(nbsp(_t("FAQ")))
|
||||
.append("</a>\n" +
|
||||
|
||||
"<a href=\"/help#legal\" target=\"_top\" title=\"")
|
||||
"<a href=\"/viewlicense\" target=\"_top\" title=\"")
|
||||
.append(_t("Information regarding software and licenses used by I2P"))
|
||||
.append("\">")
|
||||
.append(nbsp(_t("Legal")))
|
||||
@@ -974,11 +963,6 @@ class SummaryBarRenderer {
|
||||
StringBuilder buf = new StringBuilder(512);
|
||||
String consoleNonce = CSSHelper.getNonce();
|
||||
if (consoleNonce != null) {
|
||||
// Set up title and pre-headings stuff.
|
||||
//buf.append("<h3><a href=\"/configupdate\">")
|
||||
buf.append("<h3><a href=\"/news\">")
|
||||
.append(_t("News & Updates"))
|
||||
.append("</a></h3><hr class=\"b\"><div class=\"sb_newsheadings\">\n");
|
||||
// Get news content.
|
||||
List<NewsEntry> entries = Collections.emptyList();
|
||||
ClientAppManager cmgr = _context.clientAppManager();
|
||||
@@ -988,18 +972,33 @@ class SummaryBarRenderer {
|
||||
entries = nmgr.getEntries();
|
||||
}
|
||||
if (!entries.isEmpty()) {
|
||||
buf.append("<table>\n");
|
||||
DateFormat fmt = DateFormat.getDateInstance(DateFormat.SHORT);
|
||||
// the router sets the JVM time zone to UTC but saves the original here so we can get it
|
||||
fmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
|
||||
int i = 0;
|
||||
// show a min of 1, max of 3, none older than 60 days over min
|
||||
final int min = 1;
|
||||
// Except, if news fetching is disabled, min is 0 and oldest is 7 days.
|
||||
// We still show news even if disabled, because the user could click it manually
|
||||
String freq = _context.getProperty(ConfigUpdateHandler.PROP_REFRESH_FREQUENCY,
|
||||
ConfigUpdateHandler.DEFAULT_REFRESH_FREQUENCY);
|
||||
long ms = ConfigUpdateHandler.DEFAULT_REFRESH_FREQ;
|
||||
try {
|
||||
ms = Long.parseLong(freq);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
final int min = (ms > 0) ? 1 : 0;
|
||||
final int max = 3;
|
||||
final long age = (ms > 0) ? 60*24*60*60*1000L : 7*24*60*60*1000L;
|
||||
final long oldest = _context.clock().now() - age;
|
||||
for (NewsEntry entry : entries) {
|
||||
if (i >= min && entry.updated > 0 &&
|
||||
entry.updated < _context.clock().now() - 60*24*60*60*1000L)
|
||||
entry.updated < oldest)
|
||||
break;
|
||||
if (i == 0) {
|
||||
// Set up title and pre-headings stuff.
|
||||
buf.append("<h3><a href=\"/news\">")
|
||||
.append(_t("News & Updates"))
|
||||
.append("</a></h3><hr class=\"b\"><div class=\"sb_newsheadings\">\n<table>\n");
|
||||
}
|
||||
buf.append("<tr><td><a href=\"/?news=1&consoleNonce=")
|
||||
.append(consoleNonce)
|
||||
.append("\"");
|
||||
@@ -1014,17 +1013,9 @@ class SummaryBarRenderer {
|
||||
if (++i >= max)
|
||||
break;
|
||||
}
|
||||
buf.append("</table>\n");
|
||||
} else {
|
||||
buf.append("<center><i>")
|
||||
.append(_t("none"))
|
||||
.append("</i></center>");
|
||||
if (i > 0)
|
||||
buf.append("</table>\n</div>\n");
|
||||
}
|
||||
// Add post-headings stuff.
|
||||
//buf.append("<a href=\"/news\">")
|
||||
//.append(_t("Show all news"))
|
||||
//.append("</a>\n");
|
||||
buf.append("</div>\n");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import net.i2p.router.sybil.Points;
|
||||
import static net.i2p.router.sybil.Util.biLog2;
|
||||
import net.i2p.router.tunnel.pool.TunnelPool;
|
||||
import net.i2p.router.util.HashDistance;
|
||||
import net.i2p.router.web.HelperBase;
|
||||
import net.i2p.router.web.Messages;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.stat.RateAverages;
|
||||
@@ -61,16 +62,22 @@ import net.i2p.util.VersionComparator;
|
||||
public class SybilRenderer {
|
||||
|
||||
private final RouterContext _context;
|
||||
private final Log _log;
|
||||
private final DecimalFormat fmt = new DecimalFormat("#0.00");
|
||||
private final DateFormat dfmt;
|
||||
|
||||
private static final int PAIRMAX = Analysis.PAIRMAX;
|
||||
private static final int MAX = Analysis.MAX;
|
||||
private static final double MIN_CLOSE = Analysis.MIN_CLOSE;
|
||||
private static final double MIN_DISPLAY_POINTS = 12.01;
|
||||
private static final int[] HOURS = { 1, 6, 24, 7*24, 30*24, 0 };
|
||||
private static final int[] DAYS = { 2, 7, 30, 90, 365, 0 };
|
||||
|
||||
public SybilRenderer(RouterContext ctx) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(SybilRenderer.class);
|
||||
dfmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT);
|
||||
dfmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,10 +137,15 @@ public class SybilRenderer {
|
||||
Hash us = _context.routerHash();
|
||||
Analysis analysis = Analysis.getInstance(_context);
|
||||
List<RouterInfo> ris = null;
|
||||
if (mode != 0 && mode != 12 && mode != 13 && mode != 14) {
|
||||
ris = analysis.getFloodfills(us);
|
||||
if (mode != 0 && mode < 12) {
|
||||
if (mode >= 2 && mode <= 6) {
|
||||
// review all routers for family and IP analysis
|
||||
ris = analysis.getAllRouters(us);
|
||||
} else {
|
||||
ris = analysis.getFloodfills(us);
|
||||
}
|
||||
if (ris.isEmpty()) {
|
||||
out.write("<h3 class=\"sybils\">No known floodfills</h3>");
|
||||
out.write("<h3 class=\"sybils\">No known routers</h3>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -145,6 +157,7 @@ public class SybilRenderer {
|
||||
"<div id=\"sybilnav\"><ul><li><a href=\"netdb?f=3\">Review stored analysis</a>" +
|
||||
"</li><li><a href=\"netdb?f=3&m=14\">Run new analysis</a>" +
|
||||
"</li><li><a href=\"netdb?f=3&m=15\">Configure periodic analysis</a>" +
|
||||
"</li><li><a href=\"/profiles?f=3\">Review current bans</a>" +
|
||||
"</li><li><a href=\"netdb?f=3&m=1\">Floodfill Summary</a>" +
|
||||
"</li><li><a href=\"netdb?f=3&m=2\">Same Family</a>" +
|
||||
"</li><li><a href=\"netdb?f=3&m=3\">IP close to us</a>" +
|
||||
@@ -195,18 +208,21 @@ public class SybilRenderer {
|
||||
try {
|
||||
points = ps.load(date);
|
||||
} catch (IOException ioe) {
|
||||
out.write("<b>No analysis found for " + new Date(date) + "</b>");
|
||||
_log.error("loading stored analysis for date: " + date, ioe);
|
||||
out.write("<b>Failed to load analysis for " + dfmt.format(new Date(date)) + "</b>: " +
|
||||
DataHelper.escapeHTML(ioe.toString()));
|
||||
return;
|
||||
}
|
||||
if (points.isEmpty()) {
|
||||
out.write("<b>No analysis found for " + new Date(date) + "</b>");
|
||||
_log.error("empty stored analysis or bad file format for date: " + date);
|
||||
out.write("<b>Corrupt analysis file for " + dfmt.format(new Date(date)) + "</b>");
|
||||
} else {
|
||||
renderThreatsHTML(out, buf, date, points);
|
||||
}
|
||||
} else if (mode == 13) {
|
||||
} else if (mode == 13 || mode == 16) {
|
||||
// run analysis and store it
|
||||
long now = _context.clock().now();
|
||||
points = analysis.backgroundAnalysis();
|
||||
points = analysis.backgroundAnalysis(mode == 16);
|
||||
if (!points.isEmpty()) {
|
||||
PersistSybil ps = analysis.getPersister();
|
||||
try {
|
||||
@@ -244,8 +260,6 @@ public class SybilRenderer {
|
||||
"Select stored analysis: " +
|
||||
"<select name=\"date\">\n");
|
||||
boolean first = true;
|
||||
DateFormat dfmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
|
||||
dfmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
|
||||
for (Long date : dates) {
|
||||
buf.append("<option value=\"").append(date).append('\"');
|
||||
if (first) {
|
||||
@@ -270,6 +284,14 @@ public class SybilRenderer {
|
||||
"<input type=\"hidden\" name=\"m\" value=\"13\">\n" +
|
||||
"<input type=\"hidden\" name=\"nonce\" value=\"").append(nonce).append("\" >\n" +
|
||||
"<input type=\"submit\" name=\"action\" class=\"go\" value=\"Run new analysis\" />" +
|
||||
"(floodfills only)" +
|
||||
"</form><br>\n");
|
||||
buf.append("<form action=\"netdb\" method=\"POST\">\n" +
|
||||
"<input type=\"hidden\" name=\"f\" value=\"3\">\n" +
|
||||
"<input type=\"hidden\" name=\"m\" value=\"16\">\n" +
|
||||
"<input type=\"hidden\" name=\"nonce\" value=\"").append(nonce).append("\" >\n" +
|
||||
"<input type=\"submit\" name=\"action\" class=\"go\" value=\"Run new analysis\" />" +
|
||||
"(all routers)" +
|
||||
"</form>\n");
|
||||
writeBuf(out, buf);
|
||||
}
|
||||
@@ -283,10 +305,10 @@ public class SybilRenderer {
|
||||
"<input type=\"hidden\" name=\"f\" value=\"3\">\n" +
|
||||
"<input type=\"hidden\" name=\"m\" value=\"15\">\n" +
|
||||
"<input type=\"hidden\" name=\"nonce\" value=\"").append(nonce).append("\" >\n" +
|
||||
"Background analysis run frequency: <select name=\"runFrequency\">");
|
||||
"<table><tr><td>Background analysis run frequency:</td><td><select name=\"runFrequency\">");
|
||||
for (int i = 0; i < HOURS.length; i++) {
|
||||
buf.append("<option value=\"");
|
||||
buf.append(Integer.toString(HOURS[i]));
|
||||
buf.append(HOURS[i]);
|
||||
buf.append('"');
|
||||
long time = HOURS[i] * 60*60*1000L;
|
||||
if (time == freq)
|
||||
@@ -298,9 +320,40 @@ public class SybilRenderer {
|
||||
buf.append(_t("Never"));
|
||||
buf.append("</option>\n");
|
||||
}
|
||||
buf.append("</select> " +
|
||||
boolean auto = _context.getBooleanProperty(Analysis.PROP_BLOCK);
|
||||
boolean nonff = _context.getBooleanProperty(Analysis.PROP_NONFF);
|
||||
String thresh = _context.getProperty(Analysis.PROP_THRESHOLD, "50");
|
||||
long days = _context.getProperty(Analysis.PROP_BLOCKTIME, 7*24*60*60*1000L) / (24*60*60*1000L);
|
||||
buf.append("</select></td></tr>\n<tr><td>" +
|
||||
"Auto-block routers?</td><td><input type=\"checkbox\" class=\"optbox\" value=\"1\" name=\"block\" ");
|
||||
if (auto)
|
||||
buf.append(HelperBase.CHECKED);
|
||||
buf.append("></td></tr>\n<tr><td>" +
|
||||
"Include non-floodfills?</td><td><input type=\"checkbox\" class=\"optbox\" value=\"1\" name=\"nonff\" ");
|
||||
if (nonff)
|
||||
buf.append(HelperBase.CHECKED);
|
||||
buf.append("></td></tr>\n<tr><td>" +
|
||||
"Minimum threat points to block:</td><td><input type=\"text\" name=\"threshold\" value=\"").append(thresh).append("\"></td></tr>\n<tr><td>" +
|
||||
"Days to block:</td><td><input type=\"text\" name=\"days\" value=\"").append(days).append("\"></td></tr>\n<tr><td>" +
|
||||
"Delete stored analysis older than:</td><td><select name=\"deleteAge\">");
|
||||
long age = _context.getProperty(Analysis.PROP_REMOVETIME, 0L);
|
||||
for (int i = 0; i <DAYS.length; i++) {
|
||||
buf.append("<option value=\"");
|
||||
buf.append(DAYS[i]);
|
||||
buf.append('"');
|
||||
long time = DAYS[i] * 24*60*60*1000L;
|
||||
if (time == age)
|
||||
buf.append(" selected=\"selected\"");
|
||||
buf.append('>');
|
||||
if (DAYS[i] > 0)
|
||||
buf.append(DataHelper.formatDuration2(time));
|
||||
else
|
||||
buf.append(_t("Never"));
|
||||
buf.append("</option>\n");
|
||||
}
|
||||
buf.append("</td></tr>\n<tr><td></td><td>" +
|
||||
"<input type=\"submit\" name=\"action\" class=\"accept\" value=\"Save\" />" +
|
||||
"</form>\n");
|
||||
"</td></tr></table></form>\n");
|
||||
writeBuf(out, buf);
|
||||
}
|
||||
|
||||
@@ -310,8 +363,6 @@ public class SybilRenderer {
|
||||
private void renderFFSummary(Writer out, StringBuilder buf, List<RouterInfo> ris, double avgMinDist) throws IOException {
|
||||
renderRouterInfo(buf, _context.router().getRouterInfo(), null, true, false);
|
||||
buf.append("<h3 id=\"known\" class=\"sybils\">Known Floodfills: ").append(ris.size()).append("</h3>");
|
||||
DateFormat dfmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
|
||||
dfmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
|
||||
buf.append("<div id=\"sybils_summary\">\n" +
|
||||
"<b>Average closest floodfill distance:</b> ").append(fmt.format(avgMinDist)).append("<br>\n" +
|
||||
"<b>Routing Data:</b> \"").append(DataHelper.getUTF8(_context.routerKeyGenerator().getModData()))
|
||||
@@ -458,13 +509,8 @@ public class SybilRenderer {
|
||||
List<Hash> warns = new ArrayList<Hash>(points.keySet());
|
||||
Collections.sort(warns, new PointsComparator(points));
|
||||
ReasonComparator rcomp = new ReasonComparator();
|
||||
DateFormat dfmt = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
|
||||
dfmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
|
||||
buf.append("<h3 id=\"threats\" class=\"sybils\">Routers with Most Threat Points as of " + dfmt.format(new Date(date)) + "</h3>");
|
||||
for (Hash h : warns) {
|
||||
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(h);
|
||||
if (ri == null)
|
||||
continue;
|
||||
Points pp = points.get(h);
|
||||
double p = pp.getPoints();
|
||||
if (p < MIN_DISPLAY_POINTS)
|
||||
@@ -480,7 +526,15 @@ public class SybilRenderer {
|
||||
buf.append("<li><b>").append(s, 0, c+1).append("</b>").append(s, c+1, s.length()).append("</li>");
|
||||
}
|
||||
buf.append("</ul>");
|
||||
renderRouterInfo(buf, ri, null, false, false);
|
||||
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(h);
|
||||
if (ri != null) {
|
||||
renderRouterInfo(buf, ri, null, false, false);
|
||||
} else {
|
||||
String hash = h.toBase64();
|
||||
buf.append("<a name=\"").append(hash, 0, 6).append("\"></a><table class=\"sybil_routerinfo\"><tr>" +
|
||||
"<th><b>" + _t("Router") + ":</b> <code>").append(hash).append("</code></th>" +
|
||||
"<th><b>Router info not available</b></th><th></th></tr></table>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
writeBuf(out, buf);
|
||||
@@ -539,7 +593,7 @@ public class SybilRenderer {
|
||||
*/
|
||||
private void renderIPGroupsUs(Writer out, StringBuilder buf, List<RouterInfo> ri32,
|
||||
List<RouterInfo> ri24, List<RouterInfo> ri16) throws IOException {
|
||||
buf.append("<h3 id=\"ourIP\" class=\"sybils\">Floodfills close to Our IP</h3>");
|
||||
buf.append("<h3 id=\"ourIP\" class=\"sybils\">Routers close to Our IP</h3>");
|
||||
boolean found = false;
|
||||
for (RouterInfo info : ri32) {
|
||||
buf.append("<p id=\"sybil_info\"><b>");
|
||||
@@ -571,7 +625,7 @@ public class SybilRenderer {
|
||||
*
|
||||
*/
|
||||
private void renderIPGroups32(Writer out, StringBuilder buf, Map<Integer, List<RouterInfo>> map) throws IOException {
|
||||
buf.append("<h3 id=\"sameIP\" class=\"sybils\">Floodfills with the Same IP</h3>");
|
||||
buf.append("<h3 id=\"sameIP\" class=\"sybils\">Routers with the Same IP</h3>");
|
||||
List<Integer> foo = new ArrayList<Integer>(map.keySet());
|
||||
Collections.sort(foo, new FooComparator(map));
|
||||
boolean found = false;
|
||||
@@ -584,7 +638,7 @@ public class SybilRenderer {
|
||||
int i2 = (i >> 8) & 0xff;
|
||||
int i3 = i & 0xff;
|
||||
String sip = i0 + "." + i1 + '.' + i2 + '.' + i3;
|
||||
buf.append("<p class=\"sybil_info\"><b>").append(count).append(" floodfills with IP <a href=\"/netdb?ip=")
|
||||
buf.append("<p class=\"sybil_info\"><b>").append(count).append(" routers with IP <a href=\"/netdb?ip=")
|
||||
.append(sip).append("&sybil\">").append(sip)
|
||||
.append("</a>:</b></p>");
|
||||
for (RouterInfo info : ris) {
|
||||
@@ -601,7 +655,7 @@ public class SybilRenderer {
|
||||
*
|
||||
*/
|
||||
private void renderIPGroups24(Writer out, StringBuilder buf, Map<Integer, List<RouterInfo>> map) throws IOException {
|
||||
buf.append("<h3 id=\"same24\" class=\"sybils\">Floodfills in the Same /24 (2 minimum)</h3>");
|
||||
buf.append("<h3 id=\"same24\" class=\"sybils\">Routers in the Same /24 (2 minimum)</h3>");
|
||||
List<Integer> foo = new ArrayList<Integer>(map.keySet());
|
||||
Collections.sort(foo, new FooComparator(map));
|
||||
boolean found = false;
|
||||
@@ -613,7 +667,7 @@ public class SybilRenderer {
|
||||
int i1 = (i >> 8) & 0xff;
|
||||
int i2 = i & 0xff;
|
||||
String sip = i0 + "." + i1 + '.' + i2 + ".0/24";
|
||||
buf.append("<p class=\"sybil_info\"><b>").append(count).append(" floodfills with IP <a href=\"/netdb?ip=")
|
||||
buf.append("<p class=\"sybil_info\"><b>").append(count).append(" routers with IP <a href=\"/netdb?ip=")
|
||||
.append(sip).append("&sybil\">").append(sip)
|
||||
.append("</a>:</b></p>");
|
||||
for (RouterInfo info : ris) {
|
||||
@@ -630,7 +684,7 @@ public class SybilRenderer {
|
||||
*
|
||||
*/
|
||||
private void renderIPGroups16(Writer out, StringBuilder buf, Map<Integer, List<RouterInfo>> map) throws IOException {
|
||||
buf.append("<h3 id=\"same16\" class=\"sybils\">Floodfills in the Same /16 (4 minimum)</h3>");
|
||||
buf.append("<h3 id=\"same16\" class=\"sybils\">Routers in the Same /16 (4 minimum)</h3>");
|
||||
List<Integer> foo = new ArrayList<Integer>(map.keySet());
|
||||
Collections.sort(foo, new FooComparator(map));
|
||||
boolean found = false;
|
||||
@@ -642,7 +696,7 @@ public class SybilRenderer {
|
||||
int i1 = i & 0xff;
|
||||
String sip = i0 + "." + i1 + ".0.0/16";
|
||||
if (buf != null) {
|
||||
buf.append("<p class=\"sybil_info\"><b>").append(count).append(" floodfills with IP <a href=\"/netdb?ip=")
|
||||
buf.append("<p class=\"sybil_info\"><b>").append(count).append(" routers with IP <a href=\"/netdb?ip=")
|
||||
.append(sip).append("&sybil\">").append(sip)
|
||||
.append("</a></b></p>");
|
||||
}
|
||||
@@ -660,7 +714,7 @@ public class SybilRenderer {
|
||||
*
|
||||
*/
|
||||
private void renderIPGroupsFamily(Writer out, StringBuilder buf, Map<String, List<RouterInfo>> map) throws IOException {
|
||||
buf.append("<h3 id=\"samefamily\" class=\"sybils\">Floodfills in the same Family</h3><div class=\"sybil_container\">");
|
||||
buf.append("<h3 id=\"samefamily\" class=\"sybils\">Routers in the same Family</h3><div class=\"sybil_container\">");
|
||||
List<String> foo = new ArrayList<String>(map.keySet());
|
||||
Collections.sort(foo, new FoofComparator(map));
|
||||
FamilyKeyCrypto fkc = _context.router().getFamilyKeyCrypto();
|
||||
@@ -671,7 +725,7 @@ public class SybilRenderer {
|
||||
int count = list.size();
|
||||
String ss = DataHelper.escapeHTML(s);
|
||||
if (count > 1) {
|
||||
buf.append("<p class=\"family\"><b>").append(count).append(" floodfills in family: <a href=\"/netdb?fam=")
|
||||
buf.append("<p class=\"family\"><b>").append(count).append(" routers in family: <a href=\"/netdb?fam=")
|
||||
.append(ss).append("&sybil\">").append(ss).append("</a></b></p>");
|
||||
found = true;
|
||||
}
|
||||
@@ -802,14 +856,14 @@ public class SybilRenderer {
|
||||
;
|
||||
if (kr != null) {
|
||||
buf.append("<p><b>Routers:</b> ").append(DataHelper.stripHTML(kr)).append("</p>");
|
||||
} else {
|
||||
buf.append("<p class=\"sybil_filler\"><b>Routers:</b> ").append(_t("n/a")).append("</p>");
|
||||
//} else {
|
||||
// buf.append("<p class=\"sybil_filler\"><b>Routers:</b> ").append(_t("n/a")).append("</p>");
|
||||
}
|
||||
String kls = info.getOption("netdb.knownLeaseSets");
|
||||
if (kls != null) {
|
||||
buf.append("<p class=\"sybilinfo_leasesets\"><b>").append(_t("LeaseSets")).append(":</b> ").append(DataHelper.stripHTML(kls)).append("</p>\n");
|
||||
} else {
|
||||
buf.append("<p class=\"sybilinfo_leasesets filler\"><b>").append(_t("LeaseSets")).append(":</b> ").append(_t("n/a")).append("</p>");
|
||||
//} else {
|
||||
// buf.append("<p class=\"sybilinfo_leasesets filler\"><b>").append(_t("LeaseSets")).append(":</b> ").append(_t("n/a")).append("</p>");
|
||||
}
|
||||
String fam = info.getOption("family");
|
||||
if (fam != null) {
|
||||
@@ -846,29 +900,29 @@ public class SybilRenderer {
|
||||
if (heard > 0) {
|
||||
long age = Math.max(now - heard, 1);
|
||||
buf.append("<p><b>").append(_t("Last Good Lookup")).append(":</b> ").append(_t("{0} ago", DataHelper.formatDuration2(age))).append("</p>");
|
||||
} else {
|
||||
buf.append("<p class=\"sybil_filler\"><b>").append(_t("Last Good Lookup")).append(":</b> ").append(_t("n/a")).append("</p>");
|
||||
//} else {
|
||||
// buf.append("<p class=\"sybil_filler\"><b>").append(_t("Last Good Lookup")).append(":</b> ").append(_t("n/a")).append("</p>");
|
||||
}
|
||||
heard = dbh.getLastLookupFailed();
|
||||
if (heard > 0) {
|
||||
long age = Math.max(now - heard, 1);
|
||||
buf.append("<p><b>").append(_t("Last Bad Lookup")).append(":</b> ").append(_t("{0} ago", DataHelper.formatDuration2(age))).append("</p>");
|
||||
} else {
|
||||
buf.append("<p class=\"sybil_filler\"><b>").append(_t("Last Bad Lookup")).append(":</b> ").append(_t("n/a")).append("</p>");
|
||||
//} else {
|
||||
// buf.append("<p class=\"sybil_filler\"><b>").append(_t("Last Bad Lookup")).append(":</b> ").append(_t("n/a")).append("</p>");
|
||||
}
|
||||
heard = dbh.getLastStoreSuccessful();
|
||||
if (heard > 0) {
|
||||
long age = Math.max(now - heard, 1);
|
||||
buf.append("<p><b>").append(_t("Last Good Store")).append(":</b> ").append(_t("{0} ago", DataHelper.formatDuration2(age))).append("</p>");
|
||||
} else {
|
||||
buf.append("<p class=\"sybil_filler\"><b>").append(_t("Last Good Store")).append(":</b> ").append(_t("n/a")).append("</p>");
|
||||
//} else {
|
||||
// buf.append("<p class=\"sybil_filler\"><b>").append(_t("Last Good Store")).append(":</b> ").append(_t("n/a")).append("</p>");
|
||||
}
|
||||
heard = dbh.getLastStoreFailed();
|
||||
if (heard > 0) {
|
||||
long age = Math.max(now - heard, 1);
|
||||
buf.append("<p><b>").append(_t("Last Bad Store")).append(":</b> ").append(_t("{0} ago", DataHelper.formatDuration2(age))).append("</p>");
|
||||
} else {
|
||||
buf.append("<p class=\"sybil_filler\"><b>").append(_t("Last Bad Store")).append(":</b> ").append(_t("n/a")).append("</p>");
|
||||
//} else {
|
||||
// buf.append("<p class=\"sybil_filler\"><b>").append(_t("Last Bad Store")).append(":</b> ").append(_t("n/a")).append("</p>");
|
||||
}
|
||||
}
|
||||
// any other profile stuff?
|
||||
@@ -952,9 +1006,9 @@ public class SybilRenderer {
|
||||
out.write("<h3>Distance to " + from.toBase64() + "</h3>");
|
||||
prev = null;
|
||||
final int limit = Math.min(10, sybils.size());
|
||||
DateFormat dfmt = DateFormat.getDateInstance(DateFormat.MEDIUM);
|
||||
DateFormat utcfmt = DateFormat.getDateInstance(DateFormat.MEDIUM);
|
||||
for (int i = start; i <= days; i++) {
|
||||
out.write("<h3 class=\"tabletitle\">Distance for " + dfmt.format(new Date(now)) +
|
||||
out.write("<h3 class=\"tabletitle\">Distance for " + utcfmt.format(new Date(now)) +
|
||||
"</h3><table class=\"sybil_distance\"><tr><th>Hash<th>Distance<th>Distance from previous</tr>\n");
|
||||
Hash rkey = rkgen.getRoutingKey(from, now);
|
||||
xor = new XORComparator<Hash>(rkey);
|
||||
|
||||
@@ -28,7 +28,7 @@ import net.i2p.stat.RateStat;
|
||||
* For /tunnels.jsp, used by TunnelHelper.
|
||||
*/
|
||||
class TunnelRenderer {
|
||||
private RouterContext _context;
|
||||
private final RouterContext _context;
|
||||
|
||||
private static final int DISPLAY_LIMIT = 200;
|
||||
|
||||
@@ -115,7 +115,7 @@ class TunnelRenderer {
|
||||
if (bwShare > 12) {
|
||||
// Don't bother re-indenting
|
||||
if (!participating.isEmpty()) {
|
||||
Collections.sort(participating, new TunnelComparator());
|
||||
DataHelper.sort(participating, new TunnelComparator());
|
||||
out.write("<table class=\"tunneldisplay tunnels_participating\"><tr><th>" + _t("Receive on") + "</th><th>" + _t("From") + "</th><th>"
|
||||
+ _t("Send on") + "</th><th>" + _t("To") + "</th><th>" + _t("Expiration") + "</th>"
|
||||
+ "<th>" + _t("Usage") + "</th><th>" + _t("Rate") + "</th><th>" + _t("Role") + "</th></tr>\n");
|
||||
|
||||
Reference in New Issue
Block a user