From efdc8e5df0e4fc3f70bff3de1f6c28143b01c669 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 27 Feb 2010 01:16:39 +0000 Subject: [PATCH] * HTTP Proxy: - Put B32 instead of B64 in Host: header, saves 450 bytes - Eliminate some redundant lookups - Fix http://i2p/b64/ and /eepproxy/site/ requests - Disallow a port specified for an i2p address - Cleanup and comments --- .../i2p/i2ptunnel/I2PTunnelHTTPClient.java | 103 +++++++++++++++--- history.txt | 24 ++++ .../src/net/i2p/router/RouterVersion.java | 2 +- 3 files changed, 114 insertions(+), 15 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index a86e01c65..dc8a8f627 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -26,6 +26,7 @@ import net.i2p.I2PException; import net.i2p.client.streaming.I2PSocket; import net.i2p.client.streaming.I2PSocketManager; import net.i2p.client.streaming.I2PSocketOptions; +import net.i2p.data.Base32; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.Destination; @@ -46,8 +47,14 @@ import net.i2p.util.Translate; * $method http://i2p/$b64key/$path $protocolVersion * or * $method /$site/$path $protocolVersion + * or (deprecated) + * $method /eepproxy/$site/$path $protocolVersion * * + * Note that http://i2p/$b64key/... and /eepproxy/$site/... are not recommended + * in browsers or other user-visible applications, as relative links will not + * resolve correctly, cookies won't work, etc. + * * If the $site resolves with the I2P naming service, then it is directed towards * that eepsite, otherwise it is directed towards this client's outproxy (typically * "squid.i2p"). Only HTTP is supported (no HTTPS, ftp, mailto, etc). Both GET @@ -303,14 +310,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable if (method == null) { // first line (GET /base64/realaddr) if (_log.shouldLog(Log.DEBUG)) - _log.debug(getPrefix(requestId) + "Method is null for [" + line + "]"); + _log.debug(getPrefix(requestId) + "First line [" + line + "]"); int pos = line.indexOf(" "); if (pos == -1) break; method = line.substring(0, pos); // TODO use Java URL class to make all this simpler and more robust + // That will also fix IPV6 [a:b:c] String request = line.substring(pos + 1); if (request.startsWith("/") && getTunnel().getClientOptions().getProperty("i2ptunnel.noproxy") != null) { + // what is this for ??? request = "http://i2p" + request; } else if (request.startsWith("/eepproxy/")) { // /eepproxy/foo.i2p/bar/baz.html HTTP/1.0 @@ -322,6 +331,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable } // "http://" + "foo.i2p/bar/baz.html" + " HTTP/1.0" request = "http://" + uri + subRequest.substring(protopos); + } else if (request.toLowerCase().startsWith("http://i2p/")) { + // http://i2p/b64key/bar/baz.html HTTP/1.0 + String subRequest = request.substring("http://i2p/".length()); + int protopos = subRequest.indexOf(" "); + String uri = subRequest.substring(0, protopos); + if (uri.indexOf("/") == -1) { + uri = uri + "/"; + } + // "http://" + "b64key/bar/baz.html" + " HTTP/1.0" + request = "http://" + uri + subRequest.substring(protopos); } pos = request.indexOf("//"); @@ -334,6 +353,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable targetRequest = request; + // pos is the start of the path pos = request.indexOf("/"); if (pos == -1) { method = null; @@ -354,9 +374,22 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable } } - if (host.toLowerCase().equals("proxy.i2p")) { + // Go through the various types of host names, set + // the host and destination variables accordingly, + // and transform the first line. + // For all i2p network hosts, ensure that the host is a + // Base 32 hostname so that we do not reveal our name for it + // in our addressbook (all naming is local), + // and it is removed from the request line. + + if (host.length() >= 516 && host.indexOf(".") < 0) { + // http://b64key/bar/baz.html + destination = host; + host = getHostName(destination); + line = method + ' ' + request.substring(pos); + } else if (host.toLowerCase().equals("proxy.i2p")) { // so we don't do any naming service lookups - destination = "proxy.i2p"; + destination = host; usingInternalServer = true; } else if (host.toLowerCase().endsWith(".i2p")) { // Destination gets the host name @@ -482,6 +515,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix(requestId) + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!"); } else { + // what is left for here? a hostname with no dots, and != "i2p" + // and not a destination ??? + // Perhaps something in privatehosts.txt ... request = request.substring(pos + 1); pos = request.indexOf("/"); if (pos < 0) { @@ -494,31 +530,49 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable return; } destination = request.substring(0, pos); + host = getHostName(destination); line = method + " " + request.substring(pos); } // end host name processing + if (port != 80 && !usingWWWProxy) { + if (out != null) { + out.write(getErrorPage("denied", ERR_REQUEST_DENIED)); + writeFooter(out); + } + s.close(); + return; + } + boolean isValid = usingWWWProxy || usingInternalServer || isSupportedAddress(host, protocol); if (!isValid) { if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "notValid(" + host + ")"); method = null; destination = null; break; - } else if ((!usingWWWProxy) && (!usingInternalServer)) { - if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "host=getHostName(" + destination + ")"); - host = getHostName(destination); // hide original host } + // don't do this, it forces yet another hostname lookup, + // and in all cases host was already set above + //if ((!usingWWWProxy) && (!usingInternalServer)) { + // String oldhost = host; + // host = getHostName(destination); // hide original host + // if (_log.shouldLog(Log.INFO)) + // _log.info(getPrefix(requestId) + " oldhost " + oldhost + " newhost " + host + " dest " + destination); + //} + if (_log.shouldLog(Log.DEBUG)) { - _log.debug(getPrefix(requestId) + "METHOD:" + method + ":"); - _log.debug(getPrefix(requestId) + "PROTOC:" + protocol + ":"); - _log.debug(getPrefix(requestId) + "HOST :" + host + ":"); - _log.debug(getPrefix(requestId) + "DEST :" + destination + ":"); + _log.debug(getPrefix(requestId) + "METHOD: \"" + method + "\""); + _log.debug(getPrefix(requestId) + "PROTOC: \"" + protocol + "\""); + _log.debug(getPrefix(requestId) + "HOST : \"" + host + "\""); + _log.debug(getPrefix(requestId) + "DEST : \"" + destination + "\""); } // end first line processing } else { if (lowercaseLine.startsWith("host: ") && !usingWWWProxy) { + // 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)) _log.info(getPrefix(requestId) + "Setting host = " + host); @@ -575,7 +629,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable _log.debug(getPrefix(requestId) + "NewRequest header: [" + newRequest.toString() + "]"); if (method == null || destination == null) { - l.log("No HTTP method found in the request."); + //l.log("No HTTP method found in the request."); if (out != null) { if ("http://".equalsIgnoreCase(protocol)) out.write(getErrorPage("denied", ERR_REQUEST_DENIED)); @@ -598,7 +652,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable return; } - Destination clientDest = I2PTunnel.destFromName(destination); + // If the host is "i2p", the getHostName() lookup failed, don't try to + // look it up again as the naming service does not do negative caching + // so it will be slow. + + Destination clientDest; + if ("i2p".equals(host)) + clientDest = null; + else + clientDest = I2PTunnel.destFromName(destination); + if (clientDest == null) { //l.log("Could not resolve " + destination + "."); if (_log.shouldLog(Log.WARN)) @@ -679,12 +742,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable } } + /** + * @return b32hash.b32.i2p, or "i2p" on lookup failure. + * Prior to 0.7.12, returned b64 key + */ private final static String getHostName(String host) { if (host == null) return null; + if (host.length() == 60 && host.toLowerCase().endsWith(".b32.i2p")) + return host; try { Destination dest = I2PTunnel.destFromName(host); if (dest == null) return "i2p"; - return dest.toBase64(); + return Base32.encode(dest.calculateHash().getData()) + ".b32.i2p"; } catch (DataFormatException dfe) { return "i2p"; } @@ -858,8 +927,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable private final static String SUPPORTED_HOSTS[] = { "i2p", "www.i2p.com", "i2p."}; + /** @param host ignored */ private static boolean isSupportedAddress(String host, String protocol) { if ((host == null) || (protocol == null)) return false; + + /**** + * Let's not look up the name _again_ + * and now that host is a b32, this was failing + * boolean found = false; String lcHost = host.toLowerCase(); for (int i = 0; i < SUPPORTED_HOSTS.length; i++) { @@ -876,7 +951,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable } catch (DataFormatException dfe) { } } - + ****/ return protocol.equalsIgnoreCase("http://"); } diff --git a/history.txt b/history.txt index 07595dd11..a67132e12 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,27 @@ +2010-02-27 zzz + * eepsite: Add some help to index.html + * HTTP Proxy: + - Put B32 instead of B64 in Host: header, saves 450 bytes + - Eliminate some redundant lookups + - Fix http://i2p/b64/ and /eepproxy/site/ requests + - Disallow a port specified for an i2p address + - Cleanup and comments + * i2psnark: + - Fix NPE after create file failure + - Sanitize more characters in file names + * netdb: Fix NPE after OOM http://trac.i2p2.i2p/ticket/38 + * NTCP Transport: + - Replace lists with concurrent queues in EventPumper + and NTCPConnection to remove global locks + - Java 5 cleanup + * Plugins: Support console themes + * UDP Transport: + - Replace the unused-since-2006 TimedWeightedPriorityMessageQueue + with DummyThrottle + - Don't instantiate and start TWPMQ Cleaner and OutboundRefiller + threads, part of priority queues unused since 0.6.1.11 + - Don't instantiate and start UDPFlooder, it is for testing only + 2010-02-23 zzz * Unzip: Any files in the zip with a .jar.pack or .war.pack extension will be transparently unpacked with unpack200. Savings is about 60%. diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 8c54ff98c..e72c467bc 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -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 = 5; + public final static long BUILD = 6; /** for example "-test" */ public final static String EXTRA = "";