hosts) {
+ _listenHosts.clear();
+ _listenHosts.addAll(hosts);
+ }
+
+ /**
+ * Block by Host header,
+ * redirect HTTP to HTTPS,
+ * pass everything else to the delegate.
+ */
+ public void handle(String pathInContext,
+ Request baseRequest,
+ HttpServletRequest httpRequest,
+ HttpServletResponse httpResponse)
+ throws IOException, ServletException
+ {
+
+ String host = httpRequest.getHeader("Host");
+ if (!allowHost(host)) {
+ Log log = _context.logManager().getLog(HostCheckHandler.class);
+ host = getHost(host);
+ String s = "Console request denied.\n" +
+ " To allow access using the hostname \"" + host + "\", add the line \"" +
+ PROP_ALLOWED_HOSTS + '=' + host +
+ "\" in the file " + RunStandalone.APP_CONFIG_FILE.getAbsolutePath() + " and restart.";
+ log.logAlways(Log.WARN, s);
+ httpResponse.sendError(403, s);
+ baseRequest.setHandled(true);
+ return;
+ }
+ }
+
+ /**
+ * Should we allow a request with this Host header?
+ *
+ * ref: https://en.wikipedia.org/wiki/DNS_rebinding
+ *
+ * @param host the HTTP Host header, null ok
+ * @return true if OK
+ */
+ private boolean allowHost(String host) {
+ if (host == null)
+ return true;
+ // common cases
+ if (host.equals("127.0.0.1:8002") ||
+ host.equals("localhost:8002") ||
+ host.equals("[::1]:8002"))
+ return true;
+ // all allowed?
+ if (_listenHosts.isEmpty())
+ return true;
+ host = getHost(host);
+ if (_listenHosts.contains(host))
+ return true;
+ // allow all IP addresses
+ if (InetAddressUtils.isIPv4Address(host) || InetAddressUtils.isIPv6Address(host))
+ return true;
+ //System.out.println(host + " not found in " + s);
+ return false;
+ }
+
+ /**
+ * Strip [] and port from a host header
+ *
+ * @param host the HTTP Host header non-null
+ */
+ private static String getHost(String host) {
+ if (host.startsWith("[")) {
+ host = host.substring(1);
+ int brack = host.indexOf(']');
+ if (brack >= 0)
+ host = host.substring(0, brack);
+ } else {
+ int colon = host.indexOf(':');
+ if (colon >= 0)
+ host = host.substring(0, colon);
+ }
+ return host;
+ }
+}
diff --git a/apps/i2psnark/java/src/org/klomp/snark/standalone/RunStandalone.java b/apps/i2psnark/java/src/org/klomp/snark/standalone/RunStandalone.java
index 2a954020a1..b5800a516b 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/standalone/RunStandalone.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/standalone/RunStandalone.java
@@ -1,9 +1,12 @@
package org.klomp.snark.standalone;
import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.apps.systray.UrlLauncher;
+import net.i2p.data.DataHelper;
import net.i2p.jetty.JettyStart;
/**
@@ -16,9 +19,16 @@ public class RunStandalone {
private int _port = 8002;
private String _host = "127.0.0.1";
private static RunStandalone _instance;
+ static final File APP_CONFIG_FILE = new File("i2psnark-appctx.config");
private RunStandalone(String args[]) throws Exception {
- _context = I2PAppContext.getGlobalContext();
+ Properties p = new Properties();
+ if (APP_CONFIG_FILE.exists()) {
+ try {
+ DataHelper.loadProps(p, APP_CONFIG_FILE);
+ } catch (IOException ioe) {}
+ }
+ _context = new I2PAppContext(p);
File base = _context.getBaseDir();
File xml = new File(base, "jetty-i2psnark.xml");
_jettyStart = new JettyStart(_context, null, new String[] { xml.getAbsolutePath() } );
diff --git a/apps/i2psnark/java/src/org/klomp/snark/standalone/package.html b/apps/i2psnark/java/src/org/klomp/snark/standalone/package.html
new file mode 100644
index 0000000000..096772850d
--- /dev/null
+++ b/apps/i2psnark/java/src/org/klomp/snark/standalone/package.html
@@ -0,0 +1,7 @@
+
+
+
+Classes only used for, and bundled with, the standalone installation. Since 0.9.27.
+
+
+
diff --git a/apps/i2psnark/jetty-i2psnark.xml b/apps/i2psnark/jetty-i2psnark.xml
index 4a525ce8f6..f3562daa21 100644
--- a/apps/i2psnark/jetty-i2psnark.xml
+++ b/apps/i2psnark/jetty-i2psnark.xml
@@ -55,6 +55,9 @@
+ -
+
+
-
diff --git a/apps/jetty/java/src/net/i2p/servlet/filters/XSSFilter.java b/apps/jetty/java/src/net/i2p/servlet/filters/XSSFilter.java
index 9fcce1aba0..b300a03a32 100644
--- a/apps/jetty/java/src/net/i2p/servlet/filters/XSSFilter.java
+++ b/apps/jetty/java/src/net/i2p/servlet/filters/XSSFilter.java
@@ -33,7 +33,11 @@ public class XSSFilter implements Filter {
// We need to send the error quickly, if we just throw a ServletException,
// the data keeps coming and the connection gets reset.
// This way we at least get the error to the browser.
- ((HttpServletResponse)response).sendError(413, ise.getMessage());
+ try {
+ ((HttpServletResponse)response).sendError(413, ise.getMessage());
+ } catch (IllegalStateException ise2) {
+ // Committed, probably wasn't a multipart form error after all
+ }
}
}
}
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/HostCheckHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/HostCheckHandler.java
index ff862d3bba..1805f9f7e5 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/HostCheckHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/HostCheckHandler.java
@@ -78,6 +78,7 @@ public class HostCheckHandler extends HandlerWrapper
"\" to advanced configuration and restart.";
log.logAlways(Log.WARN, s);
httpResponse.sendError(403, s);
+ baseRequest.setHandled(true);
return;
}
@@ -92,6 +93,7 @@ public class HostCheckHandler extends HandlerWrapper
if (Boolean.valueOf(redir) ||
(redir == null && "1".equals(httpRequest.getHeader("Upgrade-Insecure-Requests")))) {
sendRedirect(httpsPort, httpRequest, httpResponse);
+ baseRequest.setHandled(true);
return;
}
}