forked from I2P_Developers/i2p.i2p
UPnP fixes part 7:
Don't set I2P user agent, spoof MiniUPnPc Don't convert string-to-bytes-to-string Don't fallback to alternate code after failure Don't use HttpURLConnection if proxy enabled Add location sanity checks Force Connection: close Don't attempt to set Host header, HttpURLConnection ignores it Disable following redirects
This commit is contained in:
@ -157,11 +157,14 @@ public class Router implements RouterClock.ClockShiftListener {
|
||||
System.setProperty("networkaddress.cache.ttl", DNS_CACHE_TIME);
|
||||
System.setProperty("networkaddress.cache.negative.ttl", DNS_NEG_CACHE_TIME);
|
||||
}
|
||||
if (System.getProperty("I2P_DISABLE_HTTP_AGENT_OVERRIDE") == null) {
|
||||
System.setProperty("http.agent", "I2P");
|
||||
}
|
||||
// ref: https://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html#Proxies
|
||||
// This doesn't do what you think, and a bad idea anyway
|
||||
//if (System.getProperty("I2P_DISABLE_HTTP_AGENT_OVERRIDE") == null) {
|
||||
// System.setProperty("http.agent", "I2P");
|
||||
//}
|
||||
if (System.getProperty("I2P_DISABLE_HTTP_KEEPALIVE_OVERRIDE") == null) {
|
||||
// (no need for keepalive)
|
||||
// Note that doc link above is wrong, it IS capital A Alive
|
||||
System.setProperty("http.keepAlive", "false");
|
||||
}
|
||||
// Save it for LogManager
|
||||
|
@ -27,6 +27,9 @@ import java.io.ByteArrayInputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import net.i2p.util.Addresses;
|
||||
import net.i2p.router.transport.TransportUtil;
|
||||
|
||||
import org.cybergarage.http.HTTP;
|
||||
import org.cybergarage.http.HTTPRequest;
|
||||
import org.cybergarage.http.HTTPResponse;
|
||||
@ -34,6 +37,15 @@ import org.cybergarage.util.Debug;
|
||||
|
||||
public abstract class Parser
|
||||
{
|
||||
// I2P
|
||||
private static final String USER_AGENT = "Debian/buster/sid, UPnP/1.1, MiniUPnPc/2.1";
|
||||
// HttpURLConnection proxy
|
||||
// https://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html#Proxies
|
||||
// socks and system proxies are checked in TransportManager
|
||||
private static final String PROP_HURLC_PROXY1 = "http.proxyHost";
|
||||
private static final boolean HURLC_PROXY_ENABLED = System.getProperty(PROP_HURLC_PROXY1) != null &&
|
||||
System.getProperty(PROP_HURLC_PROXY1).length() > 0;
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// Constructor
|
||||
////////////////////////////////////////////////
|
||||
@ -54,7 +66,19 @@ public abstract class Parser
|
||||
|
||||
public Node parse(URL locationURL) throws ParserException
|
||||
{
|
||||
// I2P multiple sanity checks
|
||||
if (!"http".equals(locationURL.getProtocol()))
|
||||
throw new ParserException("Not HTTP");
|
||||
String host = locationURL.getHost();
|
||||
if (host == null ||
|
||||
!Addresses.isIPv4Address(host) ||
|
||||
host.startsWith("127."))
|
||||
throw new ParserException("Bad host " + host);
|
||||
byte[] ip = Addresses.getIP(host);
|
||||
if (ip == null ||
|
||||
TransportUtil.isPubliclyRoutable(ip, false))
|
||||
throw new ParserException("Bad host " + host);
|
||||
|
||||
int port = locationURL.getPort();
|
||||
// Thanks for Hao Hu
|
||||
if (port == -1)
|
||||
@ -65,47 +89,62 @@ public abstract class Parser
|
||||
if (uri.length() <= 0)
|
||||
uri = "/";
|
||||
|
||||
HttpURLConnection urlCon = null;
|
||||
InputStream urlIn = null;
|
||||
try {
|
||||
urlCon = (HttpURLConnection)locationURL.openConnection();
|
||||
// I2P mods to prevent hangs (see HTTPRequest for more info)
|
||||
// this seems to work, getInputStream actually does the connect(),
|
||||
// (as shown by a thread dump)
|
||||
// so we can set these after openConnection()
|
||||
// Alternative would be foo = new HttpURLConnection(locationURL); foo.set timeouts; foo.connect()
|
||||
urlCon.setConnectTimeout(2*1000);
|
||||
urlCon.setReadTimeout(1000);
|
||||
urlCon.setRequestMethod("GET");
|
||||
urlCon.setRequestProperty(HTTP.CONTENT_LENGTH,"0");
|
||||
if (host != null)
|
||||
urlCon.setRequestProperty(HTTP.HOST, host);
|
||||
if (!HURLC_PROXY_ENABLED) {
|
||||
HttpURLConnection urlCon = null;
|
||||
InputStream urlIn = null;
|
||||
try {
|
||||
urlCon = (HttpURLConnection)locationURL.openConnection();
|
||||
|
||||
// I2P fix
|
||||
int code = urlCon.getResponseCode();
|
||||
if (code < 200 || code >= 300)
|
||||
throw new ParserException("Bad response code " + code);
|
||||
// I2P fix - Roku port 9080
|
||||
// not valid json either; returns "status=ok"
|
||||
if ("application/json".equals(urlCon.getContentType()))
|
||||
throw new ParserException("JSON response");
|
||||
// I2P mods to prevent hangs (see HTTPRequest for more info)
|
||||
// this seems to work, getInputStream actually does the connect(),
|
||||
// (as shown by a thread dump)
|
||||
// so we can set these after openConnection()
|
||||
// Alternative would be foo = new HttpURLConnection(locationURL); foo.set timeouts; foo.connect()
|
||||
urlCon.setConnectTimeout(2*1000);
|
||||
urlCon.setReadTimeout(1000);
|
||||
urlCon.setRequestMethod("GET");
|
||||
// See net.properties in Java install as of Java 12, however,
|
||||
// this appears to be true well earlier than that.
|
||||
// By default, the following request headers are not allowed to be set by user code
|
||||
// in HttpRequests: "connection", "content-length", "expect", "host" and "upgrade".
|
||||
// See sun.net.www.protocol.http.HttpUrlConnection source.
|
||||
// In that code, Connection: close is allowed.
|
||||
// Javadoc there says it throws IAE but it really is just ignored silently.
|
||||
urlCon.setRequestProperty("User-Agent", USER_AGENT);
|
||||
// Force even if System property http.keepAlive=true
|
||||
urlCon.setRequestProperty("Connection", "close");
|
||||
// I2P just in case
|
||||
urlCon.setAllowUserInteraction(false);
|
||||
urlCon.setUseCaches(false);
|
||||
urlCon.setInstanceFollowRedirects(false);
|
||||
|
||||
urlIn = urlCon.getInputStream();
|
||||
Node rootElem = parse(urlIn);
|
||||
// I2P fix
|
||||
int code = urlCon.getResponseCode();
|
||||
if (code < 200 || code >= 300)
|
||||
throw new ParserException("Bad response code " + code);
|
||||
// I2P fix - Roku port 9080
|
||||
// not valid json either; returns "status=ok"
|
||||
if ("application/json".equals(urlCon.getContentType()))
|
||||
throw new ParserException("JSON response");
|
||||
// I2P just in case - typ. responses are under 1KB
|
||||
if (urlCon.getContentLength() > 32768)
|
||||
throw new ParserException("too big");
|
||||
|
||||
return rootElem;
|
||||
|
||||
} catch (ParserException pe) {
|
||||
throw pe;
|
||||
} catch (Exception e) {
|
||||
// Why try twice???
|
||||
//throw new ParserException(e);
|
||||
Debug.warning("Failed fetch but retrying with HTTPRequest, URL: " + locationURL, e);
|
||||
} finally {
|
||||
if (urlIn != null) try { urlIn.close(); } catch (IOException ioe) {}
|
||||
if (urlCon != null) urlCon.disconnect();
|
||||
urlIn = urlCon.getInputStream();
|
||||
Node rootElem = parse(urlIn);
|
||||
return rootElem;
|
||||
} catch (ParserException pe) {
|
||||
throw pe;
|
||||
} catch (Exception e) {
|
||||
throw new ParserException(e);
|
||||
} finally {
|
||||
if (urlIn != null) try { urlIn.close(); } catch (IOException ioe) {}
|
||||
if (urlCon != null) urlCon.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback method, only if HURLC_PROXY_ENABLED
|
||||
// This way of doing things does not follow redirects
|
||||
HTTPRequest httpReq = new HTTPRequest();
|
||||
httpReq.setMethod(HTTP.GET);
|
||||
httpReq.setURI(uri);
|
||||
@ -115,8 +154,7 @@ public abstract class Parser
|
||||
"Unable to retrieve resource -> " + locationURL +
|
||||
"\nRequest:\n" + httpReq +
|
||||
"\nResponse:\n" + httpRes);
|
||||
String content = new String(httpRes.getContent());
|
||||
ByteArrayInputStream strBuf = new ByteArrayInputStream(content.getBytes());
|
||||
ByteArrayInputStream strBuf = new ByteArrayInputStream(httpRes.getContent());
|
||||
try {
|
||||
return parse(strBuf);
|
||||
} catch (ParserException pe) {
|
||||
|
Reference in New Issue
Block a user