Opened 7 months ago

Closed 7 months ago

#2464 closed enhancement (fixed)

Automatic blacklist management for server tunnels

Reported by: Zlatin Balevsky Owned by:
Priority: minor Milestone: undecided
Component: streaming Version: 0.9.38
Keywords: Cc:
Parent Tickets: Sensitive: no

Description

User should be able to specify thresholds of requests from any given .b32 and if they are breached the .b32 should be automatically added to a blacklist.

Subtickets

Change History (16)

comment:1 Changed 7 months ago by Zlatin Balevsky

I came up with the following DSL which is a simplified version of the DSLs for various firewalls and should not be difficult to implement;

# Default connection limit.  The large value is equivalent to always allow
default 1000000/1 

# explicit blocks, overrides any following rule.  The zero means allow zero connections, i.e. block
ConnRate 0/1 explicit asdfasf.b32

# explicit allows, overrides any following rule
ConnRate 1000000/1 explicit fdsafdsa.b32

# apply custom connection rate to the .b32s listed in the given file
ConnRate 15/5 file /path/limited.txt

# If the specified threshold is exceeded, record the .b32 in the specified file
# So you can add a rule for that file later
Record 60/10 /path/suspicious.txt

comment:2 Changed 7 months ago by zzz

Component: router/generalstreaming

DSL = Domain-Specific Language

The per-tunnel conn limits and blacklists are implemented in streaming, with the GUI in i2ptunnel.
The global streaming blacklist is stored in a router.config property and is only visible in the advanced config GUI.

A large global blacklist would exceed the char limits in the router.config file, so it would have to be moved to a new persistence mechanism. Streaming can't make changes to router.config anyway.
A large per-tunnel blacklist would exceed the char limits in the i2ptunnel.config file, so it would have to be moved to a new persistence mechanism.
Probably would want them in separate files anyway, if they're being automatically updated on the fly.
This all would need to be client-side in any case, as clients could be outside the JVM.

A GUI for this may be complex… or could configuration be done solely through i2pcontrol? Or does the "DSL" go in a file somewhere and that's how you do it?

comment:3 Changed 7 months ago by Zlatin Balevsky

GUI-wise I have in mind a 4th option in the "access control" bit of server tunnel config screen: "Disabled", "Whitelist", "Blacklist" and "Advanced". If the user chooses Advanced then they enter the DSL in the text box. The resulting text can be stored in a separate per-tunnel file.

The storage of the actual b32s will most likely end up in multiple files, which can be shared between tunnels. (I am thinking of using the Java 7 feature for watching updates to those files and reload the access lists on the fly.)

One explicit goal of this design is to allow one tunnel to add b32s to a blacklist that another tunnel (but not necessarily every other tunnel) will block. That can be achieved with the "Record" keyword from the example above:

Tunnel A:
Record 60/10 /path/suspicious.txt
Tunnel B:
ConnRate 0/1 file /path/suspicious.txt

Another explicit goal is to enable different connection rates for different sets of b32s, for example

# some reasonable default
default 15/5
# block enemies
ConnRate 0/1 file /path/enemies.txt
# no limits for friends
ConnRate 1000000/1 /path/friends.txt
# higher than default limits for known crawlers
ConnRatte 50/5 file /path/crawlers.txt

comment:4 Changed 7 months ago by zzz

per-far-end-dest (or per-socket for certain dests?) conn limits would be difficult, and goes well beyond "blacklist management" of the ticket title. We have no infrastructure at all for that in streaming.

sharing between tunnels (configured I guess, not global?) would also take some work.

the feature of watching files for changes is not easy either. I have a whole branch devoted to using that feature and I haven't been able to get it working reliably after a couple years of trying.

comment:5 Changed 7 months ago by Zlatin Balevsky

I was thinking the management would be done entirely in i2ptunnel, after the streaming connection has been established. I would intercept the incoming connection and check the access lists before connecting to the i2ptunnel destination. No changes in streaming.

That means each server tunnel has an instance of an access controller which watches the DSL descriptor as well as any files referenced there. It keeps track of incoming connection timestamps per Destination and simply closes new connections once the defined threshold is exceeded.

I haven't used the NIO2 api myself but I've seen it used in production, and am willing to give it a crack. If all else fails the necessary files can be reloaded on a timer, none of this is time-critical.

