forked from I2P_Developers/i2p.i2p
* I2PTunnel:
- Display destination even when stopped - Enable key generation, dest modification, and hashcash estimation in the GUI - Add new CONNECT client
This commit is contained in:
@@ -244,6 +244,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
runIrcClient(args, l);
|
||||
} else if ("sockstunnel".equals(cmdname)) {
|
||||
runSOCKSTunnel(args, l);
|
||||
} else if ("connectclient".equals(cmdname)) {
|
||||
runConnectClient(args, l);
|
||||
} else if ("config".equals(cmdname)) {
|
||||
runConfig(args, l);
|
||||
} else if ("listen_on".equals(cmdname)) {
|
||||
@@ -296,6 +298,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile> [<sharedClient>]");
|
||||
l.log("ircclient <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile> [<sharedClient>]");
|
||||
l.log("httpclient <port> [<sharedClient>] [<proxy>]");
|
||||
l.log("connectclient <port> [<sharedClient>] [<proxy>]");
|
||||
l.log("lookup <name>");
|
||||
l.log("quit");
|
||||
l.log("close [forced] <jobnumber>|all");
|
||||
@@ -555,7 +558,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
return;
|
||||
}
|
||||
|
||||
String proxy = "squid.i2p";
|
||||
String proxy = "";
|
||||
boolean isShared = true;
|
||||
if (args.length > 1) {
|
||||
if ("true".equalsIgnoreCase(args[1].trim())) {
|
||||
@@ -595,11 +598,66 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log(" <sharedClient> (optional) indicates if this client shares tunnels with other clients (true of false)");
|
||||
l.log(" <proxy> (optional) indicates a proxy server to be used");
|
||||
l.log(" when trying to access an address out of the .i2p domain");
|
||||
l.log(" (the default proxy is squid.i2p).");
|
||||
notifyEvent("httpclientTaskId", Integer.valueOf(-1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a CONNECT client on the given port number
|
||||
*
|
||||
* @param args {portNumber[, sharedClient][, proxy to be used for the WWW]}
|
||||
* @param l logger to receive events and output
|
||||
*/
|
||||
public void runConnectClient(String args[], Logging l) {
|
||||
if (args.length >= 1 && args.length <= 3) {
|
||||
int port = -1;
|
||||
try {
|
||||
port = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
return;
|
||||
}
|
||||
|
||||
String proxy = "";
|
||||
boolean isShared = true;
|
||||
if (args.length > 1) {
|
||||
if ("true".equalsIgnoreCase(args[1].trim())) {
|
||||
isShared = true;
|
||||
if (args.length == 3)
|
||||
proxy = args[2];
|
||||
} else if ("false".equalsIgnoreCase(args[1].trim())) {
|
||||
_log.warn("args[1] == [" + args[1] + "] and rejected explicitly");
|
||||
isShared = false;
|
||||
if (args.length == 3)
|
||||
proxy = args[2];
|
||||
} else if (args.length == 3) {
|
||||
isShared = false; // not "true"
|
||||
proxy = args[2];
|
||||
_log.warn("args[1] == [" + args[1] + "] but rejected");
|
||||
} else {
|
||||
// isShared not specified, default to true
|
||||
isShared = true;
|
||||
proxy = args[1];
|
||||
}
|
||||
}
|
||||
|
||||
I2PTunnelTask task;
|
||||
ownDest = !isShared;
|
||||
try {
|
||||
task = new I2PTunnelConnectClient(port, l, ownDest, proxy, (EventDispatcher) this, this);
|
||||
addtask(task);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
_log.error(getPrefix() + "Invalid I2PTunnel config to create an httpclient [" + host + ":"+ port + "]", iae);
|
||||
}
|
||||
} else {
|
||||
l.log("connectclient <port> [<sharedClient>] [<proxy>]");
|
||||
l.log(" creates a client that for SSL/HTTPS requests.");
|
||||
l.log(" <sharedClient> (optional) indicates if this client shares tunnels with other clients (true of false)");
|
||||
l.log(" <proxy> (optional) indicates a proxy server to be used");
|
||||
l.log(" when trying to access an address out of the .i2p domain");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an IRC client on the given port number
|
||||
*
|
||||
|
@@ -0,0 +1,369 @@
|
||||
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
|
||||
* (c) 2003 - 2004 mihi
|
||||
*/
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.EventDispatcher;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Supports the following:
|
||||
* (where protocol is generally HTTP/1.1 but is ignored)
|
||||
* (where host is one of:
|
||||
* example.i2p
|
||||
* 52chars.b32.i2p
|
||||
* 516+charsbase64
|
||||
* example.com (sent to one of the configured proxies)
|
||||
* )
|
||||
*
|
||||
* (port and protocol are ignored for i2p destinations)
|
||||
* CONNECT host
|
||||
* CONNECT host protocol
|
||||
* CONNECT host:port
|
||||
* CONNECT host:port protocol (this is the standard)
|
||||
*
|
||||
* Additional lines after the CONNECT line but before the blank line are ignored and stripped.
|
||||
* The CONNECT line is removed for .i2p accesses
|
||||
* but passed along for outproxy accesses.
|
||||
*
|
||||
* Ref:
|
||||
* INTERNET-DRAFT Ari Luotonen
|
||||
* Expires: September 26, 1997 Netscape Communications Corporation
|
||||
* <draft-luotonen-ssl-tunneling-03.txt> March 26, 1997
|
||||
* Tunneling SSL Through a WWW Proxy
|
||||
*
|
||||
* @author zzz a stripped-down I2PTunnelHTTPClient
|
||||
*/
|
||||
public class I2PTunnelConnectClient extends I2PTunnelClientBase implements Runnable {
|
||||
private static final Log _log = new Log(I2PTunnelConnectClient.class);
|
||||
|
||||
private List<String> _proxyList;
|
||||
|
||||
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_NO_OUTPROXY =
|
||||
("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: No outproxy found</H1>"+
|
||||
"Your request was for a site outside of I2P, but you have no "+
|
||||
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel")
|
||||
.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"+
|
||||
"Cache-control: no-cache\r\n"+
|
||||
"\r\n"+
|
||||
"<html><body><H1>I2P ERROR: METHOD NOT ALLOWED</H1>"+
|
||||
"The request uses a bad protocol. "+
|
||||
"The Connect Proxy supports CONNECT requests ONLY. Other methods such as GET are not allowed - Maybe you wanted the HTTP Proxy?.<BR>")
|
||||
.getBytes();
|
||||
|
||||
private final static byte[] ERR_LOCALHOST =
|
||||
("HTTP/1.1 403 Access Denied\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: REQUEST DENIED</H1>"+
|
||||
"Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>")
|
||||
.getBytes();
|
||||
|
||||
private final static byte[] SUCCESS_RESPONSE =
|
||||
("HTTP/1.1 200 Connection Established\r\n"+
|
||||
"Proxy-agent: I2P\r\n"+
|
||||
"\r\n")
|
||||
.getBytes();
|
||||
|
||||
/** used to assign unique IDs to the threads / clients. no logic or functionality */
|
||||
private static volatile long __clientId = 0;
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if the I2PTunnel does not contain
|
||||
* valid config to contact the router
|
||||
*/
|
||||
public I2PTunnelConnectClient(int localPort, Logging l, boolean ownDest,
|
||||
String wwwProxy, EventDispatcher notifyThis,
|
||||
I2PTunnel tunnel) throws IllegalArgumentException {
|
||||
super(localPort, ownDest, l, notifyThis, "HTTPHandler " + (++__clientId), tunnel);
|
||||
|
||||
if (waitEventValue("openBaseClientResult").equals("error")) {
|
||||
notifyEvent("openConnectClientResult", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
_proxyList = new ArrayList();
|
||||
if (wwwProxy != null) {
|
||||
StringTokenizer tok = new StringTokenizer(wwwProxy, ",");
|
||||
while (tok.hasMoreTokens())
|
||||
_proxyList.add(tok.nextToken().trim());
|
||||
}
|
||||
|
||||
setName(getLocalPort() + " -> ConnectClient [Outproxy list: " + wwwProxy + "]");
|
||||
|
||||
startRunning();
|
||||
}
|
||||
|
||||
private String getPrefix(long requestId) { return "Client[" + _clientId + "/" + requestId + "]: "; }
|
||||
|
||||
private String selectProxy() {
|
||||
synchronized (_proxyList) {
|
||||
int size = _proxyList.size();
|
||||
if (size <= 0)
|
||||
return null;
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
return _proxyList.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int DEFAULT_READ_TIMEOUT = 60*1000;
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions() {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, ""+DEFAULT_READ_TIMEOUT);
|
||||
if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
|
||||
defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
return opts;
|
||||
}
|
||||
|
||||
private static long __requestId = 0;
|
||||
protected void clientConnectionRun(Socket s) {
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
String targetRequest = null;
|
||||
boolean usingWWWProxy = false;
|
||||
String currentProxy = null;
|
||||
long requestId = ++__requestId;
|
||||
try {
|
||||
out = s.getOutputStream();
|
||||
in = s.getInputStream();
|
||||
String line, method = null, host = null, destination = null, restofline = null;
|
||||
StringBuffer newRequest = new StringBuffer();
|
||||
int ahelper = 0;
|
||||
while (true) {
|
||||
// Use this rather than BufferedReader because we can't have readahead,
|
||||
// since we are passing the stream on to I2PTunnelRunner
|
||||
line = DataHelper.readLine(in);
|
||||
line = line.trim();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix(requestId) + "Line=[" + line + "]");
|
||||
|
||||
if (method == null) { // first line CONNECT blah.i2p:80 HTTP/1.1
|
||||
int pos = line.indexOf(" ");
|
||||
if (pos == -1) break; // empty first line
|
||||
method = line.substring(0, pos);
|
||||
String request = line.substring(pos + 1);
|
||||
|
||||
pos = request.indexOf(":");
|
||||
if (pos == -1)
|
||||
pos = request.indexOf(" ");
|
||||
if (pos == -1) {
|
||||
host = request;
|
||||
restofline = "";
|
||||
} else {
|
||||
host = request.substring(0, pos);
|
||||
restofline = request.substring(pos); // ":80 HTTP/1.1" or " HTTP/1.1"
|
||||
}
|
||||
|
||||
if (host.toLowerCase().endsWith(".i2p")) {
|
||||
// Destination gets the host name
|
||||
destination = host;
|
||||
} else if (host.indexOf(".") != -1) {
|
||||
// The request must be forwarded to a outproxy
|
||||
currentProxy = selectProxy();
|
||||
if (currentProxy == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!");
|
||||
writeErrorMessage(ERR_NO_OUTPROXY, out);
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
destination = currentProxy;
|
||||
usingWWWProxy = true;
|
||||
newRequest.append("CONNECT ").append(host).append(restofline).append("\r\n\r\n"); // HTTP spec
|
||||
} else if (host.toLowerCase().equals("localhost")) {
|
||||
writeErrorMessage(ERR_LOCALHOST, out);
|
||||
s.close();
|
||||
return;
|
||||
} else { // full b64 address (hopefully)
|
||||
destination = host;
|
||||
}
|
||||
targetRequest = host;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(getPrefix(requestId) + "METHOD:" + method + ":");
|
||||
_log.debug(getPrefix(requestId) + "HOST :" + host + ":");
|
||||
_log.debug(getPrefix(requestId) + "REST :" + restofline + ":");
|
||||
_log.debug(getPrefix(requestId) + "DEST :" + destination + ":");
|
||||
}
|
||||
|
||||
} else if (line.length() > 0) {
|
||||
// Additional lines - shouldn't be too many. Firefox sends:
|
||||
// User-Agent: blabla
|
||||
// Proxy-Connection: keep-alive
|
||||
// Host: blabla.i2p
|
||||
//
|
||||
// We could send these (filtered like in HTTPClient) on to the outproxy,
|
||||
// but for now just chomp them all.
|
||||
line = null;
|
||||
} else {
|
||||
// do it
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (destination == null || !"CONNECT".equalsIgnoreCase(method)) {
|
||||
writeErrorMessage(ERR_BAD_PROTOCOL, out);
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
|
||||
Destination dest = I2PTunnel.destFromName(destination);
|
||||
if (dest == null) {
|
||||
String str;
|
||||
byte[] header;
|
||||
if (usingWWWProxy)
|
||||
str = FileUtil.readTextFile("docs/dnfp-header.ht", 100, true);
|
||||
else
|
||||
str = FileUtil.readTextFile("docs/dnfh-header.ht", 100, true);
|
||||
if (str != null)
|
||||
header = str.getBytes();
|
||||
else
|
||||
header = ERR_DESTINATION_UNKNOWN;
|
||||
writeErrorMessage(header, out, targetRequest, usingWWWProxy, destination);
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
|
||||
I2PSocket i2ps = createI2PSocket(dest, getDefaultOptions());
|
||||
byte[] data = null;
|
||||
byte[] response = null;
|
||||
if (usingWWWProxy)
|
||||
data = newRequest.toString().getBytes("ISO-8859-1");
|
||||
else
|
||||
response = SUCCESS_RESPONSE;
|
||||
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
I2PTunnelRunner runner = 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);
|
||||
} catch (IOException ex) {
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
handleConnectClientException(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);
|
||||
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);
|
||||
closeSocket(s);
|
||||
}
|
||||
}
|
||||
|
||||
private static class OnTimeout implements Runnable {
|
||||
private Socket _socket;
|
||||
private OutputStream _out;
|
||||
private String _target;
|
||||
private boolean _usingProxy;
|
||||
private String _wwwProxy;
|
||||
private 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;
|
||||
try {
|
||||
String str;
|
||||
byte[] header;
|
||||
if (usingWWWProxy)
|
||||
str = FileUtil.readTextFile("docs/dnfp-header.ht", 100, true);
|
||||
else
|
||||
str = FileUtil.readTextFile("docs/dnf-header.ht", 100, true);
|
||||
if (str != null)
|
||||
header = str.getBytes();
|
||||
else
|
||||
header = ERR_DESTINATION_UNKNOWN;
|
||||
writeErrorMessage(header, out, targetRequest, usingWWWProxy, wwwProxy);
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
@@ -73,7 +73,7 @@ public class TunnelController implements Logging {
|
||||
|
||||
File keyFile = new File(getPrivKeyFile());
|
||||
if (keyFile.exists()) {
|
||||
log("Not overwriting existing private keys in " + keyFile.getAbsolutePath());
|
||||
//log("Not overwriting existing private keys in " + keyFile.getAbsolutePath());
|
||||
return;
|
||||
} else {
|
||||
File parent = keyFile.getParentFile();
|
||||
@@ -87,6 +87,7 @@ public class TunnelController implements Logging {
|
||||
String destStr = dest.toBase64();
|
||||
log("Private key created and saved in " + keyFile.getAbsolutePath());
|
||||
log("New destination: " + destStr);
|
||||
log("Base32: " + Base32.encode(dest.calculateHash().getData()) + ".b32.i2p");
|
||||
} catch (I2PException ie) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error creating new destination", ie);
|
||||
@@ -139,6 +140,8 @@ public class TunnelController implements Logging {
|
||||
startIrcClient();
|
||||
} else if("sockstunnel".equals(type)) {
|
||||
startSocksClient();
|
||||
} else if("connectclient".equals(type)) {
|
||||
startConnectClient();
|
||||
} else if ("client".equals(type)) {
|
||||
startClient();
|
||||
} else if ("server".equals(type)) {
|
||||
@@ -166,6 +169,21 @@ public class TunnelController implements Logging {
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void startConnectClient() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
setListenOn();
|
||||
String listenPort = getListenPort();
|
||||
String proxyList = getProxyList();
|
||||
String sharedClient = getSharedClient();
|
||||
if (proxyList == null)
|
||||
_tunnel.runConnectClient(new String[] { listenPort, sharedClient }, this);
|
||||
else
|
||||
_tunnel.runConnectClient(new String[] { listenPort, sharedClient, proxyList }, this);
|
||||
acquire();
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void startIrcClient() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
|
@@ -18,6 +18,11 @@ import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.PrivateKeyFile;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.i2ptunnel.TunnelController;
|
||||
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
@@ -65,6 +70,9 @@ public class IndexBean {
|
||||
private boolean _removeConfirmed;
|
||||
private Set<String> _booleanOptions;
|
||||
private Map<String, String> _otherOptions;
|
||||
private int _hashCashValue;
|
||||
private int _certType;
|
||||
private String _certSigner;
|
||||
|
||||
public static final int RUNNING = 1;
|
||||
public static final int STARTING = 2;
|
||||
@@ -156,6 +164,12 @@ public class IndexBean {
|
||||
else if ("Delete this proxy".equals(_action) || // IE workaround:
|
||||
(_action.toLowerCase().indexOf("d</span>elete") >= 0))
|
||||
return deleteTunnel();
|
||||
else if ("Estimate".equals(_action))
|
||||
return PrivateKeyFile.estimateHashCashTime(_hashCashValue);
|
||||
else if ("Modify".equals(_action))
|
||||
return modifyDestination();
|
||||
else if ("Generate".equals(_action))
|
||||
return generateNewEncryptionKey();
|
||||
else
|
||||
return "Action " + _action + " unknown";
|
||||
}
|
||||
@@ -370,7 +384,7 @@ public class IndexBean {
|
||||
else if ("ircclient".equals(internalType)) return "IRC client";
|
||||
else if ("server".equals(internalType)) return "Standard server";
|
||||
else if ("httpserver".equals(internalType)) return "HTTP server";
|
||||
else if ("sockstunnel".equals(internalType)) return "SOCKS proxy";
|
||||
else if ("sockstunnel".equals(internalType)) return "SOCKS 5 proxy";
|
||||
else if ("connectclient".equals(internalType)) return "CONNECT/SSL/HTTPS proxy";
|
||||
else return internalType;
|
||||
}
|
||||
@@ -440,6 +454,16 @@ public class IndexBean {
|
||||
String rv = tun.getMyDestination();
|
||||
if (rv != null)
|
||||
return rv;
|
||||
// if not running, do this the hard way
|
||||
String keyFile = tun.getPrivKeyFile();
|
||||
if (keyFile != null && keyFile.trim().length() > 0) {
|
||||
PrivateKeyFile pkf = new PrivateKeyFile(keyFile);
|
||||
try {
|
||||
Destination d = pkf.getDestination();
|
||||
if (d != null)
|
||||
return d.toBase64();
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -616,6 +640,115 @@ public class IndexBean {
|
||||
}
|
||||
}
|
||||
|
||||
/** params needed for hashcash and dest modification */
|
||||
public void setEffort(String val) {
|
||||
if (val != null) {
|
||||
try {
|
||||
_hashCashValue = Integer.parseInt(val.trim());
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
}
|
||||
public void setCert(String val) {
|
||||
if (val != null) {
|
||||
try {
|
||||
_certType = Integer.parseInt(val.trim());
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
}
|
||||
public void setSigner(String val) {
|
||||
_certSigner = val;
|
||||
}
|
||||
|
||||
/** Modify or create a destination */
|
||||
private String modifyDestination() {
|
||||
if (_privKeyFile == null || _privKeyFile.trim().length() <= 0)
|
||||
return "Private Key File not specified";
|
||||
|
||||
TunnelController tun = getController(_tunnel);
|
||||
Properties config = getConfig();
|
||||
if (config == null)
|
||||
return "Invalid params";
|
||||
if (tun == null) {
|
||||
// creating new
|
||||
tun = new TunnelController(config, "", true);
|
||||
_group.addController(tun);
|
||||
saveChanges();
|
||||
} else if (tun.getIsRunning() || tun.getIsStarting()) {
|
||||
return "Tunnel must be stopped before modifying destination";
|
||||
}
|
||||
PrivateKeyFile pkf = new PrivateKeyFile(_privKeyFile);
|
||||
try {
|
||||
pkf.createIfAbsent();
|
||||
} catch (Exception e) {
|
||||
return "Create private key file failed: " + e;
|
||||
}
|
||||
switch (_certType) {
|
||||
case Certificate.CERTIFICATE_TYPE_NULL:
|
||||
case Certificate.CERTIFICATE_TYPE_HIDDEN:
|
||||
pkf.setCertType(_certType);
|
||||
break;
|
||||
case Certificate.CERTIFICATE_TYPE_HASHCASH:
|
||||
pkf.setHashCashCert(_hashCashValue);
|
||||
break;
|
||||
case Certificate.CERTIFICATE_TYPE_SIGNED:
|
||||
if (_certSigner == null || _certSigner.trim().length() <= 0)
|
||||
return "No signing destination specified";
|
||||
// find the signer's key file...
|
||||
String signerPKF = null;
|
||||
for (int i = 0; i < getTunnelCount(); i++) {
|
||||
TunnelController c = getController(i);
|
||||
if (_certSigner.equals(c.getConfig("").getProperty("name")) ||
|
||||
_certSigner.equals(c.getConfig("").getProperty("spoofedHost"))) {
|
||||
signerPKF = c.getConfig("").getProperty("privKeyFile");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (signerPKF == null || signerPKF.length() <= 0)
|
||||
return "Signing destination " + _certSigner + " not found";
|
||||
if (_privKeyFile.equals(signerPKF))
|
||||
return "Self-signed destinations not allowed";
|
||||
Certificate c = pkf.setSignedCert(new PrivateKeyFile(signerPKF));
|
||||
if (c == null)
|
||||
return "Signing failed - does signer destination exist?";
|
||||
break;
|
||||
default:
|
||||
return "Unknown certificate type";
|
||||
}
|
||||
Destination newdest;
|
||||
try {
|
||||
pkf.write();
|
||||
newdest = pkf.getDestination();
|
||||
} catch (Exception e) {
|
||||
return "Modification failed: " + e;
|
||||
}
|
||||
return "Destination modified - " +
|
||||
"New Base32 is " + Base32.encode(newdest.calculateHash().getData()) + ".b32.i2p " +
|
||||
"New Destination is " + newdest.toBase64();
|
||||
}
|
||||
|
||||
/** New key */
|
||||
private String generateNewEncryptionKey() {
|
||||
TunnelController tun = getController(_tunnel);
|
||||
Properties config = getConfig();
|
||||
if (config == null)
|
||||
return "Invalid params";
|
||||
if (tun == null) {
|
||||
// creating new
|
||||
tun = new TunnelController(config, "", true);
|
||||
_group.addController(tun);
|
||||
saveChanges();
|
||||
} else if (tun.getIsRunning() || tun.getIsStarting()) {
|
||||
return "Tunnel must be stopped before modifying leaseset encryption key";
|
||||
}
|
||||
byte[] data = new byte[SessionKey.KEYSIZE_BYTES];
|
||||
_context.random().nextBytes(data);
|
||||
SessionKey sk = new SessionKey(data);
|
||||
setEncryptKey(sk.toBase64());
|
||||
setEncrypt("");
|
||||
saveChanges();
|
||||
return "New Leaseset Encryption Key: " + sk.toBase64();
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on all provided data, create a set of configuration parameters
|
||||
* suitable for use in a TunnelController. This will replace (not add to)
|
||||
|
Reference in New Issue
Block a user