forked from I2P_Developers/i2p.i2p
Skeleton architecture of access filter
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package net.i2p.i2ptunnel.access;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
class AccessCounter {
|
||||
|
||||
private final List<Long> accesses = new ArrayList<Long>();
|
||||
|
||||
void recordAccess(long now) {
|
||||
accesses.add(now);
|
||||
}
|
||||
|
||||
boolean isBreached(Threshold threshold) {
|
||||
if (threshold.getConnections() == 0)
|
||||
return !accesses.isEmpty();
|
||||
if (accesses.size() < threshold.getConnections())
|
||||
return false;
|
||||
|
||||
for (int i = 0; i <= accesses.size() - threshold.getConnections(); i++) {
|
||||
long start = accesses.get(i);
|
||||
long end = start + threshold.getMinutes() * 60000;
|
||||
if (accesses.get(i + threshold.getConnections() -1) <= end)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges old accesses from the list.
|
||||
* @param olderThan remove all accesses older than the given timestamp
|
||||
* @return true if there is nothing left in the access history
|
||||
*/
|
||||
boolean purge(long olderThan) {
|
||||
while(!accesses.isEmpty() && accesses.get(0) < olderThan)
|
||||
accesses.remove(0);
|
||||
return accesses.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package net.i2p.i2ptunnel.access;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.client.streaming.IncomingConnectionFilter;
|
||||
|
||||
class AccessFilter implements IncomingConnectionFilter {
|
||||
|
||||
private final FilterDefinition definition;
|
||||
private final I2PAppContext context;
|
||||
|
||||
/**
|
||||
* Trackers for known destinations defined in access lists
|
||||
*/
|
||||
private final Map<String, DestTracker> knownDests = new HashMap<String, DestTracker>();
|
||||
/**
|
||||
* Trackers for unknown destinations not defined in access lists
|
||||
*/
|
||||
private final Map<String, DestTracker> unknownDests = new HashMap<String, DestTracker>();
|
||||
|
||||
AccessFilter(I2PAppContext context, FilterDefinition definition) {
|
||||
this.context = context;
|
||||
this.definition = definition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowDestination(Destination d) {
|
||||
String b32 = d.toBase32();
|
||||
long now = context.clock().now();
|
||||
DestTracker tracker = null;
|
||||
synchronized(knownDests) {
|
||||
tracker = knownDests.get(b32);
|
||||
}
|
||||
if (tracker == null) {
|
||||
synchronized(unknownDests) {
|
||||
tracker = unknownDests.get(b32);
|
||||
if (tracker == null) {
|
||||
tracker = new DestTracker(b32, definition.getDefaultThreshold());
|
||||
unknownDests.put(b32, tracker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !tracker.recordAccess(now);
|
||||
}
|
||||
|
||||
private void reload() throws IOException {
|
||||
synchronized(knownDests) {
|
||||
for (FilterDefinitionElement element : definition.getElements())
|
||||
element.update(knownDests);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.i2p.i2ptunnel.access;
|
||||
|
||||
|
||||
class DestTracker {
|
||||
|
||||
private final String b32;
|
||||
private final Threshold threshold;
|
||||
private final AccessCounter counter;
|
||||
|
||||
DestTracker(String b32, Threshold threshold) {
|
||||
this.b32 = b32;
|
||||
this.threshold = threshold;
|
||||
this.counter = new AccessCounter();
|
||||
}
|
||||
|
||||
String getB32() {
|
||||
return b32;
|
||||
}
|
||||
|
||||
AccessCounter getCounter() {
|
||||
return counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this access causes threshold breach
|
||||
*/
|
||||
synchronized boolean recordAccess(long now) {
|
||||
counter.recordAccess(now);
|
||||
return counter.isBreached(threshold);
|
||||
}
|
||||
|
||||
synchronized boolean purge(long olderThan) {
|
||||
return counter.purge(olderThan);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package net.i2p.i2ptunnel.access;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
class ExplicitFilterDefinitionElement extends FilterDefinitionElement {
|
||||
|
||||
private final String b32;
|
||||
|
||||
ExplicitFilterDefinitionElement(String b32, Threshold threshold) {
|
||||
super(threshold);
|
||||
this.b32 = b32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Map<String, DestTracker> map) {
|
||||
if (map.containsKey(b32))
|
||||
return;
|
||||
map.put(b32, new DestTracker(b32, threshold));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.i2p.i2ptunnel.access;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
|
||||
class FileFilterDefinitionElement extends FilterDefinitionElement {
|
||||
|
||||
private final File file;
|
||||
|
||||
FileFilterDefinitionElement(File file, Threshold threshold) {
|
||||
super(threshold);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Map<String, DestTracker> map) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(file));
|
||||
try {
|
||||
String b32;
|
||||
while((b32 = reader.readLine()) != null) {
|
||||
if (map.containsKey(b32))
|
||||
continue;
|
||||
map.put(b32, new DestTracker(b32, threshold));
|
||||
}
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try { reader.close(); } catch (IOException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package net.i2p.i2ptunnel.access;
|
||||
|
||||
class FilterDefinition {
|
||||
|
||||
private final Threshold defaultThreshold;
|
||||
private final FilterDefinitionElement[] elements;
|
||||
private final Recorder[] recorders;
|
||||
private final int purgeMinutes;
|
||||
|
||||
FilterDefinition(Threshold defaultThreshold,
|
||||
FilterDefinitionElement[] elements,
|
||||
Recorder[] recorders) {
|
||||
this.defaultThreshold = defaultThreshold;
|
||||
this.elements = elements;
|
||||
this.recorders = recorders;
|
||||
|
||||
int maxMinutes = defaultThreshold.getMinutes();
|
||||
for (FilterDefinitionElement element : elements)
|
||||
maxMinutes = Math.max(maxMinutes, element.getThreshold().getMinutes());
|
||||
for (Recorder recorder : recorders)
|
||||
maxMinutes = Math.max(maxMinutes, recorder.getThreshold().getMinutes());
|
||||
|
||||
this.purgeMinutes = maxMinutes;
|
||||
}
|
||||
|
||||
Threshold getDefaultThreshold() {
|
||||
return defaultThreshold;
|
||||
}
|
||||
|
||||
FilterDefinitionElement[] getElements() {
|
||||
return elements;
|
||||
}
|
||||
|
||||
Recorder[] getRecorders() {
|
||||
return recorders;
|
||||
}
|
||||
|
||||
int getPurgeMinutes() {
|
||||
return purgeMinutes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package net.i2p.i2ptunnel.access;
|
||||
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
|
||||
abstract class FilterDefinitionElement {
|
||||
|
||||
protected final Threshold threshold;
|
||||
|
||||
FilterDefinitionElement(Threshold threshold) {
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
abstract void update(Map<String, DestTracker> map) throws IOException;
|
||||
|
||||
Threshold getThreshold() {
|
||||
return threshold;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package net.i2p.i2ptunnel.access;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
class Recorder {
|
||||
|
||||
private final File file;
|
||||
private final Threshold threshold;
|
||||
|
||||
Recorder(File file, Threshold threshold) {
|
||||
this.file = file;
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
Threshold getThreshold() {
|
||||
return threshold;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.i2p.i2ptunnel.access;
|
||||
|
||||
class Threshold {
|
||||
|
||||
static final Threshold ALLOW = new Threshold(Integer.MAX_VALUE, 1);
|
||||
static final Threshold DENY = new Threshold(0, 1);
|
||||
|
||||
private final int connections;
|
||||
private final int minutes;
|
||||
|
||||
Threshold(int connections, int minutes) {
|
||||
if (minutes < 1)
|
||||
throw new IllegalArgumentException("Threshold must be defined over at least 1 minute");
|
||||
if (connections < 0)
|
||||
throw new IllegalArgumentException("Accesses cannot be negative");
|
||||
this.connections = connections;
|
||||
this.minutes = minutes;
|
||||
}
|
||||
|
||||
int getConnections() {
|
||||
return connections;
|
||||
}
|
||||
|
||||
int getMinutes() {
|
||||
return minutes;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user