comment:6 Changed 7 months ago by Zlatin Balevsky

I was thinking the management would be done entirely in i2ptunnel, after the streaming connection has been established.

Upon further investigation there is nothing preventing us from intercepting the connection in the streaming lib and still having all access list management be implemented in i2ptunnel. To achieve that the following interface can be introduced in the ministreaming module:

interface IncomingConnectionFilter {
    public boolean allowDestination(Destination d);
}

The actual implementation(s) of this interface will be in the i2ptunnel module and will take care of all rate limiting, persisting b32s to disk and so on.

When I2PTunnelServer creates its I2PSocketManager, it will provide a reference to an implementation of IncomingConnectionFilter and that in turn will be passed to ConnectionManager, so that filtering can be done on the first SYN packet. This can coexist with the current rate limiting code.

comment:7 Changed 7 months ago by zzz

OK so we're still talking about connection rate limiting, not bandwidth limiting. In comment 4 I got confused and was talking about outbound bandwidth limiting, which we don't do at all.

Yes the actual blocking needs to be in streaming, where it is now. You really don't want streaming to ACK the connection and then have i2ptunnel close it.

A callback to i2ptunnel for filtering is interesting way to do it. But what about the global list? That would still be in streaming? How is that managed?

My file notification branch is i2p.i2p.zzz.dirmon. Look for core/java/src/net/i2p/app/FileMonitorService.java and router/java/src/net/i2p/router/tasks/FileMonitor.java; we can discuss why I'm stuck.

So the blacklist entries are permanent, they don't expire?

comment:8 Changed 7 months ago by Zlatin Balevsky

But what about the global list? That would still be in streaming? How is that managed?

The current logic and rate limiting in streaming needs to stay for backward compatibility. I don't think any changes are required to the global blacklist, that always has precedence over any i2ptunnel access lists.

So the blacklist entries are permanent, they don't expire?

As this is going to be advanced functionality, I think (for the first iteration at least) they should not expire. It is possible to make the files easy to edit by the user by making them csv format <b32, date> with the date when a destination was added to the access list.

comment:9 Changed 7 months ago by Zlatin Balevsky

Below are proposed patches to "ministreaming" and "streaming" modules. They compile, but I haven't tested them yet.

The IncomingConnectionFilter class:

package net.i2p.client.streaming;

import net.i2p.data.Destination;

/**
 * Something that filters incoming streaming connections.
 * @since 0.9.40
 */
public interface IncomingConnectionFilter {

    /**
     * @param d the destination that wants to establish an
     * incoming connection
     * @return true if the connection should be allowed.
     */
    public boolean allowDestination(Destination d);

    /**
     * Utility implementation that allows all incoming connections
     */
    public static final IncomingConnectionFilter ALLOW = 
        new IncomingConnectionFilter() {
            public boolean allowDestination(Destination d) {
                return true;
            }
        };

