From 35a86e603b622fa04d71ddda490ac3588d443a0c Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 24 Oct 2013 11:38:28 +0000 Subject: [PATCH] * I2PTunnel standard, HTTP, and IRC servers: Route connections to specific targets based on incoming I2P port with custom option targetForPort.xxxx=myserver:yyyy This allows multiple services on a single server tunnel (ticket #1066) --- .../i2p/i2ptunnel/I2PTunnelHTTPServer.java | 2 +- .../net/i2p/i2ptunnel/I2PTunnelIRCServer.java | 2 +- .../net/i2p/i2ptunnel/I2PTunnelServer.java | 66 ++++++++++++++++++- history.txt | 6 ++ .../src/net/i2p/router/RouterVersion.java | 2 +- 5 files changed, 73 insertions(+), 5 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java index 6519d709e..d58a3364f 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java @@ -150,7 +150,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { setEntry(headers, "Accept-encoding", ""); socket.setReadTimeout(readTimeout); - Socket s = getSocket(remoteHost, remotePort); + Socket s = getSocket(socket.getLocalPort()); long afterSocket = getTunnel().getContext().clock().now(); // instead of i2ptunnelrunner, use something that reads the HTTP // request from the socket, modifies the headers, sends the request to the diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java index 1b651c34a..b50f7185e 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java @@ -137,7 +137,7 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable { buf.append("\r\n"); modifiedRegistration = buf.toString(); } - Socket s = getSocket(remoteHost, remotePort); + Socket s = getSocket(socket.getLocalPort()); new I2PTunnelRunner(s, socket, slock, null, modifiedRegistration.getBytes(), null); } catch (SocketException ex) { try { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java index 47cea2bb3..9203f58d1 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java @@ -11,12 +11,15 @@ import java.io.IOException; import java.io.InputStream; import java.net.ConnectException; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import java.security.GeneralSecurityException; import java.util.Iterator; +import java.util.Map; import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.RejectedExecutionException; @@ -74,6 +77,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { protected I2PTunnelTask task; protected boolean bidir; private ThreadPoolExecutor _executor; + private final Map _socketMap = new ConcurrentHashMap(4); /** unused? port should always be specified */ private int DEFAULT_LOCALPORT = 4488; @@ -95,6 +99,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { this.remoteHost = host; this.remotePort = port; _usePool = getUsePool(); + buildSocketMap(tunnel.getClientOptions()); sockMgr = createManager(bais); } @@ -115,6 +120,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { this.remoteHost = host; this.remotePort = port; _usePool = getUsePool(); + buildSocketMap(tunnel.getClientOptions()); FileInputStream fis = null; try { fis = new FileInputStream(privkey); @@ -145,6 +151,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { this.remoteHost = host; this.remotePort = port; _usePool = getUsePool(); + buildSocketMap(tunnel.getClientOptions()); sockMgr = createManager(privData); } @@ -162,6 +169,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { this.remotePort = port; _log = tunnel.getContext().logManager().getLog(getClass()); _usePool = false; + buildSocketMap(tunnel.getClientOptions()); sockMgr = sktMgr; open = true; } @@ -348,6 +356,37 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { return; Properties props = tunnel.getClientOptions(); sockMgr.setDefaultOptions(sockMgr.buildOptions(props)); + buildSocketMap(props); + } + + /** + * Update the ports map. + * + * @since 0.9.9 + */ + private void buildSocketMap(Properties props) { + _socketMap.clear(); + for (Map.Entry e : props.entrySet()) { + String key = (String) e.getKey(); + if (key.startsWith("targetForPort.")) { + key = key.substring("targetForPort.".length()); + try { + int myPort = Integer.parseInt(key); + String host = (String) e.getValue(); + int colon = host.indexOf(":"); + int port = Integer.parseInt(host.substring(colon + 1)); + host = host.substring(0, colon); + InetSocketAddress isa = new InetSocketAddress(host, port); + if (isa.isUnresolved()) + l.log("Warning - cannot resolve address for port " + key + ": " + host); + _socketMap.put(Integer.valueOf(myPort), isa); + } catch (NumberFormatException nfe) { + l.log("Bad socket spec for port " + key + ": " + e.getValue()); + } catch (IndexOutOfBoundsException ioobe) { + l.log("Bad socket spec for port " + key + ": " + e.getValue()); + } + } + } } protected int getHandlerCount() { @@ -473,7 +512,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { //threads. try { socket.setReadTimeout(readTimeout); - Socket s = getSocket(remoteHost, remotePort); + Socket s = getSocket(socket.getLocalPort()); afterSocket = getTunnel().getContext().clock().now(); new I2PTunnelRunner(s, socket, slock, null, null); @@ -494,7 +533,30 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { } /** - * Get a regular or SSL socket depending on config + * Get a regular or SSL socket depending on config and the incoming port. + * To configure a specific host:port as the server for incoming port xx, + * set option targetForPort.xx=host:port + * + * @since 0.9.9 + */ + protected Socket getSocket(int incomingPort) throws IOException { + InetAddress host = remoteHost; + int port = remotePort; + if (incomingPort != 0 && !_socketMap.isEmpty()) { + InetSocketAddress isa = _socketMap.get(Integer.valueOf(incomingPort)); + if (isa != null) { + host = isa.getAddress(); + if (host == null) + throw new IOException("Cannot resolve " + isa.getHostName()); + port = isa.getPort(); + } + } + return getSocket(host, port); + } + + /** + * Get a regular or SSL socket depending on config. + * The SSL config applies to all hosts/ports. * * @since 0.9.9 */ diff --git a/history.txt b/history.txt index 1f6c617e1..2442e4999 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,9 @@ +2013-10-24 zzz + * I2PTunnel standard, HTTP, and IRC servers: + Route connections to specific targets based on incoming I2P port + with custom option targetForPort.xxxx=myserver:yyyy + This allows multiple services on a single server tunnel (ticket #1066) + 2013-10-23 zzz * I2PTunnel standard and IRC clients: - Allow host:port targets; set defaults in i2ptunnel.config (ticket #1066) diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index a6204817e..f2522cd4e 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 8; + public final static long BUILD = 9; /** for example "-test" */ public final static String EXTRA = "";