allow SHOUTcast/icecast to work over the http proxy + cleanups

This commit is contained in:
sponge
2012-03-27 17:41:02 +00:00
parent 11ff89fef2
commit 97f23286d3
3 changed files with 840 additions and 785 deletions

View File

@@ -4,7 +4,6 @@
package net.i2p.i2ptunnel;
import java.io.ByteArrayOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -74,25 +73,20 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
* via address helper links
*/
private final ConcurrentHashMap<String, String> addressHelpers = new ConcurrentHashMap(8);
/**
* Used to protect actions via http://proxy.i2p/
*/
private final String _proxyNonce;
/**
* These are backups if the xxx.ht error page is missing.
*/
private final static byte[] ERR_REQUEST_DENIED =
("HTTP/1.1 403 Access Denied\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: REQUEST DENIED</H1>" +
"You attempted to connect to a non-I2P website or location.<BR>")
.getBytes();
"You attempted to connect to a non-I2P website or location.<BR>").getBytes();
private final static byte[] ERR_DESTINATION_UNKNOWN =
("HTTP/1.1 503 Service Unavailable\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" +
@@ -103,9 +97,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"wrong BASE64 I2P Destination or the link you are following is " +
"bad. The host (or the WWW proxy, if you're using one) could also " +
"be temporarily offline. You may want to <b>retry</b>. " +
"Could not find the following Destination:<BR><BR><div>")
.getBytes();
"Could not find the following Destination:<BR><BR><div>").getBytes();
/*****
private final static byte[] ERR_TIMEOUT =
("HTTP/1.1 504 Gateway Timeout\r\n"+
@@ -119,17 +111,14 @@ 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" +
"\r\n" +
"<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();
"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" +
@@ -143,9 +132,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"You can resolve the conflict by considering which key you trust, " +
"and either discarding the addresshelper link, " +
"discarding the host entry from your host database, " +
"or naming one of them differently.<p>")
.getBytes();
"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" +
@@ -154,9 +141,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"<html><body><H1>I2P ERROR: Helper key not resolvable.</H1>" +
"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();
"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" +
@@ -166,9 +151,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"The address helper link you followed is for a new host name that is not in your address book. " +
"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();
"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" +
@@ -176,18 +159,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"\r\n" +
"<html><body><H1>I2P ERROR: NON-HTTP PROTOCOL</H1>" +
"The request uses a bad protocol. " +
"The I2P HTTP Proxy supports http:// requests ONLY. Other protocols such as https:// and ftp:// are not allowed.<BR>")
.getBytes();
"The I2P HTTP Proxy supports http:// requests ONLY. Other protocols such as https:// and ftp:// are not allowed.<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" +
"Cache-control: no-cache\r\n" +
"\r\n" +
"<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();
"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_AUTH =
("HTTP/1.1 407 Proxy Authentication Required\r\n" +
"Content-Type: text/html; charset=UTF-8\r\n" +
@@ -196,8 +175,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"Proxy-Authenticate: Basic realm=\"I2P HTTP Proxy\"\r\n" +
"\r\n" +
"<html><body><H1>I2P ERROR: PROXY AUTHENTICATION REQUIRED</H1>" +
"This proxy is configured to require authentication.<BR>")
.getBytes();
"This proxy is configured to require authentication.<BR>").getBytes();
/**
* This constructor always starts the tunnel (ignoring the i2cp.delayOpen option).
@@ -215,6 +193,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
notifyEvent("openHTTPClientResult", "ok");
}
/**
* @throws IllegalArgumentException if the I2PTunnel does not contain
* valid config to contact the router
@@ -233,9 +212,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(wwwProxy != null) {
StringTokenizer tok = new StringTokenizer(wwwProxy, ", ");
while (tok.hasMoreTokens())
while(tok.hasMoreTokens()) {
_proxyList.add(tok.nextToken().trim());
}
}
setName("HTTP Proxy on " + tunnel.listenHost + ':' + localPort);
@@ -251,13 +231,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
@Override
protected I2PSocketOptions getDefaultOptions() {
Properties defaultOpts = getTunnel().getClientOptions();
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
if(!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT)) {
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, "" + DEFAULT_READ_TIMEOUT);
}
//if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
// defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
if(!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT)) {
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
}
return opts;
}
@@ -269,18 +251,20 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
Properties defaultOpts = getTunnel().getClientOptions();
defaultOpts.putAll(overrides);
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
if(!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT)) {
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, "" + DEFAULT_READ_TIMEOUT);
if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
}
if(!defaultOpts.contains("i2p.streaming.inactivityTimeout")) {
defaultOpts.setProperty("i2p.streaming.inactivityTimeout", "" + DEFAULT_READ_TIMEOUT);
}
// delayed start
verifySocketManager();
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
if(!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT)) {
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
}
return opts;
}
private InternalSocketRunner isr;
/**
@@ -301,14 +285,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
@Override
public boolean close(boolean forced) {
int reg = _context.portMapper().getPort(PortMapper.SVC_HTTP_PROXY);
if (reg == getLocalPort())
if(reg == getLocalPort()) {
_context.portMapper().unregister(PortMapper.SVC_HTTP_PROXY);
}
boolean rv = super.close(forced);
if (this.isr != null)
if(this.isr != null) {
this.isr.stopRunning();
}
return rv;
}
private static final String HELPER_PARAM = "i2paddresshelper";
public static final String LOCAL_SERVER = "proxy.i2p";
private static final boolean DEFAULT_GZIP = true;
@@ -333,6 +318,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
String internalRawQuery = null;
String currentProxy = null;
long requestId = ++__requestId;
boolean shout = false;
try {
out = s.getOutputStream();
@@ -346,22 +332,26 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
String authorization = null;
while((line = reader.readLine(method)) != null) {
line = line.trim();
if (_log.shouldLog(Log.DEBUG))
if(_log.shouldLog(Log.DEBUG)) {
_log.debug(getPrefix(requestId) + "Line=[" + line + "]");
}
String lowercaseLine = line.toLowerCase(Locale.US);
if(lowercaseLine.startsWith("connection: ") ||
lowercaseLine.startsWith("keep-alive: ") ||
lowercaseLine.startsWith("proxy-connection: "))
lowercaseLine.startsWith("proxy-connection: ")) {
continue;
}
if(method == null) { // first line (GET /base64/realaddr)
if (_log.shouldLog(Log.DEBUG))
if(_log.shouldLog(Log.DEBUG)) {
_log.debug(getPrefix(requestId) + "First line [" + line + "]");
}
String[] params = line.split(" ", 3);
if (params.length != 3)
if(params.length != 3) {
break;
}
String request = params[1];
// various obscure fixups
@@ -372,8 +362,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// Deprecated
// /eepproxy/foo.i2p/bar/baz.html
String subRequest = request.substring("/eepproxy/".length());
if (subRequest.indexOf("/") == -1)
if(subRequest.indexOf("/") == -1) {
subRequest += "/";
}
request = "http://" + subRequest;
/****
} else if (request.toLowerCase(Locale.US).startsWith("http://i2p/")) {
@@ -410,19 +401,22 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
requestURI = new URI(request);
if(requestURI.getRawUserInfo() != null || requestURI.getRawFragment() != null) {
// these should never be sent to the proxy in the request line
if (_log.shouldLog(Log.WARN))
if(_log.shouldLog(Log.WARN)) {
_log.warn(getPrefix(requestId) + "Removing userinfo or fragment [" + request + "]");
}
requestURI = changeURI(requestURI, null, 0, null);
}
if(requestURI.getPath() == null || requestURI.getPath().length() <= 0) {
// Add a path
if (_log.shouldLog(Log.WARN))
if(_log.shouldLog(Log.WARN)) {
_log.warn(getPrefix(requestId) + "Adding / path to [" + request + "]");
}
requestURI = changeURI(requestURI, null, 0, "/");
}
} catch(URISyntaxException use) {
if (_log.shouldLog(Log.WARN))
if(_log.shouldLog(Log.WARN)) {
_log.warn(getPrefix(requestId) + "Bad request [" + request + "]", use);
}
break;
}
method = params[0];
@@ -454,24 +448,25 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
internalPath = requestURI.getPath();
internalRawQuery = requestURI.getRawQuery();
} else if(hostLowerCase.equals("i2p")) {
// pull the b64 dest out of the first path element
// pull the b64 _dest out of the first path element
String oldPath = requestURI.getPath().substring(1);
int slash = oldPath.indexOf("/");
if(slash < 0) {
slash = oldPath.length();
oldPath += "/";
}
String dest = oldPath.substring(0, slash);
if (slash >= 516 && !dest.contains(".")) {
String _dest = oldPath.substring(0, slash);
if(slash >= 516 && !_dest.contains(".")) {
// possible alternative:
// redirect to b32
destination = dest;
destination = _dest;
host = getHostName(destination);
targetRequest = requestURI.toASCIIString();
String newURI = oldPath.substring(slash);
String query = requestURI.getRawQuery();
if (query != null)
if(query != null) {
newURI += '?' + query;
}
try {
requestURI = new URI(newURI);
} catch(URISyntaxException use) {
@@ -495,8 +490,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// TODO support I2P ports someday
//if (port >= 0)
// host = host + ':' + port;
if (_log.shouldLog(Log.WARN))
if(_log.shouldLog(Log.WARN)) {
_log.warn(getPrefix(requestId) + "Removing port from [" + request + "]");
}
try {
requestURI = changeURI(requestURI, null, -1, null);
} catch(URISyntaxException use) {
@@ -515,8 +511,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(helperStrings != null &&
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
query = helperStrings[0];
if (query.equals(""))
if(query.equals("")) {
query = null;
}
try {
requestURI = replaceQuery(requestURI, query);
} catch(URISyntaxException use) {
@@ -535,10 +532,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
i.e. on your eepsite put
<a href="?i2paddresshelper=name.i2p">This is the name I want to be called.</a>
*/
Destination dest = _context.namingService().lookup(ahelperKey);
if(dest==null) {
if (_log.shouldLog(Log.WARN))
Destination _dest = _context.namingService().lookup(ahelperKey);
if(_dest == null) {
if(_log.shouldLog(Log.WARN)) {
_log.warn(getPrefix(requestId) + "Could not find destination for " + ahelperKey);
}
byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
out.write(header);
out.write(("<p>" + _("This seems to be a bad destination:") + " " + ahelperKey + " " + _("i2paddresshelper cannot help you with a destination like that!") + "</p>").getBytes("UTF-8"));
@@ -547,7 +545,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
closeSocket(s);
return;
}
ahelperKey = dest.toBase64();
ahelperKey = _dest.toBase64();
}
ahelperPresent = true;
@@ -560,10 +558,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if((!ahelperNew) && !old.equals(ahelperKey)) {
// Conflict: handle when URL reconstruction done
ahelperConflict = true;
if (_log.shouldLog(Log.WARN))
if(_log.shouldLog(Log.WARN)) {
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
"], trusted key [" + old + "], specified key [" + ahelperKey + "].");
}
}
} else {
// If the host is resolvable from database, verify addresshelper key
// Silently bypass correct keys, otherwise alert
@@ -573,9 +572,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(destB64 != null && !destB64.equals(ahelperKey)) {
// Conflict: handle when URL reconstruction done
ahelperConflict = true;
if (_log.shouldLog(Log.WARN))
if(_log.shouldLog(Log.WARN)) {
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
"], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
}
}
}
@@ -617,14 +617,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
} // end query processing
String addressHelper = addressHelpers.get(destination);
if (addressHelper != null)
if(addressHelper != null) {
host = getHostName(addressHelper);
}
// now strip everything but path and query from URI
targetRequest = requestURI.toASCIIString();
String newURI = requestURI.getRawPath();
if (query != null)
if(query != null) {
newURI += '?' + query;
}
try {
requestURI = new URI(newURI);
} catch(URISyntaxException use) {
@@ -646,20 +648,24 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
s.close();
return;
} else if(host.contains(".") || host.startsWith("[")) {
if (port >= 0)
if(port >= 0) {
host = host + ':' + port;
}
// The request must be forwarded to a WWW proxy
if (_log.shouldLog(Log.DEBUG))
if(_log.shouldLog(Log.DEBUG)) {
_log.debug("Before selecting outproxy for " + host);
}
currentProxy = selectProxy();
if (_log.shouldLog(Log.DEBUG))
if(_log.shouldLog(Log.DEBUG)) {
_log.debug("After selecting outproxy for " + host + ": " + currentProxy);
}
if(currentProxy == null) {
if (_log.shouldLog(Log.WARN))
if(_log.shouldLog(Log.WARN)) {
_log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!");
}
l.log("No HTTP outproxy found for the request.");
if(out != null) {
out.write(getErrorPage("noproxy", ERR_NO_OUTPROXY));
out.write(getErrorPage("noproxy", _ERR_NO_OUTPROXY));
writeFooter(out);
}
s.close();
@@ -668,15 +674,17 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
destination = currentProxy;
usingWWWProxy = true;
targetRequest = requestURI.toASCIIString();
if (_log.shouldLog(Log.DEBUG))
if(_log.shouldLog(Log.DEBUG)) {
_log.debug(getPrefix(requestId) + " [" + host + "]: wwwProxy!");
}
} else {
// what is left for here? a hostname with no dots, and != "i2p"
// and not a destination ???
// Perhaps something in privatehosts.txt ...
// Rather than look it up, just bail out.
if (_log.shouldLog(Log.WARN))
if(_log.shouldLog(Log.WARN)) {
_log.warn("NODOTS, NOI2P: " + request);
}
if(out != null) {
out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
writeFooter(out);
@@ -687,7 +695,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
boolean isValid = usingWWWProxy || usingInternalServer || isSupportedAddress(host, protocol);
if(!isValid) {
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "notValid(" + host + ")");
if(_log.shouldLog(Log.INFO)) {
_log.info(getPrefix(requestId) + "notValid(" + host + ")");
}
method = null;
destination = null;
break;
@@ -708,8 +718,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// Note that we only pass the original Host: line through to the outproxy
// But we don't create a Host: line if it wasn't sent to us
line = "Host: " + host;
if (_log.shouldLog(Log.INFO))
if(_log.shouldLog(Log.INFO)) {
_log.info(getPrefix(requestId) + "Setting host = " + host);
}
} else if(lowercaseLine.startsWith("user-agent: ")) {
// save for deciding whether to offer address book form
userAgent = lowercaseLine.substring(12);
@@ -746,11 +757,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// hop-by-hop header, and we definitely want to block Windows NTLM after a far-end 407.
// Response to far-end shouldn't happen, as we
// strip Proxy-Authenticate from the response in HTTPResponseOutputStream
if (lowercaseLine.startsWith("proxy-authorization: basic "))
// save for auth check below
if(lowercaseLine.startsWith("proxy-authorization: basic ")) // save for auth check below
{
authorization = line.substring(27); // "proxy-authorization: basic ".length()
}
line = null;
continue;
} else if(lowercaseLine.startsWith("icy")) {
// icecast/shoutcast, We need to leave the user-agent alone.
shout = true;
}
}
@@ -758,8 +773,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// No more headers, add our own and break out of the loop
String ok = getTunnel().getClientOptions().getProperty("i2ptunnel.gzip");
boolean gzip = DEFAULT_GZIP;
if (ok != null)
if(ok != null) {
gzip = Boolean.valueOf(ok).booleanValue();
}
if(gzip && !usingInternalServer) {
// 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
@@ -767,13 +783,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
newRequest.append("Accept-Encoding: \r\n");
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");
}
if(!shout) {
if(!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT)).booleanValue()) {
// let's not advertise to external sites that we are from I2P
if (usingWWWProxy)
if(usingWWWProxy) {
newRequest.append("User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6\r\n");
else
} else {
newRequest.append("User-Agent: MYOB/6.66 (AN/ON)\r\n");
}
}
}
// Add Proxy-Authentication header for next hop (outproxy)
if(usingWWWProxy && Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_AUTH)).booleanValue()) {
// specific for this proxy
@@ -785,8 +804,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
pw = getTunnel().getClientOptions().getProperty(PROP_OUTPROXY_PW);
}
if(user != null && pw != null) {
newRequest.append("Proxy-Authorization: Basic ")
.append(Base64.encode((user + ':' + pw).getBytes(), true)) // true = use standard alphabet
newRequest.append("Proxy-Authorization: Basic ").append(Base64.encode((user + ':' + pw).getBytes(), true)) // true = use standard alphabet
.append("\r\n");
}
}
@@ -797,33 +815,37 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
}
} // end header processing
if (_log.shouldLog(Log.DEBUG))
if(_log.shouldLog(Log.DEBUG)) {
_log.debug(getPrefix(requestId) + "NewRequest header: [" + newRequest.toString() + "]");
}
if(method == null || destination == null) {
//l.log("No HTTP method found in the request.");
if(out != null) {
if (protocol != null && "http".equals(protocol.toLowerCase(Locale.US)))
if(protocol != null && "http".equals(protocol.toLowerCase(Locale.US))) {
out.write(getErrorPage("denied", ERR_REQUEST_DENIED));
else
} else {
out.write(getErrorPage("protocol", ERR_BAD_PROTOCOL));
}
writeFooter(out);
}
s.close();
return;
}
if (_log.shouldLog(Log.DEBUG))
if(_log.shouldLog(Log.DEBUG)) {
_log.debug(getPrefix(requestId) + "Destination: " + destination);
}
// Authorization
if(!authorize(s, requestId, authorization)) {
if(_log.shouldLog(Log.WARN)) {
if (authorization != null)
if(authorization != null) {
_log.warn(getPrefix(requestId) + "Auth failed, sending 407 again");
else
} else {
_log.warn(getPrefix(requestId) + "Auth required, sending 407");
}
}
out.write(getErrorPage("auth", ERR_AUTH));
writeFooter(out);
s.close();
@@ -855,8 +877,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(clientDest == null) {
// remove bad entries
addressHelpers.remove(destination.toLowerCase(Locale.US));
if (_log.shouldLog(Log.WARN))
if(_log.shouldLog(Log.WARN)) {
_log.warn(getPrefix(requestId) + "Could not find destination for " + addressHelper);
}
byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
writeErrorMessage(header, out, targetRequest, false, destination, null);
s.close();
@@ -871,8 +894,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(sess != null && !sess.isClosed()) {
byte[] hData = Base32.decode(destination.substring(0, 52));
if(hData != null) {
if (_log.shouldLog(Log.INFO))
if(_log.shouldLog(Log.INFO)) {
_log.info("lookup in-session " + destination);
}
Hash hash = Hash.create(hData);
clientDest = sess.lookupDest(hash, 20 * 1000);
}
@@ -885,22 +909,24 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(clientDest == null) {
//l.log("Could not resolve " + destination + ".");
if (_log.shouldLog(Log.WARN))
if(_log.shouldLog(Log.WARN)) {
_log.warn("Unable to resolve " + destination + " (proxy? " + usingWWWProxy + ", request: " + targetRequest);
}
byte[] header;
String jumpServers = null;
if (usingWWWProxy)
if(usingWWWProxy) {
header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
else if (ahelperPresent)
} else if(ahelperPresent) {
header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
else if (destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p"))
} else if(destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
header = getErrorPage("dnf", ERR_DESTINATION_UNKNOWN);
else {
} else {
header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN);
jumpServers = getTunnel().getClientOptions().getProperty(PROP_JUMP_SERVERS);
if (jumpServers == null)
if(jumpServers == null) {
jumpServers = DEFAULT_JUMP_SERVERS;
}
}
writeErrorMessage(header, out, targetRequest, usingWWWProxy, destination, jumpServers);
s.close();
return;
@@ -923,8 +949,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// Syndie can't handle a redirect of a POST
if(ahelperPresent && !"POST".equals(method)) {
String uri = targetRequest;
if (_log.shouldLog(Log.DEBUG))
if(_log.shouldLog(Log.DEBUG)) {
_log.debug("Auto redirecting to " + uri);
}
out.write(("HTTP/1.1 301 Address Helper Accepted\r\n" +
"Location: " + uri + "\r\n" +
"\r\n").getBytes("UTF-8"));
@@ -942,20 +969,23 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
} catch (SocketException ex) {
if (_log.shouldLog(Log.INFO))
if (_log.shouldLog(Log.INFO)) {
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
}
//l.log("Error connecting: " + ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
} catch(IOException ex) {
if (_log.shouldLog(Log.INFO))
if(_log.shouldLog(Log.INFO)) {
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
}
//l.log("Error connecting: " + ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
} catch(I2PException ex) {
if (_log.shouldLog(Log.INFO))
if(_log.shouldLog(Log.INFO)) {
_log.info("getPrefix(requestId) + Error trying to connect", ex);
}
//l.log("Error connecting: " + ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
@@ -970,8 +1000,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
/** @since 0.8.7 */
private void writeHelperSaveForm(OutputStream out, String destination, String ahelperKey, String targetRequest) throws IOException {
if (out == null)
if(out == null) {
return;
}
byte[] header = getErrorPage("ahelper-new", ERR_AHELPER_NEW);
out.write(header);
out.write(("<table><tr><td class=\"mediumtags\" align=\"right\">" + _("Host") +
@@ -980,7 +1011,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
String b32 = Base32.encode(SHA256Generator.getInstance().calculateHash(Base64.decode(ahelperKey)).getData());
out.write(("<tr><td class=\"mediumtags\" align=\"right\">" + _("Base 32") + "</td>" +
"<td><a href=\"http://" + b32 + ".b32.i2p/\">" + b32 + ".b32.i2p</a></td></tr>").getBytes());
} catch (Exception e) {}
} catch(Exception e) {
}
out.write(("<tr><td class=\"mediumtags\" align=\"right\">" + _("Destination") + "</td><td>" +
"<textarea rows=\"1\" style=\"height: 4em; min-width: 0; min-height: 0;\" cols=\"70\" wrap=\"off\" readonly=\"readonly\" >" +
ahelperKey + "</textarea></td></tr></table>\n" +
@@ -1015,9 +1047,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
*/
private static class InputReader {
InputStream _s;
public InputReader(InputStream s) {
_s = s;
}
String readLine(String method) throws IOException {
// Use unbuffered until we can find a BufferedReader that limits line length
//if (method == null || "POST".equals(method))
@@ -1033,12 +1067,17 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
* Prior to 0.7.12, returned b64 key
*/
private final String getHostName(String host) {
if (host == null) return null;
if (host.length() == 60 && host.toLowerCase(Locale.US).endsWith(".b32.i2p"))
if(host == null) {
return null;
}
if(host.length() == 60 && host.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
return host;
Destination dest = _context.namingService().lookup(host);
if (dest == null) return "i2p";
return Base32.encode(dest.calculateHash().getData()) + ".b32.i2p";
}
Destination _dest = _context.namingService().lookup(host);
if(_dest == null) {
return "i2p";
}
return Base32.encode(_dest.calculateHash().getData()) + ".b32.i2p";
}
/**
@@ -1086,7 +1125,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
}
return baos.toByteArray();
} finally {
try { if (fis != null) fis.close(); } catch (IOException foo) {}
try {
if(fis != null) {
fis.close();
}
} catch(IOException foo) {
}
}
// we won't ever get here
}
@@ -1103,12 +1147,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
}
private static class OnTimeout implements Runnable {
private Socket _socket;
private OutputStream _out;
private String _target;
private boolean _usingProxy;
private String _wwwProxy;
private long _requestId;
public OnTimeout(Socket s, OutputStream out, String target, boolean usingProxy, String wwwProxy, long id) {
_socket = s;
_out = out;
@@ -1117,6 +1163,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
_wwwProxy = wwwProxy;
_requestId = id;
}
public void run() {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Timeout occured requesting " + _target);
@@ -1125,7 +1172,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
closeSocket(_socket);
}
}
public static final String DEFAULT_JUMP_SERVERS =
"http://i2host.i2p/cgi-bin/i2hostjump?," +
"http://stats.i2p/cgi-bin/jump.cgi?a=," +
@@ -1155,21 +1201,26 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
out.write(_("Click a link below to look for an address helper by using a \"jump\" service:").getBytes("UTF-8"));
out.write("<br>\n".getBytes());
if (uri.startsWith("http://"))
if(uri.startsWith("http://")) {
uri = uri.substring(7);
}
StringTokenizer tok = new StringTokenizer(jumpServers, ", ");
while(tok.hasMoreTokens()) {
String jurl = tok.nextToken();
if (!jurl.startsWith("http://"))
if(!jurl.startsWith("http://")) {
continue;
}
// Skip jump servers we don't know
String jumphost = jurl.substring(7); // "http://"
jumphost = jumphost.substring(0, jumphost.indexOf('/'));
if (!jumphost.endsWith(".i2p"))
if(!jumphost.endsWith(".i2p")) {
continue;
}
if(!jumphost.endsWith(".b32.i2p")) {
Destination dest = I2PAppContext.getGlobalContext().namingService().lookup(jumphost);
if (dest == null) continue;
if(dest == null) {
continue;
}
}
out.write("<br><a href=\"".getBytes());
@@ -1196,10 +1247,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(out != null) {
try {
byte[] header;
if (usingWWWProxy)
if(usingWWWProxy) {
header = getErrorPage(I2PAppContext.getGlobalContext(), "dnfp", ERR_DESTINATION_UNKNOWN);
else
} else {
header = getErrorPage(I2PAppContext.getGlobalContext(), "dnf", ERR_DESTINATION_UNKNOWN);
}
writeErrorMessage(header, out, targetRequest, usingWWWProxy, wwwProxy, null);
} catch(IOException ioe) {
// static
@@ -1213,7 +1265,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
/** @param host ignored */
private static boolean isSupportedAddress(String host, String protocol) {
if ((host == null) || (protocol == null)) return false;
if((host == null) || (protocol == null)) {
return false;
}
/****
* Let's not look up the name _again_
@@ -1238,13 +1292,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
****/
return protocol.toLowerCase(Locale.US).equals("http");
}
private final static byte[] ERR_HELPER_DISABLED =
("HTTP/1.1 403 Disabled\r\n" +
"Content-Type: text/plain\r\n" +
"\r\n" +
"Address helpers disabled")
.getBytes();
"Address helpers disabled").getBytes();
/**
* Change various parts of the URI.
@@ -1314,17 +1366,19 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
char c = i < query.length() ? query.charAt(i) : '&';
if(c == ';' || c == '&') {
// end of key or value
if (valstart < 0)
if(valstart < 0) {
key = query.substring(keystart, i);
}
String decodedKey = LocalHTTPServer.decode(key);
if(decodedKey.equals(HELPER_PARAM)) {
String newQuery = keystart > 0 ? query.substring(0, keystart - 1) : "";
if(i < query.length() - 1) {
if (keystart > 0)
if(keystart > 0) {
newQuery += query.substring(i);
else
} else {
newQuery += query.substring(i + 1);
}
}
String value = valstart >= 0 ? query.substring(valstart, i) : "";
String helperValue = LocalHTTPServer.decode(value);
return new String[] {newQuery, helperValue};
@@ -1339,7 +1393,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
}
return null;
}
/****
private static String[] tests = {
"", "foo", "foo=bar", "&", "&=&", "===", "&&",
@@ -1366,7 +1419,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
}
}
****/
/** */
private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.web.messages";
@@ -1384,5 +1436,4 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
protected static String _(String key, Object o, Object o2) {
return Translate.getString(key, o, o2, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
}
}

View File

@@ -1,3 +1,7 @@
2012-03-27 sponge
* A hopeful fix to allow SHOUTcast/icecast to work over the http proxy.
* A little more code clean up
2012-03-26 zzz
* Code cleanups:
- Remove unused imports

View File

@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 22;
public final static long BUILD = 23;
/** for example "-test" */
public final static String EXTRA = "";