diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java index ce1b67a6c..26ef4d68e 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java @@ -277,7 +277,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { setEntry(headers, "Accept-encoding", ""); socket.setReadTimeout(readTimeout); - Socket s = getSocket(socket.getLocalPort()); + Socket s = getSocket(socket.getPeerDestination().calculateHash(), 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 b50f7185e..5cd691a3f 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(socket.getLocalPort()); + Socket s = getSocket(socket.getPeerDestination().calculateHash(), 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 5daa64935..e79bf7ce4 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.ConnectException; import java.net.InetAddress; +import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; @@ -33,6 +34,7 @@ import net.i2p.client.streaming.I2PSocket; import net.i2p.client.streaming.I2PSocketManager; import net.i2p.client.streaming.I2PSocketManagerFactory; import net.i2p.data.Base64; +import net.i2p.data.Hash; import net.i2p.util.EventDispatcher; import net.i2p.util.I2PAppThread; import net.i2p.util.I2PSSLSocketFactory; @@ -62,6 +64,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { private static final String PROP_USE_POOL = "i2ptunnel.usePool"; private static final boolean DEFAULT_USE_POOL = true; public static final String PROP_USE_SSL = "useSSL"; + public static final String PROP_UNIQUE_LOCAL = "enableUniqueLocal"; /** apparently unused */ protected static volatile long __serverId = 0; /** max number of threads - this many slowlorisses will DOS this server, but too high could OOM the JVM */ @@ -518,7 +521,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { //threads. try { socket.setReadTimeout(readTimeout); - Socket s = getSocket(socket.getLocalPort()); + Socket s = getSocket(socket.getPeerDestination().calculateHash(), socket.getLocalPort()); afterSocket = getTunnel().getContext().clock().now(); new I2PTunnelRunner(s, socket, slock, null, null); @@ -543,9 +546,10 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { * To configure a specific host:port as the server for incoming port xx, * set option targetForPort.xx=host:port * + * @param from may be used to construct local address since 0.9.13 * @since 0.9.9 */ - protected Socket getSocket(int incomingPort) throws IOException { + protected Socket getSocket(Hash from, int incomingPort) throws IOException { InetAddress host = remoteHost; int port = remotePort; if (incomingPort != 0 && !_socketMap.isEmpty()) { @@ -557,16 +561,17 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { port = isa.getPort(); } } - return getSocket(host, port); + return getSocket(from, host, port); } /** * Get a regular or SSL socket depending on config. * The SSL config applies to all hosts/ports. * + * @param from may be used to construct local address since 0.9.13 * @since 0.9.9 */ - protected Socket getSocket(InetAddress remoteHost, int remotePort) throws IOException { + protected Socket getSocket(Hash from, InetAddress remoteHost, int remotePort) throws IOException { String opt = getTunnel().getClientOptions().getProperty(PROP_USE_SSL); if (Boolean.parseBoolean(opt)) { synchronized(sslLock) { @@ -583,7 +588,26 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { } return _sslFactory.createSocket(remoteHost, remotePort); } else { - return new Socket(remoteHost, remotePort); + // as suggested in https://lists.torproject.org/pipermail/tor-dev/2014-March/006576.html + boolean unique = Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_UNIQUE_LOCAL)); + if (unique && remoteHost.isLoopbackAddress()) { + byte[] addr; + if (remoteHost instanceof Inet4Address) { + addr = new byte[4]; + addr[0] = 127; + System.arraycopy(from.getData(), 0, addr, 1, 3); + } else { + addr = new byte[16]; + addr[0] = (byte) 0xfd; + System.arraycopy(from.getData(), 0, addr, 1, 15); + } + InetAddress local = InetAddress.getByAddress(addr); + // Javadocs say local port of 0 allowed in Java 7. + // Not clear if supported in Java 6 or not. + return new Socket(remoteHost, remotePort, local, 0); + } else { + return new Socket(remoteHost, remotePort); + } } } } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java index 2f5152ad1..1abc56c98 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java @@ -25,6 +25,7 @@ import net.i2p.i2ptunnel.I2PTunnelHTTPClient; import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase; import net.i2p.i2ptunnel.I2PTunnelHTTPServer; import net.i2p.i2ptunnel.I2PTunnelIRCClient; +import net.i2p.i2ptunnel.I2PTunnelServer; import net.i2p.i2ptunnel.TunnelController; import net.i2p.i2ptunnel.TunnelControllerGroup; import net.i2p.util.Addresses; @@ -324,6 +325,11 @@ public class EditBean extends IndexBean { public int getPostTotalBanTime(int tunnel) { return getProperty(tunnel, I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME, I2PTunnelHTTPServer.DEFAULT_POST_TOTAL_BAN_TIME) / 60; } + + /** @since 0.9.13 */ + public boolean getUniqueLocal(int tunnel) { + return getBooleanProperty(tunnel, I2PTunnelServer.PROP_UNIQUE_LOCAL); + } private int getProperty(int tunnel, String prop, int def) { TunnelController tun = getController(tunnel); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index d69080592..d275d385c 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -823,6 +823,11 @@ public class IndexBean { return false; } + /** @since 0.9.13 */ + public void setUniqueLocal(String moo) { + _booleanOptions.add(I2PTunnelServer.PROP_UNIQUE_LOCAL); + } + protected static final String PROP_ENABLE_ACCESS_LIST = "i2cp.enableAccessList"; protected static final String PROP_ENABLE_BLACKLIST = "i2cp.enableBlackList"; @@ -1259,7 +1264,8 @@ public class IndexBean { private static final String _booleanServerOpts[] = { "i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", PROP_ENABLE_ACCESS_LIST, PROP_ENABLE_BLACKLIST, I2PTunnelServer.PROP_USE_SSL, - I2PTunnelHTTPServer.OPT_REJECT_INPROXY + I2PTunnelHTTPServer.OPT_REJECT_INPROXY, + I2PTunnelServer.PROP_UNIQUE_LOCAL }; private static final String _otherClientOpts[] = { "i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.closeIdleTime", diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp index 6a39b7a4a..4dba5773f 100644 --- a/apps/i2ptunnel/jsp/editServer.jsp +++ b/apps/i2ptunnel/jsp/editServer.jsp @@ -206,9 +206,15 @@ input.default { width: 1px; height: 1px; visibility: hidden; } <%=intl._("Local destination")%>(L): - <% if (!"".equals(editBean.getDestinationBase64(curTunnel))) { %> - <%=intl._("Add to local addressbook")%> - <% } %> + <% String b64 = editBean.getDestinationBase64(curTunnel); + if (!"".equals(b64)) { + String name = editBean.getSpoofedHost(curTunnel); + if (name == null || name.equals("")) + name = editBean.getTunnelName(curTunnel); + if (!"".equals(name)) { %> + <%=intl._("Add to local addressbook")%> + <% } + } %> <% if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) { @@ -396,7 +402,20 @@ input.default { width: 1px; height: 1px; visibility: hidden; } <% } // httpserver - %>