forked from I2P_Developers/i2p.i2p
* HTTP Proxy: Address helper refactoring, address book add form
This commit is contained in:
@ -18,16 +18,20 @@ import java.util.Date;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.I2PException;
|
import net.i2p.I2PException;
|
||||||
|
import net.i2p.client.naming.NamingService;
|
||||||
import net.i2p.client.streaming.I2PSocket;
|
import net.i2p.client.streaming.I2PSocket;
|
||||||
import net.i2p.client.streaming.I2PSocketManager;
|
import net.i2p.client.streaming.I2PSocketManager;
|
||||||
import net.i2p.client.streaming.I2PSocketOptions;
|
import net.i2p.client.streaming.I2PSocketOptions;
|
||||||
import net.i2p.data.Base32;
|
import net.i2p.data.Base32;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.util.EventDispatcher;
|
import net.i2p.util.EventDispatcher;
|
||||||
@ -62,7 +66,16 @@ import net.i2p.util.Translate;
|
|||||||
*/
|
*/
|
||||||
public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runnable {
|
public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runnable {
|
||||||
|
|
||||||
private HashMap addressHelpers = new HashMap();
|
/**
|
||||||
|
* Map of host name to base64 destination for destinations collected
|
||||||
|
* via address helper links
|
||||||
|
*/
|
||||||
|
private final ConcurrentHashMap<String, String> addressHelpers = new ConcurrentHashMap(8);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to protect actions via http://proxy.i2p/
|
||||||
|
*/
|
||||||
|
private final String _proxyNonce;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These are backups if the xxx.ht error page is missing.
|
* These are backups if the xxx.ht error page is missing.
|
||||||
@ -130,6 +143,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
"or naming one of them differently.<p>")
|
"or naming one of them differently.<p>")
|
||||||
.getBytes();
|
.getBytes();
|
||||||
|
|
||||||
|
private final static byte[] ERR_AHELPER_NEW =
|
||||||
|
("HTTP/1.1 409 New Address\r\n"+
|
||||||
|
"Content-Type: text/html; charset=iso-8859-1\r\n"+
|
||||||
|
"Cache-control: no-cache\r\n"+
|
||||||
|
"\r\n"+
|
||||||
|
"<html><body><H1>New Host Name with Address Helper</H1>"+
|
||||||
|
"The address helper link you followed is for a new host name that is not in your address book. " +
|
||||||
|
"You may either save the destination for this host name to your address book, or remember it only until your router restarts. " +
|
||||||
|
"If you save it to your address book, you will not see this message again. " +
|
||||||
|
"If you do not wish to visit this host, click the \"back\" button on your browser.")
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
private final static byte[] ERR_BAD_PROTOCOL =
|
private final static byte[] ERR_BAD_PROTOCOL =
|
||||||
("HTTP/1.1 403 Bad Protocol\r\n"+
|
("HTTP/1.1 403 Bad Protocol\r\n"+
|
||||||
"Content-Type: text/html; charset=iso-8859-1\r\n"+
|
"Content-Type: text/html; charset=iso-8859-1\r\n"+
|
||||||
@ -162,6 +187,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
|
|
||||||
public I2PTunnelHTTPClient(int localPort, Logging l, I2PSocketManager sockMgr, I2PTunnel tunnel, EventDispatcher notifyThis, long clientId) {
|
public I2PTunnelHTTPClient(int localPort, Logging l, I2PSocketManager sockMgr, I2PTunnel tunnel, EventDispatcher notifyThis, long clientId) {
|
||||||
super(localPort, l, sockMgr, tunnel, notifyThis, clientId);
|
super(localPort, l, sockMgr, tunnel, notifyThis, clientId);
|
||||||
|
_proxyNonce = Long.toString(_context.random().nextLong());
|
||||||
// proxyList = new ArrayList();
|
// proxyList = new ArrayList();
|
||||||
|
|
||||||
setName("HTTP Proxy on " + getTunnel().listenHost + ':' + localPort);
|
setName("HTTP Proxy on " + getTunnel().listenHost + ':' + localPort);
|
||||||
@ -177,6 +203,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
String wwwProxy, EventDispatcher notifyThis,
|
String wwwProxy, EventDispatcher notifyThis,
|
||||||
I2PTunnel tunnel) throws IllegalArgumentException {
|
I2PTunnel tunnel) throws IllegalArgumentException {
|
||||||
super(localPort, ownDest, l, notifyThis, "HTTP Proxy on " + tunnel.listenHost + ':' + localPort + " #" + (++__clientId), tunnel);
|
super(localPort, ownDest, l, notifyThis, "HTTP Proxy on " + tunnel.listenHost + ':' + localPort + " #" + (++__clientId), tunnel);
|
||||||
|
_proxyNonce = Long.toString(_context.random().nextLong());
|
||||||
|
|
||||||
//proxyList = new ArrayList(); // We won't use outside of i2p
|
//proxyList = new ArrayList(); // We won't use outside of i2p
|
||||||
if (waitEventValue("openBaseClientResult").equals("error")) {
|
if (waitEventValue("openBaseClientResult").equals("error")) {
|
||||||
@ -258,12 +285,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String LOCAL_SERVER = "proxy.i2p";
|
||||||
private static final boolean DEFAULT_GZIP = true;
|
private static final boolean DEFAULT_GZIP = true;
|
||||||
// all default to false
|
/** all default to false */
|
||||||
public static final String PROP_REFERER = "i2ptunnel.httpclient.sendReferer";
|
public static final String PROP_REFERER = "i2ptunnel.httpclient.sendReferer";
|
||||||
public static final String PROP_USER_AGENT = "i2ptunnel.httpclient.sendUserAgent";
|
public static final String PROP_USER_AGENT = "i2ptunnel.httpclient.sendUserAgent";
|
||||||
public static final String PROP_VIA = "i2ptunnel.httpclient.sendVia";
|
public static final String PROP_VIA = "i2ptunnel.httpclient.sendVia";
|
||||||
public static final String PROP_JUMP_SERVERS = "i2ptunnel.httpclient.jumpServers";
|
public static final String PROP_JUMP_SERVERS = "i2ptunnel.httpclient.jumpServers";
|
||||||
|
public static final String PROP_DISABLE_HELPER = "i2ptunnel.httpclient.disableAddressHelper";
|
||||||
|
|
||||||
protected void clientConnectionRun(Socket s) {
|
protected void clientConnectionRun(Socket s) {
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
@ -278,7 +307,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
InputReader reader = new InputReader(s.getInputStream());
|
InputReader reader = new InputReader(s.getInputStream());
|
||||||
String line, method = null, protocol = null, host = null, destination = null;
|
String line, method = null, protocol = null, host = null, destination = null;
|
||||||
StringBuilder newRequest = new StringBuilder();
|
StringBuilder newRequest = new StringBuilder();
|
||||||
int ahelper = 0;
|
boolean ahelperPresent = false;
|
||||||
|
boolean ahelperNew = false;
|
||||||
|
String ahelperKey = null;
|
||||||
|
String userAgent = null;
|
||||||
String authorization = null;
|
String authorization = null;
|
||||||
while ((line = reader.readLine(method)) != null) {
|
while ((line = reader.readLine(method)) != null) {
|
||||||
line = line.trim();
|
line = line.trim();
|
||||||
@ -334,6 +366,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
protocol = request.substring(0, pos + 2);
|
protocol = request.substring(0, pos + 2);
|
||||||
request = request.substring(pos + 2);
|
request = request.substring(pos + 2);
|
||||||
|
|
||||||
|
// "foo.i2p/bar/baz HTTP/1.1", with any i2paddresshelper parameter removed
|
||||||
targetRequest = request;
|
targetRequest = request;
|
||||||
|
|
||||||
// pos is the start of the path
|
// pos is the start of the path
|
||||||
@ -380,21 +413,20 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
destination = host;
|
destination = host;
|
||||||
host = getHostName(destination);
|
host = getHostName(destination);
|
||||||
line = method + ' ' + request.substring(pos);
|
line = method + ' ' + request.substring(pos);
|
||||||
} else if (host.toLowerCase().equals("proxy.i2p")) {
|
} else if (host.toLowerCase().equals(LOCAL_SERVER)) {
|
||||||
// so we don't do any naming service lookups
|
// so we don't do any naming service lookups
|
||||||
destination = host;
|
destination = host;
|
||||||
usingInternalServer = true;
|
usingInternalServer = true;
|
||||||
} else if (host.toLowerCase().endsWith(".i2p")) {
|
} else if (host.toLowerCase().endsWith(".i2p")) {
|
||||||
// Destination gets the host name
|
// Destination gets the host name
|
||||||
destination = host;
|
destination = host;
|
||||||
// Host becomes the destination key
|
// Host becomes the destination's "{b32}.b32.i2p" string, or "i2p" on lookup failure
|
||||||
host = getHostName(destination);
|
host = getHostName(destination);
|
||||||
|
|
||||||
int pos2;
|
int pos2;
|
||||||
if ((pos2 = request.indexOf("?")) != -1) {
|
if ((pos2 = request.indexOf("?")) != -1) {
|
||||||
// Try to find an address helper in the fragments
|
// Try to find an address helper in the fragments
|
||||||
// and split the request into it's component parts for rebuilding later
|
// and split the request into it's component parts for rebuilding later
|
||||||
String ahelperKey = null;
|
|
||||||
boolean ahelperConflict = false;
|
boolean ahelperConflict = false;
|
||||||
|
|
||||||
String fragments = request.substring(pos2 + 1);
|
String fragments = request.substring(pos2 + 1);
|
||||||
@ -404,6 +436,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
String urlEncoding = "";
|
String urlEncoding = "";
|
||||||
fragments = fragments.substring(0, pos2);
|
fragments = fragments.substring(0, pos2);
|
||||||
String initialFragments = fragments;
|
String initialFragments = fragments;
|
||||||
|
// FIXME split on ';' also
|
||||||
fragments = fragments + "&";
|
fragments = fragments + "&";
|
||||||
String fragment;
|
String fragment;
|
||||||
while(fragments.length() > 0) {
|
while(fragments.length() > 0) {
|
||||||
@ -412,35 +445,43 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
fragments = fragments.substring(pos2 + 1);
|
fragments = fragments.substring(pos2 + 1);
|
||||||
|
|
||||||
// Fragment looks like addresshelper key
|
// Fragment looks like addresshelper key
|
||||||
if (fragment.startsWith("i2paddresshelper=")) {
|
if (fragment.startsWith("i2paddresshelper=") &&
|
||||||
|
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
|
||||||
pos2 = fragment.indexOf("=");
|
pos2 = fragment.indexOf("=");
|
||||||
ahelperKey = fragment.substring(pos2 + 1);
|
ahelperKey = fragment.substring(pos2 + 1);
|
||||||
|
|
||||||
// Key contains data, lets not ignore it
|
// Key contains data, lets not ignore it
|
||||||
if (ahelperKey != null) {
|
if (ahelperKey != null) {
|
||||||
|
ahelperPresent = true;
|
||||||
// ahelperKey will be validated later
|
// ahelperKey will be validated later
|
||||||
|
if (host == null || "i2p".equals(host)) {
|
||||||
// Host resolvable only with addresshelper
|
// Host lookup failed - resolvable only with addresshelper
|
||||||
if ( (host == null) || ("i2p".equals(host)) )
|
// Store in local HashMap unless there is conflict
|
||||||
{
|
String old = addressHelpers.putIfAbsent(destination.toLowerCase(), ahelperKey);
|
||||||
// Cannot check, use addresshelper key
|
ahelperNew = old == null;
|
||||||
addressHelpers.put(destination,ahelperKey);
|
if ((!ahelperNew) && !old.equals(ahelperKey)) {
|
||||||
} else {
|
|
||||||
// Host resolvable from database, verify addresshelper key
|
|
||||||
// Silently bypass correct keys, otherwise alert
|
|
||||||
String destB64 = null;
|
|
||||||
Destination _dest = _context.namingService().lookup(host);
|
|
||||||
if (_dest != null)
|
|
||||||
destB64 = _dest.toBase64();
|
|
||||||
if (destB64 != null && !destB64.equals(ahelperKey))
|
|
||||||
{
|
|
||||||
// Conflict: handle when URL reconstruction done
|
// Conflict: handle when URL reconstruction done
|
||||||
ahelperConflict = true;
|
ahelperConflict = true;
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
|
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
|
||||||
|
"], trusted key [" + old + "], specified key [" + ahelperKey + "].");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If the host is resolvable from database, verify addresshelper key
|
||||||
|
// Silently bypass correct keys, otherwise alert
|
||||||
|
Destination hostDest = _context.namingService().lookup(destination);
|
||||||
|
if (hostDest != null) {
|
||||||
|
String destB64 = hostDest.toBase64();
|
||||||
|
if (destB64 != null && !destB64.equals(ahelperKey)) {
|
||||||
|
// Conflict: handle when URL reconstruction done
|
||||||
|
ahelperConflict = true;
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination +
|
||||||
|
"], trusted key [" + destB64 + "], specified key [" + ahelperKey + "].");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // ahelperKey
|
||||||
} else {
|
} else {
|
||||||
// Other fragments, just pass along
|
// Other fragments, just pass along
|
||||||
// Append each fragment to urlEncoding
|
// Append each fragment to urlEncoding
|
||||||
@ -453,11 +494,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
}
|
}
|
||||||
// Reconstruct the request minus the i2paddresshelper GET var
|
// Reconstruct the request minus the i2paddresshelper GET var
|
||||||
request = uriPath + urlEncoding + " " + protocolVersion;
|
request = uriPath + urlEncoding + " " + protocolVersion;
|
||||||
|
targetRequest = request;
|
||||||
|
|
||||||
// Did addresshelper key conflict?
|
// Did addresshelper key conflict?
|
||||||
if (ahelperConflict)
|
if (ahelperConflict) {
|
||||||
{
|
|
||||||
|
|
||||||
if (out != null) {
|
if (out != null) {
|
||||||
// convert ahelperKey to b32
|
// convert ahelperKey to b32
|
||||||
String alias = getHostName(ahelperKey);
|
String alias = getHostName(ahelperKey);
|
||||||
@ -479,16 +519,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
s.close();
|
s.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
} // end query processing
|
||||||
|
|
||||||
String addressHelper = (String) addressHelpers.get(destination);
|
String addressHelper = addressHelpers.get(destination);
|
||||||
if (addressHelper != null) {
|
if (addressHelper != null)
|
||||||
destination = addressHelper;
|
host = getHostName(addressHelper);
|
||||||
host = getHostName(destination);
|
|
||||||
ahelper = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
line = method + " " + request.substring(pos);
|
line = method + " " + request.substring(pos);
|
||||||
|
// end of (host endsWith(".i2p"))
|
||||||
|
|
||||||
} else if (host.toLowerCase().equals("localhost") || host.equals("127.0.0.1") ||
|
} else if (host.toLowerCase().equals("localhost") || host.equals("127.0.0.1") ||
|
||||||
host.startsWith("192.168.")) {
|
host.startsWith("192.168.")) {
|
||||||
// if somebody is trying to get to 192.168.example.com, oh well
|
// if somebody is trying to get to 192.168.example.com, oh well
|
||||||
@ -584,10 +623,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
line = "Host: " + host;
|
line = "Host: " + host;
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info(getPrefix(requestId) + "Setting host = " + host);
|
_log.info(getPrefix(requestId) + "Setting host = " + host);
|
||||||
} else if (lowercaseLine.startsWith("user-agent: ") &&
|
} else if (lowercaseLine.startsWith("user-agent: ")) {
|
||||||
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT)).booleanValue()) {
|
// save for deciding whether to offer address book form
|
||||||
line = null;
|
userAgent = lowercaseLine.substring(12);
|
||||||
continue;
|
if (!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_USER_AGENT)).booleanValue()) {
|
||||||
|
line = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
} else if (lowercaseLine.startsWith("accept")) {
|
} else if (lowercaseLine.startsWith("accept")) {
|
||||||
// strip the accept-blah headers, as they vary dramatically from
|
// strip the accept-blah headers, as they vary dramatically from
|
||||||
// browser to browser
|
// browser to browser
|
||||||
@ -687,15 +729,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(getPrefix(requestId) + "Destination: " + destination);
|
_log.debug(getPrefix(requestId) + "Destination: " + destination);
|
||||||
|
|
||||||
// Serve local proxy files (images, css linked from error pages)
|
|
||||||
// Ignore all the headers
|
|
||||||
// Allow without authorization
|
|
||||||
if (usingInternalServer) {
|
|
||||||
serveLocalFile(out, method, targetRequest);
|
|
||||||
s.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authorization
|
// Authorization
|
||||||
if (!authorize(s, requestId, authorization)) {
|
if (!authorize(s, requestId, authorization)) {
|
||||||
if (_log.shouldLog(Log.WARN)) {
|
if (_log.shouldLog(Log.WARN)) {
|
||||||
@ -705,19 +738,41 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
_log.warn(getPrefix(requestId) + "Auth required, sending 407");
|
_log.warn(getPrefix(requestId) + "Auth required, sending 407");
|
||||||
}
|
}
|
||||||
out.write(getErrorPage("auth", ERR_AUTH));
|
out.write(getErrorPage("auth", ERR_AUTH));
|
||||||
|
writeFooter(out);
|
||||||
s.close();
|
s.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serve local proxy files (images, css linked from error pages)
|
||||||
|
// Ignore all the headers
|
||||||
|
if (usingInternalServer) {
|
||||||
|
// disable the add form if address helper is disabled
|
||||||
|
if (targetRequest.startsWith(LOCAL_SERVER + "/add?") &&
|
||||||
|
Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
|
||||||
|
out.write(ERR_HELPER_DISABLED);
|
||||||
|
} else {
|
||||||
|
serveLocalFile(out, method, targetRequest, _proxyNonce);
|
||||||
|
}
|
||||||
|
s.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LOOKUP
|
||||||
// If the host is "i2p", the getHostName() lookup failed, don't try to
|
// If the host is "i2p", the getHostName() lookup failed, don't try to
|
||||||
// look it up again as the naming service does not do negative caching
|
// look it up again as the naming service does not do negative caching
|
||||||
// so it will be slow.
|
// so it will be slow.
|
||||||
|
|
||||||
Destination clientDest;
|
Destination clientDest;
|
||||||
if ("i2p".equals(host))
|
String addressHelper = addressHelpers.get(destination.toLowerCase());
|
||||||
|
if (addressHelper != null) {
|
||||||
|
clientDest = _context.namingService().lookup(addressHelper);
|
||||||
|
// remove bad entries
|
||||||
|
if (clientDest == null)
|
||||||
|
addressHelpers.remove(destination.toLowerCase());
|
||||||
|
} else if ("i2p".equals(host)) {
|
||||||
clientDest = null;
|
clientDest = null;
|
||||||
else
|
} else {
|
||||||
clientDest = _context.namingService().lookup(destination);
|
clientDest = _context.namingService().lookup(destination);
|
||||||
|
}
|
||||||
|
|
||||||
if (clientDest == null) {
|
if (clientDest == null) {
|
||||||
//l.log("Could not resolve " + destination + ".");
|
//l.log("Could not resolve " + destination + ".");
|
||||||
@ -727,7 +782,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
String jumpServers = null;
|
String jumpServers = null;
|
||||||
if (usingWWWProxy)
|
if (usingWWWProxy)
|
||||||
header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
|
header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
|
||||||
else if(ahelper != 0)
|
else if (ahelperPresent)
|
||||||
header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
|
header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
|
||||||
else if (destination.length() == 60 && destination.endsWith(".b32.i2p"))
|
else if (destination.length() == 60 && destination.endsWith(".b32.i2p"))
|
||||||
header = getErrorPage("dnf", ERR_DESTINATION_UNKNOWN);
|
header = getErrorPage("dnf", ERR_DESTINATION_UNKNOWN);
|
||||||
@ -741,7 +796,34 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
s.close();
|
s.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String remoteID;
|
|
||||||
|
// Address helper response form
|
||||||
|
// This will only load once - the second time it won't be "new"
|
||||||
|
// Don't do this for eepget, which uses a user-agent of "Wget"
|
||||||
|
if (ahelperNew && "GET".equals(method) &&
|
||||||
|
(userAgent == null || !userAgent.startsWith("Wget")) &&
|
||||||
|
!Boolean.valueOf(getTunnel().getClientOptions().getProperty(PROP_DISABLE_HELPER)).booleanValue()) {
|
||||||
|
writeHelperSaveForm(out, destination, ahelperKey, protocol + targetRequest);
|
||||||
|
s.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to non-addresshelper URL to not clog the browser address bar
|
||||||
|
// and not pass the parameter to the eepsite.
|
||||||
|
// This also prevents the not-found error page from looking bad
|
||||||
|
if (ahelperPresent) {
|
||||||
|
String uri = protocol + targetRequest;
|
||||||
|
int spc = uri.indexOf(" ");
|
||||||
|
if (spc >= 0)
|
||||||
|
uri = uri.substring(0, spc);
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Auto redirecting to " + uri);
|
||||||
|
out.write(("HTTP/1.1 301 Address Helper Accepted\r\n"+
|
||||||
|
"Location: " + uri + "\r\n"+
|
||||||
|
"\r\n").getBytes("UTF-8"));
|
||||||
|
s.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Properties opts = new Properties();
|
Properties opts = new Properties();
|
||||||
//opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
|
//opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
|
||||||
@ -779,6 +861,38 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 0.8.7 */
|
||||||
|
private void writeHelperSaveForm(OutputStream out, String destination, String ahelperKey, String targetRequest) throws IOException {
|
||||||
|
if (out == null)
|
||||||
|
return;
|
||||||
|
// strip HTTP/1.1
|
||||||
|
int protopos = targetRequest.indexOf(" ");
|
||||||
|
if (protopos >= 0)
|
||||||
|
targetRequest = targetRequest.substring(0, protopos);
|
||||||
|
byte[] header = getErrorPage("ahelper-new", ERR_AHELPER_NEW);
|
||||||
|
out.write(header);
|
||||||
|
out.write(("<table><tr><td class=\"mediumtags\" align=\"right\">" + _("Host") + "</td><td class=\"mediumtags\">" + destination + "</td></tr>\n" +
|
||||||
|
"<tr><td class=\"mediumtags\" align=\"right\">" + _("Destination") + "</td><td>" +
|
||||||
|
"<textarea rows=\"1\" style=\"height: 4em; min-width: 0; min-height: 0;\" cols=\"70\" wrap=\"off\" readonly=\"readonly\" >" +
|
||||||
|
ahelperKey + "</textarea></td></tr></table>\n" +
|
||||||
|
"<hr><div class=\"formaction\">"+
|
||||||
|
"<form method=\"GET\" action=\"" + targetRequest + "\">" +
|
||||||
|
"<button type=\"submit\">" + _("Continue to {0} without saving", destination) + "</button>" +
|
||||||
|
"</form>\n<form method=\"GET\" action=\"http://" + LOCAL_SERVER + "/add\">" +
|
||||||
|
"<input type=\"hidden\" name=\"host\" value=\"" + destination + "\">\n" +
|
||||||
|
"<input type=\"hidden\" name=\"dest\" value=\"" + ahelperKey + "\">\n" +
|
||||||
|
"<input type=\"hidden\" name=\"nonce\" value=\"" + _proxyNonce + "\">\n" +
|
||||||
|
"<button type=\"submit\" name=\"router\" value=\"router\">" + _("Save {0} to router address book and continue to eepsite", destination) + "</button><br>\n").getBytes("UTF-8"));
|
||||||
|
if (_context.namingService().getName().equals("BlockfileNamingService")) {
|
||||||
|
// only blockfile supports multiple books
|
||||||
|
out.write(("<button type=\"submit\" name=\"master\" value=\"master\">" + _("Save {0} to master address book and continue to eepsite", destination) + "</button><br>\n").getBytes("UTF-8"));
|
||||||
|
out.write(("<button type=\"submit\" name=\"private\" value=\"private\">" + _("Save {0} to private address book and continue to eepsite", destination) + "</button>\n").getBytes("UTF-8"));
|
||||||
|
}
|
||||||
|
out.write(("<input type=\"hidden\" name=\"url\" value=\"" + targetRequest + "\">\n" +
|
||||||
|
"</form></div></div>").getBytes());
|
||||||
|
writeFooter(out);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the first line unbuffered.
|
* Read the first line unbuffered.
|
||||||
* After that, switch to a BufferedReader, unless the method is "POST".
|
* After that, switch to a BufferedReader, unless the method is "POST".
|
||||||
@ -915,7 +1029,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
out.write(errMessage);
|
out.write(errMessage);
|
||||||
if (targetRequest != null) {
|
if (targetRequest != null) {
|
||||||
int protopos = targetRequest.indexOf(" ");
|
int protopos = targetRequest.indexOf(" ");
|
||||||
String uri = null;
|
String uri;
|
||||||
if (protopos >= 0)
|
if (protopos >= 0)
|
||||||
uri = targetRequest.substring(0, protopos);
|
uri = targetRequest.substring(0, protopos);
|
||||||
else
|
else
|
||||||
@ -1022,6 +1136,20 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
"HTTP Proxy local file not found")
|
"HTTP Proxy local file not found")
|
||||||
.getBytes();
|
.getBytes();
|
||||||
|
|
||||||
|
private final static byte[] ERR_ADD =
|
||||||
|
("HTTP/1.1 409 Bad\r\n"+
|
||||||
|
"Content-Type: text/plain\r\n"+
|
||||||
|
"\r\n"+
|
||||||
|
"Add to addressbook failed - bad parameters")
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
|
private final static byte[] ERR_HELPER_DISABLED =
|
||||||
|
("HTTP/1.1 403 Disabled\r\n"+
|
||||||
|
"Content-Type: text/plain\r\n"+
|
||||||
|
"\r\n"+
|
||||||
|
"Address helpers disabled")
|
||||||
|
.getBytes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Very simple web server.
|
* Very simple web server.
|
||||||
*
|
*
|
||||||
@ -1043,9 +1171,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
*
|
*
|
||||||
* @param targetRequest "proxy.i2p/themes/foo.png HTTP/1.1"
|
* @param targetRequest "proxy.i2p/themes/foo.png HTTP/1.1"
|
||||||
*/
|
*/
|
||||||
private static void serveLocalFile(OutputStream out, String method, String targetRequest) {
|
private static void serveLocalFile(OutputStream out, String method, String targetRequest, String proxyNonce) {
|
||||||
|
//System.err.println("targetRequest: \"" + targetRequest + "\"");
|
||||||
// a home page message for the curious...
|
// a home page message for the curious...
|
||||||
if (targetRequest.startsWith("proxy.i2p/ ")) {
|
if (targetRequest.startsWith(LOCAL_SERVER + "/ ")) {
|
||||||
try {
|
try {
|
||||||
out.write(("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nCache-Control: max-age=86400\r\n\r\nI2P HTTP proxy OK").getBytes());
|
out.write(("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nCache-Control: max-age=86400\r\n\r\nI2P HTTP proxy OK").getBytes());
|
||||||
out.flush();
|
out.flush();
|
||||||
@ -1053,12 +1182,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((method.equals("GET") || method.equals("HEAD")) &&
|
if ((method.equals("GET") || method.equals("HEAD")) &&
|
||||||
targetRequest.startsWith("proxy.i2p/themes/") &&
|
targetRequest.startsWith(LOCAL_SERVER + "/themes/") &&
|
||||||
!targetRequest.contains("..")) {
|
!targetRequest.contains("..")) {
|
||||||
int space = targetRequest.indexOf(' ');
|
int space = targetRequest.indexOf(' ');
|
||||||
String filename = null;
|
String filename = null;
|
||||||
try {
|
try {
|
||||||
filename = targetRequest.substring(17, space); // "proxy.i2p/themes/".length
|
filename = targetRequest.substring(LOCAL_SERVER.length() + 8, space); // "/themes/".length
|
||||||
} catch (IndexOutOfBoundsException ioobe) {}
|
} catch (IndexOutOfBoundsException ioobe) {}
|
||||||
// theme hack
|
// theme hack
|
||||||
if (filename.startsWith("console/default/"))
|
if (filename.startsWith("console/default/"))
|
||||||
@ -1081,9 +1210,65 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
out.write(type.getBytes());
|
out.write(type.getBytes());
|
||||||
out.write("\r\nCache-Control: max-age=86400\r\n\r\n".getBytes());
|
out.write("\r\nCache-Control: max-age=86400\r\n\r\n".getBytes());
|
||||||
FileUtil.readFile(filename, themesDir.getAbsolutePath(), out);
|
FileUtil.readFile(filename, themesDir.getAbsolutePath(), out);
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to addressbook (form submit)
|
||||||
|
// Parameters are url, host, dest, nonce, and master | router | private.
|
||||||
|
// Do the add and redirect.
|
||||||
|
if (targetRequest.startsWith(LOCAL_SERVER + "/add?")) {
|
||||||
|
int spc = targetRequest.indexOf(' ');
|
||||||
|
String query = targetRequest.substring(LOCAL_SERVER.length() + 5, spc); // "/add?".length()
|
||||||
|
Map<String, String> opts = new HashMap(8);
|
||||||
|
StringTokenizer tok = new StringTokenizer(query, "=&;");
|
||||||
|
while (tok.hasMoreTokens()) {
|
||||||
|
String k = tok.nextToken();
|
||||||
|
if (!tok.hasMoreTokens())
|
||||||
|
break;
|
||||||
|
String v = tok.nextToken();
|
||||||
|
opts.put(decode(k), decode(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
String url = opts.get("url");
|
||||||
|
String host = opts.get("host");
|
||||||
|
String b64Dest = opts.get("dest");
|
||||||
|
String nonce = opts.get("nonce");
|
||||||
|
String book = "privatehosts.txt";
|
||||||
|
if (opts.get("master") != null)
|
||||||
|
book = "userhosts.txt";
|
||||||
|
else if (opts.get("router") != null)
|
||||||
|
book = "hosts.txt";
|
||||||
|
Destination dest = null;
|
||||||
|
if (b64Dest != null) {
|
||||||
|
try {
|
||||||
|
dest = new Destination(b64Dest);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
System.err.println("Bad dest to save?" + b64Dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//System.err.println("url : \"" + url + "\"");
|
||||||
|
//System.err.println("host : \"" + host + "\"");
|
||||||
|
//System.err.println("b64dest : \"" + b64Dest + "\"");
|
||||||
|
//System.err.println("book : \"" + book + "\"");
|
||||||
|
//System.err.println("nonce : \"" + nonce + "\"");
|
||||||
|
if (proxyNonce.equals(nonce) && url != null && host != null && dest != null) {
|
||||||
|
try {
|
||||||
|
NamingService ns = I2PAppContext.getGlobalContext().namingService();
|
||||||
|
Properties nsOptions = new Properties();
|
||||||
|
nsOptions.setProperty("list", book);
|
||||||
|
nsOptions.setProperty("s", _("Added via address helper"));
|
||||||
|
boolean success = ns.put(host, dest, nsOptions);
|
||||||
|
writeRedirectPage(out, success, host, book, url);
|
||||||
return;
|
return;
|
||||||
} catch (IOException ioe) {}
|
} catch (IOException ioe) {}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
out.write(ERR_ADD);
|
||||||
|
out.flush();
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
out.write(ERR_404);
|
out.write(ERR_404);
|
||||||
@ -1091,16 +1276,68 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
} catch (IOException ioe) {}
|
} catch (IOException ioe) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 0.8.7 */
|
||||||
|
private static void writeRedirectPage(OutputStream out, boolean success, String host, String book, String url) throws IOException {
|
||||||
|
out.write(("HTTP/1.1 200 OK\r\n"+
|
||||||
|
"Content-Type: text/html; charset=UTF-8\r\n"+
|
||||||
|
"\r\n"+
|
||||||
|
"<html><head>"+
|
||||||
|
"<title>" + _("Redirecting to {0}", host) + "</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" +
|
||||||
|
"</head><body>\n" +
|
||||||
|
"<div class=logo>\n" +
|
||||||
|
"<a href=\"http://127.0.0.1:7657/\" title=\"" + _("Router Console") + "\"><img src=\"http://proxy.i2p/themes/console/images/i2plogo.png\" alt=\"I2P Router Console\" border=\"0\"></a><hr>\n" +
|
||||||
|
"<a href=\"http://127.0.0.1:7657/config\">" + _("Configuration") + "</a> <a href=\"http://127.0.0.1:7657/help.jsp\">" + _("Help") + "</a> <a href=\"http://127.0.0.1:7657/susidns/index.jsp\">" + _("Addressbook") + "</a>\n" +
|
||||||
|
"</div>" +
|
||||||
|
"<div class=warning id=warning>\n" +
|
||||||
|
"<h3>" +
|
||||||
|
(success ?
|
||||||
|
_("Saved {0} to the {1} addressbook, redirecting now.", host, book) :
|
||||||
|
_("Failed to save {0} to the {1} addressbook, redirecting now.", host, book)) +
|
||||||
|
"</h3>\n<p><a href=\"" + url + "\">" +
|
||||||
|
_("Click here if you are not redirected automatically.") +
|
||||||
|
"</a></p></div>").getBytes("UTF-8"));
|
||||||
|
writeFooter(out);
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String decode(String s) {
|
||||||
|
if (!s.contains("%"))
|
||||||
|
return s;
|
||||||
|
StringBuilder buf = new StringBuilder(s.length());
|
||||||
|
for (int i = 0; i < s.length(); i++) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
if (c != '%') {
|
||||||
|
buf.append(c);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
buf.append((char) Integer.parseInt(s.substring(++i, (++i) + 1), 16));
|
||||||
|
} catch (IndexOutOfBoundsException ioobe) {
|
||||||
|
break;
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.web.messages";
|
private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.web.messages";
|
||||||
|
|
||||||
/** lang in routerconsole.lang property, else current locale */
|
/** lang in routerconsole.lang property, else current locale */
|
||||||
public static String _(String key) {
|
protected static String _(String key) {
|
||||||
return Translate.getString(key, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
|
return Translate.getString(key, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** {0} */
|
||||||
|
protected static String _(String key, Object o) {
|
||||||
|
return Translate.getString(key, o, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
/** {0} and {1} */
|
/** {0} and {1} */
|
||||||
public static String _(String key, Object o, Object o2) {
|
protected static String _(String key, Object o, Object o2) {
|
||||||
return Translate.getString(key, o, o2, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
|
return Translate.getString(key, o, o2, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
2011-05-25 zzz
|
||||||
|
* CPUID: Load 64-bit libcpuid if available
|
||||||
|
* HTTP Proxy: Address helper refactoring, address book add form
|
||||||
|
* Naming: B32 fixes
|
||||||
|
* NetDB: Increase floodfills again
|
||||||
|
|
||||||
2011-05-23 zzz
|
2011-05-23 zzz
|
||||||
* Console:
|
* Console:
|
||||||
- Disable zh translation in graphs on windows due to font issues
|
- Disable zh translation in graphs on windows due to font issues
|
||||||
|
25
installer/resources/proxy/ahelper-new-header.ht
Normal file
25
installer/resources/proxy/ahelper-new-header.ht
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
HTTP/1.1 409 New Address
|
||||||
|
Content-Type: text/html; charset=UTF-8
|
||||||
|
Cache-control: no-cache
|
||||||
|
Connection: close
|
||||||
|
Proxy-Connection: close
|
||||||
|
|
||||||
|
<html><head>
|
||||||
|
<title>I2P Information: New Host Name</title>
|
||||||
|
<link rel="shortcut icon" href="http://proxy.i2p/themes/console/images/favicon.ico" >
|
||||||
|
<link href="http://proxy.i2p/themes/console/default/console.css" rel="stylesheet" type="text/css" >
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class=logo>
|
||||||
|
<a href="http://127.0.0.1:7657/index.jsp" title="Router Console"><img src="http://proxy.i2p/themes/console/images/i2plogo.png" alt="I2P Router Console" border="0"></a><hr>
|
||||||
|
<a href="http://127.0.0.1:7657/config.jsp">Configuration</a> <a href="http://127.0.0.1:7657/help.jsp">Help</a> <a href="http://127.0.0.1:7657/susidns/">Addressbook</a>
|
||||||
|
</div>
|
||||||
|
<div class=warning id=warning>
|
||||||
|
<h3>Information: New Host Name with Address Helper</h3>
|
||||||
|
<p>
|
||||||
|
The address helper link you followed is for a new host name that is not in your address book.
|
||||||
|
You may save this host name to your local address book.
|
||||||
|
If you save it to your address book, you will not see this message again.
|
||||||
|
If you do not save it, the host name will be forgotten after the next router restart.
|
||||||
|
If you do not wish to visit this host, click the "back" button on your browser.
|
||||||
|
</p>
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 5;
|
public final static long BUILD = 6;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
Reference in New Issue
Block a user