propagate from branch 'i2p.i2p.zzz.test' (head f4edeaaf6cd647f4a69847a09272b54cb51ef758)
to branch 'i2p.i2p' (head 0d7e18b693718b5924035d7a6f638ff0689af589)
This commit is contained in:
@ -662,7 +662,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
|||||||
* "openSOCKSTunnelResult" = "ok" or "error" after the client tunnel has
|
* "openSOCKSTunnelResult" = "ok" or "error" after the client tunnel has
|
||||||
* started.
|
* started.
|
||||||
*
|
*
|
||||||
* @param args {portNumber}
|
* @param args {portNumber [, sharedClient]}
|
||||||
* @param l logger to receive events and output
|
* @param l logger to receive events and output
|
||||||
*/
|
*/
|
||||||
public void runSOCKSTunnel(String args[], Logging l) {
|
public void runSOCKSTunnel(String args[], Logging l) {
|
||||||
@ -677,6 +677,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isShared = false;
|
||||||
|
if (args.length > 1)
|
||||||
|
isShared = "true".equalsIgnoreCase(args[1].trim());
|
||||||
|
|
||||||
|
ownDest = !isShared;
|
||||||
I2PTunnelTask task;
|
I2PTunnelTask task;
|
||||||
task = new I2PSOCKSTunnel(port, l, ownDest, (EventDispatcher) this, this);
|
task = new I2PSOCKSTunnel(port, l, ownDest, (EventDispatcher) this, this);
|
||||||
addtask(task);
|
addtask(task);
|
||||||
|
@ -135,8 +135,10 @@ public class TunnelController implements Logging {
|
|||||||
}
|
}
|
||||||
if ("httpclient".equals(type)) {
|
if ("httpclient".equals(type)) {
|
||||||
startHttpClient();
|
startHttpClient();
|
||||||
}else if("ircclient".equals(type)) {
|
} else if("ircclient".equals(type)) {
|
||||||
startIrcClient();
|
startIrcClient();
|
||||||
|
} else if("sockstunnel".equals(type)) {
|
||||||
|
startSocksClient();
|
||||||
} else if ("client".equals(type)) {
|
} else if ("client".equals(type)) {
|
||||||
startClient();
|
startClient();
|
||||||
} else if ("server".equals(type)) {
|
} else if ("server".equals(type)) {
|
||||||
@ -176,6 +178,17 @@ public class TunnelController implements Logging {
|
|||||||
_running = true;
|
_running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startSocksClient() {
|
||||||
|
setI2CPOptions();
|
||||||
|
setSessionOptions();
|
||||||
|
setListenOn();
|
||||||
|
String listenPort = getListenPort();
|
||||||
|
String sharedClient = getSharedClient();
|
||||||
|
_tunnel.runSOCKSTunnel(new String[] { listenPort, sharedClient }, this);
|
||||||
|
acquire();
|
||||||
|
_running = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note the fact that we are using some sessions, so that they dont get
|
* Note the fact that we are using some sessions, so that they dont get
|
||||||
* closed by some other tunnels
|
* closed by some other tunnels
|
||||||
|
@ -7,6 +7,12 @@
|
|||||||
package net.i2p.i2ptunnel.socks;
|
package net.i2p.i2ptunnel.socks;
|
||||||
|
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import net.i2p.client.streaming.I2PSocket;
|
import net.i2p.client.streaming.I2PSocket;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
@ -20,7 +26,7 @@ import net.i2p.util.Log;
|
|||||||
public class I2PSOCKSTunnel extends I2PTunnelClientBase {
|
public class I2PSOCKSTunnel extends I2PTunnelClientBase {
|
||||||
|
|
||||||
private static final Log _log = new Log(I2PSOCKSTunnel.class);
|
private static final Log _log = new Log(I2PSOCKSTunnel.class);
|
||||||
|
private HashMap<String, List<String>> proxies = null; // port# + "" or "default" -> hostname list
|
||||||
protected Destination outProxyDest = null;
|
protected Destination outProxyDest = null;
|
||||||
|
|
||||||
//public I2PSOCKSTunnel(int localPort, Logging l, boolean ownDest) {
|
//public I2PSOCKSTunnel(int localPort, Logging l, boolean ownDest) {
|
||||||
@ -36,7 +42,7 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setName(getLocalPort() + " -> SOCKSTunnel");
|
setName(getLocalPort() + " -> SOCKSTunnel");
|
||||||
|
parseOptions();
|
||||||
startRunning();
|
startRunning();
|
||||||
|
|
||||||
notifyEvent("openSOCKSTunnelResult", "ok");
|
notifyEvent("openSOCKSTunnelResult", "ok");
|
||||||
@ -46,11 +52,49 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
|
|||||||
try {
|
try {
|
||||||
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s);
|
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s);
|
||||||
Socket clientSock = serv.getClientSocket();
|
Socket clientSock = serv.getClientSocket();
|
||||||
I2PSocket destSock = serv.getDestinationI2PSocket();
|
I2PSocket destSock = serv.getDestinationI2PSocket(this);
|
||||||
new I2PTunnelRunner(clientSock, destSock, sockLock, null, mySockets);
|
new I2PTunnelRunner(clientSock, destSock, sockLock, null, mySockets);
|
||||||
} catch (SOCKSException e) {
|
} catch (SOCKSException e) {
|
||||||
_log.error("Error from SOCKS connection: " + e.getMessage());
|
_log.error("Error from SOCKS connection: " + e.getMessage());
|
||||||
closeSocket(s);
|
closeSocket(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String PROP_PROXY = "i2ptunnel.socks.proxy.";
|
||||||
|
private void parseOptions() {
|
||||||
|
Properties opts = getTunnel().getClientOptions();
|
||||||
|
proxies = new HashMap(0);
|
||||||
|
for (Map.Entry e : opts.entrySet()) {
|
||||||
|
String prop = (String)e.getKey();
|
||||||
|
if ((!prop.startsWith(PROP_PROXY)) || prop.length() <= PROP_PROXY.length())
|
||||||
|
continue;
|
||||||
|
String port = prop.substring(PROP_PROXY.length());
|
||||||
|
List proxyList = new ArrayList(1);
|
||||||
|
StringTokenizer tok = new StringTokenizer((String)e.getValue(), ", \t");
|
||||||
|
while (tok.hasMoreTokens()) {
|
||||||
|
String proxy = tok.nextToken().trim();
|
||||||
|
if (proxy.endsWith(".i2p"))
|
||||||
|
proxyList.add(proxy);
|
||||||
|
else
|
||||||
|
_log.error("Non-i2p SOCKS outproxy: " + proxy);
|
||||||
|
}
|
||||||
|
proxies.put(port, proxyList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<String, List<String>> getProxyMap() {
|
||||||
|
return proxies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getProxies(int port) {
|
||||||
|
List<String> rv = proxies.get(port + "");
|
||||||
|
if (rv == null)
|
||||||
|
rv = getDefaultProxies();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getDefaultProxies() {
|
||||||
|
return proxies.get("default");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -12,7 +12,14 @@ import java.io.DataOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.I2PException;
|
||||||
|
import net.i2p.client.streaming.I2PSocket;
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.i2ptunnel.I2PTunnel;
|
||||||
import net.i2p.util.HexDump;
|
import net.i2p.util.HexDump;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
@ -28,7 +35,6 @@ public class SOCKS5Server extends SOCKSServer {
|
|||||||
private static final int SOCKS_VERSION_5 = 0x05;
|
private static final int SOCKS_VERSION_5 = 0x05;
|
||||||
|
|
||||||
private Socket clientSock = null;
|
private Socket clientSock = null;
|
||||||
|
|
||||||
private boolean setupCompleted = false;
|
private boolean setupCompleted = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,6 +132,7 @@ public class SOCKS5Server extends SOCKSServer {
|
|||||||
throw new SOCKSException("UDP ASSOCIATE command not supported");
|
throw new SOCKSException("UDP ASSOCIATE command not supported");
|
||||||
default:
|
default:
|
||||||
_log.debug("unknown command in request (" + Integer.toHexString(command) + ")");
|
_log.debug("unknown command in request (" + Integer.toHexString(command) + ")");
|
||||||
|
sendRequestReply(Reply.COMMAND_NOT_SUPPORTED, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
|
||||||
throw new SOCKSException("Invalid command in request");
|
throw new SOCKSException("Invalid command in request");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,12 +173,14 @@ public class SOCKS5Server extends SOCKSServer {
|
|||||||
throw new SOCKSException("IPV6 addresses not supported");
|
throw new SOCKSException("IPV6 addresses not supported");
|
||||||
default:
|
default:
|
||||||
_log.debug("unknown address type in request (" + Integer.toHexString(command) + ")");
|
_log.debug("unknown address type in request (" + Integer.toHexString(command) + ")");
|
||||||
|
sendRequestReply(Reply.ADDRESS_TYPE_NOT_SUPPORTED, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
|
||||||
throw new SOCKSException("Invalid addresses type in request");
|
throw new SOCKSException("Invalid addresses type in request");
|
||||||
}
|
}
|
||||||
|
|
||||||
connPort = in.readUnsignedShort();
|
connPort = in.readUnsignedShort();
|
||||||
if (connPort == 0) {
|
if (connPort == 0) {
|
||||||
_log.debug("trying to connect to TCP port 0? Dropping!");
|
_log.debug("trying to connect to TCP port 0? Dropping!");
|
||||||
|
sendRequestReply(Reply.CONNECTION_NOT_ALLOWED_BY_RULESET, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
|
||||||
throw new SOCKSException("Invalid port number in request");
|
throw new SOCKSException("Invalid port number in request");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,6 +257,107 @@ public class SOCKS5Server extends SOCKSServer {
|
|||||||
out.write(reply);
|
out.write(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an I2PSocket that can be used to send/receive 8-bit clean data
|
||||||
|
* to/from the destination of the SOCKS connection.
|
||||||
|
*
|
||||||
|
* @return an I2PSocket connected with the destination
|
||||||
|
*/
|
||||||
|
public I2PSocket getDestinationI2PSocket(I2PSOCKSTunnel t) throws SOCKSException {
|
||||||
|
setupServer();
|
||||||
|
|
||||||
|
if (connHostName == null) {
|
||||||
|
_log.error("BUG: destination host name has not been initialized!");
|
||||||
|
throw new SOCKSException("BUG! See the logs!");
|
||||||
|
}
|
||||||
|
if (connPort == 0) {
|
||||||
|
_log.error("BUG: destination port has not been initialized!");
|
||||||
|
throw new SOCKSException("BUG! See the logs!");
|
||||||
|
}
|
||||||
|
|
||||||
|
DataOutputStream out; // for errors
|
||||||
|
try {
|
||||||
|
out = new DataOutputStream(clientSock.getOutputStream());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SOCKSException("Connection error (" + e.getMessage() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: here we should read our config file, select an
|
||||||
|
// outproxy, and instantiate the proper socket class that
|
||||||
|
// handles the outproxy itself (SOCKS4a, SOCKS5, HTTP CONNECT...).
|
||||||
|
I2PSocket destSock;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (connHostName.toLowerCase().endsWith(".i2p")) {
|
||||||
|
_log.debug("connecting to " + connHostName + "...");
|
||||||
|
// Let's not due a new Dest for every request, huh?
|
||||||
|
//I2PSocketManager sm = I2PSocketManagerFactory.createManager();
|
||||||
|
//destSock = sm.connect(I2PTunnel.destFromName(connHostName), null);
|
||||||
|
destSock = t.createI2PSocket(I2PTunnel.destFromName(connHostName));
|
||||||
|
} else if ("localhost".equals(connHostName) || "127.0.0.1".equals(connHostName)) {
|
||||||
|
String err = "No localhost accesses allowed through the Socks Proxy";
|
||||||
|
_log.error(err);
|
||||||
|
try {
|
||||||
|
sendRequestReply(Reply.CONNECTION_NOT_ALLOWED_BY_RULESET, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
throw new SOCKSException(err);
|
||||||
|
} else if (connPort == 80) {
|
||||||
|
// rewrite GET line to include hostname??? or add Host: line???
|
||||||
|
// or forward to local eepProxy (but that's a Socket not an I2PSocket)
|
||||||
|
// use eepProxy configured outproxies?
|
||||||
|
String err = "No handler for HTTP outproxy implemented";
|
||||||
|
_log.error(err);
|
||||||
|
try {
|
||||||
|
sendRequestReply(Reply.CONNECTION_NOT_ALLOWED_BY_RULESET, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
throw new SOCKSException(err);
|
||||||
|
} else {
|
||||||
|
List<String> proxies = t.getProxies(connPort);
|
||||||
|
if (proxies == null || proxies.size() <= 0) {
|
||||||
|
String err = "No outproxy configured for port " + connPort + " and no default configured either";
|
||||||
|
_log.error(err);
|
||||||
|
try {
|
||||||
|
sendRequestReply(Reply.CONNECTION_NOT_ALLOWED_BY_RULESET, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
throw new SOCKSException(err);
|
||||||
|
}
|
||||||
|
int p = I2PAppContext.getGlobalContext().random().nextInt(proxies.size());
|
||||||
|
String proxy = proxies.get(p);
|
||||||
|
_log.debug("connecting to port " + connPort + " proxy " + proxy + " for " + connHostName + "...");
|
||||||
|
// this isn't going to work, these need to be socks outproxies so we need
|
||||||
|
// to do a socks session to them?
|
||||||
|
destSock = t.createI2PSocket(I2PTunnel.destFromName(proxy));
|
||||||
|
}
|
||||||
|
confirmConnection();
|
||||||
|
_log.debug("connection confirmed - exchanging data...");
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
try {
|
||||||
|
sendRequestReply(Reply.HOST_UNREACHABLE, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
throw new SOCKSException("Error in destination format");
|
||||||
|
} catch (SocketException e) {
|
||||||
|
try {
|
||||||
|
sendRequestReply(Reply.HOST_UNREACHABLE, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
throw new SOCKSException("Error connecting ("
|
||||||
|
+ e.getMessage() + ")");
|
||||||
|
} catch (IOException e) {
|
||||||
|
try {
|
||||||
|
sendRequestReply(Reply.HOST_UNREACHABLE, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
throw new SOCKSException("Error connecting ("
|
||||||
|
+ e.getMessage() + ")");
|
||||||
|
} catch (I2PException e) {
|
||||||
|
try {
|
||||||
|
sendRequestReply(Reply.HOST_UNREACHABLE, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
throw new SOCKSException("Error connecting ("
|
||||||
|
+ e.getMessage() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
return destSock;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some namespaces to enclose SOCKS protocol codes
|
* Some namespaces to enclose SOCKS protocol codes
|
||||||
*/
|
*/
|
||||||
|
@ -6,15 +6,9 @@
|
|||||||
*/
|
*/
|
||||||
package net.i2p.i2ptunnel.socks;
|
package net.i2p.i2ptunnel.socks;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketException;
|
|
||||||
|
|
||||||
import net.i2p.I2PException;
|
|
||||||
import net.i2p.client.streaming.I2PSocket;
|
import net.i2p.client.streaming.I2PSocket;
|
||||||
import net.i2p.client.streaming.I2PSocketManager;
|
|
||||||
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
|
||||||
import net.i2p.data.DataFormatException;
|
|
||||||
import net.i2p.i2ptunnel.I2PTunnel;
|
import net.i2p.i2ptunnel.I2PTunnel;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
@ -30,10 +24,6 @@ public abstract class SOCKSServer {
|
|||||||
protected String connHostName = null;
|
protected String connHostName = null;
|
||||||
protected int connPort = 0;
|
protected int connPort = 0;
|
||||||
|
|
||||||
I2PSocket destSocket = null;
|
|
||||||
|
|
||||||
Object FIXME = new Object();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform server initialization (expecially regarding protected
|
* Perform server initialization (expecially regarding protected
|
||||||
* variables).
|
* variables).
|
||||||
@ -59,47 +49,6 @@ public abstract class SOCKSServer {
|
|||||||
*
|
*
|
||||||
* @return an I2PSocket connected with the destination
|
* @return an I2PSocket connected with the destination
|
||||||
*/
|
*/
|
||||||
public I2PSocket getDestinationI2PSocket() throws SOCKSException {
|
public abstract I2PSocket getDestinationI2PSocket(I2PSOCKSTunnel t) throws SOCKSException;
|
||||||
setupServer();
|
|
||||||
|
|
||||||
if (connHostName == null) {
|
|
||||||
_log.error("BUG: destination host name has not been initialized!");
|
|
||||||
throw new SOCKSException("BUG! See the logs!");
|
|
||||||
}
|
|
||||||
if (connPort == 0) {
|
|
||||||
_log.error("BUG: destination port has not been initialized!");
|
|
||||||
throw new SOCKSException("BUG! See the logs!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: here we should read our config file, select an
|
|
||||||
// outproxy, and instantiate the proper socket class that
|
|
||||||
// handles the outproxy itself (SOCKS4a, SOCKS5, HTTP CONNECT...).
|
|
||||||
I2PSocket destSock;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (connHostName.toLowerCase().endsWith(".i2p")) {
|
|
||||||
_log.debug("connecting to " + connHostName + "...");
|
|
||||||
I2PSocketManager sm = I2PSocketManagerFactory.createManager();
|
|
||||||
destSock = sm.connect(I2PTunnel.destFromName(connHostName), null);
|
|
||||||
confirmConnection();
|
|
||||||
_log.debug("connection confirmed - exchanging data...");
|
|
||||||
} else {
|
|
||||||
_log.error("We don't support outproxies (yet)");
|
|
||||||
throw new SOCKSException("Ouproxies not supported (yet)");
|
|
||||||
}
|
|
||||||
} catch (DataFormatException e) {
|
|
||||||
throw new SOCKSException("Error in destination format");
|
|
||||||
} catch (SocketException e) {
|
|
||||||
throw new SOCKSException("Error connecting ("
|
|
||||||
+ e.getMessage() + ")");
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new SOCKSException("Error connecting ("
|
|
||||||
+ e.getMessage() + ")");
|
|
||||||
} catch (I2PException e) {
|
|
||||||
throw new SOCKSException("Error connecting ("
|
|
||||||
+ e.getMessage() + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
return destSock;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
package net.i2p.i2ptunnel.socks;
|
package net.i2p.i2ptunnel.socks;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
@ -18,6 +19,15 @@ import net.i2p.util.Log;
|
|||||||
public class SOCKSServerFactory {
|
public class SOCKSServerFactory {
|
||||||
private final static Log _log = new Log(SOCKSServerFactory.class);
|
private final static Log _log = new Log(SOCKSServerFactory.class);
|
||||||
|
|
||||||
|
private final static String ERR_REQUEST_DENIED =
|
||||||
|
"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 SOCKS PROXY ERROR: REQUEST DENIED</H1>" +
|
||||||
|
"Your browser is misconfigured. This is a SOCKS proxy, not a HTTP proxy" +
|
||||||
|
"</body></html>";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new SOCKS server, using the provided socket (that must
|
* Create a new SOCKS server, using the provided socket (that must
|
||||||
* be connected to a client) to select the proper SOCKS protocol
|
* be connected to a client) to select the proper SOCKS protocol
|
||||||
@ -38,9 +48,15 @@ public class SOCKSServerFactory {
|
|||||||
// SOCKS version 5
|
// SOCKS version 5
|
||||||
serv = new SOCKS5Server(s);
|
serv = new SOCKS5Server(s);
|
||||||
break;
|
break;
|
||||||
|
case 'C':
|
||||||
|
case 'G':
|
||||||
|
case 'H':
|
||||||
|
case 'P':
|
||||||
|
DataOutputStream out = new DataOutputStream(s.getOutputStream());
|
||||||
|
out.write(ERR_REQUEST_DENIED.getBytes());
|
||||||
|
throw new SOCKSException("HTTP request to socks");
|
||||||
default:
|
default:
|
||||||
_log.debug("SOCKS protocol version not supported (" + Integer.toHexString(socksVer) + ")");
|
throw new SOCKSException("SOCKS protocol version not supported (" + Integer.toHexString(socksVer) + ")");
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
_log.debug("error reading SOCKS protocol version");
|
_log.debug("error reading SOCKS protocol version");
|
||||||
|
@ -8,9 +8,12 @@ package net.i2p.i2ptunnel.web;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import net.i2p.i2ptunnel.TunnelController;
|
import net.i2p.i2ptunnel.TunnelController;
|
||||||
@ -28,9 +31,7 @@ public class EditBean extends IndexBean {
|
|||||||
if (controllers.size() > tunnel) {
|
if (controllers.size() > tunnel) {
|
||||||
TunnelController cur = (TunnelController)controllers.get(tunnel);
|
TunnelController cur = (TunnelController)controllers.get(tunnel);
|
||||||
if (cur == null) return false;
|
if (cur == null) return false;
|
||||||
return ( ("client".equals(cur.getType())) ||
|
return isClient(cur.getType());
|
||||||
("httpclient".equals(cur.getType()))||
|
|
||||||
("ircclient".equals(cur.getType())));
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -38,7 +39,7 @@ public class EditBean extends IndexBean {
|
|||||||
|
|
||||||
public String getTargetHost(int tunnel) {
|
public String getTargetHost(int tunnel) {
|
||||||
TunnelController tun = getController(tunnel);
|
TunnelController tun = getController(tunnel);
|
||||||
if (tun != null)
|
if (tun != null && tun.getTargetHost() != null)
|
||||||
return tun.getTargetHost();
|
return tun.getTargetHost();
|
||||||
else
|
else
|
||||||
return "127.0.0.1";
|
return "127.0.0.1";
|
||||||
@ -52,7 +53,7 @@ public class EditBean extends IndexBean {
|
|||||||
}
|
}
|
||||||
public String getSpoofedHost(int tunnel) {
|
public String getSpoofedHost(int tunnel) {
|
||||||
TunnelController tun = getController(tunnel);
|
TunnelController tun = getController(tunnel);
|
||||||
if (tun != null)
|
if (tun != null && tun.getSpoofedHost() != null)
|
||||||
return tun.getSpoofedHost();
|
return tun.getSpoofedHost();
|
||||||
else
|
else
|
||||||
return "";
|
return "";
|
||||||
@ -82,119 +83,100 @@ public class EditBean extends IndexBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean shouldDelay(int tunnel) {
|
public boolean shouldDelay(int tunnel) {
|
||||||
TunnelController tun = getController(tunnel);
|
return getProperty(tunnel, "i2p.streaming.connectDelay", 0) > 0;
|
||||||
if (tun != null) {
|
|
||||||
Properties opts = getOptions(tun);
|
|
||||||
if (opts != null) {
|
|
||||||
String delay = opts.getProperty("i2p.streaming.connectDelay");
|
|
||||||
if ( (delay == null) || ("0".equals(delay)) )
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInteractive(int tunnel) {
|
public boolean isInteractive(int tunnel) {
|
||||||
TunnelController tun = getController(tunnel);
|
return getProperty(tunnel, "i2p.streaming.maxWindowSize", 128) == 12;
|
||||||
if (tun != null) {
|
|
||||||
Properties opts = getOptions(tun);
|
|
||||||
if (opts != null) {
|
|
||||||
String wsiz = opts.getProperty("i2p.streaming.maxWindowSize");
|
|
||||||
if ( (wsiz == null) || (!"1".equals(wsiz)) )
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTunnelDepth(int tunnel, int defaultLength) {
|
public int getTunnelDepth(int tunnel, int defaultLength) {
|
||||||
TunnelController tun = getController(tunnel);
|
return getProperty(tunnel, "inbound.length", defaultLength);
|
||||||
if (tun != null) {
|
|
||||||
Properties opts = getOptions(tun);
|
|
||||||
if (opts != null) {
|
|
||||||
String len = opts.getProperty("inbound.length");
|
|
||||||
if (len == null) return defaultLength;
|
|
||||||
try {
|
|
||||||
return Integer.parseInt(len);
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
return defaultLength;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return defaultLength;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return defaultLength;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTunnelQuantity(int tunnel, int defaultQuantity) {
|
public int getTunnelQuantity(int tunnel, int defaultQuantity) {
|
||||||
TunnelController tun = getController(tunnel);
|
return getProperty(tunnel, "inbound.quantity", defaultQuantity);
|
||||||
if (tun != null) {
|
|
||||||
Properties opts = getOptions(tun);
|
|
||||||
if (opts != null) {
|
|
||||||
String len = opts.getProperty("inbound.quantity");
|
|
||||||
if (len == null) return defaultQuantity;
|
|
||||||
try {
|
|
||||||
return Integer.parseInt(len);
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
return defaultQuantity;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return defaultQuantity;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return defaultQuantity;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTunnelBackupQuantity(int tunnel, int defaultBackupQuantity) {
|
public int getTunnelBackupQuantity(int tunnel, int defaultBackupQuantity) {
|
||||||
TunnelController tun = getController(tunnel);
|
return getProperty(tunnel, "inbound.backupQuantity", defaultBackupQuantity);
|
||||||
if (tun != null) {
|
|
||||||
Properties opts = getOptions(tun);
|
|
||||||
if (opts != null) {
|
|
||||||
String len = opts.getProperty("inbound.backupQuantity");
|
|
||||||
if (len == null) return defaultBackupQuantity;
|
|
||||||
try {
|
|
||||||
return Integer.parseInt(len);
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
return defaultBackupQuantity;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return defaultBackupQuantity;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return defaultBackupQuantity;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTunnelVariance(int tunnel, int defaultVariance) {
|
public int getTunnelVariance(int tunnel, int defaultVariance) {
|
||||||
|
return getProperty(tunnel, "inbound.lengthVariance", defaultVariance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getReduce(int tunnel) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getReduceCount(int tunnel) {
|
||||||
|
return getProperty(tunnel, "inbound.reduceQuantity", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getReduceTime(int tunnel) {
|
||||||
|
return getProperty(tunnel, "reduceIdleTime", 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCert(int tunnel) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEffort(int tunnel) {
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSigner(int tunnel) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getEncrypt(int tunnel) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEncryptKey(int tunnel) {
|
||||||
|
return getProperty(tunnel, "encryptKey", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getAccess(int tunnel) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessList(int tunnel) {
|
||||||
|
return getProperty(tunnel, "accessList", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getClose(int tunnel) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getNewDest(int tunnel) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getProperty(int tunnel, String prop, int def) {
|
||||||
TunnelController tun = getController(tunnel);
|
TunnelController tun = getController(tunnel);
|
||||||
if (tun != null) {
|
if (tun != null) {
|
||||||
Properties opts = getOptions(tun);
|
Properties opts = getOptions(tun);
|
||||||
if (opts != null) {
|
if (opts != null) {
|
||||||
String len = opts.getProperty("inbound.lengthVariance");
|
String s = opts.getProperty(prop);
|
||||||
if (len == null) return defaultVariance;
|
if (s == null) return def;
|
||||||
try {
|
try {
|
||||||
return Integer.parseInt(len);
|
return Integer.parseInt(s);
|
||||||
} catch (NumberFormatException nfe) {
|
} catch (NumberFormatException nfe) {}
|
||||||
return defaultVariance;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return defaultVariance;
|
|
||||||
}
|
}
|
||||||
} else {
|
return def;
|
||||||
return defaultVariance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getProperty(int tunnel, String prop, String def) {
|
||||||
|
TunnelController tun = getController(tunnel);
|
||||||
|
if (tun != null) {
|
||||||
|
Properties opts = getOptions(tun);
|
||||||
|
if (opts != null)
|
||||||
|
return opts.getProperty(prop, def);
|
||||||
|
}
|
||||||
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getI2CPHost(int tunnel) {
|
public String getI2CPHost(int tunnel) {
|
||||||
@ -213,6 +195,14 @@ public class EditBean extends IndexBean {
|
|||||||
return "7654";
|
return "7654";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String noShowProps[] = {
|
||||||
|
"inbound.length", "outbound.length", "inbound.lengthVariance", "outbound.lengthVariance",
|
||||||
|
"inbound.backupQuantity", "outbound.backupQuantity", "inbound.quantity", "outbound.quantity",
|
||||||
|
"inbound.nickname", "outbound.nickname", "i2p.streaming.connectDelay", "i2p.streaming.maxWindowSize"
|
||||||
|
};
|
||||||
|
private static final Set noShowSet = new HashSet(noShowProps.length);
|
||||||
|
static { noShowSet.addAll(Arrays.asList(noShowProps)); }
|
||||||
|
|
||||||
public String getCustomOptions(int tunnel) {
|
public String getCustomOptions(int tunnel) {
|
||||||
TunnelController tun = getController(tunnel);
|
TunnelController tun = getController(tunnel);
|
||||||
if (tun != null) {
|
if (tun != null) {
|
||||||
@ -222,19 +212,9 @@ public class EditBean extends IndexBean {
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) {
|
for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) {
|
||||||
String key = (String)iter.next();
|
String key = (String)iter.next();
|
||||||
|
if (noShowSet.contains(key))
|
||||||
|
continue;
|
||||||
String val = opts.getProperty(key);
|
String val = opts.getProperty(key);
|
||||||
if ("inbound.length".equals(key)) continue;
|
|
||||||
if ("outbound.length".equals(key)) continue;
|
|
||||||
if ("inbound.lengthVariance".equals(key)) continue;
|
|
||||||
if ("outbound.lengthVariance".equals(key)) continue;
|
|
||||||
if ("inbound.backupQuantity".equals(key)) continue;
|
|
||||||
if ("outbound.backupQuantity".equals(key)) continue;
|
|
||||||
if ("inbound.quantity".equals(key)) continue;
|
|
||||||
if ("outbound.quantity".equals(key)) continue;
|
|
||||||
if ("inbound.nickname".equals(key)) continue;
|
|
||||||
if ("outbound.nickname".equals(key)) continue;
|
|
||||||
if ("i2p.streaming.connectDelay".equals(key)) continue;
|
|
||||||
if ("i2p.streaming.maxWindowSize".equals(key)) continue;
|
|
||||||
if (i != 0) buf.append(' ');
|
if (i != 0) buf.append(' ');
|
||||||
buf.append(key).append('=').append(val);
|
buf.append(key).append('=').append(val);
|
||||||
i++;
|
i++;
|
||||||
|
@ -209,10 +209,7 @@ public class IndexBean {
|
|||||||
}
|
}
|
||||||
// Only modify other shared tunnels
|
// Only modify other shared tunnels
|
||||||
// if the current tunnel is shared, and of supported type
|
// if the current tunnel is shared, and of supported type
|
||||||
if ("true".equalsIgnoreCase(cur.getSharedClient()) &&
|
if ("true".equalsIgnoreCase(cur.getSharedClient()) && isClient(cur.getType())) {
|
||||||
("ircclient".equals(cur.getType()) ||
|
|
||||||
"httpclient".equals(cur.getType()) ||
|
|
||||||
"client".equals(cur.getType()))) {
|
|
||||||
// all clients use the same I2CP session, and as such, use the same I2CP options
|
// all clients use the same I2CP session, and as such, use the same I2CP options
|
||||||
List controllers = _group.getControllers();
|
List controllers = _group.getControllers();
|
||||||
|
|
||||||
@ -224,11 +221,7 @@ public class IndexBean {
|
|||||||
|
|
||||||
// Only modify this non-current tunnel
|
// Only modify this non-current tunnel
|
||||||
// if it belongs to a shared destination, and is of supported type
|
// if it belongs to a shared destination, and is of supported type
|
||||||
if ("true".equalsIgnoreCase(c.getSharedClient()) &&
|
if ("true".equalsIgnoreCase(c.getSharedClient()) && isClient(c.getType())) {
|
||||||
("httpclient".equals(c.getType()) ||
|
|
||||||
"ircclient".equals(c.getType()) ||
|
|
||||||
"client".equals(c.getType()))) {
|
|
||||||
|
|
||||||
Properties cOpt = c.getConfig("");
|
Properties cOpt = c.getConfig("");
|
||||||
if (_tunnelQuantity != null) {
|
if (_tunnelQuantity != null) {
|
||||||
cOpt.setProperty("option.inbound.quantity", _tunnelQuantity);
|
cOpt.setProperty("option.inbound.quantity", _tunnelQuantity);
|
||||||
@ -326,9 +319,14 @@ public class IndexBean {
|
|||||||
public boolean isClient(int tunnelNum) {
|
public boolean isClient(int tunnelNum) {
|
||||||
TunnelController cur = getController(tunnelNum);
|
TunnelController cur = getController(tunnelNum);
|
||||||
if (cur == null) return false;
|
if (cur == null) return false;
|
||||||
return ( ("client".equals(cur.getType())) ||
|
return isClient(cur.getType());
|
||||||
("httpclient".equals(cur.getType())) ||
|
}
|
||||||
("ircclient".equals(cur.getType())));
|
|
||||||
|
public static boolean isClient(String type) {
|
||||||
|
return ( ("client".equals(type)) ||
|
||||||
|
("httpclient".equals(type)) ||
|
||||||
|
("sockstunnel".equals(type)) ||
|
||||||
|
("ircclient".equals(type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTunnelName(int tunnel) {
|
public String getTunnelName(int tunnel) {
|
||||||
@ -361,6 +359,7 @@ public class IndexBean {
|
|||||||
else if ("ircclient".equals(internalType)) return "IRC client";
|
else if ("ircclient".equals(internalType)) return "IRC client";
|
||||||
else if ("server".equals(internalType)) return "Standard server";
|
else if ("server".equals(internalType)) return "Standard server";
|
||||||
else if ("httpserver".equals(internalType)) return "HTTP server";
|
else if ("httpserver".equals(internalType)) return "HTTP server";
|
||||||
|
else if ("sockstunnel".equals(internalType)) return "SOCKS proxy";
|
||||||
else return internalType;
|
else return internalType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,77 +578,40 @@ public class IndexBean {
|
|||||||
Properties config = new Properties();
|
Properties config = new Properties();
|
||||||
updateConfigGeneric(config);
|
updateConfigGeneric(config);
|
||||||
|
|
||||||
if ("httpclient".equals(_type)) {
|
if (isClient(_type)) {
|
||||||
|
// generic client stuff
|
||||||
if (_port != null)
|
if (_port != null)
|
||||||
config.setProperty("listenPort", _port);
|
config.setProperty("listenPort", _port);
|
||||||
if (_reachableByOther != null)
|
if (_reachableByOther != null)
|
||||||
config.setProperty("interface", _reachableByOther);
|
config.setProperty("interface", _reachableByOther);
|
||||||
else
|
else
|
||||||
config.setProperty("interface", _reachableBy);
|
config.setProperty("interface", _reachableBy);
|
||||||
|
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
|
||||||
|
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
|
||||||
|
if (_name != null && !_sharedClient) {
|
||||||
|
config.setProperty("option.inbound.nickname", _name);
|
||||||
|
config.setProperty("option.outbound.nickname", _name);
|
||||||
|
}
|
||||||
|
config.setProperty("sharedClient", _sharedClient + "");
|
||||||
|
} else {
|
||||||
|
// generic server stuff
|
||||||
|
if (_targetHost != null)
|
||||||
|
config.setProperty("targetHost", _targetHost);
|
||||||
|
if (_targetPort != null)
|
||||||
|
config.setProperty("targetPort", _targetPort);
|
||||||
|
if (_privKeyFile != null)
|
||||||
|
config.setProperty("privKeyFile", _privKeyFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("httpclient".equals(_type)) {
|
||||||
if (_proxyList != null)
|
if (_proxyList != null)
|
||||||
config.setProperty("proxyList", _proxyList);
|
config.setProperty("proxyList", _proxyList);
|
||||||
|
} else if ("ircclient".equals(_type) || "client".equals(_type)) {
|
||||||
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
|
|
||||||
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
|
|
||||||
if (_name != null && !_sharedClient) {
|
|
||||||
config.setProperty("option.inbound.nickname", _name);
|
|
||||||
config.setProperty("option.outbound.nickname", _name);
|
|
||||||
}
|
|
||||||
|
|
||||||
config.setProperty("sharedClient", _sharedClient + "");
|
|
||||||
}else if ("ircclient".equals(_type)) {
|
|
||||||
if (_port != null)
|
|
||||||
config.setProperty("listenPort", _port);
|
|
||||||
if (_reachableByOther != null)
|
|
||||||
config.setProperty("interface", _reachableByOther);
|
|
||||||
else
|
|
||||||
config.setProperty("interface", _reachableBy);
|
|
||||||
if (_targetDestination != null)
|
if (_targetDestination != null)
|
||||||
config.setProperty("targetDestination", _targetDestination);
|
config.setProperty("targetDestination", _targetDestination);
|
||||||
|
|
||||||
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
|
|
||||||
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
|
|
||||||
if (_name != null && !_sharedClient) {
|
|
||||||
config.setProperty("option.inbound.nickname", _name);
|
|
||||||
config.setProperty("option.outbound.nickname", _name);
|
|
||||||
}
|
|
||||||
|
|
||||||
config.setProperty("sharedClient", _sharedClient + "");
|
|
||||||
} else if ("client".equals(_type)) {
|
|
||||||
if (_port != null)
|
|
||||||
config.setProperty("listenPort", _port);
|
|
||||||
if (_reachableByOther != null)
|
|
||||||
config.setProperty("interface", _reachableByOther);
|
|
||||||
else
|
|
||||||
config.setProperty("interface", _reachableBy);
|
|
||||||
if (_targetDestination != null)
|
|
||||||
config.setProperty("targetDestination", _targetDestination);
|
|
||||||
|
|
||||||
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
|
|
||||||
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
|
|
||||||
if (_name != null && !_sharedClient) {
|
|
||||||
config.setProperty("option.inbound.nickname", _name);
|
|
||||||
config.setProperty("option.outbound.nickname", _name);
|
|
||||||
}
|
|
||||||
config.setProperty("sharedClient", _sharedClient + "");
|
|
||||||
} else if ("server".equals(_type)) {
|
|
||||||
if (_targetHost != null)
|
|
||||||
config.setProperty("targetHost", _targetHost);
|
|
||||||
if (_targetPort != null)
|
|
||||||
config.setProperty("targetPort", _targetPort);
|
|
||||||
if (_privKeyFile != null)
|
|
||||||
config.setProperty("privKeyFile", _privKeyFile);
|
|
||||||
} else if ("httpserver".equals(_type)) {
|
} else if ("httpserver".equals(_type)) {
|
||||||
if (_targetHost != null)
|
|
||||||
config.setProperty("targetHost", _targetHost);
|
|
||||||
if (_targetPort != null)
|
|
||||||
config.setProperty("targetPort", _targetPort);
|
|
||||||
if (_privKeyFile != null)
|
|
||||||
config.setProperty("privKeyFile", _privKeyFile);
|
|
||||||
if (_spoofedHost != null)
|
if (_spoofedHost != null)
|
||||||
config.setProperty("spoofedHost", _spoofedHost);
|
config.setProperty("spoofedHost", _spoofedHost);
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
@ -14,7 +14,7 @@ String tun = request.getParameter("tunnel");
|
|||||||
} else {
|
} else {
|
||||||
String type = request.getParameter("type");
|
String type = request.getParameter("type");
|
||||||
int curTunnel = -1;
|
int curTunnel = -1;
|
||||||
if ("client".equals(type) || "httpclient".equals(type) || "ircclient".equals(type)) {
|
if (EditBean.isClient(type)) {
|
||||||
%><jsp:include page="editClient.jsp" /><%
|
%><jsp:include page="editClient.jsp" /><%
|
||||||
} else if ("server".equals(type) || "httpserver".equals(type)) {
|
} else if ("server".equals(type) || "httpserver".equals(type)) {
|
||||||
%><jsp:include page="editServer.jsp" /><%
|
%><jsp:include page="editServer.jsp" /><%
|
||||||
|
@ -116,14 +116,14 @@
|
|||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if ("httpclient".equals(editBean.getInternalType(curTunnel))) {
|
<% if ("httpclient".equals(tunnelType)) {
|
||||||
%><div id="destinationField" class="rowItem">
|
%><div id="destinationField" class="rowItem">
|
||||||
<label for="proxyList" accesskey="x">
|
<label for="proxyList" accesskey="x">
|
||||||
Outpro<span class="accessKey">x</span>ies:
|
Outpro<span class="accessKey">x</span>ies:
|
||||||
</label>
|
</label>
|
||||||
<input type="text" size="30" id="proxyList" name="proxyList" title="List of Outproxy I2P destinations" value="<%=editBean.getClientDestination(curTunnel)%>" class="freetext" />
|
<input type="text" size="30" id="proxyList" name="proxyList" title="List of Outproxy I2P destinations" value="<%=editBean.getClientDestination(curTunnel)%>" class="freetext" />
|
||||||
</div>
|
</div>
|
||||||
<% } else {
|
<% } else if ("client".equals(tunnelType) || "ircclient".equals(tunnelType)) {
|
||||||
%><div id="destinationField" class="rowItem">
|
%><div id="destinationField" class="rowItem">
|
||||||
<label for="targetDestination" accesskey="T">
|
<label for="targetDestination" accesskey="T">
|
||||||
<span class="accessKey">T</span>unnel Destination:
|
<span class="accessKey">T</span>unnel Destination:
|
||||||
@ -205,12 +205,12 @@
|
|||||||
<span class="accessKey">V</span>ariance:
|
<span class="accessKey">V</span>ariance:
|
||||||
</label>
|
</label>
|
||||||
<select id="tunnelVariance" name="tunnelVariance" title="Level of Randomization for Tunnel Depth" class="selectbox">
|
<select id="tunnelVariance" name="tunnelVariance" title="Level of Randomization for Tunnel Depth" class="selectbox">
|
||||||
<% int tunnelVariance = editBean.getTunnelVariance(curTunnel, -1);
|
<% int tunnelVariance = editBean.getTunnelVariance(curTunnel, 0);
|
||||||
%><option value="0"<%=(tunnelVariance == 0 ? " selected=\"selected\"" : "") %>>0 hop variance (no randomisation, consistant performance)</option>
|
%><option value="0"<%=(tunnelVariance == 0 ? " selected=\"selected\"" : "") %>>0 hop variance (no randomisation, consistant performance)</option>
|
||||||
<option value="-1"<%=(tunnelVariance == -1 ? " selected=\"selected\"" : "") %>>+/- 0-1 hop variance (standard randomisation, standard performance)</option>
|
|
||||||
<option value="-2"<%=(tunnelVariance == -2 ? " selected=\"selected\"" : "") %>>+/- 0-2 hop variance (high randomisation, variable performance)</option>
|
|
||||||
<option value="1"<%=(tunnelVariance == 1 ? " selected=\"selected\"" : "") %>>+ 0-1 hop variance (medium additive randomisation, subtractive performance)</option>
|
<option value="1"<%=(tunnelVariance == 1 ? " selected=\"selected\"" : "") %>>+ 0-1 hop variance (medium additive randomisation, subtractive performance)</option>
|
||||||
<option value="2"<%=(tunnelVariance == 2 ? " selected=\"selected\"" : "") %>>+ 0-2 hop variance (high additive randomisation, subtractive performance)</option>
|
<option value="2"<%=(tunnelVariance == 2 ? " selected=\"selected\"" : "") %>>+ 0-2 hop variance (high additive randomisation, subtractive performance)</option>
|
||||||
|
<option value="-1"<%=(tunnelVariance == -1 ? " selected=\"selected\"" : "") %>>+/- 0-1 hop variance (standard randomisation, standard performance)</option>
|
||||||
|
<option value="-2"<%=(tunnelVariance == -2 ? " selected=\"selected\"" : "") %>>+/- 0-2 hop variance (not recommended)</option>
|
||||||
<% if (tunnelVariance > 2 || tunnelVariance < -2) {
|
<% if (tunnelVariance > 2 || tunnelVariance < -2) {
|
||||||
%> <option value="<%=tunnelVariance%>" selected="selected"><%= (tunnelVariance > 2 ? "+ " : "+/- ") %>0-<%=tunnelVariance%> hop variance</option>
|
%> <option value="<%=tunnelVariance%>" selected="selected"><%= (tunnelVariance > 2 ? "+ " : "+/- ") %>0-<%=tunnelVariance%> hop variance</option>
|
||||||
<% }
|
<% }
|
||||||
@ -270,6 +270,62 @@
|
|||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="optionsField" class="rowItem">
|
||||||
|
<label for="reduce" accesskey="c">
|
||||||
|
<span class="accessKey">C</span>lose tunnels when idle:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="access" accesskey="c">
|
||||||
|
Enable:
|
||||||
|
</label>
|
||||||
|
<input value="1" type="checkbox" id="startOnLoad" name="close" title="Close Tunnels"<%=(editBean.getClose(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="access" accesskey="c">
|
||||||
|
Generate New Destination Keys On Reopen:
|
||||||
|
</label>
|
||||||
|
<input value="1" type="checkbox" id="startOnLoad" name="newDest" title="New Destination"<%=(editBean.getNewDest(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="reduceTime" accesskey="c">
|
||||||
|
Reduce when idle (minutes):
|
||||||
|
</label>
|
||||||
|
<input type="text" id="port" name="reduceTime" size="4" maxlength="4" title="Reduced Tunnel Idle Time" value="<%=editBean.getReduceTime(curTunnel)%>" class="freetext" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="subdivider">
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="optionsField" class="rowItem">
|
||||||
|
<label for="reduce" accesskey="d">
|
||||||
|
Re<span class="accessKey">d</span>uce tunnel quantity when idle:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="access" accesskey="d">
|
||||||
|
Enable:
|
||||||
|
</label>
|
||||||
|
<input value="1" type="checkbox" id="startOnLoad" name="reduce" title="Reduce Tunnels"<%=(editBean.getReduce(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="reduceCount" accesskey="d">
|
||||||
|
Reduced tunnel count:
|
||||||
|
</label>
|
||||||
|
<input type="text" id="port" name="reduceCount" size="1" maxlength="1" title="Reduced Tunnel Count" value="<%=editBean.getReduceCount(curTunnel)%>" class="freetext" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="reduceTime" accesskey="d">
|
||||||
|
Reduce when idle (minutes):
|
||||||
|
</label>
|
||||||
|
<input type="text" id="port" name="reduceTime" size="4" maxlength="4" title="Reduced Tunnel Idle Time" value="<%=editBean.getReduceTime(curTunnel)%>" class="freetext" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="subdivider">
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="customOptionsField" class="rowItem">
|
<div id="customOptionsField" class="rowItem">
|
||||||
<label for="customOptions" accesskey="u">
|
<label for="customOptions" accesskey="u">
|
||||||
C<span class="accessKey">u</span>stom options:
|
C<span class="accessKey">u</span>stom options:
|
||||||
@ -284,8 +340,10 @@
|
|||||||
<div class="header"></div>
|
<div class="header"></div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="toolbox">
|
<div class="toolbox">
|
||||||
|
<span class="comment">NOTE: If tunnel is currently running, most changes will not take effect until tunnel is stopped and restarted</span>
|
||||||
<input type="hidden" value="true" name="removeConfirm" />
|
<input type="hidden" value="true" name="removeConfirm" />
|
||||||
<button id="controlSave" accesskey="S" class="control" type="submit" name="action" value="Save changes" title="Save Changes"><span class="accessKey">S</span>ave</button><button id="controlDelete" <%=(editBean.allowJS() ? "onclick=\"if (!confirm('Are you sure you want to delete?')) { return false; }\" " : "")%>accesskey="D" class="control" type="submit" name="action" value="Delete this proxy" title="Delete this Proxy"><span class="accessKey">D</span>elete</button>
|
<button id="controlSave" accesskey="S" class="control" type="submit" name="action" value="Save changes" title="Save Changes"><span class="accessKey">S</span>ave</button>
|
||||||
|
<button id="controlDelete" <%=(editBean.allowJS() ? "onclick=\"if (!confirm('Are you sure you want to delete?')) { return false; }\" " : "")%>accesskey="D" class="control" type="submit" name="action" value="Delete this proxy" title="Delete this Proxy"><span class="accessKey">D</span>elete</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -110,7 +110,8 @@
|
|||||||
<label for="spoofedHost" accesskey="W">
|
<label for="spoofedHost" accesskey="W">
|
||||||
<span class="accessKey">W</span>ebsite name:
|
<span class="accessKey">W</span>ebsite name:
|
||||||
</label>
|
</label>
|
||||||
<input type="text" size="20" id="spoofedHost" name="spoofedHost" title="Website Host Name" value="<%=editBean.getSpoofedHost(curTunnel)%>" class="freetext" />
|
<input type="text" size="20" id="targetHost" name="spoofedHost" title="Website Host Name" value="<%=editBean.getSpoofedHost(curTunnel)%>" class="freetext" />
|
||||||
|
<span class="comment">(leave blank for outproxies)</span>
|
||||||
</div>
|
</div>
|
||||||
<% }
|
<% }
|
||||||
%><div id="privKeyField" class="rowItem">
|
%><div id="privKeyField" class="rowItem">
|
||||||
@ -177,12 +178,12 @@
|
|||||||
<span class="accessKey">V</span>ariance:
|
<span class="accessKey">V</span>ariance:
|
||||||
</label>
|
</label>
|
||||||
<select id="tunnelVariance" name="tunnelVariance" title="Level of Randomization for Tunnel Depth" class="selectbox">
|
<select id="tunnelVariance" name="tunnelVariance" title="Level of Randomization for Tunnel Depth" class="selectbox">
|
||||||
<% int tunnelVariance = editBean.getTunnelVariance(curTunnel, -1);
|
<% int tunnelVariance = editBean.getTunnelVariance(curTunnel, 0);
|
||||||
%><option value="0"<%=(tunnelVariance == 0 ? " selected=\"selected\"" : "") %>>0 hop variance (no randomisation, consistant performance)</option>
|
%><option value="0"<%=(tunnelVariance == 0 ? " selected=\"selected\"" : "") %>>0 hop variance (no randomisation, consistant performance)</option>
|
||||||
<option value="-1"<%=(tunnelVariance == -1 ? " selected=\"selected\"" : "") %>>+/- 0-1 hop variance (standard randomisation, standard performance)</option>
|
|
||||||
<option value="-2"<%=(tunnelVariance == -2 ? " selected=\"selected\"" : "") %>>+/- 0-2 hop variance (high randomisation, variable performance)</option>
|
|
||||||
<option value="1"<%=(tunnelVariance == 1 ? " selected=\"selected\"" : "") %>>+ 0-1 hop variance (medium additive randomisation, subtractive performance)</option>
|
<option value="1"<%=(tunnelVariance == 1 ? " selected=\"selected\"" : "") %>>+ 0-1 hop variance (medium additive randomisation, subtractive performance)</option>
|
||||||
<option value="2"<%=(tunnelVariance == 2 ? " selected=\"selected\"" : "") %>>+ 0-2 hop variance (high additive randomisation, subtractive performance)</option>
|
<option value="2"<%=(tunnelVariance == 2 ? " selected=\"selected\"" : "") %>>+ 0-2 hop variance (high additive randomisation, subtractive performance)</option>
|
||||||
|
<option value="-1"<%=(tunnelVariance == -1 ? " selected=\"selected\"" : "") %>>+/- 0-1 hop variance (standard randomisation, standard performance)</option>
|
||||||
|
<option value="-2"<%=(tunnelVariance == -2 ? " selected=\"selected\"" : "") %>>+/- 0-2 hop variance (not recommended)</option>
|
||||||
<% if (tunnelVariance > 2 || tunnelVariance < -2) {
|
<% if (tunnelVariance > 2 || tunnelVariance < -2) {
|
||||||
%> <option value="<%=tunnelVariance%>" selected="selected"><%= (tunnelVariance > 2 ? "+ " : "+/- ") %>0-<%=tunnelVariance%> hop variance</option>
|
%> <option value="<%=tunnelVariance%>" selected="selected"><%= (tunnelVariance > 2 ? "+ " : "+/- ") %>0-<%=tunnelVariance%> hop variance</option>
|
||||||
<% }
|
<% }
|
||||||
@ -242,6 +243,130 @@
|
|||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="optionsField" class="rowItem">
|
||||||
|
<label for="encrypt" accesskey="e">
|
||||||
|
<span class="accessKey">E</span>ncrypt Leaseset:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="encrypt" accesskey="e">
|
||||||
|
Enable:
|
||||||
|
</label>
|
||||||
|
<input value="1" type="checkbox" id="startOnLoad" name="encrypt" title="Encrypt LeaseSet"<%=(editBean.getEncrypt(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
<div id="hostField" class="rowItem">
|
||||||
|
<label for="encrypt" accesskey="e">
|
||||||
|
Leaseset Encryption Key:
|
||||||
|
</label>
|
||||||
|
<input type="text" id="hostField" name="encryptKey" size="60" title="Encrypt Key" value="<%=editBean.getEncryptKey(curTunnel)%>" class="freetext" />
|
||||||
|
<span class="comment">(Users will require this key)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="subdivider">
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="optionsField" class="rowItem">
|
||||||
|
<label for="access" accesskey="s">
|
||||||
|
Restricted Acce<span class="accessKey">s</span>s List:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="access" accesskey="s">
|
||||||
|
Enable:
|
||||||
|
</label>
|
||||||
|
<input value="1" type="checkbox" id="startOnLoad" name="access" title="Enable Access List"<%=(editBean.getAccess(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
<div id="hostField" class="rowItem">
|
||||||
|
<label for="accessList" accesskey="s">
|
||||||
|
Access List:
|
||||||
|
</label>
|
||||||
|
<textarea rows="2" cols="60" id="hostField" title="Access List" wrap="off"><%=editBean.getAccessList(curTunnel)%></textarea>
|
||||||
|
<span class="comment">(Restrict to these clients only)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="subdivider">
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="optionsField" class="rowItem">
|
||||||
|
<label for="reduce" accesskey="d">
|
||||||
|
Re<span class="accessKey">d</span>uce tunnel quantity when idle:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="access" accesskey="d">
|
||||||
|
Enable:
|
||||||
|
</label>
|
||||||
|
<input value="1" type="checkbox" id="startOnLoad" name="reduce" title="Reduce Tunnels"<%=(editBean.getReduce(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="reduceCount" accesskey="d">
|
||||||
|
Reduced tunnel count:
|
||||||
|
</label>
|
||||||
|
<input type="text" id="port" name="reduceCount" size="1" maxlength="1" title="Reduced Tunnel Count" value="<%=editBean.getReduceCount(curTunnel)%>" class="freetext" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="reduceTime" accesskey="d">
|
||||||
|
Reduce when idle (minutes):
|
||||||
|
</label>
|
||||||
|
<input type="text" id="port" name="reduceTime" size="4" maxlength="4" title="Reduced Tunnel Idle Time" value="<%=editBean.getReduceTime(curTunnel)%>" class="freetext" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="subdivider">
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tunnelOptionsField" class="rowItem">
|
||||||
|
<label for="cert" accesskey="c">
|
||||||
|
<span class="accessKey">C</span>ertificate type:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div id="hostField" class="rowItem">
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label>None</label>
|
||||||
|
<input value="0" type="radio" id="startOnLoad" name="cert" title="No Certificate"<%=(editBean.getCert(curTunnel)==0 ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
<span class="comment"></span>
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label>Hashcash (effort)</label>
|
||||||
|
<input value="1" type="radio" id="startOnLoad" name="cert" title="Hashcash Certificate"<%=(editBean.getCert(curTunnel)==1 ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
<input type="text" id="port" name="effort" size="2" title="Hashcash Effort" value="<%=editBean.getEffort(curTunnel)%>" class="freetext" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="force" accesskey="c">
|
||||||
|
Estimate Hashcash Calc Time:
|
||||||
|
</label>
|
||||||
|
<button id="controlSave" accesskey="S" class="control" type="submit" name="action" value="Estimate Calculation Time" title="Estimate Calculation Time">Estimate</button>
|
||||||
|
</div>
|
||||||
|
<div id="hostField" class="rowItem">
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label>Hidden</label>
|
||||||
|
<input value="2" type="radio" id="startOnLoad" name="cert" title="Hidden Certificate"<%=(editBean.getCert(curTunnel)==2 ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
<span class="comment"></span>
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="signer" accesskey="c">
|
||||||
|
Signed (signed by):
|
||||||
|
</label>
|
||||||
|
<input value="3" type="radio" id="startOnLoad" name="cert" title="Signed Certificate"<%=(editBean.getCert(curTunnel)==3 ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
<input type="text" id="port" name="signer" size="50" title="Cert Signer" value="<%=editBean.getSigner(curTunnel)%>" class="freetext" />
|
||||||
|
<span class="comment"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label for="force" accesskey="c">
|
||||||
|
Modify Certificate:
|
||||||
|
</label>
|
||||||
|
<button id="controlSave" accesskey="S" class="control" type="submit" name="action" value="Modify Cert Now" title="Force New Cert Now">Modify</button>
|
||||||
|
<span class="comment">(Tunnel must be stopped first)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="subdivider">
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="customOptionsField" class="rowItem">
|
<div id="customOptionsField" class="rowItem">
|
||||||
<label for="customOptions" accesskey="u">
|
<label for="customOptions" accesskey="u">
|
||||||
C<span class="accessKey">u</span>stom options:
|
C<span class="accessKey">u</span>stom options:
|
||||||
@ -256,8 +381,10 @@
|
|||||||
<div class="header"></div>
|
<div class="header"></div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="toolbox">
|
<div class="toolbox">
|
||||||
|
<span class="comment">NOTE: If tunnel is currently running, most changes will not take effect until tunnel is stopped and restarted</span>
|
||||||
<input type="hidden" value="true" name="removeConfirm" />
|
<input type="hidden" value="true" name="removeConfirm" />
|
||||||
<button id="controlSave" accesskey="S" class="control" type="submit" name="action" value="Save changes" title="Save Changes"><span class="accessKey">S</span>ave</button><button id="controlDelete" <%=(editBean.allowJS() ? "onclick=\"if (!confirm('Are you sure you want to delete?')) { return false; }\" " : "")%>accesskey="D" class="control" type="submit" name="action" value="Delete this proxy" title="Delete this Proxy"><span class="accessKey">D</span>elete</button>
|
<button id="controlSave" accesskey="S" class="control" type="submit" name="action" value="Save changes" title="Save Changes"><span class="accessKey">S</span>ave</button>
|
||||||
|
<button id="controlDelete" <%=(editBean.allowJS() ? "onclick=\"if (!confirm('Are you sure you want to delete?')) { return false; }\" " : "")%>accesskey="D" class="control" type="submit" name="action" value="Delete this proxy" title="Delete this Proxy"><span class="accessKey">D</span>elete</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -112,10 +112,12 @@
|
|||||||
}
|
}
|
||||||
%></div>
|
%></div>
|
||||||
|
|
||||||
|
<% if (!"sockstunnel".equals(indexBean.getInternalType(curClient))) { %>
|
||||||
<div class="destinationField rowItem">
|
<div class="destinationField rowItem">
|
||||||
<label>Destination:</label>
|
<label>Destination:</label>
|
||||||
<input class="freetext" size="40" readonly="readonly" value="<%=indexBean.getClientDestination(curClient)%>" />
|
<input class="freetext" size="40" readonly="readonly" value="<%=indexBean.getClientDestination(curClient)%>" />
|
||||||
</div>
|
</div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
<div class="descriptionField rowItem">
|
<div class="descriptionField rowItem">
|
||||||
<label>Description:</label>
|
<label>Description:</label>
|
||||||
@ -140,6 +142,7 @@
|
|||||||
<option value="client">Standard</option>
|
<option value="client">Standard</option>
|
||||||
<option value="httpclient">HTTP</option>
|
<option value="httpclient">HTTP</option>
|
||||||
<option value="ircclient">IRC</option>
|
<option value="ircclient">IRC</option>
|
||||||
|
<option value="sockstunnel">SOCKS</option>
|
||||||
</select>
|
</select>
|
||||||
<input class="control" type="submit" value="Create" />
|
<input class="control" type="submit" value="Create" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -78,7 +78,6 @@
|
|||||||
<include name="jasper-runtime.jar" />
|
<include name="jasper-runtime.jar" />
|
||||||
<include name="javax.servlet.jar" />
|
<include name="javax.servlet.jar" />
|
||||||
<include name="org.mortbay.jetty.jar" />
|
<include name="org.mortbay.jetty.jar" />
|
||||||
<include name="xercesImpl.jar" />
|
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
<delete dir="jetty-5.1.12" />
|
<delete dir="jetty-5.1.12" />
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
package net.i2p.router.web;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.SessionKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support additions via B64 Destkey, B64 Desthash, or blahblah.i2p
|
||||||
|
*/
|
||||||
|
public class ConfigKeyringHandler extends FormHandler {
|
||||||
|
private String _peer;
|
||||||
|
private String _key;
|
||||||
|
|
||||||
|
protected void processForm() {
|
||||||
|
if ("Add key".equals(_action)) {
|
||||||
|
if (_peer == null || _key == null) {
|
||||||
|
addFormError("You must enter a destination and a key");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Hash h = new Hash();
|
||||||
|
try {
|
||||||
|
h.fromBase64(_peer);
|
||||||
|
} catch (DataFormatException dfe) {}
|
||||||
|
if (h.getData() == null) {
|
||||||
|
try {
|
||||||
|
Destination d = new Destination();
|
||||||
|
d.fromBase64(_peer);
|
||||||
|
h = d.calculateHash();
|
||||||
|
} catch (DataFormatException dfe) {}
|
||||||
|
}
|
||||||
|
if (h.getData() == null) {
|
||||||
|
Destination d = _context.namingService().lookup(_peer);
|
||||||
|
if (d != null)
|
||||||
|
h = d.calculateHash();
|
||||||
|
}
|
||||||
|
SessionKey sk = new SessionKey();
|
||||||
|
try {
|
||||||
|
sk.fromBase64(_key);
|
||||||
|
} catch (DataFormatException dfe) {}
|
||||||
|
if (h.getData() != null && sk.getData() != null) {
|
||||||
|
_context.keyRing().put(h, sk);
|
||||||
|
addFormNotice("Key for " + h.toBase64() + " added to keyring");
|
||||||
|
} else {
|
||||||
|
addFormError("Invalid destination or key");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addFormError("Unsupported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPeer(String peer) { _peer = peer; }
|
||||||
|
public void setKey(String peer) { _key = peer; }
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package net.i2p.router.web;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
|
||||||
|
import net.i2p.router.RouterContext;
|
||||||
|
|
||||||
|
public class ConfigKeyringHelper {
|
||||||
|
private RouterContext _context;
|
||||||
|
/**
|
||||||
|
* Configure this bean to query a particular router context
|
||||||
|
*
|
||||||
|
* @param contextId begging few characters of the routerHash, or null to pick
|
||||||
|
* the first one we come across.
|
||||||
|
*/
|
||||||
|
public void setContextId(String contextId) {
|
||||||
|
try {
|
||||||
|
_context = ContextHelper.getContext(contextId);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigKeyringHelper() {}
|
||||||
|
|
||||||
|
public String getSummary() {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
|
||||||
|
try {
|
||||||
|
_context.keyRing().renderStatusHTML(new OutputStreamWriter(baos));
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
ioe.printStackTrace();
|
||||||
|
}
|
||||||
|
return new String(baos.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ public class NetDbHelper {
|
|||||||
private RouterContext _context;
|
private RouterContext _context;
|
||||||
private Writer _out;
|
private Writer _out;
|
||||||
private String _routerPrefix;
|
private String _routerPrefix;
|
||||||
|
private boolean _full = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure this bean to query a particular router context
|
* Configure this bean to query a particular router context
|
||||||
@ -30,6 +31,7 @@ public class NetDbHelper {
|
|||||||
|
|
||||||
public void setWriter(Writer writer) { _out = writer; }
|
public void setWriter(Writer writer) { _out = writer; }
|
||||||
public void setRouter(String r) { _routerPrefix = r; }
|
public void setRouter(String r) { _routerPrefix = r; }
|
||||||
|
public void setFull(String f) { _full = "1".equals(f); };
|
||||||
|
|
||||||
public String getNetDbSummary() {
|
public String getNetDbSummary() {
|
||||||
try {
|
try {
|
||||||
@ -37,14 +39,14 @@ public class NetDbHelper {
|
|||||||
if (_routerPrefix != null)
|
if (_routerPrefix != null)
|
||||||
_context.netDb().renderRouterInfoHTML(_out, _routerPrefix);
|
_context.netDb().renderRouterInfoHTML(_out, _routerPrefix);
|
||||||
else
|
else
|
||||||
_context.netDb().renderStatusHTML(_out);
|
_context.netDb().renderStatusHTML(_out, _full);
|
||||||
return "";
|
return "";
|
||||||
} else {
|
} else {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
|
||||||
if (_routerPrefix != null)
|
if (_routerPrefix != null)
|
||||||
_context.netDb().renderRouterInfoHTML(new OutputStreamWriter(baos), _routerPrefix);
|
_context.netDb().renderRouterInfoHTML(new OutputStreamWriter(baos), _routerPrefix);
|
||||||
else
|
else
|
||||||
_context.netDb().renderStatusHTML(new OutputStreamWriter(baos));
|
_context.netDb().renderStatusHTML(new OutputStreamWriter(baos), _full);
|
||||||
return new String(baos.toByteArray());
|
return new String(baos.toByteArray());
|
||||||
}
|
}
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
|
@ -244,7 +244,7 @@ public class SummaryHelper {
|
|||||||
*/
|
*/
|
||||||
public String getInboundSecondKBps() {
|
public String getInboundSecondKBps() {
|
||||||
if (_context == null)
|
if (_context == null)
|
||||||
return "0.0";
|
return "0";
|
||||||
double kbps = _context.bandwidthLimiter().getReceiveBps()/1024d;
|
double kbps = _context.bandwidthLimiter().getReceiveBps()/1024d;
|
||||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||||
return fmt.format(kbps);
|
return fmt.format(kbps);
|
||||||
@ -256,7 +256,7 @@ public class SummaryHelper {
|
|||||||
*/
|
*/
|
||||||
public String getOutboundSecondKBps() {
|
public String getOutboundSecondKBps() {
|
||||||
if (_context == null)
|
if (_context == null)
|
||||||
return "0.0";
|
return "0";
|
||||||
double kbps = _context.bandwidthLimiter().getSendBps()/1024d;
|
double kbps = _context.bandwidthLimiter().getSendBps()/1024d;
|
||||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||||
return fmt.format(kbps);
|
return fmt.format(kbps);
|
||||||
@ -269,10 +269,10 @@ public class SummaryHelper {
|
|||||||
*/
|
*/
|
||||||
public String getInboundFiveMinuteKBps() {
|
public String getInboundFiveMinuteKBps() {
|
||||||
if (_context == null)
|
if (_context == null)
|
||||||
return "0.0";
|
return "0";
|
||||||
|
|
||||||
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
|
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
|
||||||
if (receiveRate == null) return "0.0";
|
if (receiveRate == null) return "0";
|
||||||
Rate rate = receiveRate.getRate(5*60*1000);
|
Rate rate = receiveRate.getRate(5*60*1000);
|
||||||
double kbps = rate.getAverageValue()/1024;
|
double kbps = rate.getAverageValue()/1024;
|
||||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||||
@ -286,10 +286,10 @@ public class SummaryHelper {
|
|||||||
*/
|
*/
|
||||||
public String getOutboundFiveMinuteKBps() {
|
public String getOutboundFiveMinuteKBps() {
|
||||||
if (_context == null)
|
if (_context == null)
|
||||||
return "0.0";
|
return "0";
|
||||||
|
|
||||||
RateStat receiveRate = _context.statManager().getRate("bw.sendRate");
|
RateStat receiveRate = _context.statManager().getRate("bw.sendRate");
|
||||||
if (receiveRate == null) return "0.0";
|
if (receiveRate == null) return "0";
|
||||||
Rate rate = receiveRate.getRate(5*60*1000);
|
Rate rate = receiveRate.getRate(5*60*1000);
|
||||||
double kbps = rate.getAverageValue()/1024;
|
double kbps = rate.getAverageValue()/1024;
|
||||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||||
@ -303,10 +303,10 @@ public class SummaryHelper {
|
|||||||
*/
|
*/
|
||||||
public String getInboundLifetimeKBps() {
|
public String getInboundLifetimeKBps() {
|
||||||
if (_context == null)
|
if (_context == null)
|
||||||
return "0.0";
|
return "0";
|
||||||
|
|
||||||
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
|
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
|
||||||
if (receiveRate == null) return "0.0";
|
if (receiveRate == null) return "0";
|
||||||
double kbps = receiveRate.getLifetimeAverageValue()/1024;
|
double kbps = receiveRate.getLifetimeAverageValue()/1024;
|
||||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||||
return fmt.format(kbps);
|
return fmt.format(kbps);
|
||||||
@ -319,10 +319,10 @@ public class SummaryHelper {
|
|||||||
*/
|
*/
|
||||||
public String getOutboundLifetimeKBps() {
|
public String getOutboundLifetimeKBps() {
|
||||||
if (_context == null)
|
if (_context == null)
|
||||||
return "0.0";
|
return "0";
|
||||||
|
|
||||||
RateStat sendRate = _context.statManager().getRate("bw.sendRate");
|
RateStat sendRate = _context.statManager().getRate("bw.sendRate");
|
||||||
if (sendRate == null) return "0.0";
|
if (sendRate == null) return "0";
|
||||||
double kbps = sendRate.getLifetimeAverageValue()/1024;
|
double kbps = sendRate.getLifetimeAverageValue()/1024;
|
||||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||||
return fmt.format(kbps);
|
return fmt.format(kbps);
|
||||||
@ -335,11 +335,11 @@ public class SummaryHelper {
|
|||||||
*/
|
*/
|
||||||
public String getInboundTransferred() {
|
public String getInboundTransferred() {
|
||||||
if (_context == null)
|
if (_context == null)
|
||||||
return "0.0";
|
return "0";
|
||||||
|
|
||||||
long received = _context.bandwidthLimiter().getTotalAllocatedInboundBytes();
|
long received = _context.bandwidthLimiter().getTotalAllocatedInboundBytes();
|
||||||
|
|
||||||
return getTransferred(received);
|
return DataHelper.formatSize(received) + 'B';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -349,40 +349,10 @@ public class SummaryHelper {
|
|||||||
*/
|
*/
|
||||||
public String getOutboundTransferred() {
|
public String getOutboundTransferred() {
|
||||||
if (_context == null)
|
if (_context == null)
|
||||||
return "0.0";
|
return "0";
|
||||||
|
|
||||||
long sent = _context.bandwidthLimiter().getTotalAllocatedOutboundBytes();
|
long sent = _context.bandwidthLimiter().getTotalAllocatedOutboundBytes();
|
||||||
return getTransferred(sent);
|
return DataHelper.formatSize(sent) + 'B';
|
||||||
}
|
|
||||||
|
|
||||||
private static String getTransferred(long bytes) {
|
|
||||||
double val = bytes;
|
|
||||||
int scale = 0;
|
|
||||||
if (bytes > 1024*1024*1024) {
|
|
||||||
// gigs transferred
|
|
||||||
scale = 3;
|
|
||||||
val /= (double)(1024*1024*1024);
|
|
||||||
} else if (bytes > 1024*1024) {
|
|
||||||
// megs transferred
|
|
||||||
scale = 2;
|
|
||||||
val /= (double)(1024*1024);
|
|
||||||
} else if (bytes > 1024) {
|
|
||||||
// kbytes transferred
|
|
||||||
scale = 1;
|
|
||||||
val /= (double)1024;
|
|
||||||
} else {
|
|
||||||
scale = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
|
||||||
|
|
||||||
String str = fmt.format(val);
|
|
||||||
switch (scale) {
|
|
||||||
case 1: return str + "KB";
|
|
||||||
case 2: return str + "MB";
|
|
||||||
case 3: return str + "GB";
|
|
||||||
default: return bytes + "bytes";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
58
apps/routerconsole/jsp/configkeyring.jsp
Normal file
58
apps/routerconsole/jsp/configkeyring.jsp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<%@page contentType="text/html"%>
|
||||||
|
<%@page pageEncoding="UTF-8"%>
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
|
||||||
|
<html><head>
|
||||||
|
<title>I2P Router Console - config keyring</title>
|
||||||
|
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||||
|
</head><body>
|
||||||
|
|
||||||
|
<%@include file="nav.jsp" %>
|
||||||
|
<%@include file="summary.jsp" %>
|
||||||
|
|
||||||
|
<div class="main" id="main">
|
||||||
|
<%@include file="confignav.jsp" %>
|
||||||
|
|
||||||
|
<jsp:useBean class="net.i2p.router.web.ConfigKeyringHandler" id="formhandler" scope="request" />
|
||||||
|
<jsp:setProperty name="formhandler" property="*" />
|
||||||
|
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||||
|
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
|
||||||
|
<i><jsp:getProperty name="formhandler" property="notices" /></i>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<jsp:useBean class="net.i2p.router.web.ConfigKeyringHelper" id="keyringhelper" scope="request" />
|
||||||
|
<jsp:setProperty name="keyringhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<h2>Keyring</h2>
|
||||||
|
The router keyring is used to decrypt encrypted leaseSets.
|
||||||
|
The keyring may contain keys for local or remote encrypted destinations.
|
||||||
|
<p><jsp:getProperty name="keyringhelper" property="summary" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<form action="configkeyring.jsp" method="POST">
|
||||||
|
<% String prev = System.getProperty("net.i2p.router.web.ConfigKeyringHandler.nonce");
|
||||||
|
if (prev != null) System.setProperty("net.i2p.router.web.ConfigKeyringHandler.noncePrev", prev);
|
||||||
|
System.setProperty("net.i2p.router.web.ConfigKeyringHandler.nonce", new java.util.Random().nextLong()+""); %>
|
||||||
|
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigKeyringHandler.nonce")%>" />
|
||||||
|
<h2>Manual Keyring Addition</h2>
|
||||||
|
Enter keys for encrypted remote destinations here.
|
||||||
|
Keys for local destinations must be entered on the <a href="i2ptunnel/index.jsp">I2PTunnel page</a>.
|
||||||
|
<p>
|
||||||
|
<table>
|
||||||
|
<tr><td>Dest. name, hash, or full key:
|
||||||
|
<td><textarea name="peer" cols="44" rows="1" wrap="off"></textarea>
|
||||||
|
<tr><td align="right">Session Key:
|
||||||
|
<td><input type="text" size="55" name="key" />
|
||||||
|
<tr><td><td><input type="submit" name="action" value="Add key" />
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -10,6 +10,8 @@
|
|||||||
%>Clients | <% } else { %><a href="configclients.jsp">Clients</a> | <% }
|
%>Clients | <% } else { %><a href="configclients.jsp">Clients</a> | <% }
|
||||||
if (request.getRequestURI().indexOf("configpeer.jsp") != -1) {
|
if (request.getRequestURI().indexOf("configpeer.jsp") != -1) {
|
||||||
%>Peers | <% } else { %><a href="configpeer.jsp">Peers</a> | <% }
|
%>Peers | <% } else { %><a href="configpeer.jsp">Peers</a> | <% }
|
||||||
|
if (request.getRequestURI().indexOf("configkeyring.jsp") != -1) {
|
||||||
|
%>Keyring | <% } else { %><a href="configkeyring.jsp">Keyring</a> | <% }
|
||||||
if (request.getRequestURI().indexOf("configlogging.jsp") != -1) {
|
if (request.getRequestURI().indexOf("configlogging.jsp") != -1) {
|
||||||
%>Logging | <% } else { %><a href="configlogging.jsp">Logging</a> | <% }
|
%>Logging | <% } else { %><a href="configlogging.jsp">Logging</a> | <% }
|
||||||
if (request.getRequestURI().indexOf("configstats.jsp") != -1) {
|
if (request.getRequestURI().indexOf("configstats.jsp") != -1) {
|
||||||
|
@ -34,9 +34,8 @@ licenses and dependencies. This webpage is being served as part of the I2P rout
|
|||||||
client application, which is built off a trimmed down <a href="http://jetty.mortbay.com/jetty/index.html">Jetty</a>
|
client application, which is built off a trimmed down <a href="http://jetty.mortbay.com/jetty/index.html">Jetty</a>
|
||||||
instance (trimmed down, as in, we do not include the demo apps or other add-ons, and we simplify configuration),
|
instance (trimmed down, as in, we do not include the demo apps or other add-ons, and we simplify configuration),
|
||||||
allowing you to deploy standard JSP/Servlet web applications into your router. Jetty in turn makes use of
|
allowing you to deploy standard JSP/Servlet web applications into your router. Jetty in turn makes use of
|
||||||
Apache's javax.servlet (javax.servlet.jar) implementation, as well as their xerces-j XML parser (xerces.jar).
|
Apache's javax.servlet (javax.servlet.jar) implementation.
|
||||||
Their XML parser requires the Sun XML APIs (JAXP) which is included in binary form (xml-apis.jar) as required
|
This product includes software developed by the Apache Software Foundation
|
||||||
by their binary code license. This product includes software developed by the Apache Software Foundation
|
|
||||||
(http://www.apache.org/). </p>
|
(http://www.apache.org/). </p>
|
||||||
|
|
||||||
<p>Another application you can see on this webpage is <a href="http://www.i2p2.i2p/i2ptunnel">I2PTunnel</a>
|
<p>Another application you can see on this webpage is <a href="http://www.i2p2.i2p/i2ptunnel">I2PTunnel</a>
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
<jsp:useBean class="net.i2p.router.web.NetDbHelper" id="netdbHelper" scope="request" />
|
<jsp:useBean class="net.i2p.router.web.NetDbHelper" id="netdbHelper" scope="request" />
|
||||||
<jsp:setProperty name="netdbHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
<jsp:setProperty name="netdbHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||||
<jsp:setProperty name="netdbHelper" property="writer" value="<%=out%>" />
|
<jsp:setProperty name="netdbHelper" property="writer" value="<%=out%>" />
|
||||||
|
<jsp:setProperty name="netdbHelper" property="full" value="<%=request.getParameter("f")%>" />
|
||||||
<jsp:setProperty name="netdbHelper" property="router" value="<%=request.getParameter("r")%>" />
|
<jsp:setProperty name="netdbHelper" property="router" value="<%=request.getParameter("r")%>" />
|
||||||
<jsp:getProperty name="netdbHelper" property="netDbSummary" />
|
<jsp:getProperty name="netdbHelper" property="netDbSummary" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -45,6 +45,7 @@ public class Connection {
|
|||||||
private long _congestionWindowEnd;
|
private long _congestionWindowEnd;
|
||||||
private long _highestAckedThrough;
|
private long _highestAckedThrough;
|
||||||
private boolean _isInbound;
|
private boolean _isInbound;
|
||||||
|
private boolean _updatedShareOpts;
|
||||||
/** Packet ID (Long) to PacketLocal for sent but unacked packets */
|
/** Packet ID (Long) to PacketLocal for sent but unacked packets */
|
||||||
private Map _outboundPackets;
|
private Map _outboundPackets;
|
||||||
private PacketQueue _outboundQueue;
|
private PacketQueue _outboundQueue;
|
||||||
@ -120,6 +121,7 @@ public class Connection {
|
|||||||
_activeResends = 0;
|
_activeResends = 0;
|
||||||
_resetSentOn = -1;
|
_resetSentOn = -1;
|
||||||
_isInbound = false;
|
_isInbound = false;
|
||||||
|
_updatedShareOpts = false;
|
||||||
_connectionEvent = new ConEvent();
|
_connectionEvent = new ConEvent();
|
||||||
_hardDisconnected = false;
|
_hardDisconnected = false;
|
||||||
_context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
_context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||||
@ -586,6 +588,8 @@ public class Connection {
|
|||||||
if (_remotePeerSet) throw new RuntimeException("Remote peer already set [" + _remotePeer + ", " + peer + "]");
|
if (_remotePeerSet) throw new RuntimeException("Remote peer already set [" + _remotePeer + ", " + peer + "]");
|
||||||
_remotePeerSet = true;
|
_remotePeerSet = true;
|
||||||
_remotePeer = peer;
|
_remotePeer = peer;
|
||||||
|
// now that we know who the other end is, get the rtt etc. from the cache
|
||||||
|
_connectionManager.updateOptsFromShare(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean _sendStreamIdSet = false;
|
private boolean _sendStreamIdSet = false;
|
||||||
@ -710,6 +714,12 @@ public class Connection {
|
|||||||
public long getCloseReceivedOn() { return _closeReceivedOn; }
|
public long getCloseReceivedOn() { return _closeReceivedOn; }
|
||||||
public void setCloseReceivedOn(long when) { _closeReceivedOn = when; }
|
public void setCloseReceivedOn(long when) { _closeReceivedOn = when; }
|
||||||
|
|
||||||
|
public void updateShareOpts() {
|
||||||
|
if (_closeSentOn > 0 && !_updatedShareOpts) {
|
||||||
|
_connectionManager.updateShareOpts(this);
|
||||||
|
_updatedShareOpts = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
public void incrementUnackedPacketsReceived() { _unackedPacketsReceived++; }
|
public void incrementUnackedPacketsReceived() { _unackedPacketsReceived++; }
|
||||||
public int getUnackedPacketsReceived() { return _unackedPacketsReceived; }
|
public int getUnackedPacketsReceived() { return _unackedPacketsReceived; }
|
||||||
/** how many packets have we sent but not yet received an ACK for?
|
/** how many packets have we sent but not yet received an ACK for?
|
||||||
@ -998,7 +1008,7 @@ public class Connection {
|
|||||||
/**
|
/**
|
||||||
* Coordinate the resends of a given packet
|
* Coordinate the resends of a given packet
|
||||||
*/
|
*/
|
||||||
private class ResendPacketEvent implements SimpleTimer.TimedEvent {
|
public class ResendPacketEvent implements SimpleTimer.TimedEvent {
|
||||||
private PacketLocal _packet;
|
private PacketLocal _packet;
|
||||||
private long _nextSendTime;
|
private long _nextSendTime;
|
||||||
public ResendPacketEvent(PacketLocal packet, long sendTime) {
|
public ResendPacketEvent(PacketLocal packet, long sendTime) {
|
||||||
@ -1104,26 +1114,6 @@ public class Connection {
|
|||||||
_context.sessionKeyManager().failTags(_remotePeer.getPublicKey());
|
_context.sessionKeyManager().failTags(_remotePeer.getPublicKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numSends - 1 <= _options.getMaxResends()) {
|
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Resend packet " + _packet + " time " + numSends +
|
|
||||||
" activeResends: " + _activeResends +
|
|
||||||
" (wsize "
|
|
||||||
+ newWindowSize + " lifetime "
|
|
||||||
+ (_context.clock().now() - _packet.getCreatedOn()) + "ms)");
|
|
||||||
_outboundQueue.enqueue(_packet);
|
|
||||||
_lastSendTime = _context.clock().now();
|
|
||||||
}
|
|
||||||
|
|
||||||
// acked during resending (... or somethin')
|
|
||||||
if ( (_packet.getAckTime() > 0) && (_packet.getNumSends() > 1) ) {
|
|
||||||
_activeResends--;
|
|
||||||
synchronized (_outboundPackets) {
|
|
||||||
_outboundPackets.notifyAll();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numSends - 1 > _options.getMaxResends()) {
|
if (numSends - 1 > _options.getMaxResends()) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Too many resends");
|
_log.debug("Too many resends");
|
||||||
@ -1137,11 +1127,32 @@ public class Connection {
|
|||||||
long timeout = rto << (numSends-1);
|
long timeout = rto << (numSends-1);
|
||||||
if ( (timeout > MAX_RESEND_DELAY) || (timeout <= 0) )
|
if ( (timeout > MAX_RESEND_DELAY) || (timeout <= 0) )
|
||||||
timeout = MAX_RESEND_DELAY;
|
timeout = MAX_RESEND_DELAY;
|
||||||
|
// set this before enqueue() as it passes it on to the router
|
||||||
|
_nextSendTime = timeout + _context.clock().now();
|
||||||
|
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Resend packet " + _packet + " time " + numSends +
|
||||||
|
" activeResends: " + _activeResends +
|
||||||
|
" (wsize "
|
||||||
|
+ newWindowSize + " lifetime "
|
||||||
|
+ (_context.clock().now() - _packet.getCreatedOn()) + "ms)");
|
||||||
|
_outboundQueue.enqueue(_packet);
|
||||||
|
_lastSendTime = _context.clock().now();
|
||||||
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Scheduling resend in " + timeout + "ms for " + _packet);
|
_log.debug("Scheduling resend in " + timeout + "ms for " + _packet);
|
||||||
RetransmissionTimer.getInstance().addEvent(ResendPacketEvent.this, timeout);
|
RetransmissionTimer.getInstance().addEvent(ResendPacketEvent.this, timeout);
|
||||||
_nextSendTime = timeout + _context.clock().now();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// acked during resending (... or somethin')
|
||||||
|
if ( (_packet.getAckTime() > 0) && (_packet.getNumSends() > 1) ) {
|
||||||
|
_activeResends--;
|
||||||
|
synchronized (_outboundPackets) {
|
||||||
|
_outboundPackets.notifyAll();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
|
@ -30,6 +30,7 @@ public class ConnectionManager {
|
|||||||
private PacketQueue _outboundQueue;
|
private PacketQueue _outboundQueue;
|
||||||
private SchedulerChooser _schedulerChooser;
|
private SchedulerChooser _schedulerChooser;
|
||||||
private ConnectionPacketHandler _conPacketHandler;
|
private ConnectionPacketHandler _conPacketHandler;
|
||||||
|
private TCBShare _tcbShare;
|
||||||
/** Inbound stream ID (Long) to Connection map */
|
/** Inbound stream ID (Long) to Connection map */
|
||||||
private Map _connectionByInboundId;
|
private Map _connectionByInboundId;
|
||||||
/** Ping ID (Long) to PingRequest */
|
/** Ping ID (Long) to PingRequest */
|
||||||
@ -52,6 +53,7 @@ public class ConnectionManager {
|
|||||||
_connectionHandler = new ConnectionHandler(context, this);
|
_connectionHandler = new ConnectionHandler(context, this);
|
||||||
_schedulerChooser = new SchedulerChooser(context);
|
_schedulerChooser = new SchedulerChooser(context);
|
||||||
_conPacketHandler = new ConnectionPacketHandler(context);
|
_conPacketHandler = new ConnectionPacketHandler(context);
|
||||||
|
_tcbShare = new TCBShare(context);
|
||||||
_session = session;
|
_session = session;
|
||||||
session.setSessionListener(_messageHandler);
|
session.setSessionListener(_messageHandler);
|
||||||
_outboundQueue = new PacketQueue(context, session, this);
|
_outboundQueue = new PacketQueue(context, session, this);
|
||||||
@ -127,6 +129,7 @@ public class ConnectionManager {
|
|||||||
*/
|
*/
|
||||||
public Connection receiveConnection(Packet synPacket) {
|
public Connection receiveConnection(Packet synPacket) {
|
||||||
Connection con = new Connection(_context, this, _schedulerChooser, _outboundQueue, _conPacketHandler, new ConnectionOptions(_defaultOptions));
|
Connection con = new Connection(_context, this, _schedulerChooser, _outboundQueue, _conPacketHandler, new ConnectionOptions(_defaultOptions));
|
||||||
|
_tcbShare.updateOptsFromShare(con);
|
||||||
con.setInbound();
|
con.setInbound();
|
||||||
long receiveId = _context.random().nextLong(Packet.MAX_STREAM_ID-1)+1;
|
long receiveId = _context.random().nextLong(Packet.MAX_STREAM_ID-1)+1;
|
||||||
boolean reject = false;
|
boolean reject = false;
|
||||||
@ -277,6 +280,8 @@ public class ConnectionManager {
|
|||||||
public ConnectionHandler getConnectionHandler() { return _connectionHandler; }
|
public ConnectionHandler getConnectionHandler() { return _connectionHandler; }
|
||||||
public I2PSession getSession() { return _session; }
|
public I2PSession getSession() { return _session; }
|
||||||
public PacketQueue getPacketQueue() { return _outboundQueue; }
|
public PacketQueue getPacketQueue() { return _outboundQueue; }
|
||||||
|
public void updateOptsFromShare(Connection con) { _tcbShare.updateOptsFromShare(con); }
|
||||||
|
public void updateShareOpts(Connection con) { _tcbShare.updateShareOpts(con); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Something b0rked hard, so kill all of our connections without mercy.
|
* Something b0rked hard, so kill all of our connections without mercy.
|
||||||
@ -292,6 +297,7 @@ public class ConnectionManager {
|
|||||||
_connectionByInboundId.clear();
|
_connectionByInboundId.clear();
|
||||||
_connectionLock.notifyAll();
|
_connectionLock.notifyAll();
|
||||||
}
|
}
|
||||||
|
_tcbShare.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -213,6 +213,10 @@ public class ConnectionPacketHandler {
|
|||||||
packet.releasePayload();
|
packet.releasePayload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update the TCB Cache now that we've processed the acks and updated our rtt etc.
|
||||||
|
if (isNew && packet.isFlagSet(Packet.FLAG_CLOSE) && packet.isFlagSet(Packet.FLAG_SIGNATURE_INCLUDED))
|
||||||
|
con.updateShareOpts();
|
||||||
|
|
||||||
//if (choke)
|
//if (choke)
|
||||||
// con.fastRetransmit();
|
// con.fastRetransmit();
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,15 @@ class PacketQueue {
|
|||||||
|
|
||||||
// this should not block!
|
// this should not block!
|
||||||
begin = _context.clock().now();
|
begin = _context.clock().now();
|
||||||
|
long expires = 0;
|
||||||
|
Connection.ResendPacketEvent rpe = (Connection.ResendPacketEvent) packet.getResendEvent();
|
||||||
|
if (rpe != null)
|
||||||
|
// we want the router to expire it a little before we do,
|
||||||
|
// so if we retransmit it will use a new tunnel/lease combo
|
||||||
|
expires = rpe.getNextSendTime() - 500;
|
||||||
|
if (expires > 0)
|
||||||
|
sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, expires);
|
||||||
|
else
|
||||||
sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent);
|
sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent);
|
||||||
end = _context.clock().now();
|
end = _context.clock().now();
|
||||||
|
|
||||||
|
137
apps/streaming/java/src/net/i2p/client/streaming/TCBShare.java
Normal file
137
apps/streaming/java/src/net/i2p/client/streaming/TCBShare.java
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package net.i2p.client.streaming;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.SimpleTimer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Share important TCP Control Block parameters across Connections
|
||||||
|
* to the same remote peer.
|
||||||
|
* This is intended for "temporal" sharing at connection open/close time,
|
||||||
|
* not "ensemble" sharing during a connection. Ref. RFC 2140.
|
||||||
|
*
|
||||||
|
* There is a TCB share per ConnectionManager (i.e. per local Destination)
|
||||||
|
* so that there is no information leakage to other Destinations on the
|
||||||
|
* same router.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class TCBShare {
|
||||||
|
private I2PAppContext _context;
|
||||||
|
private Log _log;
|
||||||
|
private Map<Destination, Entry> _cache;
|
||||||
|
private CleanEvent _cleaner;
|
||||||
|
|
||||||
|
private static final long EXPIRE_TIME = 30*60*1000;
|
||||||
|
private static final long CLEAN_TIME = 10*60*1000;
|
||||||
|
private static final double RTT_DAMPENING = 0.75;
|
||||||
|
private static final double WDW_DAMPENING = 0.75;
|
||||||
|
private static final int MAX_RTT = ((int) Connection.MAX_RESEND_DELAY) / 2;
|
||||||
|
private static final int MAX_WINDOW_SIZE = Connection.MAX_WINDOW_SIZE / 4;
|
||||||
|
|
||||||
|
public TCBShare(I2PAppContext ctx) {
|
||||||
|
_context = ctx;
|
||||||
|
_log = ctx.logManager().getLog(TCBShare.class);
|
||||||
|
_cache = new ConcurrentHashMap(4);
|
||||||
|
_cleaner = new CleanEvent();
|
||||||
|
SimpleTimer.getInstance().addEvent(_cleaner, CLEAN_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
SimpleTimer.getInstance().removeEvent(_cleaner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateOptsFromShare(Connection con) {
|
||||||
|
Destination dest = con.getRemotePeer();
|
||||||
|
if (dest == null)
|
||||||
|
return;
|
||||||
|
ConnectionOptions opts = con.getOptions();
|
||||||
|
if (opts == null)
|
||||||
|
return;
|
||||||
|
Entry e = _cache.get(dest);
|
||||||
|
if (e == null || e.isExpired())
|
||||||
|
return;
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("From cache: " +
|
||||||
|
con.getSession().getMyDestination().calculateHash().toBase64().substring(0, 4) +
|
||||||
|
'-' +
|
||||||
|
dest.calculateHash().toBase64().substring(0, 4) +
|
||||||
|
" RTT: " + e.getRTT() + " wdw: " + e.getWindowSize());
|
||||||
|
opts.setRTT(e.getRTT());
|
||||||
|
opts.setWindowSize(e.getWindowSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateShareOpts(Connection con) {
|
||||||
|
Destination dest = con.getRemotePeer();
|
||||||
|
if (dest == null)
|
||||||
|
return;
|
||||||
|
if (con.getAckedPackets() <= 0)
|
||||||
|
return;
|
||||||
|
ConnectionOptions opts = con.getOptions();
|
||||||
|
if (opts == null)
|
||||||
|
return;
|
||||||
|
int old = -1;
|
||||||
|
int oldw = -1;
|
||||||
|
Entry e = _cache.get(dest);
|
||||||
|
if (e == null || e.isExpired()) {
|
||||||
|
e = new Entry(opts.getRTT(), opts.getWindowSize());
|
||||||
|
_cache.put(dest, e);
|
||||||
|
} else {
|
||||||
|
old = e.getRTT();
|
||||||
|
oldw = e.getWindowSize();
|
||||||
|
e.setRTT(opts.getRTT());
|
||||||
|
e.setWindowSize(opts.getWindowSize());
|
||||||
|
}
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("To cache: " +
|
||||||
|
con.getSession().getMyDestination().calculateHash().toBase64().substring(0, 4) +
|
||||||
|
'-' +
|
||||||
|
dest.calculateHash().toBase64().substring(0, 4) +
|
||||||
|
" old: " + old + " con: " + opts.getRTT() + " new: " + e.getRTT() +
|
||||||
|
" oldw: " + oldw + " conw: " + opts.getWindowSize() + " neww: " + e.getWindowSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Entry {
|
||||||
|
int _rtt;
|
||||||
|
int _wdw;
|
||||||
|
long _updated;
|
||||||
|
|
||||||
|
public Entry(int ms, int wdw) {
|
||||||
|
_rtt = ms;
|
||||||
|
_wdw = wdw;
|
||||||
|
_updated = _context.clock().now();
|
||||||
|
}
|
||||||
|
public int getRTT() { return _rtt; }
|
||||||
|
public void setRTT(int ms) {
|
||||||
|
_rtt = (int)(RTT_DAMPENING*_rtt + (1-RTT_DAMPENING)*ms);
|
||||||
|
if (_rtt > MAX_RTT)
|
||||||
|
_rtt = MAX_RTT;
|
||||||
|
_updated = _context.clock().now();
|
||||||
|
}
|
||||||
|
public int getWindowSize() { return _wdw; }
|
||||||
|
public void setWindowSize(int wdw) {
|
||||||
|
_wdw = (int)(0.5 + WDW_DAMPENING*_wdw + (1-WDW_DAMPENING)*wdw);
|
||||||
|
if (_wdw > MAX_WINDOW_SIZE)
|
||||||
|
_wdw = MAX_WINDOW_SIZE;
|
||||||
|
_updated = _context.clock().now();
|
||||||
|
}
|
||||||
|
public boolean isExpired() {
|
||||||
|
return _updated < _context.clock().now() - EXPIRE_TIME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CleanEvent implements SimpleTimer.TimedEvent {
|
||||||
|
public CleanEvent() {}
|
||||||
|
public void timeReached() {
|
||||||
|
for (Iterator iter = _cache.keySet().iterator(); iter.hasNext(); ) {
|
||||||
|
if (_cache.get(iter.next()).isExpired())
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
SimpleTimer.getInstance().addEvent(CleanEvent.this, CLEAN_TIME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
build.xml
13
build.xml
@ -60,7 +60,6 @@
|
|||||||
<copy file="apps/jetty/jettylib/jasper-runtime.jar" todir="build/" />
|
<copy file="apps/jetty/jettylib/jasper-runtime.jar" todir="build/" />
|
||||||
<copy file="apps/jetty/jettylib/commons-logging.jar" todir="build/" />
|
<copy file="apps/jetty/jettylib/commons-logging.jar" todir="build/" />
|
||||||
<copy file="apps/jetty/jettylib/commons-el.jar" todir="build/" />
|
<copy file="apps/jetty/jettylib/commons-el.jar" todir="build/" />
|
||||||
<copy file="apps/jetty/jettylib/xercesImpl.jar" todir="build/" />
|
|
||||||
<copy file="apps/jetty/jettylib/javax.servlet.jar" todir="build/" />
|
<copy file="apps/jetty/jettylib/javax.servlet.jar" todir="build/" />
|
||||||
</target>
|
</target>
|
||||||
<target name="buildexe">
|
<target name="buildexe">
|
||||||
@ -87,7 +86,7 @@
|
|||||||
<jar destfile="./build/launchi2p.jar">
|
<jar destfile="./build/launchi2p.jar">
|
||||||
<manifest>
|
<manifest>
|
||||||
<attribute name="Main-Class" value="net.i2p.router.RouterLaunch" />
|
<attribute name="Main-Class" value="net.i2p.router.RouterLaunch" />
|
||||||
<attribute name="Class-Path" value="lib/i2p.jar lib/router.jar lib/jbigi.jar lib/BOB.jar lib/sam.jar lib/mstreaming.jar lib/streaming.jar lib/routerconsole.jar lib/i2ptunnel.jar lib/org.mortbay.jetty.jar lib/javax.servlet.jar lib/jasper-compiler.jar lib/jasper-runtime.jar lib/commons-logging.jar lib/commons-el.jar lib/ant.jar lib/xercesImpl.jar lib/wrapper.jar lib/systray.jar lib/systray4j.jar" />
|
<attribute name="Class-Path" value="lib/i2p.jar lib/router.jar lib/jbigi.jar lib/BOB.jar lib/sam.jar lib/mstreaming.jar lib/streaming.jar lib/routerconsole.jar lib/i2ptunnel.jar lib/org.mortbay.jetty.jar lib/javax.servlet.jar lib/jasper-compiler.jar lib/jasper-runtime.jar lib/commons-logging.jar lib/commons-el.jar lib/ant.jar lib/wrapper.jar lib/systray.jar lib/systray4j.jar" />
|
||||||
</manifest>
|
</manifest>
|
||||||
</jar>
|
</jar>
|
||||||
<!-- now the standalone launcher exe -->
|
<!-- now the standalone launcher exe -->
|
||||||
@ -219,7 +218,6 @@
|
|||||||
<copy file="apps/systray/java/lib/systray4j.dll" todir="pkg-temp/lib" />
|
<copy file="apps/systray/java/lib/systray4j.dll" todir="pkg-temp/lib" />
|
||||||
<copy file="apps/systray/java/resources/iggy.ico" todir="pkg-temp/icons" />
|
<copy file="apps/systray/java/resources/iggy.ico" todir="pkg-temp/icons" />
|
||||||
<copy file="apps/systray/java/resources/iggy.xpm" todir="pkg-temp/icons" />
|
<copy file="apps/systray/java/resources/iggy.xpm" todir="pkg-temp/icons" />
|
||||||
<copy file="build/xercesImpl.jar" todir="pkg-temp/lib/" />
|
|
||||||
<copy file="build/i2ptunnel.war" todir="pkg-temp/webapps/" />
|
<copy file="build/i2ptunnel.war" todir="pkg-temp/webapps/" />
|
||||||
<copy file="build/routerconsole.war" todir="pkg-temp/webapps/" />
|
<copy file="build/routerconsole.war" todir="pkg-temp/webapps/" />
|
||||||
<copy file="build/addressbook.war" todir="pkg-temp/webapps/" />
|
<copy file="build/addressbook.war" todir="pkg-temp/webapps/" />
|
||||||
@ -371,17 +369,16 @@
|
|||||||
<copy file="build/commons-el.jar" todir="pkg-temp/lib/" />
|
<copy file="build/commons-el.jar" todir="pkg-temp/lib/" />
|
||||||
<copy file="build/javax.servlet.jar" todir="pkg-temp/lib/" />
|
<copy file="build/javax.servlet.jar" todir="pkg-temp/lib/" />
|
||||||
<copy file="build/org.mortbay.jetty.jar" todir="pkg-temp/lib/" />
|
<copy file="build/org.mortbay.jetty.jar" todir="pkg-temp/lib/" />
|
||||||
<copy file="build/xercesImpl.jar" todir="pkg-temp/lib/" />
|
|
||||||
</target>
|
</target>
|
||||||
<target name="installer" depends="preppkg">
|
<target name="installer" depends="preppkg">
|
||||||
<taskdef name="izpack" classpath="${basedir}/installer/lib/izpack/standalone-compiler.jar" classname="com.izforge.izpack.ant.IzPackTask" />
|
<taskdef name="izpack" classpath="${basedir}/installer/lib/izpack/standalone-compiler.jar" classname="com.izforge.izpack.ant.IzPackTask" />
|
||||||
<jar destfile="./pkg-temp/lib/copy.jar" basedir="./core/java/build/obj" includes="net/i2p/util/*.class">
|
<jar destfile="./pkg-temp/lib/copy.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Copy.class net/i2p/util/FileUtil.class">
|
||||||
<manifest><attribute name="Main-Class" value="net.i2p.util.Copy" /></manifest>
|
<manifest><attribute name="Main-Class" value="net.i2p.util.Copy" /></manifest>
|
||||||
</jar>
|
</jar>
|
||||||
<jar destfile="./pkg-temp/lib/delete.jar" basedir="./core/java/build/obj" includes="net/i2p/util/*.class">
|
<jar destfile="./pkg-temp/lib/delete.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Delete.class net/i2p/util/FileUtil.class">
|
||||||
<manifest><attribute name="Main-Class" value="net.i2p.util.Delete" /></manifest>
|
<manifest><attribute name="Main-Class" value="net.i2p.util.Delete" /></manifest>
|
||||||
</jar>
|
</jar>
|
||||||
<jar destfile="./pkg-temp/lib/exec.jar" basedir="./core/java/build/obj" includes="net/i2p/util/*.class">
|
<jar destfile="./pkg-temp/lib/exec.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Exec.class">
|
||||||
<manifest><attribute name="Main-Class" value="net.i2p.util.Exec" /></manifest>
|
<manifest><attribute name="Main-Class" value="net.i2p.util.Exec" /></manifest>
|
||||||
</jar>
|
</jar>
|
||||||
<izpack input="${basedir}/installer/install.xml" output="${basedir}/install.jar" installerType="standard" basedir="${basedir}" />
|
<izpack input="${basedir}/installer/install.xml" output="${basedir}/install.jar" installerType="standard" basedir="${basedir}" />
|
||||||
@ -452,7 +449,7 @@
|
|||||||
<arg value="-output"/>
|
<arg value="-output"/>
|
||||||
<arg value="findbugs.xml"/>
|
<arg value="findbugs.xml"/>
|
||||||
<arg value="-auxclasspath"/>
|
<arg value="-auxclasspath"/>
|
||||||
<arg value="build/ant.jar:build/commons-el.jar:build/commons-logging.jar:build/jasper-compiler.jar:build/jasper-runtime.jar:build/javax.servlet.jar:build/org.mortbay.jetty.jar:apps/jrobin/jrobin-1.4.0.jar:apps/systray/java/lib/systray4j.jar:installer/lib/wrapper/linux/wrapper.jar:build/xercesImpl.jar"/>
|
<arg value="build/ant.jar:build/commons-el.jar:build/commons-logging.jar:build/jasper-compiler.jar:build/jasper-runtime.jar:build/javax.servlet.jar:build/org.mortbay.jetty.jar:apps/jrobin/jrobin-1.4.0.jar:apps/systray/java/lib/systray4j.jar:installer/lib/wrapper/linux/wrapper.jar"/>
|
||||||
<arg value="-sourcepath"/>
|
<arg value="-sourcepath"/>
|
||||||
<arg value="apps/BOB/src/:apps/addressbook/java/src/:apps/i2psnark/java/src/:apps/i2ptunnel/java/src/:apps/ministreaming/java/src/:apps/routerconsole/java/src/:apps/sam/java/src/:apps/streaming/java/src/:apps/susidns/src/java/src/:apps/susimail/src/src/:apps/systray/java/src/:core/java/src/:router/java/src/"/>
|
<arg value="apps/BOB/src/:apps/addressbook/java/src/:apps/i2psnark/java/src/:apps/i2ptunnel/java/src/:apps/ministreaming/java/src/:apps/routerconsole/java/src/:apps/sam/java/src/:apps/streaming/java/src/:apps/susidns/src/java/src/:apps/susimail/src/src/:apps/systray/java/src/:core/java/src/:router/java/src/"/>
|
||||||
<!-- start of the files to be analyzed -->
|
<!-- start of the files to be analyzed -->
|
||||||
|
@ -24,6 +24,7 @@ import net.i2p.data.RoutingKeyGenerator;
|
|||||||
import net.i2p.stat.StatManager;
|
import net.i2p.stat.StatManager;
|
||||||
import net.i2p.util.Clock;
|
import net.i2p.util.Clock;
|
||||||
import net.i2p.util.FortunaRandomSource;
|
import net.i2p.util.FortunaRandomSource;
|
||||||
|
import net.i2p.util.KeyRing;
|
||||||
import net.i2p.util.LogManager;
|
import net.i2p.util.LogManager;
|
||||||
import net.i2p.util.PooledRandomSource;
|
import net.i2p.util.PooledRandomSource;
|
||||||
import net.i2p.util.RandomSource;
|
import net.i2p.util.RandomSource;
|
||||||
@ -75,6 +76,7 @@ public class I2PAppContext {
|
|||||||
private RoutingKeyGenerator _routingKeyGenerator;
|
private RoutingKeyGenerator _routingKeyGenerator;
|
||||||
private RandomSource _random;
|
private RandomSource _random;
|
||||||
private KeyGenerator _keyGenerator;
|
private KeyGenerator _keyGenerator;
|
||||||
|
protected KeyRing _keyRing; // overridden in RouterContext
|
||||||
private volatile boolean _statManagerInitialized;
|
private volatile boolean _statManagerInitialized;
|
||||||
private volatile boolean _sessionKeyManagerInitialized;
|
private volatile boolean _sessionKeyManagerInitialized;
|
||||||
private volatile boolean _namingServiceInitialized;
|
private volatile boolean _namingServiceInitialized;
|
||||||
@ -91,6 +93,7 @@ public class I2PAppContext {
|
|||||||
private volatile boolean _routingKeyGeneratorInitialized;
|
private volatile boolean _routingKeyGeneratorInitialized;
|
||||||
private volatile boolean _randomInitialized;
|
private volatile boolean _randomInitialized;
|
||||||
private volatile boolean _keyGeneratorInitialized;
|
private volatile boolean _keyGeneratorInitialized;
|
||||||
|
protected volatile boolean _keyRingInitialized; // used in RouterContext
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,12 +144,14 @@ public class I2PAppContext {
|
|||||||
_elGamalEngine = null;
|
_elGamalEngine = null;
|
||||||
_elGamalAESEngine = null;
|
_elGamalAESEngine = null;
|
||||||
_logManager = null;
|
_logManager = null;
|
||||||
|
_keyRing = null;
|
||||||
_statManagerInitialized = false;
|
_statManagerInitialized = false;
|
||||||
_sessionKeyManagerInitialized = false;
|
_sessionKeyManagerInitialized = false;
|
||||||
_namingServiceInitialized = false;
|
_namingServiceInitialized = false;
|
||||||
_elGamalEngineInitialized = false;
|
_elGamalEngineInitialized = false;
|
||||||
_elGamalAESEngineInitialized = false;
|
_elGamalAESEngineInitialized = false;
|
||||||
_logManagerInitialized = false;
|
_logManagerInitialized = false;
|
||||||
|
_keyRingInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -512,6 +517,23 @@ public class I2PAppContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic hash map
|
||||||
|
*/
|
||||||
|
public KeyRing keyRing() {
|
||||||
|
if (!_keyRingInitialized)
|
||||||
|
initializeKeyRing();
|
||||||
|
return _keyRing;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initializeKeyRing() {
|
||||||
|
synchronized (this) {
|
||||||
|
if (_keyRing == null)
|
||||||
|
_keyRing = new KeyRing();
|
||||||
|
_keyRingInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [insert snarky comment here]
|
* [insert snarky comment here]
|
||||||
*
|
*
|
||||||
|
@ -9,6 +9,7 @@ package net.i2p.client;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
@ -28,6 +29,7 @@ import net.i2p.data.i2cp.DestroySessionMessage;
|
|||||||
import net.i2p.data.i2cp.MessageId;
|
import net.i2p.data.i2cp.MessageId;
|
||||||
import net.i2p.data.i2cp.ReportAbuseMessage;
|
import net.i2p.data.i2cp.ReportAbuseMessage;
|
||||||
import net.i2p.data.i2cp.SendMessageMessage;
|
import net.i2p.data.i2cp.SendMessageMessage;
|
||||||
|
import net.i2p.data.i2cp.SendMessageExpiresMessage;
|
||||||
import net.i2p.data.i2cp.SessionConfig;
|
import net.i2p.data.i2cp.SessionConfig;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
@ -91,8 +93,13 @@ class I2CPMessageProducer {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag,
|
public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag,
|
||||||
SessionKey key, Set tags, SessionKey newKey) throws I2PSessionException {
|
SessionKey key, Set tags, SessionKey newKey, long expires) throws I2PSessionException {
|
||||||
SendMessageMessage msg = new SendMessageMessage();
|
SendMessageMessage msg;
|
||||||
|
if (expires > 0) {
|
||||||
|
msg = new SendMessageExpiresMessage();
|
||||||
|
((SendMessageExpiresMessage)msg).setExpiration(new Date(expires));
|
||||||
|
} else
|
||||||
|
msg = new SendMessageMessage();
|
||||||
msg.setDestination(dest);
|
msg.setDestination(dest);
|
||||||
msg.setSessionId(session.getSessionId());
|
msg.setSessionId(session.getSessionId());
|
||||||
msg.setNonce(nonce);
|
msg.setNonce(nonce);
|
||||||
|
@ -70,6 +70,7 @@ public interface I2PSession {
|
|||||||
*/
|
*/
|
||||||
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
|
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
|
||||||
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
|
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
|
||||||
|
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire) throws I2PSessionException;
|
||||||
|
|
||||||
/** Receive a message that the router has notified the client about, returning
|
/** Receive a message that the router has notified the client about, returning
|
||||||
* the payload.
|
* the payload.
|
||||||
|
@ -550,10 +550,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
* Pass off the error to the listener
|
* Pass off the error to the listener
|
||||||
*/
|
*/
|
||||||
void propogateError(String msg, Throwable error) {
|
void propogateError(String msg, Throwable error) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
_log.warn(getPrefix() + "Error occurred: " + msg + " - " + error.getMessage());
|
_log.error(getPrefix() + "Error occurred: " + msg + " - " + error.getMessage());
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
_log.warn(getPrefix() + " cause", error);
|
_log.error(getPrefix() + " cause", error);
|
||||||
|
|
||||||
if (_sessionListener != null) _sessionListener.errorOccurred(this, msg, error);
|
if (_sessionListener != null) _sessionListener.errorOccurred(this, msg, error);
|
||||||
}
|
}
|
||||||
|
@ -107,15 +107,19 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
return sendMessage(dest, payload, 0, payload.length);
|
return sendMessage(dest, payload, 0, payload.length);
|
||||||
}
|
}
|
||||||
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size) throws I2PSessionException {
|
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size) throws I2PSessionException {
|
||||||
return sendMessage(dest, payload, offset, size, new SessionKey(), new HashSet(64));
|
return sendMessage(dest, payload, offset, size, new SessionKey(), new HashSet(64), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException {
|
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException {
|
||||||
return sendMessage(dest, payload, 0, payload.length, keyUsed, tagsSent);
|
return sendMessage(dest, payload, 0, payload.length, keyUsed, tagsSent, 0);
|
||||||
}
|
}
|
||||||
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent)
|
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent)
|
||||||
throws I2PSessionException {
|
throws I2PSessionException {
|
||||||
|
return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0);
|
||||||
|
}
|
||||||
|
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expires)
|
||||||
|
throws I2PSessionException {
|
||||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message");
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message");
|
||||||
if (isClosed()) throw new I2PSessionException("Already closed");
|
if (isClosed()) throw new I2PSessionException("Already closed");
|
||||||
|
|
||||||
@ -142,7 +146,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
}
|
}
|
||||||
_context.statManager().addRateData("i2cp.tx.msgCompressed", compressed, 0);
|
_context.statManager().addRateData("i2cp.tx.msgCompressed", compressed, 0);
|
||||||
_context.statManager().addRateData("i2cp.tx.msgExpanded", size, 0);
|
_context.statManager().addRateData("i2cp.tx.msgExpanded", size, 0);
|
||||||
return sendBestEffort(dest, payload, keyUsed, tagsSent);
|
return sendBestEffort(dest, payload, keyUsed, tagsSent, expires);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -168,7 +172,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
|
|
||||||
private static final int NUM_TAGS = 50;
|
private static final int NUM_TAGS = 50;
|
||||||
|
|
||||||
private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent)
|
private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent, long expires)
|
||||||
throws I2PSessionException {
|
throws I2PSessionException {
|
||||||
SessionKey key = null;
|
SessionKey key = null;
|
||||||
SessionKey newKey = null;
|
SessionKey newKey = null;
|
||||||
@ -176,6 +180,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
Set sentTags = null;
|
Set sentTags = null;
|
||||||
int oldTags = 0;
|
int oldTags = 0;
|
||||||
long begin = _context.clock().now();
|
long begin = _context.clock().now();
|
||||||
|
/***********
|
||||||
if (I2CPMessageProducer.END_TO_END_CRYPTO) {
|
if (I2CPMessageProducer.END_TO_END_CRYPTO) {
|
||||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("begin sendBestEffort");
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("begin sendBestEffort");
|
||||||
key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey());
|
key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey());
|
||||||
@ -220,6 +225,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
} else {
|
} else {
|
||||||
// not using end to end crypto, so don't ever bundle any tags
|
// not using end to end crypto, so don't ever bundle any tags
|
||||||
}
|
}
|
||||||
|
**********/
|
||||||
|
|
||||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("before creating nonce");
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("before creating nonce");
|
||||||
|
|
||||||
@ -233,14 +239,14 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key);
|
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key);
|
||||||
|
|
||||||
if (keyUsed != null) {
|
if (keyUsed != null) {
|
||||||
if (I2CPMessageProducer.END_TO_END_CRYPTO) {
|
//if (I2CPMessageProducer.END_TO_END_CRYPTO) {
|
||||||
if (newKey != null)
|
// if (newKey != null)
|
||||||
keyUsed.setData(newKey.getData());
|
// keyUsed.setData(newKey.getData());
|
||||||
else
|
// else
|
||||||
keyUsed.setData(key.getData());
|
// keyUsed.setData(key.getData());
|
||||||
} else {
|
//} else {
|
||||||
keyUsed.setData(SessionKey.INVALID_KEY.getData());
|
keyUsed.setData(SessionKey.INVALID_KEY.getData());
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
if (tagsSent != null) {
|
if (tagsSent != null) {
|
||||||
if (sentTags != null) {
|
if (sentTags != null) {
|
||||||
@ -261,7 +267,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
+ state.getNonce() + " for best effort "
|
+ state.getNonce() + " for best effort "
|
||||||
+ " sync took " + (inSendingSync-beforeSendingSync)
|
+ " sync took " + (inSendingSync-beforeSendingSync)
|
||||||
+ " add took " + (afterSendingSync-inSendingSync));
|
+ " add took " + (afterSendingSync-inSendingSync));
|
||||||
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
|
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey, expires);
|
||||||
|
|
||||||
// since this is 'best effort', all we're waiting for is a status update
|
// since this is 'best effort', all we're waiting for is a status update
|
||||||
// saying that the router received it - in theory, that should come back
|
// saying that the router received it - in theory, that should come back
|
||||||
|
@ -21,6 +21,7 @@ import net.i2p.data.Lease;
|
|||||||
import net.i2p.data.LeaseSet;
|
import net.i2p.data.LeaseSet;
|
||||||
import net.i2p.data.PrivateKey;
|
import net.i2p.data.PrivateKey;
|
||||||
import net.i2p.data.PublicKey;
|
import net.i2p.data.PublicKey;
|
||||||
|
import net.i2p.data.SessionKey;
|
||||||
import net.i2p.data.SigningPrivateKey;
|
import net.i2p.data.SigningPrivateKey;
|
||||||
import net.i2p.data.SigningPublicKey;
|
import net.i2p.data.SigningPublicKey;
|
||||||
import net.i2p.data.i2cp.I2CPMessage;
|
import net.i2p.data.i2cp.I2CPMessage;
|
||||||
@ -78,6 +79,17 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
|
|||||||
|
|
||||||
leaseSet.setEncryptionKey(li.getPublicKey());
|
leaseSet.setEncryptionKey(li.getPublicKey());
|
||||||
leaseSet.setSigningKey(li.getSigningPublicKey());
|
leaseSet.setSigningKey(li.getSigningPublicKey());
|
||||||
|
String sk = session.getOptions().getProperty("i2cp.sessionKey");
|
||||||
|
if (sk != null) {
|
||||||
|
SessionKey key = new SessionKey();
|
||||||
|
try {
|
||||||
|
key.fromBase64(sk);
|
||||||
|
leaseSet.encrypt(key);
|
||||||
|
_context.keyRing().put(session.getMyDestination().calculateHash(), key);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
_log.error("Bad session key: " + sk);
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
leaseSet.sign(session.getPrivateKey());
|
leaseSet.sign(session.getPrivateKey());
|
||||||
session.getProducer().createLeaseSet(session, leaseSet, li.getSigningPrivateKey(), li.getPrivateKey());
|
session.getProducer().createLeaseSet(session, leaseSet, li.getSigningPrivateKey(), li.getPrivateKey());
|
||||||
|
@ -25,6 +25,7 @@ import java.io.OutputStream;
|
|||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -235,7 +236,7 @@ public class DataHelper {
|
|||||||
int split = line.indexOf('=');
|
int split = line.indexOf('=');
|
||||||
if (split <= 0) continue;
|
if (split <= 0) continue;
|
||||||
String key = line.substring(0, split);
|
String key = line.substring(0, split);
|
||||||
String val = line.substring(split+1);
|
String val = line.substring(split+1); //.trim() ??????????????
|
||||||
// Unescape line breaks after loading.
|
// Unescape line breaks after loading.
|
||||||
// Remember: "\" needs escaping both for regex and string.
|
// Remember: "\" needs escaping both for regex and string.
|
||||||
val = val.replaceAll("\\\\r","\r");
|
val = val.replaceAll("\\\\r","\r");
|
||||||
@ -842,6 +843,29 @@ public class DataHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caller should append 'B' or 'b' as appropriate
|
||||||
|
*/
|
||||||
|
public static String formatSize(long bytes) {
|
||||||
|
double val = bytes;
|
||||||
|
int scale = 0;
|
||||||
|
while (val >= 1024) {
|
||||||
|
scale++;
|
||||||
|
val /= 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||||
|
|
||||||
|
String str = fmt.format(val);
|
||||||
|
switch (scale) {
|
||||||
|
case 1: return str + "K";
|
||||||
|
case 2: return str + "M";
|
||||||
|
case 3: return str + "G";
|
||||||
|
case 4: return str + "T";
|
||||||
|
default: return bytes + "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strip out any HTML (simply removing any less than / greater than symbols)
|
* Strip out any HTML (simply removing any less than / greater than symbols)
|
||||||
*/
|
*/
|
||||||
|
@ -9,6 +9,7 @@ package net.i2p.data;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -17,13 +18,34 @@ import java.util.ArrayList;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.crypto.DSAEngine;
|
import net.i2p.crypto.DSAEngine;
|
||||||
import net.i2p.util.Clock;
|
import net.i2p.util.Clock;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.RandomSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the set of leases a destination currently has.
|
* Defines the set of leases a destination currently has.
|
||||||
*
|
*
|
||||||
|
* Support encryption and decryption with a supplied key.
|
||||||
|
* Only the gateways and tunnel IDs in the individual
|
||||||
|
* leases are encrypted.
|
||||||
|
*
|
||||||
|
* Encrypted leases are not indicated as such.
|
||||||
|
* The only way to tell a lease is encrypted is to
|
||||||
|
* determine that the listed gateways do not exist.
|
||||||
|
* Routers wishing to decrypt a leaseset must have the
|
||||||
|
* desthash and key in their keyring.
|
||||||
|
* This is required for the local router as well, since
|
||||||
|
* the encryption is done on the client side of I2CP, the
|
||||||
|
* router must decrypt it back again for local usage
|
||||||
|
* (but not for transmission to the floodfills)
|
||||||
|
*
|
||||||
|
* Decrypted leases are only available through the getLease()
|
||||||
|
* method, so that storage and network transmission via
|
||||||
|
* writeBytes() will output the original encrypted
|
||||||
|
* leases and the original leaseset signature.
|
||||||
|
*
|
||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
public class LeaseSet extends DataStructureImpl {
|
public class LeaseSet extends DataStructureImpl {
|
||||||
@ -40,6 +62,9 @@ public class LeaseSet extends DataStructureImpl {
|
|||||||
// Store these since isCurrent() and getEarliestLeaseDate() are called frequently
|
// Store these since isCurrent() and getEarliestLeaseDate() are called frequently
|
||||||
private long _firstExpiration;
|
private long _firstExpiration;
|
||||||
private long _lastExpiration;
|
private long _lastExpiration;
|
||||||
|
private List _decryptedLeases;
|
||||||
|
private boolean _decrypted;
|
||||||
|
private boolean _checked;
|
||||||
|
|
||||||
/** This seems like plenty */
|
/** This seems like plenty */
|
||||||
private final static int MAX_LEASES = 6;
|
private final static int MAX_LEASES = 6;
|
||||||
@ -55,6 +80,8 @@ public class LeaseSet extends DataStructureImpl {
|
|||||||
_receivedAsPublished = false;
|
_receivedAsPublished = false;
|
||||||
_firstExpiration = Long.MAX_VALUE;
|
_firstExpiration = Long.MAX_VALUE;
|
||||||
_lastExpiration = 0;
|
_lastExpiration = 0;
|
||||||
|
_decrypted = false;
|
||||||
|
_checked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Destination getDestination() {
|
public Destination getDestination() {
|
||||||
@ -104,10 +131,16 @@ public class LeaseSet extends DataStructureImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getLeaseCount() {
|
public int getLeaseCount() {
|
||||||
|
if (isEncrypted())
|
||||||
|
return _leases.size() - 1;
|
||||||
|
else
|
||||||
return _leases.size();
|
return _leases.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Lease getLease(int index) {
|
public Lease getLease(int index) {
|
||||||
|
if (isEncrypted())
|
||||||
|
return (Lease) _decryptedLeases.get(index);
|
||||||
|
else
|
||||||
return (Lease) _leases.get(index);
|
return (Lease) _leases.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,4 +368,139 @@ public class LeaseSet extends DataStructureImpl {
|
|||||||
buf.append("]");
|
buf.append("]");
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final int DATA_LEN = Hash.HASH_LENGTH + 4;
|
||||||
|
private static final int IV_LEN = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt the gateway and tunnel ID of each lease, leaving the expire dates unchanged.
|
||||||
|
* This adds an extra dummy lease, because AES data must be padded to 16 bytes.
|
||||||
|
* The fact that it is encrypted is not stored anywhere.
|
||||||
|
* Must be called after all the leases are in place, but before sign().
|
||||||
|
*/
|
||||||
|
public void encrypt(SessionKey key) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("encrypting lease: " + _destination.calculateHash());
|
||||||
|
try {
|
||||||
|
encryp(key);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
_log.error("Error encrypting lease: " + _destination.calculateHash());
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
_log.error("Error encrypting lease: " + _destination.calculateHash());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* - Put the {Gateway Hash, TunnelID} pairs for all the leases in a buffer
|
||||||
|
* - Pad with random data to a multiple of 16 bytes
|
||||||
|
* - Use the first part of the dest's public key as an IV
|
||||||
|
* - Encrypt
|
||||||
|
* - Pad with random data to a multiple of 36 bytes
|
||||||
|
* - Add an extra lease
|
||||||
|
* - Replace the Hash and TunnelID in each Lease
|
||||||
|
*/
|
||||||
|
private void encryp(SessionKey key) throws DataFormatException, IOException {
|
||||||
|
int size = _leases.size();
|
||||||
|
if (size < 1 || size > MAX_LEASES-1)
|
||||||
|
throw new IllegalArgumentException("Bad number of leases for encryption");
|
||||||
|
int datalen = ((DATA_LEN * size / 16) + 1) * 16;
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(datalen);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
((Lease)_leases.get(i)).getGateway().writeBytes(baos);
|
||||||
|
((Lease)_leases.get(i)).getTunnelId().writeBytes(baos);
|
||||||
|
}
|
||||||
|
// pad out to multiple of 16 with random data before encryption
|
||||||
|
int padlen = datalen - (DATA_LEN * size);
|
||||||
|
byte[] pad = new byte[padlen];
|
||||||
|
RandomSource.getInstance().nextBytes(pad);
|
||||||
|
baos.write(pad);
|
||||||
|
byte[] iv = new byte[IV_LEN];
|
||||||
|
System.arraycopy(_destination.getPublicKey().getData(), 0, iv, 0, IV_LEN);
|
||||||
|
byte[] enc = new byte[DATA_LEN * (size + 1)];
|
||||||
|
I2PAppContext.getGlobalContext().aes().encrypt(baos.toByteArray(), 0, enc, 0, key, iv, datalen);
|
||||||
|
// pad out to multiple of 36 with random data after encryption
|
||||||
|
// (even for 4 leases, where 36*4 is a multiple of 16, we add another, just to be consistent)
|
||||||
|
padlen = enc.length - datalen;
|
||||||
|
pad = new byte[padlen];
|
||||||
|
RandomSource.getInstance().nextBytes(pad);
|
||||||
|
System.arraycopy(pad, 0, enc, datalen, padlen);
|
||||||
|
// add the padded lease...
|
||||||
|
Lease padLease = new Lease();
|
||||||
|
padLease.setEndDate(((Lease)_leases.get(0)).getEndDate());
|
||||||
|
_leases.add(padLease);
|
||||||
|
// ...and replace all the gateways and tunnel ids
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(enc);
|
||||||
|
for (int i = 0; i < size+1; i++) {
|
||||||
|
Hash h = new Hash();
|
||||||
|
h.readBytes(bais);
|
||||||
|
((Lease)_leases.get(i)).setGateway(h);
|
||||||
|
TunnelId t = new TunnelId();
|
||||||
|
t.readBytes(bais);
|
||||||
|
((Lease)_leases.get(i)).setTunnelId(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt the leases, except for the last one which is partially padding.
|
||||||
|
* Store the new decrypted leases in a backing store,
|
||||||
|
* and keep the original leases so that verify() still works and the
|
||||||
|
* encrypted leaseset can be sent on to others (via writeBytes())
|
||||||
|
*/
|
||||||
|
private void decrypt(SessionKey key) throws DataFormatException, IOException {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("decrypting lease: " + _destination.calculateHash());
|
||||||
|
int size = _leases.size();
|
||||||
|
if (size < 2)
|
||||||
|
throw new DataFormatException("Bad number of leases for decryption");
|
||||||
|
int datalen = DATA_LEN * size;
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(datalen);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
((Lease)_leases.get(i)).getGateway().writeBytes(baos);
|
||||||
|
((Lease)_leases.get(i)).getTunnelId().writeBytes(baos);
|
||||||
|
}
|
||||||
|
byte[] iv = new byte[IV_LEN];
|
||||||
|
System.arraycopy(_destination.getPublicKey().getData(), 0, iv, 0, IV_LEN);
|
||||||
|
int enclen = ((DATA_LEN * (size - 1) / 16) + 1) * 16;
|
||||||
|
byte[] enc = new byte[enclen];
|
||||||
|
System.arraycopy(baos.toByteArray(), 0, enc, 0, enclen);
|
||||||
|
byte[] dec = new byte[enclen];
|
||||||
|
I2PAppContext.getGlobalContext().aes().decrypt(enc, 0, dec, 0, key, iv, enclen);
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(dec);
|
||||||
|
_decryptedLeases = new ArrayList(size - 1);
|
||||||
|
for (int i = 0; i < size-1; i++) {
|
||||||
|
Lease l = new Lease();
|
||||||
|
Hash h = new Hash();
|
||||||
|
h.readBytes(bais);
|
||||||
|
l.setGateway(h);
|
||||||
|
TunnelId t = new TunnelId();
|
||||||
|
t.readBytes(bais);
|
||||||
|
l.setTunnelId(t);
|
||||||
|
l.setEndDate(((Lease)_leases.get(i)).getEndDate());
|
||||||
|
_decryptedLeases.add(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if it was encrypted, and we decrypted it successfully.
|
||||||
|
* Decrypts on first call.
|
||||||
|
*/
|
||||||
|
private synchronized boolean isEncrypted() {
|
||||||
|
if (_decrypted)
|
||||||
|
return true;
|
||||||
|
if (_checked || _destination == null)
|
||||||
|
return false;
|
||||||
|
SessionKey key = I2PAppContext.getGlobalContext().keyRing().get(_destination.calculateHash());
|
||||||
|
if (key != null) {
|
||||||
|
try {
|
||||||
|
decrypt(key);
|
||||||
|
_decrypted = true;
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
_log.error("Error decrypting lease: " + _destination.calculateHash() + dfe);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
_log.error("Error decrypting lease: " + _destination.calculateHash() + ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_checked = true;
|
||||||
|
return _decrypted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import net.i2p.data.DataHelper;
|
|||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle messages from the server for the client
|
* Handle messages from the server for the client or vice versa
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class I2CPMessageHandler {
|
public class I2CPMessageHandler {
|
||||||
@ -75,6 +75,8 @@ public class I2CPMessageHandler {
|
|||||||
return new RequestLeaseSetMessage();
|
return new RequestLeaseSetMessage();
|
||||||
case SendMessageMessage.MESSAGE_TYPE:
|
case SendMessageMessage.MESSAGE_TYPE:
|
||||||
return new SendMessageMessage();
|
return new SendMessageMessage();
|
||||||
|
case SendMessageExpiresMessage.MESSAGE_TYPE:
|
||||||
|
return new SendMessageExpiresMessage();
|
||||||
case SessionStatusMessage.MESSAGE_TYPE:
|
case SessionStatusMessage.MESSAGE_TYPE:
|
||||||
return new SessionStatusMessage();
|
return new SessionStatusMessage();
|
||||||
case GetDateMessage.MESSAGE_TYPE:
|
case GetDateMessage.MESSAGE_TYPE:
|
||||||
|
103
core/java/src/net/i2p/data/i2cp/ReconfigureSessionMessage.java
Normal file
103
core/java/src/net/i2p/data/i2cp/ReconfigureSessionMessage.java
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package net.i2p.data.i2cp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free (adj.): unencumbered; not under the control of others
|
||||||
|
* Written by jrandom in 2003 and released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
* It probably won't make your computer catch on fire, or eat
|
||||||
|
* your children, but it might. Use at your own risk.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the message a client sends to a router when
|
||||||
|
* updating the config on an existing session.
|
||||||
|
*
|
||||||
|
* @author zzz
|
||||||
|
*/
|
||||||
|
public class ReconfigureSessionMessage extends I2CPMessageImpl {
|
||||||
|
private final static Log _log = new Log(ReconfigureSessionMessage.class);
|
||||||
|
public final static int MESSAGE_TYPE = 2;
|
||||||
|
private SessionId _sessionId;
|
||||||
|
private SessionConfig _sessionConfig;
|
||||||
|
|
||||||
|
public ReconfigureSessionMessage() {
|
||||||
|
_sessionId = null;
|
||||||
|
_sessionConfig = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionId getSessionId() {
|
||||||
|
return _sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSessionId(SessionId id) {
|
||||||
|
_sessionId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionConfig getSessionConfig() {
|
||||||
|
return _sessionConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSessionConfig(SessionConfig config) {
|
||||||
|
_sessionConfig = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||||
|
try {
|
||||||
|
_sessionId = new SessionId();
|
||||||
|
_sessionId.readBytes(in);
|
||||||
|
_sessionConfig = new SessionConfig();
|
||||||
|
_sessionConfig.readBytes(in);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
throw new I2CPMessageException("Unable to load the message data", dfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||||
|
if (_sessionId == null || _sessionConfig == null)
|
||||||
|
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream(64);
|
||||||
|
try {
|
||||||
|
_sessionId.writeBytes(os);
|
||||||
|
_sessionConfig.writeBytes(os);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
throw new I2CPMessageException("Error writing out the message data", dfe);
|
||||||
|
}
|
||||||
|
return os.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return MESSAGE_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if ((object != null) && (object instanceof ReconfigureSessionMessage)) {
|
||||||
|
ReconfigureSessionMessage msg = (ReconfigureSessionMessage) object;
|
||||||
|
return DataHelper.eq(getSessionId(), msg.getSessionId())
|
||||||
|
&& DataHelper.eq(getSessionConfig(), msg.getSessionConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
buf.append("[ReconfigureSessionMessage: ");
|
||||||
|
buf.append("\n\tSessionId: ").append(getSessionId());
|
||||||
|
buf.append("\n\tSessionConfig: ").append(getSessionConfig());
|
||||||
|
buf.append("]");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
117
core/java/src/net/i2p/data/i2cp/SendMessageExpiresMessage.java
Normal file
117
core/java/src/net/i2p/data/i2cp/SendMessageExpiresMessage.java
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package net.i2p.data.i2cp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free (adj.): unencumbered; not under the control of others
|
||||||
|
* Written by jrandom in 2003 and released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
* It probably won't make your computer catch on fire, or eat
|
||||||
|
* your children, but it might. Use at your own risk.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.Payload;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as SendMessageMessage, but with an expiration to be passed to the router
|
||||||
|
*
|
||||||
|
* @author zzz
|
||||||
|
*/
|
||||||
|
public class SendMessageExpiresMessage extends SendMessageMessage {
|
||||||
|
private final static Log _log = new Log(SendMessageExpiresMessage.class);
|
||||||
|
public final static int MESSAGE_TYPE = 36;
|
||||||
|
private SessionId _sessionId;
|
||||||
|
private Destination _destination;
|
||||||
|
private Payload _payload;
|
||||||
|
private Date _expiration;
|
||||||
|
|
||||||
|
public SendMessageExpiresMessage() {
|
||||||
|
super();
|
||||||
|
setExpiration(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getExpiration() {
|
||||||
|
return _expiration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpiration(Date d) {
|
||||||
|
_expiration = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the body into the data structures
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void readMessage(InputStream in, int length, int type) throws I2CPMessageException, IOException {
|
||||||
|
super.readMessage(in, length, type);
|
||||||
|
|
||||||
|
try {
|
||||||
|
_expiration = DataHelper.readDate(in);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
throw new I2CPMessageException("Unable to load the message data", dfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write out the full message to the stream, including the 4 byte size and 1
|
||||||
|
* byte type header. Override the parent so we can be more mem efficient
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void writeMessage(OutputStream out) throws I2CPMessageException, IOException {
|
||||||
|
if ((getSessionId() == null) || (getDestination() == null) || (getPayload() == null) || (getNonce() <= 0) || (_expiration == null))
|
||||||
|
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
||||||
|
int len = 2 + getDestination().size() + getPayload().getSize() + 4 + 4 + DataHelper.DATE_LENGTH;
|
||||||
|
|
||||||
|
try {
|
||||||
|
DataHelper.writeLong(out, 4, len);
|
||||||
|
DataHelper.writeLong(out, 1, getType());
|
||||||
|
getSessionId().writeBytes(out);
|
||||||
|
getDestination().writeBytes(out);
|
||||||
|
getPayload().writeBytes(out);
|
||||||
|
DataHelper.writeLong(out, 4, getNonce());
|
||||||
|
DataHelper.writeDate(out, _expiration);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
throw new I2CPMessageException("Error writing the msg", dfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return MESSAGE_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if ((object != null) && (object instanceof SendMessageExpiresMessage)) {
|
||||||
|
SendMessageExpiresMessage msg = (SendMessageExpiresMessage) object;
|
||||||
|
return super.equals(object)
|
||||||
|
&& DataHelper.eq(getExpiration(), msg.getExpiration());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
buf.append("[SendMessageMessage: ");
|
||||||
|
buf.append("\n\tSessionId: ").append(getSessionId());
|
||||||
|
buf.append("\n\tNonce: ").append(getNonce());
|
||||||
|
buf.append("\n\tDestination: ").append(getDestination());
|
||||||
|
buf.append("\n\tExpiration: ").append(getExpiration());
|
||||||
|
buf.append("\n\tPayload: ").append(getPayload());
|
||||||
|
buf.append("]");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
20
core/java/src/net/i2p/util/KeyRing.java
Normal file
20
core/java/src/net/i2p/util/KeyRing.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package net.i2p.util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.SessionKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* simple
|
||||||
|
*/
|
||||||
|
public class KeyRing extends ConcurrentHashMap<Hash, SessionKey> {
|
||||||
|
public KeyRing() {
|
||||||
|
super(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderStatusHTML(Writer out) throws IOException {}
|
||||||
|
}
|
@ -47,14 +47,13 @@ wrapper.java.classpath.12=lib/jasper-runtime.jar
|
|||||||
wrapper.java.classpath.13=lib/commons-logging.jar
|
wrapper.java.classpath.13=lib/commons-logging.jar
|
||||||
wrapper.java.classpath.14=lib/commons-el.jar
|
wrapper.java.classpath.14=lib/commons-el.jar
|
||||||
wrapper.java.classpath.15=lib/ant.jar
|
wrapper.java.classpath.15=lib/ant.jar
|
||||||
wrapper.java.classpath.16=lib/xercesImpl.jar
|
|
||||||
# java service wrapper, BSD
|
# java service wrapper, BSD
|
||||||
wrapper.java.classpath.17=lib/wrapper.jar
|
wrapper.java.classpath.16=lib/wrapper.jar
|
||||||
# systray, LGPL
|
# systray, LGPL
|
||||||
wrapper.java.classpath.18=lib/systray.jar
|
wrapper.java.classpath.17=lib/systray.jar
|
||||||
wrapper.java.classpath.19=lib/systray4j.jar
|
wrapper.java.classpath.18=lib/systray4j.jar
|
||||||
# BOB
|
# BOB
|
||||||
wrapper.java.classpath.20=lib/BOB.jar
|
wrapper.java.classpath.19=lib/BOB.jar
|
||||||
|
|
||||||
# Java Library Path (location of Wrapper.DLL or libwrapper.so)
|
# Java Library Path (location of Wrapper.DLL or libwrapper.so)
|
||||||
wrapper.java.library.path.1=.
|
wrapper.java.library.path.1=.
|
||||||
|
@ -27,6 +27,7 @@ public class ClientMessage {
|
|||||||
private SessionConfig _senderConfig;
|
private SessionConfig _senderConfig;
|
||||||
private Hash _destinationHash;
|
private Hash _destinationHash;
|
||||||
private MessageId _messageId;
|
private MessageId _messageId;
|
||||||
|
private long _expiration;
|
||||||
|
|
||||||
public ClientMessage() {
|
public ClientMessage() {
|
||||||
setPayload(null);
|
setPayload(null);
|
||||||
@ -36,6 +37,7 @@ public class ClientMessage {
|
|||||||
setSenderConfig(null);
|
setSenderConfig(null);
|
||||||
setDestinationHash(null);
|
setDestinationHash(null);
|
||||||
setMessageId(null);
|
setMessageId(null);
|
||||||
|
setExpiration(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,4 +93,12 @@ public class ClientMessage {
|
|||||||
*/
|
*/
|
||||||
public SessionConfig getSenderConfig() { return _senderConfig; }
|
public SessionConfig getSenderConfig() { return _senderConfig; }
|
||||||
public void setSenderConfig(SessionConfig config) { _senderConfig = config; }
|
public void setSenderConfig(SessionConfig config) { _senderConfig = config; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expiration requested by the client that sent the message. This will only be available
|
||||||
|
* for locally originated messages.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public long getExpiration() { return _expiration; }
|
||||||
|
public void setExpiration(long e) { _expiration = e; }
|
||||||
}
|
}
|
||||||
|
@ -62,4 +62,5 @@ public abstract class NetworkDatabaseFacade implements Service {
|
|||||||
public int getKnownRouters() { return 0; }
|
public int getKnownRouters() { return 0; }
|
||||||
public int getKnownLeaseSets() { return 0; }
|
public int getKnownLeaseSets() { return 0; }
|
||||||
public void renderRouterInfoHTML(Writer out, String s) throws IOException {}
|
public void renderRouterInfoHTML(Writer out, String s) throws IOException {}
|
||||||
|
public void renderStatusHTML(Writer out, boolean b) throws IOException {}
|
||||||
}
|
}
|
||||||
|
103
router/java/src/net/i2p/router/PersistentKeyRing.java
Normal file
103
router/java/src/net/i2p/router/PersistentKeyRing.java
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package net.i2p.router;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import net.i2p.data.Base64;
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.LeaseSet;
|
||||||
|
import net.i2p.data.SessionKey;
|
||||||
|
import net.i2p.router.TunnelPoolSettings;
|
||||||
|
import net.i2p.util.KeyRing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConcurrentHashMap with backing in the router.config file.
|
||||||
|
* router.keyring.key.{base64 hash, with = replaced with $}={base64 session key}
|
||||||
|
* Caution - not all HashMap methods are overridden.
|
||||||
|
*/
|
||||||
|
public class PersistentKeyRing extends KeyRing {
|
||||||
|
private RouterContext _ctx;
|
||||||
|
private static final String PROP_PFX = "router.keyring.key.";
|
||||||
|
|
||||||
|
public PersistentKeyRing(RouterContext ctx) {
|
||||||
|
super();
|
||||||
|
_ctx = ctx;
|
||||||
|
addFromProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionKey put(Hash h, SessionKey sk) {
|
||||||
|
SessionKey old = super.put(h, sk);
|
||||||
|
if (!sk.equals(old)) {
|
||||||
|
_ctx.router().setConfigSetting(PROP_PFX + h.toBase64().replace("=", "$"),
|
||||||
|
sk.toBase64());
|
||||||
|
_ctx.router().saveConfig();
|
||||||
|
}
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionKey remove(Hash h) {
|
||||||
|
_ctx.router().removeConfigSetting(PROP_PFX + h.toBase64().replace("=", "$"));
|
||||||
|
_ctx.router().saveConfig();
|
||||||
|
return super.remove(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFromProperties() {
|
||||||
|
for (Iterator iter = _ctx.getPropertyNames().iterator(); iter.hasNext(); ) {
|
||||||
|
String prop = (String) iter.next();
|
||||||
|
if (!prop.startsWith(PROP_PFX))
|
||||||
|
continue;
|
||||||
|
String key = _ctx.getProperty(prop);
|
||||||
|
if (key == null || key.length() != 44)
|
||||||
|
continue;
|
||||||
|
String hb = prop.substring(PROP_PFX.length());
|
||||||
|
hb.replace("$", "=");
|
||||||
|
Hash dest = new Hash();
|
||||||
|
SessionKey sk = new SessionKey();
|
||||||
|
try {
|
||||||
|
dest.fromBase64(hb);
|
||||||
|
sk.fromBase64(key);
|
||||||
|
super.put(dest, sk);
|
||||||
|
} catch (DataFormatException dfe) { continue; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderStatusHTML(Writer out) throws IOException {
|
||||||
|
StringBuffer buf = new StringBuffer(1024);
|
||||||
|
buf.append("\n<table border=\"1\"><tr><th align=\"left\">Destination Hash<th align=\"left\">Name or Dest.<th align=\"left\">Session Key</tr>");
|
||||||
|
for (Entry<Hash, SessionKey> e : entrySet()) {
|
||||||
|
buf.append("\n<tr><td>");
|
||||||
|
Hash h = e.getKey();
|
||||||
|
buf.append(h.toBase64().substring(0, 6)).append("...");
|
||||||
|
buf.append("<td>");
|
||||||
|
LeaseSet ls = _ctx.netDb().lookupLeaseSetLocally(h);
|
||||||
|
if (ls != null) {
|
||||||
|
Destination dest = ls.getDestination();
|
||||||
|
if (_ctx.clientManager().isLocal(dest)) {
|
||||||
|
TunnelPoolSettings in = _ctx.tunnelManager().getInboundSettings(h);
|
||||||
|
if (in != null && in.getDestinationNickname() != null)
|
||||||
|
buf.append(in.getDestinationNickname());
|
||||||
|
else
|
||||||
|
buf.append(dest.toBase64().substring(0, 6)).append("...");
|
||||||
|
} else {
|
||||||
|
String host = _ctx.namingService().reverseLookup(dest);
|
||||||
|
if (host != null)
|
||||||
|
buf.append(host);
|
||||||
|
else
|
||||||
|
buf.append(dest.toBase64().substring(0, 6)).append("...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.append("<td>");
|
||||||
|
SessionKey sk = e.getValue();
|
||||||
|
buf.append(sk.toBase64());
|
||||||
|
}
|
||||||
|
buf.append("\n</table>\n");
|
||||||
|
out.write(buf.toString());
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,7 @@ import net.i2p.router.transport.VMCommSystem;
|
|||||||
import net.i2p.router.tunnel.TunnelDispatcher;
|
import net.i2p.router.tunnel.TunnelDispatcher;
|
||||||
import net.i2p.router.tunnel.pool.TunnelPoolManager;
|
import net.i2p.router.tunnel.pool.TunnelPoolManager;
|
||||||
import net.i2p.util.Clock;
|
import net.i2p.util.Clock;
|
||||||
|
import net.i2p.util.KeyRing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build off the core I2P context to provide a root for a router instance to
|
* Build off the core I2P context to provide a root for a router instance to
|
||||||
@ -366,4 +367,21 @@ public class RouterContext extends I2PAppContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** override to support storage in router.config */
|
||||||
|
@Override
|
||||||
|
public KeyRing keyRing() {
|
||||||
|
if (!_keyRingInitialized)
|
||||||
|
initializeKeyRing();
|
||||||
|
return _keyRing;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initializeKeyRing() {
|
||||||
|
synchronized (this) {
|
||||||
|
if (_keyRing == null)
|
||||||
|
_keyRing = new PersistentKeyRing(this);
|
||||||
|
_keyRingInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ import net.i2p.data.i2cp.I2CPMessageReader;
|
|||||||
import net.i2p.data.i2cp.MessageId;
|
import net.i2p.data.i2cp.MessageId;
|
||||||
import net.i2p.data.i2cp.MessageStatusMessage;
|
import net.i2p.data.i2cp.MessageStatusMessage;
|
||||||
import net.i2p.data.i2cp.SendMessageMessage;
|
import net.i2p.data.i2cp.SendMessageMessage;
|
||||||
|
import net.i2p.data.i2cp.SendMessageExpiresMessage;
|
||||||
import net.i2p.data.i2cp.SessionConfig;
|
import net.i2p.data.i2cp.SessionConfig;
|
||||||
import net.i2p.data.i2cp.SessionId;
|
import net.i2p.data.i2cp.SessionId;
|
||||||
import net.i2p.router.Job;
|
import net.i2p.router.Job;
|
||||||
@ -270,6 +271,9 @@ public class ClientConnectionRunner {
|
|||||||
Destination dest = message.getDestination();
|
Destination dest = message.getDestination();
|
||||||
MessageId id = new MessageId();
|
MessageId id = new MessageId();
|
||||||
id.setMessageId(getNextMessageId());
|
id.setMessageId(getNextMessageId());
|
||||||
|
long expiration = 0;
|
||||||
|
if (message instanceof SendMessageExpiresMessage)
|
||||||
|
expiration = ((SendMessageExpiresMessage) message).getExpiration().getTime();
|
||||||
long beforeLock = _context.clock().now();
|
long beforeLock = _context.clock().now();
|
||||||
long inLock = 0;
|
long inLock = 0;
|
||||||
synchronized (_acceptedPending) {
|
synchronized (_acceptedPending) {
|
||||||
@ -291,7 +295,7 @@ public class ClientConnectionRunner {
|
|||||||
// the following blocks as described above
|
// the following blocks as described above
|
||||||
SessionConfig cfg = _config;
|
SessionConfig cfg = _config;
|
||||||
if (cfg != null)
|
if (cfg != null)
|
||||||
_manager.distributeMessage(cfg.getDestination(), dest, payload, id);
|
_manager.distributeMessage(cfg.getDestination(), dest, payload, id, expiration);
|
||||||
long timeToDistribute = _context.clock().now() - beforeDistribute;
|
long timeToDistribute = _context.clock().now() - beforeDistribute;
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.warn("Time to distribute in the manager to "
|
_log.warn("Time to distribute in the manager to "
|
||||||
|
@ -140,7 +140,7 @@ public class ClientManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void distributeMessage(Destination fromDest, Destination toDest, Payload payload, MessageId msgId) {
|
void distributeMessage(Destination fromDest, Destination toDest, Payload payload, MessageId msgId, long expiration) {
|
||||||
// check if there is a runner for it
|
// check if there is a runner for it
|
||||||
ClientConnectionRunner runner = getRunner(toDest);
|
ClientConnectionRunner runner = getRunner(toDest);
|
||||||
if (runner != null) {
|
if (runner != null) {
|
||||||
@ -168,6 +168,7 @@ public class ClientManager {
|
|||||||
msg.setSenderConfig(runner.getConfig());
|
msg.setSenderConfig(runner.getConfig());
|
||||||
msg.setFromDestination(runner.getConfig().getDestination());
|
msg.setFromDestination(runner.getConfig().getDestination());
|
||||||
msg.setMessageId(msgId);
|
msg.setMessageId(msgId);
|
||||||
|
msg.setExpiration(expiration);
|
||||||
_ctx.clientMessagePool().add(msg, true);
|
_ctx.clientMessagePool().add(msg, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,9 @@ import net.i2p.data.i2cp.MessageId;
|
|||||||
import net.i2p.data.i2cp.MessagePayloadMessage;
|
import net.i2p.data.i2cp.MessagePayloadMessage;
|
||||||
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
|
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
|
||||||
import net.i2p.data.i2cp.ReceiveMessageEndMessage;
|
import net.i2p.data.i2cp.ReceiveMessageEndMessage;
|
||||||
|
import net.i2p.data.i2cp.ReconfigureSessionMessage;
|
||||||
import net.i2p.data.i2cp.SendMessageMessage;
|
import net.i2p.data.i2cp.SendMessageMessage;
|
||||||
|
import net.i2p.data.i2cp.SendMessageExpiresMessage;
|
||||||
import net.i2p.data.i2cp.SessionId;
|
import net.i2p.data.i2cp.SessionId;
|
||||||
import net.i2p.data.i2cp.SessionStatusMessage;
|
import net.i2p.data.i2cp.SessionStatusMessage;
|
||||||
import net.i2p.data.i2cp.SetDateMessage;
|
import net.i2p.data.i2cp.SetDateMessage;
|
||||||
@ -67,6 +69,9 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
|
|||||||
case SendMessageMessage.MESSAGE_TYPE:
|
case SendMessageMessage.MESSAGE_TYPE:
|
||||||
handleSendMessage(reader, (SendMessageMessage)message);
|
handleSendMessage(reader, (SendMessageMessage)message);
|
||||||
break;
|
break;
|
||||||
|
case SendMessageExpiresMessage.MESSAGE_TYPE:
|
||||||
|
handleSendMessage(reader, (SendMessageExpiresMessage)message);
|
||||||
|
break;
|
||||||
case ReceiveMessageBeginMessage.MESSAGE_TYPE:
|
case ReceiveMessageBeginMessage.MESSAGE_TYPE:
|
||||||
handleReceiveBegin(reader, (ReceiveMessageBeginMessage)message);
|
handleReceiveBegin(reader, (ReceiveMessageBeginMessage)message);
|
||||||
break;
|
break;
|
||||||
@ -237,6 +242,17 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
|
|||||||
_context.jobQueue().addJob(new LookupDestJob(_context, _runner, message.getHash()));
|
_context.jobQueue().addJob(new LookupDestJob(_context, _runner, message.getHash()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message's Session ID ignored. This doesn't support removing previously set options.
|
||||||
|
* Nor do we bother with message.getSessionConfig().verifySignature() ... should we?
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private void handleReconfigureSession(I2CPMessageReader reader, ReconfigureSessionMessage message) {
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Updating options - session " + _runner.getSessionId());
|
||||||
|
_runner.getConfig().getOptions().putAll(message.getSessionConfig().getOptions());
|
||||||
|
}
|
||||||
|
|
||||||
// this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME
|
// this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME
|
||||||
private final static int MAX_SESSION_ID = 32767;
|
private final static int MAX_SESSION_ID = 32767;
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
|||||||
private long _leaseSetLookupBegin;
|
private long _leaseSetLookupBegin;
|
||||||
private TunnelInfo _outTunnel;
|
private TunnelInfo _outTunnel;
|
||||||
private TunnelInfo _inTunnel;
|
private TunnelInfo _inTunnel;
|
||||||
|
private boolean _wantACK;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* final timeout (in milliseconds) that the outbound message will fail in.
|
* final timeout (in milliseconds) that the outbound message will fail in.
|
||||||
@ -69,6 +70,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
|||||||
*/
|
*/
|
||||||
public final static String OVERALL_TIMEOUT_MS_PARAM = "clientMessageTimeout";
|
public final static String OVERALL_TIMEOUT_MS_PARAM = "clientMessageTimeout";
|
||||||
private final static long OVERALL_TIMEOUT_MS_DEFAULT = 60*1000;
|
private final static long OVERALL_TIMEOUT_MS_DEFAULT = 60*1000;
|
||||||
|
private final static long OVERALL_TIMEOUT_MS_MIN = 5*1000;
|
||||||
|
|
||||||
/** priority of messages, that might get honored some day... */
|
/** priority of messages, that might get honored some day... */
|
||||||
private final static int SEND_PRIORITY = 500;
|
private final static int SEND_PRIORITY = 500;
|
||||||
@ -125,7 +127,17 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
|||||||
_to = msg.getDestination();
|
_to = msg.getDestination();
|
||||||
_toString = _to.calculateHash().toBase64().substring(0,4);
|
_toString = _to.calculateHash().toBase64().substring(0,4);
|
||||||
_leaseSetLookupBegin = -1;
|
_leaseSetLookupBegin = -1;
|
||||||
|
_start = getContext().clock().now();
|
||||||
|
|
||||||
|
// use expiration requested by client if available, otherwise session config,
|
||||||
|
// otherwise router config, otherwise default
|
||||||
|
_overallExpiration = msg.getExpiration();
|
||||||
|
if (_overallExpiration > 0) {
|
||||||
|
_overallExpiration = Math.max(_overallExpiration, _start + OVERALL_TIMEOUT_MS_MIN);
|
||||||
|
_overallExpiration = Math.min(_overallExpiration, _start + OVERALL_TIMEOUT_MS_DEFAULT);
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Message Expiration (ms): " + (_overallExpiration - _start));
|
||||||
|
} else {
|
||||||
String param = msg.getSenderConfig().getOptions().getProperty(OVERALL_TIMEOUT_MS_PARAM);
|
String param = msg.getSenderConfig().getOptions().getProperty(OVERALL_TIMEOUT_MS_PARAM);
|
||||||
if (param == null)
|
if (param == null)
|
||||||
param = ctx.router().getConfigSetting(OVERALL_TIMEOUT_MS_PARAM);
|
param = ctx.router().getConfigSetting(OVERALL_TIMEOUT_MS_PARAM);
|
||||||
@ -139,9 +151,10 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
|||||||
timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
|
timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_start = getContext().clock().now();
|
|
||||||
_overallExpiration = timeoutMs + _start;
|
_overallExpiration = timeoutMs + _start;
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Default Expiration (ms): " + timeoutMs);
|
||||||
|
}
|
||||||
_finished = false;
|
_finished = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,6 +280,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
|||||||
long lookupTime = getContext().clock().now() - _leaseSetLookupBegin;
|
long lookupTime = getContext().clock().now() - _leaseSetLookupBegin;
|
||||||
getContext().statManager().addRateData("client.leaseSetFoundRemoteTime", lookupTime, lookupTime);
|
getContext().statManager().addRateData("client.leaseSetFoundRemoteTime", lookupTime, lookupTime);
|
||||||
}
|
}
|
||||||
|
_wantACK = false;
|
||||||
boolean ok = getNextLease();
|
boolean ok = getNextLease();
|
||||||
if (ok) {
|
if (ok) {
|
||||||
send();
|
send();
|
||||||
@ -400,6 +414,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
|||||||
}
|
}
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Added to cache - lease for " + _toString);
|
_log.info("Added to cache - lease for " + _toString);
|
||||||
|
_wantACK = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,10 +458,14 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
|||||||
dieFatal();
|
dieFatal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean wantACK = true;
|
|
||||||
int existingTags = GarlicMessageBuilder.estimateAvailableTags(getContext(), _leaseSet.getEncryptionKey());
|
int existingTags = GarlicMessageBuilder.estimateAvailableTags(getContext(), _leaseSet.getEncryptionKey());
|
||||||
if ( (existingTags > 30) && (getContext().random().nextInt(100) >= 5) )
|
_outTunnel = selectOutboundTunnel(_to);
|
||||||
wantACK = false;
|
// what's the point of 5% random? possible improvements or replacements:
|
||||||
|
// - wantACK if we changed their inbound lease (getNextLease() sets _wantACK)
|
||||||
|
// - wantACK if we changed our outbound tunnel (selectOutboundTunnel() sets _wantACK)
|
||||||
|
// - wantACK if we haven't in last 1m (requires a new static cache probably)
|
||||||
|
boolean wantACK = _wantACK || existingTags <= 30 || getContext().random().nextInt(100) < 5;
|
||||||
|
|
||||||
PublicKey key = _leaseSet.getEncryptionKey();
|
PublicKey key = _leaseSet.getEncryptionKey();
|
||||||
SessionKey sessKey = new SessionKey();
|
SessionKey sessKey = new SessionKey();
|
||||||
@ -503,7 +522,6 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
|||||||
+ _lease.getTunnelId() + " on "
|
+ _lease.getTunnelId() + " on "
|
||||||
+ _lease.getGateway().toBase64());
|
+ _lease.getGateway().toBase64());
|
||||||
|
|
||||||
_outTunnel = selectOutboundTunnel(_to);
|
|
||||||
if (_outTunnel != null) {
|
if (_outTunnel != null) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(getJobId() + ": Sending tunnel message out " + _outTunnel.getSendTunnelId(0) + " to "
|
_log.debug(getJobId() + ": Sending tunnel message out " + _outTunnel.getSendTunnelId(0) + " to "
|
||||||
@ -718,6 +736,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
|||||||
_log.warn("Switching back to tunnel " + tunnel + " for " + _toString);
|
_log.warn("Switching back to tunnel " + tunnel + " for " + _toString);
|
||||||
_backloggedTunnelCache.remove(hashPair());
|
_backloggedTunnelCache.remove(hashPair());
|
||||||
_tunnelCache.put(hashPair(), tunnel);
|
_tunnelCache.put(hashPair(), tunnel);
|
||||||
|
_wantACK = true;
|
||||||
return tunnel;
|
return tunnel;
|
||||||
} // else still backlogged
|
} // else still backlogged
|
||||||
} else // no longer valid
|
} else // no longer valid
|
||||||
@ -740,6 +759,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
|||||||
tunnel = selectOutboundTunnel();
|
tunnel = selectOutboundTunnel();
|
||||||
if (tunnel != null)
|
if (tunnel != null)
|
||||||
_tunnelCache.put(hashPair(), tunnel);
|
_tunnelCache.put(hashPair(), tunnel);
|
||||||
|
_wantACK = true;
|
||||||
}
|
}
|
||||||
return tunnel;
|
return tunnel;
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,12 @@ import net.i2p.router.RouterContext;
|
|||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publish the local router's RouterInfo every 5 to 10 minutes
|
* Publish the local router's RouterInfo periodically
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class PublishLocalRouterInfoJob extends JobImpl {
|
public class PublishLocalRouterInfoJob extends JobImpl {
|
||||||
private Log _log;
|
private Log _log;
|
||||||
final static long PUBLISH_DELAY = 5*60*1000; // every 5 to 10 minutes (since we randomize)
|
final static long PUBLISH_DELAY = 20*60*1000;
|
||||||
|
|
||||||
public PublishLocalRouterInfoJob(RouterContext ctx) {
|
public PublishLocalRouterInfoJob(RouterContext ctx) {
|
||||||
super(ctx);
|
super(ctx);
|
||||||
@ -67,6 +67,6 @@ public class PublishLocalRouterInfoJob extends JobImpl {
|
|||||||
} catch (DataFormatException dfe) {
|
} catch (DataFormatException dfe) {
|
||||||
_log.error("Error signing the updated local router info!", dfe);
|
_log.error("Error signing the updated local router info!", dfe);
|
||||||
}
|
}
|
||||||
requeue(PUBLISH_DELAY + getContext().random().nextInt((int)PUBLISH_DELAY));
|
requeue((PUBLISH_DELAY/2) + getContext().random().nextInt((int)PUBLISH_DELAY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
private final static long ROUTER_INFO_EXPIRATION_SHORT = 90*60*1000l;
|
private final static long ROUTER_INFO_EXPIRATION_SHORT = 90*60*1000l;
|
||||||
|
|
||||||
private final static long EXPLORE_JOB_DELAY = 10*60*1000l;
|
private final static long EXPLORE_JOB_DELAY = 10*60*1000l;
|
||||||
|
private final static long PUBLISH_JOB_DELAY = 5*60*1000l;
|
||||||
|
|
||||||
public KademliaNetworkDatabaseFacade(RouterContext context) {
|
public KademliaNetworkDatabaseFacade(RouterContext context) {
|
||||||
_context = context;
|
_context = context;
|
||||||
@ -326,7 +327,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
}
|
}
|
||||||
// periodically update and resign the router's 'published date', which basically
|
// periodically update and resign the router's 'published date', which basically
|
||||||
// serves as a version
|
// serves as a version
|
||||||
_context.jobQueue().addJob(new PublishLocalRouterInfoJob(_context));
|
Job plrij = new PublishLocalRouterInfoJob(_context);
|
||||||
|
plrij.getTiming().setStartAfter(_context.clock().now() + PUBLISH_JOB_DELAY);
|
||||||
|
_context.jobQueue().addJob(plrij);
|
||||||
try {
|
try {
|
||||||
publish(ri);
|
publish(ri);
|
||||||
} catch (IllegalArgumentException iae) {
|
} catch (IllegalArgumentException iae) {
|
||||||
@ -970,7 +973,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
StringBuffer buf = new StringBuffer(4*1024);
|
StringBuffer buf = new StringBuffer(4*1024);
|
||||||
buf.append("<h2>Network Database RouterInfo Lookup</h2>\n");
|
buf.append("<h2>Network Database RouterInfo Lookup</h2>\n");
|
||||||
if (".".equals(routerPrefix)) {
|
if (".".equals(routerPrefix)) {
|
||||||
renderRouterInfo(buf, _context.router().getRouterInfo(), true);
|
renderRouterInfo(buf, _context.router().getRouterInfo(), true, true);
|
||||||
} else {
|
} else {
|
||||||
boolean notFound = true;
|
boolean notFound = true;
|
||||||
Set routers = getRouters();
|
Set routers = getRouters();
|
||||||
@ -978,7 +981,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
RouterInfo ri = (RouterInfo)iter.next();
|
RouterInfo ri = (RouterInfo)iter.next();
|
||||||
Hash key = ri.getIdentity().getHash();
|
Hash key = ri.getIdentity().getHash();
|
||||||
if (key.toBase64().startsWith(routerPrefix)) {
|
if (key.toBase64().startsWith(routerPrefix)) {
|
||||||
renderRouterInfo(buf, ri, false);
|
renderRouterInfo(buf, ri, false, true);
|
||||||
notFound = false;
|
notFound = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -990,7 +993,14 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void renderStatusHTML(Writer out) throws IOException {
|
public void renderStatusHTML(Writer out) throws IOException {
|
||||||
StringBuffer buf = new StringBuffer(getKnownRouters() * 2048);
|
renderStatusHTML(out, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderStatusHTML(Writer out, boolean full) throws IOException {
|
||||||
|
int size = getKnownRouters() * 512;
|
||||||
|
if (full)
|
||||||
|
size *= 4;
|
||||||
|
StringBuffer buf = new StringBuffer(size);
|
||||||
buf.append("<h2>Network Database Contents</h2>\n");
|
buf.append("<h2>Network Database Contents</h2>\n");
|
||||||
if (!_initialized) {
|
if (!_initialized) {
|
||||||
buf.append("<i>Not initialized</i>\n");
|
buf.append("<i>Not initialized</i>\n");
|
||||||
@ -1044,10 +1054,15 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Hash us = _context.routerHash();
|
Hash us = _context.routerHash();
|
||||||
out.write("<h3>Routers</h3>\n");
|
out.write("<a name=\"routers\" /><h3>Routers (<a href=\"netdb.jsp");
|
||||||
|
if (full)
|
||||||
|
out.write("#routers\" >view without");
|
||||||
|
else
|
||||||
|
out.write("?f=1#routers\" >view with");
|
||||||
|
out.write(" stats</a>)</h3>\n");
|
||||||
|
|
||||||
RouterInfo ourInfo = _context.router().getRouterInfo();
|
RouterInfo ourInfo = _context.router().getRouterInfo();
|
||||||
renderRouterInfo(buf, ourInfo, true);
|
renderRouterInfo(buf, ourInfo, true, true);
|
||||||
out.write(buf.toString());
|
out.write(buf.toString());
|
||||||
buf.setLength(0);
|
buf.setLength(0);
|
||||||
|
|
||||||
@ -1061,7 +1076,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
Hash key = ri.getIdentity().getHash();
|
Hash key = ri.getIdentity().getHash();
|
||||||
boolean isUs = key.equals(us);
|
boolean isUs = key.equals(us);
|
||||||
if (!isUs) {
|
if (!isUs) {
|
||||||
renderRouterInfo(buf, ri, false);
|
renderRouterInfo(buf, ri, false, full);
|
||||||
out.write(buf.toString());
|
out.write(buf.toString());
|
||||||
buf.setLength(0);
|
buf.setLength(0);
|
||||||
String coreVersion = ri.getOption("coreVersion");
|
String coreVersion = ri.getOption("coreVersion");
|
||||||
@ -1102,7 +1117,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderRouterInfo(StringBuffer buf, RouterInfo info, boolean isUs) {
|
private void renderRouterInfo(StringBuffer buf, RouterInfo info, boolean isUs, boolean full) {
|
||||||
String hash = info.getIdentity().getHash().toBase64();
|
String hash = info.getIdentity().getHash().toBase64();
|
||||||
buf.append("<a name=\"").append(hash.substring(0, 6)).append("\" />");
|
buf.append("<a name=\"").append(hash.substring(0, 6)).append("\" />");
|
||||||
if (isUs) {
|
if (isUs) {
|
||||||
@ -1129,13 +1144,18 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf.append("</i><br />\n");
|
buf.append("</i><br />\n");
|
||||||
|
if (full) {
|
||||||
buf.append("Stats: <br /><i><code>\n");
|
buf.append("Stats: <br /><i><code>\n");
|
||||||
for (Iterator iter = info.getOptions().keySet().iterator(); iter.hasNext(); ) {
|
for (Iterator iter = info.getOptions().keySet().iterator(); iter.hasNext(); ) {
|
||||||
String key = (String)iter.next();
|
String key = (String)iter.next();
|
||||||
String val = info.getOption(key);
|
String val = info.getOption(key);
|
||||||
buf.append(DataHelper.stripHTML(key)).append(" = ").append(DataHelper.stripHTML(val)).append("<br />\n");
|
buf.append(DataHelper.stripHTML(key)).append(" = ").append(DataHelper.stripHTML(val)).append("<br />\n");
|
||||||
}
|
}
|
||||||
buf.append("</code></i><hr />\n");
|
buf.append("</code></i>\n");
|
||||||
|
} else {
|
||||||
|
buf.append("<a href=\"netdb.jsp?r=").append(hash.substring(0, 6)).append("\" >Full entry</a>\n");
|
||||||
|
}
|
||||||
|
buf.append("<hr />\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -65,10 +65,6 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
return super.remove(key);
|
return super.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataStructure removeLease(Hash key) {
|
|
||||||
return super.removeLease(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void put(Hash key, DataStructure data) {
|
public void put(Hash key, DataStructure data) {
|
||||||
if ( (data == null) || (key == null) ) return;
|
if ( (data == null) || (key == null) ) return;
|
||||||
super.put(key, data);
|
super.put(key, data);
|
||||||
@ -77,26 +73,6 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
_writer.queue(key, data);
|
_writer.queue(key, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* We don't store leasesets here anymore, use the TransientDataStore count
|
|
||||||
*
|
|
||||||
public int countLeaseSets() {
|
|
||||||
File dbDir = null;
|
|
||||||
try {
|
|
||||||
dbDir = getDbDir();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (dbDir == null)
|
|
||||||
return 0;
|
|
||||||
File leaseSetFiles[] = dbDir.listFiles(LeaseSetFilter.getInstance());
|
|
||||||
if (leaseSetFiles == null)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return leaseSetFiles.length;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
private void accept(LeaseSet ls) {
|
private void accept(LeaseSet ls) {
|
||||||
super.put(ls.getDestination().calculateHash(), ls);
|
super.put(ls.getDestination().calculateHash(), ls);
|
||||||
}
|
}
|
||||||
@ -249,18 +225,6 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
int routerCount = 0;
|
int routerCount = 0;
|
||||||
try {
|
try {
|
||||||
File dbDir = getDbDir();
|
File dbDir = getDbDir();
|
||||||
/****
|
|
||||||
if (getContext().router().getUptime() < 10*60*1000) {
|
|
||||||
File leaseSetFiles[] = dbDir.listFiles(LeaseSetFilter.getInstance());
|
|
||||||
if (leaseSetFiles != null) {
|
|
||||||
for (int i = 0; i < leaseSetFiles.length; i++) {
|
|
||||||
Hash key = getLeaseSetHash(leaseSetFiles[i].getName());
|
|
||||||
if ( (key != null) && (!isKnown(key)) )
|
|
||||||
PersistentDataStore.this._context.jobQueue().addJob(new ReadLeaseJob(leaseSetFiles[i], key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
****/
|
|
||||||
File routerInfoFiles[] = dbDir.listFiles(RouterInfoFilter.getInstance());
|
File routerInfoFiles[] = dbDir.listFiles(RouterInfoFilter.getInstance());
|
||||||
if (routerInfoFiles != null) {
|
if (routerInfoFiles != null) {
|
||||||
routerCount += routerInfoFiles.length;
|
routerCount += routerInfoFiles.length;
|
||||||
@ -283,63 +247,6 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/****
|
|
||||||
private class ReadLeaseJob extends JobImpl {
|
|
||||||
private File _leaseFile;
|
|
||||||
private Hash _key;
|
|
||||||
public ReadLeaseJob(File leaseFile, Hash key) {
|
|
||||||
super(PersistentDataStore.this._context);
|
|
||||||
_leaseFile = leaseFile;
|
|
||||||
_key = key;
|
|
||||||
}
|
|
||||||
public String getName() { return "Read LeaseSet"; }
|
|
||||||
private boolean shouldRead() {
|
|
||||||
DataStructure data = get(_key);
|
|
||||||
if (data == null) return true;
|
|
||||||
if (data instanceof LeaseSet) {
|
|
||||||
long knownDate = ((LeaseSet)data).getEarliestLeaseDate();
|
|
||||||
long fileDate = _leaseFile.lastModified();
|
|
||||||
if (fileDate > knownDate)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// wtf
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void runJob() {
|
|
||||||
if (!shouldRead()) return;
|
|
||||||
try {
|
|
||||||
FileInputStream fis = null;
|
|
||||||
boolean corrupt = false;
|
|
||||||
try {
|
|
||||||
fis = new FileInputStream(_leaseFile);
|
|
||||||
LeaseSet ls = new LeaseSet();
|
|
||||||
ls.readBytes(fis);
|
|
||||||
try {
|
|
||||||
_facade.store(ls.getDestination().calculateHash(), ls);
|
|
||||||
} catch (IllegalArgumentException iae) {
|
|
||||||
_log.info("Refused locally loaded leaseSet - deleting");
|
|
||||||
corrupt = true;
|
|
||||||
}
|
|
||||||
} catch (DataFormatException dfe) {
|
|
||||||
_log.warn("Error reading the leaseSet from " + _leaseFile.getAbsolutePath(), dfe);
|
|
||||||
corrupt = true;
|
|
||||||
} catch (FileNotFoundException fnfe) {
|
|
||||||
_log.debug("Deleted prior to read.. a race during expiration / load");
|
|
||||||
corrupt = false;
|
|
||||||
} finally {
|
|
||||||
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
|
|
||||||
}
|
|
||||||
if (corrupt) _leaseFile.delete();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.warn("Error reading the leaseSet from " + _leaseFile.getAbsolutePath(), ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
****/
|
|
||||||
|
|
||||||
private class ReadRouterJob extends JobImpl {
|
private class ReadRouterJob extends JobImpl {
|
||||||
private File _routerFile;
|
private File _routerFile;
|
||||||
private Hash _key;
|
private Hash _key;
|
||||||
@ -464,31 +371,8 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
_log.info("Removed router info at " + f.getAbsolutePath());
|
_log.info("Removed router info at " + f.getAbsolutePath());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/***
|
|
||||||
String lsName = getLeaseSetName(key);
|
|
||||||
File f = new File(dir, lsName);
|
|
||||||
if (f.exists()) {
|
|
||||||
boolean removed = f.delete();
|
|
||||||
if (!removed)
|
|
||||||
_log.warn("Unable to remove lease set at " + f.getAbsolutePath());
|
|
||||||
else
|
|
||||||
_log.info("Removed lease set at " + f.getAbsolutePath());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
***/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
private final static class LeaseSetFilter implements FilenameFilter {
|
|
||||||
private static final FilenameFilter _instance = new LeaseSetFilter();
|
|
||||||
public static final FilenameFilter getInstance() { return _instance; }
|
|
||||||
public boolean accept(File dir, String name) {
|
|
||||||
if (name == null) return false;
|
|
||||||
name = name.toUpperCase();
|
|
||||||
return (name.startsWith(LEASESET_PREFIX.toUpperCase()) && name.endsWith(LEASESET_SUFFIX.toUpperCase()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
***/
|
|
||||||
private final static class RouterInfoFilter implements FilenameFilter {
|
private final static class RouterInfoFilter implements FilenameFilter {
|
||||||
private static final FilenameFilter _instance = new RouterInfoFilter();
|
private static final FilenameFilter _instance = new RouterInfoFilter();
|
||||||
public static final FilenameFilter getInstance() { return _instance; }
|
public static final FilenameFilter getInstance() { return _instance; }
|
||||||
|
@ -28,6 +28,13 @@ import net.i2p.router.RouterContext;
|
|||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* This used be called from StartAcceptingClientsJob but is now disabled.
|
||||||
|
* It is still called once from LoadRouterInfoJob (but not run as a Job).
|
||||||
|
*
|
||||||
|
* The following comments appear to be incorrect...
|
||||||
|
* it rebuilds if the router.info file does not exist.
|
||||||
|
* There is no check for a router.info.rebuild file.
|
||||||
|
*
|
||||||
* If the file router.info.rebuild exists, rebuild the router info and republish.
|
* If the file router.info.rebuild exists, rebuild the router info and republish.
|
||||||
* This is useful for dhcp or other situations where the router addresses change -
|
* This is useful for dhcp or other situations where the router addresses change -
|
||||||
* simply create the router.info.rebuild file after modifying router.config and within
|
* simply create the router.info.rebuild file after modifying router.config and within
|
||||||
|
@ -28,7 +28,8 @@ public class StartAcceptingClientsJob extends JobImpl {
|
|||||||
getContext().clientManager().startup();
|
getContext().clientManager().startup();
|
||||||
|
|
||||||
getContext().jobQueue().addJob(new ReadConfigJob(getContext()));
|
getContext().jobQueue().addJob(new ReadConfigJob(getContext()));
|
||||||
getContext().jobQueue().addJob(new RebuildRouterInfoJob(getContext()));
|
// pointless
|
||||||
|
//getContext().jobQueue().addJob(new RebuildRouterInfoJob(getContext()));
|
||||||
getContext().jobQueue().allowParallelOperation();
|
getContext().jobQueue().allowParallelOperation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -507,7 +507,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
|||||||
}
|
}
|
||||||
out.write("</table>\n");
|
out.write("</table>\n");
|
||||||
out.write("Inactive participating tunnels: " + inactive + "<br />\n");
|
out.write("Inactive participating tunnels: " + inactive + "<br />\n");
|
||||||
out.write("Lifetime bandwidth usage: " + processed + "KB<br />\n");
|
out.write("Lifetime bandwidth usage: " + DataHelper.formatSize(processed*1024) + "B<br />\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
class TunnelComparator implements Comparator {
|
class TunnelComparator implements Comparator {
|
||||||
@ -577,7 +577,8 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
|||||||
}
|
}
|
||||||
if (live <= 0)
|
if (live <= 0)
|
||||||
out.write("<b>No tunnels, waiting for the grace period to end</b><br />\n");
|
out.write("<b>No tunnels, waiting for the grace period to end</b><br />\n");
|
||||||
out.write("Lifetime bandwidth usage: " + processedIn + "KB in, " + processedOut + "KB out<br />");
|
out.write("Lifetime bandwidth usage: " + DataHelper.formatSize(processedIn*1024) + "B in, " +
|
||||||
|
DataHelper.formatSize(processedOut*1024) + "B out<br />");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getCapacity(Hash peer) {
|
private String getCapacity(Hash peer) {
|
||||||
|
Reference in New Issue
Block a user