Skeleton architecture of access filter

This commit is contained in:
zab2
2019-03-27 10:06:49 +00:00
parent a5e568ffa1
commit 62f7b2cece
9 changed files with 299 additions and 0 deletions

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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) {}
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}