* I2PTunnel:

- Display custom error pages for I2PSocketExceptions (ticket #788)
   - Tag I2PSocketException text for translation (no bundles yet)
   - Move methods from superclasses to I2PTunnelHTTPClientBase
   - Fix connect client error pages, but they aren't displayed anyway
   - Don't start I2PTunnelRunner threads in constructor (ticket #973)
   - Synch close() in I2PTunnelServer
   - Cleanups and javadocs
This commit is contained in:
zzz
2014-05-18 21:13:22 +00:00
parent 2467856011
commit d1bd893a7b
16 changed files with 549 additions and 318 deletions

View File

@ -122,7 +122,9 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
int port = addr.getPort();
i2ps = createI2PSocket(clientDest, port);
i2ps.setReadTimeout(readTimeout);
new I2PTunnelRunner(s, i2ps, sockLock, null, mySockets);
Thread t = new I2PTunnelRunner(s, i2ps, sockLock, null, null, mySockets,
(I2PTunnelRunner.FailCallback) null);
t.start();
} catch (Exception ex) {
if (_log.shouldLog(Log.INFO))
_log.info("Error connecting", ex);

View File

@ -59,18 +59,6 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
public static final String AUTH_REALM = "I2P SSL Proxy";
private final static byte[] ERR_DESTINATION_UNKNOWN =
("HTTP/1.1 503 Service Unavailable\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+
"\r\n"+
"<html><body><H1>I2P ERROR: DESTINATION NOT FOUND</H1>"+
"That I2P Destination was not found. "+
"The host (or the outproxy, if you're using one) could also "+
"be temporarily offline. You may want to <b>retry</b>. "+
"Could not find the following Destination:<BR><BR><div>")
.getBytes();
private final static byte[] ERR_BAD_PROTOCOL =
("HTTP/1.1 405 Bad Method\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+
@ -293,9 +281,9 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
if (clientDest == null) {
byte[] header;
if (usingWWWProxy)
header = getErrorPage("dnfp-header.ht", ERR_DESTINATION_UNKNOWN);
header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
else
header = getErrorPage("dnfh-header.ht", ERR_DESTINATION_UNKNOWN);
header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN);
writeErrorMessage(header, out, targetRequest, usingWWWProxy, destination);
s.close();
return;
@ -308,89 +296,29 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
data = newRequest.toString().getBytes("ISO-8859-1");
else
response = SUCCESS_RESPONSE;
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
// starts itself
new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
} catch (SocketException ex) {
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
handleConnectClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
Thread t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
t.start();
} catch (IOException ex) {
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
handleConnectClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
} catch (I2PException ex) {
_log.info("getPrefix(requestId) + Error trying to connect", ex);
handleConnectClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
} catch (OutOfMemoryError oom) {
IOException ex = new IOException("OOM");
_log.info("getPrefix(requestId) + Error trying to connect", ex);
handleConnectClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
}
}
private static class OnTimeout implements Runnable {
private final Socket _socket;
private final OutputStream _out;
private final String _target;
private final boolean _usingProxy;
private final String _wwwProxy;
private final long _requestId;
public OnTimeout(Socket s, OutputStream out, String target, boolean usingProxy, String wwwProxy, long id) {
_socket = s;
_out = out;
_target = target;
_usingProxy = usingProxy;
_wwwProxy = wwwProxy;
_requestId = id;
}
public void run() {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Timeout occured requesting " + _target);
handleConnectClientException(new RuntimeException("Timeout"), _out,
_target, _usingProxy, _wwwProxy, _requestId);
closeSocket(_socket);
}
}
private static void writeErrorMessage(byte[] errMessage, OutputStream out) throws IOException {
if (out == null)
return;
out.write(errMessage);
out.write("\n</body></html>\n".getBytes());
out.flush();
}
private static void writeErrorMessage(byte[] errMessage, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy) throws IOException {
if (out != null) {
out.write(errMessage);
if (targetRequest != null) {
out.write(targetRequest.getBytes());
if (usingWWWProxy)
out.write(("<br />WWW proxy: " + wwwProxy).getBytes());
}
out.write("</div>".getBytes());
out.write("\n</body></html>\n".getBytes());
out.flush();
}
}
private static void handleConnectClientException(Exception ex, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy, long requestId) {
if (out == null)
return;
byte[] header;
if (usingWWWProxy)
header = getErrorPage(I2PAppContext.getGlobalContext(), "dnfp-header.ht", ERR_DESTINATION_UNKNOWN);
else
header = getErrorPage(I2PAppContext.getGlobalContext(), "dnf-header.ht", ERR_DESTINATION_UNKNOWN);
try {
writeErrorMessage(header, out, targetRequest, usingWWWProxy, wwwProxy);
} catch (IOException ioe) {}
writeFooter(out);
}
}

View File

