Compare commits

...

2 Commits

Author SHA1 Message Date
idk
9982e3d470 treat negative expireInterval values as 0-or-disabled 2023-06-16 16:10:30 -04:00
idk
5c3a6509db add user-configurable expire-time to blocklists.
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.
2023-06-10 22:56:21 -04:00

View File

@ -17,6 +17,7 @@ import java.net.UnknownHostException;
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<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 */
@ -147,6 +150,37 @@ public class Blocklist {
_singleIPv6Blocklist = _haveIPv6 ? new LHMCache<BigInteger, Object>(MAX_IPV6_SINGLES) : null;
}
private int expireInterval(){
String expireIntervalValue = _context.getProperty(PROP_BLOCKLIST_EXPIRE_INTERVAL, "0");
try{
Integer expireIntervalInt = 0;
if (expireIntervalValue.endsWith("s")) {
expireIntervalValue = expireIntervalValue.substring(0, expireIntervalValue.length() - 1);
expireIntervalInt = Integer.parseInt(expireIntervalValue) * 1000;
}else if(expireIntervalValue.endsWith("m")){
expireIntervalValue = expireIntervalValue.substring(0, expireIntervalValue.length() - 1);
expireIntervalInt = Integer.parseInt(expireIntervalValue) * 60000;
}else if(expireIntervalValue.endsWith("h")){
expireIntervalValue = expireIntervalValue.substring(0, expireIntervalValue.length() - 1);
expireIntervalInt = Integer.parseInt(expireIntervalValue) * 3600000;
}else if (expireIntervalValue.endsWith("d")) {
expireIntervalValue = expireIntervalValue.substring(0, expireIntervalValue.length() - 1);
expireIntervalInt = Integer.parseInt(expireIntervalValue) * 86400000;
}else{
expireIntervalInt = Integer.parseInt(expireIntervalValue);
}
if (expireIntervalInt < 0)
expireIntervalInt = 0;
return expireIntervalInt;
}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 +227,11 @@ public class Blocklist {
// 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 +271,32 @@ public class Blocklist {
}
}
}
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;
@ -285,13 +350,20 @@ public class Blocklist {
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 +958,9 @@ public class Blocklist {
/**
* 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);
@ -905,6 +980,8 @@ public class Blocklist {
/**
* 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 +1218,7 @@ public class Blocklist {
_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 +1246,7 @@ public class Blocklist {
}
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 +1262,13 @@ public class Blocklist {
* 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;
@ -1205,7 +1288,7 @@ public class Blocklist {
// 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 +1319,7 @@ public class Blocklist {
//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;
}
}