diff --git a/router/java/src/net/i2p/router/Blocklist.java b/router/java/src/net/i2p/router/Blocklist.java index 7ed5631742..8862c8c519 100644 --- a/router/java/src/net/i2p/router/Blocklist.java +++ b/router/java/src/net/i2p/router/Blocklist.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -89,12 +90,14 @@ public class Blocklist { private final File _blocklistFeedFile; private final boolean _haveIPv6; private boolean _started; + private long _lastExpired = 0; // temp private final Map _peerBlocklist = new HashMap(4); 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 PROP_BLOCKLIST_EXPIRE_INTERVAL = "router.blocklist.expireInterval"; 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 */ @@ -147,6 +150,33 @@ private Blocklist() { _singleIPv6Blocklist = _haveIPv6 ? new LHMCache(MAX_IPV6_SINGLES) : null; } + + private int expireInterval(){ + String expireIntervalValue = _context.getProperty(PROP_BLOCKLIST_EXPIRE_INTERVAL, "0"); + try{ + if (expireIntervalValue.endsWith("s")) { + expireIntervalValue = expireIntervalValue.substring(0, expireIntervalValue.length() - 1); + return Integer.parseInt(expireIntervalValue) * 1000; + }else if(expireIntervalValue.endsWith("m")){ + expireIntervalValue = expireIntervalValue.substring(0, expireIntervalValue.length() - 1); + return Integer.parseInt(expireIntervalValue) * 60000; + }else if(expireIntervalValue.endsWith("h")){ + expireIntervalValue = expireIntervalValue.substring(0, expireIntervalValue.length() - 1); + return Integer.parseInt(expireIntervalValue) * 3600000; + }else if (expireIntervalValue.endsWith("d")) { + expireIntervalValue = expireIntervalValue.substring(0, expireIntervalValue.length() - 1); + return Integer.parseInt(expireIntervalValue) * 86400000; + }else{ + return Integer.parseInt(expireIntervalValue); + } + }catch(NumberFormatException nfe){ + if (_log.shouldLog(_log.ERROR)) + _log.error("format error in "+PROP_BLOCKLIST_EXPIRE_INTERVAL, nfe); + } + // if we don't have a valid value in this field, return 0 which is the same as disabling it. + return 0; + } + /** * Loads the following files in-order: * $I2P/blocklist.txt @@ -193,6 +223,11 @@ public synchronized void startup() { // but it's important to have this initialized before we read in the netdb. //job.getTiming().setStartAfter(_context.clock().now() + 30*1000); _context.jobQueue().addJob(job); + if (expireInterval() > 0) { + Job cleanupJob = new CleanupJob(); + cleanupJob.getTiming().setStartAfter(_context.clock().now() + expireInterval()); + _context.jobQueue().addJob(cleanupJob); + } } /** @@ -232,6 +267,32 @@ public void timeReached() { } } } + + private class CleanupJob extends JobImpl { + public CleanupJob() { + super(_context); + } + public String getName(){ + return "Expire blocklist at user-defined interval of " + expireInterval(); + } + public void runJob() { + clear(); + _lastExpired = System.currentTimeMillis(); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Expiring blocklist entrys at" + _lastExpired); + // schedule the next one + super.requeue(expireInterval()); + } + } + + private void clear(){ + synchronized(_singleIPBlocklist) { + _singleIPBlocklist.clear(); + } + synchronized(_singleIPv6Blocklist) { + _singleIPv6Blocklist.clear(); + } + } private class ReadinJob extends JobImpl { private final List _files; @@ -285,13 +346,20 @@ private int process() { reason = _x("Banned by router hash: {0}"); else reason = _x("Banned by router hash"); - _context.banlist().banlistRouterForever(peer, reason, comment); + banlistRouter(peer, reason, comment); } _peerBlocklist.clear(); return count; } } + private void banlistRouter(Hash peer, String reason, String comment) { + if (expireInterval() > 0) + _context.banlist().banlistRouter(peer, reason, comment, null, expireInterval()); + else + _context.banlist().banlistRouterForever(peer, reason, comment); + } + /** * The blocklist-country.txt file was created or updated. * Read it in. Not required normally, as the country file @@ -886,6 +954,9 @@ private List getAddresses(RouterInfo pinfo) { /** * Does the peer's IP address appear in the blocklist? * If so, and it isn't banlisted, banlist it forever... + * or, if the user configured an override, ban it for the + * override period. + * @since 0.9.29 */ public boolean isBlocklisted(Hash peer) { List ips = getAddresses(peer); @@ -905,6 +976,8 @@ public boolean isBlocklisted(Hash peer) { /** * Does the peer's IP address appear in the blocklist? * If so, and it isn't banlisted, banlist it forever... + * or, if the user configured an override, ban it for the + * override period. * @since 0.9.29 */ public boolean isBlocklisted(RouterInfo pinfo) { @@ -1141,7 +1214,7 @@ private void banlist(Hash peer, byte[] ip) { _context.clock().now() + Banlist.BANLIST_DURATION_LOCALHOST); return; } - _context.banlist().banlistRouterForever(peer, reason, sip); + banlistRouter(peer, reason, sip); if (! _context.getBooleanPropertyDefaultTrue(PROP_BLOCKLIST_DETAIL)) return; boolean shouldRunJob; @@ -1169,7 +1242,7 @@ public BanlistJob (Hash p, List ips) { } public String getName() { return "Ban Peer by IP"; } public void runJob() { - banlistForever(_peer, _ips); + banlistRouter(_peer, _ips, expireInterval()); synchronized (_inProcess) { _inProcess.remove(_peer); } @@ -1185,7 +1258,13 @@ public void runJob() { * So we also stagger these jobs. * */ - private synchronized void banlistForever(Hash peer, List ips) { + private void banlistRouter( Hash peer, String reason, String reasonCode, long duration) { + if (duration > 0) + _context.banlist().banlistRouter(peer, reason, reasonCode, null, System.currentTimeMillis()+expireInterval()); + else + _context.banlist().banlistRouterForever(peer, reason, reasonCode); + } + private synchronized void banlistRouter(Hash peer, List ips, long duration) { // This only checks one file for now, pick the best one // user specified File blFile = null; @@ -1205,7 +1284,7 @@ private synchronized void banlistForever(Hash peer, List ips) { // just ban it and be done if (_log.shouldLog(Log.WARN)) _log.warn("Banlisting " + peer); - _context.banlist().banlistRouterForever(peer, "Banned"); + banlistRouter(peer, "Banned", "Banned", expireInterval()); return; } @@ -1236,7 +1315,7 @@ private synchronized void banlistForever(Hash peer, List ips) { //reason = reason + " banned by " + BLOCKLIST_FILE_DEFAULT + " entry \"" + buf + "\""; if (_log.shouldLog(Log.WARN)) _log.warn("Banlisting " + peer + " " + reason); - _context.banlist().banlistRouterForever(peer, reason, buf.toString()); + banlistRouter(peer, reason, buf.toString(), expireInterval()); return; } }