forked from I2P_Developers/i2p.i2p
* 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:
@ -122,7 +122,9 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
|||||||
int port = addr.getPort();
|
int port = addr.getPort();
|
||||||
i2ps = createI2PSocket(clientDest, port);
|
i2ps = createI2PSocket(clientDest, port);
|
||||||
i2ps.setReadTimeout(readTimeout);
|
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) {
|
} catch (Exception ex) {
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Error connecting", ex);
|
_log.info("Error connecting", ex);
|
||||||
|
@ -59,18 +59,6 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
|
|||||||
|
|
||||||
public static final String AUTH_REALM = "I2P SSL Proxy";
|
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 =
|
private final static byte[] ERR_BAD_PROTOCOL =
|
||||||
("HTTP/1.1 405 Bad Method\r\n"+
|
("HTTP/1.1 405 Bad Method\r\n"+
|
||||||
"Content-Type: text/html; charset=iso-8859-1\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) {
|
if (clientDest == null) {
|
||||||
byte[] header;
|
byte[] header;
|
||||||
if (usingWWWProxy)
|
if (usingWWWProxy)
|
||||||
header = getErrorPage("dnfp-header.ht", ERR_DESTINATION_UNKNOWN);
|
header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
|
||||||
else
|
else
|
||||||
header = getErrorPage("dnfh-header.ht", ERR_DESTINATION_UNKNOWN);
|
header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN);
|
||||||
writeErrorMessage(header, out, targetRequest, usingWWWProxy, destination);
|
writeErrorMessage(header, out, targetRequest, usingWWWProxy, destination);
|
||||||
s.close();
|
s.close();
|
||||||
return;
|
return;
|
||||||
@ -308,89 +296,29 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
|
|||||||
data = newRequest.toString().getBytes("ISO-8859-1");
|
data = newRequest.toString().getBytes("ISO-8859-1");
|
||||||
else
|
else
|
||||||
response = SUCCESS_RESPONSE;
|
response = SUCCESS_RESPONSE;
|
||||||
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
|
OnTimeout onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||||
// starts itself
|
Thread t = new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
|
||||||
new I2PTunnelRunner(s, i2ps, sockLock, data, response, mySockets, onTimeout);
|
t.start();
|
||||||
} catch (SocketException ex) {
|
|
||||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
|
||||||
handleConnectClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
|
||||||
closeSocket(s);
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
_log.info(getPrefix(requestId) + "Error trying to connect", 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);
|
closeSocket(s);
|
||||||
} catch (I2PException ex) {
|
} catch (I2PException ex) {
|
||||||
_log.info("getPrefix(requestId) + Error trying to connect", 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);
|
closeSocket(s);
|
||||||
} catch (OutOfMemoryError oom) {
|
} catch (OutOfMemoryError oom) {
|
||||||
IOException ex = new IOException("OOM");
|
IOException ex = new IOException("OOM");
|
||||||
_log.info("getPrefix(requestId) + Error trying to connect", 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);
|
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 {
|
private static void writeErrorMessage(byte[] errMessage, OutputStream out) throws IOException {
|
||||||
if (out == null)
|
if (out == null)
|
||||||
return;
|
return;
|
||||||
out.write(errMessage);
|
out.write(errMessage);
|
||||||
out.write("\n</body></html>\n".getBytes());
|
writeFooter(out);
|
||||||
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) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import java.net.Socket;
|
|||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
@ -35,7 +34,6 @@ import net.i2p.i2ptunnel.localServer.LocalHTTPServer;
|
|||||||
import net.i2p.util.EventDispatcher;
|
import net.i2p.util.EventDispatcher;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
import net.i2p.util.PortMapper;
|
import net.i2p.util.PortMapper;
|
||||||
import net.i2p.util.Translate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Act as a mini HTTP proxy, handling various different types of requests,
|
* 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" +
|
"\r\n" +
|
||||||
"<html><body><H1>I2P ERROR: REQUEST DENIED</H1>" +
|
"<html><body><H1>I2P ERROR: REQUEST DENIED</H1>" +
|
||||||
"You attempted to connect to a non-I2P website or location.<BR>").getBytes();
|
"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 =
|
private final static byte[] ERR_TIMEOUT =
|
||||||
("HTTP/1.1 504 Gateway Timeout\r\n"+
|
("HTTP/1.1 504 Gateway Timeout\r\n"+
|
||||||
@ -642,7 +630,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
if(alias.equals("i2p")) {
|
if(alias.equals("i2p")) {
|
||||||
// bad ahelperKey
|
// bad ahelperKey
|
||||||
byte[] header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
|
byte[] header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
|
||||||
writeErrorMessage(header, out, targetRequest, false, destination, null);
|
writeErrorMessage(header, out, targetRequest, false, destination);
|
||||||
} else {
|
} else {
|
||||||
String trustedURL = requestURI.toASCIIString();
|
String trustedURL = requestURI.toASCIIString();
|
||||||
URI conflictURI;
|
URI conflictURI;
|
||||||
@ -951,7 +939,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
// no destination, going to outproxy plugin
|
// no destination, going to outproxy plugin
|
||||||
if (usingInternalOutproxy) {
|
if (usingInternalOutproxy) {
|
||||||
Socket outSocket = outproxy.connect(host, remotePort);
|
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[] data;
|
||||||
byte[] response;
|
byte[] response;
|
||||||
if (method.toUpperCase(Locale.US).equals("CONNECT")) {
|
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");
|
data = newRequest.toString().getBytes("ISO-8859-1");
|
||||||
response = null;
|
response = null;
|
||||||
}
|
}
|
||||||
new I2PTunnelOutproxyRunner(s, outSocket, sockLock, data, response, onTimeout);
|
Thread t = new I2PTunnelOutproxyRunner(s, outSocket, sockLock, data, response, onTimeout);
|
||||||
|
t.start();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -980,7 +969,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
_log.warn(getPrefix(requestId) + "Could not find destination for " + addressHelper);
|
_log.warn(getPrefix(requestId) + "Could not find destination for " + addressHelper);
|
||||||
}
|
}
|
||||||
byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
|
byte[] header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
|
||||||
writeErrorMessage(header, out, targetRequest, false, destination, null);
|
writeErrorMessage(header, out, targetRequest, false, destination);
|
||||||
s.close();
|
s.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1018,7 +1007,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
} else if(ahelperPresent) {
|
} else if(ahelperPresent) {
|
||||||
header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
|
header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
|
||||||
} else if(destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
|
} 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 {
|
} else {
|
||||||
header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN);
|
header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN);
|
||||||
jumpServers = getTunnel().getClientOptions().getProperty(PROP_JUMP_SERVERS);
|
jumpServers = getTunnel().getClientOptions().getProperty(PROP_JUMP_SERVERS);
|
||||||
@ -1067,7 +1056,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
if (remotePort > 0)
|
if (remotePort > 0)
|
||||||
sktOpts.setPort(remotePort);
|
sktOpts.setPort(remotePort);
|
||||||
I2PSocket i2ps = createI2PSocket(clientDest, sktOpts);
|
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")) {
|
if (method.toUpperCase(Locale.US).equals("CONNECT")) {
|
||||||
byte[] data;
|
byte[] data;
|
||||||
byte[] response;
|
byte[] response;
|
||||||
@ -1078,30 +1067,32 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
data = null;
|
data = null;
|
||||||
response = I2PTunnelConnectClient.SUCCESS_RESPONSE;
|
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 {
|
} else {
|
||||||
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
|
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) {
|
} catch(IOException ex) {
|
||||||
if(_log.shouldLog(Log.INFO)) {
|
if(_log.shouldLog(Log.INFO)) {
|
||||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||||
}
|
}
|
||||||
//l.log("Error connecting: " + ex.getMessage());
|
//l.log("Error connecting: " + ex.getMessage());
|
||||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||||
closeSocket(s);
|
closeSocket(s);
|
||||||
} catch(I2PException ex) {
|
} catch(I2PException ex) {
|
||||||
if(_log.shouldLog(Log.INFO)) {
|
if(_log.shouldLog(Log.INFO)) {
|
||||||
_log.info("getPrefix(requestId) + Error trying to connect", ex);
|
_log.info("getPrefix(requestId) + Error trying to connect", ex);
|
||||||
}
|
}
|
||||||
//l.log("Error connecting: " + ex.getMessage());
|
//l.log("Error connecting: " + ex.getMessage());
|
||||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||||
closeSocket(s);
|
closeSocket(s);
|
||||||
} catch(OutOfMemoryError oom) {
|
} catch(OutOfMemoryError oom) {
|
||||||
IOException ex = new IOException("OOM");
|
IOException ex = new IOException("OOM");
|
||||||
_log.error("getPrefix(requestId) + Error trying to connect", oom);
|
_log.error("getPrefix(requestId) + Error trying to connect", oom);
|
||||||
//l.log("Error connecting: " + ex.getMessage());
|
//l.log("Error connecting: " + ex.getMessage());
|
||||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
handleClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||||
closeSocket(s);
|
closeSocket(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1229,43 +1220,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
return Base32.encode(_dest.calculateHash().getData()) + ".b32.i2p";
|
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 =
|
public static final String DEFAULT_JUMP_SERVERS =
|
||||||
"http://i2host.i2p/cgi-bin/i2hostjump?," +
|
"http://i2host.i2p/cgi-bin/i2hostjump?," +
|
||||||
"http://stats.i2p/cgi-bin/jump.cgi?a=," +
|
"http://stats.i2p/cgi-bin/jump.cgi?a=," +
|
||||||
@ -1273,101 +1227,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
"http://i2pjump.i2p/jump/";
|
"http://i2pjump.i2p/jump/";
|
||||||
//"http://i2jump.i2p/";
|
//"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("&", "&");
|
|
||||||
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 */
|
/** @param host ignored */
|
||||||
private static boolean isSupportedAddress(String host, String protocol) {
|
private static boolean isSupportedAddress(String host, String protocol) {
|
||||||
if((host == null) || (protocol == null)) {
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,18 @@
|
|||||||
*/
|
*/
|
||||||
package net.i2p.i2ptunnel;
|
package net.i2p.i2ptunnel;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.io.File;
|
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -23,14 +27,18 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.client.streaming.I2PSocketException;
|
||||||
import net.i2p.client.streaming.I2PSocketManager;
|
import net.i2p.client.streaming.I2PSocketManager;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
import net.i2p.data.DataHelper;
|
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.EepGet;
|
||||||
import net.i2p.util.EventDispatcher;
|
import net.i2p.util.EventDispatcher;
|
||||||
import net.i2p.util.InternalSocket;
|
import net.i2p.util.InternalSocket;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
import net.i2p.util.PasswordManager;
|
import net.i2p.util.PasswordManager;
|
||||||
|
import net.i2p.util.Translate;
|
||||||
import net.i2p.util.TranslateReader;
|
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")
|
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel")
|
||||||
.getBytes();
|
.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 byte[] _proxyNonce;
|
||||||
private final ConcurrentHashMap<String, NonceInfo> _nonces;
|
private final ConcurrentHashMap<String, NonceInfo> _nonces;
|
||||||
private final AtomicInteger _nonceCleanCounter = new AtomicInteger();
|
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() {
|
protected String selectProxy() {
|
||||||
synchronized (_proxyList) {
|
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";
|
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
|
// 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("&", "&");
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,11 +21,16 @@ import net.i2p.util.Log;
|
|||||||
* and the server should echo it. However, both broken and malicious
|
* and the server should echo it. However, both broken and malicious
|
||||||
* servers could ignore that, potentially confusing the user.
|
* servers could ignore that, potentially confusing the user.
|
||||||
*
|
*
|
||||||
|
* Warning - not maintained as a stable API for external use.
|
||||||
*/
|
*/
|
||||||
public class I2PTunnelHTTPClientRunner extends I2PTunnelRunner {
|
public class I2PTunnelHTTPClientRunner extends I2PTunnelRunner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does NOT start itself. Caller must call start().
|
||||||
|
*/
|
||||||
public I2PTunnelHTTPClientRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
|
public I2PTunnelHTTPClientRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
|
||||||
List<I2PSocket> sockList, Runnable onTimeout) {
|
List<I2PSocket> sockList, FailCallback onFail) {
|
||||||
super(s, i2ps, slock, initialI2PData, sockList, onTimeout);
|
super(s, i2ps, slock, initialI2PData, null, sockList, onFail);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -307,7 +307,9 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
|||||||
Thread.currentThread().getName()+".hc");
|
Thread.currentThread().getName()+".hc");
|
||||||
req.start();
|
req.start();
|
||||||
} else {
|
} 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();
|
long afterHandle = getTunnel().getContext().clock().now();
|
||||||
|
@ -138,7 +138,9 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
|
|||||||
modifiedRegistration = buf.toString();
|
modifiedRegistration = buf.toString();
|
||||||
}
|
}
|
||||||
Socket s = getSocket(socket.getPeerDestination().calculateHash(), socket.getLocalPort());
|
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) {
|
} catch (SocketException ex) {
|
||||||
try {
|
try {
|
||||||
// Send a response so the user doesn't just see a disconnect
|
// Send a response so the user doesn't just see a disconnect
|
||||||
|
@ -26,6 +26,8 @@ import net.i2p.util.Log;
|
|||||||
/**
|
/**
|
||||||
* Like I2PTunnelRunner but socket-to-socket
|
* Like I2PTunnelRunner but socket-to-socket
|
||||||
*
|
*
|
||||||
|
* Warning - not maintained as a stable API for external use.
|
||||||
|
*
|
||||||
* @since 0.9.11
|
* @since 0.9.11
|
||||||
*/
|
*/
|
||||||
public class I2PTunnelOutproxyRunner extends I2PAppThread {
|
public class I2PTunnelOutproxyRunner extends I2PAppThread {
|
||||||
@ -54,22 +56,22 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
|
|||||||
/** when the runner started up */
|
/** when the runner started up */
|
||||||
private final long startedOn;
|
private final long startedOn;
|
||||||
/** if we die before receiving any data, run this job */
|
/** if we die before receiving any data, run this job */
|
||||||
private final Runnable onTimeout;
|
private final I2PTunnelRunner.FailCallback onTimeout;
|
||||||
private long totalSent;
|
private long totalSent;
|
||||||
private long totalReceived;
|
private long totalReceived;
|
||||||
|
|
||||||
private static final AtomicLong __forwarderId = new AtomicLong();
|
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 slock the socket lock, non-null
|
||||||
* @param initialI2PData may be 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.
|
it will be run before closing s.
|
||||||
*/
|
*/
|
||||||
public I2PTunnelOutproxyRunner(Socket s, Socket i2ps, Object slock, byte[] initialI2PData,
|
public I2PTunnelOutproxyRunner(Socket s, Socket i2ps, Object slock, byte[] initialI2PData,
|
||||||
byte[] initialSocketData, Runnable onTimeout) {
|
byte[] initialSocketData, I2PTunnelRunner.FailCallback onTimeout) {
|
||||||
this.s = s;
|
this.s = s;
|
||||||
this.i2ps = i2ps;
|
this.i2ps = i2ps;
|
||||||
this.slock = slock;
|
this.slock = slock;
|
||||||
@ -83,7 +85,6 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
|
|||||||
_log.info("OutproxyRunner started");
|
_log.info("OutproxyRunner started");
|
||||||
_runnerId = __runnerId.incrementAndGet();
|
_runnerId = __runnerId.incrementAndGet();
|
||||||
setName("OutproxyRunner " + _runnerId);
|
setName("OutproxyRunner " + _runnerId);
|
||||||
start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,6 +145,9 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
|
|||||||
in = new BufferedInputStream(in, 2*NETWORK_BUFFER_SIZE);
|
in = new BufferedInputStream(in, 2*NETWORK_BUFFER_SIZE);
|
||||||
Thread t1 = new StreamForwarder(in, i2pout, true);
|
Thread t1 = new StreamForwarder(in, i2pout, true);
|
||||||
Thread t2 = new StreamForwarder(i2pin, out, false);
|
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) {
|
synchronized (finishLock) {
|
||||||
while (!finished) {
|
while (!finished) {
|
||||||
finishLock.wait();
|
finishLock.wait();
|
||||||
@ -159,7 +163,7 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
|
|||||||
+ " totalSent = " + totalSent + " job = " + onTimeout);
|
+ " totalSent = " + totalSent + " job = " + onTimeout);
|
||||||
// Run even if totalSent > 0, as that's probably POST data.
|
// Run even if totalSent > 0, as that's probably POST data.
|
||||||
if (totalReceived <= 0)
|
if (totalReceived <= 0)
|
||||||
onTimeout.run();
|
onTimeout.onFail(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now one connection is dead - kill the other as well, after making sure we flush
|
// 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 class StreamForwarder extends I2PAppThread {
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
@ -250,6 +257,9 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
|
|||||||
private final boolean _toI2P;
|
private final boolean _toI2P;
|
||||||
private final ByteCache _cache;
|
private final ByteCache _cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does not start itself. Caller must start()
|
||||||
|
*/
|
||||||
private StreamForwarder(InputStream in, OutputStream out, boolean toI2P) {
|
private StreamForwarder(InputStream in, OutputStream out, boolean toI2P) {
|
||||||
this.in = in;
|
this.in = in;
|
||||||
this.out = out;
|
this.out = out;
|
||||||
@ -257,7 +267,6 @@ public class I2PTunnelOutproxyRunner extends I2PAppThread {
|
|||||||
direction = (toI2P ? "toOutproxy" : "fromOutproxy");
|
direction = (toI2P ? "toOutproxy" : "fromOutproxy");
|
||||||
_cache = ByteCache.getInstance(32, NETWORK_BUFFER_SIZE);
|
_cache = ByteCache.getInstance(32, NETWORK_BUFFER_SIZE);
|
||||||
setName("OutproxyForwarder " + _runnerId + '.' + __forwarderId.incrementAndGet());
|
setName("OutproxyForwarder " + _runnerId + '.' + __forwarderId.incrementAndGet());
|
||||||
start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,6 +25,11 @@ import net.i2p.util.I2PAppThread;
|
|||||||
import net.i2p.util.InternalSocket;
|
import net.i2p.util.InternalSocket;
|
||||||
import net.i2p.util.Log;
|
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 {
|
public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErrorListener {
|
||||||
protected final Log _log;
|
protected final Log _log;
|
||||||
|
|
||||||
@ -43,7 +48,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
|||||||
private final Socket s;
|
private final Socket s;
|
||||||
private final I2PSocket i2ps;
|
private final I2PSocket i2ps;
|
||||||
private final Object slock, finishLock = new Object();
|
private final Object slock, finishLock = new Object();
|
||||||
volatile boolean finished = false;
|
private volatile boolean finished;
|
||||||
private final byte[] initialI2PData;
|
private final byte[] initialI2PData;
|
||||||
private final byte[] initialSocketData;
|
private final byte[] initialSocketData;
|
||||||
/** when the last data was sent/received (or -1 if never) */
|
/** 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;
|
private final List<I2PSocket> sockList;
|
||||||
/** if we die before receiving any data, run this job */
|
/** if we die before receiving any data, run this job */
|
||||||
private final Runnable onTimeout;
|
private final Runnable onTimeout;
|
||||||
|
private final FailCallback _onFail;
|
||||||
private long totalSent;
|
private long totalSent;
|
||||||
private long totalReceived;
|
private long totalReceived;
|
||||||
|
|
||||||
private static final AtomicLong __forwarderId = new AtomicLong();
|
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,
|
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
|
||||||
List<I2PSocket> sockList) {
|
List<I2PSocket> sockList) {
|
||||||
this(s, i2ps, slock, initialI2PData, null, sockList, null);
|
this(s, i2ps, slock, initialI2PData, null, sockList, null, null, true);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,11 +97,78 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
|||||||
* @param initialSocketData 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.
|
* @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.
|
* Will synchronize on slock when removing.
|
||||||
* @param onTimeout May be null. If non-null and no data (except initial data) was sent or received,
|
* @deprecated use FailCallback constructor
|
||||||
it will be run before closing s.
|
*/
|
||||||
|
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,
|
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData,
|
||||||
byte[] initialSocketData, List<I2PSocket> sockList, Runnable onTimeout) {
|
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.sockList = sockList;
|
||||||
this.s = s;
|
this.s = s;
|
||||||
this.i2ps = i2ps;
|
this.i2ps = i2ps;
|
||||||
@ -93,6 +176,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
|||||||
this.initialI2PData = initialI2PData;
|
this.initialI2PData = initialI2PData;
|
||||||
this.initialSocketData = initialSocketData;
|
this.initialSocketData = initialSocketData;
|
||||||
this.onTimeout = onTimeout;
|
this.onTimeout = onTimeout;
|
||||||
|
_onFail = onFail;
|
||||||
lastActivityOn = -1;
|
lastActivityOn = -1;
|
||||||
startedOn = Clock.getInstance().now();
|
startedOn = Clock.getInstance().now();
|
||||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
|
_log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
|
||||||
@ -100,7 +184,8 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
|||||||
_log.info("I2PTunnelRunner started");
|
_log.info("I2PTunnelRunner started");
|
||||||
_runnerId = __runnerId.incrementAndGet();
|
_runnerId = __runnerId.incrementAndGet();
|
||||||
setName("I2PTunnelRunner " + _runnerId);
|
setName("I2PTunnelRunner " + _runnerId);
|
||||||
start();
|
if (shouldStart)
|
||||||
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,8 +266,11 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
|||||||
+ " written to the socket, starting forwarders");
|
+ " written to the socket, starting forwarders");
|
||||||
if (!(s instanceof InternalSocket))
|
if (!(s instanceof InternalSocket))
|
||||||
in = new BufferedInputStream(in, 2*NETWORK_BUFFER_SIZE);
|
in = new BufferedInputStream(in, 2*NETWORK_BUFFER_SIZE);
|
||||||
Thread t1 = new StreamForwarder(in, i2pout, true);
|
StreamForwarder toI2P = new StreamForwarder(in, i2pout, true);
|
||||||
Thread t2 = new StreamForwarder(i2pin, out, false);
|
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) {
|
synchronized (finishLock) {
|
||||||
while (!finished) {
|
while (!finished) {
|
||||||
finishLock.wait();
|
finishLock.wait();
|
||||||
@ -192,7 +280,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
|||||||
_log.debug("At least one forwarder completed, closing and joining");
|
_log.debug("At least one forwarder completed, closing and joining");
|
||||||
|
|
||||||
// this task is useful for the httpclient
|
// this task is useful for the httpclient
|
||||||
if (onTimeout != null) {
|
if (onTimeout != null || _onFail != null) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("runner has a timeout job, totalReceived = " + totalReceived
|
_log.debug("runner has a timeout job, totalReceived = " + totalReceived
|
||||||
+ " totalSent = " + totalSent + " job = " + onTimeout);
|
+ " 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
|
// 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.
|
// responsibility to know that and decide whether or not to write to the socket.
|
||||||
// HTTPClient never sets initialSocketData.
|
// HTTPClient never sets initialSocketData.
|
||||||
if (totalReceived <= 0)
|
if (totalReceived <= 0) {
|
||||||
onTimeout.run();
|
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
|
// 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) {
|
} catch (InterruptedException ex) {
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
_log.error("Interrupted", ex);
|
_log.error("Interrupted", ex);
|
||||||
@ -291,6 +387,9 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
|||||||
t2.join(30*1000);
|
t2.join(30*1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deprecated, unimplemented in streaming, never called.
|
||||||
|
*/
|
||||||
public void errorOccurred() {
|
public void errorOccurred() {
|
||||||
synchronized (finishLock) {
|
synchronized (finishLock) {
|
||||||
finished = true;
|
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 class StreamForwarder extends I2PAppThread {
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
@ -313,15 +415,18 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
|||||||
private final String direction;
|
private final String direction;
|
||||||
private final boolean _toI2P;
|
private final boolean _toI2P;
|
||||||
private final ByteCache _cache;
|
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.in = in;
|
||||||
this.out = out;
|
this.out = out;
|
||||||
_toI2P = toI2P;
|
_toI2P = toI2P;
|
||||||
direction = (toI2P ? "toI2P" : "fromI2P");
|
direction = (toI2P ? "toI2P" : "fromI2P");
|
||||||
_cache = ByteCache.getInstance(32, NETWORK_BUFFER_SIZE);
|
_cache = ByteCache.getInstance(32, NETWORK_BUFFER_SIZE);
|
||||||
setName("StreamForwarder " + _runnerId + '.' + __forwarderId.incrementAndGet());
|
setName("StreamForwarder " + _runnerId + '.' + __forwarderId.incrementAndGet());
|
||||||
start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -377,10 +482,12 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
|||||||
ex);
|
ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_failure = ex;
|
||||||
} catch (InterruptedIOException ex) {
|
} catch (InterruptedIOException ex) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn(direction + ": Closing connection due to timeout (error: \""
|
_log.warn(direction + ": Closing connection due to timeout (error: \""
|
||||||
+ ex.getMessage() + "\")");
|
+ ex.getMessage() + "\")");
|
||||||
|
_failure = ex;
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
if (!finished) {
|
if (!finished) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
@ -388,6 +495,7 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
|||||||
}
|
}
|
||||||
//else
|
//else
|
||||||
// _log.warn("You may ignore this", ex);
|
// _log.warn("You may ignore this", ex);
|
||||||
|
_failure = ex;
|
||||||
} finally {
|
} finally {
|
||||||
_cache.release(ba);
|
_cache.release(ba);
|
||||||
if (_log.shouldLog(Log.INFO)) {
|
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,
|
// 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.
|
// 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.
|
// 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();
|
out.close();
|
||||||
else if (_log.shouldLog(Log.INFO))
|
else if (_log.shouldLog(Log.INFO))
|
||||||
_log.info(direction + ": not closing so we can write the error message");
|
_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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,7 +314,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
|||||||
return readTimeout;
|
return readTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean close(boolean forced) {
|
public synchronized boolean close(boolean forced) {
|
||||||
if (!open) return true;
|
if (!open) return true;
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
task.close(forced);
|
task.close(forced);
|
||||||
@ -523,7 +523,9 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
|||||||
socket.setReadTimeout(readTimeout);
|
socket.setReadTimeout(readTimeout);
|
||||||
Socket s = getSocket(socket.getPeerDestination().calculateHash(), socket.getLocalPort());
|
Socket s = getSocket(socket.getPeerDestination().calculateHash(), socket.getLocalPort());
|
||||||
afterSocket = getTunnel().getContext().clock().now();
|
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 afterHandle = getTunnel().getContext().clock().now();
|
||||||
long timeToHandle = afterHandle - afterAccept;
|
long timeToHandle = afterHandle - afterAccept;
|
||||||
|
@ -75,7 +75,8 @@ public class I2PTunnelDCCClient extends I2PTunnelClientBase {
|
|||||||
opts.setPort(_remotePort);
|
opts.setPort(_remotePort);
|
||||||
try {
|
try {
|
||||||
i2ps = createI2PSocket(dest, opts);
|
i2ps = createI2PSocket(dest, opts);
|
||||||
new Runner(s, i2ps);
|
Thread t = new Runner(s, i2ps);
|
||||||
|
t.start();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
_log.error("Could not make DCC connection to " + _dest + ':' + _remotePort, ex);
|
_log.error("Could not make DCC connection to " + _dest + ':' + _remotePort, ex);
|
||||||
closeSocket(s);
|
closeSocket(s);
|
||||||
@ -115,9 +116,11 @@ public class I2PTunnelDCCClient extends I2PTunnelClientBase {
|
|||||||
*/
|
*/
|
||||||
private class Runner extends I2PTunnelRunner {
|
private class Runner extends I2PTunnelRunner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does NOT start itself. Caller must call start().
|
||||||
|
*/
|
||||||
public Runner(Socket s, I2PSocket i2ps) {
|
public Runner(Socket s, I2PSocket i2ps) {
|
||||||
// super calls start()
|
super(s, i2ps, sockLock, null, null, mySockets, (FailCallback) null);
|
||||||
super(s, i2ps, sockLock, null, mySockets);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -109,7 +109,9 @@ public class I2PTunnelDCCServer extends I2PTunnelServer {
|
|||||||
try {
|
try {
|
||||||
Socket s = new Socket(local.ia, local.port);
|
Socket s = new Socket(local.ia, local.port);
|
||||||
_sockList.add(socket);
|
_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.socket = socket;
|
||||||
local.expire = getTunnel().getContext().clock().now() + OUTBOUND_EXPIRE;
|
local.expire = getTunnel().getContext().clock().now() + OUTBOUND_EXPIRE;
|
||||||
_active.put(Integer.valueOf(myPort), local);
|
_active.put(Integer.valueOf(myPort), local);
|
||||||
|
@ -54,7 +54,9 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
|
|||||||
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s, getTunnel().getClientOptions());
|
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s, getTunnel().getClientOptions());
|
||||||
Socket clientSock = serv.getClientSocket();
|
Socket clientSock = serv.getClientSocket();
|
||||||
I2PSocket destSock = serv.getDestinationI2PSocket(this);
|
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) {
|
} catch (SOCKSException e) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Error from SOCKS connection", e);
|
_log.warn("Error from SOCKS connection", e);
|
||||||
|
@ -2,11 +2,13 @@ package net.i2p.client.streaming;
|
|||||||
|
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.client.SendMessageStatusListener;
|
import net.i2p.client.SendMessageStatusListener;
|
||||||
import net.i2p.data.i2cp.MessageStatusMessage;
|
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.
|
* with a stored status code to be used for programmatic responses.
|
||||||
*
|
*
|
||||||
* @since 0.9.14
|
* @since 0.9.14
|
||||||
@ -15,6 +17,7 @@ public class I2PSocketException extends SocketException {
|
|||||||
|
|
||||||
private final int _status;
|
private final int _status;
|
||||||
private static final int CUSTOM = -1;
|
private static final int CUSTOM = -1;
|
||||||
|
private static final String BUNDLE_NAME = "net.i2p.client.streaming.messages";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use canned message for this status code
|
* Use canned message for this status code
|
||||||
@ -52,55 +55,55 @@ public class I2PSocketException extends SocketException {
|
|||||||
switch (_status) {
|
switch (_status) {
|
||||||
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
|
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
|
||||||
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
|
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
|
||||||
return "Message timeout";
|
return _x("Message timeout");
|
||||||
|
|
||||||
case MessageStatusMessage.STATUS_SEND_FAILURE_LOCAL:
|
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:
|
case MessageStatusMessage.STATUS_SEND_FAILURE_ROUTER:
|
||||||
return "Local router failure";
|
return _x("Local router failure");
|
||||||
|
|
||||||
case MessageStatusMessage.STATUS_SEND_FAILURE_NETWORK:
|
case MessageStatusMessage.STATUS_SEND_FAILURE_NETWORK:
|
||||||
return "Local network failure";
|
return _x("Local network failure");
|
||||||
|
|
||||||
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_SESSION:
|
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_SESSION:
|
||||||
return "Session closed";
|
return _x("Session closed");
|
||||||
|
|
||||||
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_MESSAGE:
|
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_MESSAGE:
|
||||||
return "Invalid message";
|
return _x("Invalid message");
|
||||||
|
|
||||||
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_OPTIONS:
|
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_OPTIONS:
|
||||||
return "Invalid message options";
|
return _x("Invalid message options");
|
||||||
|
|
||||||
case MessageStatusMessage.STATUS_SEND_FAILURE_OVERFLOW:
|
case MessageStatusMessage.STATUS_SEND_FAILURE_OVERFLOW:
|
||||||
return "Buffer overflow";
|
return _x("Buffer overflow");
|
||||||
|
|
||||||
case MessageStatusMessage.STATUS_SEND_FAILURE_EXPIRED:
|
case MessageStatusMessage.STATUS_SEND_FAILURE_EXPIRED:
|
||||||
return "Message expired";
|
return _x("Message expired");
|
||||||
|
|
||||||
case MessageStatusMessage.STATUS_SEND_FAILURE_LOCAL_LEASESET:
|
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:
|
case MessageStatusMessage.STATUS_SEND_FAILURE_NO_TUNNELS:
|
||||||
return "No local tunnels";
|
return _x("No local tunnels");
|
||||||
|
|
||||||
case MessageStatusMessage.STATUS_SEND_FAILURE_UNSUPPORTED_ENCRYPTION:
|
case MessageStatusMessage.STATUS_SEND_FAILURE_UNSUPPORTED_ENCRYPTION:
|
||||||
return "Unsupported encryption options";
|
return _x("Unsupported encryption options");
|
||||||
|
|
||||||
case MessageStatusMessage.STATUS_SEND_FAILURE_DESTINATION:
|
case MessageStatusMessage.STATUS_SEND_FAILURE_DESTINATION:
|
||||||
return "Invalid destination";
|
return _x("Invalid destination");
|
||||||
|
|
||||||
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_LEASESET:
|
case MessageStatusMessage.STATUS_SEND_FAILURE_BAD_LEASESET:
|
||||||
return "Local router failure";
|
return _x("Local router failure");
|
||||||
|
|
||||||
case MessageStatusMessage.STATUS_SEND_FAILURE_EXPIRED_LEASESET:
|
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:
|
case MessageStatusMessage.STATUS_SEND_FAILURE_NO_LEASESET:
|
||||||
return "Destination lease set not found";
|
return _x("Destination lease set not found");
|
||||||
|
|
||||||
case SendMessageStatusListener.STATUS_CANCELLED:
|
case SendMessageStatusListener.STATUS_CANCELLED:
|
||||||
return "Local destination shutdown";
|
return _x("Local destination shutdown");
|
||||||
|
|
||||||
case CUSTOM:
|
case CUSTOM:
|
||||||
return super.getMessage();
|
return super.getMessage();
|
||||||
@ -109,4 +112,22 @@ public class I2PSocketException extends SocketException {
|
|||||||
return "Failure code: " + _status;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
26
installer/resources/proxy/nols-header.ht
Normal file
26
installer/resources/proxy/nols-header.ht
Normal 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>
|
26
installer/resources/proxy/nolsp-header.ht
Normal file
26
installer/resources/proxy/nolsp-header.ht
Normal 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>
|
Reference in New Issue
Block a user