@ -10,7 +10,6 @@ import java.net.Socket;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.Locale;
import java.util.Properties;
import java.util.StringTokenizer;
@ -35,7 +34,6 @@ import net.i2p.i2ptunnel.localServer.LocalHTTPServer;
import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
import net.i2p.util.PortMapper;
import net.i2p.util.Translate;
/**
* Act as a mini HTTP proxy, handling various different types of requests,
@ -93,17 +91,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"\r\n" +
"<html><body><H1>I2P ERROR: REQUEST DENIED</H1>" +
"You attempted to connect to a non-I2P website or location.<BR>").getBytes();
private final static byte[] ERR_DESTINATION_UNKNOWN =
("HTTP/1.1 503 Service Unavailable\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" +
"Cache-control: no-cache\r\n" +
"\r\n" +
"<html><body><H1>I2P ERROR: DESTINATION NOT FOUND</H1>" +
"That I2P Destination was not found. Perhaps you pasted in the " +
"wrong BASE64 I2P Destination or the link you are following is " +
"bad. The host (or the WWW proxy, if you're using one) could also " +
"be temporarily offline. You may want to <b>retry</b>. " +
"Could not find the following Destination:<BR><BR><div>").getBytes();
/*****
private final static byte[] ERR_TIMEOUT =
("HTTP/1.1 504 Gateway Timeout\r\n"+
@ -642,7 +630,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if(alias.equals("i2p")) {
// bad ahelperKey
byte[] header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
writeErrorMessage(header, out, targetRequest, false, destination, null);
writeErrorMessage(header, out, targetRequest, false, destination);
} else {
String trustedURL = requestURI.toASCIIString();
URI conflictURI;
@ -951,7 +939,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// no destination, going to outproxy plugin
if (usingInternalOutproxy) {
Socket outSocket = outproxy.connect(host, remotePort);
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
byte[] data;
byte[] response;
if (method.toUpperCase(Locale.US).equals("CONNECT")) {
@ -961,7 +949,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
data = newRequest.toString().getBytes("ISO-8859-1");
response = null;
}
new I2PTunnelOutproxyRunner(s, outSocket, sockLock, data, response, onTimeout);
Thread t = new I2PTunnelOutproxyRunner(s, outSocket, sockLock, data, response, onTimeout);
t.start();
return;
}
@ -980,7 +969,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
_log.warn(getPrefix(requestId) + "Could not find destination for " + addressHelper);
}
byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
writeErrorMessage(header, out, targetRequest, false, destination, null);
writeErrorMessage(header, out, targetRequest, false, destination);
s.close();
return;
}
@ -1018,7 +1007,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
} else if(ahelperPresent) {
header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
} else if(destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
header = getErrorPage("dnf", ERR_DESTINATION_UNKNOWN);
header = getErrorPage("nols", ERR_DESTINATION_UNKNOWN);
} else {
header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN);
jumpServers = getTunnel().getClientOptions().getProperty(PROP_JUMP_SERVERS);
@ -1067,7 +1056,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
if (remotePort > 0)
sktOpts.setPort(remotePort);
I2PSocket i2ps = createI2PSocket(clientDest, sktOpts);
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
if (method.toUpperCase(Locale.US).equals("CONNECT")) {
byte[] data;
byte[] response;
@ -1078,30 +1067,32 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
data = null;
response = I2PTunnelConnectClient.SUCCESS_RESPONSE;
}
new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
Thread t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
t.start();
} else {
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
Thread t = new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
t.start();
}
} catch(IOException ex) {
if(_log.shouldLog(Log.INFO)) {
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
}
//l.log("Error connecting: " + ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
} catch(I2PException ex) {
if(_log.shouldLog(Log.INFO)) {
_log.info("getPrefix(requestId) + Error trying to connect", ex);
}
//l.log("Error connecting: " + ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
} catch(OutOfMemoryError oom) {
IOException ex = new IOException("OOM");
_log.error("getPrefix(requestId) + Error trying to connect", oom);
//l.log("Error connecting: " + ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
}
}
@ -1229,43 +1220,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
return Base32.encode(_dest.calculateHash().getData()) + ".b32.i2p";
}
/**
* Public only for LocalHTTPServer, not for general use
*/
public static void writeFooter(OutputStream out) throws IOException {
// the css is hiding this div for now, but we'll keep it here anyway
out.write("<div class=\"proxyfooter\"><p><i>I2P HTTP Proxy Server<br>Generated on: ".getBytes());
out.write(new Date().toString().getBytes());
out.write("</i></div></body></html>\n".getBytes());
out.flush();
}
private static class OnTimeout implements Runnable {
private final Socket _socket;
private final OutputStream _out;
private final String _target;
private final boolean _usingProxy;
private final String _wwwProxy;
private final long _requestId;
public OnTimeout(Socket s, OutputStream out, String target, boolean usingProxy, String wwwProxy, long id) {
_socket = s;
_out = out;
_target = target;
_usingProxy = usingProxy;
_wwwProxy = wwwProxy;
_requestId = id;
}
public void run() {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Timeout occured requesting " + _target);
handleHTTPClientException(new RuntimeException("Timeout"), _out,
_target, _usingProxy, _wwwProxy, _requestId);
closeSocket(_socket);
}
}
public static final String DEFAULT_JUMP_SERVERS =
"http://i2host.i2p/cgi-bin/i2hostjump?," +
"http://stats.i2p/cgi-bin/jump.cgi?a=," +
@ -1273,101 +1227,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
"http://i2pjump.i2p/jump/";
//"http://i2jump.i2p/";
/**
* @param jumpServers comma- or space-separated list, or null
*/
private static void writeErrorMessage(byte[] errMessage, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy, String jumpServers) throws IOException {
if(out != null) {
out.write(errMessage);
if(targetRequest != null) {
String uri = targetRequest.replace("&", "&amp;");
out.write("<a href=\"".getBytes());
out.write(uri.getBytes());
out.write("\">".getBytes());
out.write(uri.getBytes());
out.write("</a>".getBytes());
if(usingWWWProxy) {
out.write(("<br><br><b>").getBytes());
out.write(_("HTTP Outproxy").getBytes("UTF-8"));
out.write((":</b> " + wwwProxy).getBytes());
}
if(jumpServers != null && jumpServers.length() > 0) {
boolean first = true;
if(uri.startsWith("http://")) {
uri = uri.substring(7);
}
StringTokenizer tok = new StringTokenizer(jumpServers, ", ");
while(tok.hasMoreTokens()) {
String jurl = tok.nextToken();
String jumphost;
try {
URI jURI = new URI(jurl);
String proto = jURI.getScheme();
jumphost = jURI.getHost();
if (proto == null || jumphost == null ||
!proto.toLowerCase(Locale.US).equals("http"))
continue;
jumphost = jumphost.toLowerCase(Locale.US);
if (!jumphost.endsWith(".i2p"))
continue;
} catch(URISyntaxException use) {
continue;
}
// Skip jump servers we don't know
if(!jumphost.endsWith(".b32.i2p")) {
Destination dest = I2PAppContext.getGlobalContext().namingService().lookup(jumphost);
if(dest == null) {
continue;
}
}
if (first) {
first = false;
out.write("<br><br>".getBytes());
out.write(_("Click a link below to look for an address helper by using a \"jump\" service:").getBytes("UTF-8"));
out.write("<br>\n".getBytes());
}
out.write("<br><a href=\"".getBytes());
out.write(jurl.getBytes());
out.write(uri.getBytes());
out.write("\">".getBytes());
// Translators: parameter is a host name
out.write(_("{0} jump service", jumphost).getBytes());
out.write("</a>\n".getBytes());
}
}
}
out.write("</div>".getBytes());
writeFooter(out);
}
}
private static void handleHTTPClientException(Exception ex, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy, long requestId) {
// static
//if (_log.shouldLog(Log.WARN))
// _log.warn(getPrefix(requestId) + "Error sending to " + wwwProxy + " (proxy? " + usingWWWProxy + ", request: " + targetRequest, ex);
if(out != null) {
try {
byte[] header;
if(usingWWWProxy) {
header = getErrorPage(I2PAppContext.getGlobalContext(), "dnfp", ERR_DESTINATION_UNKNOWN);
} else {
header = getErrorPage(I2PAppContext.getGlobalContext(), "dnf", ERR_DESTINATION_UNKNOWN);
}
writeErrorMessage(header, out, targetRequest, usingWWWProxy, wwwProxy, null);
} catch(IOException ioe) {
// static
//_log.warn(getPrefix(requestId) + "Error writing out the 'destination was unknown' " + "message", ioe);
}
} else {
// static
//_log.warn(getPrefix(requestId) + "Client disconnected before we could say that destination " + "was unknown", ex);
}
}
/** @param host ignored */
private static boolean isSupportedAddress(String host, String protocol) {
if((host == null) || (protocol == null)) {
@ -1526,22 +1385,4 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
}
}
****/
/** these strings go in the jar, not the war */
private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.proxy.messages";
/** lang in routerconsole.lang property, else current locale */
protected static String _(String key) {
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} */
protected static String _(String key, Object o, Object o2) {
return Translate.getString(key, o, o2, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
}
}

