From a5e568ffa1d18342ea797d8a5416f0e2e15ac57c Mon Sep 17 00:00:00 2001 From: zab2 Date: Tue, 26 Mar 2019 15:34:15 +0000 Subject: [PATCH] Hooks into streaming for filtering of incoming connections --- .../streaming/I2PSocketManagerFactory.java | 208 ++++++++++++++++-- .../streaming/IncomingConnectionFilter.java | 37 ++++ .../streaming/impl/ConnectionManager.java | 12 +- .../streaming/impl/I2PSocketManagerFull.java | 6 +- 4 files changed, 243 insertions(+), 20 deletions(-) create mode 100644 apps/ministreaming/java/src/net/i2p/client/streaming/IncomingConnectionFilter.java diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java index 2f619e9390..26ee57c437 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java @@ -55,7 +55,24 @@ 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); } /** @@ -69,7 +86,23 @@ 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); } /** @@ -84,9 +117,26 @@ 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 <= 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. @@ -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 <= 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,7 +197,28 @@ 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); + } /** @@ -143,7 +234,26 @@ 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); } /** @@ -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 <= 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 <= 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 <= 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); diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/IncomingConnectionFilter.java b/apps/ministreaming/java/src/net/i2p/client/streaming/IncomingConnectionFilter.java new file mode 100644 index 0000000000..e80a21a55a --- /dev/null +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/IncomingConnectionFilter.java @@ -0,0 +1,37 @@ +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; + } + }; +} diff --git a/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionManager.java b/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionManager.java index 42cece4f5a..dae65c7038 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionManager.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionManager.java @@ -21,6 +21,7 @@ import net.i2p.util.ConvertToHash; 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 _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(32); _pendingPings = new ConcurrentHashMap(4); @@ -655,6 +661,10 @@ class ConnectionManager { " per minute"; } + if (!_connectionFilter.allowDestination(from)) { + return "not allowed by filter"; + } + return null; } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/impl/I2PSocketManagerFull.java b/apps/streaming/java/src/net/i2p/client/streaming/impl/I2PSocketManagerFull.java index 48d125e772..ae46c4320e 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/impl/I2PSocketManagerFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/impl/I2PSocketManagerFull.java @@ -28,6 +28,7 @@ import net.i2p.client.streaming.I2PServerSocket; 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 implements I2PSocketManager { * @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(4); @@ -200,7 +202,7 @@ public class I2PSocketManagerFull implements I2PSocketManager { _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)) {