From aab8b10adf095241f1df8c061a9878980ece1028 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Thu, 20 Feb 2014 14:07:02 +0000
Subject: [PATCH]  * i2ptunnel: Add inproxy block option to HTTP server

---
 .../i2p/i2ptunnel/I2PTunnelHTTPServer.java    | 45 +++++++++++++++++--
 .../src/net/i2p/i2ptunnel/web/IndexBean.java  | 18 +++++++-
 apps/i2ptunnel/jsp/editServer.jsp             | 17 ++++++-
 3 files changed, 74 insertions(+), 6 deletions(-)

diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java
index 4e96b6a5cb..49137a1d8f 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java
@@ -48,6 +48,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
     public static final String OPT_POST_TOTAL_BAN_TIME = "postTotalBanTime";
     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 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;
@@ -95,6 +96,19 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
          "</body></html>")
          .getBytes();
 
+    private final static byte[] ERR_INPROXY =
+        ("HTTP/1.1 403 Denied\r\n"+
+         "Content-Type: text/html; charset=iso-8859-1\r\n"+
+         "Cache-control: no-cache\r\n"+
+         "Connection: close\r\n"+
+         "Proxy-Connection: close\r\n"+
+         "\r\n"+
+         "<html><head><title>403 Denied</title></head>\n"+
+         "<body><h2>403 Denied</h2>\n" +
+         "<p>Inproxy access denied. You must run <a href=\"https://geti2p.net/\">I2P</a> to access this site.</p>\n" +
+         "</body></html>")
+         .getBytes();
+
     public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
         super(host, port, privData, l, notifyThis, tunnel);
         setupI2PTunnelHTTPServer(spoofHost);
@@ -198,6 +212,24 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
                 CLIENT_SKIPHEADERS, getTunnel().getContext());
             long afterHeaders = getTunnel().getContext().clock().now();
 
+            Properties opts = getTunnel().getClientOptions();
+            if (Boolean.parseBoolean(opts.getProperty(OPT_REJECT_INPROXY)) &&
+                (headers.containsKey("X-Forwarded-For") ||
+                 headers.containsKey("X-Forwarded-Server") ||
+                 headers.containsKey("X-Forwarded-Host"))) {
+                if (_log.shouldLog(Log.WARN))
+                    _log.warn("Refusing inproxy access: " + peerHash.toBase64());
+                try {
+                    // Send a 403, so the user doesn't get an HTTP Proxy error message
+                    // and blame his router or the network.
+                    socket.getOutputStream().write(ERR_INPROXY);
+                } 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 ")) {
@@ -221,7 +253,6 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
             addEntry(headers, DEST64_HEADER, socket.getPeerDestination().toBase64());
 
             // Port-specific spoofhost
-            Properties opts = getTunnel().getClientOptions();
             String spoofHost;
             int ourPort = socket.getLocalPort();
             if (ourPort != 80 && ourPort > 0 && ourPort <= 65535 && opts != null) {
@@ -668,15 +699,21 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
                 else
                     value = "";
 
-                if ("accept-encoding".equals(name.toLowerCase(Locale.US)))
+                String lcName = name.toLowerCase(Locale.US);
+                if ("accept-encoding".equals(lcName))
                     name = "Accept-encoding";
-                else if ("x-accept-encoding".equals(name.toLowerCase(Locale.US)))
+                else if ("x-accept-encoding".equals(lcName))
                     name = "X-Accept-encoding";
+                else if ("x-forwarded-for".equals(lcName))
+                    name = "X-Forwarded-For";
+                else if ("x-forwarded-server".equals(lcName))
+                    name = "X-Forwarded-Server";
+                else if ("x-forwarded-host".equals(lcName))
+                    name = "X-Forwarded-Host";
 
                 // For incoming, we remove certain headers to prevent spoofing.
                 // For outgoing, we remove certain headers to improve anonymity.
                 boolean skip = false;
-                String lcName = name.toLowerCase(Locale.US);
                 for (String skipHeader: skipHeaders) {
                     if (skipHeader.toLowerCase(Locale.US).equals(lcName)) {
                         skip = true;
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 9b9ad31ad4..d69080592c 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
@@ -808,6 +808,21 @@ public class IndexBean {
         return false;
     }
 
+    /** @since 0.9.12 */
+    public void setRejectInproxy(String moo) {
+        _booleanOptions.add(I2PTunnelHTTPServer.OPT_REJECT_INPROXY);
+    }
+
+    /** @since 0.9.12 */
+    public boolean isRejectInproxy(int tunnel) {
+        TunnelController tun = getController(tunnel);
+        if (tun != null) {
+            Properties opts = tun.getClientOptionProps();
+            return Boolean.parseBoolean(opts.getProperty(I2PTunnelHTTPServer.OPT_REJECT_INPROXY));
+        }
+        return false;
+    }
+
     protected static final String PROP_ENABLE_ACCESS_LIST = "i2cp.enableAccessList";
     protected static final String PROP_ENABLE_BLACKLIST = "i2cp.enableBlackList";
 
@@ -1243,7 +1258,8 @@ public class IndexBean {
         };
     private static final String _booleanServerOpts[] = {
         "i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", PROP_ENABLE_ACCESS_LIST, PROP_ENABLE_BLACKLIST,
-        I2PTunnelServer.PROP_USE_SSL
+        I2PTunnelServer.PROP_USE_SSL,
+        I2PTunnelHTTPServer.OPT_REJECT_INPROXY
         };
     private static final String _otherClientOpts[] = {
         "i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.closeIdleTime",
diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp
index 978afd63ac..1a109b22d9 100644
--- a/apps/i2ptunnel/jsp/editServer.jsp
+++ b/apps/i2ptunnel/jsp/editServer.jsp
@@ -381,7 +381,22 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
                 <textarea rows="2" style="height: 8em;" cols="60" id="hostField" name="accessList" title="Access List" wrap="off" spellcheck="false"><%=editBean.getAccessList(curTunnel)%></textarea>               
             </div>
                  
-            <div class="subdivider">
+            <% if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
+            %><div class="rowItem">
+                <div id="optionsField" class="rowItem">
+                    <label>
+                        <%=intl._("Block Access via Inproxies")%>:
+                    </label>
+                </div>
+                <div id="portField" class="rowItem">
+                    <label for="access" accesskey="d">
+                        <%=intl._("Enable")%>:
+                    </label>
+                    <input value="1" type="checkbox" id="startOnLoad" name="rejectInproxy" title="Deny inproxy access when enabled"<%=(editBean.isRejectInproxy(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />                
+                </div>
+              </div>
+            <% } // httpserver
+            %><div class="subdivider">
                 <hr />
             </div>
 
-- 
GitLab