diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java index 37c906fbeb18d6f9847c72346b8236c273b9828d..de219ff39b36367c3707278299086294db81f22f 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java @@ -14,6 +14,9 @@ import java.util.StringTokenizer; import net.i2p.I2PAppContext; import net.i2p.I2PException; +import net.i2p.app.ClientApp; +import net.i2p.app.ClientAppManager; +import net.i2p.app.Outproxy; import net.i2p.client.streaming.I2PSocket; import net.i2p.client.streaming.I2PSocketOptions; import net.i2p.data.Base64; @@ -34,7 +37,7 @@ import net.i2p.util.PortMapper; * example.com (sent to one of the configured proxies) * ) * - * (port and protocol are ignored for i2p destinations) + * (protocol is ignored for i2p destinations) * CONNECT host * CONNECT host protocol * CONNECT host:port @@ -147,6 +150,9 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R String targetRequest = null; boolean usingWWWProxy = false; String currentProxy = null; + // local outproxy plugin + boolean usingInternalOutproxy = false; + Outproxy outproxy = null; long requestId = __requestId.incrementAndGet(); try { out = s.getOutputStream(); @@ -154,6 +160,7 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R String line, method = null, host = null, destination = null, restofline = null; StringBuilder newRequest = new StringBuilder(); String authorization = null; + int remotePort = 443; while (true) { // Use this rather than BufferedReader because we can't have readahead, // since we are passing the stream on to I2PTunnelRunner @@ -172,8 +179,20 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R String request = line.substring(pos + 1); pos = request.indexOf(':'); - if (pos == -1) + if (pos == -1) { pos = request.indexOf(' '); + } else { + int spos = request.indexOf(' '); + if (spos > 0) { + try { + remotePort = Integer.parseInt(request.substring(pos + 1, spos)); + } catch (NumberFormatException nfe) { + break; + } catch (IndexOutOfBoundsException ioobe) { + break; + } + } + } if (pos == -1) { host = request; restofline = ""; @@ -185,19 +204,36 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R if (host.toLowerCase(Locale.US).endsWith(".i2p")) { // Destination gets the host name destination = host; - } else if (host.indexOf('.') != -1) { - // The request must be forwarded to a outproxy - currentProxy = selectProxy(); - if (currentProxy == null) { - if (_log.shouldLog(Log.WARN)) - _log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!"); - writeErrorMessage(ERR_NO_OUTPROXY, out); - s.close(); - return; + } else if (host.contains(".") || host.startsWith("[")) { + if (Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_USE_OUTPROXY_PLUGIN, "true"))) { + ClientAppManager mgr = _context.clientAppManager(); + if (mgr != null) { + ClientApp op = mgr.getRegisteredApp(Outproxy.NAME); + if (op != null) { + outproxy = (Outproxy) op; + usingInternalOutproxy = true; + if (host.startsWith("[")) { + host = host.substring(1); + if (host.endsWith("]")) + host = host.substring(0, host.length() - 1); + } + } + } } - destination = currentProxy; - usingWWWProxy = true; - newRequest.append("CONNECT ").append(host).append(restofline).append("\r\n"); // HTTP spec + if (!usingInternalOutproxy) { + // The request must be forwarded to a outproxy + currentProxy = selectProxy(); + if (currentProxy == null) { + if (_log.shouldLog(Log.WARN)) + _log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!"); + writeErrorMessage(ERR_NO_OUTPROXY, out); + s.close(); + return; + } + destination = currentProxy; + usingWWWProxy = true; + newRequest.append("CONNECT ").append(host).append(restofline).append("\r\n"); // HTTP spec + } } else if (host.toLowerCase(Locale.US).equals("localhost")) { writeErrorMessage(ERR_LOCALHOST, out); s.close(); @@ -208,10 +244,12 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R targetRequest = host; if (_log.shouldLog(Log.DEBUG)) { - _log.debug(getPrefix(requestId) + "METHOD:" + method + ":"); - _log.debug(getPrefix(requestId) + "HOST :" + host + ":"); - _log.debug(getPrefix(requestId) + "REST :" + restofline + ":"); - _log.debug(getPrefix(requestId) + "DEST :" + destination + ":"); + _log.debug(getPrefix(requestId) + "METHOD:" + method + ":\n" + + "HOST :" + host + ":\n" + + "PORT :" + remotePort + ":\n" + + "REST :" + restofline + ":\n" + + "DEST :" + destination + ":\n" + + "www proxy? " + usingWWWProxy + " internal proxy? " + usingInternalOutproxy); } } else if (line.toLowerCase(Locale.US).startsWith("proxy-authorization: ")) { // strip Proxy-Authenticate from the response in HTTPResponseOutputStream @@ -250,7 +288,24 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R } } - if (destination == null || method == null || !"CONNECT".equals(method.toUpperCase(Locale.US))) { + if (method == null || !"CONNECT".equals(method.toUpperCase(Locale.US))) { + writeErrorMessage(ERR_BAD_PROTOCOL, out); + s.close(); + return; + } + + // no destination, going to outproxy plugin + if (usingInternalOutproxy) { + Socket outSocket = outproxy.connect(host, remotePort); + OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId); + byte[] response = SUCCESS_RESPONSE.getBytes("UTF-8"); + Thread t = new I2PTunnelOutproxyRunner(s, outSocket, sockLock, null, response, onTimeout); + // we are called from an unlimited thread pool, so run inline + t.run(); + return; + } + + if (destination == null) { writeErrorMessage(ERR_BAD_PROTOCOL, out); s.close(); return; @@ -282,7 +337,10 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R return; } - I2PSocket i2ps = createI2PSocket(clientDest, getDefaultOptions()); + I2PSocketOptions sktOpts = getDefaultOptions(); + if (!usingWWWProxy && remotePort > 0) + sktOpts.setPort(remotePort); + I2PSocket i2ps = createI2PSocket(clientDest, sktOpts); byte[] data = null; byte[] response = null; if (usingWWWProxy) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index 6e40b9a5722b31c4739df7d295e3ca147711b533..4fe25039f8a8b462d230427ca3c9b5ace7ab01e7 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -357,8 +357,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn public static final String PROP_JUMP_SERVERS = "i2ptunnel.httpclient.jumpServers"; public static final String PROP_DISABLE_HELPER = "i2ptunnel.httpclient.disableAddressHelper"; /** @since 0.9.11 */ - public static final String PROP_USE_OUTPROXY_PLUGIN = "i2ptunnel.useLocalOutproxy"; - /** @since 0.9.11 */ public static final String PROP_SSL_OUTPROXIES = "i2ptunnel.httpclient.SSLOutproxies"; /** @since 0.9.14 */ public static final String PROP_ACCEPT = "i2ptunnel.httpclient.sendAccept"; diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java index 354f5e816140f36e3ec7620115ab4148b90860c4..399269c5a9c0e2b19330c1c1d15225ad3115733d 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java @@ -59,6 +59,8 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem private static final int NONCE_BYTES = DataHelper.DATE_LENGTH + MD5_BYTES; private static final long MAX_NONCE_AGE = 60*60*1000L; private static final int MAX_NONCE_COUNT = 1024; + /** @since 0.9.11, moved to Base in 0.9.29 */ + public static final String PROP_USE_OUTPROXY_PLUGIN = "i2ptunnel.useLocalOutproxy"; private static final String ERR_AUTH1 = "HTTP/1.1 407 Proxy Authentication Required\r\n" + diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServer.java index b269b6798dcae875b36bbfad2e909dc5b47abce6..34f79567f5fbe31b2fa5ee50f2070d51fa976bb7 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServer.java @@ -14,7 +14,7 @@ import net.i2p.app.ClientApp; import net.i2p.app.ClientAppManager; import net.i2p.app.Outproxy; import net.i2p.client.streaming.I2PSocket; -import net.i2p.i2ptunnel.I2PTunnelHTTPClient; +import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase; import net.i2p.util.Log; /** @@ -89,7 +89,7 @@ abstract class SOCKSServer { * @since 0.9.27 */ private boolean shouldUseOutproxyPlugin() { - return Boolean.parseBoolean(props.getProperty(I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN, "true")); + return Boolean.parseBoolean(props.getProperty(I2PTunnelHTTPClientBase.PROP_USE_OUTPROXY_PLUGIN, "true")); } /** diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/GeneralHelper.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/GeneralHelper.java index 556982a43bf4be430bb2ebe4c7854dad7335d186..26f4265e023c16d7c1b86c58a5a6360965f3b966 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/GeneralHelper.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/GeneralHelper.java @@ -579,7 +579,7 @@ public class GeneralHelper { * Default true */ public boolean getUseOutproxyPlugin(int tunnel) { - return getBooleanProperty(tunnel, I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN, true); + return getBooleanProperty(tunnel, I2PTunnelHTTPClientBase.PROP_USE_OUTPROXY_PLUGIN, true); } /** all of these are @since 0.8.3 */ diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java index 0a007e37835534ca4fc181f3da3c8a37c5617b1c..6dd91cce3650c57ee1cedcce4fa46618b8d7691c 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java @@ -409,9 +409,9 @@ public class TunnelConfig { public void setUseOutproxyPlugin(boolean val) { if (val) - _booleanOptions.add(I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN); + _booleanOptions.add(I2PTunnelHTTPClientBase.PROP_USE_OUTPROXY_PLUGIN); else - _booleanOptions.remove(I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN); + _booleanOptions.remove(I2PTunnelHTTPClientBase.PROP_USE_OUTPROXY_PLUGIN); } /** @@ -695,7 +695,7 @@ public class TunnelConfig { }; private static final String _booleanProxyOpts[] = { I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH, - I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN, + I2PTunnelHTTPClientBase.PROP_USE_OUTPROXY_PLUGIN, I2PTunnelHTTPClient.PROP_USER_AGENT, I2PTunnelHTTPClient.PROP_REFERER, I2PTunnelHTTPClient.PROP_ACCEPT, 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 15897cb618f78be766a5358f513d80b6ecae09db..fe1c30b17b865204fa7de73076e968820c2e6a97 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -499,7 +499,7 @@ public class IndexBean { if (tun != null) { if (TunnelController.TYPE_HTTP_CLIENT.equals(tun.getType())) { Properties opts = tun.getClientOptionProps(); - if (Boolean.parseBoolean(opts.getProperty(I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN, "true"))) { + if (Boolean.parseBoolean(opts.getProperty(I2PTunnelHTTPClientBase.PROP_USE_OUTPROXY_PLUGIN, "true"))) { ClientAppManager mgr = _context.clientAppManager(); if (mgr != null) return mgr.getRegisteredApp(Outproxy.NAME) != null;