Compare commits

...

30 Commits

Author SHA1 Message Date
idk
b2c3f4d724 Make failTimeout and encodeUTF static, make failTimeout and _log final, remove FIXME, set targetHost prior to performing tests 2021-07-12 10:18:02 -04:00
idk
ab8b4470a4 don't allow the headerContents function to assume the query is present 2021-07-08 11:16:54 -04:00
idk
c9120f7760 remove excess whitespace 2021-07-08 11:13:57 -04:00
idk
b15d646673 remove EEPSITE_DIR and unclutter diff a little bit 2021-07-08 11:11:55 -04:00
idk
b5bd4dff79 delete copyConfig and move functionality back into parseArgs 2021-07-08 11:09:34 -04:00
idk
7b46003f1d Set up the URL for inside of the header by constructing a URI, it would appear that this is the most reliable way to ensure that everything is properly escaped, URLEncode will not cut it 2021-07-08 10:55:00 -04:00
idk
a25698b4be also match ipv6 all addresses '::', write down what it actually does 2021-07-06 23:22:48 -04:00
idk
f1ca79a310 It answers on hostnames if the hostname matches the config file or the server listens on all addresses 2021-07-06 18:05:02 -04:00
idk
52bc480119 no need to copy ALTSVC_CONFIG because it doesn't exist 2021-06-25 19:03:32 -04:00
idk
fb80a32086 Remove ALTSVC_CONFIG final variable 2021-06-23 11:53:51 -04:00
idk
22c1eb49d5 undo changes to base-context.xml 2021-06-23 11:50:18 -04:00
idk
7fcf764ce7 Only encode the query, not the whole URL 2021-06-22 16:00:51 -04:00
idk
404c69500a fix the URL encoding and limit the number of times that a key-check will be attempted to once every ten minutes 2021-06-22 14:52:12 -04:00
idk
4185eeda67 Use getPathInfo and getQueryString instead of constructing a whole URL 2021-06-21 08:32:56 -04:00
idk
353ea48503 Second nested if in handle should be new if, not else-if 2021-06-20 00:11:13 -04:00
idk
555467dda9 Switch to using the X-I2P-DestHash to determine if a request came from inside of I2P. 2021-06-20 00:08:34 -04:00
idk
cfdb0d411d This time, only attempt to set the x-i2p-location header if the request comes in from a non-I2P domain. This should prevent it from unduly impacting the speed of those responses. If it never gets a request from a non-i2p domain, it never attempts to load the keys and therefore doesn't block. Style changes, camelcase fixes. 2021-06-19 23:40:27 -04:00
idk
bfa17c53ec Match 0.0.0.0 during hostname check 2021-06-16 23:34:07 -04:00
idk
d5a10247bb Add the HandlerWrapper example from zzz's comment on i2pgit.org 2021-06-16 22:54:29 -04:00
idk
47f35e6666 Remove ID parameter which isn't used in base-context.xml 2021-06-03 23:56:47 -04:00
idk
5e94d1cc8d Work on moving configuration out of base-context.xml 2021-06-03 23:47:50 -04:00
idk
f3cd03589a Work on moving configuration out of base-context.xml 2021-06-03 23:38:35 -04:00
idk
6d1b92cb44 synchronize X_I2P_LOCATION 2021-06-03 13:56:31 -04:00
idk
f16b7ed7aa Add the filter back to base-context.xml 2021-04-08 19:34:54 -04:00
idk
ffb7ef838b Switch to finding the correct keys by searching the i2ptunnel.config file or the i2ptunnel.config.d directory without needing the TunnelControllerGroup to be initialized. It will technically fail on the first run, but since it checks for the site's b32 every time the filter is run until it has a copy, the errors will go away after the keys are fully generated. 2021-04-08 19:30:28 -04:00
idk
d943fcc0e5 Merge branch 'master' of i2pgit.org:idk/i2p.i2p into x-i2p-location 2021-04-08 12:46:13 -04:00
idk
5ce995972d checkin to clear up all the easy parts of the review, i.e. redundant while loop, reduce scope of GetTunnelControllerByAddress, null checks instead of blank string checks 2021-03-01 20:15:45 -05:00
idk
33b573be5f fix missing </Call> 2021-02-16 19:20:53 -05:00
idk
c36425c190 add configuration to base-context for the default site 2021-02-16 18:23:27 -05:00
idk
532bb07ae0 Added x-i2p-location support 2021-02-16 18:14:37 -05:00
2 changed files with 192 additions and 1 deletions

View File