View File

@ -3,14 +3,18 @@
*/
package net.i2p.i2ptunnel;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.io.File;
import java.util.BitSet;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -23,14 +27,18 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PSocketException;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.util.EepGet;
import net.i2p.util.EventDispatcher;
import net.i2p.util.InternalSocket;
import net.i2p.util.Log;
import net.i2p.util.PasswordManager;
import net.i2p.util.Translate;
import net.i2p.util.TranslateReader;
/**
@ -73,11 +81,25 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel")
.getBytes();
protected final static byte[] ERR_DESTINATION_UNKNOWN =
("HTTP/1.1 503 Service Unavailable\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" +
"Cache-control: no-cache\r\n" +
"\r\n" +
"<html><body><H1>I2P ERROR: DESTINATION NOT FOUND</H1>" +
"That I2P Destination was not found. Perhaps you pasted in the " +
"wrong BASE64 I2P Destination or the link you are following is " +
"bad. The host (or the WWW proxy, if you're using one) could also " +
"be temporarily offline. You may want to <b>retry</b>. " +
"Could not find the following Destination:<BR><BR><div>").getBytes();
private final byte[] _proxyNonce;
private final ConcurrentHashMap<String, NonceInfo> _nonces;
private final AtomicInteger _nonceCleanCounter = new AtomicInteger();
protected String getPrefix(long requestId) { return "Client[" + _clientId + "/" + requestId + "]: "; }
protected String getPrefix(long requestId) {
return "HTTPClient[" + _clientId + '/' + requestId + "]: ";
}
protected String selectProxy() {
synchronized (_proxyList) {
@ -481,6 +503,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
}
}
/** these strings go in the jar, not the war */
private static final String BUNDLE_NAME = "net.i2p.i2ptunnel.proxy.messages";
/**
@ -505,4 +528,226 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
}
// we won't ever get here
}
/**
* @since 0.9.14 moved from superclasses
*/
protected class OnTimeout implements I2PTunnelRunner.FailCallback {
private final Socket _socket;
private final OutputStream _out;
private final String _target;
private final boolean _usingProxy;
private final String _wwwProxy;
private final long _requestId;
public OnTimeout(Socket s, OutputStream out, String target, boolean usingProxy, String wwwProxy, long id) {
_socket = s;
_out = out;
_target = target;
_usingProxy = usingProxy;
_wwwProxy = wwwProxy;
_requestId = id;
}
public void onFail(Exception ex) {
Throwable cause = ex.getCause();
if (cause != null && cause instanceof I2PSocketException) {
I2PSocketException ise = (I2PSocketException) cause;
handleI2PSocketException(ise, _out, _target, _usingProxy, _wwwProxy);
} else {
handleClientException(ex, _out, _target, _usingProxy, _wwwProxy, _requestId);
}
closeSocket(_socket);
}
}
/**
* @since 0.9.14 moved from superclasses
*/
protected void handleClientException(Exception ex, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy, long requestId) {
if (out == null)
return;
byte[] header;
if (usingWWWProxy)
header = getErrorPage(I2PAppContext.getGlobalContext(), "dnfp", ERR_DESTINATION_UNKNOWN);
else
header = getErrorPage(I2PAppContext.getGlobalContext(), "dnf", ERR_DESTINATION_UNKNOWN);
try {
writeErrorMessage(header, out, targetRequest, usingWWWProxy, wwwProxy);
} catch (IOException ioe) {}
}
/**
* Generate an error page based on the status code
* in our custom exception.
*
* @since 0.9.14
*/
protected void handleI2PSocketException(I2PSocketException ise, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy) {
if (out == null)
return;
int status = ise.getStatus();
String error;
//TODO MessageStatusMessage.STATUS_SEND_FAILURE_UNSUPPORTED_ENCRYPTION
if (status == MessageStatusMessage.STATUS_SEND_FAILURE_NO_LEASESET) {
error = usingWWWProxy ? "nolsp" : "nols";
} else {
error = usingWWWProxy ? "dnfp" : "dnf";
}
byte[] header = getErrorPage(error, ERR_DESTINATION_UNKNOWN);
String message = ise.getLocalizedMessage();
try {
writeErrorMessage(header, message, out, targetRequest, usingWWWProxy, wwwProxy);
} catch(IOException ioe) {}
}
/**
* No jump servers or extra message
* @since 0.9.14
*/
protected void writeErrorMessage(byte[] errMessage, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy) throws IOException {
writeErrorMessage(errMessage, null, out, targetRequest, usingWWWProxy, wwwProxy, null);
}
/**
* No extra message
* @param jumpServers comma- or space-separated list, or null
* @since 0.9.14 moved from superclasses
*/
protected void writeErrorMessage(byte[] errMessage, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy, String jumpServers) throws IOException {
writeErrorMessage(errMessage, null, out, targetRequest, usingWWWProxy, wwwProxy, jumpServers);
}
/**
* No jump servers
* @param extraMessage extra message
* @since 0.9.14
*/
protected void writeErrorMessage(byte[] errMessage, String extraMessage,
OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy) throws IOException {
writeErrorMessage(errMessage, extraMessage, out, targetRequest, usingWWWProxy, wwwProxy, null);
}
/**
* @param jumpServers comma- or space-separated list, or null
* @param msg extra message
* @since 0.9.14
*/
protected void writeErrorMessage(byte[] errMessage, String extraMessage,
OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy,
String jumpServers) throws IOException {
if (out == null)
return;
out.write(errMessage);
if (targetRequest != null) {
String uri = targetRequest.replace("&", "&amp;");
out.write("<a href=\"".getBytes());
out.write(uri.getBytes());
out.write("\">".getBytes());
out.write(uri.getBytes());
out.write("</a>".getBytes());
if (usingWWWProxy) {
out.write(("<br><br><b>").getBytes());
out.write(_("HTTP Outproxy").getBytes("UTF-8"));
out.write((":</b> " + wwwProxy).getBytes());
}
if (extraMessage != null) {
out.write(("<br><br><b>" + extraMessage + "</b>").getBytes());
}
if (jumpServers != null && jumpServers.length() > 0) {
boolean first = true;
if(uri.startsWith("http://")) {
uri = uri.substring(7);
}
StringTokenizer tok = new StringTokenizer(jumpServers, ", ");
while(tok.hasMoreTokens()) {
String jurl = tok.nextToken();
String jumphost;
try {
URI jURI = new URI(jurl);
String proto = jURI.getScheme();
jumphost = jURI.getHost();
if (proto == null || jumphost == null ||
!proto.toLowerCase(Locale.US).equals("http"))
continue;
jumphost = jumphost.toLowerCase(Locale.US);
if (!jumphost.endsWith(".i2p"))
continue;
} catch(URISyntaxException use) {
continue;
}
// Skip jump servers we don't know
if (!jumphost.endsWith(".b32.i2p")) {
Destination dest = _context.namingService().lookup(jumphost);
if(dest == null) {
continue;
}
}
if (first) {
first = false;
out.write("<br><br>".getBytes());
out.write(_("Click a link below to look for an address helper by using a \"jump\" service:").getBytes("UTF-8"));
out.write("<br>\n".getBytes());
}
out.write("<br><a href=\"".getBytes());
out.write(jurl.getBytes());
out.write(uri.getBytes());
out.write("\">".getBytes());
// Translators: parameter is a host name
out.write(_("{0} jump service", jumphost).getBytes());
out.write("</a>\n".getBytes());
}
}
}
out.write("</div>".getBytes());
writeFooter(out);
}
/**
* Flushes.
*
* Public only for LocalHTTPServer, not for general use
* @since 0.9.14 moved from I2PTunnelHTTPClient
*/
public static void writeFooter(OutputStream out) throws IOException {
// The css is hiding this div for now, but we'll keep it here anyway
// Tag the strings below for translation if we unhide it.
out.write("<div class=\"proxyfooter\"><p><i>I2P HTTP Proxy Server<br>Generated on: ".getBytes());
out.write(new Date().toString().getBytes());
out.write("</i></div></body></html>\n".getBytes());
out.flush();
}
/**
* Translate
* @since 0.9.14 moved from I2PTunnelHTTPClient
*/
protected String _(String key) {
return Translate.getString(key, _context, BUNDLE_NAME);
}
/**
* Translate
* {0}
* @since 0.9.14 moved from I2PTunnelHTTPClient
*/
protected String _(String key, Object o) {
return Translate.getString(key, o, _context, BUNDLE_NAME);
}
/**
* Translate
* {0} and {1}
* @since 0.9.14 moved from I2PTunnelHTTPClient
*/
protected String _(String key, Object o, Object o2) {
return Translate.getString(key, o, o2, _context, BUNDLE_NAME);
}
}

