From aa07775a327d94d4ece9b40144894b4640b3c480 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Fri, 11 Sep 2020 17:21:04 +0000 Subject: [PATCH] Blocklist: Move HTML status generation to console --- .../router/web/helpers/ConfigPeerHelper.java | 147 ++++++++++- router/java/src/net/i2p/router/Blocklist.java | 248 +++++++----------- 2 files changed, 233 insertions(+), 162 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigPeerHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigPeerHelper.java index 49dce3f324..3ac90c728e 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigPeerHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigPeerHelper.java @@ -1,21 +1,152 @@ package net.i2p.router.web.helpers; -import java.io.IOException; import java.io.StringWriter; +import java.math.BigInteger; +import java.util.Collections; +import java.util.List; +import net.i2p.router.Blocklist; import net.i2p.router.web.HelperBase; +import net.i2p.util.Addresses; public class ConfigPeerHelper extends HelperBase { - public ConfigPeerHelper() {} + + private static final int MAX_DISPLAY = 1000; public String getBlocklistSummary() { - StringWriter sw = new StringWriter(4*1024); - try { - _context.blocklist().renderStatusHTML(sw); - } catch (IOException ioe) { - ioe.printStackTrace(); + StringWriter out = new StringWriter(4*1024); + Blocklist bl = _context.blocklist(); + out.write("<table id=\"bannedips\"><tr><td>" + + "<table id=\"banneduntilrestart\"><tr><th align=\"center\"><b>"); + out.write(_t("IPs Banned Until Restart")); + out.write("</b></th></tr>"); + List<Integer> singles = bl.getTransientIPv4Blocks(); + List<BigInteger> s6 = bl.getTransientIPv6Blocks(); + if (!(singles.isEmpty() && s6.isEmpty())) { + if (!singles.isEmpty()) { + Collections.sort(singles); + out.write("<tr id=\"ipv4\" align=\"center\"><td><b>"); + out.write(_t("IPv4 Addresses")); + out.write("</b></td></tr>"); + } + // first 0 - 127 + for (Integer ii : singles) { + int ip = ii.intValue(); + if (ip < 0) + continue; + // don't display if on the permanent blocklist also + if (bl.isPermanentlyBlocklisted(ip)) + continue; + out.write("<tr><td align=\"center\">"); + out.write(Blocklist.toStr(ip)); + out.write("</td></tr>\n"); + } + // then 128 - 255 + for (Integer ii : singles) { + int ip = ii.intValue(); + if (ip >= 0) + break; + // don't display if on the permanent blocklist also + if (bl.isPermanentlyBlocklisted(ip)) + continue; + out.write("<tr><td align=\"center\">"); + out.write(Blocklist.toStr(ip)); + out.write("</td></tr>\n"); + } + // then IPv6 + if (!s6.isEmpty()) { + out.write("<tr id=\"ipv6\" align=\"center\"><td><b>"); + out.write(_t("IPv6 Addresses")); + out.write("</b></td></tr>"); + Collections.sort(s6); + for (BigInteger bi : s6) { + out.write("<tr><td align=\"center\">"); + out.write(Addresses.toString(toIPBytes(bi))); + out.write("</td></tr>\n"); + } + } + } else { + out.write("<tr><td><i>"); + out.write(_t("none")); + out.write("</i></td></tr>"); + } + out.write("</table>"); + out.write("</td><td>"); + out.write("<table id=\"permabanned\"><tr><th align=\"center\" colspan=\"3\"><b>"); + out.write(_t("IPs Permanently Banned")); + out.write("</b></th></tr>"); + int blocklistSize = bl.getBlocklistSize(); + if (blocklistSize > 0) { + out.write("<tr><td align=\"center\" width=\"49%\"><b>"); + out.write(_t("From")); + out.write("</b></td><td></td><td align=\"center\" width=\"49%\"><b>"); + out.write(_t("To")); + out.write("</b></td></tr>"); + long[] blocklist = bl.getPermanentBlocks(MAX_DISPLAY); + // first 0 - 127 + for (int i = 0; i < blocklist.length; i++) { + int from = Blocklist.getFrom(blocklist[i]); + if (from < 0) + continue; + out.write("<tr><td align=\"center\" width=\"49%\">"); + out.write(Blocklist.toStr(from)); + out.write("</td>"); + int to = Blocklist.getTo(blocklist[i]); + if (to != from) { + out.write("<td align=\"center\">-</td><td align=\"center\" width=\"49%\">"); + out.write(Blocklist.toStr(to)); + out.write("</td></tr>\n"); + } else { + out.write("<td></td><td width=\"49%\"> </td></tr>\n"); + } + } + // then 128 - 255 + for (int i = 0; i < blocklist.length; i++) { + int from = Blocklist.getFrom(blocklist[i]); + if (from >= 0) + break; + out.write("<tr><td align=\"center\" width=\"49%\">"); + out.write(Blocklist.toStr(from)); + out.write("</td>"); + int to = Blocklist.getTo(blocklist[i]); + if (to != from) { + out.write("<td align=\"center\">-</td><td align=\"center\" width=\"49%\">"); + out.write(Blocklist.toStr(to)); + out.write("</td></tr>\n"); + } else { + out.write("<td></td><td width=\"49%\"> </td></tr>\n"); + } + } + if (blocklistSize > MAX_DISPLAY) + // very rare, don't bother translating + out.write("<tr><th colspan=3>First " + MAX_DISPLAY + " displayed, see the " + + Blocklist.BLOCKLIST_FILE_DEFAULT + " file for the full list</th></tr>"); + } else { + out.write("<tr><td><i>"); + out.write(_t("none")); + out.write("</i></td></tr>"); } - return sw.toString(); + out.write("</table>" + + "</td></tr></table>"); + return out.toString(); + } + + /** + * Convert a (non-negative) two's complement IP to exactly 16 bytes + * + * @since IPv6, moved from Blocklist in 0.9.48 + */ + private static byte[] toIPBytes(BigInteger bi) { + byte[] ba = bi.toByteArray(); + int len = ba.length; + if (len == 16) + return ba; + byte[] rv = new byte[16]; + if (len < 16) + System.arraycopy(ba, 0, rv, 16 - len, len); + else + System.arraycopy(ba, len - 16, rv, 0, 16); + return rv; } } diff --git a/router/java/src/net/i2p/router/Blocklist.java b/router/java/src/net/i2p/router/Blocklist.java index 666867dd31..c8af3a2f23 100644 --- a/router/java/src/net/i2p/router/Blocklist.java +++ b/router/java/src/net/i2p/router/Blocklist.java @@ -88,7 +88,7 @@ public class Blocklist { private static final String PROP_BLOCKLIST_ENABLED = "router.blocklist.enable"; private static final String PROP_BLOCKLIST_DETAIL = "router.blocklist.detail"; private static final String PROP_BLOCKLIST_FILE = "router.blocklist.file"; - private static final String BLOCKLIST_FILE_DEFAULT = "blocklist.txt"; + public static final String BLOCKLIST_FILE_DEFAULT = "blocklist.txt"; private static final String BLOCKLIST_FEED_FILE = "docs/feed/blocklist/blocklist.txt"; /** @since 0.9.48 */ public static final String BLOCKLIST_COUNTRY_FILE = "blocklist-country.txt"; @@ -437,11 +437,11 @@ public class Blocklist { } int blocklistSize = count - removed; if (_log.shouldLog(Log.INFO)) { - _log.info("Merged Stats"); - _log.info("Read " + count + " total entries from the blocklists"); - _log.info("Merged " + removed + " overlapping entries"); - _log.info("Result is " + blocklistSize + " entries"); - _log.info("Blocklist processing finished, time: " + (_context.clock().now() - start)); + _log.info("Merged Stats:\n" + + "Read " + count + " total entries from the blocklists\n" + + "Merged " + removed + " overlapping entries\n" + + "Result is " + blocklistSize + " entries\n" + + "Blocklist processing finished, time: " + (_context.clock().now() - start)); } return blocklistSize; } @@ -610,8 +610,8 @@ public class Blocklist { for (int next = i + 1; next < count; next++) { if (to < getFrom(blist[next])) break; - if (_log.shouldLog(Log.WARN)) - _log.warn("Combining entries " + toStr(blist[i]) + " and " + toStr(blist[next])); + if (_log.shouldInfo()) + _log.info("Combining entries " + toStr(blist[i]) + " and " + toStr(blist[next])); int nextTo = getTo(blist[next]); if (nextTo > to) // else entry next is totally inside entry i store(getFrom(blist[i]), nextTo, blist, i); @@ -652,8 +652,8 @@ public class Blocklist { rv = add(new BigInteger(1, ip)); else rv = false; - if (rv && _log.shouldLog(Log.WARN)) - _log.warn("Adding IP to blocklist: " + Addresses.toString(ip)); + if (rv && _log.shouldInfo()) + _log.info("Adding IP to blocklist: " + Addresses.toString(ip)); } /** @@ -836,9 +836,11 @@ public class Blocklist { * The array is sorted in signed order, but we don't care. * Each long is ((from << 32) | to) * - * @since 0.9.45 split out from above + * Public for console only, not a public API + * + * @since 0.9.45 split out from above, public since 0.9.48 for console */ - private boolean isPermanentlyBlocklisted(int ip) { + public boolean isPermanentlyBlocklisted(int ip) { int hi = _blocklistSize - 1; if (hi <= 0) return false; @@ -883,11 +885,19 @@ public class Blocklist { // methods to get and store the from/to values in the array - private static int getFrom(long entry) { + /** + * Public for console only, not a public API + * @since public since 0.9.48 + */ + public static int getFrom(long entry) { return (int) ((entry >> 32) & 0xffffffff); } - private static int getTo(long entry) { + /** + * Public for console only, not a public API + * @since public since 0.9.48 + */ + public static int getTo(long entry) { return (int) (entry & 0xffffffff); } @@ -940,7 +950,11 @@ public class Blocklist { return buf.toString(); } - private static String toStr(int ip) { + /** + * Public for console only, not a public API + * @since public since 0.9.48 + */ + public static String toStr(int ip) { StringBuilder buf = new StringBuilder(16); for (int i = 3; i >= 0; i--) { buf.append((ip >> (8*i)) & 0xff); @@ -1070,149 +1084,82 @@ public class Blocklist { // We already banlisted in banlist(peer), that's good enough } - private static final int MAX_DISPLAY = 1000; + /** + * Single IPs blocked until restart. Unsorted. + * + * Public for console only, not a public API + * + * @return a copy, unsorted + * @since 0.9.48 + */ + public List<Integer> getTransientIPv4Blocks() { + return new ArrayList<Integer>(_singleIPBlocklist); + } /** - * Write directly to the stream so we don't OOM on a huge list. - * Go through each list twice since we store out-of-order. + * Single IPs blocked until restart. Unsorted. * - * TODO move to routerconsole, but that would require exposing the _blocklist array. + * Public for console only, not a public API + * + * @return a copy, unsorted + * @since 0.9.48 */ - public void renderStatusHTML(Writer out) throws IOException { - // move to the jsp - //out.write("<h2>Banned IPs</h2>"); - out.write("<table id=\"bannedips\"><tr><td>"); - out.write("<table id=\"banneduntilrestart\"><tr><th align=\"center\"><b>"); - out.write(_t("IPs Banned Until Restart")); - out.write("</b></th></tr>"); - Set<Integer> singles = new TreeSet<Integer>(); - singles.addAll(_singleIPBlocklist); - if (!(singles.isEmpty() && _singleIPv6Blocklist.isEmpty())) { - if (!singles.isEmpty()) { - out.write("<tr id=\"ipv4\" align=\"center\"><td><b>"); - out.write(_t("IPv4 Addresses")); - out.write("</b></td></tr>"); - } - // first 0 - 127 - for (Integer ii : singles) { - int ip = ii.intValue(); - if (ip < 0) - continue; - // don't display if on the permanent blocklist also - if (isPermanentlyBlocklisted(ip)) - continue; - out.write("<tr><td align=\"center\">"); - out.write(toStr(ip)); - out.write("</td></tr>\n"); - } - // then 128 - 255 - for (Integer ii : singles) { - int ip = ii.intValue(); - if (ip >= 0) - break; - // don't display if on the permanent blocklist also - if (isPermanentlyBlocklisted(ip)) - continue; - out.write("<tr><td align=\"center\">"); - out.write(toStr(ip)); - out.write("</td></tr>\n"); - } - // then IPv6 - if (!_singleIPv6Blocklist.isEmpty()) { - out.write("<tr id=\"ipv6\" align=\"center\"><td><b>"); - out.write(_t("IPv6 Addresses")); - out.write("</b></td></tr>"); - List<BigInteger> s6; - synchronized(_singleIPv6Blocklist) { - s6 = new ArrayList<BigInteger>(_singleIPv6Blocklist.keySet()); - } - Collections.sort(s6); - for (BigInteger bi : s6) { - out.write("<tr><td align=\"center\">"); - out.write(Addresses.toString(toIPBytes(bi))); - out.write("</td></tr>\n"); - } - } - } else { - out.write("<tr><td><i>"); - out.write(_t("none")); - out.write("</i></td></tr>"); + public List<BigInteger> getTransientIPv6Blocks() { + synchronized(_singleIPv6Blocklist) { + return new ArrayList<BigInteger>(_singleIPv6Blocklist.keySet()); } - out.write("</table>"); - out.write("</td><td>"); - out.write("<table id=\"permabanned\"><tr><th align=\"center\" colspan=\"3\"><b>"); - out.write(_t("IPs Permanently Banned")); - out.write("</b></th></tr>"); - if (_blocklistSize > 0) { - out.write("<tr><td align=\"center\" width=\"49%\"><b>"); - out.write(_t("From")); - out.write("</b></td><td></td><td align=\"center\" width=\"49%\"><b>"); - out.write(_t("To")); - out.write("</b></td></tr>"); - int max = Math.min(_blocklistSize, MAX_DISPLAY); - int displayed = 0; - // first 0 - 127 - for (int i = 0; i < _blocklistSize && displayed < max; i++) { - int from = getFrom(_blocklist[i]); - if (from < 0) - continue; - out.write("<tr><td align=\"center\" width=\"49%\">"); - out.write(toStr(from)); - out.write("</td>"); - int to = getTo(_blocklist[i]); - if (to != from) { - out.write("<td align=\"center\">-</td><td align=\"center\" width=\"49%\">"); - out.write(toStr(to)); - out.write("</td></tr>\n"); - } else - out.write("<td></td><td width=\"49%\"> </td></tr>\n"); - displayed++; - } - // then 128 - 255 - for (int i = 0; i < max && displayed++ < max; i++) { - int from = getFrom(_blocklist[i]); - if (from >= 0) - break; - out.write("<tr><td align=\"center\" width=\"49%\">"); - out.write(toStr(from)); - out.write("</td>"); - int to = getTo(_blocklist[i]); - if (to != from) { - out.write("<td align=\"center\">-</td><td align=\"center\" width=\"49%\">"); - out.write(toStr(to)); - out.write("</td></tr>\n"); - } else - out.write("<td></td><td width=\"49%\"> </td></tr>\n"); - } - if (_blocklistSize > MAX_DISPLAY) - // very rare, don't bother translating - out.write("<tr><th colspan=3>First " + MAX_DISPLAY + " displayed, see the " + - BLOCKLIST_FILE_DEFAULT + " file for the full list</th></tr>"); + } + + /** + * IP ranges blocked until restart. Sorted, + * but as signed longs, so 128-255 are first + * + * Public for console only, not a public API + * + * @param max maximum entries to return + * @return a copy, sorted + * @since 0.9.48 + */ + public synchronized long[] getPermanentBlocks(int max) { + long[] rv; + if (_blocklistSize <= max) { + rv = new long[_blocklistSize]; + System.arraycopy(_blocklist, 0, rv, 0, _blocklistSize); } else { - out.write("<tr><td><i>"); - out.write(_t("none")); - out.write("</i></td></tr>"); + // skip ahead to the positive entries + int i = 0; + for (; i < _blocklistSize; i++) { + int from = Blocklist.getFrom(_blocklist[i]); + if (from >= 0) + break; + } + int sz = Math.min(_blocklistSize - i, max); + rv = new long[sz]; + System.arraycopy(_blocklist, i, rv, 0, sz); } - out.write("</table>"); - out.write("</td></tr></table>"); - out.flush(); + return rv; + } + + /** + * Size of permanent blocklist + * + * Public for console only, not a public API + * + * @param max maximum entries to return + * @return sorted + * @since 0.9.48 + */ + public synchronized int getBlocklistSize() { + return _blocklistSize; } /** - * Convert a (non-negative) two's complement IP to exactly 16 bytes - * @since IPv6 + * Does nothing, moved to console ConfigPeerHelper + * + * @deprecated */ - private static byte[] toIPBytes(BigInteger bi) { - byte[] ba = bi.toByteArray(); - int len = ba.length; - if (len == 16) - return ba; - byte[] rv = new byte[16]; - if (len < 16) - System.arraycopy(ba, 0, rv, 16 - len, len); - else - System.arraycopy(ba, len - 16, rv, 0, 16); - return rv; + @Deprecated + public void renderStatusHTML(Writer out) throws IOException { } /** @@ -1225,13 +1172,6 @@ public class Blocklist { return s; } - private static final String BUNDLE_NAME = "net.i2p.router.web.messages"; - - /** translate */ - private String _t(String key) { - return Translate.getString(key, _context, BUNDLE_NAME); - } - /**** public static void main(String args[]) throws Exception { Blocklist b = new Blocklist(new Router().getContext()); -- GitLab