propagate from branch 'i2p.i2p.zzz.test' (head f4edeaaf6cd647f4a69847a09272b54cb51ef758)

to branch 'i2p.i2p' (head 0d7e18b693718b5924035d7a6f638ff0689af589)
This commit is contained in:
zzz
2009-01-25 01:15:45 +00:00
54 changed files with 1638 additions and 518 deletions

View File

@ -662,7 +662,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
* "openSOCKSTunnelResult" = "ok" or "error" after the client tunnel has
* started.
*
* @param args {portNumber}
* @param args {portNumber [, sharedClient]}
* @param l logger to receive events and output
*/
public void runSOCKSTunnel(String args[], Logging l) {
@ -677,6 +677,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
return;
}
boolean isShared = false;
if (args.length > 1)
isShared = "true".equalsIgnoreCase(args[1].trim());
ownDest = !isShared;
I2PTunnelTask task;
task = new I2PSOCKSTunnel(port, l, ownDest, (EventDispatcher) this, this);
addtask(task);

View File

@ -135,8 +135,10 @@ public class TunnelController implements Logging {
}
if ("httpclient".equals(type)) {
startHttpClient();
}else if("ircclient".equals(type)) {
} else if("ircclient".equals(type)) {
startIrcClient();
} else if("sockstunnel".equals(type)) {
startSocksClient();
} else if ("client".equals(type)) {
startClient();
} else if ("server".equals(type)) {
@ -176,6 +178,17 @@ public class TunnelController implements Logging {
_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
* closed by some other tunnels

View File

@ -7,6 +7,12 @@
package net.i2p.i2ptunnel.socks;
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.data.Destination;
@ -20,7 +26,7 @@ import net.i2p.util.Log;
public class I2PSOCKSTunnel extends I2PTunnelClientBase {
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;
//public I2PSOCKSTunnel(int localPort, Logging l, boolean ownDest) {
@ -36,7 +42,7 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
}
setName(getLocalPort() + " -> SOCKSTunnel");
parseOptions();
startRunning();
notifyEvent("openSOCKSTunnelResult", "ok");
@ -46,11 +52,49 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
try {
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s);
Socket clientSock = serv.getClientSocket();
I2PSocket destSock = serv.getDestinationI2PSocket();
I2PSocket destSock = serv.getDestinationI2PSocket(this);
new I2PTunnelRunner(clientSock, destSock, sockLock, null, mySockets);
} catch (SOCKSException e) {
_log.error("Error from SOCKS connection: " + e.getMessage());
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");
}
}

View File

@ -12,7 +12,14 @@ import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
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.Log;
@ -28,7 +35,6 @@ public class SOCKS5Server extends SOCKSServer {
private static final int SOCKS_VERSION_5 = 0x05;
private Socket clientSock = null;
private boolean setupCompleted = false;
/**
@ -126,6 +132,7 @@ public class SOCKS5Server extends SOCKSServer {
throw new SOCKSException("UDP ASSOCIATE command not supported");
default:
_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");
}
@ -166,12 +173,14 @@ public class SOCKS5Server extends SOCKSServer {
throw new SOCKSException("IPV6 addresses not supported");
default:
_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");
}
connPort = in.readUnsignedShort();
if (connPort == 0) {
_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");
}
}
@ -248,6 +257,107 @@ public class SOCKS5Server extends SOCKSServer {
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
*/
@ -279,4 +389,4 @@ public class SOCKS5Server extends SOCKSServer {
private static final int COMMAND_NOT_SUPPORTED = 0x07;
private static final int ADDRESS_TYPE_NOT_SUPPORTED = 0x08;
}
}
}

View File

@ -6,15 +6,9 @@
*/
package net.i2p.i2ptunnel.socks;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import net.i2p.I2PException;
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.util.Log;
@ -30,10 +24,6 @@ public abstract class SOCKSServer {
protected String connHostName = null;
protected int connPort = 0;
I2PSocket destSocket = null;
Object FIXME = new Object();
/**
* Perform server initialization (expecially regarding protected
* variables).
@ -59,47 +49,6 @@ public abstract class SOCKSServer {
*
* @return an I2PSocket connected with the destination
*/
public I2PSocket getDestinationI2PSocket() throws SOCKSException {
setupServer();
public abstract I2PSocket getDestinationI2PSocket(I2PSOCKSTunnel t) throws SOCKSException;
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;
}
}

View File

@ -7,6 +7,7 @@
package net.i2p.i2ptunnel.socks;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
@ -18,6 +19,15 @@ import net.i2p.util.Log;
public class SOCKSServerFactory {
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
* be connected to a client) to select the proper SOCKS protocol
@ -38,9 +48,15 @@ public class SOCKSServerFactory {
// SOCKS version 5
serv = new SOCKS5Server(s);
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:
_log.debug("SOCKS protocol version not supported (" + Integer.toHexString(socksVer) + ")");
return null;
throw new SOCKSException("SOCKS protocol version not supported (" + Integer.toHexString(socksVer) + ")");
}
} catch (IOException e) {
_log.debug("error reading SOCKS protocol version");
@ -49,4 +65,4 @@ public class SOCKSServerFactory {
return serv;
}
}
}

View File