    /**
     * Utility implementation that denies all incoming connections
     */
    public static final IncomingConnectionFilter DENY = 
        new IncomingConnectionFilter() {
            public boolean allowDestination(Destination d) {
                return false;
            }
        };
}
#
# old_revision [a0def2edfc7337694ee0cff37df2fb1ebb71e479]
#
# patch "apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java"
#  from [3a09f93cdd9131fb9b8ac28f5d4f1d4cfb3c6d37]
#    to [d50b0ebaaaeae356efa1695c0245db475ba20352]
# 
# patch "apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionManager.java"
#  from [69390bb6ae3db0cf74ea265546f59c11217779f3]
#    to [432913c1d5a58803c4c8794dc0b805255d35b4f3]
# 
# patch "apps/streaming/java/src/net/i2p/client/streaming/impl/I2PSocketManagerFull.java"
#  from [8a204d9c4769d7d159998c82ab502d70111b18d3]
#    to [45797a03bbd210aad01c2410d68b1241a8d4b505]
#
============================================================
--- apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java	3a09f93cdd9131fb9b8ac28f5d4f1d4cfb3c6d37
+++ apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java	d50b0ebaaaeae356efa1695c0245db475ba20352
@@ -55,8 +55,25 @@ public class I2PSocketManagerFactory {
      * @return the newly created socket manager, or null if there were errors
      */
     public static I2PSocketManager createManager() {
-        return createManager(getHost(), getPort(), (Properties) System.getProperties().clone());
+        return createManager(getHost(), getPort(), (Properties) System.getProperties().clone(), 
+                    IncomingConnectionFilter.ALLOW);
     }
+
+    /**
+     * Create a socket manager using a brand new destination connected to the
+     * I2CP router on the local machine on the default port (7654) with the
+     * specified incoming connection filter.
+     * 
+     * Blocks for a long time while the router builds tunnels.
+     * The nonblocking createDisconnectedManager() is preferred.
+     * 
+     * @since 0.9.40
+     * @param filter The filter for incoming connections
+     * @return the newly created socket manager, or null if there were errors
+     */
+    public static I2PSocketManager createManager(IncomingConnectionFilter filter) {
+        return createManager(getHost(), getPort(), (Properties) System.getProperties().clone(), filter);
+    }
     
     /**
      * Create a socket manager using a brand new destination connected to the
@@ -69,11 +86,27 @@ public class I2PSocketManagerFactory {
      * @return the newly created socket manager, or null if there were errors
      */
     public static I2PSocketManager createManager(Properties opts) {
-        return createManager(getHost(), getPort(), opts);
+        return createManager(getHost(), getPort(), opts, IncomingConnectionFilter.ALLOW);
     }
 
     /**
      * Create a socket manager using a brand new destination connected to the
+     * I2CP router on the local machine on the default port (7654).
+     *
+     * Blocks for a long time while the router builds tunnels.
+     * The nonblocking createDisconnectedManager() is preferred.
+     *
+     * @since 0.9.40
+     * @param opts Streaming and I2CP options, may be null
+     * @param filter The filter to use for incoming connections
+     * @return the newly created socket manager, or null if there were errors
+     */
+    public static I2PSocketManager createManager(Properties opts, IncomingConnectionFilter filter) {
+        return createManager(getHost(), getPort(), opts, filter);
+    }
+
+    /**
+     * Create a socket manager using a brand new destination connected to the
      * I2CP router on the specified host and port.
      * 
      * Blocks for a long time while the router builds tunnels.
@@ -84,11 +117,28 @@ public class I2PSocketManagerFactory {
      * @return the newly created socket manager, or null if there were errors
      */
     public static I2PSocketManager createManager(String host, int port) {
-        return createManager(host, port, (Properties) System.getProperties().clone());
+        return createManager(host, port, (Properties) System.getProperties().clone(),
+                      IncomingConnectionFilter.ALLOW);
     }
 
     /**
      * Create a socket manager using a brand new destination connected to the
+     * I2CP router on the specified host and port with the specified connection filter
+     * 
+     * Blocks for a long time while the router builds tunnels.
+     * The nonblocking createDisconnectedManager() is preferred.
+     * 
+     * @param host I2CP host null to use default, ignored if in router context
+     * @param port I2CP port &lt;= 0 to use default, ignored if in router context
+     * @param filter The filter to use for incoming connections
+     * @return the newly created socket manager, or null if there were errors
+     */
+    public static I2PSocketManager createManager(String host, int port, IncomingConnectionFilter filter) {
+        return createManager(host, port, (Properties) System.getProperties().clone(), filter);
+    }
+    
+    /**
+     * Create a socket manager using a brand new destination connected to the
      * I2CP router on the given machine reachable through the given port.
      * 
      * Blocks for a long time while the router builds tunnels.
@@ -100,12 +150,32 @@ public class I2PSocketManagerFactory {
      * @return the newly created socket manager, or null if there were errors
      */
     public static I2PSocketManager createManager(String i2cpHost, int i2cpPort, Properties opts) {
+        return createManager(i2cpHost, i2cpPort, opts, IncomingConnectionFilter.ALLOW);
+    }
+
+    /**
+     * Create a socket manager using a brand new destination connected to the
+     * I2CP router on the given machine reachable through the given port with 
+     * the specified connection filter
+     * 
+     * Blocks for a long time while the router builds tunnels.
+     * The nonblocking createDisconnectedManager() is preferred.
+     *
+     * @since 0.9.40
+     * @param i2cpHost I2CP host null to use default, ignored if in router context
+     * @param i2cpPort I2CP port &lt;= 0 to use default, ignored if in router context
+     * @param opts Streaming and I2CP options, may be null
+     * @param filter The filter to use for incoming connections
+     * @return the newly created socket manager, or null if there were errors
+     */
+    public static I2PSocketManager createManager(String i2cpHost, int i2cpPort, Properties opts,
+                    IncomingConnectionFilter filter) {
         I2PClient client = I2PClientFactory.createClient();
         ByteArrayOutputStream keyStream = new ByteArrayOutputStream(1024);
         try {
             client.createDestination(keyStream, getSigType(opts));
             ByteArrayInputStream in = new ByteArrayInputStream(keyStream.toByteArray());
-            return createManager(in, i2cpHost, i2cpPort, opts);
+            return createManager(in, i2cpHost, i2cpPort, opts, filter);
         } catch (IOException ioe) {
             getLog().error("Error creating the destination for socket manager", ioe);
             return null;
@@ -127,8 +197,29 @@ public class I2PSocketManagerFactory {
      * @return the newly created socket manager, or null if there were errors
      */
     public static I2PSocketManager createManager(InputStream myPrivateKeyStream) {
-        return createManager(myPrivateKeyStream, getHost(), getPort(), (Properties) System.getProperties().clone());
+        return createManager(myPrivateKeyStream, IncomingConnectionFilter.ALLOW);
     }
+
+    /**
+     * Create a socket manager using the destination loaded from the given private key
+     * stream and connected to the default I2CP host and port with the specified connection filter
+     * 
+     * Blocks for a long time while the router builds tunnels.
+     * The nonblocking createDisconnectedManager() is preferred.
+     *
+     * @since 0.9.40
+     * @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
+     *                           or null for a transient destination. Caller must close.
+     * @param filter The filter to use for incoming connections
+     * @return the newly created socket manager, or null if there were errors
+     */
+    public static I2PSocketManager createManager(InputStream myPrivateKeyStream, 
+                    IncomingConnectionFilter filter) {
+        return createManager(myPrivateKeyStream, getHost(), getPort(), 
+                             (Properties) System.getProperties().clone(),
+                             filter);
+
+    }
     
     /**
      * Create a socket manager using the destination loaded from the given private key
@@ -143,8 +234,27 @@ public class I2PSocketManagerFactory {
      * @return the newly created socket manager, or null if there were errors
      */
     public static I2PSocketManager createManager(InputStream myPrivateKeyStream, Properties opts) {
-        return createManager(myPrivateKeyStream, getHost(), getPort(), opts);
+        return createManager(myPrivateKeyStream, opts, IncomingConnectionFilter.ALLOW);
     }
+
+    /**
+     * Create a socket manager using the destination loaded from the given private key
+     * stream and connected to the default I2CP host and port.
+     * 
+     * Blocks for a long time while the router builds tunnels.
+     * The nonblocking createDisconnectedManager() is preferred.
+     *
+     * @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
+     *                           or null for a transient destination. Caller must close.
+     * @param opts Streaming and I2CP options, may be null
+     * @param filter The filter to use for incoming connections
+     * @return the newly created socket manager, or null if there were errors
+     */
+    public static I2PSocketManager createManager(InputStream myPrivateKeyStream,
+		                                 Properties opts,
+                                                 IncomingConnectionFilter filter) {
+        return createManager(myPrivateKeyStream, getHost(), getPort(), opts, filter);
+    }
     
     /**
      * Create a socket manager using the destination loaded from the given private key
@@ -163,14 +273,38 @@ public class I2PSocketManagerFactory {
      */
     public static I2PSocketManager createManager(InputStream myPrivateKeyStream, String i2cpHost, int i2cpPort,
                                                  Properties opts) {
+         return createManager(myPrivateKeyStream, i2cpHost, i2cpPort, opts, IncomingConnectionFilter.ALLOW);
+    }
+
+    /**
+     * Create a socket manager using the destination loaded from the given private key
+     * stream and connected to the I2CP router on the specified machine on the given
+     * port.
+     * 
+     * Blocks for a long time while the router builds tunnels.
+     * The nonblocking createDisconnectedManager() is preferred.
+     *
+     * @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
+     *                           or null for a transient destination. Caller must close.
+     * @param i2cpHost I2CP host null to use default, ignored if in router context
+     * @param i2cpPort I2CP port &lt;= 0 to use default, ignored if in router context
+     * @param opts Streaming and I2CP options, may be null
+     * @param filter The filter to use for incoming connections
+     * @return the newly created socket manager, or null if there were errors
+     */
+    public static I2PSocketManager createManager(InputStream myPrivateKeyStream,
+		                                 String i2cpHost,
+                                                 int i2cpPort,
+                                                 Properties opts,
+                                                 IncomingConnectionFilter filter) {
         try {
-            return createManager(myPrivateKeyStream, i2cpHost, i2cpPort, opts, true);
+            return createManager(myPrivateKeyStream, i2cpHost, i2cpPort, opts, true, filter);
         } catch (I2PSessionException ise) {
             getLog().error("Error creating session for socket manager", ise);
             return null;
         }
-    }
-    
+    }	    
+
     /**
      * Create a disconnected socket manager using the destination loaded from the given private key
      * stream, or null for a transient destination.
@@ -191,6 +325,38 @@ public class I2PSocketManagerFactory {
      */
     public static I2PSocketManager createDisconnectedManager(InputStream myPrivateKeyStream, String i2cpHost,
                                                              int i2cpPort, Properties opts) throws I2PSessionException {
+        return createDisconnectedManager(myPrivateKeyStream,
+                                         i2cpHost,
+                                         i2cpPort,
+                                         opts,
+                                         IncomingConnectionFilter.ALLOW);	 
+    }
+    
+    /**
+     * Create a disconnected socket manager using the destination loaded from the given private key
+     * stream, or null for a transient destination.
+     * 
+     * Non-blocking. Does not connect to the router or build tunnels.
+     * For servers, caller MUST call getSession().connect() to build tunnels and start listening.
+     * For clients, caller may do that to build tunnels in advance;
+     * otherwise, the first call to connect() will initiate a connection to the router,
+     * with significant delay for tunnel building.
+     *
+     * @param myPrivateKeyStream private key stream, format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
+     *                           or null for a transient destination. Caller must close.
+     * @param i2cpHost I2CP host null to use default, ignored if in router context
+     * @param i2cpPort I2CP port &lt;= 0 to use default, ignored if in router context
+     * @param opts Streaming and I2CP options, may be null
+     * @param filter The filter to use for incoming connections
+     * @return the newly created socket manager, non-null (throws on error)
+     * @since 0.9.40
+     */
+    public static I2PSocketManager createDisconnectedManager(InputStream myPrivateKeyStream,
+                                                             String i2cpHost,
+                                                             int i2cpPort,
+                                                             Properties opts,
+                                                             IncomingConnectionFilter filter) 
+                                    throws I2PSessionException {
         if (myPrivateKeyStream == null) {
             I2PClient client = I2PClientFactory.createClient();
             ByteArrayOutputStream keyStream = new ByteArrayOutputStream(1024);
@@ -203,9 +369,9 @@ public class I2PSocketManagerFactory {
             }
             myPrivateKeyStream = new ByteArrayInputStream(keyStream.toByteArray());
         }
-        return createManager(myPrivateKeyStream, i2cpHost, i2cpPort, opts, false);
+        return createManager(myPrivateKeyStream, i2cpHost, i2cpPort, opts, false, filter);
     }
-    
+
     /**
      * Create a socket manager using the destination loaded from the given private key
      * stream and connected to the I2CP router on the specified machine on the given
@@ -219,11 +385,13 @@ public class I2PSocketManagerFactory {
      * @param i2cpPort I2CP port &lt;= 0 to use default, ignored if in router context
      * @param opts Streaming and I2CP options, may be null
      * @param connect true to connect (blocking)
+     * @param filter The filter to use for incoming connections
      * @return the newly created socket manager, non-null (throws on error)
-     * @since 0.9.7
+     * @since 0.9.40
      */
     private static I2PSocketManager createManager(InputStream myPrivateKeyStream, String i2cpHost, int i2cpPort,
-                                                 Properties opts, boolean connect) throws I2PSessionException {
+                                                 Properties opts, boolean connect,
+                                                 IncomingConnectionFilter filter) throws I2PSessionException {
         I2PClient client = I2PClientFactory.createClient();
         if (opts == null)
             opts = new Properties();
@@ -245,11 +413,12 @@ public class I2PSocketManagerFactory {
         I2PSession session = client.createSession(myPrivateKeyStream, opts);
         if (connect)
             session.connect();
-        I2PSocketManager sockMgr = createManager(session, opts, "manager");
+        I2PSocketManager sockMgr = createManager(session, opts, "manager", filter);
         return sockMgr;
     }
 
-    private static I2PSocketManager createManager(I2PSession session, Properties opts, String name) {
+    private static I2PSocketManager createManager(I2PSession session, Properties opts, String name,
+                                                  IncomingConnectionFilter filter) {
         I2PAppContext context = I2PAppContext.getGlobalContext();
         // As of 0.9.12, ignore this setting, as jwebcache and i2phex set it to the old value.
         // There is no other valid manager.
@@ -260,8 +429,13 @@ public class I2PSocketManagerFactory {
             if (!I2PSocketManager.class.isAssignableFrom(cls))
                 throw new IllegalArgumentException(classname + " is not an I2PSocketManager");
             Constructor<?> con =
-                  cls.getConstructor(I2PAppContext.class, I2PSession.class, Properties.class, String.class);
-            I2PSocketManager mgr = (I2PSocketManager) con.newInstance(new Object[] {context, session, opts, name});
+                  cls.getConstructor(I2PAppContext.class, 
+                                     I2PSession.class,
+                                     Properties.class,
+                                     String.class,
+                                     IncomingConnectionFilter.class);
+            I2PSocketManager mgr = (I2PSocketManager) con.newInstance(
+                                   new Object[] {context, session, opts, name, filter});
             return mgr;
         } catch (Throwable t) {
             getLog().log(Log.CRIT, "Error loading " + classname, t);
============================================================
--- apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionManager.java	69390bb6ae3db0cf74ea265546f59c11217779f3
+++ apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionManager.java	432913c1d5a58803c4c8794dc0b805255d35b4f3
@@ -21,6 +21,7 @@ import net.i2p.util.SimpleTimer2;
 import net.i2p.util.LHMCache;
 import net.i2p.util.Log;
 import net.i2p.util.SimpleTimer2;
+import net.i2p.client.streaming.IncomingConnectionFilter;
 
 /**
  * Coordinate all of the connections for a single local destination.
@@ -38,6 +39,7 @@ class ConnectionManager {
     private final SchedulerChooser _schedulerChooser;
     private final ConnectionPacketHandler _conPacketHandler;
     private final TCBShare _tcbShare;
+    private final IncomingConnectionFilter _connectionFilter;
     /** Inbound stream ID (Long) to Connection map */
     private final ConcurrentHashMap<Long, Connection> _connectionByInboundId;
     /** Ping ID (Long) to PingRequest */
@@ -81,10 +83,14 @@ class ConnectionManager {
     /**
      *  Manage all conns for this session
      */
-    public ConnectionManager(I2PAppContext context, I2PSession session, ConnectionOptions defaultOptions) {
+    public ConnectionManager(I2PAppContext context, 
+                             I2PSession session, 
+                             ConnectionOptions defaultOptions,
+                             IncomingConnectionFilter connectionFilter) {
         _context = context;
         _session = session;
         _defaultOptions = defaultOptions;
+        _connectionFilter = connectionFilter;
         _log = _context.logManager().getLog(ConnectionManager.class);
         _connectionByInboundId = new ConcurrentHashMap<Long,Connection>(32);
         _pendingPings = new ConcurrentHashMap<Long,PingRequest>(4);
@@ -655,6 +661,10 @@ class ConnectionManager {
                         " per minute";
         }
 
+        if (!_connectionFilter.allowDestination(from)) {
+            return "not allowed by filter";
+        }
+
         return null;
     }
 
============================================================
--- apps/streaming/java/src/net/i2p/client/streaming/impl/I2PSocketManagerFull.java	8a204d9c4769d7d159998c82ab502d70111b18d3
+++ apps/streaming/java/src/net/i2p/client/streaming/impl/I2PSocketManagerFull.java	45797a03bbd210aad01c2410d68b1241a8d4b505
@@ -28,6 +28,7 @@ import net.i2p.client.streaming.I2PSocke
 import net.i2p.client.streaming.I2PSocket;
 import net.i2p.client.streaming.I2PSocketManager;
 import net.i2p.client.streaming.I2PSocketOptions;
+import net.i2p.client.streaming.IncomingConnectionFilter;
 import net.i2p.crypto.SigAlgo;
 import net.i2p.crypto.SigType;
 import net.i2p.data.Certificate;
@@ -191,7 +192,8 @@ public class I2PSocketManagerFull implem
      * @param opts may be null
      * @param name non-null
      */
-    public I2PSocketManagerFull(I2PAppContext context, I2PSession session, Properties opts, String name) {
+    public I2PSocketManagerFull(I2PAppContext context, I2PSession session, Properties opts, String name,
+                IncomingConnectionFilter connectionFilter) {
         _context = context;
         _session = session;
         _subsessions = new ConcurrentHashSet<I2PSession>(4);
@@ -200,7 +202,7 @@ public class I2PSocketManagerFull implem
         _name = name + " " + (__managerId.incrementAndGet());
         _acceptTimeout = ACCEPT_TIMEOUT_DEFAULT;
         _defaultOptions = new ConnectionOptions(opts);
-        _connectionManager = new ConnectionManager(_context, _session, _defaultOptions);
+        _connectionManager = new ConnectionManager(_context, _session, _defaultOptions, connectionFilter);
         _serverSocket = new I2PServerSocketFull(this);
         
         if (_log.shouldLog(Log.INFO)) {

comment:10 Changed 7 months ago by zzz

maybe adding a setter to I2PSocketManager or I2PServerSocket would be cleaner? Probably not I2PServerSocket, too late…

e.g.

public void setIncomingConnectionFilter(IncomingConnectionFilter? filter) { … }

comment:11 Changed 7 months ago by zzz

Why can't the "DSL" just be more properties like foo=bar that go in the custom options at the bottom? Supporting a new configuration format that gets put in the access list box is going to be a big mess to implement in i2ptunnel.

comment:12 Changed 7 months ago by Zlatin Balevsky

maybe adding a setter to I2PSocketManager or I2PServerSocket would be cleaner? Probably not I2PServerSocket, too late…

Two reasons:

  1. I'm not fan of null checks, but this is minor
  2. The setter would have to be at the I2PSocketManager which then delegates it to ConnectionManager.

IMO this approach is not as clean, even though the diff would be smaller.

comment:13 Changed 7 months ago by Zlatin Balevsky

Why can't the "DSL" just be more properties like foo=bar

It sure can, but I think it would be significantly harder to express more complicated access list rules this way. How do you envision the DSL snippets in comment 3 would look in a foo=bar format?

Supporting a new configuration format that gets put in the access list box is going to be a big mess to implement in i2ptunnel.

I've given some thought to this and don't think it would be too bad at all. There would be an extra radio button "Advanced". If that button is checked, a single new property is added to i2ptunnel.config: tunnnel.N.accessConfig=tunellName.dsl where the "tunnelName.dsl" file contains the DSL.

comment:14 Changed 7 months ago by zzz

re: setter vs. new arg in factory methods, fine if you like the new arg better.

re: DSL - So the config that gets pasted into the access list field is going to be stored in a separate config file, not in i2ptunnel.config? Yup, you said that in comment 3.

And then there will be multiple new config files that contains the blacklist entries themselves, format TBD? e.g. that's what enemies.txt, friends.txt, crawlers.txt are in comment 3?

If you were going to keep the DSL config in i2ptunnel.config properties, then properties is obviously a lot easier. Even if in a separate file, we have a full set of utilities to load and store config files in properties format, so that's another reason.

A possible example of property format, not pretty but would work:

# some reasonable default
default=15/5
# block enemies
ConnRate.1=0/1,file,/path/enemies.txt
# no limits for friends
ConnRate.2=1000000/1,/path/friends.txt
# higher than default limits for known crawlers
ConnRatte.350/5,file,/path/crawlers.txt

comment:15 Changed 7 months ago by Zlatin Balevsky

Work started on branch i2p.i2p.zab.2464

comment:16 Changed 7 months ago by Zlatin Balevsky

Resolution: fixed
Status: newclosed
Note: See TracTickets for help on using tickets.