i2ptunnel: Add support for outproxy plugin to CONNECT proxy (tickets #1364, #1895)

Add support for ports to CONNECT proxy
This commit is contained in:
zzz
2017-01-30 00:33:43 +00:00
parent 1cf6030646
commit 7cb5dab67f
7 changed files with 87 additions and 29 deletions

View File

@ -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)

View File

@ -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";

View File

@ -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" +

View File

@ -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"));
}
/**

View File

@ -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 */

View File

@ -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,

View File

@ -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;