diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index 966a9bee7..389f0eb99 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -105,7 +105,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn "the following Destination:

") .getBytes(); *****/ - private final static byte[] _ERR_NO_OUTPROXY = + private final static byte[] ERR_NO_OUTPROXY = ("HTTP/1.1 503 Service Unavailable\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + "Cache-control: no-cache\r\n" + @@ -113,6 +113,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn "

I2P ERROR: No outproxy found

" + "Your request was for a site outside of I2P, but you have no " + "HTTP outproxy configured. Please configure an outproxy in I2PTunnel").getBytes(); + private final static byte[] ERR_AHELPER_CONFLICT = ("HTTP/1.1 409 Conflict\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + @@ -127,6 +128,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn "and either discarding the addresshelper link, " + "discarding the host entry from your host database, " + "or naming one of them differently.

").getBytes(); + private final static byte[] ERR_AHELPER_NOTFOUND = ("HTTP/1.1 404 Not Found\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + @@ -136,6 +138,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn "The helper key you put for i2paddresshelper= is not resolvable. " + "It seems to be garbage data, or a mistyped b32. Check your URL " + "to try and fix the helper key to be either a b32 or a base64.").getBytes(); + private final static byte[] ERR_AHELPER_NEW = ("HTTP/1.1 409 New Address\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + @@ -146,6 +149,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn "You may either save the destination for this host name to your address book, or remember it only until your router restarts. " + "If you save it to your address book, you will not see this message again. " + "If you do not wish to visit this host, click the \"back\" button on your browser.").getBytes(); + private final static byte[] ERR_BAD_PROTOCOL = ("HTTP/1.1 403 Bad Protocol\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + @@ -154,6 +158,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn "

I2P ERROR: NON-HTTP PROTOCOL

" + "The request uses a bad protocol. " + "The I2P HTTP Proxy supports HTTP and HTTPS requests only. Other protocols such as FTP are not allowed.
").getBytes(); + private final static byte[] ERR_BAD_URI = ("HTTP/1.1 403 Bad URI\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + @@ -162,6 +167,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn "

I2P ERROR: INVALID REQUEST URI

" + "The request URI is invalid, and probably contains illegal characters. " + "If you clicked e.g. a forum link, check the end of the URI for any characters the browser has mistakenly added on.
").getBytes(); + private final static byte[] ERR_LOCALHOST = ("HTTP/1.1 403 Access Denied\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + @@ -170,6 +176,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn "

I2P ERROR: REQUEST DENIED

" + "Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.
").getBytes(); + private final static byte[] ERR_INTERNAL_SSL = + ("HTTP/1.1 403 SSL Rejected\r\n" + + "Content-Type: text/html; charset=iso-8859-1\r\n" + + "Cache-control: no-cache\r\n" + + "\r\n" + + "

I2P ERROR: SSL to I2P address rejected

" + + "SSL for to .i2p addresses denied by configuration." + + "You may change the configuration in I2PTunnel").getBytes(); + /** * This constructor always starts the tunnel (ignoring the i2cp.delayOpen option). * It is used to add a client to an existing socket manager. @@ -317,6 +332,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn 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"; + /** @since 0.9.14 */ + public static final String PROP_INTERNAL_SSL = "i2ptunnel.httpclient.allowInternalSSL"; protected void clientConnectionRun(Socket s) { OutputStream out = null; @@ -728,7 +747,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn _log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!"); } l.log("No outproxy found for the request."); - out.write(getErrorPage("noproxy", _ERR_NO_OUTPROXY)); + out.write(getErrorPage("noproxy", ERR_NO_OUTPROXY)); writeFooter(out); reader.drain(); s.close(); @@ -800,8 +819,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn } else if(lowercaseLine.startsWith("accept")) { // strip the accept-blah headers, as they vary dramatically from // browser to browser - line = null; - continue; + if(!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT))) { + line = null; + continue; + } } else if (lowercaseLine.startsWith("referer: ")) { // save for address helper form below referer = line.substring(9); @@ -850,7 +871,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn // according to rfc2616 s14.3, this *should* force identity, even if // an explicit q=0 for gzip doesn't. tested against orion.i2p, and it // seems to work. - newRequest.append("Accept-Encoding: \r\n"); + if(!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT))) + newRequest.append("Accept-Encoding: \r\n"); if (!usingInternalOutproxy) newRequest.append("X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n"); } @@ -1002,12 +1024,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn } byte[] header; String jumpServers = null; + String extraMessage = null; if(usingWWWProxy) { header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN); } else if(ahelperPresent) { header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN); } else if(destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) { header = getErrorPage("nols", ERR_DESTINATION_UNKNOWN); + extraMessage = _("Destination lease set not found"); } else { header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN); jumpServers = getTunnel().getClientOptions().getProperty(PROP_JUMP_SERVERS); @@ -1015,11 +1039,21 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn jumpServers = DEFAULT_JUMP_SERVERS; } } - writeErrorMessage(header, out, targetRequest, usingWWWProxy, destination, jumpServers); + writeErrorMessage(header, extraMessage, out, targetRequest, usingWWWProxy, destination, jumpServers); s.close(); return; } + if (method.toUpperCase(Locale.US).equals("CONNECT") && + !usingWWWProxy && + !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_INTERNAL_SSL))) { + writeErrorMessage(ERR_INTERNAL_SSL, out, targetRequest, false, destination); + s.close(); + if (_log.shouldLog(Log.WARN)) + _log.warn("SSL to i2p destinations denied by configuration: " + targetRequest); + return; + } + // Address helper response form // This will only load once - the second time it won't be "new" // Don't do this for eepget, which uses a user-agent of "Wget" diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java index 14a886ad3..b00611311 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClientBase.java @@ -549,8 +549,11 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem _requestId = id; } + /** + * @param ex may be null + */ public void onFail(Exception ex) { - Throwable cause = ex.getCause(); + Throwable cause = ex != null ? ex.getCause() : null; if (cause != null && cause instanceof I2PSocketException) { I2PSocketException ise = (I2PSocketException) cause; handleI2PSocketException(ise, _out, _target, _usingProxy, _wwwProxy); @@ -562,6 +565,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem } /** + * @param ex may be null * @since 0.9.14 moved from subclasses */ protected void handleClientException(Exception ex, OutputStream out, String targetRequest, @@ -582,13 +586,14 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem * Generate an error page based on the status code * in our custom exception. * + * @param ise may be null * @since 0.9.14 */ protected void handleI2PSocketException(I2PSocketException ise, OutputStream out, String targetRequest, boolean usingWWWProxy, String wwwProxy) { if (out == null) return; - int status = ise.getStatus(); + int status = ise != null ? ise.getStatus() : -1; String error; //TODO MessageStatusMessage.STATUS_SEND_FAILURE_UNSUPPORTED_ENCRYPTION if (status == MessageStatusMessage.STATUS_SEND_FAILURE_NO_LEASESET) { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java index 057c6acaa..d4bd1d455 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java @@ -134,7 +134,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr */ public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, byte[] initialSocketData, List sockList, Runnable onTimeout) { - this(s, i2ps, slock, initialI2PData, null, sockList, onTimeout, null, true); + this(s, i2ps, slock, initialI2PData, initialSocketData, sockList, onTimeout, null, true); } /** @@ -150,7 +150,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr */ public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, byte[] initialSocketData, List sockList, FailCallback onFail) { - this(s, i2ps, slock, initialI2PData, null, sockList, null, onFail, false); + this(s, i2ps, slock, initialI2PData, initialSocketData, sockList, null, onFail, false); } /** 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 1abc56c98..12fded0d8 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java @@ -238,7 +238,27 @@ public class EditBean extends IndexBean { public boolean getDelayOpen(int tunnel) { return getBooleanProperty(tunnel, "i2cp.delayOpen"); } - + + /** @since 0.9.14 */ + public boolean getAllowUserAgent(int tunnel) { + return getBooleanProperty(tunnel, I2PTunnelHTTPClient.PROP_USER_AGENT); + } + + /** @since 0.9.14 */ + public boolean getAllowReferer(int tunnel) { + return getBooleanProperty(tunnel, I2PTunnelHTTPClient.PROP_REFERER); + } + + /** @since 0.9.14 */ + public boolean getAllowAccept(int tunnel) { + return getBooleanProperty(tunnel, I2PTunnelHTTPClient.PROP_ACCEPT); + } + + /** @since 0.9.14 */ + public boolean getAllowInternalSSL(int tunnel) { + return getBooleanProperty(tunnel, I2PTunnelHTTPClient.PROP_INTERNAL_SSL); + } + /** all proxy auth @since 0.8.2 */ public boolean getProxyAuth(int tunnel) { return getProperty(tunnel, I2PTunnelHTTPClientBase.PROP_AUTH, "false") != "false"; 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 cb1b25ae7..0e5ff09f7 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -892,6 +892,26 @@ public class IndexBean { } } + /** @since 0.9.14 */ + public void setAllowUserAgent(String moo) { + _booleanOptions.add(I2PTunnelHTTPClient.PROP_USER_AGENT); + } + + /** @since 0.9.14 */ + public void setAllowReferer(String moo) { + _booleanOptions.add(I2PTunnelHTTPClient.PROP_REFERER); + } + + /** @since 0.9.14 */ + public void setAllowAccept(String moo) { + _booleanOptions.add(I2PTunnelHTTPClient.PROP_ACCEPT); + } + + /** @since 0.9.14 */ + public void setAllowInternalSSL(String moo) { + _booleanOptions.add(I2PTunnelHTTPClient.PROP_INTERNAL_SSL); + } + /** all proxy auth @since 0.8.2 */ public void setProxyAuth(String s) { if (s != null) @@ -1269,7 +1289,11 @@ public class IndexBean { }; private static final String _booleanProxyOpts[] = { I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH, - I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN + I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN, + I2PTunnelHTTPClient.PROP_USER_AGENT, + I2PTunnelHTTPClient.PROP_REFERER, + I2PTunnelHTTPClient.PROP_ACCEPT, + I2PTunnelHTTPClient.PROP_INTERNAL_SSL }; private static final String _booleanServerOpts[] = { "i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", PROP_ENABLE_ACCESS_LIST, PROP_ENABLE_BLACKLIST, diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp index 739b9f5f4..1762da29a 100644 --- a/apps/i2ptunnel/jsp/editClient.jsp +++ b/apps/i2ptunnel/jsp/editClient.jsp @@ -448,6 +448,40 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
<% } %> + + <% if ("httpclient".equals(tunnelType)) { %> +
+ +
+
+ + class="tickbox" /> +

+
+ +
+
+ + class="tickbox" /> +

+
+ +
+
+ + class="tickbox" /> +

+
+ +
+
+ + class="tickbox" /> +
+
+
+
+ <% } // if httpclient %> <% if (true /* editBean.isAdvanced() */ ) { %>