propagate from branch 'i2p.i2p.zzz.test4' (head 56ba5c9f8d0779f91259df05b7be0826fe08cd84)

to branch 'i2p.i2p' (head 0ba2cc80363f5c7086bce7a43f43a9b095ed2d9e)
This commit is contained in:
zzz
2010-12-27 16:21:09 +00:00
120 changed files with 4192 additions and 2231 deletions

View File

@ -137,7 +137,7 @@ public class ConnectionAcceptor implements Runnable
}
}
} else {
Thread t = new I2PAppThread(new Handler(socket), "Connection-" + socket);
Thread t = new I2PAppThread(new Handler(socket), "I2PSnark incoming connection");
t.start();
}
}

View File

@ -14,11 +14,13 @@ import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketEepGet;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.data.Base32;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
@ -316,21 +318,44 @@ public class I2PSnarkUtil {
}
}
private static final int BASE32_HASH_LENGTH = 52; // 1 + Hash.HASH_LENGTH * 8 / 5
/** Base64 Hash or Hash.i2p or name.i2p using naming service */
Destination getDestination(String ip) {
if (ip == null) return null;
if (ip.endsWith(".i2p")) {
if (ip.length() < 520) { // key + ".i2p"
Destination dest = _context.namingService().lookup(ip);
if (dest != null)
return dest;
if (_manager != null && ip.length() == BASE32_HASH_LENGTH + 8 && ip.endsWith(".b32.i2p")) {
// Use existing I2PSession for b32 lookups if we have it
// This is much more efficient than using the naming service
I2PSession sess = _manager.getSession();
if (sess != null) {
byte[] b = Base32.decode(ip.substring(0, BASE32_HASH_LENGTH));
if (b != null) {
Hash h = new Hash(b);
if (_log.shouldLog(Log.INFO))
_log.info("Using existing session for lookup of " + ip);
try {
return sess.lookupDest(h);
} catch (I2PSessionException ise) {
}
}
}
}
if (_log.shouldLog(Log.INFO))
_log.info("Using naming service for lookup of " + ip);
return _context.namingService().lookup(ip);
}
if (_log.shouldLog(Log.INFO))
_log.info("Creating Destination for " + ip);
try {
return new Destination(ip.substring(0, ip.length()-4)); // sans .i2p
} catch (DataFormatException dfe) {
return null;
}
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Creating Destination for " + ip);
try {
return new Destination(ip);
} catch (DataFormatException dfe) {

View File

@ -39,7 +39,13 @@ class Message
final static byte REQUEST = 6;
final static byte PIECE = 7;
final static byte CANCEL = 8;
final static byte EXTENSION = 20;
final static byte PORT = 9; // DHT (BEP 5)
final static byte SUGGEST = 13; // Fast (BEP 6)
final static byte HAVE_ALL = 14; // Fast (BEP 6)
final static byte HAVE_NONE = 15; // Fast (BEP 6)
final static byte REJECT = 16; // Fast (BEP 6)
final static byte ALLOWED_FAST = 17; // Fast (BEP 6)
final static byte EXTENSION = 20; // BEP 10
// Not all fields are used for every message.
// KEEP_ALIVE doesn't have a real wire representation

View File

@ -291,7 +291,7 @@ public class MetaInfo
if (piece >= 0 && piece < pieces -1)
return piece_length;
else if (piece == pieces -1)
return (int)(length - piece * piece_length);
return (int)(length - ((long)piece * piece_length));
else
throw new IndexOutOfBoundsException("no piece: " + piece);
}

View File

@ -63,6 +63,7 @@ public class Peer implements Comparable
// bytes per bt spec: 0011223344556677
static final long OPTION_EXTENSION = 0x0000000000100000l;
static final long OPTION_FAST = 0x0000000000000004l;
static final long OPTION_DHT = 0x0000000000000001l;
private long options;
/**
@ -77,7 +78,7 @@ public class Peer implements Comparable
this.my_id = my_id;
this.metainfo = metainfo;
_id = ++__id;
//_log.debug("Creating a new peer with " + peerID.getAddress().calculateHash().toBase64(), new Exception("creating"));
//_log.debug("Creating a new peer with " + peerID.toString(), new Exception("creating"));
}
/**
@ -101,7 +102,7 @@ public class Peer implements Comparable
this.peerID = new PeerID(id, sock.getPeerDestination());
_id = ++__id;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Creating a new peer with " + peerID.getAddress().calculateHash().toBase64(), new Exception("creating " + _id));
_log.debug("Creating a new peer with " + peerID.toString(), new Exception("creating " + _id));
}
/**
@ -197,7 +198,7 @@ public class Peer implements Comparable
throw new IllegalStateException("Peer already started");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Running connection to " + peerID.getAddress().calculateHash().toBase64(), new Exception("connecting"));
_log.debug("Running connection to " + peerID.toString(), new Exception("connecting"));
try
{
// Do we need to handshake?

View File

@ -428,7 +428,7 @@ public class PeerCoordinator implements PeerListener
peer.runConnection(_util, listener, bitfield);
}
};
String threadName = peer.toString();
String threadName = "Snark peer " + peer.toString();
new I2PAppThread(r, threadName).start();
return true;
}

View File

@ -72,8 +72,10 @@ public class TrackerClient extends I2PAppThread
public TrackerClient(I2PSnarkUtil util, MetaInfo meta, PeerCoordinator coordinator)
{
super();
// Set unique name.
super("TrackerClient-" + urlencode(coordinator.getID()));
String id = urlencode(coordinator.getID());
setName("TrackerClient " + id.substring(id.length() - 12));
_util = util;
this.meta = meta;
this.coordinator = coordinator;
@ -274,7 +276,7 @@ public class TrackerClient extends I2PAppThread
// only delay if we actually make an attempt to add peer
if(coordinator.addPeer(cur)) {
int delay = DELAY_MUL;
delay *= ((int)cur.getPeerID().getAddress().calculateHash().toBase64().charAt(0)) % 10;
delay *= r.nextInt(10);
delay += DELAY_MIN;
sleptTime += delay;
try { Thread.sleep(delay); } catch (InterruptedException ie) {}

View File

@ -157,7 +157,7 @@ public class BEValue
* values. This operation only succeeds when the BEValue is actually
* a Map, otherwise it will throw a InvalidBEncodingException.
*/
public Map<BEValue, BEValue> getMap() throws InvalidBEncodingException
public Map<String, BEValue> getMap() throws InvalidBEncodingException
{
try
{

View File

@ -16,6 +16,7 @@ import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.concurrent.RejectedExecutionException;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
@ -228,7 +229,15 @@ class HTTPResponseOutputStream extends FilterOutputStream {
//out.flush();
PipedInputStream pi = new PipedInputStream();
PipedOutputStream po = new PipedOutputStream(pi);
new I2PAppThread(new Pusher(pi, out), "HTTP decompressor").start();
// Run in the client thread pool, as there should be an unused thread
// there after the accept().
// Overridden in I2PTunnelHTTPServer, where it does not use the client pool.
try {
I2PTunnelClientBase._executor.execute(new Pusher(pi, out));
} catch (RejectedExecutionException ree) {
// shouldn't happen
throw ree;
}
out = po;
}

View File

@ -16,8 +16,6 @@ import net.i2p.util.Log;
public class I2PTunnelClient extends I2PTunnelClientBase {
private static final Log _log = new Log(I2PTunnelClient.class);
/** list of Destination objects that we point at */
protected List<Destination> dests;
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1

View File

@ -17,6 +17,13 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadFactory;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
@ -34,9 +41,9 @@ import net.i2p.util.SimpleTimer;
public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runnable {
private static final Log _log = new Log(I2PTunnelClientBase.class);
protected I2PAppContext _context;
protected Logging l;
protected final Log _log;
protected final I2PAppContext _context;
protected final Logging l;
static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000;
@ -64,35 +71,24 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
private String handlerName;
private String privKeyFile;
// private Object conLock = new Object();
/** List of Socket for those accept()ed but not yet started up */
protected final List _waitingSockets = new ArrayList(4); // FIXME should be final and use a factory. FIXME
/** How many connections will we allow to be in the process of being built at once? */
private int _numConnectionBuilders;
/** How long will we allow sockets to sit in the _waitingSockets map before killing them? */
private int _maxWaitTime;
/**
* How many concurrent connections this I2PTunnel instance will allow to be
* in the process of connecting (or if less than 1, there is no limit)?
*/
public static final String PROP_NUM_CONNECTION_BUILDERS = "i2ptunnel.numConnectionBuilders";
/**
* How long will we let a socket wait after being accept()ed without getting
* pumped through a connection builder (in milliseconds). If this time is
* reached, the socket is unceremoniously closed and discarded. If the max
* wait time is less than 1, there is no limit.
*
*/
public static final String PROP_MAX_WAIT_TIME = "i2ptunnel.maxWaitTime";
private static final int DEFAULT_NUM_CONNECTION_BUILDERS = 5;
private static final int DEFAULT_MAX_WAIT_TIME = 30*1000;
// true if we are chained from a server.
private boolean chained = false;
/** how long to wait before dropping an idle thread */
private static final long HANDLER_KEEPALIVE_MS = 2*60*1000;
/**
* We keep a static pool of socket handlers for all clients,
* as there is no need for isolation on the client side.
* Extending classes may use it for other purposes.
* Not for use by servers, as there is no limit on threads.
*/
static final Executor _executor;
private static int _executorThreadCount;
static {
_executor = new CustomThreadPoolExecutor();
}
public I2PTunnelClientBase(int localPort, Logging l, I2PSocketManager sktMgr,
I2PTunnel tunnel, EventDispatcher notifyThis, long clientId )
throws IllegalArgumentException {
@ -109,9 +105,9 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
_context.statManager().createRateStat("i2ptunnel.client.closeNoBacklog", "How many pending sockets remain when it was removed prior to backlog timeout?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.manageTime", "How long it takes to accept a socket and fire it into an i2ptunnel runner (or queue it for the pool)?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.buildRunTime", "How long it takes to run a queued socket into an i2ptunnel runner?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_log = _context.logManager().getLog(getClass());
Thread t = new I2PAppThread(this);
t.setName("Client " + _clientId);
Thread t = new I2PAppThread(this, "Client " + tunnel.listenHost + ':' + localPort);
listenerReady = false;
t.start();
open = true;
@ -125,8 +121,6 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
}
configurePool(tunnel);
if (open && listenerReady) {
l.log("Client ready, listening on " + tunnel.listenHost + ':' + localPort);
notifyEvent("openBaseClientResult", "ok");
@ -135,6 +129,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
notifyEvent("openBaseClientResult", "error");
}
}
public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l,
EventDispatcher notifyThis, String handlerName,
I2PTunnel tunnel) throws IllegalArgumentException {
@ -163,6 +158,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
_context.statManager().createRateStat("i2ptunnel.client.closeNoBacklog", "How many pending sockets remain when it was removed prior to backlog timeout?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.manageTime", "How long it takes to accept a socket and fire it into an i2ptunnel runner (or queue it for the pool)?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.buildRunTime", "How long it takes to run a queued socket into an i2ptunnel runner?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_log = _context.logManager().getLog(getClass());
// normalize path so we can find it
if (pkf != null) {
@ -210,8 +206,6 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
}
configurePool(tunnel);
if (open && listenerReady) {
if (openNow)
l.log("Client ready, listening on " + tunnel.listenHost + ':' + localPort);
@ -224,37 +218,6 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
}
/**
* build and configure the pool handling accept()ed but not yet
* established connections
*
*/
private void configurePool(I2PTunnel tunnel) {
//_waitingSockets = new ArrayList(4);
Properties opts = tunnel.getClientOptions();
String maxWait = opts.getProperty(PROP_MAX_WAIT_TIME, DEFAULT_MAX_WAIT_TIME+"");
try {
_maxWaitTime = Integer.parseInt(maxWait);
} catch (NumberFormatException nfe) {
_maxWaitTime = DEFAULT_MAX_WAIT_TIME;
}
String numBuild = opts.getProperty(PROP_NUM_CONNECTION_BUILDERS, DEFAULT_NUM_CONNECTION_BUILDERS+"");
try {
_numConnectionBuilders = Integer.parseInt(numBuild);
} catch (NumberFormatException nfe) {
_numConnectionBuilders = DEFAULT_NUM_CONNECTION_BUILDERS;
}
for (int i = 0; i < _numConnectionBuilders; i++) {
String name = "ClientBuilder" + _clientId + '.' + i;
I2PAppThread b = new I2PAppThread(new TunnelConnectionBuilder(), name);
b.setDaemon(true);
b.start();
}
}
/**
* Sets the this.sockMgr field if it is null, or if we want a new one
*
@ -321,6 +284,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
* badly that we cant create a socketManager
*/
protected static synchronized I2PSocketManager getSocketManager(I2PTunnel tunnel, String pkf) {
// shadows instance _log
Log _log = tunnel.getContext().logManager().getLog(I2PTunnelClientBase.class);
if (socketManager != null) {
I2PSession s = socketManager.getSession();
if ( (s == null) || (s.isClosed()) ) {
@ -378,6 +343,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
* badly that we cant create a socketManager
*/
protected static I2PSocketManager buildSocketManager(I2PTunnel tunnel, String pkf, Logging log) {
// shadows instance _log
Log _log = tunnel.getContext().logManager().getLog(I2PTunnelClientBase.class);
Properties props = new Properties();
props.putAll(tunnel.getClientOptions());
int portNum = 7654;
@ -537,7 +504,6 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
synchronized (this) {
notifyAll();
}
synchronized (_waitingSockets) { _waitingSockets.notifyAll(); }
return;
}
ss = new ServerSocket(localPort, 0, addr);
@ -566,12 +532,9 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
}
while (true) {
while (open) {
Socket s = ss.accept();
long before = System.currentTimeMillis();
manageConnection(s);
long total = System.currentTimeMillis() - before;
_context.statManager().addRateData("i2ptunnel.client.manageTime", total, total);
}
} catch (IOException ex) {
if (open) {
@ -586,9 +549,6 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
notifyAll();
}
}
synchronized (_waitingSockets) {
_waitingSockets.notifyAll();
}
}
/**
@ -598,24 +558,38 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
*/
protected void manageConnection(Socket s) {
if (s == null) return;
if (_numConnectionBuilders <= 0) {
new I2PAppThread(new BlockingRunner(s), "Clinet run").start();
return;
try {
_executor.execute(new BlockingRunner(s));
} catch (RejectedExecutionException ree) {
// should never happen, we have an unbounded pool and never stop the executor
try {
s.close();
} catch (IOException ioe) {}
}
if (_maxWaitTime > 0)
SimpleScheduler.getInstance().addEvent(new CloseEvent(s), _maxWaitTime);
}
synchronized (_waitingSockets) {
_waitingSockets.add(s);
_waitingSockets.notifyAll();
/**
* Not really needed for now but in case we want to add some hooks like afterExecute().
*/
private static class CustomThreadPoolExecutor extends ThreadPoolExecutor {
public CustomThreadPoolExecutor() {
super(0, Integer.MAX_VALUE, HANDLER_KEEPALIVE_MS, TimeUnit.MILLISECONDS,
new SynchronousQueue(), new CustomThreadFactory());
}
}
/** just to set the name and set Daemon */
private static class CustomThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
Thread rv = Executors.defaultThreadFactory().newThread(r);
rv.setName("I2PTunnel Client Runner " + (++_executorThreadCount));
rv.setDaemon(true);
return rv;
}
}
/**
* Blocking runner, used during the connection establishment whenever we
* are not using the queued builders.
*
* Blocking runner, used during the connection establishment
*/
private class BlockingRunner implements Runnable {
private Socket _s;
@ -625,32 +599,6 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
}
/**
* Remove and close the socket from the waiting list, if it is still there.
*
*/
private class CloseEvent implements SimpleTimer.TimedEvent {
private Socket _s;
public CloseEvent(Socket s) { _s = s; }
public void timeReached() {
int remaining = 0;
boolean stillWaiting = false;
synchronized (_waitingSockets) {
stillWaiting = _waitingSockets.remove(_s);
remaining = _waitingSockets.size();
}
if (stillWaiting) {
try { _s.close(); } catch (IOException ioe) {}
if (_log.shouldLog(Log.INFO)) {
_context.statManager().addRateData("i2ptunnel.client.closeBacklog", remaining, 0);
_log.info("Closed a waiting socket because of backlog");
}
} else {
_context.statManager().addRateData("i2ptunnel.client.closeNoBacklog", remaining, 0);
}
}
}
public boolean close(boolean forced) {
if (_log.shouldLog(Log.INFO))
_log.info("close() called: forced = " + forced + " open = " + open + " sockMgr = " + sockMgr);
@ -688,7 +636,6 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
//l.log("Client closed.");
}
synchronized (_waitingSockets) { _waitingSockets.notifyAll(); }
return true;
}
@ -696,40 +643,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
try {
s.close();
} catch (IOException ex) {
_log.error("Could not close socket", ex);
//_log.error("Could not close socket", ex);
}
}
/**
* Pool runner pulling sockets off the waiting list and pushing them
* through clientConnectionRun. This dies when the I2PTunnel instance
* is closed.
*
*/
private class TunnelConnectionBuilder implements Runnable {
public void run() {
Socket s = null;
while (open) {
try {
synchronized (_waitingSockets) {
if (_waitingSockets.isEmpty())
_waitingSockets.wait();
else
s = (Socket)_waitingSockets.remove(0);
}
} catch (InterruptedException ie) {}
if (s != null) {
long before = System.currentTimeMillis();
clientConnectionRun(s);
long total = System.currentTimeMillis() - before;
_context.statManager().addRateData("i2ptunnel.client.buildRunTime", total, 0);
}
s = null;
}
}
}
/**
* Manage a connection in a separate thread. This only works if
* you do not override manageConnection()

View File

@ -58,7 +58,6 @@ import net.i2p.util.Log;
* @author zzz a stripped-down I2PTunnelHTTPClient
*/
public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements Runnable {
private static final Log _log = new Log(I2PTunnelConnectClient.class);
private final static byte[] ERR_DESTINATION_UNKNOWN =
("HTTP/1.1 503 Service Unavailable\r\n"+
@ -340,8 +339,8 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
_requestId = id;
}
public void run() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Timeout occured requesting " + _target);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Timeout occured requesting " + _target);
handleConnectClientException(new RuntimeException("Timeout"), _out,
_target, _usingProxy, _wwwProxy, _requestId);
closeSocket(_socket);

View File

@ -11,7 +11,6 @@ import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
public class I2PTunnelHTTPBidirServer extends I2PTunnelHTTPServer {
private final static Log log = new Log(I2PTunnelHTTPBidirServer.class);
public I2PTunnelHTTPBidirServer(InetAddress host, int port, int proxyport, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privData, spoofHost, l, notifyThis, tunnel);

View File

@ -61,7 +61,6 @@ import net.i2p.util.Translate;
*
*/
public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runnable {
private static final Log _log = new Log(I2PTunnelHTTPClient.class);
private HashMap addressHelpers = new HashMap();
@ -894,15 +893,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
_requestId = id;
}
public void run() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Timeout occured requesting " + _target);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Timeout occured requesting " + _target);
handleHTTPClientException(new RuntimeException("Timeout"), _out,
_target, _usingProxy, _wwwProxy, _requestId);
closeSocket(_socket);
}
}
private static String DEFAULT_JUMP_SERVERS =
public static final String DEFAULT_JUMP_SERVERS =
"http://i2host.i2p/cgi-bin/i2hostjump?," +
"http://stats.i2p/cgi-bin/jump.cgi?a=," +
"http://i2jump.i2p/";
@ -940,8 +939,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// Skip jump servers we don't know
String jumphost = jurl.substring(7); // "http://"
jumphost = jumphost.substring(0, jumphost.indexOf('/'));
Destination dest = I2PAppContext.getGlobalContext().namingService().lookup(jumphost);
if (dest == null) continue;
if (!jumphost.endsWith(".i2p"))
continue;
if (!jumphost.endsWith(".b32.i2p")) {
Destination dest = I2PAppContext.getGlobalContext().namingService().lookup(jumphost);
if (dest == null) continue;
}
out.write("<br><a href=\"".getBytes());
out.write(jurl.getBytes());

View File

@ -25,7 +25,7 @@ import net.i2p.util.Log;
* @since 0.8.2
*/
public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implements Runnable {
private static final Log _log = new Log(I2PTunnelHTTPClientBase.class);
protected final List<String> _proxyList;
protected final static byte[] ERR_NO_OUTPROXY =

View File

@ -31,7 +31,7 @@ import net.i2p.data.Base32;
*
*/
public class I2PTunnelHTTPServer extends I2PTunnelServer {
private final static Log _log = new Log(I2PTunnelHTTPServer.class);
/** what Host: should we seem to be to the webserver? */
private String _spoofHost;
private static final String HASH_HEADER = "X-I2P-DestHash";
@ -40,6 +40,20 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
private static final String[] CLIENT_SKIPHEADERS = {HASH_HEADER, DEST64_HEADER, DEST32_HEADER};
private static final String SERVER_HEADER = "Server";
private static final String[] SERVER_SKIPHEADERS = {SERVER_HEADER};
private static final long HEADER_TIMEOUT = 60*1000;
private final static byte[] ERR_UNAVAILABLE =
("HTTP/1.1 503 Service Unavailable\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+
"Connection: close\r\n"+
"Proxy-Connection: close\r\n"+
"\r\n"+
"<html><head><title>503 Service Unavailable<title></head>\n"+
"<body><h2>503 Service Unavailable</h2>\n" +
"<p>This I2P eepsite is unavailable. It may be down or undergoing maintenance.</p>\n" +
"</body></html>")
.getBytes();
public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privData, l, notifyThis, tunnel);
@ -73,8 +87,9 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
//local is fast, so synchronously. Does not need that many
//threads.
try {
// give them 5 seconds to send in the HTTP request
socket.setReadTimeout(5*1000);
// The headers _should_ be in the first packet, but
// may not be, depending on the client-side options
socket.setReadTimeout(HEADER_TIMEOUT);
InputStream in = socket.getInputStream();
@ -124,19 +139,30 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
if (allowGZIP && useGZIP) {
I2PAppThread req = new I2PAppThread(
new CompressedRequestor(s, socket, modifiedHeader, getTunnel().getContext()),
new CompressedRequestor(s, socket, modifiedHeader, getTunnel().getContext(), _log),
Thread.currentThread().getName()+".hc");
req.start();
} else {
new I2PTunnelRunner(s, socket, slock, null, modifiedHeader.getBytes(), null);
}
long afterHandle = getTunnel().getContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
getTunnel().getContext().statManager().addRateData("i2ptunnel.httpserver.blockingHandleTime", timeToHandle, 0);
if ( (timeToHandle > 1000) && (_log.shouldLog(Log.WARN)) )
_log.warn("Took a while to handle the request for " + remoteHost + ':' + remotePort +
" [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
} catch (SocketException ex) {
try {
// Send a 503, so the user doesn't get an HTTP Proxy error message
// and blame his router or the network.
socket.getOutputStream().write(ERR_UNAVAILABLE);
} catch (IOException ioe) {}
try {
socket.close();
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error while closing the received i2p con", ex);
}
} catch (IOException ioe) {}
if (_log.shouldLog(Log.ERROR))
_log.error("Error connecting to HTTP server " + remoteHost + ':' + remotePort, ex);
} catch (IOException ex) {
try {
socket.close();
@ -150,25 +176,24 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
if (_log.shouldLog(Log.ERROR))
_log.error("OOM in HTTP server", oom);
}
long afterHandle = getTunnel().getContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
getTunnel().getContext().statManager().addRateData("i2ptunnel.httpserver.blockingHandleTime", timeToHandle, 0);
if ( (timeToHandle > 1000) && (_log.shouldLog(Log.WARN)) )
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
}
private static class CompressedRequestor implements Runnable {
private Socket _webserver;
private I2PSocket _browser;
private String _headers;
private I2PAppContext _ctx;
public CompressedRequestor(Socket webserver, I2PSocket browser, String headers, I2PAppContext ctx) {
private final Socket _webserver;
private final I2PSocket _browser;
private final String _headers;
private final I2PAppContext _ctx;
// shadows _log in super()
private final Log _log;
public CompressedRequestor(Socket webserver, I2PSocket browser, String headers, I2PAppContext ctx, Log log) {
_webserver = webserver;
_browser = browser;
_headers = headers;
_ctx = ctx;
_log = log;
}
public void run() {
if (_log.shouldLog(Log.INFO))
_log.info("Compressed requestor running");
@ -183,7 +208,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
_log.info("request headers: " + _headers);
serverout.write(_headers.getBytes());
browserin = _browser.getInputStream();
I2PAppThread sender = new I2PAppThread(new Sender(serverout, browserin, "server: browser to server"), Thread.currentThread().getName() + "hcs");
I2PAppThread sender = new I2PAppThread(new Sender(serverout, browserin, "server: browser to server", _log), Thread.currentThread().getName() + "hcs");
sender.start();
browserout = _browser.getOutputStream();
@ -214,7 +239,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
String modifiedHeaders = formatHeaders(headers, command);
compressedOut.write(modifiedHeaders.getBytes());
Sender s = new Sender(compressedOut, serverin, "server: server to browser");
Sender s = new Sender(compressedOut, serverin, "server: server to browser", _log);
if (_log.shouldLog(Log.INFO))
_log.info("Before pumping the compressed response");
s.run(); // same thread
@ -233,14 +258,19 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
}
private static class Sender implements Runnable {
private OutputStream _out;
private InputStream _in;
private String _name;
public Sender(OutputStream out, InputStream in, String name) {
private final OutputStream _out;
private final InputStream _in;
private final String _name;
// shadows _log in super()
private final Log _log;
public Sender(OutputStream out, InputStream in, String name, Log log) {
_out = out;
_in = in;
_name = name;
_log = log;
}
public void run() {
if (_log.shouldLog(Log.INFO))
_log.info(_name + ": Begin sending");
@ -277,16 +307,16 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
protected boolean shouldCompress() { return true; }
@Override
protected void finishHeaders() throws IOException {
if (_log.shouldLog(Log.INFO))
_log.info("Including x-i2p-gzip as the content encoding in the response");
//if (_log.shouldLog(Log.INFO))
// _log.info("Including x-i2p-gzip as the content encoding in the response");
out.write("Content-encoding: x-i2p-gzip\r\n".getBytes());
super.finishHeaders();
}
@Override
protected void beginProcessing() throws IOException {
if (_log.shouldLog(Log.INFO))
_log.info("Beginning compression processing");
//if (_log.shouldLog(Log.INFO))
// _log.info("Beginning compression processing");
//out.flush();
_gzipOut = new InternalGZIPOutputStream(out);
out = _gzipOut;
@ -352,8 +382,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
boolean ok = DataHelper.readLine(in, command);
if (!ok) throw new IOException("EOF reached while reading the HTTP command [" + command.toString() + "]");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the http command [" + command.toString() + "]");
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Read the http command [" + command.toString() + "]");
// FIXME we probably don't need or want this in the outgoing direction
int trimmed = 0;
@ -409,8 +439,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
}
headers.setProperty(name, value);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the header [" + name + "] = [" + value + "]");
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Read the header [" + name + "] = [" + value + "]");
}
}
}

View File

@ -20,8 +20,6 @@ import net.i2p.util.Log;
*/
public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable {
private static final Log _log = new Log(I2PTunnelIRCClient.class);
/** used to assign unique IDs to the threads / clients. no logic or functionality */
private static volatile long __clientId = 0;
@ -130,6 +128,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
private Socket local;
private I2PSocket remote;
private StringBuffer expectedPong;
// shadows _log in super()
private final Log _log = new Log(I2PTunnelIRCClient.class);
public IrcInboundFilter(Socket _local, I2PSocket _remote, StringBuffer pong) {
local=_local;
@ -207,6 +207,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
private Socket local;
private I2PSocket remote;
private StringBuffer expectedPong;
// shadows _log in super()
private final Log _log = new Log(I2PTunnelIRCClient.class);
public IrcOutboundFilter(Socket _local, I2PSocket _remote, StringBuffer pong) {
local=_local;
@ -308,7 +310,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
try { command = field[idx++]; }
catch (IndexOutOfBoundsException ioobe) // wtf, server sent borked command?
{
_log.warn("Dropping defective message: index out of bounds while extracting command.");
//_log.warn("Dropping defective message: index out of bounds while extracting command.");
return null;
}
@ -431,13 +433,13 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
rv = "PING " + field[1];
expectedPong.append("PONG ").append(field[2]).append(" :").append(field[1]); // PONG serverLocation nonce
} else {
if (_log.shouldLog(Log.ERROR))
_log.error("IRC client sent a PING we don't understand, filtering it (\"" + s + "\")");
//if (_log.shouldLog(Log.ERROR))
// _log.error("IRC client sent a PING we don't understand, filtering it (\"" + s + "\")");
rv = null;
}
if (_log.shouldLog(Log.WARN))
_log.warn("sending ping [" + rv + "], waiting for [" + expectedPong + "] orig was [" + s + "]");
//if (_log.shouldLog(Log.WARN))
// _log.warn("sending ping [" + rv + "], waiting for [" + expectedPong + "] orig was [" + s + "]");
return rv;
}

View File

@ -61,9 +61,7 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
public static final String PROP_WEBIRC_SPOOF_IP_DEFAULT="127.0.0.1";
public static final String PROP_HOSTNAME="ircserver.fakeHostname";
public static final String PROP_HOSTNAME_DEFAULT="%f.b32.i2p";
private static final Log _log = new Log(I2PTunnelIRCServer.class);
private static final long HEADER_TIMEOUT = 60*1000;
/**
* @throws IllegalArgumentException if the I2PTunnel does not contain
@ -108,8 +106,9 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
try {
String modifiedRegistration;
if(!this.method.equals("webirc")) {
// give them 15 seconds to send in the request
socket.setReadTimeout(15*1000);
// The headers _should_ be in the first packet, but
// may not be, depending on the client-side options
socket.setReadTimeout(HEADER_TIMEOUT);
InputStream in = socket.getInputStream();
modifiedRegistration = filterRegistration(in, cloakDest(socket.getPeerDestination()));
socket.setReadTimeout(readTimeout);
@ -126,12 +125,12 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
Socket s = new Socket(remoteHost, remotePort);
new I2PTunnelRunner(s, socket, slock, null, modifiedRegistration.getBytes(), null);
} catch (SocketException ex) {
// TODO send the equivalent of a 503?
try {
socket.close();
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error while closing the received i2p con", ex);
}
} catch (IOException ioe) {}
if (_log.shouldLog(Log.ERROR))
_log.error("Error connecting to IRC server " + remoteHost + ':' + remotePort, ex);
} catch (IOException ex) {
try {
socket.close();
@ -181,8 +180,8 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
if (++lineCount > 10)
throw new IOException("Too many lines before USER or SERVER, giving up");
s = s.trim();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got line: " + s);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Got line: " + s);
String field[]=s.split(" ",5);
String command;
@ -214,8 +213,8 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
if ("SERVER".equalsIgnoreCase(command))
break;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("All done, sending: " + buf.toString());
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("All done, sending: " + buf.toString());
return buf.toString();
}

View File

@ -16,6 +16,12 @@ import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Iterator;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadFactory;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
@ -30,8 +36,7 @@ import net.i2p.util.Log;
public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
private final static Log _log = new Log(I2PTunnelServer.class);
protected final Log _log;
protected I2PSocketManager sockMgr;
protected I2PServerSocket i2pss;
@ -48,12 +53,17 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
/** default timeout to 3 minutes - override if desired */
protected long readTimeout = DEFAULT_READ_TIMEOUT;
private static final boolean DEFAULT_USE_POOL = false;
/** do we use threads? default true (ignored for standard servers, always false) */
private static final String PROP_USE_POOL = "i2ptunnel.usePool";
private static final boolean DEFAULT_USE_POOL = true;
protected static volatile long __serverId = 0;
/** max number of threads - this many slowlorisses will DOS this server, but too high could OOM the JVM */
private static final String PROP_HANDLER_COUNT = "i2ptunnel.blockingHandlerCount";
private static final int DEFAULT_HANDLER_COUNT = 10;
private static final int DEFAULT_HANDLER_COUNT = 65;
/** min number of threads */
private static final int MIN_HANDLERS = 0;
/** how long to wait before dropping an idle thread */
private static final long HANDLER_KEEPALIVE_MS = 30*1000;
protected I2PTunnelTask task = null;
protected boolean bidir = false;
@ -67,8 +77,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
*/
public I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super("Server at " + host + ':' + port, notifyThis, tunnel);
_log = tunnel.getContext().logManager().getLog(getClass());
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(privData));
SetUsePool(tunnel);
init(host, port, bais, privData, l);
}
@ -79,7 +89,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
public I2PTunnelServer(InetAddress host, int port, File privkey, String privkeyname, Logging l,
EventDispatcher notifyThis, I2PTunnel tunnel) {
super("Server at " + host + ':' + port, notifyThis, tunnel);
SetUsePool(tunnel);
_log = tunnel.getContext().logManager().getLog(getClass());
FileInputStream fis = null;
try {
fis = new FileInputStream(privkey);
@ -99,19 +109,10 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
*/
public I2PTunnelServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super("Server at " + host + ':' + port, notifyThis, tunnel);
SetUsePool(tunnel);
_log = tunnel.getContext().logManager().getLog(getClass());
init(host, port, privData, privkeyname, l);
}
private void SetUsePool(I2PTunnel Tunnel) {
String usePool = Tunnel.getClientOptions().getProperty("i2ptunnel.usePool");
if (usePool != null)
_usePool = "true".equalsIgnoreCase(usePool);
else
_usePool = DEFAULT_USE_POOL;
}
private static final int RETRY_DELAY = 20*1000;
private static final int MAX_RETRIES = 4;
@ -143,6 +144,16 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
return;
}
// extending classes default to threaded, but for a standard server, we can't get slowlorissed
_usePool = !getClass().equals(I2PTunnelServer.class);
if (_usePool) {
String usePool = getTunnel().getClientOptions().getProperty(PROP_USE_POOL);
if (usePool != null)
_usePool = "true".equalsIgnoreCase(usePool);
else
_usePool = DEFAULT_USE_POOL;
}
// Todo: Can't stop a tunnel from the UI while it's in this loop (no session yet)
int retries = 0;
while (sockMgr == null) {
@ -199,8 +210,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
*
*/
public void startRunning() {
Thread t = new I2PAppThread(this);
t.setName("Server " + (++__serverId));
Thread t = new I2PAppThread(this, "Server " + remoteHost + ':' + remotePort, true);
t.start();
}
@ -236,7 +246,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
}
return false;
}
l.log("Stopping tunnels for server at " + getTunnel().listenHost + ':' + this.remotePort);
l.log("Stopping tunnels for server at " + this.remoteHost + ':' + this.remotePort);
try {
if (i2pss != null) i2pss.close();
getTunnel().removeSession(sockMgr.getSession());
@ -259,67 +269,106 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
rv = Integer.parseInt(cnt);
if (rv <= 0)
rv = DEFAULT_HANDLER_COUNT;
} catch (NumberFormatException nfe) {
rv = DEFAULT_HANDLER_COUNT;
}
} catch (NumberFormatException nfe) {}
}
return rv;
}
/**
* If usePool is set, this starts the executor pool.
* Then, do the accept() loop, and either
* hands each I2P socket to the executor or runs it in-line.
*/
public void run() {
if (shouldUsePool()) {
I2PServerSocket i2pS_S = sockMgr.getServerSocket();
int handlers = getHandlerCount();
for (int i = 0; i < handlers; i++) {
I2PAppThread handler = new I2PAppThread(new Handler(i2pS_S), "Handle Server " + i);
handler.start();
}
} else {
I2PServerSocket i2pS_S = sockMgr.getServerSocket();
while (true) {
try {
final I2PSocket i2ps = i2pS_S.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
new I2PAppThread(new Runnable() { public void run() { blockingHandle(i2ps); } }).start();
} catch (I2PException ipe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error accepting - KILLING THE TUNNEL SERVER", ipe);
return;
} catch (ConnectException ce) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error accepting", ce);
// not killing the server..
} catch(SocketTimeoutException ste) {
// ignored, we never set the timeout
I2PServerSocket i2pS_S = sockMgr.getServerSocket();
ThreadPoolExecutor executor = null;
if (_log.shouldLog(Log.WARN)) {
if (_usePool)
_log.warn("Starting executor with " + getHandlerCount() + " threads max");
else
_log.warn("Threads disabled, running blockingHandles inline");
}
if (_usePool) {
executor = new CustomThreadPoolExecutor(getHandlerCount(), "ServerHandler pool " + remoteHost + ':' + remotePort);
}
while (open) {
try {
final I2PSocket i2ps = i2pS_S.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
if (_usePool) {
try {
executor.execute(new Handler(i2ps));
} catch (RejectedExecutionException ree) {
try {
i2ps.close();
} catch (IOException ioe) {}
if (open && _log.shouldLog(Log.ERROR))
_log.error("ServerHandler queue full for " + remoteHost + ':' + remotePort +
"; increase " + PROP_HANDLER_COUNT + '?', ree);
}
} else {
// use only for standard servers that can't get slowlorissed! Not for http or irc
blockingHandle(i2ps);
}
} catch (I2PException ipe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error accepting - KILLING THE TUNNEL SERVER", ipe);
return;
} catch (ConnectException ce) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error accepting", ce);
// not killing the server..
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException ie) {}
} catch(SocketTimeoutException ste) {
// ignored, we never set the timeout
}
}
if (executor != null)
executor.shutdownNow();
}
/**
* Not really needed for now but in case we want to add some hooks like afterExecute().
*/
private static class CustomThreadPoolExecutor extends ThreadPoolExecutor {
public CustomThreadPoolExecutor(int max, String name) {
super(MIN_HANDLERS, max, HANDLER_KEEPALIVE_MS, TimeUnit.MILLISECONDS,
new SynchronousQueue(), new CustomThreadFactory(name));
}
}
/** just to set the name and set Daemon */
private static class CustomThreadFactory implements ThreadFactory {
private String _name;
public CustomThreadFactory(String name) {
_name = name;
}
public Thread newThread(Runnable r) {
Thread rv = Executors.defaultThreadFactory().newThread(r);
rv.setName(_name);
rv.setDaemon(true);
return rv;
}
}
public boolean shouldUsePool() { return _usePool; }
/**
* minor thread pool to pull off the accept() concurrently. there are still lots
* (and lots) of wasted threads within the I2PTunnelRunner, but its a start
*
* Run the blockingHandler.
*/
private class Handler implements Runnable {
private I2PServerSocket _serverSocket;
public Handler(I2PServerSocket serverSocket) {
_serverSocket = serverSocket;
private I2PSocket _i2ps;
public Handler(I2PSocket socket) {
_i2ps = socket;
}
public void run() {
while (open) {
try {
blockingHandle(_serverSocket.accept());
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
return;
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
return;
}
}
blockingHandle(_i2ps);
}
}
@ -335,20 +384,21 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
Socket s = new Socket(remoteHost, remotePort);
afterSocket = I2PAppContext.getGlobalContext().clock().now();
new I2PTunnelRunner(s, socket, slock, null, null);
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if ( (timeToHandle > 1000) && (_log.shouldLog(Log.WARN)) )
_log.warn("Took a while to handle the request for " + remoteHost + ':' + remotePort +
" [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
} catch (SocketException ex) {
try {
socket.close();
} catch (IOException ioe) {
_log.error("Error while closing the received i2p con", ex);
}
} catch (IOException ioe) {}
if (_log.shouldLog(Log.ERROR))
_log.error("Error connecting to server " + remoteHost + ':' + remotePort, ex);
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if (timeToHandle > 1000)
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
}
}

View File

@ -30,7 +30,6 @@ import net.i2p.util.Log;
*/
public class I2PSOCKSIRCTunnel extends I2PSOCKSTunnel {
private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(I2PSOCKSIRCTunnel.class);
private static int __clientId = 0;
/** @param pkf private key file name or null for transient key */

View File

@ -26,7 +26,6 @@ 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;

View File

@ -45,7 +45,6 @@ import net.i2p.util.Log;
*/
public abstract class I2PTunnelUDPClientBase extends I2PTunnelTask implements Source, Sink {
private static final Log _log = new Log(I2PTunnelUDPClientBase.class);
protected I2PAppContext _context;
protected Logging l;

View File

@ -46,7 +46,7 @@ import net.i2p.util.Log;
public class I2PTunnelUDPServerBase extends I2PTunnelTask implements Source, Sink {
private final static Log _log = new Log(I2PTunnelUDPServerBase.class);
private final Log _log;
private final Object lock = new Object();
protected Object slock = new Object();
@ -73,6 +73,7 @@ public class I2PTunnelUDPServerBase extends I2PTunnelTask implements Source, Sin
public I2PTunnelUDPServerBase(boolean verify, File privkey, String privkeyname, Logging l,
EventDispatcher notifyThis, I2PTunnel tunnel) {
super("UDPServer <- " + privkeyname, notifyThis, tunnel);
_log = tunnel.getContext().logManager().getLog(I2PTunnelUDPServerBase.class);
FileInputStream fis = null;
try {
fis = new FileInputStream(privkey);

View File

@ -18,6 +18,7 @@ import net.i2p.data.Destination;
import net.i2p.data.PrivateKeyFile;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
import net.i2p.i2ptunnel.I2PTunnelHTTPClient;
import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.i2ptunnel.TunnelControllerGroup;
@ -171,14 +172,23 @@ public class EditBean extends IndexBean {
return getProperty(tunnel, "i2cp.leaseSetKey", "");
}
public boolean getAccess(int tunnel) {
return getBooleanProperty(tunnel, "i2cp.enableAccessList");
public String getAccessMode(int tunnel) {
if (getBooleanProperty(tunnel, PROP_ENABLE_ACCESS_LIST))
return "1";
if (getBooleanProperty(tunnel, PROP_ENABLE_BLACKLIST))
return "2";
return "0";
}
public String getAccessList(int tunnel) {
return getProperty(tunnel, "i2cp.accessList", "").replace(",", "\n");
}
public String getJumpList(int tunnel) {
return getProperty(tunnel, I2PTunnelHTTPClient.PROP_JUMP_SERVERS,
I2PTunnelHTTPClient.DEFAULT_JUMP_SERVERS).replace(",", "\n");
}
public boolean getClose(int tunnel) {
return getBooleanProperty(tunnel, "i2cp.closeOnIdle");
}
@ -234,6 +244,35 @@ public class EditBean extends IndexBean {
return getProperty(tunnel, I2PTunnelHTTPClientBase.PROP_OUTPROXY_PW, "");
}
/** all of these are @since 0.8.3 */
public String getLimitMinute(int tunnel) {
return getProperty(tunnel, PROP_MAX_CONNS_MIN, "0");
}
public String getLimitHour(int tunnel) {
return getProperty(tunnel, PROP_MAX_CONNS_HOUR, "0");
}
public String getLimitDay(int tunnel) {
return getProperty(tunnel, PROP_MAX_CONNS_DAY, "0");
}
public String getTotalMinute(int tunnel) {
return getProperty(tunnel, PROP_MAX_TOTAL_CONNS_MIN, "0");
}
public String getTotalHour(int tunnel) {
return getProperty(tunnel, PROP_MAX_TOTAL_CONNS_HOUR, "0");
}
public String getTotalDay(int tunnel) {
return getProperty(tunnel, PROP_MAX_TOTAL_CONNS_DAY, "0");
}
public String getMaxStreams(int tunnel) {
return getProperty(tunnel, PROP_MAX_STREAMS, "0");
}
private int getProperty(int tunnel, String prop, int def) {
TunnelController tun = getController(tunnel);
if (tun != null) {
@ -270,7 +309,14 @@ public class EditBean extends IndexBean {
return false;
}
/** @since 0.8.3 */
public boolean isRouterContext() {
return _context.isRouterContext();
}
public String getI2CPHost(int tunnel) {
if (_context.isRouterContext())
return _("internal");
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getI2CPHost();
@ -279,6 +325,8 @@ public class EditBean extends IndexBean {
}
public String getI2CPPort(int tunnel) {
if (_context.isRouterContext())
return _("internal");
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getI2CPPort();

View File

@ -24,6 +24,7 @@ import net.i2p.data.Certificate;
import net.i2p.data.Destination;
import net.i2p.data.PrivateKeyFile;
import net.i2p.data.SessionKey;
import net.i2p.i2ptunnel.I2PTunnelHTTPClient;
import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.i2ptunnel.TunnelControllerGroup;
@ -537,11 +538,11 @@ public class IndexBean {
public void setDescription(String description) {
_description = (description != null ? description.trim() : null);
}
/** I2CP host the router is on */
/** I2CP host the router is on, ignored when in router context */
public void setClientHost(String host) {
_i2cpHost = (host != null ? host.trim() : null);
}
/** I2CP port the router is on */
/** I2CP port the router is on, ignored when in router context */
public void setClientport(String port) {
_i2cpPort = (port != null ? port.trim() : null);
}
@ -643,9 +644,17 @@ public class IndexBean {
public void setEncrypt(String moo) {
_booleanOptions.add("i2cp.encryptLeaseSet");
}
public void setAccess(String moo) {
_booleanOptions.add("i2cp.enableAccessList");
protected static final String PROP_ENABLE_ACCESS_LIST = "i2cp.enableAccessList";
protected static final String PROP_ENABLE_BLACKLIST = "i2cp.enableBlackList";
public void setAccessMode(String val) {
if ("1".equals(val))
_booleanOptions.add(PROP_ENABLE_ACCESS_LIST);
else if ("2".equals(val))
_booleanOptions.add(PROP_ENABLE_BLACKLIST);
}
public void setDelayOpen(String moo) {
_booleanOptions.add("i2cp.delayOpen");
}
@ -671,10 +680,17 @@ public class IndexBean {
if (val != null)
_otherOptions.put("i2cp.leaseSetKey", val.trim());
}
public void setAccessList(String val) {
if (val != null)
_otherOptions.put("i2cp.accessList", val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ","));
}
public void setJumpList(String val) {
if (val != null)
_otherOptions.put(I2PTunnelHTTPClient.PROP_JUMP_SERVERS, val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ","));
}
public void setCloseTime(String val) {
if (val != null) {
try {
@ -712,6 +728,50 @@ public class IndexBean {
_otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_PW, s.trim());
}
/** all of these are @since 0.8.3 */
protected static final String PROP_MAX_CONNS_MIN = "i2p.streaming.maxConnsPerMinute";
protected static final String PROP_MAX_CONNS_HOUR = "i2p.streaming.maxConnsPerHour";
protected static final String PROP_MAX_CONNS_DAY = "i2p.streaming.maxConnsPerDay";
protected static final String PROP_MAX_TOTAL_CONNS_MIN = "i2p.streaming.maxTotalConnsPerMinute";
protected static final String PROP_MAX_TOTAL_CONNS_HOUR = "i2p.streaming.maxTotalConnsPerHour";
protected static final String PROP_MAX_TOTAL_CONNS_DAY = "i2p.streaming.maxTotalConnsPerDay";
protected static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams";
public void setLimitMinute(String s) {
if (s != null)
_otherOptions.put(PROP_MAX_CONNS_MIN, s.trim());
}
public void setLimitHour(String s) {
if (s != null)
_otherOptions.put(PROP_MAX_CONNS_HOUR, s.trim());
}
public void setLimitDay(String s) {
if (s != null)
_otherOptions.put(PROP_MAX_CONNS_DAY, s.trim());
}
public void setTotalMinute(String s) {
if (s != null)
_otherOptions.put(PROP_MAX_TOTAL_CONNS_MIN, s.trim());
}
public void setTotalHour(String s) {
if (s != null)
_otherOptions.put(PROP_MAX_TOTAL_CONNS_HOUR, s.trim());
}
public void setTotalDay(String s) {
if (s != null)
_otherOptions.put(PROP_MAX_TOTAL_CONNS_DAY, s.trim());
}
public void setMaxStreams(String s) {
if (s != null)
_otherOptions.put(PROP_MAX_STREAMS, s.trim());
}
/** params needed for hashcash and dest modification */
public void setEffort(String val) {
if (val != null) {
@ -904,16 +964,20 @@ public class IndexBean {
I2PTunnelHTTPClientBase.PROP_AUTH, I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH
};
private static final String _booleanServerOpts[] = {
"i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", "i2cp.enableAccessList"
"i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", PROP_ENABLE_ACCESS_LIST, PROP_ENABLE_BLACKLIST
};
private static final String _otherClientOpts[] = {
"i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.closeIdleTime",
"proxyUsername", "proxyPassword", "outproxyUsername", "outproxyPassword"
"proxyUsername", "proxyPassword", "outproxyUsername", "outproxyPassword",
I2PTunnelHTTPClient.PROP_JUMP_SERVERS
};
private static final String _otherServerOpts[] = {
"i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.leaseSetKey", "i2cp.accessList"
"i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.leaseSetKey", "i2cp.accessList",
PROP_MAX_CONNS_MIN, PROP_MAX_CONNS_HOUR, PROP_MAX_CONNS_DAY,
PROP_MAX_TOTAL_CONNS_MIN, PROP_MAX_TOTAL_CONNS_HOUR, PROP_MAX_TOTAL_CONNS_DAY,
PROP_MAX_STREAMS
};
protected static final Set _noShowSet = new HashSet();
protected static final Set _noShowSet = new HashSet(64);
static {
_noShowSet.addAll(Arrays.asList(_noShowOpts));
_noShowSet.addAll(Arrays.asList(_booleanClientOpts));
@ -929,12 +993,14 @@ public class IndexBean {
config.setProperty("name", _name);
if (_description != null)
config.setProperty("description", _description);
if (_i2cpHost != null)
config.setProperty("i2cpHost", _i2cpHost);
if ( (_i2cpPort != null) && (_i2cpPort.trim().length() > 0) ) {
config.setProperty("i2cpPort", _i2cpPort);
} else {
config.setProperty("i2cpPort", "7654");
if (!_context.isRouterContext()) {
if (_i2cpHost != null)
config.setProperty("i2cpHost", _i2cpHost);
if ( (_i2cpPort != null) && (_i2cpPort.trim().length() > 0) ) {
config.setProperty("i2cpPort", _i2cpPort);
} else {
config.setProperty("i2cpPort", "7654");
}
}
if (_privKeyFile != null)
config.setProperty("privKeyFile", _privKeyFile);
@ -1020,7 +1086,7 @@ public class IndexBean {
}
}
private String _(String key) {
protected String _(String key) {
return Messages._(key, _context);
}
}

View File

@ -286,19 +286,19 @@
<% } // !streamrclient %>
<div id="optionsField" class="rowItem">
<label><%=intl._("I2CP Options")%>:</label>
<label><%=intl._("Router I2CP Address")%>:</label>
</div>
<div id="optionsHostField" class="rowItem">
<label for="clientHost" accesskey="o">
<%=intl._("Host")%>(<span class="accessKey">o</span>):
</label>
<input type="text" id="clientHost" name="clientHost" size="20" title="I2CP Hostname or IP" value="<%=editBean.getI2CPHost(curTunnel)%>" class="freetext" />
<input type="text" id="clientHost" name="clientHost" size="20" title="I2CP Hostname or IP" value="<%=editBean.getI2CPHost(curTunnel)%>" class="freetext" <% if (editBean.isRouterContext()) { %> readonly="readonly" <% } %> />
</div>
<div id="optionsPortField" class="rowItem">
<label for="clientPort" accesskey="r">
<%=intl._("Port")%>(<span class="accessKey">r</span>):
</label>
<input type="text" id="port" name="clientport" size="20" title="I2CP Port Number" value="<%=editBean.getI2CPPort(curTunnel)%>" class="freetext" />
<input type="text" id="clientPort" name="clientport" size="20" title="I2CP Port Number" value="<%=editBean.getI2CPPort(curTunnel)%>" class="freetext" <% if (editBean.isRouterContext()) { %> readonly="readonly" <% } %> />
</div>
<% if (!"streamrclient".equals(tunnelType)) { // streamr client sends pings so it will never be idle %>
@ -465,6 +465,18 @@
</div>
<% } // httpclient || connect || socks || socksirc %>
<% if ("httpclient".equals(tunnelType)) { %>
<div id="optionsField" class="rowItem">
<label><%=intl._("Jump URL List")%>:</label>
</div>
<div id="hostField" class="rowItem">
<textarea rows="2" style="height: 8em;" cols="60" id="hostField" name="jumpList" title="List of helper URLs to offer when a host is not found in your addressbook" wrap="off"><%=editBean.getJumpList(curTunnel)%></textarea>
</div>
<div class="subdivider">
<hr />
</div>
<% } // httpclient %>
<div id="customOptionsField" class="rowItem">
<label for="customOptions" accesskey="u">
<%=intl._("Custom options")%>(<span class="accessKey">u</span>):

View File

@ -305,19 +305,19 @@
<% } // !streamrserver %>
<div id="optionsField" class="rowItem">
<label><%=intl._("I2CP Options")%>:</label>
<label><%=intl._("Router I2CP Address")%>:</label>
</div>
<div id="optionsHostField" class="rowItem">
<label for="clientHost" accesskey="o">
<%=intl._("Host")%>(<span class="accessKey">o</span>):
</label>
<input type="text" id="clientHost" name="clientHost" size="20" title="I2CP Hostname or IP" value="<%=editBean.getI2CPHost(curTunnel)%>" class="freetext" />
<input type="text" id="clientHost" name="clientHost" size="20" title="I2CP Hostname or IP" value="<%=editBean.getI2CPHost(curTunnel)%>" class="freetext" <% if (editBean.isRouterContext()) { %> readonly="readonly" <% } %> />
</div>
<div id="optionsPortField" class="rowItem">
<label for="clientPort" accesskey="r">
<%=intl._("Port")%>(<span class="accessKey">r</span>):
</label>
<input type="text" id="clientPort" name="clientport" size="20" title="I2CP Port Number" value="<%=editBean.getI2CPPort(curTunnel)%>" class="freetext" />
<input type="text" id="clientPort" name="clientport" size="20" title="I2CP Port Number" value="<%=editBean.getI2CPPort(curTunnel)%>" class="freetext" <% if (editBean.isRouterContext()) { %> readonly="readonly" <% } %> />
</div>
<div class="subdivider">
@ -333,7 +333,7 @@
<label for="encrypt" accesskey="e">
<%=intl._("Enable")%>:
</label>
<input value="1" type="checkbox" id="startOnLoad" name="encrypt" title="Encrypt LeaseSet"<%=(editBean.getEncrypt(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
<input value="1" type="checkbox" id="startOnLoad" name="encrypt" title="ONLY clients with the encryption key will be able to connect"<%=(editBean.getEncrypt(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
</div>
<div id="portField" class="rowItem">
<label for="encrypt" accesskey="e">
@ -359,19 +359,64 @@
</label>
</div>
<div id="portField" class="rowItem">
<label for="access" accesskey="s">
<%=intl._("Enable")%>:
</label>
<input value="1" type="checkbox" id="startOnLoad" name="access" title="Enable Access List"<%=(editBean.getAccess(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" />
<label><%=intl._("Disable")%></label>
<input value="0" type="radio" id="startOnLoad" name="accessMode" title="Allow all clients"<%=(editBean.getAccessMode(curTunnel).equals("0") ? " checked=\"checked\"" : "")%> class="tickbox" />
<label><%=intl._("Whitelist")%></label>
<input value="1" type="radio" id="startOnLoad" name="accessMode" title="Allow listed clients only"<%=(editBean.getAccessMode(curTunnel).equals("1") ? " checked=\"checked\"" : "")%> class="tickbox" />
<label><%=intl._("Blacklist")%></label>
<input value="2" type="radio" id="startOnLoad" name="accessMode" title="Reject listed clients"<%=(editBean.getAccessMode(curTunnel).equals("2") ? " checked=\"checked\"" : "")%> class="tickbox" />
</div>
<div id="hostField" class="rowItem">
<label for="accessList" accesskey="s">
<%=intl._("Access List")%>:
</label>
<textarea rows="2" style="height: 4em;" cols="60" id="hostField" name="accessList" title="Access List" wrap="off"><%=editBean.getAccessList(curTunnel)%></textarea>
<span class="comment"><%=intl._("(Restrict to these clients only)")%></span>
<textarea rows="2" style="height: 8em;" cols="60" id="hostField" name="accessList" title="Access List" wrap="off"><%=editBean.getAccessList(curTunnel)%></textarea>
</div>
<div class="subdivider">
<hr />
</div>
<div class="rowItem">
<div id="optionsField" class="rowItem">
<label><%=intl._("Inbound connection limits (0 to disable)")%><br><%=intl._("Per client")%>:</label>
</div>
<div id="portField" class="rowItem">
<label><%=intl._("Per minute")%>:</label>
<input type="text" id="port" name="limitMinute" value="<%=editBean.getLimitMinute(curTunnel)%>" class="freetext" />
</div>
<div id="portField" class="rowItem">
<label><%=intl._("Per hour")%>:</label>
<input type="text" id="port" name="limitHour" value="<%=editBean.getLimitHour(curTunnel)%>" class="freetext" />
</div>
<div id="portField" class="rowItem">
<label><%=intl._("Per day")%>:</label>
<input type="text" id="port" name="limitDay" value="<%=editBean.getLimitDay(curTunnel)%>" class="freetext" />
</div>
</div>
<div class="rowItem">
<div id="optionsField" class="rowItem">
<label><%=intl._("Total")%>:</label>
</div>
<div id="portField" class="rowItem">
<input type="text" id="port" name="totalMinute" value="<%=editBean.getTotalMinute(curTunnel)%>" class="freetext" />
</div>
<div id="portField" class="rowItem">
<input type="text" id="port" name="totalHour" value="<%=editBean.getTotalHour(curTunnel)%>" class="freetext" />
</div>
<div id="portField" class="rowItem">
<input type="text" id="port" name="totalDay" value="<%=editBean.getTotalDay(curTunnel)%>" class="freetext" />
</div>
</div>
<div class="rowItem">
<div id="optionsField" class="rowItem">
<label><%=intl._("Max concurrent connections (0 to disable)")%>:</label>
</div>
<div id="portField" class="rowItem">
<input type="text" id="port" name="maxStreams" value="<%=editBean.getMaxStreams(curTunnel)%>" class="freetext" />
</div>
</div>
<div class="subdivider">
<hr />
</div>

View File

@ -19,9 +19,10 @@ public interface I2PServerSocket {
/**
* Waits for the next socket connecting. If a remote user tried to make a
* connection and the local application wasn't .accept()ing new connections,
* they should get refused (if .accept() doesnt occur in some small period)
* they should get refused (if .accept() doesnt occur in some small period).
* Warning - unlike regular ServerSocket, may return null.
*
* @return a connected I2PSocket
* @return a connected I2PSocket OR NULL
*
* @throws I2PException if there is a problem with reading a new socket
* from the data available (aka the I2PSession closed, etc)

View File

@ -9,6 +9,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.Set;
import net.i2p.router.client.ClientManagerFacadeImpl;
import net.i2p.router.startup.ClientAppConfig;
import net.i2p.router.startup.LoadClientAppsJob;
@ -35,6 +36,10 @@ public class ConfigClientsHandler extends FormHandler {
saveClientChanges();
return;
}
if (_action.equals(_("Save Interface Configuration"))) {
saveInterfaceChanges();
return;
}
if (_action.equals(_("Save WebApp Configuration"))) {
saveWebAppChanges();
return;
@ -193,7 +198,7 @@ public class ConfigClientsHandler extends FormHandler {
String[] arr = (String[]) _settings.get(key);
if (arr == null)
return null;
return arr[0];
return arr[0].trim();
}
private void startClient(int i) {
@ -337,4 +342,37 @@ public class ConfigClientsHandler extends FormHandler {
_log.error("Error starting plugin " + app, e);
}
}
/**
* Handle interface form
* @since 0.8.3
*/
private void saveInterfaceChanges() {
String port = getJettyString("port");
if (port != null)
_context.router().setConfigSetting(ClientManagerFacadeImpl.PROP_CLIENT_PORT, port);
String intfc = getJettyString("interface");
if (intfc != null)
_context.router().setConfigSetting(ClientManagerFacadeImpl.PROP_CLIENT_HOST, intfc);
String user = getJettyString("user");
if (user != null)
_context.router().setConfigSetting(ConfigClientsHelper.PROP_USER, user);
String pw = getJettyString("pw");
if (pw != null)
_context.router().setConfigSetting(ConfigClientsHelper.PROP_PW, pw);
String mode = getJettyString("mode");
boolean disabled = "0".equals(mode);
boolean ssl = "2".equals(mode);
_context.router().setConfigSetting(ConfigClientsHelper.PROP_DISABLE_EXTERNAL,
Boolean.toString(disabled));
_context.router().setConfigSetting(ConfigClientsHelper.PROP_ENABLE_SSL,
Boolean.toString(ssl));
_context.router().setConfigSetting(ConfigClientsHelper.PROP_AUTH,
Boolean.toString((_settings.get("auth") != null)));
boolean all = "0.0.0.0".equals(intfc) || "0:0:0:0:0:0:0:0".equals(intfc) ||
"::".equals(intfc);
_context.router().setConfigSetting(ConfigClientsHelper.BIND_ALL_INTERFACES, Boolean.toString(all));
_context.router().saveConfig();
addFormNotice(_("Interface configuration saved successfully - restart required to take effect."));
}
}

View File

@ -1,6 +1,7 @@
package net.i2p.router.web;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@ -8,13 +9,76 @@ import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import net.i2p.router.client.ClientManagerFacadeImpl;
import net.i2p.router.startup.ClientAppConfig;
import net.i2p.util.Addresses;
public class ConfigClientsHelper extends HelperBase {
private String _edit;
/** from ClientListenerRunner */
public static final String BIND_ALL_INTERFACES = "i2cp.tcp.bindAllInterfaces";
/** from ClientManager */
public static final String PROP_DISABLE_EXTERNAL = "i2cp.disableInterface";
public static final String PROP_ENABLE_SSL = "i2cp.SSL";
/** from ClientMessageEventListener */
public static final String PROP_AUTH = "i2cp.auth";
public static final String PROP_USER = "i2cp.username";
public static final String PROP_PW = "i2cp.password";
public ConfigClientsHelper() {}
/** @since 0.8.3 */
public String getPort() {
return _context.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_PORT,
Integer.toString(ClientManagerFacadeImpl.DEFAULT_PORT));
}
/** @since 0.8.3 */
public String getUser() {
return _context.getProperty(PROP_USER, "");
}
/** @since 0.8.3 */
public String getPw() {
return _context.getProperty(PROP_PW, "");
}
/** @since 0.8.3 */
public String i2cpModeChecked(int mode) {
boolean disabled = _context.getBooleanProperty(PROP_DISABLE_EXTERNAL);
boolean ssl = _context.getBooleanProperty(PROP_ENABLE_SSL);
if ((mode == 0 && disabled) ||
(mode == 1 && (!disabled) && (!ssl)) ||
(mode == 2 && (!disabled) && ssl))
return "checked=\"true\"";
return "";
}
/** @since 0.8.3 */
public String getAuth() {
boolean enabled = _context.getBooleanProperty(PROP_AUTH);
if (enabled)
return "checked=\"true\"";
return "";
}
/** @since 0.8.3 */
public String[] intfcAddresses() {
ArrayList<String> al = new ArrayList(Addresses.getAllAddresses());
return al.toArray(new String[al.size()]);
}
/** @since 0.8.3 */
public boolean isIFSelected(String addr) {
boolean bindAll = _context.getBooleanProperty(BIND_ALL_INTERFACES);
if (bindAll && addr.equals("0.0.0.0") || addr.equals("::"))
return true;
String host = _context.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_HOST,
ClientManagerFacadeImpl.DEFAULT_HOST);
return (host.equals(addr));
}
public void setEdit(String edit) {
if (edit == null)
return;
@ -92,9 +156,9 @@ public class ConfigClientsHelper extends HelperBase {
continue;
StringBuilder desc = new StringBuilder(256);
desc.append("<table border=\"0\">")
.append("<tr><td><b>").append(_("Version")).append("<td>").append(stripHTML(appProps, "version"))
.append("<tr><td><b>").append(_("Version")).append("</b></td><td>").append(stripHTML(appProps, "version"))
.append("<tr><td><b>")
.append(_("Signed by")).append("<td>");
.append(_("Signed by")).append("</b></td><td>");
String s = stripHTML(appProps, "signer");
if (s != null) {
if (s.indexOf("@") > 0)
@ -111,13 +175,13 @@ public class ConfigClientsHelper extends HelperBase {
if (ms > 0) {
String date = (new SimpleDateFormat("yyyy-MM-dd HH:mm")).format(new Date(ms));
desc.append("<tr><td><b>")
.append(_("Date")).append("<td>").append(date);
.append(_("Date")).append("</b></td><td>").append(date);
}
}
s = stripHTML(appProps, "author");
if (s != null) {
desc.append("<tr><td><b>")
.append(_("Author")).append("<td>");
.append(_("Author")).append("</b></td><td>");
if (s.indexOf("@") > 0)
desc.append("<a href=\"mailto:").append(s).append("\">").append(s).append("</a>");
else
@ -128,12 +192,12 @@ public class ConfigClientsHelper extends HelperBase {
s = stripHTML(appProps, "description");
if (s != null) {
desc.append("<tr><td><b>")
.append(_("Description")).append("<td>").append(s);
.append(_("Description")).append("</b></td><td>").append(s);
}
s = stripHTML(appProps, "license");
if (s != null) {
desc.append("<tr><td><b>")
.append(_("License")).append("<td>").append(s);
.append(_("License")).append("</b></td><td>").append(s);
}
s = stripHTML(appProps, "websiteURL");
if (s != null) {

View File

@ -1,14 +1,16 @@
package net.i2p.router.web;
import java.util.ArrayList;
import net.i2p.data.DataHelper;
import net.i2p.data.RouterAddress;
import net.i2p.router.CommSystemFacade;
import net.i2p.router.Router;
import net.i2p.router.transport.Addresses;
import net.i2p.router.transport.TransportManager;
import net.i2p.router.transport.udp.UDPAddress;
import net.i2p.router.transport.udp.UDPTransport;
import net.i2p.time.Timestamper;
import net.i2p.util.Addresses;
public class ConfigNetHelper extends HelperBase {
public ConfigNetHelper() {}
@ -147,7 +149,8 @@ public class ConfigNetHelper extends HelperBase {
}
public String[] getAddresses() {
return Addresses.getAddresses();
ArrayList<String> al = new ArrayList(Addresses.getAddresses());
return al.toArray(new String[al.size()]);
}
public String getInboundRate() {

View File

@ -139,6 +139,6 @@ public class ConfigStatsHelper extends HelperBase {
public boolean getCurrentCanBeGraphed() { return _currentCanBeGraphed; }
public String getExplicitFilter() { return _filter; }
public boolean getIsFull() {
return _context.getBooleanPropertyDefaultTrue(StatManager.PROP_STAT_FULL);
return _context.getBooleanProperty(StatManager.PROP_STAT_FULL);
}
}

View File

@ -3,6 +3,7 @@ package net.i2p.router.web;
import net.i2p.I2PAppContext;
import net.i2p.crypto.TrustedUpdate;
import net.i2p.data.DataHelper;
import net.i2p.util.FileUtil;
/**
*
@ -61,14 +62,10 @@ public class ConfigUpdateHandler extends FormHandler {
public static final String DEFAULT_UPDATE_URL;
static {
String foo;
try {
Class.forName("java.util.jar.Pack200", false, ClassLoader.getSystemClassLoader());
foo = PACK200_URLS;
} catch (ClassNotFoundException cnfe) {
foo = NO_PACK200_URLS;
}
DEFAULT_UPDATE_URL = foo;
if (FileUtil.isPack200Supported())
DEFAULT_UPDATE_URL = PACK200_URLS;
else
DEFAULT_UPDATE_URL = NO_PACK200_URLS;
}
public static final String PROP_TRUSTED_KEYS = "router.trustedUpdateKeys";

View File

@ -59,7 +59,13 @@ public class GraphHelper extends FormHandler {
try { _width = Math.min(Integer.parseInt(str), MAX_X); } catch (NumberFormatException nfe) {}
}
public void setRefreshDelay(String str) {
try { _refreshDelaySeconds = Math.max(Integer.parseInt(str), MIN_REFRESH); } catch (NumberFormatException nfe) {}
try {
int rds = Integer.parseInt(str);
if (rds > 0)
_refreshDelaySeconds = Math.max(rds, MIN_REFRESH);
else
_refreshDelaySeconds = -1;
} catch (NumberFormatException nfe) {}
}
public String getImages() {
@ -83,7 +89,7 @@ public class GraphHelper extends FormHandler {
+ "&amp;periodCount=" + (3 * _periodCount )
+ "&amp;width=" + (3 * _width)
+ "&amp;height=" + (3 * _height)
+ "\" / target=\"_blank\">");
+ "\" target=\"_blank\">");
String title = _("Combined bandwidth graph");
_out.write("<img class=\"statimage\" width=\""
+ (_width + 83) + "\" height=\"" + (_height + 92)
@ -129,6 +135,8 @@ public class GraphHelper extends FormHandler {
return "";
}
private static final int[] times = { 60, 2*60, 5*60, 10*60, 30*60, 60*60, -1 };
public String getForm() {
String prev = System.getProperty("net.i2p.router.web.GraphHelper.nonce");
if (prev != null) System.setProperty("net.i2p.router.web.GraphHelper.noncePrev", prev);
@ -145,8 +153,22 @@ public class GraphHelper extends FormHandler {
_out.write(_("Image sizes") + ": " + _("width") + ": <input size=\"4\" type=\"text\" name=\"width\" value=\"" + _width
+ "\"> " + _("pixels") + ", " + _("height") + ": <input size=\"4\" type=\"text\" name=\"height\" value=\"" + _height
+ "\"> " + _("pixels") + "<br>\n");
_out.write(_("Refresh delay") + ": <select name=\"refreshDelay\"><option value=\"60\">1 " + _("minute") + "</option><option value=\"120\">2 " + _("minutes") + "</option><option value=\"300\">5 " + _("minutes") + "</option><option value=\"600\">10 " + _("minutes") + "</option><option value=\"1800\">30 " + _("minutes") + "</option><option value=\"3600\">1 " + _("hour") + "</option><option value=\"-1\">" + _("Never") + "</option></select><br>\n");
_out.write("<hr><div class=\"formaction\"><input type=\"submit\" value=\"" + _("Redraw") + "\"></div></form>");
_out.write(_("Refresh delay") + ": <select name=\"refreshDelay\">");
for (int i = 0; i < times.length; i++) {
_out.write("<option value=\"");
_out.write(Integer.toString(times[i]));
_out.write("\"");
if (times[i] == _refreshDelaySeconds)
_out.write(" selected=\"true\"");
_out.write(">");
if (times[i] > 0)
_out.write(DataHelper.formatDuration2(times[i] * 1000));
else
_out.write(_("Never"));
_out.write("</option>\n");
}
_out.write("</select><br>\n" +
"<hr><div class=\"formaction\"><input type=\"submit\" value=\"" + _("Redraw") + "\"></div></form>");
} catch (IOException ioe) {
ioe.printStackTrace();
}

View File

@ -53,7 +53,8 @@ public class LocaleWebAppHandler extends WebApplicationHandler
// home page
pathInContext = "/index.jsp";
} else if (pathInContext.indexOf("/", 1) < 0 &&
!pathInContext.endsWith(".jsp")) {
(!pathInContext.endsWith(".jsp")) &&
(!pathInContext.endsWith(".txt"))) {
// add .jsp to pages at top level
pathInContext += ".jsp";
}

View File

@ -4,37 +4,53 @@ import java.util.ArrayList;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.security.KeyStore;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.apps.systray.SysTray;
import net.i2p.data.Base32;
import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread;
import net.i2p.util.SecureDirectory;
import net.i2p.util.SecureFileOutputStream;
import net.i2p.util.ShellCommand;
import org.mortbay.http.DigestAuthenticator;
import org.mortbay.http.HashUserRealm;
import org.mortbay.http.SecurityConstraint;
import org.mortbay.http.SslListener;
import org.mortbay.http.handler.SecurityHandler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.WebApplicationContext;
import org.mortbay.jetty.servlet.WebApplicationHandler;
import org.mortbay.util.InetAddrPort;
public class RouterConsoleRunner {
private Server _server;
private String _listenPort = "7657";
private String _listenHost = "127.0.0.1";
private String _webAppsDir = "./webapps/";
private String _listenPort;
private String _listenHost;
private String _sslListenPort;
private String _sslListenHost;
private String _webAppsDir;
private static final String PROP_WEBAPP_CONFIG_FILENAME = "router.webappsConfigFile";
private static final String DEFAULT_WEBAPP_CONFIG_FILENAME = "webapps.config";
private static final DigestAuthenticator authenticator = new DigestAuthenticator();
public static final String ROUTERCONSOLE = "routerconsole";
public static final String PREFIX = "webapps.";
public static final String ENABLED = ".startOnLoad";
private static final String PROP_KEYSTORE_PASSWORD = "routerconsole.keystorePassword";
private static final String DEFAULT_KEYSTORE_PASSWORD = "changeit";
private static final String PROP_KEY_PASSWORD = "routerconsole.keyPassword";
private static final String DEFAULT_LISTEN_PORT = "7657";
private static final String DEFAULT_LISTEN_HOST = "127.0.0.1";
private static final String DEFAULT_WEBAPPS_DIR = "./webapps/";
private static final String USAGE = "Bad RouterConsoleRunner arguments, check clientApp.0.args in your clients.config file! " +
"Usage: [[port host[,host]] [-s sslPort [host[,host]]] [webAppsDir]]";
static {
System.setProperty("org.mortbay.http.Version.paranoid", "true");
@ -42,6 +58,27 @@ public class RouterConsoleRunner {
}
/**
* <pre>
* non-SSL:
* RouterConsoleRunner
* RouterConsoleRunner 7657
* RouterConsoleRunner 7657 127.0.0.1
* RouterConsoleRunner 7657 127.0.0.1,::1
* RouterConsoleRunner 7657 127.0.0.1,::1 ./webapps/
*
* SSL:
* RouterConsoleRunner -s 7657
* RouterConsoleRunner -s 7657 127.0.0.1
* RouterConsoleRunner -s 7657 127.0.0.1,::1
* RouterConsoleRunner -s 7657 127.0.0.1,::1 ./webapps/
*
* If using both, non-SSL must be first:
* RouterConsoleRunner 7657 127.0.0.1 -s 7667
* RouterConsoleRunner 7657 127.0.0.1 -s 7667 127.0.0.1
* RouterConsoleRunner 7657 127.0.0.1,::1 -s 7667 127.0.0.1,::1
* RouterConsoleRunner 7657 127.0.0.1,::1 -s 7667 127.0.0.1,::1 ./webapps/
* </pre>
*
* @param args second arg may be a comma-separated list of bind addresses,
* for example ::1,127.0.0.1
* On XP, the other order (127.0.0.1,::1) fails the IPV6 bind,
@ -50,10 +87,40 @@ public class RouterConsoleRunner {
* So the wise choice is ::1,127.0.0.1
*/
public RouterConsoleRunner(String args[]) {
if (args.length == 3) {
_listenPort = args[0].trim();
_listenHost = args[1].trim();
_webAppsDir = args[2].trim();
if (args.length == 0) {
// _listenHost and _webAppsDir are defaulted below
_listenPort = DEFAULT_LISTEN_PORT;
} else {
boolean ssl = false;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-s"))
ssl = true;
else if ((!ssl) && _listenPort == null)
_listenPort = args[i];
else if ((!ssl) && _listenHost == null)
_listenHost = args[i];
else if (ssl && _sslListenPort == null)
_sslListenPort = args[i];
else if (ssl && _sslListenHost == null)
_sslListenHost = args[i];
else if (_webAppsDir == null)
_webAppsDir = args[i];
else {
System.err.println(USAGE);
throw new IllegalArgumentException(USAGE);
}
}
}
if (_listenHost == null)
_listenHost = DEFAULT_LISTEN_HOST;
if (_sslListenHost == null)
_sslListenHost = _listenHost;
if (_webAppsDir == null)
_webAppsDir = DEFAULT_WEBAPPS_DIR;
// _listenPort and _sslListenPort are not defaulted, if one or the other is null, do not enable
if (_listenPort == null && _sslListenPort == null) {
System.err.println(USAGE);
throw new IllegalArgumentException(USAGE);
}
}
@ -96,27 +163,69 @@ public class RouterConsoleRunner {
List<String> notStarted = new ArrayList();
WebApplicationHandler baseHandler = null;
try {
StringTokenizer tok = new StringTokenizer(_listenHost, " ,");
int boundAddresses = 0;
while (tok.hasMoreTokens()) {
String host = tok.nextToken().trim();
try {
if (host.indexOf(":") >= 0) // IPV6 - requires patched Jetty 5
_server.addListener('[' + host + "]:" + _listenPort);
else
_server.addListener(host + ':' + _listenPort);
boundAddresses++;
} catch (IOException ioe) { // this doesn't seem to work, exceptions don't happen until start() below
System.err.println("Unable to bind routerconsole to " + host + " port " + _listenPort + ' ' + ioe);
// add standard listeners
if (_listenPort != null) {
StringTokenizer tok = new StringTokenizer(_listenHost, " ,");
while (tok.hasMoreTokens()) {
String host = tok.nextToken().trim();
try {
if (host.indexOf(":") >= 0) // IPV6 - requires patched Jetty 5
_server.addListener('[' + host + "]:" + _listenPort);
else
_server.addListener(host + ':' + _listenPort);
boundAddresses++;
} catch (IOException ioe) { // this doesn't seem to work, exceptions don't happen until start() below
System.err.println("Unable to bind routerconsole to " + host + " port " + _listenPort + ' ' + ioe);
}
}
}
// add SSL listeners
int sslPort = 0;
if (_sslListenPort != null) {
try {
sslPort = Integer.parseInt(_sslListenPort);
} catch (NumberFormatException nfe) {}
if (sslPort <= 0)
System.err.println("Bad routerconsole SSL port " + _sslListenPort);
}
if (sslPort > 0) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
File keyStore = new File(ctx.getConfigDir(), "keystore/console.ks");
if (verifyKeyStore(keyStore)) {
StringTokenizer tok = new StringTokenizer(_sslListenHost, " ,");
while (tok.hasMoreTokens()) {
String host = tok.nextToken().trim();
// doing it this way means we don't have to escape an IPv6 host with []
InetAddrPort iap = new InetAddrPort(host, sslPort);
try {
SslListener ssll = new SslListener(iap);
// the keystore path and password
ssll.setKeystore(keyStore.getAbsolutePath());
ssll.setPassword(ctx.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD));
// the X.509 cert password (if not present, verifyKeyStore() returned false)
ssll.setKeyPassword(ctx.getProperty(PROP_KEY_PASSWORD, "thisWontWork"));
_server.addListener(ssll);
boundAddresses++;
} catch (Exception e) { // probably no exceptions at this point
System.err.println("Unable to bind routerconsole to " + host + " port " + sslPort + " for SSL: " + e);
}
}
} else {
System.err.println("Unable to create or access keystore for SSL: " + keyStore.getAbsolutePath());
}
}
if (boundAddresses <= 0) {
System.err.println("Unable to bind routerconsole to any address on port " + _listenPort);
System.err.println("Unable to bind routerconsole to any address on port " + _listenPort + (sslPort > 0 ? (" or SSL port " + sslPort) : ""));
return;
}
_server.setRootWebApp(ROUTERCONSOLE);
WebApplicationContext wac = _server.addWebApplication("/", _webAppsDir + ROUTERCONSOLE + ".war");
File tmpdir = new SecureDirectory(workDir, ROUTERCONSOLE + "-" + _listenPort);
File tmpdir = new SecureDirectory(workDir, ROUTERCONSOLE + "-" +
(_listenPort != null ? _listenPort : _sslListenPort));
tmpdir.mkdir();
wac.setTempDirectory(tmpdir);
baseHandler = new LocaleWebAppHandler(I2PAppContext.getGlobalContext());
@ -131,7 +240,8 @@ public class RouterConsoleRunner {
String enabled = props.getProperty(PREFIX + appName + ENABLED);
if (! "false".equals(enabled)) {
String path = new File(dir, fileNames[i]).getCanonicalPath();
tmpdir = new SecureDirectory(workDir, appName + "-" + _listenPort);
tmpdir = new SecureDirectory(workDir, appName + "-" +
(_listenPort != null ? _listenPort : _sslListenPort));
WebAppStarter.addWebApp(I2PAppContext.getGlobalContext(), _server, appName, path, tmpdir);
if (enabled == null) {
@ -201,6 +311,90 @@ public class RouterConsoleRunner {
}
}
/**
* @return success if it exists and we have a password, or it was created successfully.
* @since 0.8.3
*/
private static boolean verifyKeyStore(File ks) {
if (ks.exists()) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
boolean rv = ctx.getProperty(PROP_KEY_PASSWORD) != null;
if (!rv)
System.err.println("Console SSL error, must set " + PROP_KEY_PASSWORD + " in " + (new File(ctx.getConfigDir(), "router.config")).getAbsolutePath());
return rv;
}
File dir = ks.getParentFile();
if (!dir.exists()) {
File sdir = new SecureDirectory(dir.getAbsolutePath());
if (!sdir.mkdir())
return false;
}
return createKeyStore(ks);
}
/**
* Call out to keytool to create a new keystore with a keypair in it.
* Trying to do this programatically is a nightmare, requiring either BouncyCastle
* libs or using proprietary Sun libs, and it's a huge mess.
*
* @return success
* @since 0.8.3
*/
private static boolean createKeyStore(File ks) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
// make a random 48 character password (30 * 8 / 5)
byte[] rand = new byte[30];
ctx.random().nextBytes(rand);
String keyPassword = Base32.encode(rand);
// and one for the cname
ctx.random().nextBytes(rand);
String cname = Base32.encode(rand) + ".console.i2p.net";
String keytool = (new File(System.getProperty("java.home"), "bin/keytool")).getAbsolutePath();
String[] args = new String[] {
keytool,
"-genkey", // -genkeypair preferred in newer keytools, but this works with more
"-storetype", KeyStore.getDefaultType(),
"-keystore", ks.getAbsolutePath(),
"-storepass", DEFAULT_KEYSTORE_PASSWORD,
"-alias", "console",
"-dname", "CN=" + cname + ",OU=Console,O=I2P Anonymous Network,L=XX,ST=XX,C=XX",
"-validity", "3652", // 10 years
"-keyalg", "DSA",
"-keysize", "1024",
"-keypass", keyPassword};
boolean success = (new ShellCommand()).executeSilentAndWaitTimed(args, 30); // 30 secs
if (success) {
success = ks.exists();
if (success) {
SecureFileOutputStream.setPerms(ks);
try {
RouterContext rctx = (RouterContext) ctx;
rctx.router().setConfigSetting(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD);
rctx.router().setConfigSetting(PROP_KEY_PASSWORD, keyPassword);
rctx.router().saveConfig();
} catch (Exception e) {} // class cast exception
}
}
if (success) {
System.err.println("Created self-signed certificate for " + cname + " in keystore: " + ks.getAbsolutePath() + "\n" +
"The certificate name was generated randomly, and is not associated with your " +
"IP address, host name, router identity, or destination keys.");
} else {
System.err.println("Failed to create console SSL keystore using command line:");
StringBuilder buf = new StringBuilder(256);
for (int i = 0; i < args.length; i++) {
buf.append('"').append(args[i]).append("\" ");
}
System.err.println(buf.toString());
System.err.println("This is for the Sun/Oracle keytool, others may be incompatible.\n" +
"If you create the keystore manually, you must add " + PROP_KEYSTORE_PASSWORD + " and " + PROP_KEY_PASSWORD +
" to " + (new File(ctx.getConfigDir(), "router.config")).getAbsolutePath());
}
return success;
}
static void initialize(WebApplicationContext context) {
String password = getPassword();
if (password != null) {

View File

@ -1,8 +1,11 @@
package net.i2p.router.web;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
@ -15,10 +18,12 @@ import net.i2p.router.RouterVersion;
import net.i2p.util.EepGet;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.PartialEepGet;
import net.i2p.util.VersionComparator;
/**
* <p>Handles the request to update the router by firing off an
* {@link net.i2p.util.EepGet} call to download the latest signed update file
* <p>Handles the request to update the router by firing one or more
* {@link net.i2p.util.EepGet} calls to download the latest signed update file
* and displaying the status to anyone who asks.
* </p>
* <p>After the download completes the signed update file is verified with
@ -125,6 +130,11 @@ public class UpdateHandler {
protected boolean done;
protected EepGet _get;
protected final DecimalFormat _pct = new DecimalFormat("0.0%");
/** tells the listeners what mode we are in */
private boolean _isPartial;
/** set by the listeners on completion */
private boolean _isNewer;
private ByteArrayOutputStream _baos;
public UpdateRunner() {
_isRunning = false;
@ -141,39 +151,88 @@ public class UpdateHandler {
System.setProperty(PROP_UPDATE_IN_PROGRESS, "false");
_isRunning = false;
}
/**
* Loop through the entire list of update URLs.
* For each one, first get the version from the first 56 bytes and see if
* it is newer than what we are running now.
* If it is, get the whole thing.
*/
protected void update() {
updateStatus("<b>" + _("Updating") + "</b>");
// TODO:
// Do a PartialEepGet on the selected URL, check for version we expect,
// and loop if it isn't what we want.
// This will allow us to do a release without waiting for the last host to install the update.
// Alternative: In bytesTransferred(), Check the data in the output file after
// we've received at least 56 bytes. Need a cancel() method in EepGet ?
String updateURL = selectUpdateURL();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Selected update URL: " + updateURL);
boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
int proxyPort = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT_INT);
try {
if (shouldProxy)
// 40 retries!!
_get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, updateURL, false);
else
_get = new EepGet(_context, 1, _updateFile, updateURL, false);
_get.addStatusListener(UpdateRunner.this);
_get.fetch();
} catch (Throwable t) {
_log.error("Error updating", t);
List<String> urls = getUpdateURLs();
if (urls.isEmpty()) {
// not likely, don't bother translating
updateStatus("<b>Update source list is empty, cannot download update</b>");
_log.log(Log.CRIT, "Update source list is empty - cannot download update");
return;
}
if (shouldProxy)
_baos = new ByteArrayOutputStream(TrustedUpdate.HEADER_BYTES);
for (String updateURL : urls) {
updateStatus("<b>" + _("Updating from {0}", linkify(updateURL)) + "</b>");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Selected update URL: " + updateURL);
// Check the first 56 bytes for the version
if (shouldProxy) {
_isPartial = true;
_isNewer = false;
_baos.reset();
try {
// no retries
_get = new PartialEepGet(_context, proxyHost, proxyPort, _baos, updateURL, TrustedUpdate.HEADER_BYTES);
_get.addStatusListener(UpdateRunner.this);
_get.fetch();
} catch (Throwable t) {
_isNewer = false;
}
_isPartial = false;
if (!_isNewer)
continue;
}
// Now get the whole thing
try {
if (shouldProxy)
// 40 retries!!
_get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, updateURL, false);
else
_get = new EepGet(_context, 1, _updateFile, updateURL, false);
_get.addStatusListener(UpdateRunner.this);
_get.fetch();
} catch (Throwable t) {
_log.error("Error updating", t);
}
if (this.done)
break;
}
}
// EepGet Listeners below.
// We use the same for both the partial and the full EepGet,
// with a couple of adjustments depending on which mode.
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
_isNewer = false;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Attempt failed on " + url, cause);
// ignored
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
if (_isPartial)
return;
StringBuilder buf = new StringBuilder(64);
buf.append("<b>").append(_("Updating")).append("</b> ");
double pct = ((double)alreadyTransferred + (double)currentWrite) /
@ -186,6 +245,19 @@ public class UpdateHandler {
updateStatus(buf.toString());
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
if (_isPartial) {
// Compare version with what we have now
String newVersion = TrustedUpdate.getVersionString(new ByteArrayInputStream(_baos.toByteArray()));
boolean newer = (new VersionComparator()).compare(newVersion, RouterVersion.VERSION) > 0;
if (!newer) {
updateStatus("<b>" + _("No new version found at {0}", linkify(url)) + "</b>");
if (_log.shouldLog(Log.WARN))
_log.warn("Found old version \"" + newVersion + "\" at " + url);
}
_isNewer = newer;
return;
}
// Process the .sud/.su2 file
updateStatus("<b>" + _("Update downloaded") + "</b>");
TrustedUpdate up = new TrustedUpdate(_context);
File f = new File(_updateFile);
@ -223,15 +295,16 @@ public class UpdateHandler {
}
} else {
_log.log(Log.CRIT, err + " from " + url);
updateStatus("<b>" + err + ' ' + _("from {0}", url) + " </b>");
updateStatus("<b>" + err + ' ' + _("from {0}", linkify(url)) + " </b>");
}
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_isNewer = false;
// don't display bytesTransferred as it is meaningless
_log.log(Log.CRIT, "Update from " + url + " did not download completely (" +
_log.error("Update from " + url + " did not download completely (" +
bytesRemaining + " remaining after " + currentAttempt + " tries)");
updateStatus("<b>" + _("Transfer failed") + "</b>");
updateStatus("<b>" + _("Transfer failed from {0}", linkify(url)) + "</b>");
}
public void headerReceived(String url, int attemptNum, String key, String val) {}
public void attempting(String url) {}
@ -242,27 +315,24 @@ public class UpdateHandler {
_context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART);
}
private String selectUpdateURL() {
private List<String> getUpdateURLs() {
String URLs = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL, ConfigUpdateHandler.DEFAULT_UPDATE_URL);
StringTokenizer tok = new StringTokenizer(URLs, " ,\r\n");
List URLList = new ArrayList();
List<String> URLList = new ArrayList();
while (tok.hasMoreTokens())
URLList.add(tok.nextToken().trim());
int size = URLList.size();
//_log.log(Log.DEBUG, "Picking update source from " + size + " candidates.");
if (size <= 0) {
_log.log(Log.CRIT, "Update source list is empty - cannot download update");
return null;
}
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
_log.log(Log.DEBUG, "Picked update source " + index + ".");
return (String) URLList.get(index);
Collections.shuffle(URLList, _context.random());
return URLList;
}
protected void updateStatus(String s) {
_status = s;
}
protected static String linkify(String url) {
return "<a target=\"_blank\" href=\"" + url + "\"/>" + url + "</a>";
}
/** translate a string */
protected String _(String s) {
return Messages.getString(s, _context);

View File

@ -47,7 +47,51 @@ button span.hide{
<input type="submit" name="edit" value="<%=intl._("Add Client")%>" />
<% } %>
<input type="submit" name="action" value="<%=intl._("Save Client Configuration")%>" />
</div></div><h3><a name="webapp"></a><%=intl._("WebApp Configuration")%></h3><p>
</div></div>
<h3><a name="i2cp"></a><%=intl._("Advanced Client Interface Configuration")%></h3><p>
<b><%=intl._("External I2CP (I2P Client Protocol) Interface Configuration")%></b><br>
<input type="radio" class="optbox" name="mode" value="1" <%=clientshelper.i2cpModeChecked(1) %> >
<%=intl._("Enabled without SSL")%><br>
<input type="radio" class="optbox" name="mode" value="2" <%=clientshelper.i2cpModeChecked(2) %> >
<%=intl._("Enabled with SSL required")%><br>
<input type="radio" class="optbox" name="mode" value="0" <%=clientshelper.i2cpModeChecked(0) %> >
<%=intl._("Disabled - Clients outside this Java process may not connect")%><br>
<%=intl._("I2CP Port")%>:
<input name="port" type="text" size="5" maxlength="5" value="<jsp:getProperty name="clientshelper" property="port" />" ><br>
<%=intl._("I2CP Interface")%>:
<select name="interface">
<%
String[] ips = clientshelper.intfcAddresses();
for (int i = 0; i < ips.length; i++) {
out.print("<option value=\"");
out.print(ips[i]);
out.print('\"');
if (clientshelper.isIFSelected(ips[i]))
out.print(" selected=\"selected\"");
out.print('>');
out.print(ips[i]);
out.print("</option>\n");
}
%>
</select><br>
<b><%=intl._("Authorization")%></b><br>
<input type="checkbox" class="optbox" name="auth" value="true" <jsp:getProperty name="clientshelper" property="auth" /> >
<%=intl._("Requre username and password")%><br>
<%=intl._("Username")%>:
<input name="user" type="text" value="<jsp:getProperty name="clientshelper" property="user" />" ><br>
<%=intl._("Password")%>:
<input name="pw" type="password" value="<jsp:getProperty name="clientshelper" property="pw" />" ><br>
</p><p><b><%=intl._("The default settings will work for most people.")%></b>
<%=intl._("Any changes made here must also be configured in the external client.")%>
<%=intl._("Many clients do not support SSL or authorization.")%>
<i><%=intl._("All changes require restart to take effect.")%></i>
</p><hr><div class="formaction">
<input type="submit" name="foo" value="<%=intl._("Cancel")%>" />
<input type="submit" name="action" value="<%=intl._("Save Interface Configuration")%>" />
</div>
<h3><a name="webapp"></a><%=intl._("WebApp Configuration")%></h3><p>
<%=intl._("The Java web applications listed below are started by the webConsole client and run in the same JVM as the router. They are usually web applications accessible through the router console. They may be complete applications (e.g. i2psnark),front-ends to another client or application which must be separately enabled (e.g. susidns, i2ptunnel), or have no web interface at all (e.g. addressbook).")%>
</p><p>
<%=intl._("A web app may also be disabled by removing the .war file from the webapps directory; however the .war file and web app will reappear when you update your router to a newer version, so disabling the web app here is the preferred method.")%>

View File

@ -0,0 +1,12 @@
<%
/*
* USE CAUTION WHEN EDITING
* Trailing whitespace OR NEWLINE on the last line will cause
* IllegalStateExceptions !!!
*
* Do not tag this file for translation.
*/
response.setContentType("text/plain");
String base = net.i2p.I2PAppContext.getGlobalContext().getBaseDir().getAbsolutePath();
net.i2p.util.FileUtil.readFile("history.txt", base, response.getOutputStream());
%>

View File

@ -17,6 +17,11 @@
<url-pattern>/javadoc/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>net.i2p.router.web.jsp.viewhistory_jsp</servlet-name>
<url-pattern>/history.txt</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30

View File

@ -15,8 +15,9 @@ class I2PServerSocketFull implements I2PServerSocket {
}
/**
* Warning, unlike regular ServerSocket, may return null
*
* @return I2PSocket
* @return I2PSocket OR NULL
* @throws net.i2p.I2PException
* @throws SocketTimeoutException
*/

View File

@ -114,7 +114,7 @@ public class I2PSocketManagerFull implements I2PSocketManager {
/**
*
* @return connected I2PSocket
* @return connected I2PSocket OR NULL
* @throws net.i2p.I2PException
* @throws java.net.SocketTimeoutException
*/

View File

@ -6,10 +6,8 @@
<!--
<property name="javac.compilerargs" value="-warn:-unchecked,raw,unused,serial" />
-->
<!-- Add Apache Harmony's Pack200 library if you don't have java.util.jar.Pack200
See core/java/src/net/i2p/util/FileUtil.java for code changes required
to use this library instead of Sun's version.
Or to comment it all out if you don't have either.
<!-- Additional classpath. No longer required; we find pack200 classes at runtime.
See core/java/src/net/i2p/util/FileUtil.java for more info.
-->
<!--
<property name="javac.classpath" value="/PATH/TO/pack200.jar" />
@ -239,7 +237,7 @@
splitindex="true"
doctitle="I2P Javadocs for Release ${release.number} Build ${build.number}"
windowtitle="I2P Anonymous Network - Java Documentation - Version ${release.number}">
<group title="Core SDK (i2p.jar)" packages="net.i2p:net.i2p.*:net.i2p.client:net.i2p.client.*:freenet.support.CPUInformation:org.bouncycastle.crypto:org.bouncycastle.crypto.*:gnu.crypto.*:gnu.gettext:org.xlattice.crypto.filters:com.nettgryppa.security" />
<group title="Core SDK (i2p.jar)" packages="net.i2p:net.i2p.*:net.i2p.client:net.i2p.client.*:net.i2p.internal:net.i2p.internal.*:freenet.support.CPUInformation:org.bouncycastle.crypto:org.bouncycastle.crypto.*:gnu.crypto.*:gnu.gettext:org.xlattice.crypto.filters:com.nettgryppa.security" />
<group title="Streaming Library" packages="net.i2p.client.streaming" />
<group title="Router" packages="net.i2p.router:net.i2p.router.*:net.i2p.data.i2np:org.cybergarage.*:org.freenetproject" />
<group title="Router Console" packages="net.i2p.router.web" />

View File

@ -168,6 +168,7 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
//System.out.println("Refilling " + (++refillCount) + " after " + diff + " for the PRNG took " + refillTime);
}
/*****
public static void main(String args[]) {
try {
AsyncFortunaStandalone rand = new AsyncFortunaStandalone(null); // Will cause NPEs above; fix this if you want to test! Sorry...
@ -195,4 +196,5 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
} catch (Exception e) { e.printStackTrace(); }
try { Thread.sleep(5*60*1000); } catch (InterruptedException ie) {}
}
*****/
}

View File

@ -351,6 +351,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
}
}
/*****
public static void main(String args[]) {
byte in[] = new byte[16];
byte out[] = new byte[16];
@ -379,7 +380,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
long after = System.currentTimeMillis();
System.out.println("encrypting 4MB took " + (after-beforeAll));
} catch (Exception e) { e.printStackTrace(); }
/*
****/ /*
FortunaStandalone f = new FortunaStandalone();
java.util.HashMap props = new java.util.HashMap();
byte initSeed[] = new byte[1234];
@ -394,5 +395,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
long time = System.currentTimeMillis() - before;
System.out.println("512MB took " + time + ", or " + (8*64d)/((double)time/1000d) +"MBps");
*/
/*****
}
*****/
}

View File

@ -3,6 +3,7 @@ package net.i2p;
import java.io.File;
import java.util.HashSet;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import net.i2p.client.naming.NamingService;
@ -21,7 +22,9 @@ import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.SHA256Generator;
import net.i2p.crypto.SessionKeyManager;
import net.i2p.crypto.TransientSessionKeyManager;
import net.i2p.data.Base64;
import net.i2p.data.RoutingKeyGenerator;
import net.i2p.internal.InternalClientManager;
import net.i2p.stat.StatManager;
import net.i2p.util.Clock;
import net.i2p.util.ConcurrentHashSet;
@ -363,10 +366,12 @@ public class I2PAppContext {
if (_tmpDir == null) {
String d = getProperty("i2p.dir.temp", System.getProperty("java.io.tmpdir"));
// our random() probably isn't warmed up yet
String f = "i2p-" + Math.abs((new java.util.Random()).nextInt()) + ".tmp";
byte[] rand = new byte[6];
(new Random()).nextBytes(rand);
String f = "i2p-" + Base64.encode(rand) + ".tmp";
_tmpDir = new SecureDirectory(d, f);
if (_tmpDir.exists()) {
// good or bad ?
// good or bad ? loop and try again?
} else if (_tmpDir.mkdir()) {
_tmpDir.deleteOnExit();
} else {
@ -843,4 +848,13 @@ public class I2PAppContext {
public boolean isRouterContext() {
return false;
}
/**
* Use this to connect to the router in the same JVM.
* @return always null in I2PAppContext, the client manager if in RouterContext
* @since 0.8.3
*/
public InternalClientManager internalClientManager() {
return null;
}
}

View File

@ -9,6 +9,7 @@ import java.util.concurrent.LinkedBlockingQueue;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.I2CPMessageImpl;
import net.i2p.data.i2cp.I2CPMessageException;
import net.i2p.internal.PoisonI2CPMessage;
import net.i2p.util.I2PAppThread;
/**
@ -50,7 +51,7 @@ class ClientWriterRunner implements Runnable {
public void stopWriting() {
_messagesToWrite.clear();
try {
_messagesToWrite.put(new PoisonMessage());
_messagesToWrite.put(new PoisonI2CPMessage());
} catch (InterruptedException ie) {}
}
@ -62,7 +63,7 @@ class ClientWriterRunner implements Runnable {
} catch (InterruptedException ie) {
continue;
}
if (msg.getType() == PoisonMessage.MESSAGE_TYPE)
if (msg.getType() == PoisonI2CPMessage.MESSAGE_TYPE)
break;
// only thread, we don't need synchronized
try {
@ -80,18 +81,4 @@ class ClientWriterRunner implements Runnable {
}
_messagesToWrite.clear();
}
/**
* End-of-stream msg used to stop the concurrent queue
* See http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html
*
*/
private static class PoisonMessage extends I2CPMessageImpl {
public static final int MESSAGE_TYPE = 999999;
public int getType() {
return MESSAGE_TYPE;
}
public void doReadMessage(InputStream buf, int size) throws I2CPMessageException, IOException {}
public byte[] doWriteMessage() throws I2CPMessageException, IOException { return null; }
}
}

View File

@ -10,6 +10,9 @@ import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.DestReplyMessage;
import net.i2p.util.Log;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
/**
* Handle I2CP dest replies from the router
*/
@ -22,6 +25,12 @@ class DestReplyMessageHandler extends HandlerImpl {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handle message " + message);
DestReplyMessage msg = (DestReplyMessage) message;
((I2PSimpleSession)session).destReceived(msg.getDestination());
Destination d = msg.getDestination();
if (d != null)
session.destReceived(d);
Hash h = msg.getHash();
if (h != null)
session.destLookupFailed(h);
// else let it time out
}
}

View File

@ -0,0 +1,183 @@
package net.i2p.client;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.security.KeyStore;
import java.security.GeneralSecurityException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
/**
* Loads trusted ASCII certs from ~/.i2p/certificates/ and $CWD/certificates/.
* Keeps a single static SSLContext for the whole JVM.
*
* @author zzz
* @since 0.8.3
*/
class I2CPSSLSocketFactory {
private static final Object _initLock = new Object();
private static SSLSocketFactory _factory;
private static Log _log;
private static final String CERT_DIR = "certificates";
/**
* Initializes the static SSL Context if required, then returns a socket
* to the host.
*
* @param ctx just for logging
* @throws IOException on init error or usual socket errors
*/
public static Socket createSocket(I2PAppContext ctx, String host, int port) throws IOException {
synchronized(_initLock) {
if (_factory == null) {
_log = ctx.logManager().getLog(I2CPSSLSocketFactory.class);
initSSLContext(ctx);
if (_factory == null)
throw new IOException("Unable to create SSL Context for I2CP Client");
_log.info("I2CP Client-side SSL Context initialized");
}
}
return _factory.createSocket(host, port);
}
/**
* Loads certs from
* the ~/.i2p/certificates/ and $CWD/certificates/ directories.
*/
private static void initSSLContext(I2PAppContext context) {
KeyStore ks;
try {
ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, "".toCharArray());
} catch (GeneralSecurityException gse) {
_log.error("Key Store init error", gse);
return;
} catch (IOException ioe) {
_log.error("Key Store init error", ioe);
return;
}
File dir = new File(context.getConfigDir(), CERT_DIR);
int adds = addCerts(dir, ks);
int totalAdds = adds;
if (adds > 0 && _log.shouldLog(Log.INFO))
_log.info("Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
File dir2 = new File(System.getProperty("user.dir"), CERT_DIR);
if (!dir.getAbsolutePath().equals(dir2.getAbsolutePath())) {
adds = addCerts(dir2, ks);
totalAdds += adds;
if (adds > 0 && _log.shouldLog(Log.INFO))
_log.info("Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
}
if (totalAdds > 0) {
if (_log.shouldLog(Log.INFO))
_log.info("Loaded total of " + totalAdds + " new trusted certificates");
} else {
_log.error("No trusted certificates loaded (looked in " +
dir.getAbsolutePath() + (dir.getAbsolutePath().equals(dir2.getAbsolutePath()) ? "" : (" and " + dir2.getAbsolutePath())) +
", I2CP SSL client connections will fail. " +
"Copy the file certificates/i2cp.local.crt from the router to the directory.");
// don't continue, since we didn't load the system keystore, we have nothing.
return;
}
try {
SSLContext sslc = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
sslc.init(null, tmf.getTrustManagers(), context.random());
_factory = sslc.getSocketFactory();
} catch (GeneralSecurityException gse) {
_log.error("SSL context init error", gse);
}
}
/**
* Load all X509 Certs from a directory and add them to the
* trusted set of certificates in the key store
*
* @return number successfully added
*/
private static int addCerts(File dir, KeyStore ks) {
if (_log.shouldLog(Log.INFO))
_log.info("Looking for X509 Certificates in " + dir.getAbsolutePath());
int added = 0;
if (dir.exists() && dir.isDirectory()) {
File[] files = dir.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
File f = files[i];
if (!f.isFile())
continue;
// use file name as alias
String alias = f.getName().toLowerCase();
boolean success = addCert(f, alias, ks);
if (success)
added++;
}
}
}
return added;
}
/**
* Load an X509 Cert from a file and add it to the
* trusted set of certificates in the key store
*
* @return success
*/
private static boolean addCert(File file, String alias, KeyStore ks) {
InputStream fis = null;
try {
fis = new FileInputStream(file);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
if (_log.shouldLog(Log.INFO)) {
_log.info("Read X509 Certificate from " + file.getAbsolutePath() +
" Issuer: " + cert.getIssuerX500Principal() +
"; Valid From: " + cert.getNotBefore() +
" To: " + cert.getNotAfter());
}
try {
cert.checkValidity();
} catch (CertificateExpiredException cee) {
_log.error("Rejecting expired X509 Certificate: " + file.getAbsolutePath(), cee);
return false;
} catch (CertificateNotYetValidException cnyve) {
_log.error("Rejecting X509 Certificate not yet valid: " + file.getAbsolutePath(), cnyve);
return false;
}
ks.setCertificateEntry(alias, cert);
if (_log.shouldLog(Log.INFO))
_log.info("Now trusting X509 Certificate, Issuer: " + cert.getIssuerX500Principal());
} catch (GeneralSecurityException gse) {
_log.error("Error reading X509 Certificate: " + file.getAbsolutePath(), gse);
return false;
} catch (IOException ioe) {
_log.error("Error reading X509 Certificate: " + file.getAbsolutePath(), ioe);
return false;
} finally {
try { if (fis != null) fis.close(); } catch (IOException foo) {}
}
return true;
}
}

View File

@ -10,6 +10,8 @@ package net.i2p.client;
*/
import net.i2p.I2PAppContext;
import net.i2p.data.i2cp.BandwidthLimitsMessage;
import net.i2p.data.i2cp.DestReplyMessage;
import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
@ -36,6 +38,8 @@ class I2PClientMessageHandlerMap {
highest = Math.max(highest, MessagePayloadMessage.MESSAGE_TYPE);
highest = Math.max(highest, MessageStatusMessage.MESSAGE_TYPE);
highest = Math.max(highest, SetDateMessage.MESSAGE_TYPE);
highest = Math.max(highest, DestReplyMessage.MESSAGE_TYPE);
highest = Math.max(highest, BandwidthLimitsMessage.MESSAGE_TYPE);
_handlers = new I2CPMessageHandler[highest+1];
_handlers[DisconnectMessage.MESSAGE_TYPE] = new DisconnectMessageHandler(context);
@ -44,6 +48,8 @@ class I2PClientMessageHandlerMap {
_handlers[MessagePayloadMessage.MESSAGE_TYPE] = new MessagePayloadMessageHandler(context);
_handlers[MessageStatusMessage.MESSAGE_TYPE] = new MessageStatusMessageHandler(context);
_handlers[SetDateMessage.MESSAGE_TYPE] = new SetDateMessageHandler(context);
_handlers[DestReplyMessage.MESSAGE_TYPE] = new DestReplyMessageHandler(context);
_handlers[BandwidthLimitsMessage.MESSAGE_TYPE] = new BWLimitsMessageHandler(context);
}
public I2CPMessageHandler getHandler(int messageTypeId) {

View File

@ -138,13 +138,21 @@ public interface I2PSession {
public SigningPrivateKey getPrivateKey();
/**
* Lookup up a Hash
*
* Lookup a Destination by Hash.
* Blocking. Waits a max of 10 seconds by default.
*/
public Destination lookupDest(Hash h) throws I2PSessionException;
/**
* Get the current bandwidth limits
* Blocking.
* @param maxWait ms
* @since 0.8.3
* @return null on failure
*/
public Destination lookupDest(Hash h, long maxWait) throws I2PSessionException;
/**
* Get the current bandwidth limits. Blocking.
*/
public int[] bandwidthLimits() throws I2PSessionException;

View File

@ -15,7 +15,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@ -23,6 +22,8 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
@ -33,14 +34,18 @@ import net.i2p.data.PrivateKey;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.i2cp.DestLookupMessage;
import net.i2p.data.i2cp.GetBandwidthLimitsMessage;
import net.i2p.data.i2cp.GetDateMessage;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.I2CPMessageException;
import net.i2p.data.i2cp.I2CPMessageReader;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.SessionId;
import net.i2p.util.I2PThread;
import net.i2p.util.InternalSocket;
import net.i2p.internal.I2CPMessageQueue;
import net.i2p.internal.InternalClientManager;
import net.i2p.internal.QueuedI2CPMessageReader;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer;
@ -66,9 +71,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
/** currently granted lease set, or null */
private LeaseSet _leaseSet;
/** hostname of router */
/** hostname of router - will be null if in RouterContext */
protected String _hostname;
/** port num to router */
/** port num to router - will be 0 if in RouterContext */
protected int _portNum;
/** socket for comm */
protected Socket _socket;
@ -79,6 +84,13 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
/** where we pipe our messages */
protected /* FIXME final FIXME */OutputStream _out;
/**
* Used for internal connections to the router.
* If this is set, _socket, _writer, and _out will be null.
* @since 0.8.3
*/
protected I2CPMessageQueue _queue;
/** who we send events to */
protected I2PSessionListener _sessionListener;
@ -86,6 +98,11 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
protected I2CPMessageProducer _producer;
/** map of Long --> MessagePayloadMessage */
protected Map<Long, MessagePayloadMessage> _availableMessages;
/** hashes of lookups we are waiting for */
protected final LinkedBlockingQueue<LookupWaiter> _pendingLookups = new LinkedBlockingQueue();
protected final Object _bwReceivedLock = new Object();
protected int[] _bwLimits;
protected I2PClientMessageHandlerMap _handlerMap;
@ -122,6 +139,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
private long _lastActivity;
private boolean _isReduced;
/** SSL interface (only) @since 0.8.3 */
protected static final String PROP_ENABLE_SSL = "i2cp.SSL";
void dateUpdated() {
_dateReceived = true;
synchronized (_dateReceivedLock) {
@ -172,19 +192,24 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
protected void loadConfig(Properties options) {
_options = new Properties();
_options.putAll(filter(options));
_hostname = _options.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
String portNum = _options.getProperty(I2PClient.PROP_TCP_PORT, LISTEN_PORT + "");
try {
_portNum = Integer.parseInt(portNum);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "Invalid port number specified, defaulting to "
+ LISTEN_PORT, nfe);
_portNum = LISTEN_PORT;
if (_context.isRouterContext()) {
// just for logging
_hostname = "[internal connection]";
} else {
_hostname = _options.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
String portNum = _options.getProperty(I2PClient.PROP_TCP_PORT, LISTEN_PORT + "");
try {
_portNum = Integer.parseInt(portNum);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "Invalid port number specified, defaulting to "
+ LISTEN_PORT, nfe);
_portNum = LISTEN_PORT;
}
}
// auto-add auth if required, not set in the options, and we are in the same JVM
if (_context.isRouterContext() &&
// auto-add auth if required, not set in the options, and we are not in the same JVM
if ((!_context.isRouterContext()) &&
Boolean.valueOf(_context.getProperty("i2cp.auth")).booleanValue() &&
((!options.containsKey("i2cp.username")) || (!options.containsKey("i2cp.password")))) {
String configUser = _context.getProperty("i2cp.username");
@ -272,10 +297,6 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
setOpening(true);
_closed = false;
_availabilityNotifier.stopNotifying();
I2PThread notifier = new I2PThread(_availabilityNotifier);
notifier.setName("Notifier " + _myDestination.calculateHash().toBase64().substring(0,4));
notifier.setDaemon(true);
notifier.start();
if ( (_options != null) &&
(I2PClient.PROP_RELIABILITY_GUARANTEED.equals(_options.getProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT))) ) {
@ -285,17 +306,32 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
long startConnect = _context.clock().now();
try {
// If we are in the router JVM, connect using the interal pseudo-socket
_socket = InternalSocket.getSocket(_hostname, _portNum);
// _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
_out = _socket.getOutputStream();
synchronized (_out) {
_out.write(I2PClient.PROTOCOL_BYTE);
_out.flush();
// If we are in the router JVM, connect using the interal queue
if (_context.isRouterContext()) {
// _socket, _out, and _writer remain null
InternalClientManager mgr = _context.internalClientManager();
if (mgr == null)
throw new I2PSessionException("Router is not ready for connections");
// the following may throw an I2PSessionException
_queue = mgr.connect();
_reader = new QueuedI2CPMessageReader(_queue, this);
} else {
if (Boolean.valueOf(_options.getProperty(PROP_ENABLE_SSL)).booleanValue())
_socket = I2CPSSLSocketFactory.createSocket(_context, _hostname, _portNum);
else
_socket = new Socket(_hostname, _portNum);
// _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
_out = _socket.getOutputStream();
synchronized (_out) {
_out.write(I2PClient.PROTOCOL_BYTE);
_out.flush();
}
_writer = new ClientWriterRunner(_out, this);
InputStream in = _socket.getInputStream();
_reader = new I2CPMessageReader(in, this);
}
_writer = new ClientWriterRunner(_out, this);
InputStream in = _socket.getInputStream();
_reader = new I2CPMessageReader(in, this);
Thread notifier = new I2PAppThread(_availabilityNotifier, "ClientNotifier " + getPrefix(), true);
notifier.start();
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "before startReading");
_reader.startReading();
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Before getDate");
@ -435,6 +471,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
}
}
/**
* This notifies the client of payload messages.
* Needs work.
*/
protected class AvailabilityNotifier implements Runnable {
private List _pendingIds;
private List _pendingSizes;
@ -497,8 +537,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
}
/**
* Recieve notification of some I2CP message and handle it if possible
*
* The I2CPMessageEventListener callback.
* Recieve notification of some I2CP message and handle it if possible.
* @param reader unused
*/
public void messageReceived(I2CPMessageReader reader, I2CPMessage message) {
I2CPMessageHandler handler = _handlerMap.getHandler(message.getType());
@ -515,7 +556,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
}
/**
* Recieve notifiation of an error reading the I2CP stream
* The I2CPMessageEventListener callback.
* Recieve notifiation of an error reading the I2CP stream.
* @param reader unused
* @param error non-null
*/
public void readError(I2CPMessageReader reader, Exception error) {
@ -567,9 +610,14 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* @throws I2PSessionException if the message is malformed or there is an error writing it out
*/
void sendMessage(I2CPMessage message) throws I2PSessionException {
if (isClosed() || _writer == null)
if (isClosed())
throw new I2PSessionException("Already closed");
_writer.addMessage(message);
else if (_queue != null)
_queue.offer(message); // internal
else if (_writer == null)
throw new I2PSessionException("Already closed");
else
_writer.addMessage(message);
}
/**
@ -581,8 +629,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
// Only log as WARN if the router went away
int level;
String msgpfx;
if ((error instanceof EOFException) ||
(error.getMessage() != null && error.getMessage().startsWith("Pipe closed"))) {
if (error instanceof EOFException) {
level = Log.WARN;
msgpfx = "Router closed connection: ";
} else {
@ -631,7 +678,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_log.warn("Error destroying the session", ipe);
}
}
_availabilityNotifier.stopNotifying();
// SimpleSession does not initialize
if (_availabilityNotifier != null)
_availabilityNotifier.stopNotifying();
_closed = true;
_closing = false;
closeSocket();
@ -649,6 +698,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_reader.stopReading();
_reader = null;
}
if (_queue != null) {
// internal
_queue.close();
}
if (_writer != null) {
_writer.stopWriting();
_writer = null;
@ -666,7 +719,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
}
/**
* Recieve notification that the I2CP connection was disconnected
* The I2CPMessageEventListener callback.
* Recieve notification that the I2CP connection was disconnected.
* @param reader unused
*/
public void disconnected(I2CPMessageReader reader) {
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Disconnected", new Exception("Disconnected"));
@ -733,21 +788,113 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
buf.append(s);
else
buf.append(getClass().getSimpleName());
buf.append(" #");
if (_sessionId != null)
buf.append(_sessionId.getSessionId());
else
buf.append("n/a");
buf.append(" #").append(_sessionId.getSessionId());
buf.append("]: ");
return buf.toString();
}
public Destination lookupDest(Hash h) throws I2PSessionException {
return null;
/** called by the message handler */
void destReceived(Destination d) {
Hash h = d.calculateHash();
for (LookupWaiter w : _pendingLookups) {
if (w.hash.equals(h)) {
w.destination = d;
synchronized (w) {
w.notifyAll();
}
}
}
}
/** called by the message handler */
void destLookupFailed(Hash h) {
for (LookupWaiter w : _pendingLookups) {
if (w.hash.equals(h)) {
synchronized (w) {
w.notifyAll();
}
}
}
}
/** called by the message handler */
void bwReceived(int[] i) {
_bwLimits = i;
synchronized (_bwReceivedLock) {
_bwReceivedLock.notifyAll();
}
}
/**
* Simple object to wait for lookup replies
* @since 0.8.3
*/
private static class LookupWaiter {
/** the request */
public final Hash hash;
/** the reply */
public Destination destination;
public LookupWaiter(Hash h) {
this.hash = h;
}
}
/**
* Blocking. Waits a max of 10 seconds by default.
* See lookupDest with maxWait parameter to change.
* Implemented in 0.8.3 in I2PSessionImpl;
* previously was available only in I2PSimpleSession.
* Multiple outstanding lookups are now allowed.
* @return null on failure
*/
public Destination lookupDest(Hash h) throws I2PSessionException {
return lookupDest(h, 10*1000);
}
/**
* Blocking.
* @param maxWait ms
* @since 0.8.3
* @return null on failure
*/
public Destination lookupDest(Hash h, long maxWait) throws I2PSessionException {
if (_closed)
return null;
LookupWaiter waiter = new LookupWaiter(h);
_pendingLookups.offer(waiter);
try {
sendMessage(new DestLookupMessage(h));
try {
synchronized (waiter) {
waiter.wait(maxWait);
}
} catch (InterruptedException ie) {}
} finally {
_pendingLookups.remove(waiter);
}
return waiter.destination;
}
/**
* Blocking. Waits a max of 5 seconds.
* But shouldn't take long.
* Implemented in 0.8.3 in I2PSessionImpl;
* previously was available only in I2PSimpleSession.
* Multiple outstanding lookups are now allowed.
* @return null on failure
*/
public int[] bandwidthLimits() throws I2PSessionException {
return null;
if (_closed)
return null;
sendMessage(new GetBandwidthLimitsMessage());
try {
synchronized (_bwReceivedLock) {
_bwReceivedLock.wait(5*1000);
}
} catch (InterruptedException ie) {}
return _bwLimits;
}
protected void updateActivity() {

View File

@ -19,8 +19,10 @@ import net.i2p.data.i2cp.DestLookupMessage;
import net.i2p.data.i2cp.DestReplyMessage;
import net.i2p.data.i2cp.GetBandwidthLimitsMessage;
import net.i2p.data.i2cp.I2CPMessageReader;
import net.i2p.util.I2PThread;
import net.i2p.util.InternalSocket;
import net.i2p.internal.I2CPMessageQueue;
import net.i2p.internal.InternalClientManager;
import net.i2p.internal.QueuedI2CPMessageReader;
import net.i2p.util.I2PAppThread;
/**
* Create a new session for doing naming and bandwidth queries only. Do not create a Destination.
@ -31,12 +33,6 @@ import net.i2p.util.InternalSocket;
* @author zzz
*/
class I2PSimpleSession extends I2PSessionImpl2 {
private boolean _destReceived;
private /* FIXME final FIXME */ Object _destReceivedLock;
private Destination _destination;
private boolean _bwReceived;
private /* FIXME final FIXME */ Object _bwReceivedLock;
private int[] _bwLimits;
/**
* Create a new session for doing naming and bandwidth queries only. Do not create a destination.
@ -44,12 +40,12 @@ class I2PSimpleSession extends I2PSessionImpl2 {
* @throws I2PSessionException if there is a problem
*/
public I2PSimpleSession(I2PAppContext context, Properties options) throws I2PSessionException {
// Warning, does not call super()
_context = context;
_log = context.logManager().getLog(I2PSimpleSession.class);
_handlerMap = new SimpleMessageHandlerMap(context);
_closed = true;
_closing = false;
_availabilityNotifier = new AvailabilityNotifier();
if (options == null)
options = System.getProperties();
loadConfig(options);
@ -65,23 +61,32 @@ class I2PSimpleSession extends I2PSessionImpl2 {
@Override
public void connect() throws I2PSessionException {
_closed = false;
_availabilityNotifier.stopNotifying();
I2PThread notifier = new I2PThread(_availabilityNotifier);
notifier.setName("Simple Notifier");
notifier.setDaemon(true);
notifier.start();
try {
// If we are in the router JVM, connect using the interal pseudo-socket
_socket = InternalSocket.getSocket(_hostname, _portNum);
_out = _socket.getOutputStream();
synchronized (_out) {
_out.write(I2PClient.PROTOCOL_BYTE);
_out.flush();
// If we are in the router JVM, connect using the interal queue
if (_context.isRouterContext()) {
// _socket, _out, and _writer remain null
InternalClientManager mgr = _context.internalClientManager();
if (mgr == null)
throw new I2PSessionException("Router is not ready for connections");
// the following may throw an I2PSessionException
_queue = mgr.connect();
_reader = new QueuedI2CPMessageReader(_queue, this);
} else {
if (Boolean.valueOf(getOptions().getProperty(PROP_ENABLE_SSL)).booleanValue())
_socket = I2CPSSLSocketFactory.createSocket(_context, _hostname, _portNum);
else
_socket = new Socket(_hostname, _portNum);
_out = _socket.getOutputStream();
synchronized (_out) {
_out.write(I2PClient.PROTOCOL_BYTE);
_out.flush();
}
_writer = new ClientWriterRunner(_out, this);
InputStream in = _socket.getInputStream();
_reader = new I2CPMessageReader(in, this);
}
_writer = new ClientWriterRunner(_out, this);
InputStream in = _socket.getInputStream();
_reader = new I2CPMessageReader(in, this);
// we do not receive payload messages, so we do not need an AvailabilityNotifier
_reader.startReading();
} catch (UnknownHostException uhe) {
@ -93,57 +98,6 @@ class I2PSimpleSession extends I2PSessionImpl2 {
}
}
/** called by the message handler */
void destReceived(Destination d) {
_destReceived = true;
_destination = d;
synchronized (_destReceivedLock) {
_destReceivedLock.notifyAll();
}
}
void bwReceived(int[] i) {
_bwReceived = true;
_bwLimits = i;
synchronized (_bwReceivedLock) {
_bwReceivedLock.notifyAll();
}
}
@Override
public Destination lookupDest(Hash h) throws I2PSessionException {
if (_closed)
return null;
_destReceivedLock = new Object();
sendMessage(new DestLookupMessage(h));
for (int i = 0; i < 10 && !_destReceived; i++) {
try {
synchronized (_destReceivedLock) {
_destReceivedLock.wait(1000);
}
} catch (InterruptedException ie) {}
}
_destReceived = false;
return _destination;
}
@Override
public int[] bandwidthLimits() throws I2PSessionException {
if (_closed)
return null;
_bwReceivedLock = new Object();
sendMessage(new GetBandwidthLimitsMessage());
for (int i = 0; i < 5 && !_bwReceived; i++) {
try {
synchronized (_bwReceivedLock) {
_bwReceivedLock.wait(1000);
}
} catch (InterruptedException ie) {}
}
_bwReceived = false;
return _bwLimits;
}
/**
* Only map message handlers that we will use
*/

View File

@ -22,6 +22,13 @@ import net.i2p.data.Hash;
*
* All calls are blocking and return null on failure.
* Timeout is set to 10 seconds in I2PSimpleSession.
*
* As of 0.8.3, standard I2PSessions support lookups,
* including multiple lookups in parallel, and overriding
* the default timeout.
* Using an existing I2PSession is much more efficient and
* flexible than using this class.
*
*/
class LookupDest {

View File

@ -22,13 +22,14 @@ import net.i2p.util.RandomSource;
* See CryptixAESEngine for the real thing.
*/
public class AESEngine {
private Log _log;
private I2PAppContext _context;
protected final Log _log;
protected final I2PAppContext _context;
public AESEngine(I2PAppContext ctx) {
_context = ctx;
_log = _context.logManager().getLog(AESEngine.class);
if (getClass() == AESEngine.class)
_log.warn("Warning: AES is disabled");
_log = _context.logManager().getLog(getClass());
if (getClass().equals(AESEngine.class))
_log.logAlways(Log.WARN, "AES is disabled");
}
/** Encrypt the payload with the session key
@ -44,7 +45,10 @@ public class AESEngine {
encrypt(payload, payloadIndex, out, outIndex, sessionKey, iv, 0, length);
}
/** Encrypt the payload with the session key
/**
* Encrypt the payload with the session key.
* This just copies payload to out, see extension for the real thing.
*
* @param payload data to be encrypted
* @param payloadIndex index into the payload to start encrypting
* @param out where to store the result
@ -55,7 +59,7 @@ public class AESEngine {
*/
public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int ivOffset, int length) {
System.arraycopy(payload, payloadIndex, out, outIndex, length);
_log.warn("Warning: AES is disabled");
_log.logAlways(Log.WARN, "AES is disabled");
}
public byte[] safeEncrypt(byte payload[], SessionKey sessionKey, byte iv[], int paddedSize) {
@ -118,7 +122,6 @@ public class AESEngine {
return data;
}
/** Decrypt the data with the session key
* @param payload data to be decrypted
* @param payloadIndex index into the payload to start decrypting
@ -132,7 +135,10 @@ public class AESEngine {
decrypt(payload, payloadIndex, out, outIndex, sessionKey, iv, 0, length);
}
/** Decrypt the data with the session key
/**
* Decrypt the data with the session key.
* This just copies payload to out, see extension for the real thing.
*
* @param payload data to be decrypted
* @param payloadIndex index into the payload to start decrypting
* @param out where to store the cleartext
@ -143,18 +149,20 @@ public class AESEngine {
*/
public void decrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int ivOffset, int length) {
System.arraycopy(payload, payloadIndex, out, outIndex, length);
_log.warn("Warning: AES is disabled");
_log.logAlways(Log.WARN, "AES is disabled");
}
/**
* Just copies payload to out
* This just copies payload to out, see extension for the real thing.
* @param sessionKey unused
*/
public void encryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte out[], int outIndex) {
System.arraycopy(payload, inIndex, out, outIndex, out.length - outIndex);
}
/** decrypt the data with the session key provided
/**
* This just copies payload to rv, see extension for the real thing.
*
* @param payload encrypted data
* @param sessionKey private session key
*/

View File

@ -27,18 +27,16 @@ import net.i2p.util.Log;
* @author jrandom, thecrypto
*/
public class CryptixAESEngine extends AESEngine {
private Log _log;
private final static CryptixRijndael_Algorithm _algo = new CryptixRijndael_Algorithm();
private final static boolean USE_FAKE_CRYPTO = false;
private final static byte FAKE_KEY = 0x2A;
private CryptixAESKeyCache _cache;
// keys are now cached in the SessionKey objects
//private CryptixAESKeyCache _cache;
private static final ByteCache _prevCache = ByteCache.getInstance(16, 16);
public CryptixAESEngine(I2PAppContext context) {
super(context);
_log = context.logManager().getLog(CryptixAESEngine.class);
_cache = new CryptixAESKeyCache();
//_cache = new CryptixAESKeyCache();
}
/** @param length must be a multiple of 16 */

View File

@ -8,6 +8,8 @@ import java.util.concurrent.LinkedBlockingQueue;
* data referenced in it is needed (which often is only one or two lines
* of code)
*
* Unused as a class, as the keys are cached in the SessionKey objects,
* but the static methods are used in FortunaStandalone.
*/
public final class CryptixAESKeyCache {
private final LinkedBlockingQueue<KeyCacheEntry> _availableKeys;
@ -20,6 +22,9 @@ public final class CryptixAESKeyCache {
private static final int MAX_KEYS = 64;
/*
* @deprecated unused, keys are now cached in the SessionKey objects
*/
public CryptixAESKeyCache() {
_availableKeys = new LinkedBlockingQueue(MAX_KEYS);
}
@ -27,6 +32,7 @@ public final class CryptixAESKeyCache {
/**
* Get the next available structure, either from the cache or a brand new one
*
* @deprecated unused, keys are now cached in the SessionKey objects
*/
public final KeyCacheEntry acquireKey() {
KeyCacheEntry rv = _availableKeys.poll();
@ -38,6 +44,7 @@ public final class CryptixAESKeyCache {
/**
* Put this structure back onto the available cache for reuse
*
* @deprecated unused, keys are now cached in the SessionKey objects
*/
public final void releaseKey(KeyCacheEntry key) {
_availableKeys.offer(key);

View File

@ -13,8 +13,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
@ -48,14 +47,15 @@ import net.i2p.util.RandomSource;
* @author jrandom
*/
public class DHSessionKeyBuilder {
private static I2PAppContext _context = I2PAppContext.getGlobalContext();
private final static Log _log = new Log(DHSessionKeyBuilder.class);
private static int MIN_NUM_BUILDERS = -1;
private static int MAX_NUM_BUILDERS = -1;
private static int CALC_DELAY = -1;
/* FIXME this should be final if you syncronize FIXME */
private static volatile List _builders = new ArrayList(50);
private static Thread _precalcThread = null;
private static final I2PAppContext _context = I2PAppContext.getGlobalContext();
private static final Log _log;
private static final int MIN_NUM_BUILDERS;
private static final int MAX_NUM_BUILDERS;
private static final int CALC_DELAY;
private static final LinkedBlockingQueue<DHSessionKeyBuilder> _builders;
private static final Thread _precalcThread;
// the data of importance
private BigInteger _myPrivateValue;
private BigInteger _myPublicValue;
private BigInteger _peerValue;
@ -65,17 +65,31 @@ public class DHSessionKeyBuilder {
public final static String PROP_DH_PRECALC_MIN = "crypto.dh.precalc.min";
public final static String PROP_DH_PRECALC_MAX = "crypto.dh.precalc.max";
public final static String PROP_DH_PRECALC_DELAY = "crypto.dh.precalc.delay";
public final static int DEFAULT_DH_PRECALC_MIN = 5;
public final static int DEFAULT_DH_PRECALC_MAX = 50;
public final static int DEFAULT_DH_PRECALC_DELAY = 10000;
public final static int DEFAULT_DH_PRECALC_MIN = 15;
public final static int DEFAULT_DH_PRECALC_MAX = 40;
public final static int DEFAULT_DH_PRECALC_DELAY = 200;
/** check every 30 seconds whether we have less than the minimum */
private static long _checkDelay = 30 * 1000;
static {
I2PAppContext ctx = _context;
ctx.statManager().createRateStat("crypto.dhGeneratePublicTime", "How long it takes to create x and X", "Encryption", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
ctx.statManager().createRateStat("crypto.dhCalculateSessionTime", "How long it takes to create the session key", "Encryption", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
MIN_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MIN, DEFAULT_DH_PRECALC_MIN);
MAX_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MAX, DEFAULT_DH_PRECALC_MAX);
_log = ctx.logManager().getLog(DHSessionKeyBuilder.class);
ctx.statManager().createRateStat("crypto.dhGeneratePublicTime", "How long it takes to create x and X", "Encryption", new long[] { 60*60*1000 });
ctx.statManager().createRateStat("crypto.dhCalculateSessionTime", "How long it takes to create the session key", "Encryption", new long[] { 60*60*1000 });
ctx.statManager().createRateStat("crypto.DHUsed", "Need a DH from the queue", "Encryption", new long[] { 60*60*1000 });
ctx.statManager().createRateStat("crypto.DHEmpty", "DH queue empty", "Encryption", new long[] { 60*60*1000 });
// add to the defaults for every 128MB of RAM, up to 512MB
long maxMemory = Runtime.getRuntime().maxMemory();
int factor = Math.min(4, (int) (1 + (maxMemory / (128*1024*1024l))));
int defaultMin = DEFAULT_DH_PRECALC_MIN * factor;
int defaultMax = DEFAULT_DH_PRECALC_MAX * factor;
MIN_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MIN, defaultMin);
MAX_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MAX, defaultMax);
CALC_DELAY = ctx.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY);
_builders = new LinkedBlockingQueue(MAX_NUM_BUILDERS);
if (_log.shouldLog(Log.DEBUG))
_log.debug("DH Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: "
@ -90,40 +104,33 @@ public class DHSessionKeyBuilder {
/**
* Construct a new DH key builder
*
* or pulls a prebuilt one from the queue.
*/
public DHSessionKeyBuilder() {
this(false);
DHSessionKeyBuilder builder = null;
synchronized (_builders) {
if (!_builders.isEmpty()) {
builder = (DHSessionKeyBuilder) _builders.remove(0);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Removing a builder. # left = " + _builders.size());
} else {
if (_log.shouldLog(Log.WARN)) _log.warn("NO MORE BUILDERS! creating one now");
}
}
_context.statManager().addRateData("crypto.DHUsed", 1, 0);
DHSessionKeyBuilder builder = _builders.poll();
if (builder != null) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Removing a builder. # left = " + _builders.size());
_myPrivateValue = builder._myPrivateValue;
_myPublicValue = builder._myPublicValue;
_peerValue = builder._peerValue;
_sessionKey = builder._sessionKey;
// these two are still null after precalc
//_peerValue = builder._peerValue;
//_sessionKey = builder._sessionKey;
_extraExchangedBytes = builder._extraExchangedBytes;
} else {
_myPrivateValue = null;
_myPublicValue = null;
_peerValue = null;
_sessionKey = null;
if (_log.shouldLog(Log.INFO)) _log.info("No more builders, creating one now");
_context.statManager().addRateData("crypto.DHEmpty", 1, 0);
// sets _myPrivateValue as a side effect
_myPublicValue = generateMyValue();
_extraExchangedBytes = new ByteArray();
}
}
public DHSessionKeyBuilder(boolean usePool) {
_myPrivateValue = null;
_myPublicValue = null;
_peerValue = null;
_sessionKey = null;
/**
* Only for internal use
* @parameter usePool unused, just to make it different from other constructor
*/
private DHSessionKeyBuilder(boolean usePool) {
_extraExchangedBytes = new ByteArray();
}
@ -189,18 +196,12 @@ public class DHSessionKeyBuilder {
}
private static final int getSize() {
synchronized (_builders) {
return _builders.size();
}
}
private static final int addBuilder(DHSessionKeyBuilder builder) {
int sz = 0;
synchronized (_builders) {
_builders.add(builder);
sz = _builders.size();
}
return sz;
/** @return true if successful, false if full */
private static final boolean addBuilder(DHSessionKeyBuilder builder) {
return _builders.offer(builder);
}
/**
@ -210,7 +211,7 @@ public class DHSessionKeyBuilder {
*/
public BigInteger generateMyValue() {
long start = System.currentTimeMillis();
_myPrivateValue = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, RandomSource.getInstance());
_myPrivateValue = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, _context.random());
BigInteger myValue = CryptoConstants.elgg.modPow(_myPrivateValue, CryptoConstants.elgp);
long end = System.currentTimeMillis();
long diff = end - start;
@ -314,6 +315,7 @@ public class DHSessionKeyBuilder {
* If there aren't enough bytes (with all of them being consumed by the 32 byte key),
* the SHA256 of the key itself is used.
*
* @return non-null (but rv.getData() may be null)
*/
public ByteArray getExtraBytes() {
return _extraExchangedBytes;
@ -406,6 +408,7 @@ public class DHSessionKeyBuilder {
}
*/
/******
public static void main(String args[]) {
//if (true) { testValidation(); return; }
@ -419,7 +422,7 @@ public class DHSessionKeyBuilder {
long negTime = 0;
try {
for (int i = 0; i < 5; i++) {
long startNeg = Clock.getInstance().now();
long startNeg = System.currentTimeMillis();
DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder();
DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder();
BigInteger pub1 = builder1.getMyPublicValue();
@ -428,7 +431,7 @@ public class DHSessionKeyBuilder {
builder1.setPeerPublicValue(pub2);
SessionKey key1 = builder1.getSessionKey();
SessionKey key2 = builder2.getSessionKey();
long endNeg = Clock.getInstance().now();
long endNeg = System.currentTimeMillis();
negTime += endNeg - startNeg;
if (!key1.equals(key2))
@ -458,10 +461,11 @@ public class DHSessionKeyBuilder {
} catch (InterruptedException ie) { // nop
}
}
******/
private static class DHSessionKeyBuilderPrecalcRunner implements Runnable {
private int _minSize;
private int _maxSize;
private final int _minSize;
private final int _maxSize;
private DHSessionKeyBuilderPrecalcRunner(int minSize, int maxSize) {
_minSize = minSize;
@ -472,22 +476,28 @@ public class DHSessionKeyBuilder {
while (true) {
int curSize = 0;
long start = Clock.getInstance().now();
long start = System.currentTimeMillis();
int startSize = getSize();
// Adjust delay
if (startSize <= (_minSize * 2 / 3) && _checkDelay > 1000)
_checkDelay -= 1000;
else if (startSize > (_minSize * 3 / 2) && _checkDelay < 60*1000)
_checkDelay += 1000;
curSize = startSize;
while (curSize < _minSize) {
while (curSize < _maxSize) {
if (curSize < _minSize) {
for (int i = curSize; i < _maxSize; i++) {
long curStart = System.currentTimeMillis();
curSize = addBuilder(precalc(curSize));
if (!addBuilder(precalc()))
break;
long curCalc = System.currentTimeMillis() - curStart;
// for some relief...
try {
Thread.sleep(CALC_DELAY + curCalc * 10);
Thread.sleep(CALC_DELAY + (curCalc * 3));
} catch (InterruptedException ie) { // nop
}
}
}
long end = Clock.getInstance().now();
long end = System.currentTimeMillis();
int numCalc = curSize - startSize;
if (numCalc > 0) {
if (_log.shouldLog(Log.DEBUG))
@ -496,16 +506,15 @@ public class DHSessionKeyBuilder {
+ (CALC_DELAY * numCalc) + "ms relief). now sleeping");
}
try {
Thread.sleep(30 * 1000);
Thread.sleep(_checkDelay);
} catch (InterruptedException ie) { // nop
}
}
}
private DHSessionKeyBuilder precalc(int i) {
private static DHSessionKeyBuilder precalc() {
DHSessionKeyBuilder builder = new DHSessionKeyBuilder(false);
builder.getMyPublicValue();
//_log.debug("Precalc " + i + " complete");
return builder;
}
}

View File

@ -29,17 +29,17 @@ import net.i2p.util.Log;
/**
* Handles the actual ElGamal+AES encryption and decryption scenarios using the
* supplied keys and data.
*
* No, this does not extend AESEngine or CryptixAESEngine.
*/
public class ElGamalAESEngine {
private final static Log _log = new Log(ElGamalAESEngine.class);
private final Log _log;
private final static int MIN_ENCRYPTED_SIZE = 80; // smallest possible resulting size
private I2PAppContext _context;
private ElGamalAESEngine() { // nop
}
private final I2PAppContext _context;
public ElGamalAESEngine(I2PAppContext ctx) {
_context = ctx;
_log = _context.logManager().getLog(ElGamalAESEngine.class);
_context.statManager().createFrequencyStat("crypto.elGamalAES.encryptNewSession",
"how frequently we encrypt to a new ElGamal/AES+SessionTag session?",
@ -627,6 +627,7 @@ public class ElGamalAESEngine {
return numPadding;
}
/****
public static void main(String args[]) {
I2PAppContext ctx = new I2PAppContext();
ElGamalAESEngine e = new ElGamalAESEngine(ctx);
@ -656,4 +657,5 @@ public class ElGamalAESEngine {
}
}
}
****/
}

View File

@ -14,8 +14,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.DataStructureImpl;
import net.i2p.data.SimpleDataStructure;
/**
* Because DSAEngine was abusing Hash for 20-byte hashes
@ -23,44 +22,31 @@ import net.i2p.data.DataStructureImpl;
* @since 0.8.1
* @author zzz
*/
public class SHA1Hash extends DataStructureImpl {
private byte[] _data;
public class SHA1Hash extends SimpleDataStructure {
private int _cachedHashCode;
public final static int HASH_LENGTH = SHA1.HASH_LENGTH;
/** @throws IllegalArgumentException if data is not 20 bytes (null is ok) */
public SHA1Hash(byte data[]) {
setData(data);
super(data);
}
public byte[] getData() {
return _data;
public int length() {
return HASH_LENGTH;
}
/** @throws IllegalArgumentException if data is not 20 bytes (null is ok) */
@Override
public void setData(byte[] data) {
// FIXME DSAEngine uses a SHA-1 "Hash" as parameters and return values!
if (data != null && data.length != HASH_LENGTH)
throw new IllegalArgumentException("Hash must be 20 bytes");
_data = data;
_cachedHashCode = calcHashCode();
}
/** @throws IOException always */
public void readBytes(InputStream in) throws DataFormatException, IOException {
throw new IOException("unimplemented");
}
/** @throws IOException always */
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
throw new IOException("unimplemented");
super.setData(data);
_cachedHashCode = super.hashCode();
}
@Override
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof SHA1Hash)) return false;
return DataHelper.eq(_data, ((SHA1Hash) obj)._data);
public void readBytes(InputStream in) throws DataFormatException, IOException {
super.readBytes(in);
_cachedHashCode = super.hashCode();
}
/** a Hash is a hash, so just use the first 4 bytes for speed */
@ -68,14 +54,4 @@ public class SHA1Hash extends DataStructureImpl {
public int hashCode() {
return _cachedHashCode;
}
/** a Hash is a hash, so just use the first 4 bytes for speed */
private int calcHashCode() {
int rv = 0;
if (_data != null) {
for (int i = 0; i < 4; i++)
rv ^= (_data[i] << (i*8));
}
return rv;
}
}

View File

@ -41,7 +41,7 @@ class YKGenerator {
private static final int MIN_NUM_BUILDERS;
private static final int MAX_NUM_BUILDERS;
private static final int CALC_DELAY;
private static final LinkedBlockingQueue<BigInteger[]> _values = new LinkedBlockingQueue(50); // list of BigInteger[] values (y and k)
private static final LinkedBlockingQueue<BigInteger[]> _values;
private static final Thread _precalcThread;
private static final I2PAppContext ctx;
@ -53,13 +53,21 @@ class YKGenerator {
public final static int DEFAULT_YK_PRECALC_DELAY = 200;
/** check every 30 seconds whether we have less than the minimum */
private static long CHECK_DELAY = 30 * 1000;
private static long _checkDelay = 30 * 1000;
static {
ctx = I2PAppContext.getGlobalContext();
MIN_NUM_BUILDERS = ctx.getProperty(PROP_YK_PRECALC_MIN, DEFAULT_YK_PRECALC_MIN);
MAX_NUM_BUILDERS = ctx.getProperty(PROP_YK_PRECALC_MAX, DEFAULT_YK_PRECALC_MAX);
// add to the defaults for every 128MB of RAM, up to 1GB
long maxMemory = Runtime.getRuntime().maxMemory();
int factor = Math.min(8, (int) (1 + (maxMemory / (128*1024*1024l))));
int defaultMin = DEFAULT_YK_PRECALC_MIN * factor;
int defaultMax = DEFAULT_YK_PRECALC_MAX * factor;
MIN_NUM_BUILDERS = ctx.getProperty(PROP_YK_PRECALC_MIN, defaultMin);
MAX_NUM_BUILDERS = ctx.getProperty(PROP_YK_PRECALC_MAX, defaultMax);
CALC_DELAY = ctx.getProperty(PROP_YK_PRECALC_DELAY, DEFAULT_YK_PRECALC_DELAY);
_values = new LinkedBlockingQueue(MAX_NUM_BUILDERS);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("ElGamal YK Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: "
@ -137,12 +145,13 @@ class YKGenerator {
long endNeg = Clock.getInstance().now();
negTime += endNeg - startNeg;
}
// 173ms each on a 2008 netbook
System.out.println("YK fetch time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each");
}
private static class YKPrecalcRunner implements Runnable {
private int _minSize;
private int _maxSize;
private final int _minSize;
private final int _maxSize;
private YKPrecalcRunner(int minSize, int maxSize) {
_minSize = minSize;
@ -155,10 +164,10 @@ class YKGenerator {
//long start = Clock.getInstance().now();
int startSize = getSize();
// Adjust delay
if (startSize <= (_minSize / 2) && CHECK_DELAY > 1000)
CHECK_DELAY -= 1000;
else if (startSize > (_minSize * 2) && CHECK_DELAY < 60000)
CHECK_DELAY += 1000;
if (startSize <= (_minSize * 2 / 3) && _checkDelay > 1000)
_checkDelay -= 1000;
else if (startSize > (_minSize * 3 / 2) && _checkDelay < 60*1000)
_checkDelay += 1000;
curSize = startSize;
if (curSize < _minSize) {
for (int i = curSize; i < _maxSize; i++) {
@ -183,7 +192,7 @@ class YKGenerator {
// + (CALC_DELAY * numCalc) + "ms relief). now sleeping");
//}
try {
Thread.sleep(CHECK_DELAY);
Thread.sleep(_checkDelay);
} catch (InterruptedException ie) { // nop
}
}

View File

@ -26,8 +26,10 @@ import java.io.OutputStream;
* @author jrandom
*/
public class Certificate extends DataStructureImpl {
private int _type;
private byte[] _payload;
public final static Certificate NULL_CERT = new NullCert();
protected int _type;
protected byte[] _payload;
/** Specifies a null certificate type with no payload */
public final static int CERTIFICATE_TYPE_NULL = 0;
@ -41,6 +43,25 @@ public class Certificate extends DataStructureImpl {
/** Contains multiple certs */
public final static int CERTIFICATE_TYPE_MULTIPLE = 4;
/**
* If null cert, return immutable static instance, else create new
* @since 0.8.3
*/
public static Certificate create(InputStream in) throws DataFormatException, IOException {
int type = (int) DataHelper.readLong(in, 1);
int length = (int) DataHelper.readLong(in, 2);
if (type == 0 && length == 0)
return NULL_CERT;
// from here down roughly the same as readBytes() below
if (length == 0)
return new Certificate(type, null);
byte[] payload = new byte[length];
int read = DataHelper.read(in, payload);
if (read != length)
throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length + ')');
return new Certificate(type, payload);
}
public Certificate() {
}
@ -90,8 +111,10 @@ public class Certificate extends DataStructureImpl {
DataHelper.writeLong(out, 2, 0L);
}
}
/**
* @return the written length (NOT the new offset)
*/
public int writeBytes(byte target[], int offset) {
int cur = offset;
DataHelper.toLong(target, cur, 1, _type);
@ -140,10 +163,12 @@ public class Certificate extends DataStructureImpl {
Certificate cert = (Certificate) object;
return _type == cert.getCertificateType() && DataHelper.eq(_payload, cert.getPayload());
}
@Override
public int hashCode() {
return _type + DataHelper.hashCode(_payload);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
@ -177,4 +202,67 @@ public class Certificate extends DataStructureImpl {
buf.append("]");
return buf.toString();
}
/**
* An immutable null certificate.
* @since 0.8.3
*/
private static final class NullCert extends Certificate {
private static final int NULL_LENGTH = 1 + 2;
private static final byte[] NULL_DATA = new byte[NULL_LENGTH];
public NullCert() {
// zero already
//_type = CERTIFICATE_TYPE_NULL;
}
/** @throws RuntimeException always */
@Override
public void setCertificateType(int type) {
throw new RuntimeException("Data already set");
}
/** @throws RuntimeException always */
@Override
public void setPayload(byte[] payload) {
throw new RuntimeException("Data already set");
}
/** @throws RuntimeException always */
@Override
public void readBytes(InputStream in) throws DataFormatException, IOException {
throw new RuntimeException("Data already set");
}
/** Overridden for efficiency */
@Override
public void writeBytes(OutputStream out) throws IOException {
out.write(NULL_DATA);
}
/** Overridden for efficiency */
@Override
public int writeBytes(byte target[], int offset) {
System.arraycopy(NULL_DATA, 0, target, offset, NULL_LENGTH);
return NULL_LENGTH;
}
/** @throws RuntimeException always */
@Override
public int readBytes(byte source[], int offset) throws DataFormatException {
throw new RuntimeException("Data already set");
}
/** Overridden for efficiency */
@Override
public int size() {
return NULL_LENGTH;
}
/** Overridden for efficiency */
@Override
public int hashCode() {
return 99999;
}
}
}

View File

@ -914,9 +914,20 @@ public class DataHelper {
return c;
}
/**
* This is different than InputStream.read(target), in that it
* does repeated reads until the full data is received.
*/
public static int read(InputStream in, byte target[]) throws IOException {
return read(in, target, 0, target.length);
}
/**
* This is different than InputStream.read(target, offset, length), in that it
* returns the new offset (== old offset + bytes read).
* It also does repeated reads until the full data is received.
* @return the new offset (== old offset + bytes read)
*/
public static int read(InputStream in, byte target[], int offset, int length) throws IOException {
int cur = offset;
while (cur < length) {

View File

@ -28,7 +28,10 @@ public class Destination extends KeysAndCert {
fromBase64(s);
}
/** deprecated, used only by Packet.java in streaming */
/**
* deprecated, used only by Packet.java in streaming
* @return the written length (NOT the new offset)
*/
public int writeBytes(byte target[], int offset) {
int cur = offset;
System.arraycopy(_publicKey.getData(), 0, target, cur, PublicKey.KEYSIZE_BYTES);

View File

@ -65,8 +65,9 @@ public class KeysAndCert extends DataStructureImpl {
_publicKey.readBytes(in);
_signingKey = new SigningPublicKey();
_signingKey.readBytes(in);
_certificate = new Certificate();
_certificate.readBytes(in);
//_certificate = new Certificate();
//_certificate.readBytes(in);
_certificate = Certificate.create(in);
__calculatedHash = null;
}

View File

@ -93,6 +93,10 @@ public class Payload extends DataStructureImpl {
if (_log.shouldLog(Log.DEBUG))
_log.debug("wrote payload: " + _encryptedData.length);
}
/**
* @return the written length (NOT the new offset)
*/
public int writeBytes(byte target[], int offset) {
if (_encryptedData == null) throw new IllegalStateException("Not yet encrypted. Please set the encrypted data");
DataHelper.toLong(target, offset, 4, _encryptedData.length);

View File

@ -20,6 +20,12 @@ import net.i2p.crypto.SHA256Generator;
*
* Implemented in 0.8.2 and retrofitted over several of the classes in this package.
*
* As of 0.8.3, SDS objects may be cached. An SDS may be instantiated with null data,
* and setData(null) is also OK. However,
* once non-null data is set, the data reference is immutable;
* subsequent attempts to set the data via setData(), readBytes(),
* or fromBase64() will throw a RuntimeException.
*
* @since 0.8.2
* @author zzz
*/
@ -57,14 +63,24 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
* Sets the data.
* @param data of correct length, or null
* @throws IllegalArgumentException if data is not the legal number of bytes (but null is ok)
* @throws RuntimeException if data already set.
*/
public void setData(byte[] data) {
if (_data != null)
throw new RuntimeException("Data already set");
if (data != null && data.length != _length)
throw new IllegalArgumentException("Bad data length");
_data = data;
}
/**
* Sets the data.
* @param data of correct length, or null
* @throws RuntimeException if data already set.
*/
public void readBytes(InputStream in) throws DataFormatException, IOException {
if (_data != null)
throw new RuntimeException("Data already set");
_data = new byte[_length];
int read = read(in, _data);
if (read != _length) throw new DataFormatException("Not enough bytes to read the data");
@ -85,6 +101,7 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
/**
* Sets the data.
* @throws DataFormatException if decoded data is not the legal number of bytes or on decoding error
* @throws RuntimeException if data already set.
*/
@Override
public void fromBase64(String data) throws DataFormatException {
@ -162,5 +179,4 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
if ((obj == null) || !(obj instanceof SimpleDataStructure)) return false;
return DataHelper.eq(_data, ((SimpleDataStructure) obj)._data);
}
}

View File

@ -13,14 +13,18 @@ import java.io.InputStream;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
/**
* Response to DestLookupMessage
*
* Response to DestLookupMessage.
* As of 0.8.3, the response may include the hash from the request, indicating
* a failure for a specific request.
* Payload may be empty (failure), a Hash (failure), or a Destination.
*/
public class DestReplyMessage extends I2CPMessageImpl {
public final static int MESSAGE_TYPE = 35;
private Destination _dest;
private Hash _hash;
public DestReplyMessage() {
super();
@ -30,23 +34,52 @@ public class DestReplyMessage extends I2CPMessageImpl {
_dest = d;
}
/**
* @param h non-null with non-null data
* @since 0.8.3
*/
public DestReplyMessage(Hash h) {
_hash = h;
}
public Destination getDestination() {
return _dest;
}
/**
* @since 0.8.3
*/
public Hash getHash() {
return _hash;
}
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
try {
Destination d = new Destination();
d.readBytes(in);
_dest = d;
} catch (DataFormatException dfe) {
_dest = null; // null dest allowed
if (size == 0) {
_dest = null;
_hash = null;
} else {
try {
if (size == Hash.HASH_LENGTH) {
Hash h = new Hash();
h.readBytes(in);
_hash = h;
} else {
Destination d = new Destination();
d.readBytes(in);
_dest = d;
}
} catch (DataFormatException dfe) {
_dest = null;
_hash = null;
}
}
}
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if (_dest == null)
if (_dest == null && _hash == null)
return new byte[0]; // null response allowed
if (_dest == null && _hash != null)
return _hash.getData();
ByteArrayOutputStream os = new ByteArrayOutputStream(_dest.size());
try {
_dest.writeBytes(os);
@ -65,7 +98,8 @@ public class DestReplyMessage extends I2CPMessageImpl {
public boolean equals(Object object) {
if ((object != null) && (object instanceof DestReplyMessage)) {
DestReplyMessage msg = (DestReplyMessage) object;
return DataHelper.eq(getDestination(), msg.getDestination());
return DataHelper.eq(getDestination(), msg.getDestination()) &&
DataHelper.eq(getHash(), msg.getHash());
}
return false;
}
@ -75,6 +109,7 @@ public class DestReplyMessage extends I2CPMessageImpl {
StringBuilder buf = new StringBuilder();
buf.append("[DestReplyMessage: ");
buf.append("\n\tDestination: ").append(_dest);
buf.append("\n\tHash: ").append(_hash);
buf.append("]");
return buf.toString();
}

View File

@ -27,11 +27,11 @@ import net.i2p.util.Log;
public class I2CPMessageReader {
private final static Log _log = new Log(I2CPMessageReader.class);
private InputStream _stream;
private I2CPMessageEventListener _listener;
private I2CPMessageReaderRunner _reader;
private Thread _readerThread;
protected I2CPMessageEventListener _listener;
protected I2CPMessageReaderRunner _reader;
protected Thread _readerThread;
private static volatile long __readerId = 0;
protected static volatile long __readerId = 0;
public I2CPMessageReader(InputStream stream, I2CPMessageEventListener lsnr) {
_stream = stream;
@ -42,6 +42,14 @@ public class I2CPMessageReader {
_readerThread.setName("I2CP Reader " + (++__readerId));
}
/**
* For internal extension only. No stream.
* @since 0.8.3
*/
protected I2CPMessageReader(I2CPMessageEventListener lsnr) {
setListener(lsnr);
}
public void setListener(I2CPMessageEventListener lsnr) {
_listener = lsnr;
}
@ -114,9 +122,9 @@ public class I2CPMessageReader {
public void disconnected(I2CPMessageReader reader);
}
private class I2CPMessageReaderRunner implements Runnable {
private volatile boolean _doRun;
private volatile boolean _stayAlive;
protected class I2CPMessageReaderRunner implements Runnable {
protected volatile boolean _doRun;
protected volatile boolean _stayAlive;
public I2CPMessageReaderRunner() {
_doRun = true;
@ -175,7 +183,8 @@ public class I2CPMessageReader {
cancelRunner();
}
}
if (!_doRun) {
// ??? unused
if (_stayAlive && !_doRun) {
// pause .5 secs when we're paused
try {
Thread.sleep(500);

View File

@ -0,0 +1,51 @@
package net.i2p.internal;
import net.i2p.data.i2cp.I2CPMessage;
/**
* Contains the methods to talk to a router or client via I2CP,
* when both are in the same JVM.
* This interface contains methods to access two queues,
* one for transmission and one for receiving.
* The methods are identical to those in java.util.concurrent.BlockingQueue.
*
* Reading may be done in a thread using the QueuedI2CPMessageReader class.
* Non-blocking writing may be done directly with offer().
*
* @author zzz
* @since 0.8.3
*/
public abstract class I2CPMessageQueue {
/**
* Send a message, nonblocking.
* @return success (false if no space available)
*/
public abstract boolean offer(I2CPMessage msg);
/**
* Receive a message, nonblocking.
* Unused for now.
* @return message or null if none available
*/
public abstract I2CPMessage poll();
/**
* Send a message, blocking until space is available.
* Unused for now.
*/
public abstract void put(I2CPMessage msg) throws InterruptedException;
/**
* Receive a message, blocking until one is available.
* @return message
*/
public abstract I2CPMessage take() throws InterruptedException;
/**
* == offer(new PoisonI2CPMessage());
*/
public void close() {
offer(new PoisonI2CPMessage());
}
}

View File

@ -0,0 +1,19 @@
package net.i2p.internal;
import net.i2p.client.I2PSessionException;
import net.i2p.data.i2cp.I2CPMessage;
/**
* A manager for the in-JVM I2CP message interface
*
* @author zzz
* @since 0.8.3
*/
public interface InternalClientManager {
/**
* Connect to the router, receiving a message queue to talk to the router with.
* @throws I2PSessionException if the router isn't ready
*/
public I2CPMessageQueue connect() throws I2PSessionException;
}

View File

@ -0,0 +1,56 @@
package net.i2p.internal;
import java.io.InputStream;
import net.i2p.data.i2cp.I2CPMessageException;
import net.i2p.data.i2cp.I2CPMessageImpl;
/**
* For marking end-of-queues in a standard manner.
* Don't actually send it.
*
* @author zzz
* @since 0.8.3
*/
public class PoisonI2CPMessage extends I2CPMessageImpl {
public final static int MESSAGE_TYPE = 999999;
public PoisonI2CPMessage() {
super();
}
/**
* @deprecated don't do this
* @throws I2CPMessageException always
*/
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException {
throw new I2CPMessageException("Don't do this");
}
/**
* @deprecated don't do this
* @throws I2CPMessageException always
*/
protected byte[] doWriteMessage() throws I2CPMessageException {
throw new I2CPMessageException("Don't do this");
}
public int getType() {
return MESSAGE_TYPE;
}
/* FIXME missing hashCode() method FIXME */
@Override
public boolean equals(Object object) {
if ((object != null) && (object instanceof PoisonI2CPMessage)) {
return true;
}
return false;
}
@Override
public String toString() {
return "[PoisonMessage]";
}
}

View File

@ -0,0 +1,62 @@
package net.i2p.internal;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.I2CPMessageReader;
import net.i2p.util.I2PThread;
/**
* Get messages off an In-JVM queue, zero-copy
*
* @author zzz
* @since 0.8.3
*/
public class QueuedI2CPMessageReader extends I2CPMessageReader {
private final I2CPMessageQueue in;
public QueuedI2CPMessageReader(I2CPMessageQueue in, I2CPMessageEventListener lsnr) {
super(lsnr);
this.in = in;
_reader = new QueuedI2CPMessageReaderRunner();
_readerThread = new I2PThread(_reader, "I2CP Internal Reader " + (++__readerId), true);
}
protected class QueuedI2CPMessageReaderRunner extends I2CPMessageReaderRunner implements Runnable {
public QueuedI2CPMessageReaderRunner() {
super();
}
@Override
public void cancelRunner() {
super.cancelRunner();
_readerThread.interrupt();
}
@Override
public void run() {
while (_stayAlive) {
while (_doRun) {
// do read
I2CPMessage msg = null;
try {
msg = in.take();
if (msg.getType() == PoisonI2CPMessage.MESSAGE_TYPE)
cancelRunner();
else
_listener.messageReceived(QueuedI2CPMessageReader.this, msg);
} catch (InterruptedException ie) {}
}
// ??? unused
if (_stayAlive && !_doRun) {
// pause .5 secs when we're paused
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
_listener.disconnected(QueuedI2CPMessageReader.this);
cancelRunner();
}
}
}
}
}
}

View File

@ -0,0 +1,7 @@
<html><body>
<p>
Interface and classes for a router and client
within the same JVM to directly pass I2CP messages using Queues
instead of serialized messages over socket streams.
</p>
</body></html>

View File

@ -206,7 +206,7 @@ public class StatManager {
* @return true if the stat should be ignored.
*/
public boolean ignoreStat(String statName) {
if (_context.getBooleanPropertyDefaultTrue(PROP_STAT_FULL))
if (_context.getBooleanProperty(PROP_STAT_FULL))
return false;
String required = _context.getProperty(PROP_STAT_REQUIRED, DEFAULT_STAT_REQUIRED);
String req[] = required.split(",");

View File

@ -0,0 +1,126 @@
package net.i2p.util;
/*
* public domain
*/
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Get the local addresses
*
* @since 0.8.3 moved to core
* @author zzz
*/
public abstract class Addresses {
/** @return the first non-local address it finds, or null */
public static String getAnyAddress() {
SortedSet<String> a = getAddresses();
if (!a.isEmpty())
return a.first();
return null;
}
/**
* @return a sorted set of all addresses, excluding
* IPv6, local, broadcast, multicast, etc.
*/
public static SortedSet<String> getAddresses() {
return getAddresses(false, false);
}
/**
* @return a sorted set of all addresses, excluding
* only link local and multicast
* @since 0.8.3
*/
public static SortedSet<String> getAllAddresses() {
return getAddresses(true, true);
}
/**
* @return a sorted array of all addresses
* @param whether to exclude IPV6 and local
* @return an array of all addresses
* @since 0.8.3
*/
public static SortedSet<String> getAddresses(boolean includeLocal, boolean includeIPv6) {
boolean haveIPv4 = false;
boolean haveIPv6 = false;
SortedSet<String> rv = new TreeSet();
try {
InetAddress localhost = InetAddress.getLocalHost();
InetAddress[] allMyIps = InetAddress.getAllByName(localhost.getCanonicalHostName());
if (allMyIps != null) {
for (int i = 0; i < allMyIps.length; i++) {
if (allMyIps[i] instanceof Inet4Address)
haveIPv4 = true;
else
haveIPv6 = true;
if (shouldInclude(allMyIps[i], includeLocal, includeIPv6))
rv.add(allMyIps[i].getHostAddress());
}
}
} catch (UnknownHostException e) {}
try {
for(Enumeration<NetworkInterface> ifcs = NetworkInterface.getNetworkInterfaces(); ifcs.hasMoreElements();) {
NetworkInterface ifc = ifcs.nextElement();
for(Enumeration<InetAddress> addrs = ifc.getInetAddresses(); addrs.hasMoreElements();) {
InetAddress addr = addrs.nextElement();
if (addr instanceof Inet4Address)
haveIPv4 = true;
else
haveIPv6 = true;
if (shouldInclude(addr, includeLocal, includeIPv6))
rv.add(addr.getHostAddress());
}
}
} catch (SocketException e) {}
if (includeLocal && haveIPv4)
rv.add("0.0.0.0");
if (includeLocal && includeIPv6 && haveIPv6)
rv.add("0:0:0:0:0:0:0:0"); // we could do "::" but all the other ones are probably in long form
return rv;
}
private static boolean shouldInclude(InetAddress ia, boolean includeLocal, boolean includeIPv6) {
return
(!ia.isLinkLocalAddress()) &&
(!ia.isMulticastAddress()) &&
(includeLocal ||
((!ia.isAnyLocalAddress()) &&
(!ia.isLoopbackAddress()) &&
(!ia.isSiteLocalAddress()))) &&
// Hamachi 5/8 allocated to RIPE (30 November 2010)
// Removed from TransportImpl.isPubliclyRoutable()
// Check moved to here, for now, but will eventually need to
// remove it from here also.
(includeLocal ||
(!ia.getHostAddress().startsWith("5."))) &&
(includeIPv6 ||
(ia instanceof Inet4Address));
}
public static void main(String[] args) {
System.err.println("External Addresses:");
Set<String> a = getAddresses(false, false);
for (String s : a)
System.err.println(s);
System.err.println("All addresses:");
a = getAddresses(true, true);
for (String s : a)
System.err.println(s);
}
}

View File

@ -204,6 +204,7 @@ public class BufferedRandomSource extends RandomSource {
return super.nextGaussian();
}
/*****
public static void main(String args[]) {
for (int i = 0; i < 16; i++)
test();
@ -232,4 +233,5 @@ public class BufferedRandomSource extends RandomSource {
}
return buf.toString();
}
*****/
}

View File

@ -9,6 +9,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
@ -16,13 +18,11 @@ import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
// Pack200 import
// you must also uncomment the correct line in unpack() below
// For gcj, gij, etc., comment both out
// Pack200 now loaded dynamically in unpack() below
//
// For Sun, OpenJDK, IcedTea, etc, use this
import java.util.jar.Pack200;
//import java.util.jar.Pack200;
//
// For Apache Harmony or if you put its pack200.jar in your library directory use this
//import org.apache.harmony.unpack200.Archive;
@ -231,37 +231,79 @@ public class FileUtil {
}
/**
* This won't work right if one of the two options in unpack() is commented out.
* Public since 0.8.3
* @since 0.8.1
*/
private static boolean isPack200Supported() {
public static boolean isPack200Supported() {
try {
Class.forName("java.util.jar.Pack200", false, ClassLoader.getSystemClassLoader());
return true;
} catch (Exception e) {}
try {
Class.forName("org.apache.harmony.pack200.Archive", false, ClassLoader.getSystemClassLoader());
Class.forName("org.apache.harmony.unpack200.Archive", false, ClassLoader.getSystemClassLoader());
return true;
} catch (Exception e) {}
return false;
}
private static boolean _failedOracle;
private static boolean _failedApache;
/**
* Unpack using either Oracle or Apache's unpack200 library,
* with the classes discovered at runtime so neither is required at compile time.
*
* Caller must close streams
* @throws IOException on unpack error or if neither library is available.
* Will not throw ClassNotFoundException.
* @throws org.apache.harmony.pack200.Pack200Exception which is not an IOException
* @since 0.8.1
*/
private static void unpack(InputStream in, JarOutputStream out) throws Exception {
// For Sun, OpenJDK, IcedTea, etc, use this
Pack200.newUnpacker().unpack(in, out);
//Pack200.newUnpacker().unpack(in, out);
if (!_failedOracle) {
try {
Class p200 = Class.forName("java.util.jar.Pack200", true, ClassLoader.getSystemClassLoader());
Method newUnpacker = p200.getMethod("newUnpacker", (Class[]) null);
Object unpacker = newUnpacker.invoke(null,(Object[]) null);
Method unpack = unpacker.getClass().getMethod("unpack", new Class[] {InputStream.class, JarOutputStream.class});
// throws IOException
unpack.invoke(unpacker, new Object[] {in, out});
return;
} catch (ClassNotFoundException e) {
_failedOracle = true;
//e.printStackTrace();
} catch (NoSuchMethodException e) {
_failedOracle = true;
//e.printStackTrace();
}
}
// ------------------
// For Apache Harmony or if you put its pack200.jar in your library directory use this
//(new Archive(in, out)).unpack();
if (!_failedApache) {
try {
Class p200 = Class.forName("org.apache.harmony.unpack200.Archive", true, ClassLoader.getSystemClassLoader());
Constructor newUnpacker = p200.getConstructor(new Class[] {InputStream.class, JarOutputStream.class});
Object unpacker = newUnpacker.newInstance(new Object[] {in, out});
Method unpack = unpacker.getClass().getMethod("unpack", (Class[]) null);
// throws IOException or Pack200Exception
unpack.invoke(unpacker, (Object[]) null);
return;
} catch (ClassNotFoundException e) {
_failedApache = true;
//e.printStackTrace();
} catch (NoSuchMethodException e) {
_failedApache = true;
//e.printStackTrace();
}
}
// ------------------
// For gcj, gij, etc., use this
//throw new IOException("Pack200 not supported");
throw new IOException("Unpack200 not supported");
}
/**
@ -378,12 +420,13 @@ public class FileUtil {
}
/**
* Usage: FileUtil (delete path | copy source dest)
* Usage: FileUtil (delete path | copy source dest | unzip path.zip)
*
*/
public static void main(String args[]) {
if ( (args == null) || (args.length < 2) ) {
testRmdir();
System.err.println("Usage: delete path | copy source dest | unzip path.zip");
//testRmdir();
} else if ("delete".equals(args[0])) {
boolean deleted = FileUtil.rmdir(args[1], false);
if (!deleted)
@ -407,6 +450,7 @@ public class FileUtil {
}
}
/*****
private static void testRmdir() {
File t = new File("rmdirTest/test/subdir/blah");
boolean created = t.mkdirs();
@ -417,4 +461,5 @@ public class FileUtil {
else
System.out.println("PASS: rmdirTest deleted");
}
*****/
}

View File

@ -23,7 +23,7 @@ import net.i2p.crypto.EntropyHarvester;
*
*/
public class FortunaRandomSource extends RandomSource implements EntropyHarvester {
private AsyncFortunaStandalone _fortuna;
private final AsyncFortunaStandalone _fortuna;
private double _nextGaussian;
private boolean _haveNextGaussian;
@ -210,6 +210,7 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
_fortuna.addRandomBytes(data, offset, len);
}
/*****
public static void main(String args[]) {
try {
RandomSource rand = I2PAppContext.getGlobalContext().random();
@ -231,4 +232,5 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
System.out.println("Compressed size of 1MB: " + compressed.length);
} catch (Exception e) { e.printStackTrace(); }
}
*****/
}

View File

@ -61,8 +61,8 @@ public class I2PThread extends Thread {
_createdBy = new Exception("Created by");
}
private void log(int level, String msg) { log(level, msg, null); }
private void log(int level, String msg, Throwable t) {
private static void log(int level, String msg) { log(level, msg, null); }
private static void log(int level, String msg, Throwable t) {
// we cant assume log is created
if (_log == null) _log = new Log(I2PThread.class);
if (_log.shouldLog(level))
@ -72,12 +72,12 @@ public class I2PThread extends Thread {
@Override
public void run() {
_name = Thread.currentThread().getName();
log(Log.DEBUG, "New thread started: " + _name, _createdBy);
log(Log.INFO, "New thread started" + (isDaemon() ? " (daemon): " : ": ") + _name, _createdBy);
try {
super.run();
} catch (Throwable t) {
try {
log(Log.CRIT, "Killing thread " + getName(), t);
log(Log.CRIT, "Thread terminated unexpectedly: " + getName(), t);
} catch (Throwable woof) {
System.err.println("Died within the OOM itself");
t.printStackTrace();
@ -85,12 +85,12 @@ public class I2PThread extends Thread {
if (t instanceof OutOfMemoryError)
fireOOM((OutOfMemoryError)t);
}
log(Log.DEBUG, "Thread finished gracefully: " + _name);
log(Log.INFO, "Thread finished normally: " + _name);
}
@Override
protected void finalize() throws Throwable {
log(Log.DEBUG, "Thread finalized: " + _name);
//log(Log.DEBUG, "Thread finalized: " + _name);
super.finalize();
}

View File

@ -196,6 +196,7 @@ public class PooledRandomSource extends RandomSource {
return prng.harvester();
}
/*****
public static void main(String args[]) {
//PooledRandomSource prng = new PooledRandomSource(I2PAppContext.getGlobalContext());
long start = System.currentTimeMillis();
@ -214,4 +215,5 @@ public class PooledRandomSource extends RandomSource {
System.out.println("Written to random.file: create took " + (created-start) + ", generate took " + (done-created));
prng.saveSeed();
}
*****/
}

View File

@ -25,18 +25,23 @@ import net.i2p.data.Base64;
* @author jrandom
*/
public class RandomSource extends SecureRandom implements EntropyHarvester {
private Log _log;
private EntropyHarvester _entropyHarvester;
protected I2PAppContext _context;
private final EntropyHarvester _entropyHarvester;
protected final I2PAppContext _context;
public RandomSource(I2PAppContext context) {
super();
_context = context;
_log = context.logManager().getLog(RandomSource.class);
// when we replace to have hooks for fortuna (etc), replace with
// a factory (or just a factory method)
_entropyHarvester = this;
}
/**
* Singleton for whatever PRNG i2p uses.
* Same as I2PAppContext.getGlobalContext().random();
* use context.random() if you have a context already.
* @return I2PAppContext.getGlobalContext().random()
*/
public static RandomSource getInstance() {
return I2PAppContext.getGlobalContext().random();
}
@ -72,46 +77,30 @@ public class RandomSource extends SecureRandom implements EntropyHarvester {
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public boolean nextBoolean() { return super.nextBoolean(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public void nextBytes(byte buf[]) { super.nextBytes(buf); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public double nextDouble() { return super.nextDouble(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public float nextFloat() { return super.nextFloat(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public double nextGaussian() { return super.nextGaussian(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public int nextInt() { return super.nextInt(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public long nextLong() { return super.nextLong(); }
*****/
/** */
public EntropyHarvester harvester() { return _entropyHarvester; }
public void feedEntropy(String source, long data, int bitoffset, int bits) {

View File

@ -51,17 +51,18 @@ public class ShellCommand {
*/
private class CommandThread extends Thread {
final Object caller;
boolean consumeOutput;
String shellCommand;
private final Object caller;
private final boolean consumeOutput;
private final Object shellCommand;
CommandThread(Object caller, String shellCommand, boolean consumeOutput) {
/**
* @param shellCommand either a String or a String[] (since 0.8.3)
*/
CommandThread(Object caller, Object shellCommand, boolean consumeOutput) {
super("CommandThread");
this.caller = caller;
this.shellCommand = shellCommand;
this.consumeOutput = consumeOutput;
_commandSuccessful = false;
_commandCompleted = false;
}
@Override
@ -200,6 +201,9 @@ public class ShellCommand {
* {@link #getErrorStream()}, respectively. Input can be passed to the
* <code>STDIN</code> of the shell process via {@link #getInputStream()}.
*
* Warning, no good way to quote or escape spaces in arguments with this method.
* @deprecated unused
*
* @param shellCommand The command for the shell to execute.
*/
public void execute(String shellCommand) {
@ -215,6 +219,9 @@ public class ShellCommand {
* Input can be passed to the <code>STDIN</code> of the shell process via
* {@link #getInputStream()}.
*
* Warning, no good way to quote or escape spaces in arguments with this method.
* @deprecated unused
*
* @param shellCommand The command for the shell to execute.
* @return <code>true</code> if the spawned shell process
* returns an exit status of 0 (indicating success),
@ -237,6 +244,9 @@ public class ShellCommand {
* {@link #getErrorStream()}, respectively. Input can be passed to the
* <code>STDIN</code> of the shell process via {@link #getInputStream()}.
*
* Warning, no good way to quote or escape spaces in arguments with this method.
* @deprecated unused
*
* @param shellCommand The command for the shell to execute.
* @param seconds The method will return <code>true</code> if this
* number of seconds elapses without the process
@ -276,6 +286,9 @@ public class ShellCommand {
* without waiting for an exit status. Any output produced by the executed
* command will not be displayed.
*
* Warning, no good way to quote or escape spaces in arguments with this method.
* @deprecated unused
*
* @param shellCommand The command for the shell to execute.
* @throws IOException
*/
@ -288,6 +301,8 @@ public class ShellCommand {
* all of the command's resulting shell processes have completed. Any output
* produced by the executed command will not be displayed.
*
* Warning, no good way to quote or escape spaces in arguments with this method.
*
* @param shellCommand The command for the shell to execute.
* @return <code>true</code> if the spawned shell process
* returns an exit status of 0 (indicating success),
@ -307,7 +322,12 @@ public class ShellCommand {
* specified number of seconds has elapsed first. Any output produced by the
* executed command will not be displayed.
*
* @param shellCommand The command for the shell to execute.
* Warning, no good way to quote or escape spaces in arguments when shellCommand is a String.
* Use a String array for best results, especially on Windows.
*
* @param shellCommand The command for the shell to execute, as a String.
* You can't quote arguments successfully.
* See Runtime.exec(String) for more info.
* @param seconds The method will return <code>true</code> if this
* number of seconds elapses without the process
* returning an exit status. A value of <code>0</code>
@ -317,7 +337,33 @@ public class ShellCommand {
* else <code>false</code>.
*/
public synchronized boolean executeSilentAndWaitTimed(String shellCommand, int seconds) {
return executeSAWT(shellCommand, seconds);
}
/**
* Passes a command to the shell for execution. This method blocks until
* all of the command's resulting shell processes have completed unless a
* specified number of seconds has elapsed first. Any output produced by the
* executed command will not be displayed.
*
* @param commandArray The command for the shell to execute,
* as a String[].
* See Runtime.exec(String[]) for more info.
* @param seconds The method will return <code>true</code> if this
* number of seconds elapses without the process
* returning an exit status. A value of <code>0</code>
* here disables waiting.
* @return <code>true</code> if the spawned shell process
* returns an exit status of 0 (indicating success),
* else <code>false</code>.
* @since 0.8.3
*/
public synchronized boolean executeSilentAndWaitTimed(String[] commandArray, int seconds) {
return executeSAWT(commandArray, seconds);
}
/** @since 0.8.3 */
private boolean executeSAWT(Object shellCommand, int seconds) {
_commandThread = new CommandThread(this, shellCommand, CONSUME_OUTPUT);
_commandThread.start();
try {
@ -364,7 +410,10 @@ public class ShellCommand {
return;
}
private boolean execute(String shellCommand, boolean consumeOutput, boolean waitForExitStatus) {
/**
* @param shellCommand either a String or a String[] (since 0.8.3) - quick hack
*/
private boolean execute(Object shellCommand, boolean consumeOutput, boolean waitForExitStatus) {
StreamConsumer processStderrConsumer;
StreamConsumer processStdoutConsumer;
@ -374,7 +423,13 @@ public class ShellCommand {
StreamReader processStdoutReader;
try {
_process = Runtime.getRuntime().exec(shellCommand, null);
// easy way so we don't have to copy this whole method
if (shellCommand instanceof String)
_process = Runtime.getRuntime().exec((String)shellCommand);
else if (shellCommand instanceof String[])
_process = Runtime.getRuntime().exec((String[])shellCommand);
else
throw new ClassCastException("shell command must be a String or a String[]");
if (consumeOutput) {
processStderrConsumer = new StreamConsumer(_process.getErrorStream());
processStderrConsumer.start();

View File

@ -28,12 +28,14 @@ import net.i2p.I2PAppContext;
public class SimpleScheduler {
private static final SimpleScheduler _instance = new SimpleScheduler();
public static SimpleScheduler getInstance() { return _instance; }
private static final int THREADS = 4;
private static final int MIN_THREADS = 2;
private static final int MAX_THREADS = 4;
private I2PAppContext _context;
private Log _log;
private ScheduledThreadPoolExecutor _executor;
private String _name;
private int _count;
private final int _threads;
protected SimpleScheduler() { this("SimpleScheduler"); }
protected SimpleScheduler(String name) {
@ -41,7 +43,9 @@ public class SimpleScheduler {
_log = _context.logManager().getLog(SimpleScheduler.class);
_name = name;
_count = 0;
_executor = new ScheduledThreadPoolExecutor(THREADS, new CustomThreadFactory());
long maxMemory = Runtime.getRuntime().maxMemory();
_threads = (int) Math.max(MIN_THREADS, Math.min(MAX_THREADS, 1 + (maxMemory / (32*1024*1024))));
_executor = new ScheduledThreadPoolExecutor(_threads, new CustomThreadFactory());
_executor.prestartAllCoreThreads();
}
@ -65,6 +69,13 @@ public class SimpleScheduler {
re.schedule();
}
/**
* Queue up the given event to be fired after timeoutMs and every
* timeoutMs thereafter. The TimedEvent must not do its own rescheduling.
* As all Exceptions are caught in run(), these will not prevent
* subsequent executions (unlike SimpleTimer, where the TimedEvent does
* its own rescheduling).
*/
public void addPeriodicEvent(SimpleTimer.TimedEvent event, long timeoutMs) {
addPeriodicEvent(event, timeoutMs, timeoutMs);
}
@ -90,7 +101,7 @@ public class SimpleScheduler {
private class CustomThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
Thread rv = Executors.defaultThreadFactory().newThread(r);
rv.setName(_name + ' ' + (++_count) + '/' + THREADS);
rv.setName(_name + ' ' + (++_count) + '/' + _threads);
// Uncomment this to test threadgrouping, but we should be all safe now that the constructor preallocates!
// String name = rv.getThreadGroup().getName();
// if(!name.equals("main")) {

View File

@ -18,14 +18,16 @@ import net.i2p.I2PAppContext;
public class SimpleTimer {
private static final SimpleTimer _instance = new SimpleTimer();
public static SimpleTimer getInstance() { return _instance; }
private I2PAppContext _context;
private Log _log;
private final I2PAppContext _context;
private final Log _log;
/** event time (Long) to event (TimedEvent) mapping */
private final TreeMap _events;
/** event (TimedEvent) to event time (Long) mapping */
private Map _eventTimes;
private final List _readyEvents;
private SimpleStore runn;
private static final int MIN_THREADS = 2;
private static final int MAX_THREADS = 4;
protected SimpleTimer() { this("SimpleTimer"); }
protected SimpleTimer(String name) {
@ -39,9 +41,11 @@ public class SimpleTimer {
runner.setName(name);
runner.setDaemon(true);
runner.start();
for (int i = 0; i < 3; i++) {
long maxMemory = Runtime.getRuntime().maxMemory();
int threads = (int) Math.max(MIN_THREADS, Math.min(MAX_THREADS, 1 + (maxMemory / (32*1024*1024))));
for (int i = 1; i <= threads ; i++) {
I2PThread executor = new I2PThread(new Executor(_context, _log, _readyEvents, runn));
executor.setName(name + "Executor " + i);
executor.setName(name + "Executor " + i + '/' + threads);
executor.setDaemon(true);
executor.start();
}

View File

@ -27,12 +27,14 @@ import net.i2p.I2PAppContext;
public class SimpleTimer2 {
private static final SimpleTimer2 _instance = new SimpleTimer2();
public static SimpleTimer2 getInstance() { return _instance; }
private static final int THREADS = 4;
private static final int MIN_THREADS = 2;
private static final int MAX_THREADS = 4;
private I2PAppContext _context;
private static Log _log; // static so TimedEvent can use it
private ScheduledThreadPoolExecutor _executor;
private String _name;
private int _count;
private final int _threads;
protected SimpleTimer2() { this("SimpleTimer2"); }
protected SimpleTimer2(String name) {
@ -40,7 +42,9 @@ public class SimpleTimer2 {
_log = _context.logManager().getLog(SimpleTimer2.class);
_name = name;
_count = 0;
_executor = new CustomScheduledThreadPoolExecutor(THREADS, new CustomThreadFactory());
long maxMemory = Runtime.getRuntime().maxMemory();
_threads = (int) Math.max(MIN_THREADS, Math.min(MAX_THREADS, 1 + (maxMemory / (32*1024*1024))));
_executor = new CustomScheduledThreadPoolExecutor(_threads, new CustomThreadFactory());
_executor.prestartAllCoreThreads();
}
@ -67,7 +71,7 @@ public class SimpleTimer2 {
private class CustomThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
Thread rv = Executors.defaultThreadFactory().newThread(r);
rv.setName(_name + ' ' + (++_count) + '/' + THREADS);
rv.setName(_name + ' ' + (++_count) + '/' + _threads);
// Uncomment this to test threadgrouping, but we should be all safe now that the constructor preallocates!
// String name = rv.getThreadGroup().getName();
// if(!name.equals("main")) {

View File

@ -6,6 +6,21 @@
#
# fire up the web console
## There are several choices, here are some examples:
## non-SSL, bind to local IPv4 only
#clientApp.0.args=7657 127.0.0.1 ./webapps/
## non-SSL, bind to local IPv6 only
#clientApp.0.args=7657 ::1 ./webapps/
## non-SSL, bind to all IPv4 addresses
#clientApp.0.args=7657 0.0.0.0 ./webapps/
## non-SSL, bind to all IPv6 addresses
#clientApp.0.args=7657 :: ./webapps/
## For SSL only, change clientApp.4.args below to https://
## SSL only
#clientApp.0.args=-s 7657 ::1,127.0.0.1 ./webapps/
## non-SSL and SSL
#clientApp.0.args=7657 ::1,127.0.0.1 -s 7667 ::1,127.0.0.1 ./webapps/
## non-SSL only, both IPv6 and IPv4 local interfaces
clientApp.0.args=7657 ::1,127.0.0.1 ./webapps/
clientApp.0.main=net.i2p.router.web.RouterConsoleRunner
clientApp.0.name=I2P Router Console

View File

@ -71,17 +71,29 @@
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Add a HTTPS SSL listener on port 8443 -->
<!-- Add a HTTPS SSL listener on port 8443 -->
<!-- -->
<!-- In the unlikely event you would want SSL support for your eepsite. -->
<!-- You would need to generate a selfsigned certificate in a keystore -->
<!-- in ~/.i2p/eepsite/keystore.ks, for example with the command line: -->
<!--
keytool -genkey -storetype JKS -keystore ~/.i2p/eepsite/keystore.ks -storepass changeit -alias console -dname CN=xyz123.eepsite.i2p.net,OU=Eepsite,O=I2P Anonymous Network,L=XX,ST=XX,C=XX -validity 3650 -keyalg DSA -keysize 1024 -keypass myKeyPassword
-->
<!-- Change the CN and key password in the example, of course. -->
<!-- You wouldn't want to open this up to the regular internet, -->
<!-- would you?? Untested and not recommended. -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- UNCOMMENT TO ACTIVATE
<Call name="addListener">
<Arg>
<New class="org.mortbay.http.SunJsseListener">
<New class="org.mortbay.http.SslListener">
<Set name="Port">8443</Set>
<Set name="PoolName">main</Set>
<Set name="Keystore"><SystemProperty name="jetty.home" default="."/>/etc/demokeystore</Set>
<Set name="Password">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
<Set name="KeyPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
<Set name="Keystore">./eepsite/keystore.ks</Set>
<!-- the keystore password -->
<Set name="Password">changeit</Set>
<!-- the X.509 certificate password -->
<Set name="KeyPassword">myKeyPassword</Set>
<Set name="NonPersistentUserAgent">MSIE 5</Set>
</New>
</Arg>

View File

@ -718,7 +718,7 @@ input {
vertical-align: middle;
}
input[type=text] {
input[type=text], input[type=password] {
margin: 3px 5px 3px 5px;
vertical-align: middle;
}

File diff suppressed because it is too large Load Diff

View File

@ -921,7 +921,7 @@ input:active {
-moz-box-shadow: inset 0px 0px 0px 1px #f60;
}
input[type=text] {
input[type=text], input[type=password] {
background: #ffe;
color: #001;
margin: 5px 10px 5px 10px;

View File

@ -755,7 +755,7 @@ input {
vertical-align: middle;
}
input[type=text] {
input[type=text], input[type=password] {
margin: 3px 5px 3px 5px;
vertical-align: middle;
}
@ -765,7 +765,7 @@ select {
vertical-align: middle;
}
input[type=text], select {
input[type=text], input[type=password] select {
background: #001;
color: #eef;
border: 1px solid #99f;

View File

@ -68,8 +68,9 @@ public class GarlicClove extends DataStructureImpl {
_expiration = DataHelper.readDate(in);
if (_log.shouldLog(Log.DEBUG))
_log.debug("CloveID read: " + _cloveId + " expiration read: " + _expiration);
_certificate = new Certificate();
_certificate.readBytes(in);
//_certificate = new Certificate();
//_certificate.readBytes(in);
_certificate = Certificate.create(in);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read cert: " + _certificate);
}

View File

@ -161,7 +161,8 @@ public class I2NPMessageReader {
cancelRunner();
}
}
if (!_doRun) {
// ??? unused
if (_stayAlive && !_doRun) {
// pause .5 secs when we're paused
try { Thread.sleep(500); } catch (InterruptedException ie) {}
}

View File

@ -58,7 +58,16 @@ public class JobQueue {
private final Object _jobLock;
/** how many when we go parallel */
private static final int RUNNERS = 4;
private static final int RUNNERS;
static {
long maxMemory = Runtime.getRuntime().maxMemory();
if (maxMemory < 64*1024*1024)
RUNNERS = 3;
else if (maxMemory < 256*1024*1024)
RUNNERS = 4;
else
RUNNERS = 5;
}
/** default max # job queue runners operating */
private final static int DEFAULT_MAX_RUNNERS = 1;
@ -395,10 +404,8 @@ public class JobQueue {
for (int i = _queueRunners.size(); i < numThreads; i++) {
JobQueueRunner runner = new JobQueueRunner(_context, i);
_queueRunners.put(Integer.valueOf(i), runner);
Thread t = new I2PThread(runner);
t.setName("JobQueue"+(_runnerId++));
Thread t = new I2PThread(runner, "JobQueue " + (++_runnerId) + '/' + numThreads, false);
//t.setPriority(I2PThread.MAX_PRIORITY-1);
t.setDaemon(false);
t.start();
}
} else if (_queueRunners.size() == numThreads) {

View File

@ -1281,11 +1281,7 @@ public class Router {
*/
private void beginMarkingLiveliness() {
File f = getPingFile();
// not an I2PThread for context creation issues
Thread t = new Thread(new MarkLiveliness(_context, this, f));
t.setName("Mark router liveliness");
t.setDaemon(true);
t.start();
SimpleScheduler.getInstance().addPeriodicEvent(new MarkLiveliness(this, f), 0, LIVELINESS_DELAY);
}
public static final String PROP_BANDWIDTH_SHARE_PERCENTAGE = "router.sharePercentage";
@ -1523,22 +1519,24 @@ private static class UpdateRoutingKeyModifierJob extends JobImpl {
}
}
private static class MarkLiveliness implements Runnable {
private RouterContext _context;
/**
* Write a timestamp to the ping file where the wrapper can see it
*/
private static class MarkLiveliness implements SimpleTimer.TimedEvent {
private Router _router;
private File _pingFile;
public MarkLiveliness(RouterContext ctx, Router router, File pingFile) {
_context = ctx;
public MarkLiveliness(Router router, File pingFile) {
_router = router;
_pingFile = pingFile;
}
public void run() {
_pingFile.deleteOnExit();
do {
}
public void timeReached() {
if (_router.isAlive())
ping();
try { Thread.sleep(Router.LIVELINESS_DELAY); } catch (InterruptedException ie) {}
} while (_router.isAlive());
_pingFile.delete();
else
_pingFile.delete();
}
private void ping() {

View File

@ -6,6 +6,7 @@ import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.data.Hash;
import net.i2p.internal.InternalClientManager;
import net.i2p.router.client.ClientManagerFacadeImpl;
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.router.peermanager.Calculator;
@ -34,7 +35,7 @@ import net.i2p.util.KeyRing;
*/
public class RouterContext extends I2PAppContext {
private Router _router;
private ClientManagerFacade _clientManagerFacade;
private ClientManagerFacadeImpl _clientManagerFacade;
private ClientMessagePool _clientMessagePool;
private JobQueue _jobQueue;
private InNetMessagePool _inNetMessagePool;
@ -106,10 +107,12 @@ public class RouterContext extends I2PAppContext {
}
public void initAll() {
if ("false".equals(getProperty("i2p.dummyClientFacade", "false")))
_clientManagerFacade = new ClientManagerFacadeImpl(this);
else
_clientManagerFacade = new DummyClientManagerFacade(this);
if (getBooleanProperty("i2p.dummyClientFacade"))
System.err.println("i2p.dummpClientFacade currently unsupported");
_clientManagerFacade = new ClientManagerFacadeImpl(this);
// removed since it doesn't implement InternalClientManager for now
//else
// _clientManagerFacade = new DummyClientManagerFacade(this);
_clientMessagePool = new ClientMessagePool(this);
_jobQueue = new JobQueue(this);
_inNetMessagePool = new InNetMessagePool(this);
@ -395,4 +398,13 @@ public class RouterContext extends I2PAppContext {
public boolean isRouterContext() {
return true;
}
/**
* Use this to connect to the router in the same JVM.
* @return the client manager
* @since 0.8.3
*/
public InternalClientManager internalClientManager() {
return _clientManagerFacade;
}
}

View File

@ -50,9 +50,9 @@ import net.i2p.util.SimpleTimer;
*
* @author jrandom
*/
public class ClientConnectionRunner {
class ClientConnectionRunner {
private Log _log;
private RouterContext _context;
protected final RouterContext _context;
private ClientManager _manager;
/** socket for this particular peer connection */
private Socket _socket;
@ -71,7 +71,7 @@ public class ClientConnectionRunner {
/** set of messageIds created but not yet ACCEPTED */
private Set<MessageId> _acceptedPending;
/** thingy that does stuff */
private I2CPMessageReader _reader;
protected I2CPMessageReader _reader;
/** just for this destination */
private SessionKeyManager _sessionKeyManager;
/**
@ -109,7 +109,7 @@ public class ClientConnectionRunner {
*/
public void startRunning() {
try {
_reader = new I2CPMessageReader(_socket.getInputStream(), new ClientMessageEventListener(_context, this));
_reader = new I2CPMessageReader(_socket.getInputStream(), new ClientMessageEventListener(_context, this, true));
_writer = new ClientWriterRunner(_context, this);
I2PThread t = new I2PThread(_writer);
t.setName("I2CP Writer " + ++__id);
@ -469,18 +469,8 @@ public class ClientConnectionRunner {
_log.warn("Error sending I2CP message - client went away", eofe);
stopRunning();
} catch (IOException ioe) {
// only warn if client went away
int level;
String emsg;
if (ioe.getMessage() != null && ioe.getMessage().startsWith("Pipe closed")) {
level = Log.WARN;
emsg = "Error sending I2CP message - client went away";
} else {
level = Log.ERROR;
emsg = "IO Error sending I2CP message to client";
}
if (_log.shouldLog(level))
_log.log(level, emsg, ioe);
if (_log.shouldLog(Log.ERROR))
_log.error("IO Error sending I2CP message to client", ioe);
stopRunning();
} catch (Throwable t) {
_log.log(Log.CRIT, "Unhandled exception sending I2CP message to client", t);

View File

@ -24,13 +24,13 @@ import net.i2p.util.Log;
*
* @author jrandom
*/
public class ClientListenerRunner implements Runnable {
protected Log _log;
protected RouterContext _context;
protected ClientManager _manager;
class ClientListenerRunner implements Runnable {
protected final Log _log;
protected final RouterContext _context;
protected final ClientManager _manager;
protected ServerSocket _socket;
protected int _port;
private boolean _bindAllInterfaces;
protected final int _port;
protected final boolean _bindAllInterfaces;
protected boolean _running;
protected boolean _listening;
@ -38,18 +38,33 @@ public class ClientListenerRunner implements Runnable {
public ClientListenerRunner(RouterContext context, ClientManager manager, int port) {
_context = context;
_log = _context.logManager().getLog(ClientListenerRunner.class);
_log = _context.logManager().getLog(getClass());
_manager = manager;
_port = port;
String val = context.getProperty(BIND_ALL_INTERFACES);
_bindAllInterfaces = Boolean.valueOf(val).booleanValue();
_bindAllInterfaces = context.getBooleanProperty(BIND_ALL_INTERFACES);
}
public void setPort(int port) { _port = port; }
public int getPort() { return _port; }
public boolean isListening() { return _running && _listening; }
/**
* Get a ServerSocket.
* Split out so it can be overridden for SSL.
* @since 0.8.3
*/
protected ServerSocket getServerSocket() throws IOException {
if (_bindAllInterfaces) {
if (_log.shouldLog(Log.INFO))
_log.info("Listening on port " + _port + " on all interfaces");
return new ServerSocket(_port);
} else {
String listenInterface = _context.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_HOST,
ClientManagerFacadeImpl.DEFAULT_HOST);
if (_log.shouldLog(Log.INFO))
_log.info("Listening on port " + _port + " of the specific interface: " + listenInterface);
return new ServerSocket(_port, 0, InetAddress.getByName(listenInterface));
}
}
/**
* Start up the socket listener, listens for connections, and
* fires those connections off via {@link #runConnection runConnection}.
@ -62,18 +77,7 @@ public class ClientListenerRunner implements Runnable {
int curDelay = 1000;
while (_running) {
try {
if (_bindAllInterfaces) {
if (_log.shouldLog(Log.INFO))
_log.info("Listening on port " + _port + " on all interfaces");
_socket = new ServerSocket(_port);
} else {
String listenInterface = _context.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_HOST,
ClientManagerFacadeImpl.DEFAULT_HOST);
if (_log.shouldLog(Log.INFO))
_log.info("Listening on port " + _port + " of the specific interface: " + listenInterface);
_socket = new ServerSocket(_port, 0, InetAddress.getByName(listenInterface));
}
_socket = getServerSocket();
if (_log.shouldLog(Log.DEBUG))
_log.debug("ServerSocket created, before accept: " + _socket);
@ -131,7 +135,8 @@ public class ClientListenerRunner implements Runnable {
}
/** give the i2cp client 5 seconds to show that they're really i2cp clients */
private final static int CONNECT_TIMEOUT = 5*1000;
protected final static int CONNECT_TIMEOUT = 5*1000;
private final static int LOOP_DELAY = 250;
/**
* Verify the first byte.
@ -141,16 +146,17 @@ public class ClientListenerRunner implements Runnable {
protected boolean validate(Socket socket) {
try {
InputStream is = socket.getInputStream();
for (int i = 0; i < 20; i++) {
for (int i = 0; i < CONNECT_TIMEOUT / LOOP_DELAY; i++) {
if (is.available() > 0)
return is.read() == I2PClient.PROTOCOL_BYTE;
try { Thread.sleep(250); } catch (InterruptedException ie) {}
try { Thread.sleep(LOOP_DELAY); } catch (InterruptedException ie) {}
}
} catch (IOException ioe) {}
if (_log.shouldLog(Log.WARN))
_log.warn("Peer did not authenticate themselves as I2CP quickly enough, dropping");
return false;
}
/**
* Handle the connection by passing it off to a {@link ClientConnectionRunner ClientConnectionRunner}
*

View File

@ -15,7 +15,9 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import net.i2p.client.I2PSessionException;
import net.i2p.crypto.SessionKeyManager;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
@ -23,8 +25,10 @@ import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
import net.i2p.data.Payload;
import net.i2p.data.TunnelId;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.SessionConfig;
import net.i2p.internal.I2CPMessageQueue;
import net.i2p.router.ClientManagerFacade;
import net.i2p.router.ClientMessage;
import net.i2p.router.Job;
@ -39,13 +43,18 @@ import net.i2p.util.Log;
*
* @author jrandom
*/
public class ClientManager {
private Log _log;
class ClientManager {
private final Log _log;
private ClientListenerRunner _listener;
private ClientListenerRunner _internalListener;
private final HashMap<Destination, ClientConnectionRunner> _runners; // Destination --> ClientConnectionRunner
private final Set<ClientConnectionRunner> _pendingRunners; // ClientConnectionRunner for clients w/out a Dest yet
private RouterContext _ctx;
private final RouterContext _ctx;
private boolean _isStarted;
/** Disable external interface, allow internal clients only @since 0.8.3 */
private static final String PROP_DISABLE_EXTERNAL = "i2cp.disableInterface";
/** SSL interface (only) @since 0.8.3 */
private static final String PROP_ENABLE_SSL = "i2cp.SSL";
/** ms to wait before rechecking for inbound messages to deliver to clients */
private final static int INBOUND_POLL_INTERVAL = 300;
@ -53,10 +62,10 @@ public class ClientManager {
public ClientManager(RouterContext context, int port) {
_ctx = context;
_log = context.logManager().getLog(ClientManager.class);
_ctx.statManager().createRateStat("client.receiveMessageSize",
"How large are messages received by the client?",
"ClientMessages",
new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
//_ctx.statManager().createRateStat("client.receiveMessageSize",
// "How large are messages received by the client?",
// "ClientMessages",
// new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
_runners = new HashMap();
_pendingRunners = new HashSet();
startListeners(port);
@ -64,16 +73,16 @@ public class ClientManager {
/** Todo: Start a 3rd listener for IPV6? */
private void startListeners(int port) {
_listener = new ClientListenerRunner(_ctx, this, port);
Thread t = new I2PThread(_listener);
t.setName("ClientListener:" + port);
t.setDaemon(true);
t.start();
_internalListener = new InternalClientListenerRunner(_ctx, this, port);
t = new I2PThread(_internalListener);
t.setName("ClientListener:" + port + "-i");
t.setDaemon(true);
t.start();
if (!_ctx.getBooleanProperty(PROP_DISABLE_EXTERNAL)) {
// there's no option to start both an SSL and non-SSL listener
if (_ctx.getBooleanProperty(PROP_ENABLE_SSL))
_listener = new SSLClientListenerRunner(_ctx, this, port);
else
_listener = new ClientListenerRunner(_ctx, this, port);
Thread t = new I2PThread(_listener, "ClientListener:" + port, true);
t.start();
}
_isStarted = true;
}
public void restart() {
@ -95,9 +104,10 @@ public class ClientManager {
}
public void shutdown() {
_isStarted = false;
_log.info("Shutting down the ClientManager");
_listener.stopListening();
_internalListener.stopListening();
if (_listener != null)
_listener.stopListening();
Set<ClientConnectionRunner> runners = new HashSet();
synchronized (_runners) {
for (Iterator<ClientConnectionRunner> iter = _runners.values().iterator(); iter.hasNext();) {
@ -117,7 +127,28 @@ public class ClientManager {
}
}
public boolean isAlive() { return _listener.isListening(); }
/**
* The InternalClientManager interface.
* Connects to the router, receiving a message queue to talk to the router with.
* @throws I2PSessionException if the router isn't ready
* @since 0.8.3
*/
public I2CPMessageQueue internalConnect() throws I2PSessionException {
if (!_isStarted)
throw new I2PSessionException("Router client manager is shut down");
// for now we make these unlimited size
LinkedBlockingQueue<I2CPMessage> in = new LinkedBlockingQueue();
LinkedBlockingQueue<I2CPMessage> out = new LinkedBlockingQueue();
I2CPMessageQueue myQueue = new I2CPMessageQueueImpl(in, out);
I2CPMessageQueue hisQueue = new I2CPMessageQueueImpl(out, in);
ClientConnectionRunner runner = new QueuedClientConnectionRunner(_ctx, this, myQueue);
registerConnection(runner);
return hisQueue;
}
public boolean isAlive() {
return _isStarted && (_listener == null || _listener.isListening());
}
public void registerConnection(ClientConnectionRunner runner) {
synchronized (_pendingRunners) {
@ -469,8 +500,8 @@ public class ClientManager {
runner = getRunner(_msg.getDestinationHash());
if (runner != null) {
_ctx.statManager().addRateData("client.receiveMessageSize",
_msg.getPayload().getSize(), 0);
//_ctx.statManager().addRateData("client.receiveMessageSize",
// _msg.getPayload().getSize(), 0);
runner.receiveMessage(_msg.getDestination(), null, _msg.getPayload());
} else {
// no client connection...

Some files were not shown because too many files have changed in this diff Show More