@ -8,9 +8,12 @@ package net.i2p.i2ptunnel.web;
*
*/
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import net.i2p.i2ptunnel.TunnelController;
@ -28,9 +31,7 @@ public class EditBean extends IndexBean {
if (controllers.size() > tunnel) {
TunnelController cur = (TunnelController)controllers.get(tunnel);
if (cur == null) return false;
return ( ("client".equals(cur.getType())) ||
("httpclient".equals(cur.getType()))||
("ircclient".equals(cur.getType())));
return isClient(cur.getType());
} else {
return false;
}
@ -38,7 +39,7 @@ public class EditBean extends IndexBean {
public String getTargetHost(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
if (tun != null && tun.getTargetHost() != null)
return tun.getTargetHost();
else
return "127.0.0.1";
@ -52,7 +53,7 @@ public class EditBean extends IndexBean {
}
public String getSpoofedHost(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
if (tun != null && tun.getSpoofedHost() != null)
return tun.getSpoofedHost();
else
return "";
@ -82,119 +83,100 @@ public class EditBean extends IndexBean {
}
public boolean shouldDelay(int tunnel) {
TunnelController tun = getController(tunnel);
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;
}
return getProperty(tunnel, "i2p.streaming.connectDelay", 0) > 0;
}
public boolean isInteractive(int tunnel) {
TunnelController tun = getController(tunnel);
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;
}
return getProperty(tunnel, "i2p.streaming.maxWindowSize", 128) == 12;
}
public int getTunnelDepth(int tunnel, int defaultLength) {
TunnelController tun = getController(tunnel);
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;
}
return getProperty(tunnel, "inbound.length", defaultLength);
}
public int getTunnelQuantity(int tunnel, int defaultQuantity) {
TunnelController tun = getController(tunnel);
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;
}
return getProperty(tunnel, "inbound.quantity", defaultQuantity);
}
public int getTunnelBackupQuantity(int tunnel, int defaultBackupQuantity) {
TunnelController tun = getController(tunnel);
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;
}
return getProperty(tunnel, "inbound.backupQuantity", defaultBackupQuantity);
}
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);
if (tun != null) {
Properties opts = getOptions(tun);
if (opts != null) {
String len = opts.getProperty("inbound.lengthVariance");
if (len == null) return defaultVariance;
String s = opts.getProperty(prop);
if (s == null) return def;
try {
return Integer.parseInt(len);
} catch (NumberFormatException nfe) {
return defaultVariance;
}
} else {
return defaultVariance;
return Integer.parseInt(s);
} catch (NumberFormatException nfe) {}
}
} else {
return defaultVariance;
}
return def;
}
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) {
@ -213,6 +195,14 @@ public class EditBean extends IndexBean {
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) {
TunnelController tun = getController(tunnel);
if (tun != null) {
@ -222,19 +212,9 @@ public class EditBean extends IndexBean {
int i = 0;
for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
if (noShowSet.contains(key))
continue;
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(' ');
buf.append(key).append('=').append(val);
i++;

View File

@ -209,10 +209,7 @@ public class IndexBean {
}
// Only modify other shared tunnels
// if the current tunnel is shared, and of supported type
if ("true".equalsIgnoreCase(cur.getSharedClient()) &&
("ircclient".equals(cur.getType()) ||
"httpclient".equals(cur.getType()) ||
"client".equals(cur.getType()))) {
if ("true".equalsIgnoreCase(cur.getSharedClient()) && isClient(cur.getType())) {
// all clients use the same I2CP session, and as such, use the same I2CP options
List controllers = _group.getControllers();
@ -224,11 +221,7 @@ public class IndexBean {
// Only modify this non-current tunnel
// if it belongs to a shared destination, and is of supported type
if ("true".equalsIgnoreCase(c.getSharedClient()) &&
("httpclient".equals(c.getType()) ||
"ircclient".equals(c.getType()) ||
"client".equals(c.getType()))) {
if ("true".equalsIgnoreCase(c.getSharedClient()) && isClient(c.getType())) {
Properties cOpt = c.getConfig("");
if (_tunnelQuantity != null) {
cOpt.setProperty("option.inbound.quantity", _tunnelQuantity);
@ -326,9 +319,14 @@ public class IndexBean {
public boolean isClient(int tunnelNum) {
TunnelController cur = getController(tunnelNum);
if (cur == null) return false;
return ( ("client".equals(cur.getType())) ||
("httpclient".equals(cur.getType())) ||
("ircclient".equals(cur.getType())));
return isClient(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) {
@ -361,6 +359,7 @@ public class IndexBean {
else if ("ircclient".equals(internalType)) return "IRC client";
else if ("server".equals(internalType)) return "Standard server";
else if ("httpserver".equals(internalType)) return "HTTP server";
else if ("sockstunnel".equals(internalType)) return "SOCKS proxy";
else return internalType;
}
@ -579,77 +578,40 @@ public class IndexBean {
Properties config = new Properties();
updateConfigGeneric(config);
if ("httpclient".equals(_type)) {
if (isClient(_type)) {
// generic client stuff
if (_port != null)
config.setProperty("listenPort", _port);
if (_reachableByOther != null)
config.setProperty("interface", _reachableByOther);
else
config.setProperty("interface", _reachableBy);
if (_proxyList != null)
config.setProperty("proxyList", _proxyList);
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
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)
config.setProperty("targetDestination", _targetDestination);
} 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);
}
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 ("httpclient".equals(_type)) {
if (_proxyList != null)
config.setProperty("proxyList", _proxyList);
} else if ("ircclient".equals(_type) || "client".equals(_type)) {
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)) {
if (_targetHost != null)
config.setProperty("targetHost", _targetHost);
if (_targetPort != null)
config.setProperty("targetPort", _targetPort);
if (_privKeyFile != null)
config.setProperty("privKeyFile", _privKeyFile);
if (_spoofedHost != null)
config.setProperty("spoofedHost", _spoofedHost);
} else {
return null;
}
return config;

View File

@ -14,7 +14,7 @@ String tun = request.getParameter("tunnel");
} else {
String type = request.getParameter("type");
int curTunnel = -1;
if ("client".equals(type) || "httpclient".equals(type) || "ircclient".equals(type)) {
if (EditBean.isClient(type)) {
%><jsp:include page="editClient.jsp" /><%
} else if ("server".equals(type) || "httpserver".equals(type)) {
%><jsp:include page="editServer.jsp" /><%
@ -22,4 +22,4 @@ String tun = request.getParameter("tunnel");
%>Invalid tunnel type<%
}
}
%>
%>

View File

@ -116,14 +116,14 @@
<hr />
</div>
<% if ("httpclient".equals(editBean.getInternalType(curTunnel))) {
<% if ("httpclient".equals(tunnelType)) {
%><div id="destinationField" class="rowItem">
<label for="proxyList" accesskey="x">
Outpro<span class="accessKey">x</span>ies:
</label>
<input type="text" size="30" id="proxyList" name="proxyList" title="List of Outproxy I2P destinations" value="<%=editBean.getClientDestination(curTunnel)%>" class="freetext" />
</div>
<% } else {
<% } else if ("client".equals(tunnelType) || "ircclient".equals(tunnelType)) {
%><div id="destinationField" class="rowItem">
<label for="targetDestination" accesskey="T">
<span class="accessKey">T</span>unnel Destination:
@ -205,12 +205,12 @@
<span class="accessKey">V</span>ariance:
</label>
<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="-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="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) {
%> <option value="<%=tunnelVariance%>" selected="selected"><%= (tunnelVariance > 2 ? "+ " : "+/- ") %>0-<%=tunnelVariance%> hop variance</option>
<% }
@ -265,6 +265,62 @@
</label>
<input type="text" id="clientPort" name="clientport" size="20" title="I2CP Port Number" value="<%=editBean.getI2CPPort(curTunnel)%>" class="freetext" />
</div>
<div class="subdivider">
<hr />
</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 />
@ -284,8 +340,10 @@
<div class="header"></div>
<div class="footer">
<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" />
<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>

View File

@ -110,7 +110,8 @@
<label for="spoofedHost" accesskey="W">
<span class="accessKey">W</span>ebsite name:
</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 id="privKeyField" class="rowItem">
@ -177,12 +178,12 @@
<span class="accessKey">V</span>ariance:
</label>
<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="-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="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) {
%> <option value="<%=tunnelVariance%>" selected="selected"><%= (tunnelVariance > 2 ? "+ " : "+/- ") %>0-<%=tunnelVariance%> hop variance</option>
<% }
@ -242,6 +243,130 @@
<hr />
</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">
<label for="customOptions" accesskey="u">
C<span class="accessKey">u</span>stom options:
@ -256,8 +381,10 @@
<div class="header"></div>
<div class="footer">
<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" />
<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>

View File

@ -112,10 +112,12 @@
}
%></div>
<% if (!"sockstunnel".equals(indexBean.getInternalType(curClient))) { %>
<div class="destinationField rowItem">
<label>Destination:</label>
<input class="freetext" size="40" readonly="readonly" value="<%=indexBean.getClientDestination(curClient)%>" />
</div>
<% } %>
<div class="descriptionField rowItem">
<label>Description:</label>
@ -140,6 +142,7 @@
<option value="client">Standard</option>
<option value="httpclient">HTTP</option>
<option value="ircclient">IRC</option>
<option value="sockstunnel">SOCKS</option>
</select>
<input class="control" type="submit" value="Create" />
</div>

View File

@ -78,7 +78,6 @@
<include name="jasper-runtime.jar" />
<include name="javax.servlet.jar" />
<include name="org.mortbay.jetty.jar" />
<include name="xercesImpl.jar" />
</fileset>
</copy>
<delete dir="jetty-5.1.12" />

View File

@ -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; }
}

View File

@ -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());
}
}

View File

@ -11,6 +11,7 @@ public class NetDbHelper {
private RouterContext _context;
private Writer _out;
private String _routerPrefix;
private boolean _full = false;
/**
* 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 setRouter(String r) { _routerPrefix = r; }
public void setFull(String f) { _full = "1".equals(f); };
public String getNetDbSummary() {
try {
@ -37,14 +39,14 @@ public class NetDbHelper {
if (_routerPrefix != null)
_context.netDb().renderRouterInfoHTML(_out, _routerPrefix);
else
_context.netDb().renderStatusHTML(_out);
_context.netDb().renderStatusHTML(_out, _full);
return "";
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
if (_routerPrefix != null)
_context.netDb().renderRouterInfoHTML(new OutputStreamWriter(baos), _routerPrefix);
else
_context.netDb().renderStatusHTML(new OutputStreamWriter(baos));
_context.netDb().renderStatusHTML(new OutputStreamWriter(baos), _full);
return new String(baos.toByteArray());
}
} catch (IOException ioe) {

View File

@ -244,7 +244,7 @@ public class SummaryHelper {
*/
public String getInboundSecondKBps() {
if (_context == null)
return "0.0";
return "0";
double kbps = _context.bandwidthLimiter().getReceiveBps()/1024d;
DecimalFormat fmt = new DecimalFormat("##0.00");
return fmt.format(kbps);
@ -256,7 +256,7 @@ public class SummaryHelper {
*/
public String getOutboundSecondKBps() {
if (_context == null)
return "0.0";
return "0";
double kbps = _context.bandwidthLimiter().getSendBps()/1024d;
DecimalFormat fmt = new DecimalFormat("##0.00");
return fmt.format(kbps);
@ -269,10 +269,10 @@ public class SummaryHelper {
*/
public String getInboundFiveMinuteKBps() {
if (_context == null)
return "0.0";
return "0";
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);
double kbps = rate.getAverageValue()/1024;
DecimalFormat fmt = new DecimalFormat("##0.00");
@ -286,10 +286,10 @@ public class SummaryHelper {
*/
public String getOutboundFiveMinuteKBps() {
if (_context == null)
return "0.0";
return "0";
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);
double kbps = rate.getAverageValue()/1024;
DecimalFormat fmt = new DecimalFormat("##0.00");
@ -303,10 +303,10 @@ public class SummaryHelper {
*/
public String getInboundLifetimeKBps() {
if (_context == null)
return "0.0";
return "0";
RateStat receiveRate = _context.statManager().getRate("bw.recvRate");
if (receiveRate == null) return "0.0";
if (receiveRate == null) return "0";
double kbps = receiveRate.getLifetimeAverageValue()/1024;
DecimalFormat fmt = new DecimalFormat("##0.00");
return fmt.format(kbps);
@ -319,10 +319,10 @@ public class SummaryHelper {
*/
public String getOutboundLifetimeKBps() {
if (_context == null)
return "0.0";
return "0";
RateStat sendRate = _context.statManager().getRate("bw.sendRate");
if (sendRate == null) return "0.0";
if (sendRate == null) return "0";
double kbps = sendRate.getLifetimeAverageValue()/1024;
DecimalFormat fmt = new DecimalFormat("##0.00");
return fmt.format(kbps);
@ -335,11 +335,11 @@ public class SummaryHelper {
*/
public String getInboundTransferred() {
if (_context == null)
return "0.0";
return "0";
long received = _context.bandwidthLimiter().getTotalAllocatedInboundBytes();
return getTransferred(received);
return DataHelper.formatSize(received) + 'B';
}
/**
@ -349,40 +349,10 @@ public class SummaryHelper {
*/
public String getOutboundTransferred() {
if (_context == null)
return "0.0";
return "0";
long sent = _context.bandwidthLimiter().getTotalAllocatedOutboundBytes();
return getTransferred(sent);
}
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";
}
return DataHelper.formatSize(sent) + 'B';
}
/**

View 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>

View File

@ -10,6 +10,8 @@
%>Clients | <% } else { %><a href="configclients.jsp">Clients</a> | <% }
if (request.getRequestURI().indexOf("configpeer.jsp") != -1) {
%>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) {
%>Logging | <% } else { %><a href="configlogging.jsp">Logging</a> | <% }
if (request.getRequestURI().indexOf("configstats.jsp") != -1) {

View File

@ -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>
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
Apache's javax.servlet (javax.servlet.jar) implementation, as well as their xerces-j XML parser (xerces.jar).
Their XML parser requires the Sun XML APIs (JAXP) which is included in binary form (xml-apis.jar) as required
by their binary code license. This product includes software developed by the Apache Software Foundation
Apache's javax.servlet (javax.servlet.jar) implementation.
This product includes software developed by the Apache Software Foundation
(http://www.apache.org/). </p>
<p>Another application you can see on this webpage is <a href="http://www.i2p2.i2p/i2ptunnel">I2PTunnel</a>

View File

@ -14,6 +14,7 @@
<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="writer" value="<%=out%>" />
<jsp:setProperty name="netdbHelper" property="full" value="<%=request.getParameter("f")%>" />
<jsp:setProperty name="netdbHelper" property="router" value="<%=request.getParameter("r")%>" />
<jsp:getProperty name="netdbHelper" property="netDbSummary" />
</div>

View File

@ -45,6 +45,7 @@ public class Connection {
private long _congestionWindowEnd;
private long _highestAckedThrough;
private boolean _isInbound;
private boolean _updatedShareOpts;
/** Packet ID (Long) to PacketLocal for sent but unacked packets */
private Map _outboundPackets;
private PacketQueue _outboundQueue;
@ -120,6 +121,7 @@ public class Connection {
_activeResends = 0;
_resetSentOn = -1;
_isInbound = false;
_updatedShareOpts = false;
_connectionEvent = new ConEvent();
_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 });
@ -586,6 +588,8 @@ public class Connection {
if (_remotePeerSet) throw new RuntimeException("Remote peer already set [" + _remotePeer + ", " + peer + "]");
_remotePeerSet = true;
_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;
@ -709,7 +713,13 @@ public class Connection {
}
public long getCloseReceivedOn() { return _closeReceivedOn; }
public void setCloseReceivedOn(long when) { _closeReceivedOn = when; }
public void updateShareOpts() {
if (_closeSentOn > 0 && !_updatedShareOpts) {
_connectionManager.updateShareOpts(this);
_updatedShareOpts = true;
}
}
public void incrementUnackedPacketsReceived() { _unackedPacketsReceived++; }
public int getUnackedPacketsReceived() { return _unackedPacketsReceived; }
/** 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
*/
private class ResendPacketEvent implements SimpleTimer.TimedEvent {
public class ResendPacketEvent implements SimpleTimer.TimedEvent {
private PacketLocal _packet;
private long _nextSendTime;
public ResendPacketEvent(PacketLocal packet, long sendTime) {
@ -1104,26 +1114,6 @@ public class Connection {
_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 (_log.shouldLog(Log.DEBUG))
_log.debug("Too many resends");
@ -1137,11 +1127,32 @@ public class Connection {
long timeout = rto << (numSends-1);
if ( (timeout > MAX_RESEND_DELAY) || (timeout <= 0) )
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))
_log.debug("Scheduling resend in " + timeout + "ms for " + _packet);
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;
} else {
//if (_log.shouldLog(Log.DEBUG))

View File

@ -30,6 +30,7 @@ public class ConnectionManager {
private PacketQueue _outboundQueue;
private SchedulerChooser _schedulerChooser;
private ConnectionPacketHandler _conPacketHandler;
private TCBShare _tcbShare;
/** Inbound stream ID (Long) to Connection map */
private Map _connectionByInboundId;
/** Ping ID (Long) to PingRequest */
@ -52,6 +53,7 @@ public class ConnectionManager {
_connectionHandler = new ConnectionHandler(context, this);
_schedulerChooser = new SchedulerChooser(context);
_conPacketHandler = new ConnectionPacketHandler(context);
_tcbShare = new TCBShare(context);
_session = session;
session.setSessionListener(_messageHandler);
_outboundQueue = new PacketQueue(context, session, this);
@ -127,6 +129,7 @@ public class ConnectionManager {
*/
public Connection receiveConnection(Packet synPacket) {
Connection con = new Connection(_context, this, _schedulerChooser, _outboundQueue, _conPacketHandler, new ConnectionOptions(_defaultOptions));
_tcbShare.updateOptsFromShare(con);
con.setInbound();
long receiveId = _context.random().nextLong(Packet.MAX_STREAM_ID-1)+1;
boolean reject = false;
@ -277,6 +280,8 @@ public class ConnectionManager {
public ConnectionHandler getConnectionHandler() { return _connectionHandler; }
public I2PSession getSession() { return _session; }
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.
@ -292,6 +297,7 @@ public class ConnectionManager {
_connectionByInboundId.clear();
_connectionLock.notifyAll();
}
_tcbShare.stop();
}
/**

View File

@ -213,6 +213,10 @@ public class ConnectionPacketHandler {
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)
// con.fastRetransmit();
}

View File

@ -82,7 +82,16 @@ class PacketQueue {
// this should not block!
begin = _context.clock().now();
sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent);
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);
end = _context.clock().now();
if ( (end-begin > 1000) && (_log.shouldLog(Log.WARN)) )

View 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);
}
}
}

View File

@ -60,7 +60,6 @@
<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-el.jar" todir="build/" />
<copy file="apps/jetty/jettylib/xercesImpl.jar" todir="build/" />
<copy file="apps/jetty/jettylib/javax.servlet.jar" todir="build/" />
</target>
<target name="buildexe">
@ -87,7 +86,7 @@
<jar destfile="./build/launchi2p.jar">
<manifest>
<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>
</jar>
<!-- 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/resources/iggy.ico" 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/routerconsole.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/javax.servlet.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 name="installer" depends="preppkg">
<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>
</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>
</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>
</jar>
<izpack input="${basedir}/installer/install.xml" output="${basedir}/install.jar" installerType="standard" basedir="${basedir}" />
@ -452,7 +449,7 @@
<arg value="-output"/>
<arg value="findbugs.xml"/>
<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="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 -->

View File

@ -24,6 +24,7 @@ import net.i2p.data.RoutingKeyGenerator;
import net.i2p.stat.StatManager;
import net.i2p.util.Clock;
import net.i2p.util.FortunaRandomSource;
import net.i2p.util.KeyRing;
import net.i2p.util.LogManager;
import net.i2p.util.PooledRandomSource;
import net.i2p.util.RandomSource;
@ -75,6 +76,7 @@ public class I2PAppContext {
private RoutingKeyGenerator _routingKeyGenerator;
private RandomSource _random;
private KeyGenerator _keyGenerator;
protected KeyRing _keyRing; // overridden in RouterContext
private volatile boolean _statManagerInitialized;
private volatile boolean _sessionKeyManagerInitialized;
private volatile boolean _namingServiceInitialized;
@ -91,6 +93,7 @@ public class I2PAppContext {
private volatile boolean _routingKeyGeneratorInitialized;
private volatile boolean _randomInitialized;
private volatile boolean _keyGeneratorInitialized;
protected volatile boolean _keyRingInitialized; // used in RouterContext
/**
@ -141,12 +144,14 @@ public class I2PAppContext {
_elGamalEngine = null;
_elGamalAESEngine = null;
_logManager = null;
_keyRing = null;
_statManagerInitialized = false;
_sessionKeyManagerInitialized = false;
_namingServiceInitialized = false;
_elGamalEngineInitialized = false;
_elGamalAESEngineInitialized = 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]
*

View File

@ -9,6 +9,7 @@ package net.i2p.client;
*
*/
import java.util.Date;
import java.util.Set;
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.ReportAbuseMessage;
import net.i2p.data.i2cp.SendMessageMessage;
import net.i2p.data.i2cp.SendMessageExpiresMessage;
import net.i2p.data.i2cp.SessionConfig;
import net.i2p.util.Log;
@ -91,8 +93,13 @@ class I2CPMessageProducer {
*
*/
public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag,
SessionKey key, Set tags, SessionKey newKey) throws I2PSessionException {
SendMessageMessage msg = new SendMessageMessage();
SessionKey key, Set tags, SessionKey newKey, long expires) throws I2PSessionException {
SendMessageMessage msg;
if (expires > 0) {
msg = new SendMessageExpiresMessage();
((SendMessageExpiresMessage)msg).setExpiration(new Date(expires));
} else
msg = new SendMessageMessage();
msg.setDestination(dest);
msg.setSessionId(session.getSessionId());
msg.setNonce(nonce);

View File

@ -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, 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
* the payload.

View File

@ -550,10 +550,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* Pass off the error to the listener
*/
void propogateError(String msg, Throwable error) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "Error occurred: " + msg + " - " + error.getMessage());
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + " cause", error);
if (_log.shouldLog(Log.ERROR))
_log.error(getPrefix() + "Error occurred: " + msg + " - " + error.getMessage());
if (_log.shouldLog(Log.ERROR))
_log.error(getPrefix() + " cause", error);
if (_sessionListener != null) _sessionListener.errorOccurred(this, msg, error);
}

View File

@ -107,15 +107,19 @@ class I2PSessionImpl2 extends I2PSessionImpl {
return sendMessage(dest, payload, 0, payload.length);
}
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
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)
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 (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.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 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 {
SessionKey key = null;
SessionKey newKey = null;
@ -176,6 +180,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
Set sentTags = null;
int oldTags = 0;
long begin = _context.clock().now();
/***********
if (I2CPMessageProducer.END_TO_END_CRYPTO) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("begin sendBestEffort");
key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey());
@ -220,6 +225,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
} else {
// not using end to end crypto, so don't ever bundle any tags
}
**********/
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 (keyUsed != null) {
if (I2CPMessageProducer.END_TO_END_CRYPTO) {
if (newKey != null)
keyUsed.setData(newKey.getData());
else
keyUsed.setData(key.getData());
} else {
//if (I2CPMessageProducer.END_TO_END_CRYPTO) {
// if (newKey != null)
// keyUsed.setData(newKey.getData());
// else
// keyUsed.setData(key.getData());
//} else {
keyUsed.setData(SessionKey.INVALID_KEY.getData());
}
//}
}
if (tagsSent != null) {
if (sentTags != null) {
@ -261,7 +267,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
+ state.getNonce() + " for best effort "
+ " sync took " + (inSendingSync-beforeSendingSync)
+ " 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
// saying that the router received it - in theory, that should come back

View File

@ -21,6 +21,7 @@ import net.i2p.data.Lease;
import net.i2p.data.LeaseSet;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.data.i2cp.I2CPMessage;
@ -78,6 +79,17 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
leaseSet.setEncryptionKey(li.getPublicKey());
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 {
leaseSet.sign(session.getPrivateKey());
session.getProducer().createLeaseSet(session, leaseSet, li.getSigningPrivateKey(), li.getPrivateKey());
@ -137,4 +149,4 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
&& DataHelper.eq(_signingPrivKey, li.getSigningPrivateKey());
}
}
}
}

View File

@ -25,6 +25,7 @@ import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -235,7 +236,7 @@ public class DataHelper {
int split = line.indexOf('=');
if (split <= 0) continue;
String key = line.substring(0, split);
String val = line.substring(split+1);
String val = line.substring(split+1); //.trim() ??????????????
// Unescape line breaks after loading.
// Remember: "\" needs escaping both for regex and string.
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)
*/

View File

@ -9,6 +9,7 @@ package net.i2p.data;
*
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@ -17,13 +18,34 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.crypto.DSAEngine;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.RandomSource;
/**
* 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
*/
public class LeaseSet extends DataStructureImpl {
@ -40,6 +62,9 @@ public class LeaseSet extends DataStructureImpl {
// Store these since isCurrent() and getEarliestLeaseDate() are called frequently
private long _firstExpiration;
private long _lastExpiration;
private List _decryptedLeases;
private boolean _decrypted;
private boolean _checked;
/** This seems like plenty */
private final static int MAX_LEASES = 6;
@ -55,6 +80,8 @@ public class LeaseSet extends DataStructureImpl {
_receivedAsPublished = false;
_firstExpiration = Long.MAX_VALUE;
_lastExpiration = 0;
_decrypted = false;
_checked = false;
}
public Destination getDestination() {
@ -104,11 +131,17 @@ public class LeaseSet extends DataStructureImpl {
}
public int getLeaseCount() {
return _leases.size();
if (isEncrypted())
return _leases.size() - 1;
else
return _leases.size();
}
public Lease getLease(int index) {
return (Lease) _leases.get(index);
if (isEncrypted())
return (Lease) _decryptedLeases.get(index);
else
return (Lease) _leases.get(index);
}
public Signature getSignature() {
@ -335,4 +368,139 @@ public class LeaseSet extends DataStructureImpl {
buf.append("]");
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;
}
}

View File

@ -18,7 +18,7 @@ import net.i2p.data.DataHelper;
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 {
@ -75,6 +75,8 @@ public class I2CPMessageHandler {
return new RequestLeaseSetMessage();
case SendMessageMessage.MESSAGE_TYPE:
return new SendMessageMessage();
case SendMessageExpiresMessage.MESSAGE_TYPE:
return new SendMessageExpiresMessage();
case SessionStatusMessage.MESSAGE_TYPE:
return new SessionStatusMessage();
case GetDateMessage.MESSAGE_TYPE:

View 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();
}
}

View 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();
}
}

View 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 {}
}

View File

@ -47,14 +47,13 @@ wrapper.java.classpath.12=lib/jasper-runtime.jar
wrapper.java.classpath.13=lib/commons-logging.jar
wrapper.java.classpath.14=lib/commons-el.jar
wrapper.java.classpath.15=lib/ant.jar
wrapper.java.classpath.16=lib/xercesImpl.jar
# java service wrapper, BSD
wrapper.java.classpath.17=lib/wrapper.jar
wrapper.java.classpath.16=lib/wrapper.jar
# systray, LGPL
wrapper.java.classpath.18=lib/systray.jar
wrapper.java.classpath.19=lib/systray4j.jar
wrapper.java.classpath.17=lib/systray.jar
wrapper.java.classpath.18=lib/systray4j.jar
# 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)
wrapper.java.library.path.1=.

View File

@ -27,6 +27,7 @@ public class ClientMessage {
private SessionConfig _senderConfig;
private Hash _destinationHash;
private MessageId _messageId;
private long _expiration;
public ClientMessage() {
setPayload(null);
@ -36,6 +37,7 @@ public class ClientMessage {
setSenderConfig(null);
setDestinationHash(null);
setMessageId(null);
setExpiration(0);
}
/**
@ -91,4 +93,12 @@ public class ClientMessage {
*/
public SessionConfig getSenderConfig() { return _senderConfig; }
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; }
}

View File

@ -62,4 +62,5 @@ public abstract class NetworkDatabaseFacade implements Service {
public int getKnownRouters() { return 0; }
public int getKnownLeaseSets() { return 0; }
public void renderRouterInfoHTML(Writer out, String s) throws IOException {}
public void renderStatusHTML(Writer out, boolean b) throws IOException {}
}

View 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();
}
}

View File

@ -26,6 +26,7 @@ import net.i2p.router.transport.VMCommSystem;
import net.i2p.router.tunnel.TunnelDispatcher;
import net.i2p.router.tunnel.pool.TunnelPoolManager;
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
@ -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;
}
}
}

View File

@ -29,6 +29,7 @@ import net.i2p.data.i2cp.I2CPMessageReader;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.SendMessageMessage;
import net.i2p.data.i2cp.SendMessageExpiresMessage;
import net.i2p.data.i2cp.SessionConfig;
import net.i2p.data.i2cp.SessionId;
import net.i2p.router.Job;
@ -270,6 +271,9 @@ public class ClientConnectionRunner {
Destination dest = message.getDestination();
MessageId id = new MessageId();
id.setMessageId(getNextMessageId());
long expiration = 0;
if (message instanceof SendMessageExpiresMessage)
expiration = ((SendMessageExpiresMessage) message).getExpiration().getTime();
long beforeLock = _context.clock().now();
long inLock = 0;
synchronized (_acceptedPending) {
@ -291,7 +295,7 @@ public class ClientConnectionRunner {
// the following blocks as described above
SessionConfig cfg = _config;
if (cfg != null)
_manager.distributeMessage(cfg.getDestination(), dest, payload, id);
_manager.distributeMessage(cfg.getDestination(), dest, payload, id, expiration);
long timeToDistribute = _context.clock().now() - beforeDistribute;
if (_log.shouldLog(Log.DEBUG))
_log.warn("Time to distribute in the manager to "

View File

@ -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
ClientConnectionRunner runner = getRunner(toDest);
if (runner != null) {
@ -168,6 +168,7 @@ public class ClientManager {
msg.setSenderConfig(runner.getConfig());
msg.setFromDestination(runner.getConfig().getDestination());
msg.setMessageId(msgId);
msg.setExpiration(expiration);
_ctx.clientMessagePool().add(msg, true);
}
}

View File

@ -21,7 +21,9 @@ import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
import net.i2p.data.i2cp.ReceiveMessageEndMessage;
import net.i2p.data.i2cp.ReconfigureSessionMessage;
import net.i2p.data.i2cp.SendMessageMessage;
import net.i2p.data.i2cp.SendMessageExpiresMessage;
import net.i2p.data.i2cp.SessionId;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.data.i2cp.SetDateMessage;
@ -67,6 +69,9 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
case SendMessageMessage.MESSAGE_TYPE:
handleSendMessage(reader, (SendMessageMessage)message);
break;
case SendMessageExpiresMessage.MESSAGE_TYPE:
handleSendMessage(reader, (SendMessageExpiresMessage)message);
break;
case ReceiveMessageBeginMessage.MESSAGE_TYPE:
handleReceiveBegin(reader, (ReceiveMessageBeginMessage)message);
break;
@ -237,6 +242,17 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
_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
private final static int MAX_SESSION_ID = 32767;

View File

@ -61,6 +61,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
private long _leaseSetLookupBegin;
private TunnelInfo _outTunnel;
private TunnelInfo _inTunnel;
private boolean _wantACK;
/**
* 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";
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... */
private final static int SEND_PRIORITY = 500;
@ -125,23 +127,34 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
_to = msg.getDestination();
_toString = _to.calculateHash().toBase64().substring(0,4);
_leaseSetLookupBegin = -1;
String param = msg.getSenderConfig().getOptions().getProperty(OVERALL_TIMEOUT_MS_PARAM);
if (param == null)
param = ctx.router().getConfigSetting(OVERALL_TIMEOUT_MS_PARAM);
if (param != null) {
try {
timeoutMs = Long.parseLong(param);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Invalid client message timeout specified [" + param
+ "], defaulting to " + OVERALL_TIMEOUT_MS_DEFAULT, nfe);
timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
}
}
_start = getContext().clock().now();
_overallExpiration = timeoutMs + _start;
// 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);
if (param == null)
param = ctx.router().getConfigSetting(OVERALL_TIMEOUT_MS_PARAM);
if (param != null) {
try {
timeoutMs = Long.parseLong(param);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Invalid client message timeout specified [" + param
+ "], defaulting to " + OVERALL_TIMEOUT_MS_DEFAULT, nfe);
timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
}
}
_overallExpiration = timeoutMs + _start;
if (_log.shouldLog(Log.WARN))
_log.warn("Default Expiration (ms): " + timeoutMs);
}
_finished = false;
}
@ -267,6 +280,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
long lookupTime = getContext().clock().now() - _leaseSetLookupBegin;
getContext().statManager().addRateData("client.leaseSetFoundRemoteTime", lookupTime, lookupTime);
}
_wantACK = false;
boolean ok = getNextLease();
if (ok) {
send();
@ -400,6 +414,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
}
if (_log.shouldLog(Log.INFO))
_log.info("Added to cache - lease for " + _toString);
_wantACK = true;
return true;
}
@ -443,10 +458,14 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
dieFatal();
return;
}
boolean wantACK = true;
int existingTags = GarlicMessageBuilder.estimateAvailableTags(getContext(), _leaseSet.getEncryptionKey());
if ( (existingTags > 30) && (getContext().random().nextInt(100) >= 5) )
wantACK = false;
_outTunnel = selectOutboundTunnel(_to);
// 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();
SessionKey sessKey = new SessionKey();
@ -503,7 +522,6 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
+ _lease.getTunnelId() + " on "
+ _lease.getGateway().toBase64());
_outTunnel = selectOutboundTunnel(_to);
if (_outTunnel != null) {
if (_log.shouldLog(Log.DEBUG))
_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);
_backloggedTunnelCache.remove(hashPair());
_tunnelCache.put(hashPair(), tunnel);
_wantACK = true;
return tunnel;
} // else still backlogged
} else // no longer valid
@ -740,6 +759,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
tunnel = selectOutboundTunnel();
if (tunnel != null)
_tunnelCache.put(hashPair(), tunnel);
_wantACK = true;
}
return tunnel;
}

View File

@ -20,12 +20,12 @@ import net.i2p.router.RouterContext;
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 {
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) {
super(ctx);
@ -67,6 +67,6 @@ public class PublishLocalRouterInfoJob extends JobImpl {
} catch (DataFormatException 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));
}
}

View File

@ -125,6 +125,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
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 PUBLISH_JOB_DELAY = 5*60*1000l;
public KademliaNetworkDatabaseFacade(RouterContext context) {
_context = context;
@ -326,7 +327,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
}
// periodically update and resign the router's 'published date', which basically
// 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 {
publish(ri);
} catch (IllegalArgumentException iae) {
@ -970,7 +973,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
StringBuffer buf = new StringBuffer(4*1024);
buf.append("<h2>Network Database RouterInfo Lookup</h2>\n");
if (".".equals(routerPrefix)) {
renderRouterInfo(buf, _context.router().getRouterInfo(), true);
renderRouterInfo(buf, _context.router().getRouterInfo(), true, true);
} else {
boolean notFound = true;
Set routers = getRouters();
@ -978,7 +981,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
RouterInfo ri = (RouterInfo)iter.next();
Hash key = ri.getIdentity().getHash();
if (key.toBase64().startsWith(routerPrefix)) {
renderRouterInfo(buf, ri, false);
renderRouterInfo(buf, ri, false, true);
notFound = false;
}
}
@ -990,7 +993,14 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
}
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");
if (!_initialized) {
buf.append("<i>Not initialized</i>\n");
@ -1044,10 +1054,15 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
}
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();
renderRouterInfo(buf, ourInfo, true);
renderRouterInfo(buf, ourInfo, true, true);
out.write(buf.toString());
buf.setLength(0);
@ -1061,7 +1076,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
Hash key = ri.getIdentity().getHash();
boolean isUs = key.equals(us);
if (!isUs) {
renderRouterInfo(buf, ri, false);
renderRouterInfo(buf, ri, false, full);
out.write(buf.toString());
buf.setLength(0);
String coreVersion = ri.getOption("coreVersion");
@ -1102,7 +1117,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
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();
buf.append("<a name=\"").append(hash.substring(0, 6)).append("\" />");
if (isUs) {
@ -1129,13 +1144,18 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
}
}
buf.append("</i><br />\n");
buf.append("Stats: <br /><i><code>\n");
for (Iterator iter = info.getOptions().keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = info.getOption(key);
buf.append(DataHelper.stripHTML(key)).append(" = ").append(DataHelper.stripHTML(val)).append("<br />\n");
if (full) {
buf.append("Stats: <br /><i><code>\n");
for (Iterator iter = info.getOptions().keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = info.getOption(key);
buf.append(DataHelper.stripHTML(key)).append(" = ").append(DataHelper.stripHTML(val)).append("<br />\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("</code></i><hr />\n");
buf.append("<hr />\n");
}
}

View File

@ -65,10 +65,6 @@ class PersistentDataStore extends TransientDataStore {
return super.remove(key);
}
public DataStructure removeLease(Hash key) {
return super.removeLease(key);
}
public void put(Hash key, DataStructure data) {
if ( (data == null) || (key == null) ) return;
super.put(key, data);
@ -77,26 +73,6 @@ class PersistentDataStore extends TransientDataStore {
_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) {
super.put(ls.getDestination().calculateHash(), ls);
}
@ -249,18 +225,6 @@ class PersistentDataStore extends TransientDataStore {
int routerCount = 0;
try {
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());
if (routerInfoFiles != null) {
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 File _routerFile;
private Hash _key;
@ -464,31 +371,8 @@ class PersistentDataStore extends TransientDataStore {
_log.info("Removed router info at " + f.getAbsolutePath());
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 static final FilenameFilter _instance = new RouterInfoFilter();
public static final FilenameFilter getInstance() { return _instance; }

View File

@ -28,6 +28,13 @@ import net.i2p.router.RouterContext;
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.
* 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

View File

@ -28,7 +28,8 @@ public class StartAcceptingClientsJob extends JobImpl {
getContext().clientManager().startup();
getContext().jobQueue().addJob(new ReadConfigJob(getContext()));
getContext().jobQueue().addJob(new RebuildRouterInfoJob(getContext()));
// pointless
//getContext().jobQueue().addJob(new RebuildRouterInfoJob(getContext()));
getContext().jobQueue().allowParallelOperation();
}
}

View File

@ -507,7 +507,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
}
out.write("</table>\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 {
@ -577,7 +577,8 @@ public class TunnelPoolManager implements TunnelManagerFacade {
}
if (live <= 0)
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) {