Proxy: Decode IDN hostnames in error pages

This commit is contained in:
zzz
2021-04-18 10:39:35 -04:00
parent bbc8501ba5
commit 0546ef4fa4
3 changed files with 71 additions and 12 deletions

View File

@@ -1428,16 +1428,20 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
}
}
/** @since 0.8.7 */
/**
* @param destination the hostname
* @since 0.8.7
*/
private void writeHelperSaveForm(OutputStream outs, String destination, String ahelperKey,
String targetRequest, String referer) throws IOException {
if(outs == null)
return;
String idn = decodeIDNHost(destination);
Writer out = new BufferedWriter(new OutputStreamWriter(outs, "UTF-8"));
String header = getErrorPage("ahelper-new", ERR_AHELPER_NEW);
out.write(header);
out.write("<table id=\"proxyNewHost\">\n<tr><td align=\"right\">" + _t("Host") +
"</td><td>" + destination + "</td></tr>\n");
"</td><td>" + idn + "</td></tr>\n");
try {
String b32 = Base32.encode(SHA256Generator.getInstance().calculateHash(Base64.decode(ahelperKey)).getData());
out.write("<tr><td align=\"right\">" + _t("Base32") + "</td>" +
@@ -1450,7 +1454,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// FIXME if there is a query remaining it is lost
"<form method=\"GET\" action=\"" + targetRequest + "\">\n" +
"<h4>" + _t("Continue to {0} without saving", destination) + "</h4>\n<p>" +
"<h4>" + _t("Continue to {0} without saving", idn) + "</h4>\n<p>" +
_t("You can browse to the site without saving it to the address book. The address will be remembered until you restart your I2P router.") +
"</p>\n<div class=\"formaction\"><button type=\"submit\" class=\"go\">" + _t("Continue without saving") + "</button></div>" + "\n</form>\n" +
@@ -1459,7 +1463,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"<input type=\"hidden\" name=\"dest\" value=\"" + ahelperKey + "\">\n" +
"<input type=\"hidden\" name=\"nonce\" value=\"" + _proxyNonce + "\">\n" +
"<h4>" + _t("Save {0} to router address book and continue to website", destination) + "</h4>\n<p>" +
"<h4>" + _t("Save {0} to router address book and continue to website", idn) + "</h4>\n<p>" +
_t("This address will be saved to your Router address book where your subscription-based addresses are stored."));
if(_context.namingService().getName().equals("BlockfileNamingService")) {
out.write(" " + _t("If you want to keep track of sites you have added manually, add to your Local or Private address book instead."));
@@ -1472,12 +1476,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(_context.namingService().getName().equals("BlockfileNamingService")) {
// only blockfile supports multiple books
out.write("<h4>" + _t("Save {0} to local address book and continue to website", destination) + "</h4>\n<p>" +
out.write("<h4>" + _t("Save {0} to local address book and continue to website", idn) + "</h4>\n<p>" +
_t("This address will be saved to your Local address book. Select this option for addresses you wish to keep separate from the main router address book, but don't mind publishing.") +
"</p>\n<div class=\"formaction\"><button type=\"submit\" class=\"accept\" name=\"local\" value=\"local\">" +
label + "</button></div>\n");
out.write("<h4>" + _t("Save {0} to private address book and continue to website", destination) + "</h4>\n<p>" +
out.write("<h4>" + _t("Save {0} to private address book and continue to website", idn) + "</h4>\n<p>" +
_t("This address will be saved to your Private address book, ensuring it is never published.") +
"</p>\n<div class=\"formaction\"><button type=\"submit\" class=\"accept\" name=\"private\" value=\"private\">" +
label + "</button></div>\n");

View File

@@ -15,6 +15,7 @@ import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.IDN;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URI;
@@ -144,6 +145,19 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
// very simple, remember last-failed only
private String _lastFailedSSLProxy;
/** available as of Java 6 and Android API 9 */
private static final boolean _haveIDN;
static {
boolean h;
try {
Class.forName("java.net.IDN", false, ClassLoader.getSystemClassLoader());
h = true;
} catch (ClassNotFoundException cnfe) {
h = false;
}
_haveIDN = h;
}
protected String getPrefix(long requestId) {
return "HTTPClient[" + _clientId + '/' + requestId + "]: ";
}
@@ -929,7 +943,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
out.write(uri);
out.write("\">");
// Long URLs are handled in CSS
out.write(uri);
out.write(decodeIDNURI(uri));
out.write("</a>");
if (usingWWWProxy) {
out.write("<br><br><b>");
@@ -996,6 +1010,45 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
writeFooter(out);
}
/**
* Decode the host part of a URI for display.
* Returns original string on any error.
*
* @since 0.9.50
*/
private static String decodeIDNURI(String uri) {
if (!_haveIDN)
return uri;
if (!uri.contains("xn--"))
return uri;
try {
URI u = new URI(uri);
String h = u.getHost();
String hu = IDN.toUnicode(h);
if (hu == null || h.equals(hu))
return uri;
int idx = uri.indexOf(h);
if (idx < 0)
return uri;
return uri.substring(0, idx) + hu + uri.substring(idx + h.length(), uri.length());
} catch(URISyntaxException use) {}
return uri;
}
/**
* Decode a hostname for display.
* Returns original string on any error.
*
* @since 0.9.50
*/
public static String decodeIDNHost(String host) {
if (!_haveIDN)
return host;
if (!host.contains("xn--"))
return host;
return IDN.toUnicode(host);
}
/**
* Flushes.
*

View File

@@ -365,6 +365,7 @@ public abstract class LocalHTTPServer {
PortMapper pm = I2PAppContext.getGlobalContext().portMapper();
String conURL = pm.getConsoleURL();
String idn = I2PTunnelHTTPClientBase.decodeIDNHost(host);
out.write(("HTTP/1.1 200 OK\r\n"+
"Content-Type: text/html; charset=UTF-8\r\n"+
"Referrer-Policy: no-referrer\r\n"+
@@ -372,7 +373,7 @@ public abstract class LocalHTTPServer {
"Proxy-Connection: close\r\n"+
"\r\n"+
"<html><head>"+
"<title>" + _t("Redirecting to {0}", host) + "</title>\n" +
"<title>" + _t("Redirecting to {0}", idn) + "</title>\n" +
"<link rel=\"shortcut icon\" href=\"http://proxy.i2p/themes/console/images/favicon.ico\" >\n" +
"<link href=\"http://proxy.i2p/themes/console/default/console.css\" rel=\"stylesheet\" type=\"text/css\" >\n" +
"<meta http-equiv=\"Refresh\" content=\"1; url=" + url + "\">\n" +
@@ -386,8 +387,8 @@ public abstract class LocalHTTPServer {
"<div class=warning id=warning>\n" +
"<h3>" +
(success ?
_t("Saved {0} to the {1} address book, redirecting now.", host, tbook) :
_t("Failed to save {0} to the {1} address book, redirecting now.", host, tbook)) +
_t("Saved {0} to the {1} address book, redirecting now.", idn, tbook) :
_t("Failed to save {0} to the {1} address book, redirecting now.", idn, tbook)) +
"</h3>\n<p><a href=\"" + url + "\">" +
_t("Click here if you are not redirected automatically.") +
"</a></p></div>").getBytes("UTF-8"));
@@ -399,6 +400,7 @@ public abstract class LocalHTTPServer {
private static void writeB32RedirectPage(OutputStream out, String host, String url) throws IOException {
PortMapper pm = I2PAppContext.getGlobalContext().portMapper();
String conURL = pm.getConsoleURL();
String idn = I2PTunnelHTTPClientBase.decodeIDNHost(host);
out.write(("HTTP/1.1 200 OK\r\n"+
"Content-Type: text/html; charset=UTF-8\r\n"+
"Referrer-Policy: no-referrer\r\n"+
@@ -406,7 +408,7 @@ public abstract class LocalHTTPServer {
"Proxy-Connection: close\r\n"+
"\r\n"+
"<html><head>"+
"<title>" + _t("Redirecting to {0}", host) + "</title>\n" +
"<title>" + _t("Redirecting to {0}", idn) + "</title>\n" +
"<link rel=\"shortcut icon\" href=\"http://proxy.i2p/themes/console/images/favicon.ico\" >\n" +
"<link href=\"http://proxy.i2p/themes/console/default/console.css\" rel=\"stylesheet\" type=\"text/css\" >\n" +
"<meta http-equiv=\"Refresh\" content=\"1; url=" + url + "\">\n" +
@@ -419,7 +421,7 @@ public abstract class LocalHTTPServer {
out.write(("</div>" +
"<div class=warning id=warning>\n" +
"<h3>" +
_t("Saved the authentication for {0}, redirecting now.", host) +
_t("Saved the authentication for {0}, redirecting now.", idn) +
"</h3>\n<p><a href=\"" + url + "\">" +
_t("Click here if you are not redirected automatically.") +
"</a></p></div>").getBytes("UTF-8"));