View File

@ -21,11 +21,16 @@ import net.i2p.util.Log;
* and the server should echo it. However, both broken and malicious
* servers could ignore that, potentially confusing the user.
*
* Warning - not maintained as a stable API for external use.
*/
public class I2PTunnelHTTPClientRunner extends I2PTunnelRunner {
/**
* Does NOT start itself. Caller must call start().
*/
public I2PTunnelHTTPClientRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
List<I2PSocket> sockList, Runnable onTimeout) {
super(s, i2ps, slock, initialI2PData, sockList, onTimeout);
List<I2PSocket> sockList, FailCallback onFail) {
super(s, i2ps, slock, initialI2PData, null, sockList, onFail);
}
@Override

View File

@ -307,7 +307,9 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
Thread.currentThread().getName()+".hc");
req.start();
} else {
new I2PTunnelRunner(s, socket, slock, null, modifiedHeader.getBytes(), null);
Thread t = new I2PTunnelRunner(s, socket, slock, null, modifiedHeader.getBytes(),
null, (I2PTunnelRunner.FailCallback) null);
t.start();
}
long afterHandle = getTunnel().getContext().clock().now();

View File

@ -138,7 +138,9 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
modifiedRegistration = buf.toString();
}
Socket s = getSocket(socket.getPeerDestination().calculateHash(), socket.getLocalPort());
new I2PTunnelRunner(s, socket, slock, null, modifiedRegistration.getBytes(), null);
Thread t = new I2PTunnelRunner(s, socket, slock, null, modifiedRegistration.getBytes(),
null, (I2PTunnelRunner.FailCallback) null);
t.start();
} catch (SocketException ex) {
try {
// Send a response so the user doesn't just see a disconnect

View File

@ -26,6 +26,8 @@ import net.i2p.util.Log;
/**
* Like I2PTunnelRunner but socket-to-socket
*
* Warning - not maintained as a stable API for external use.
*
* @since 0.9.11
*/
public class I2PTunnelOutproxyRunner extends I2PAppThread {
@ -54,22 +56,22 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
/** when the runner started up */
private final long startedOn;
/** if we die before receiving any data, run this job */
private final Runnable onTimeout;
private final I2PTunnelRunner.FailCallback onTimeout;
private long totalSent;
private long totalReceived;
private static final AtomicLong __forwarderId = new AtomicLong();
/**
* Starts itself (fixme)
* Does NOT start itself. Caller must call start().
*
* @param slock the socket lock, non-null
* @param initialI2PData may be null
* @param onTimeout May be null. If non-null and no data (except initial data) was sent or received,
* @param onTimeout May be null. If non-null and no data (except initial data) was received,
it will be run before closing s.
*/
public I2PTunnelOutproxyRunner(Socket s, Socket i2ps, Object slock, byte[] initialI2PData,
byte[] initialSocketData, Runnable onTimeout) {
byte[] initialSocketData, I2PTunnelRunner.FailCallback onTimeout) {
this.s = s;
this.i2ps = i2ps;
this.slock = slock;
@ -83,7 +85,6 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
_log.info("OutproxyRunner started");
_runnerId = __runnerId.incrementAndGet();
setName("OutproxyRunner " + _runnerId);
start();
}
/**
@ -144,6 +145,9 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
in = new BufferedInputStream(in, 2*NETWORK_BUFFER_SIZE);
Thread t1 = new StreamForwarder(in, i2pout, true);
Thread t2 = new StreamForwarder(i2pin, out, false);
// TODO can we run one of these inline and save a thread?
t1.start();
t2.start();
synchronized (finishLock) {
while (!finished) {
finishLock.wait();
@ -159,7 +163,7 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
+ " totalSent = " + totalSent + " job = " + onTimeout);
// Run even if totalSent > 0, as that's probably POST data.
if (totalReceived <= 0)
onTimeout.run();
onTimeout.onFail(null);
}
// now one connection is dead - kill the other as well, after making sure we flush
@ -242,6 +246,9 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
}
}
/**
* Forward data in one direction
*/
private class StreamForwarder extends I2PAppThread {
private final InputStream in;
@ -250,6 +257,9 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
private final boolean _toI2P;
private final ByteCache _cache;
/**
* Does not start itself. Caller must start()
*/
private StreamForwarder(InputStream in, OutputStream out, boolean toI2P) {
this.in = in;
this.out = out;
@ -257,7 +267,6 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
direction = (toI2P ? "toOutproxy" : "fromOutproxy");
_cache = ByteCache.getInstance(32, NETWORK_BUFFER_SIZE);
setName("OutproxyForwarder " + _runnerId + '.' + __forwarderId.incrementAndGet());
start();
}
@Override

View File

@ -25,6 +25,11 @@ import net.i2p.util.I2PAppThread;
import net.i2p.util.InternalSocket;
import net.i2p.util.Log;
/**
* A thread that starts two more threads, one to forward traffic in each direction.
*
* Warning - not maintained as a stable API for external use.
*/
public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErrorListener {
protected final Log _log;
@ -43,7 +48,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
private final Socket s;
private final I2PSocket i2ps;
private final Object slock, finishLock = new Object();
volatile boolean finished = false;
private volatile boolean finished;
private final byte[] initialI2PData;
private final byte[] initialSocketData;
/** when the last data was sent/received (or -1 if never) */
@ -53,24 +58,35 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
private final List<I2PSocket> sockList;
/** if we die before receiving any data, run this job */
private final Runnable onTimeout;
private final FailCallback _onFail;
private long totalSent;
private long totalReceived;
private static final AtomicLong __forwarderId = new AtomicLong();
/**
* For use in new constructor
* @since 0.9.14
*/
public interface FailCallback {
/**
* @param e may be null
*/
public void onFail(Exception e);
}
/**
* Starts itself
*
* @param slock the socket lock, non-null
* @param initialI2PData may be null
* @param sockList may be null. Caller must add i2ps to the list! It will be removed here on completion.
* Will synchronize on slock when removing.
* @deprecated use FailCallback constructor
*/
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
List<I2PSocket> sockList) {
this(s, i2ps, slock, initialI2PData, null, sockList, null);
}
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
byte[] initialSocketData, List<I2PSocket> sockList) {
this(s, i2ps, slock, initialI2PData, initialSocketData, sockList, null);
}
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
List<I2PSocket> sockList, Runnable onTimeout) {
this(s, i2ps, slock, initialI2PData, null, sockList, onTimeout);
this(s, i2ps, slock, initialI2PData, null, sockList, null, null, true);
}
/**
@ -81,11 +97,78 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
* @param initialSocketData may be null
* @param sockList may be null. Caller must add i2ps to the list! It will be removed here on completion.
* Will synchronize on slock when removing.
* @param onTimeout May be null. If non-null and no data (except initial data) was sent or received,
it will be run before closing s.
* @deprecated use FailCallback constructor
*/
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
byte[] initialSocketData, List<I2PSocket> sockList) {
this(s, i2ps, slock, initialI2PData, initialSocketData, sockList, null, null, true);
}
/**
* Starts itself
*
* @param slock the socket lock, non-null
* @param initialI2PData may be null
* @param sockList may be null. Caller must add i2ps to the list! It will be removed here on completion.
* Will synchronize on slock when removing.
* @param onTimeout May be null. If non-null and no data (except initial data) was received,
* it will be run before closing s.
* @deprecated use FailCallback constructor
*/
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
List<I2PSocket> sockList, Runnable onTimeout) {
this(s, i2ps, slock, initialI2PData, null, sockList, onTimeout, null, true);
}
/**
* Starts itself
*
* @param slock the socket lock, non-null
* @param initialI2PData may be null
* @param initialSocketData may be null
* @param sockList may be null. Caller must add i2ps to the list! It will be removed here on completion.
* Will synchronize on slock when removing.
* @param onTimeout May be null. If non-null and no data (except initial data) was received,
* it will be run before closing s.
* @deprecated use FailCallback constructor
*/
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
byte[] initialSocketData, List<I2PSocket> sockList, Runnable onTimeout) {
this(s, i2ps, slock, initialI2PData, null, sockList, onTimeout, null, true);
}
/**
* Recommended new constructor. Does NOT start itself. Caller must call start().
*
* @param slock the socket lock, non-null
* @param initialI2PData may be null
* @param initialSocketData may be null
* @param sockList may be null. Caller must add i2ps to the list! It will be removed here on completion.
* Will synchronize on slock when removing.
* @param onFail May be null. If non-null and no data (except initial data) was received,
* it will be run before closing s.
*/
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
byte[] initialSocketData, List<I2PSocket> sockList, FailCallback onFail) {
this(s, i2ps, slock, initialI2PData, null, sockList, null, onFail, false);
}
/**
* Base constructor
*
* @param slock the socket lock, non-null
* @param initialI2PData may be null
* @param initialSocketData may be null
* @param sockList may be null. Caller must add i2ps to the list! It will be removed here on completion.
* Will synchronize on slock when removing.
* @param onTimeout May be null. If non-null and no data (except initial data) was received,
* it will be run before closing s.
* @param onFail Trumps onTimeout
* @param shouldStart should thread be started in constructor (bad, false recommended)
*/
private I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
byte[] initialSocketData, List<I2PSocket> sockList, Runnable onTimeout,
FailCallback onFail, boolean shouldStart) {
this.sockList = sockList;
this.s = s;
this.i2ps = i2ps;
@ -93,6 +176,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
this.initialI2PData = initialI2PData;
this.initialSocketData = initialSocketData;
this.onTimeout = onTimeout;
_onFail = onFail;
lastActivityOn = -1;
startedOn = Clock.getInstance().now();
_log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
@ -100,6 +184,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
_log.info("I2PTunnelRunner started");
_runnerId = __runnerId.incrementAndGet();
setName("I2PTunnelRunner " + _runnerId);
if (shouldStart)
start();
}
@ -181,8 +266,11 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
+ " written to the socket, starting forwarders");
if (!(s instanceof InternalSocket))
in = new BufferedInputStream(in, 2*NETWORK_BUFFER_SIZE);
Thread t1 = new StreamForwarder(in, i2pout, true);
Thread t2 = new StreamForwarder(i2pin, out, false);
StreamForwarder toI2P = new StreamForwarder(in, i2pout, true);
StreamForwarder fromI2P = new StreamForwarder(i2pin, out, false);
// TODO can we run one of these inline and save a thread?
toI2P.start();
fromI2P.start();
synchronized (finishLock) {
while (!finished) {
finishLock.wait();
@ -192,7 +280,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
_log.debug("At least one forwarder completed, closing and joining");
// this task is useful for the httpclient
if (onTimeout != null) {
if (onTimeout != null || _onFail != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("runner has a timeout job, totalReceived = " + totalReceived
+ " totalSent = " + totalSent + " job = " + onTimeout);
@ -200,12 +288,20 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
// This will be run even if initialSocketData != null, it's the timeout job's
// responsibility to know that and decide whether or not to write to the socket.
// HTTPClient never sets initialSocketData.
if (totalReceived <= 0)
if (totalReceived <= 0) {
if (_onFail != null) {
Exception e = fromI2P.getFailure();
if (e == null)
e = toI2P.getFailure();
_onFail.onFail(e);
} else {
onTimeout.run();
}
}
}
// now one connection is dead - kill the other as well, after making sure we flush
close(out, in, i2pout, i2pin, s, i2ps, t1, t2);
close(out, in, i2pout, i2pin, s, i2ps, toI2P, fromI2P);
} catch (InterruptedException ex) {
if (_log.shouldLog(Log.ERROR))
_log.error("Interrupted", ex);
@ -291,6 +387,9 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
t2.join(30*1000);
}
/**
* Deprecated, unimplemented in streaming, never called.
*/
public void errorOccurred() {
synchronized (finishLock) {
finished = true;
@ -306,6 +405,9 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
}
}
/**
* Forward data in one direction
*/
private class StreamForwarder extends I2PAppThread {
private final InputStream in;
@ -313,15 +415,18 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
private final String direction;
private final boolean _toI2P;
private final ByteCache _cache;
private volatile Exception _failure;
private StreamForwarder(InputStream in, OutputStream out, boolean toI2P) {
/**
* Does not start itself. Caller must start()
*/
public StreamForwarder(InputStream in, OutputStream out, boolean toI2P) {
this.in = in;
this.out = out;
_toI2P = toI2P;
direction = (toI2P ? "toI2P" : "fromI2P");
_cache = ByteCache.getInstance(32, NETWORK_BUFFER_SIZE);
setName("StreamForwarder " + _runnerId + '.' + __forwarderId.incrementAndGet());
start();
}
@Override
@ -377,10 +482,12 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
ex);
}
}
_failure = ex;
} catch (InterruptedIOException ex) {
if (_log.shouldLog(Log.WARN))
_log.warn(direction + ": Closing connection due to timeout (error: \""
+ ex.getMessage() + "\")");
_failure = ex;
} catch (IOException ex) {
if (!finished) {
if (_log.shouldLog(Log.WARN))
@ -388,6 +495,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
}
//else
// _log.warn("You may ignore this", ex);
_failure = ex;
} finally {
_cache.release(ba);
if (_log.shouldLog(Log.INFO)) {
@ -408,7 +516,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
// DON'T close if we have a timeout job and we haven't received anything,
// or else the timeout job can't write the error message to the stream.
// close() above will close it after the timeout job is run.
if (!(onTimeout != null && (!_toI2P) && totalReceived <= 0))
if (!((onTimeout != null || _onFail != null) && (!_toI2P) && totalReceived <= 0))
out.close();
else if (_log.shouldLog(Log.INFO))
_log.info(direction + ": not closing so we can write the error message");
@ -423,5 +531,12 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
}
}
}
/**
* @since 0.9.14
*/
public Exception getFailure() {
return _failure;
}
}
}