@ -65,7 +65,6 @@ public class JettyStart implements ClientApp {
private static final String GZIP_DIR = "eepsite-jetty9.3";
private static final String GZIP_CONFIG = "jetty-gzip.xml";
private static final String MIN_GZIP_HANDLER_VER = "9.3";
/**
* All args must be XML file names.
* Does not support any of the other argument types from org.mortbay.start.Main.
@ -179,6 +178,10 @@ public class JettyStart implements ClientApp {
public void run() {
for (LifeCycle lc : _jettys) {
if (!lc.isRunning()) {
if (lc instanceof Server) {
Server server = (Server) lc;
server.insertHandler(new XI2PLocationFilter());
}
try {
lc.start();
if (_context != null) {

View File

@ -0,0 +1,188 @@
package net.i2p.jetty;
import java.io.IOException;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.PrivateKeyFile;
import net.i2p.util.Log;
/**
* Adds a header, X-I2P-Location, to requests when they do **not** come in on an I2P hostname.
* This header contains a URL that looks like: [scheme://][i2phostname.i2p][/path][?query]
* and expresses the I2P-Equivalent URL of the clearnet query. Clients can use this to prompt
* users to switch from a non-I2P host to an I2P host or to redirect them automatically. It
* automatically enabled on the default I2P site located on port 7658 by default.
*
* @since 0.9.51
*/
public class XI2PLocationFilter extends HandlerWrapper {
private String X_I2P_Location = null;
private long lastFailure = -1;
private static final long failTimeout = 600000;
private static final String encodeUTF = StandardCharsets.UTF_8.toString();
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(XI2PLocationFilter.class);
private synchronized void setLocation(String xi2plocation) {
if (_log.shouldInfo())
_log.info("Checking X-I2P-Location header prefix" + xi2plocation);
if (X_I2P_Location != null)
return ;
if (xi2plocation == null)
return ;
if (xi2plocation.equals(""))
return ;
X_I2P_Location = xi2plocation;
if (_log.shouldInfo())
_log.info("Caching X-I2P-Location header prefix" + X_I2P_Location);
}
private synchronized boolean shouldRecheck(){
boolean settable = (X_I2P_Location == null);
if (!settable) return settable;
if (lastFailure == -1) {
lastFailure = System.currentTimeMillis();
if (_log.shouldDebug())
_log.debug("New instance, attempting to set X-I2P-Location header for the first time");
return settable;
}
if ((System.currentTimeMillis() - lastFailure) > failTimeout){
lastFailure = System.currentTimeMillis();
if (_log.shouldDebug())
_log.debug("More than ten minutes since failing attempt to re-check X-I2P-Location header");
return settable;
}
if (_log.shouldDebug())
_log.debug("Not attempting to re-check X-I2P-Location header");
return false;
}
private synchronized String getXI2PLocation(String host, String port) {
File configDir = I2PAppContext.getGlobalContext().getConfigDir();
File tunnelConfig = new File(configDir, "i2ptunnel.config");
boolean isSingleFile = tunnelConfig.exists();
if (!isSingleFile) {
File tunnelConfigD = new File(configDir, "i2ptunnel.config.d");
File[] configFiles = tunnelConfigD.listFiles(new net.i2p.util.FileSuffixFilter(".config"));
if (configFiles == null)
return null;
for (int fnum=0; fnum < configFiles.length; fnum++) {
Properties tunnelProps = new Properties();
try {
DataHelper.loadProps(tunnelProps, configFiles[fnum]);
String targetHost = tunnelProps.getProperty("targetHost");
boolean hostmatch = (host.equals(targetHost) || "0.0.0.0".equals(targetHost) || "::".equals(targetHost));
if ( hostmatch && port.equals(tunnelProps.getProperty("targetPort")) ) {
String sh = tunnelProps.getProperty("spoofedHost");
if (sh != null) {
if (sh.endsWith(".i2p"))
return sh;
}
String kf = tunnelProps.getProperty("privKeyFile");
if (kf != null) {
File keyFile = new File(kf);
if (!keyFile.isAbsolute())
keyFile = new File(configDir, kf);
if (keyFile.exists()) {
PrivateKeyFile pkf = new PrivateKeyFile(keyFile);
try {
Destination rv = pkf.getDestination();
if (rv != null)
return rv.toBase32();
} catch (I2PException e) {
if (_log.shouldWarn())
_log.warn("I2PException Unable to set X-I2P-Location, keys arent ready. This is probably safe to ignore and will go away after the first run." + e);
return null;
} catch (IOException e) {
if (_log.shouldWarn())
_log.warn("IOE Unable to set X-I2P-Location, location is uninitialized due file not found. This probably means the keys aren't ready. This is probably safe to ignore." + e);
return null;
}
}
}
if (_log.shouldWarn())
_log.warn("Unable to set X-I2P-Location, location is target not found in any I2PTunnel config file. This should never happen.");
return null;
}
} catch (IOException ioe) {
if (_log.shouldWarn())
_log.warn("IOE Unable to set X-I2P-Location, location is uninitialized. This is probably safe to ignore. location='" + ioe + "'");
return null;
}
}
} else {
// don't bother
}
return null;
}
private synchronized String headerContents(final HttpServletRequest httpRequest) {
if (X_I2P_Location != null) {
String scheme = httpRequest.getScheme();
if (scheme == null)
scheme = "";
String path = httpRequest.getPathInfo();
if (path == null)
path = "";
String query = httpRequest.getQueryString();
if (query == null)
query = "";
try {
if (query.equals("")) {
URI uri = new URI(scheme, X_I2P_Location, path, null);
String encodedURL = uri.toASCIIString();
return encodedURL;
} else {
URI uri = new URI(scheme, X_I2P_Location, path, query, null);
String encodedURL = uri.toASCIIString();
return encodedURL;
}
}catch(URISyntaxException use){
return null;
}
}
return null;
}
@Override
public void handle(final String target, final Request request, final HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException {
final String hashHeader = httpRequest.getHeader("X-I2P-DestHash");
if (hashHeader == null) {
if (shouldRecheck()) {
String xi2plocation = getXI2PLocation(request.getLocalAddr(), String.valueOf(request.getLocalPort()));
if (_log.shouldInfo())
_log.info("Checking X-I2P-Location header IP " + request.getLocalAddr() + " port " + request.getLocalPort() + " prefix " + xi2plocation);
setLocation(xi2plocation);
}
String headerURL = headerContents(httpRequest);
if (headerURL != null) {
if (_log.shouldInfo())
_log.info("Checking X-I2P-Location header" + headerURL);
httpResponse.addHeader("X-I2P-Location", headerURL);
}
}
_handler.handle(target, request, httpRequest, httpResponse);
}
}