HTTP Client:

- Add GUI options for user-agent, referer, accept
  - Fix SSL (initial socket data in I2PTunnelRunner)
  - Disable SSL to i2p addresses by default, add GUI option
  - Fix NPE in error handler
This commit is contained in:
zzz
2014-05-25 19:17:36 +00:00
parent 73943b1a08
commit ef96c88719
6 changed files with 129 additions and 12 deletions

View File

@@ -105,7 +105,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"the following Destination:<BR><BR>")
.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
"<html><body><H1>I2P ERROR: No outproxy found</H1>" +
"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.<p>").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
"<html><body><H1>I2P ERROR: NON-HTTP PROTOCOL</H1>" +
"The request uses a bad protocol. " +
"The I2P HTTP Proxy supports HTTP and HTTPS requests only. Other protocols such as FTP are not allowed.<BR>").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
"<html><body><H1>I2P ERROR: INVALID REQUEST URI</H1>" +
"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.<BR>").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
"<html><body><H1>I2P ERROR: REQUEST DENIED</H1>" +
"Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>").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" +
"<html><body><H1>I2P ERROR: SSL to I2P address rejected</H1>" +
"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"

View File

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

View File

@@ -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<I2PSocket> 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<I2PSocket> sockList, FailCallback onFail) {
this(s, i2ps, slock, initialI2PData, null, sockList, null, onFail, false);
this(s, i2ps, slock, initialI2PData, initialSocketData, sockList, null, onFail, false);
}
/**

View File

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

View File

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

View File

@@ -448,6 +448,40 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
<hr />
</div>
<% } %>
<% if ("httpclient".equals(tunnelType)) { %>
<div id="optionsField" class="rowItem">
<label><%=intl._("Pass User-Agent header through")%>:</label>
</div>
<div id="portField" class="rowItem">
<label><%=intl._("Enable")%>:</label>
<input value="1" type="checkbox" id="startOnLoad" name="allowUserAgent" title="Do not spoof user agent string when checked"<%=(editBean.getAllowUserAgent(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
</div><br />
<div id="optionsField" class="rowItem">
<label><%=intl._("Pass Referer header through")%>:</label>
</div>
<div id="portField" class="rowItem">
<label><%=intl._("Enable")%>:</label>
<input value="1" type="checkbox" id="startOnLoad" name="allowReferer" title="Do not block referer header when checked"<%=(editBean.getAllowReferer(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
</div><br />
<div id="optionsField" class="rowItem">
<label><%=intl._("Pass Accept headers through")%>:</label>
</div>
<div id="portField" class="rowItem">
<label><%=intl._("Enable")%>:</label>
<input value="1" type="checkbox" id="startOnLoad" name="allowAccept" title="Do not block accept headers when checked"<%=(editBean.getAllowAccept(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
</div><br />
<div id="optionsField" class="rowItem">
<label><%=intl._("Allow SSL to I2P addresses")%>:</label>
</div>
<div id="portField" class="rowItem">
<label><%=intl._("Enable")%>:</label>
<input value="1" type="checkbox" id="startOnLoad" name="allowInternalSSL" title="Allow SSL to I2P addresses when checked"<%=(editBean.getAllowInternalSSL(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
</div>
<div class="subdivider">
<hr />
</div>
<% } // if httpclient %>
<% if (true /* editBean.isAdvanced() */ ) { %>
<div id="tunnelOptionsField" class="rowItem">