From 19b1b3cec4760314d0833dc655a357cc5e6d8c90 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 9 Oct 2018 14:55:13 +0000 Subject: [PATCH] Sybil: Prep for background analysis sort threat points consolidate strings --- .../i2p/router/web/helpers/SybilRenderer.java | 270 ++++++++++++------ 1 file changed, 183 insertions(+), 87 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/SybilRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/SybilRenderer.java index e5f2569ff..36d6943a4 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/SybilRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/SybilRenderer.java @@ -108,7 +108,7 @@ class SybilRenderer { private static class Points implements Comparable { private double points; private final List reasons; - /** @param us ROUTING KEY */ + public Points(double points, String reason) { this.points = points; reasons = new ArrayList(4); @@ -121,7 +121,7 @@ class SybilRenderer { private static class PointsComparator implements Comparator, Serializable { private final Map _points; - /** @param us ROUTING KEY */ + public PointsComparator(Map points) { _points = points; } @@ -131,13 +131,39 @@ class SybilRenderer { } } + /** + * Reverse points, then forward by text + * @since 0.9.38 + */ + private static class ReasonComparator implements Comparator, Serializable { + public int compare(String l, String r) { + int lc = l.indexOf(':'); + int rc = r.indexOf(':'); + if (lc <= 0 || rc <= 0) + return 0; + double ld, rd; + try { + // skip "" + ld = Double.parseDouble(l.substring(3, lc)); + rd = Double.parseDouble(r.substring(3, rc)); + } catch (NumberFormatException nfe) { + return 0; + } + int rv = Double.compare(rd, ld); + if (rv != 0) + return rv; + return l.compareTo(r); + } + } + private void addPoints(Map points, Hash h, double d, String reason) { + String rsn = "" + fmt.format(d) + ": " + reason; Points dd = points.get(h); if (dd != null) { dd.points += d; - dd.reasons.add("" + fmt.format(d) + ": " + reason); + dd.reasons.add(rsn); } else { - points.put(h, new Points(d, "" + fmt.format(d) + ": " + reason)); + points.put(h, new Points(d, rsn)); } } @@ -194,13 +220,13 @@ class SybilRenderer { tot += d; } double avgMinDist = tot / count; - buf.append("
\n"); - buf.append("Average closest floodfill distance: ").append(fmt.format(avgMinDist)).append("
\n"); - buf.append("Routing Data: \"").append(DataHelper.getUTF8(_context.routerKeyGenerator().getModData())) - .append("\" Last Changed: ").append(new Date(_context.routerKeyGenerator().getLastChanged())).append("
\n"); - buf.append("Next Routing Data: \"").append(DataHelper.getUTF8(_context.routerKeyGenerator().getNextModData())) - .append("\" Rotates in: ").append(DataHelper.formatDuration(_context.routerKeyGenerator().getTimeTillMidnight())).append("\n"); - buf.append("
\n"); + buf.append("
\n" + + "Average closest floodfill distance: ").append(fmt.format(avgMinDist)).append("
\n" + + "Routing Data: \"").append(DataHelper.getUTF8(_context.routerKeyGenerator().getModData())) + .append("\" Last Changed: ").append(new Date(_context.routerKeyGenerator().getLastChanged())).append("
\n" + + "Next Routing Data: \"").append(DataHelper.getUTF8(_context.routerKeyGenerator().getNextModData())) + .append("\" Rotates in: ").append(DataHelper.formatDuration(_context.routerKeyGenerator().getTimeTillMidnight())).append("\n" + + "
\n"); Map points = new HashMap(64); @@ -270,6 +296,8 @@ class SybilRenderer { if (p < MIN_DISPLAY_POINTS) break; // sorted buf.append("

Threat Points: " + fmt.format(p) + "

    "); + if (pp.reasons.size() > 1) + Collections.sort(pp.reasons, new ReasonComparator()); for (String s : pp.reasons) { buf.append("
  • ").append(s).append("
  • "); } @@ -294,6 +322,10 @@ class SybilRenderer { } } + /** + * @param out null for background analysis + * @param buf null for background analysis + */ private void renderPairDistance(Writer out, StringBuilder buf, List ris, Map points) throws IOException { int sz = ris.size(); List pairs = new ArrayList(PAIRMAX); @@ -317,18 +349,19 @@ class SybilRenderer { } double avg = total / (sz * sz / 2d); - buf.append("

    Average Floodfill Distance is ").append(fmt.format(avg)).append("

    "); - - buf.append("

    Closest Floodfill Pairs by Hash

    "); + if (buf != null) { + buf.append("

    Average Floodfill Distance is ").append(fmt.format(avg)).append("

    " + + "

    Closest Floodfill Pairs by Hash

    "); + } for (Pair p : pairs) { double distance = biLog2(p.dist); double point = MIN_CLOSE - distance; if (point < 0) break; // sorted; - if (point >= 2) { + if (buf != null && point >= 2) { // limit display - buf.append("

    Hash Distance: ").append(fmt.format(distance)).append(": "); - buf.append("

    "); + buf.append("

    Hash Distance: ").append(fmt.format(distance)).append(": " + + "

    "); renderRouterInfo(buf, p.r1, null, false, false); renderRouterInfo(buf, p.r2, null, false, false); } @@ -340,9 +373,11 @@ class SybilRenderer { addPoints(points, p.r2.getHash(), point, "Very close (" + fmt.format(distance) + ") to other floodfill " + b1 + ""); } - out.write(buf.toString()); - out.flush(); - buf.setLength(0); + if (buf != null) { + out.write(buf.toString()); + out.flush(); + buf.setLength(0); + } } private double closestDistance(Hash h, List ris) throws IOException { @@ -392,45 +427,63 @@ class SybilRenderer { } } + /** + * @param out null for background analysis + * @param buf null for background analysis + */ private void renderIPGroupsUs(Writer out, StringBuilder buf, List ris, Map points) throws IOException { RouterInfo us = _context.router().getRouterInfo(); byte[] ourIP = getIP(us); if (ourIP == null) return; - buf.append("

    Floodfills close to Our IP

    "); + if (buf != null) + buf.append("

    Floodfills close to Our IP

    "); boolean found = false; for (RouterInfo info : ris) { byte[] ip = getIP(info); if (ip == null) continue; if (ip[0] == ourIP[0] && ip[1] == ourIP[1]) { - buf.append("

    "); + if (buf != null) + buf.append("

    "); if (ip[2] == ourIP[2]) { if (ip[3] == ourIP[3]) { - buf.append("Same IP as us"); + if (buf != null) + buf.append("Same IP as us"); addPoints(points, info.getHash(), POINTS_US32, "Same IP as us"); } else { - buf.append("Same /24 as us"); + if (buf != null) + buf.append("Same /24 as us"); addPoints(points, info.getHash(), POINTS_US24, "Same /24 as us"); } } else { - buf.append("Same /16 as us"); + if (buf != null) + buf.append("Same /16 as us"); addPoints(points, info.getHash(), POINTS_US16, "Same /16 as us"); } - buf.append(":

    "); - renderRouterInfo(buf, info, null, false, false); + if (buf != null) { + buf.append(":

    "); + renderRouterInfo(buf, info, null, false, false); + } found = true; } } - if (!found) - buf.append("

    None

    "); - out.write(buf.toString()); - out.flush(); - buf.setLength(0); + if (buf != null) { + if (!found) + buf.append("

    None

    "); + out.write(buf.toString()); + out.flush(); + buf.setLength(0); + } } + /** + * @param out null for background analysis + * @param buf null for background analysis + */ private void renderIPGroups32(Writer out, StringBuilder buf, List ris, Map points) throws IOException { - buf.append("

    Floodfills with the Same IP

    "); + if (buf != null) + buf.append("

    Floodfills with the Same IP

    "); ObjectCounter oc = new ObjectCounter(); for (RouterInfo info : ris) { byte[] ip = getIP(info); @@ -455,9 +508,11 @@ class SybilRenderer { int i2 = (i >> 8) & 0xff; int i3 = i & 0xff; String sip = i0 + "." + i1 + '.' + i2 + '.' + i3; - buf.append("

    ").append(count).append(" floodfills with IP ").append(sip) - .append(":

    "); + if (buf != null) { + buf.append("

    ").append(count).append(" floodfills with IP ").append(sip) + .append(":

    "); + } for (RouterInfo info : ris) { byte[] ip = getIP(info); if (ip == null) @@ -471,20 +526,28 @@ class SybilRenderer { if ((ip[3] & 0xff) != i3) continue; found = true; - renderRouterInfo(buf, info, null, false, false); + if (buf != null) + renderRouterInfo(buf, info, null, false, false); double point = POINTS32 * (count - 1); addPoints(points, info.getHash(), point, "Same IP with " + (count - 1) + " other" + (( count > 2) ? "s" : "")); } } - if (!found) - buf.append("

    None

    "); - out.write(buf.toString()); - out.flush(); - buf.setLength(0); + if (buf != null) { + if (!found) + buf.append("

    None

    "); + out.write(buf.toString()); + out.flush(); + buf.setLength(0); + } } + /** + * @param out null for background analysis + * @param buf null for background analysis + */ private void renderIPGroups24(Writer out, StringBuilder buf, List ris, Map points) throws IOException { - buf.append("

    Floodfills in the Same /24 (2 minimum)

    "); + if (buf != null) + buf.append("

    Floodfills in the Same /24 (2 minimum)

    "); ObjectCounter oc = new ObjectCounter(); for (RouterInfo info : ris) { byte[] ip = getIP(info); @@ -508,9 +571,11 @@ class SybilRenderer { int i1 = (i >> 8) & 0xff; int i2 = i & 0xff; String sip = i0 + "." + i1 + '.' + i2 + ".0/24"; - buf.append("

    ").append(count).append(" floodfills with IP ").append(sip) - .append(":

    "); + if (buf != null) { + buf.append("

    ").append(count).append(" floodfills with IP ").append(sip) + .append(":

    "); + } for (RouterInfo info : ris) { byte[] ip = getIP(info); if (ip == null) @@ -522,20 +587,28 @@ class SybilRenderer { if ((ip[2] & 0xff) != i2) continue; found = true; - renderRouterInfo(buf, info, null, false, false); + if (buf != null) + renderRouterInfo(buf, info, null, false, false); double point = POINTS24 * (count - 1); addPoints(points, info.getHash(), point, "Same /24 IP with " + (count - 1) + " other" + (( count > 2) ? "s" : "")); } } - if (!found) - buf.append("

    None

    "); - out.write(buf.toString()); - out.flush(); - buf.setLength(0); + if (buf != null) { + if (!found) + buf.append("

    None

    "); + out.write(buf.toString()); + out.flush(); + buf.setLength(0); + } } + /** + * @param out null for background analysis + * @param buf null for background analysis + */ private void renderIPGroups16(Writer out, StringBuilder buf, List ris, Map points) throws IOException { - buf.append("

    Floodfills in the Same /16 (4 minimum)

    "); + if (buf != null) + buf.append("

    Floodfills in the Same /16 (4 minimum)

    "); ObjectCounter oc = new ObjectCounter(); for (RouterInfo info : ris) { byte[] ip = getIP(info); @@ -558,9 +631,11 @@ class SybilRenderer { int i0 = i >> 8; int i1 = i & 0xff; String sip = i0 + "." + i1 + ".0.0/16"; - buf.append("

    ").append(count).append(" floodfills with IP ").append(sip) - .append("

    "); + if (buf != null) { + buf.append("

    ").append(count).append(" floodfills with IP ").append(sip) + .append("

    "); + } for (RouterInfo info : ris) { byte[] ip = getIP(info); if (ip == null) @@ -576,15 +651,22 @@ class SybilRenderer { addPoints(points, info.getHash(), point, "Same /16 IP with " + (count - 1) + " other" + (( count > 2) ? "s" : "")); } } - if (!found) - buf.append("

    None

    "); - out.write(buf.toString()); - out.flush(); - buf.setLength(0); + if (buf != null) { + if (!found) + buf.append("

    None

    "); + out.write(buf.toString()); + out.flush(); + buf.setLength(0); + } } + /** + * @param out null for background analysis + * @param buf null for background analysis + */ private void renderIPGroupsFamily(Writer out, StringBuilder buf, List ris, Map points) throws IOException { - buf.append("

    Floodfills in the same Family

    "); + if (buf != null) + buf.append("

    Floodfills in the same Family

    "); ObjectCounter oc = new ObjectCounter(); for (RouterInfo info : ris) { String fam = info.getOption("family"); @@ -600,7 +682,7 @@ class SybilRenderer { for (String s : foo) { int count = oc.count(s); String ss = DataHelper.escapeHTML(s); - if (count > 1) { + if (buf != null && count > 1) { buf.append("

    ").append(count).append(" floodfills in family:  ").append(ss).append("

    "); } @@ -626,12 +708,14 @@ class SybilRenderer { } } } - if (!found) - buf.append("

    None

    "); - buf.append("
    "); - out.write(buf.toString()); - out.flush(); - buf.setLength(0); + if (buf != null) { + if (!found) + buf.append("

    None

    "); + buf.append("
    "); + out.write(buf.toString()); + out.flush(); + buf.setLength(0); + } } private static final long DAY = 24*60*60*1000L; @@ -712,6 +796,10 @@ class SybilRenderer { } } + /** + * @param out null for background analysis + * @param buf null for background analysis + */ private void renderRouterInfoHTML(Writer out, StringBuilder buf, Hash us, double avgMinDist, List ris, Map points) throws IOException { Collections.sort(ris, new RouterInfoRoutingKeyComparator(us)); @@ -724,8 +812,14 @@ class SybilRenderer { int medIdx = isEven ? (count / 2) - 1 : (count / 2); for (int i = 0; i < count; i++) { RouterInfo ri = ris.get(i); - double dist = renderRouterInfo(buf, ri, us, false, false); - if (dist < avgMinDist) { + double dist; + if (buf != null) { + dist = renderRouterInfo(buf, ri, us, false, false); + } else { + BigInteger bidist = HashDistance.getDistance(us, ri.getHash()); + dist = biLog2(bidist); + } + if (buf != null && dist < avgMinDist) { if (i == 0) { //buf.append("

    Not to worry, but above router is closer than average minimum distance " + fmt.format(avgMinDist) + "

    "); } else if (i == 1) { @@ -754,12 +848,14 @@ class SybilRenderer { if (i >= MAX - 1) break; } - double avg = tot / count; - buf.append("

    Totals for " + count + " floodfills:  MIN: " + fmt.format(min) + "  AVG: " + - fmt.format(avg) + "  MEDIAN: " + fmt.format(median) + "  MAX: " + fmt.format(max) + "

    \n"); - out.write(buf.toString()); - out.flush(); - buf.setLength(0); + if (buf != null) { + double avg = tot / count; + buf.append("

    Totals for " + count + " floodfills:  MIN: " + fmt.format(min) + "  AVG: " + + fmt.format(avg) + "  MEDIAN: " + fmt.format(median) + "  MAX: " + fmt.format(max) + "

    \n"); + out.write(buf.toString()); + out.flush(); + buf.setLength(0); + } } /** @@ -802,10 +898,10 @@ class SybilRenderer { String country = _context.commSystem().getCountry(info.getIdentity().getHash()); buf.append(""); if(country != null) { - buf.append(""); - buf.append("\"").append(country.toUpperCase(Locale.US)).append('\"'); ").append(""); + buf.append("" + + "\"").append(country.toUpperCase(Locale.US)).append('\"' ").append(""); } if (!full) { buf.append("Hash Distance: ").append(fmt.format(distance)).append("

    \n"); } } - buf.append("

    Version: ").append(DataHelper.stripHTML(info.getVersion())).append("

    \n"); - buf.append("

    Caps: ").append(DataHelper.stripHTML(info.getCapabilities())).append("

    \n"); + buf.append("

    Version: ").append(DataHelper.stripHTML(info.getVersion())).append("

    \n" + + "

    Caps: ").append(DataHelper.stripHTML(info.getCapabilities())).append("

    \n"); String kr = info.getOption("netdb.knownRouters"); ; if (kr != null) { @@ -907,8 +1003,8 @@ class SybilRenderer { } buf.append("

    ").append(_t("Signing Key")).append(": ") .append(info.getIdentity().getSigningPublicKey().getType().toString()).append("

    \n"); - buf.append("

     

    "); - buf.append("\n"); - buf.append("
    " + _t("Addresses") + ":"); + buf.append("

     

    " + + "
    " + _t("Addresses") + ":"); Collection addrs = info.getAddresses(); if (addrs.size() > 1) { // addrs is unmodifiable @@ -931,8 +1027,8 @@ class SybilRenderer { buf.append(" "); } } - buf.append("
    \n"); + buf.append("\n" + + "\n"); return distance; }