View File

@ -314,7 +314,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
return readTimeout;
}
public boolean close(boolean forced) {
public synchronized boolean close(boolean forced) {
if (!open) return true;
if (task != null) {
task.close(forced);
@ -523,7 +523,9 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
socket.setReadTimeout(readTimeout);
Socket s = getSocket(socket.getPeerDestination().calculateHash(), socket.getLocalPort());
afterSocket = getTunnel().getContext().clock().now();
new I2PTunnelRunner(s, socket, slock, null, null);
Thread t = new I2PTunnelRunner(s, socket, slock, null, null,
null, (I2PTunnelRunner.FailCallback) null);
t.start();
long afterHandle = getTunnel().getContext().clock().now();
long timeToHandle = afterHandle - afterAccept;

View File

@ -75,7 +75,8 @@ public class I2PTunnelDCCClient extends I2PTunnelClientBase {
opts.setPort(_remotePort);
try {
i2ps = createI2PSocket(dest, opts);
new Runner(s, i2ps);
Thread t = new Runner(s, i2ps);
t.start();
} catch (Exception ex) {
_log.error("Could not make DCC connection to " + _dest + ':' + _remotePort, ex);
closeSocket(s);
@ -115,9 +116,11 @@ public class I2PTunnelDCCClient extends I2PTunnelClientBase {
*/
private class Runner extends I2PTunnelRunner {
/**
* Does NOT start itself. Caller must call start().
*/
public Runner(Socket s, I2PSocket i2ps) {
// super calls start()
super(s, i2ps, sockLock, null, mySockets);
super(s, i2ps, sockLock, null, null, mySockets, (FailCallback) null);
}
@Override

View File

@ -109,7 +109,9 @@ public class I2PTunnelDCCServer extends I2PTunnelServer {
try {
Socket s = new Socket(local.ia, local.port);
_sockList.add(socket);
new I2PTunnelRunner(s, socket, slock, null, _sockList);
Thread t = new I2PTunnelRunner(s, socket, slock, null, null, _sockList,
(I2PTunnelRunner.FailCallback) null);
t.start();
local.socket = socket;
local.expire = getTunnel().getContext().clock().now() + OUTBOUND_EXPIRE;
_active.put(Integer.valueOf(myPort), local);

View File

@ -54,7 +54,9 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s, getTunnel().getClientOptions());
Socket clientSock = serv.getClientSocket();
I2PSocket destSock = serv.getDestinationI2PSocket(this);
new I2PTunnelRunner(clientSock, destSock, sockLock, null, mySockets);
Thread t = new I2PTunnelRunner(clientSock, destSock, sockLock, null, null, mySockets,
(I2PTunnelRunner.FailCallback) null);
t.start();
} catch (SOCKSException e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error from SOCKS connection", e);

View File

@ -2,11 +2,13 @@ package net.i2p.client.streaming;
import java.net.SocketException;
import net.i2p.I2PAppContext;
import net.i2p.client.SendMessageStatusListener;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.util.Translate;
/**
* An I2P-specific IOException thrown from input and output streams.
* An I2P-specific IOException thrown from input and output streams,
* with a stored status code to be used for programmatic responses.
*
* @since 0.9.14
@ -15,6 +17,7 @@ public class I2PSocketException extends SocketException {
private final int _status;
private static final int CUSTOM = -1;
private static final String BUNDLE_NAME = "net.i2p.client.streaming.messages";
/**
* Use canned message for this status code
@ -52,55 +55,55 @@ public class I2PSocketException extends SocketException {
switch (_status) {
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
return "Message timeout";
return _x("Message timeout");
case MessageStatusMessage.STATUS_SEND_FAILURE_LOCAL:
return "Failed delivery to local destination";
return _x("Failed delivery to local destination");
case MessageStatusMessage.STATUS_SEND_FAILURE_ROUTER:
return "Local router failure";
return _x("Local router failure");
case MessageStatusMessage.STATUS_SEND_FAILURE_NETWORK:
return "Local network failure";
return _x("Local network failure");
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_SESSION:
return "Session closed";
return _x("Session closed");
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_MESSAGE:
return "Invalid message";
return _x("Invalid message");
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_OPTIONS:
return "Invalid message options";
return _x("Invalid message options");
case MessageStatusMessage.STATUS_SEND_FAILURE_OVERFLOW:
return "Buffer overflow";
return _x("Buffer overflow");
case MessageStatusMessage.STATUS_SEND_FAILURE_EXPIRED:
return "Message expired";
return _x("Message expired");
case MessageStatusMessage.STATUS_SEND_FAILURE_LOCAL_LEASESET:
return "Local lease set invalid";
return _x("Local lease set invalid");
case MessageStatusMessage.STATUS_SEND_FAILURE_NO_TUNNELS:
return "No local tunnels";
return _x("No local tunnels");
case MessageStatusMessage.STATUS_SEND_FAILURE_UNSUPPORTED_ENCRYPTION:
return "Unsupported encryption options";
return _x("Unsupported encryption options");
case MessageStatusMessage.STATUS_SEND_FAILURE_DESTINATION:
return "Invalid destination";
return _x("Invalid destination");
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_LEASESET:
return "Local router failure";
return _x("Local router failure");
case MessageStatusMessage.STATUS_SEND_FAILURE_EXPIRED_LEASESET:
return "Destination lease set expired";
return _x("Destination lease set expired");
case MessageStatusMessage.STATUS_SEND_FAILURE_NO_LEASESET:
return "Destination lease set not found";
return _x("Destination lease set not found");
case SendMessageStatusListener.STATUS_CANCELLED:
return "Local destination shutdown";
return _x("Local destination shutdown");
case CUSTOM:
return super.getMessage();
@ -109,4 +112,22 @@ public class I2PSocketException extends SocketException {
return "Failure code: " + _status;
}
}
/**
* Translated
*/
@Override
public String getLocalizedMessage() {
String s = getMessage();
if (s == null)
return null;
return Translate.getString(s, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
}
/**
* Tag for translation
*/
private static String _x(String s) {
return s;
}
}

View File

@ -0,0 +1,26 @@
HTTP/1.1 504 Gateway Timeout
Content-Type: text/html; charset=UTF-8
Cache-control: no-cache
Connection: close
Proxy-Connection: close
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>_("Warning: Eepsite Unreachable")</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/" 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/index">_("Addressbook")</a>
</div>
<div class="warning" id="warning">
<h3>_("Warning: Eepsite Unreachable")</h3>
<p>
_("The eepsite was not reachable, because its lease set was not found.")
_("The eepsite is probably down.")
_("You may want to {0}retry{1}.", "<a href=\"javascript:window.location.reload()\">", "</a>")</p>
<hr>
<p><b>_("Could not find the following destination:")</b>
</p>

View File

@ -0,0 +1,26 @@
HTTP/1.1 504 Gateway Timeout
Content-Type: text/html; charset=UTF-8
Cache-control: no-cache
Connection: close
Proxy-Connection: close
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>_("Warning: Outproxy Not Found")</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/" 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/index">_("Addressbook")</a>
</div>
<div class="warning" id="warning">
<h3>_("Warning: Outproxy Not Found")</h3>
<p>
_("The HTTP outproxy was not reachable, because its lease set was not found.")
_("The outproxy is probably down.")
_("You may want to {0}retry{1} as this will randomly reselect an outproxy from the pool you have defined {2}here{3} (if you have more than one configured).", "<a href=\"javascript:parent.window.location.reload()\">", "</a>", "<a href=\"http://127.0.0.1:7657/i2ptunnel/index.jsp\">", "</a>")
_("If you continue to have trouble you may want to edit your outproxy list {0}here{1}.", "<a href=\"http://127.0.0.1:7657/i2ptunnel/edit.jsp?tunnel=0\">", "</a>")
</p>
<hr><p><b>_("Could not find the following destination:")</b></p>