Skip to content

Commit

Permalink
add user-configurable expire-time to blocklists.
Browse files Browse the repository at this point in the history
This adds a new config setting, router.blocklist.expireInterval, which causes the blocklist entries to expire at a fixed interval.
It is off by default. It may have a value of time in milliseconds, or time with a unit, e.g. 1d, 2m, 6h, etc, or 0. 0 means off.
If configured, it runs a job when the interval is reached, which completely clears out the blocklist. The blocklist can still be
used for new entries.

The precise behavior is as follows:

 - When a router is added to the transient blocklist, it is given an IP block which expires either at the next router.blocklist.expireInterval or when the router is restarted.
 - When a router is added to the banlist, it is given an expiration of either 'forever'(0) or a delay in milliseconds, equal to router.blocklist.expireInterval

So transient blocklist entries will all expire at the same time, an event which is scheduled after the router is started and run at regular intervals.
Peers which are banned because their IP is in the blocklist, on the other hand, are scheduled to be un-banned relative to the time which they were banned at.
  • Loading branch information
idk committed Jun 11, 2023
1 parent 90aacc6 commit 5c3a650
Showing 1 changed file with 85 additions and 6 deletions.
91 changes: 85 additions & 6 deletions router/java/src/net/i2p/router/Blocklist.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Hash, String> _peerBlocklist = new HashMap<Hash, String>(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 */
Expand Down Expand Up @@ -147,6 +150,33 @@ private Blocklist() {
_singleIPv6Blocklist = _haveIPv6 ? new LHMCache<BigInteger, Object>(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
Expand Down Expand Up @@ -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);
}
}

/**
Expand Down Expand Up @@ -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<BLFile> _files;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -886,6 +954,9 @@ private List<byte[]> 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<byte[]> ips = getAddresses(peer);
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1169,7 +1242,7 @@ public BanlistJob (Hash p, List<byte[]> ips) {
}
public String getName() { return "Ban Peer by IP"; }
public void runJob() {
banlistForever(_peer, _ips);
banlistRouter(_peer, _ips, expireInterval());
synchronized (_inProcess) {
_inProcess.remove(_peer);
}
Expand All @@ -1185,7 +1258,13 @@ public void runJob() {
* So we also stagger these jobs.
*
*/
private synchronized void banlistForever(Hash peer, List<byte[]> 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<byte[]> ips, long duration) {
// This only checks one file for now, pick the best one
// user specified
File blFile = null;
Expand All @@ -1205,7 +1284,7 @@ private synchronized void banlistForever(Hash peer, List<byte[]> 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;
}

Expand Down Expand Up @@ -1236,7 +1315,7 @@ private synchronized void banlistForever(Hash peer, List<byte[]> 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;
}
}
Expand Down

0 comments on commit 5c3a650

Please sign in to comment.