diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java index 157a94011..acb1c532b 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java @@ -25,6 +25,7 @@ import javax.net.ssl.SSLException; import net.i2p.client.streaming.I2PSocket; import net.i2p.I2PAppContext; +import net.i2p.data.Base32; import net.i2p.data.ByteArray; import net.i2p.data.DataHelper; import net.i2p.data.Hash; @@ -50,6 +51,9 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { public static final String OPT_POST_MAX = "maxPosts"; public static final String OPT_POST_TOTAL_MAX = "maxTotalPosts"; public static final String OPT_REJECT_INPROXY = "rejectInproxy"; + public static final String OPT_REJECT_REFERER = "rejectReferer"; + public static final String OPT_REJECT_USER_AGENTS = "rejectUserAgents"; + public static final String OPT_USER_AGENTS = "userAgentRejectList"; public static final int DEFAULT_POST_WINDOW = 5*60; public static final int DEFAULT_POST_BAN_TIME = 30*60; public static final int DEFAULT_POST_TOTAL_BAN_TIME = 10*60; @@ -64,7 +68,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { private static final String[] CLIENT_SKIPHEADERS = {HASH_HEADER, DEST64_HEADER, DEST32_HEADER}; private static final String SERVER_HEADER = "Server"; private static final String X_POWERED_BY_HEADER = "X-Powered-By"; - private static final String[] SERVER_SKIPHEADERS = {SERVER_HEADER, X_POWERED_BY_HEADER}; + private static final String X_RUNTIME_HEADER = "X-Runtime"; // Rails + private static final String[] SERVER_SKIPHEADERS = {SERVER_HEADER, X_POWERED_BY_HEADER, X_RUNTIME_HEADER }; /** timeout for first request line */ private static final long HEADER_TIMEOUT = 15*1000; /** total timeout for the request and all the headers */ @@ -357,7 +362,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { headers.containsKey("X-Forwarded-Host"))) { if (_log.shouldLog(Log.WARN)) { StringBuilder buf = new StringBuilder(); - buf.append("Refusing inproxy access: ").append(peerHash.toBase64()); + buf.append("Refusing inproxy access: ").append(Base32.encode(peerHash.getData())).append(".b32.i2p"); List h = headers.get("X-Forwarded-For"); if (h != null) buf.append(" from: ").append(h.get(0)); @@ -380,12 +385,55 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { return; } + if (Boolean.parseBoolean(opts.getProperty(OPT_REJECT_REFERER)) && + headers.containsKey("Referer")) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Refusing access from: " + + Base32.encode(peerHash.getData()) + ".b32.i2p" + + " with Referer: " + headers.get("Referer").get(0)); + try { + socket.getOutputStream().write(ERR_INPROXY.getBytes("UTF-8")); + } catch (IOException ioe) {} + try { + socket.close(); + } catch (IOException ioe) {} + return; + } + + if (Boolean.parseBoolean(opts.getProperty(OPT_REJECT_USER_AGENTS)) && + headers.containsKey("User-Agent")) { + String ua = headers.get("User-Agent").get(0); + if (!ua.startsWith("MYOB")) { + String blockAgents = opts.getProperty(OPT_USER_AGENTS); + if (blockAgents != null) { + String[] agents = DataHelper.split(blockAgents, ","); + for (int i = 0; i < agents.length; i++) { + String ag = agents[i].trim(); + if (ag.length() > 0 && ua.contains(ag)) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Refusing access from: " + + Base32.encode(peerHash.getData()) + ".b32.i2p" + + " with User-Agent: " + ua); + try { + socket.getOutputStream().write(ERR_INPROXY.getBytes("UTF-8")); + } catch (IOException ioe) {} + try { + socket.close(); + } catch (IOException ioe) {} + return; + } + } + } + } + } + if (_postThrottler != null && command.length() >= 5 && command.substring(0, 5).toUpperCase(Locale.US).equals("POST ")) { if (_postThrottler.shouldThrottle(peerHash)) { if (_log.shouldLog(Log.WARN)) - _log.warn("Refusing POST since peer is throttled: " + peerHash.toBase64()); + _log.warn("Refusing POST since peer is throttled: " + + Base32.encode(peerHash.getData()) + ".b32.i2p"); try { // Send a 403, so the user doesn't get an HTTP Proxy error message // and blame his router or the network. @@ -490,7 +538,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { socket.close(); } catch (IOException ioe) {} if (_log.shouldLog(Log.WARN)) - _log.warn("Error while receiving the new HTTP request", ex); + _log.warn("Error while receiving the new HTTP request from: " + Base32.encode(peerHash.getData()) + ".b32.i2p", ex); } catch (OutOfMemoryError oom) { // Often actually a file handle limit problem so we can safely send a response // java.lang.OutOfMemoryError: unable to create new native thread @@ -814,6 +862,9 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { * From I2P to server: socket non-null, in null. * From server to I2P: socket null, in non-null. * + * Note: This does not handle RFC 2616 header line splitting, + * which is obsoleted in RFC 7230. + * * @param socket if null, use in as InputStream * @param in if null, use socket.getInputStream() as InputStream * @param command out parameter, first line @@ -871,7 +922,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { } int split = buf.indexOf(":"); if (split <= 0) - throw new BadRequestException("Invalid HTTP header, missing colon"); + throw new BadRequestException("Invalid HTTP header, missing colon: \"" + buf + + "\" request: \"" + command + '"'); totalSize += buf.length(); if (totalSize > MAX_TOTAL_HEADER_SIZE) throw new LineTooLongException("Req+headers too big"); @@ -893,6 +945,10 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { name = "X-Forwarded-Server"; else if ("x-forwarded-host".equals(lcName)) name = "X-Forwarded-Host"; + else if ("user-agent".equals(lcName)) + name = "User-Agent"; + else if ("referer".equals(lcName)) + name = "Referer"; // For incoming, we remove certain headers to prevent spoofing. // For outgoing, we remove certain headers to improve anonymity. diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/GeneralHelper.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/GeneralHelper.java index eb481f6ea..50d57b710 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/GeneralHelper.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/GeneralHelper.java @@ -630,6 +630,21 @@ public class GeneralHelper { return getBooleanProperty(tunnel, I2PTunnelHTTPServer.OPT_REJECT_INPROXY); } + /** @since 0.9.25 */ + public boolean getRejectReferer(int tunnel) { + return getBooleanProperty(tunnel, I2PTunnelHTTPServer.OPT_REJECT_REFERER); + } + + /** @since 0.9.25 */ + public boolean getRejectUserAgents(int tunnel) { + return getBooleanProperty(tunnel, I2PTunnelHTTPServer.OPT_REJECT_USER_AGENTS); + } + + /** @since 0.9.25 */ + public String getUserAgents(int tunnel) { + return getProperty(tunnel, I2PTunnelHTTPServer.OPT_USER_AGENTS, ""); + } + public boolean getUniqueLocal(int tunnel) { return getBooleanProperty(tunnel, I2PTunnelServer.PROP_UNIQUE_LOCAL); } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java index c0839e503..7cf91f1ba 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/ui/TunnelConfig.java @@ -214,6 +214,29 @@ public class TunnelConfig { else _booleanOptions.remove(I2PTunnelHTTPServer.OPT_REJECT_INPROXY); } + + /** @since 0.9.25 */ + public void setRejectReferer(boolean val) { + if (val) + _booleanOptions.add(I2PTunnelHTTPServer.OPT_REJECT_REFERER); + else + _booleanOptions.remove(I2PTunnelHTTPServer.OPT_REJECT_REFERER); + } + + /** @since 0.9.25 */ + public void setRejectUserAgents(boolean val) { + if (val) + _booleanOptions.add(I2PTunnelHTTPServer.OPT_REJECT_USER_AGENTS); + else + _booleanOptions.remove(I2PTunnelHTTPServer.OPT_REJECT_USER_AGENTS); + } + + /** @since 0.9.25 */ + public void setUserAgents(String val) { + if (val != null) + _otherOptions.put(I2PTunnelHTTPServer.OPT_USER_AGENTS, val.trim()); + } + public void setUniqueLocal(boolean val) { if (val) _booleanOptions.add(I2PTunnelServer.PROP_UNIQUE_LOCAL); @@ -675,6 +698,8 @@ public class TunnelConfig { "i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", PROP_ENABLE_ACCESS_LIST, PROP_ENABLE_BLACKLIST, I2PTunnelServer.PROP_USE_SSL, I2PTunnelHTTPServer.OPT_REJECT_INPROXY, + I2PTunnelHTTPServer.OPT_REJECT_REFERER, + I2PTunnelHTTPServer.OPT_REJECT_USER_AGENTS, I2PTunnelServer.PROP_UNIQUE_LOCAL, "shouldBundleReplyInfo" }; @@ -700,7 +725,8 @@ public class TunnelConfig { I2PTunnelHTTPServer.OPT_POST_BAN_TIME, I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME, I2PTunnelHTTPServer.OPT_POST_MAX, - I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX + I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX, + I2PTunnelHTTPServer.OPT_USER_AGENTS }; /** 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 d960dc93a..e328f64ae 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java @@ -255,6 +255,11 @@ public class EditBean extends IndexBean { return _helper.getMultihome(tunnel); } + /** @since 0.9.25 */ + public String getUserAgents(int tunnel) { + return _helper.getUserAgents(tunnel); + } + /** all proxy auth @since 0.8.2 */ public boolean getProxyAuth(int tunnel) { return _helper.getProxyAuth(tunnel) != "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 2f2509c08..15897cb61 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -679,6 +679,31 @@ public class IndexBean { return _helper.getRejectInproxy(tunnel); } + /** @since 0.9.25 */ + public void setRejectReferer(String moo) { + _config.setRejectReferer(true); + } + + /** @since 0.9.25 */ + public boolean isRejectReferer(int tunnel) { + return _helper.getRejectReferer(tunnel); + } + + /** @since 0.9.25 */ + public void setRejectUserAgents(String moo) { + _config.setRejectUserAgents(true); + } + + /** @since 0.9.25 */ + public boolean isRejectUserAgents(int tunnel) { + return _helper.getRejectUserAgents(tunnel); + } + + /** @since 0.9.25 */ + public void setUserAgents(String agents) { + _config.setUserAgents(agents); + } + /** @since 0.9.13 */ public void setUniqueLocal(String moo) { _config.setUniqueLocal(true); diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp index 22810a2ee..a7fb132d5 100644 --- a/apps/i2ptunnel/jsp/editServer.jsp +++ b/apps/i2ptunnel/jsp/editServer.jsp @@ -405,11 +405,11 @@ input.default { width: 1px; height: 1px; visibility: hidden; } class="tickbox" /> -
+
- +
<% if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) { @@ -426,6 +426,35 @@ input.default { width: 1px; height: 1px; visibility: hidden; } class="tickbox" />
+
+
+ +
+
+ + class="tickbox" /> +
+
+
+
+ +
+
+ + class="tickbox" /> +
+
+ +
+
<% } // httpserver %>
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionManager.java b/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionManager.java index 5861be6d1..debd75a5c 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionManager.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionManager.java @@ -241,7 +241,7 @@ class ConnectionManager { if (why != null) { if ((!_defaultOptions.getDisableRejectLogging()) || _log.shouldLog(Log.WARN)) _log.logAlways(Log.WARN, "Refusing connection since peer is " + why + - (synPacket.getOptionalFrom() == null ? "" : ": " + synPacket.getOptionalFrom().calculateHash().toBase64())); + (synPacket.getOptionalFrom() == null ? "" : ": " + synPacket.getOptionalFrom().toBase32())); reject = true; } else { assignReceiveStreamId(con); diff --git a/installer/resources/themes/console/light/i2ptunnel.css b/installer/resources/themes/console/light/i2ptunnel.css index 124bc43af..d5c43d37e 100644 --- a/installer/resources/themes/console/light/i2ptunnel.css +++ b/installer/resources/themes/console/light/i2ptunnel.css @@ -33,6 +33,11 @@ margin-right: 4px; } +#tunnelEditPage #accessListField { + width: 434px; + margin-right: 4px; +} + #tunnelEditPage #portField, #tunnelEditPage #optionsPortField, #tunnelEditPage #backupField, #tunnelEditPage #varianceField { width: 140px; }