Blocklist: Move HTML status generation to console

This commit is contained in:
zzz
2020-09-11 17:21:04 +00:00
parent 30244f9d9b
commit aa07775a32
2 changed files with 236 additions and 165 deletions

View File

@ -1,21 +1,152 @@
package net.i2p.router.web.helpers; package net.i2p.router.web.helpers;
import java.io.IOException;
import java.io.StringWriter; 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.router.web.HelperBase;
import net.i2p.util.Addresses;
public class ConfigPeerHelper extends HelperBase { public class ConfigPeerHelper extends HelperBase {
public ConfigPeerHelper() {}
private static final int MAX_DISPLAY = 1000;
public String getBlocklistSummary() { public String getBlocklistSummary() {
StringWriter sw = new StringWriter(4*1024); StringWriter out = new StringWriter(4*1024);
try { Blocklist bl = _context.blocklist();
_context.blocklist().renderStatusHTML(sw); out.write("<table id=\"bannedips\"><tr><td>" +
} catch (IOException ioe) { "<table id=\"banneduntilrestart\"><tr><th align=\"center\"><b>");
ioe.printStackTrace(); 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>");
} }
return sw.toString(); 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%\">&nbsp;</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%\">&nbsp;</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>");
}
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;
} }
} }

View File

@ -88,7 +88,7 @@ public class Blocklist {
private static final String PROP_BLOCKLIST_ENABLED = "router.blocklist.enable"; 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_DETAIL = "router.blocklist.detail";
private static final String PROP_BLOCKLIST_FILE = "router.blocklist.file"; 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"; private static final String BLOCKLIST_FEED_FILE = "docs/feed/blocklist/blocklist.txt";
/** @since 0.9.48 */ /** @since 0.9.48 */
public static final String BLOCKLIST_COUNTRY_FILE = "blocklist-country.txt"; public static final String BLOCKLIST_COUNTRY_FILE = "blocklist-country.txt";
@ -437,11 +437,11 @@ public class Blocklist {
} }
int blocklistSize = count - removed; int blocklistSize = count - removed;
if (_log.shouldLog(Log.INFO)) { if (_log.shouldLog(Log.INFO)) {
_log.info("Merged Stats"); _log.info("Merged Stats:\n" +
_log.info("Read " + count + " total entries from the blocklists"); "Read " + count + " total entries from the blocklists\n" +
_log.info("Merged " + removed + " overlapping entries"); "Merged " + removed + " overlapping entries\n" +
_log.info("Result is " + blocklistSize + " entries"); "Result is " + blocklistSize + " entries\n" +
_log.info("Blocklist processing finished, time: " + (_context.clock().now() - start)); "Blocklist processing finished, time: " + (_context.clock().now() - start));
} }
return blocklistSize; return blocklistSize;
} }
@ -610,8 +610,8 @@ public class Blocklist {
for (int next = i + 1; next < count; next++) { for (int next = i + 1; next < count; next++) {
if (to < getFrom(blist[next])) if (to < getFrom(blist[next]))
break; break;
if (_log.shouldLog(Log.WARN)) if (_log.shouldInfo())
_log.warn("Combining entries " + toStr(blist[i]) + " and " + toStr(blist[next])); _log.info("Combining entries " + toStr(blist[i]) + " and " + toStr(blist[next]));
int nextTo = getTo(blist[next]); int nextTo = getTo(blist[next]);
if (nextTo > to) // else entry next is totally inside entry i if (nextTo > to) // else entry next is totally inside entry i
store(getFrom(blist[i]), nextTo, blist, i); store(getFrom(blist[i]), nextTo, blist, i);
@ -652,8 +652,8 @@ public class Blocklist {
rv = add(new BigInteger(1, ip)); rv = add(new BigInteger(1, ip));
else else
rv = false; rv = false;
if (rv && _log.shouldLog(Log.WARN)) if (rv && _log.shouldInfo())
_log.warn("Adding IP to blocklist: " + Addresses.toString(ip)); _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. * The array is sorted in signed order, but we don't care.
* Each long is ((from << 32) | to) * 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; int hi = _blocklistSize - 1;
if (hi <= 0) if (hi <= 0)
return false; return false;
@ -883,11 +885,19 @@ public class Blocklist {
// methods to get and store the from/to values in the array // 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); 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); return (int) (entry & 0xffffffff);
} }
@ -940,7 +950,11 @@ public class Blocklist {
return buf.toString(); 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); StringBuilder buf = new StringBuilder(16);
for (int i = 3; i >= 0; i--) { for (int i = 3; i >= 0; i--) {
buf.append((ip >> (8*i)) & 0xff); buf.append((ip >> (8*i)) & 0xff);
@ -1070,151 +1084,84 @@ public class Blocklist {
// We already banlisted in banlist(peer), that's good enough // We already banlisted in banlist(peer), that's good enough
} }
private static final int MAX_DISPLAY = 1000;
/** /**
* Write directly to the stream so we don't OOM on a huge list. * Single IPs blocked until restart. Unsorted.
* Go through each list twice since we store out-of-order.
* *
* 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 { public List<Integer> getTransientIPv4Blocks() {
// move to the jsp return new ArrayList<Integer>(_singleIPBlocklist);
//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>");
}
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%\">&nbsp;</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%\">&nbsp;</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>");
} else {
out.write("<tr><td><i>");
out.write(_t("none"));
out.write("</i></td></tr>");
}
out.write("</table>");
out.write("</td></tr></table>");
out.flush();
} }
/** /**
* Convert a (non-negative) two's complement IP to exactly 16 bytes * Single IPs blocked until restart. Unsorted.
* @since IPv6 *
* Public for console only, not a public API
*
* @return a copy, unsorted
* @since 0.9.48
*/ */
private static byte[] toIPBytes(BigInteger bi) { public List<BigInteger> getTransientIPv6Blocks() {
byte[] ba = bi.toByteArray(); synchronized(_singleIPv6Blocklist) {
int len = ba.length; return new ArrayList<BigInteger>(_singleIPv6Blocklist.keySet());
if (len == 16) }
return ba; }
byte[] rv = new byte[16];
if (len < 16) /**
System.arraycopy(ba, 0, rv, 16 - len, len); * IP ranges blocked until restart. Sorted,
else * but as signed longs, so 128-255 are first
System.arraycopy(ba, len - 16, rv, 0, 16); *
* 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 {
// 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);
}
return rv; 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;
}
/**
* Does nothing, moved to console ConfigPeerHelper
*
* @deprecated
*/
@Deprecated
public void renderStatusHTML(Writer out) throws IOException {
}
/** /**
* Mark a string for extraction by xgettext and translation. * Mark a string for extraction by xgettext and translation.
* Use this only in static initializers. * Use this only in static initializers.
@ -1225,13 +1172,6 @@ public class Blocklist {
return s; 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 { public static void main(String args[]) throws Exception {
Blocklist b = new Blocklist(new Router().getContext()); Blocklist b = new Blocklist(new Router().getContext());