diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index 75a276aa00a939c8b7ae6ba154e742fda950ef3d..293703ac9c9786580ad4a1e6c6b691099d95d2e8 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -43,13 +43,13 @@ import java.io.OutputStream; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; +import java.util.concurrent.CopyOnWriteArraySet; import net.i2p.I2PAppContext; import net.i2p.I2PException; @@ -99,7 +99,7 @@ public class I2PTunnel implements Logging, EventDispatcher { private final List tasks = new ArrayList(); private int next_task_id = 1; - private final Set listeners = new HashSet(); + private final Set listeners = new CopyOnWriteArraySet(); public static void main(String[] args) throws IOException { new I2PTunnel(args); @@ -118,8 +118,8 @@ public class I2PTunnel implements Logging, EventDispatcher { _tunnelId = ++__tunnelId; _log = _context.logManager().getLog(I2PTunnel.class); _event = new EventDispatcherImpl(); - Properties p = new Properties(); - p.putAll(System.getProperties()); + // as of 0.8.4, include context properties + Properties p = _context.getProperties(); _clientOptions = p; _sessions = new ArrayList(1); @@ -1626,16 +1626,12 @@ public class I2PTunnel implements Logging, EventDispatcher { public void addConnectionEventListener(ConnectionEventListener lsnr) { if (lsnr == null) return; - synchronized (listeners) { - listeners.add(lsnr); - } + listeners.add(lsnr); } public void removeConnectionEventListener(ConnectionEventListener lsnr) { if (lsnr == null) return; - synchronized (listeners) { - listeners.remove(lsnr); - } + listeners.remove(lsnr); } private String getPrefix() { return "[" + _tunnelId + "]: "; } @@ -1649,12 +1645,10 @@ public class I2PTunnel implements Logging, EventDispatcher { */ void routerDisconnected() { _log.error(getPrefix() + "Router disconnected - firing notification events"); - synchronized (listeners) { for (Iterator iter = listeners.iterator(); iter.hasNext();) { ConnectionEventListener lsnr = (ConnectionEventListener) iter.next(); if (lsnr != null) lsnr.routerDisconnected(); } - } } /** diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java index 618b035f7b68cf8bb7838cbb67f897d116c902d8..d021131edda2ccba7742e4d3d8b1f3f7aa51bb70 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java @@ -310,7 +310,7 @@ public class TunnelControllerGroup { synchronized (_sessions) { Set<TunnelController> owners = _sessions.get(session); if (owners == null) { - owners = new HashSet(1); + owners = new HashSet(2); _sessions.put(session, owners); } owners.add(controller); diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/ByteCollector.java b/apps/ministreaming/java/src/net/i2p/client/streaming/ByteCollector.java index 39249b449266897fadcefb03b2b82299987f1827..38d96ddb742a04edbd344abfb3fe48c65961dcf3 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/ByteCollector.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/ByteCollector.java @@ -4,6 +4,7 @@ package net.i2p.client.streaming; * Like a StringBuffer, but for bytes. This class is not internally synchronized, * so care should be taken when using in a multithreaded environment. * + * @deprecated Only used by deprecated I2PSocketImpl */ class ByteCollector { byte[] contents; @@ -294,4 +295,4 @@ class ByteCollector { size = 0; return bb; } -} \ No newline at end of file +} diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java index 02bef1a0aebba569c7419491f6ab6ce1f0e384d9..1651d40d2cbbe2adee60c5bf5a66e1d53ea404cd 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java @@ -4,9 +4,12 @@ */ package net.i2p.client.streaming; +import java.io.IOException; import java.io.InterruptedIOException; import java.net.ConnectException; import java.net.NoRouteToHostException; +import java.net.ServerSocket; +import java.net.Socket; import java.util.Properties; import java.util.Set; @@ -84,7 +87,7 @@ public interface I2PSocketManager { * * @return a set of currently connected I2PSockets */ - public Set listSockets(); + public Set<I2PSocket> listSockets(); /** * Ping the specified peer, returning true if they replied to the ping within @@ -107,4 +110,25 @@ public interface I2PSocketManager { public static interface DisconnectListener { public void sessionDisconnected(); } + + /** + * Like getServerSocket but returns a real ServerSocket for easier porting of apps. + * @since 0.8.4 + */ + public ServerSocket getStandardServerSocket() throws IOException; + + /** + * Like connect() but returns a real Socket, and throws only IOE, + * for easier porting of apps. + * @since 0.8.4 + */ + public Socket connectToSocket(Destination peer) throws IOException; + + /** + * Like connect() but returns a real Socket, and throws only IOE, + * for easier porting of apps. + * @param timeout ms if > 0, forces blocking (disables connectDelay) + * @since 0.8.4 + */ + public Socket connectToSocket(Destination peer, int timeout) throws IOException; } diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java index fb2589b52bdc5ec2ed1d0e2368e17f13845c3099..e0a5b022fec16a1e682a02924de8d39903cd0f31 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java @@ -10,6 +10,8 @@ import java.io.InterruptedIOException; import java.io.UnsupportedEncodingException; import java.net.ConnectException; import java.net.NoRouteToHostException; +import java.net.ServerSocket; +import java.net.Socket; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -461,6 +463,14 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener { return _serverSocket; } + /** + * @throws UnsupportedOperationException + * @since 0.8.4 + */ + public ServerSocket getStandardServerSocket() { + throw new UnsupportedOperationException(); + } + /** * Create a new connected socket (block until the socket is created) * @@ -601,6 +611,22 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener { return connect(peer, null); } + /** + * @throws UnsupportedOperationException + * @since 0.8.4 + */ + public Socket connectToSocket(Destination peer) { + throw new UnsupportedOperationException(); + } + + /** + * @throws UnsupportedOperationException + * @since 0.8.4 + */ + public Socket connectToSocket(Destination peer, int timeout) { + throw new UnsupportedOperationException(); + } + /** * Destroy the socket manager, freeing all the associated resources. This * method will block untill all the managed sockets are closed. @@ -660,7 +686,7 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener { * Retrieve a set of currently connected I2PSockets, either initiated locally or remotely. * */ - public Set listSockets() { + public Set<I2PSocket> listSockets() { Set<I2PSocket> sockets = new HashSet<I2PSocket>(8); synchronized (lock) { sockets.addAll(_inSockets.values()); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java index 9388d06d2a45fc2ed5669b755d5f91e8ce9197fc..6dd3aa987ada615be2a89376107b6fe631e41f79 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java @@ -100,7 +100,7 @@ public class ConfigNetHelper extends HelperBase { } public String getTcpAutoIPChecked(int mode) { - boolean enabled = TransportManager.enableNTCP(_context); + boolean enabled = TransportManager.isNTCPEnabled(_context); String hostname = _context.getProperty(PROP_I2NP_NTCP_HOSTNAME); boolean specified = hostname != null && hostname.length() > 0; String auto = _context.getProperty(PROP_I2NP_NTCP_AUTO_IP, "false"); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java index 8daa63dac157465108d12872375bb5f00c01d022..ada49d0aa737bd4ccbf3a9fd4a4a472d74332ebb 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java @@ -25,6 +25,7 @@ import org.mortbay.http.DigestAuthenticator; import org.mortbay.http.HashUserRealm; import org.mortbay.http.NCSARequestLog; import org.mortbay.http.SecurityConstraint; +import org.mortbay.http.SocketListener; import org.mortbay.http.SslListener; import org.mortbay.http.handler.SecurityHandler; import org.mortbay.jetty.Server; @@ -185,11 +186,21 @@ public class RouterConsoleRunner { 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); + //if (host.indexOf(":") >= 0) // IPV6 - requires patched Jetty 5 + // _server.addListener('[' + host + "]:" + _listenPort); + //else + // _server.addListener(host + ':' + _listenPort); + Integer lport = Integer.parseInt(_listenPort); + InetAddrPort iap = new InetAddrPort(host, lport); + SocketListener lsnr = new SocketListener(iap); + lsnr.setMinThreads(1); // default 2 + lsnr.setMaxThreads(24); // default 256 + lsnr.setMaxIdleTimeMs(90*1000); // default 10 sec + lsnr.setName("ConsoleSocket"); // all with same name will use the same thread pool + _server.addListener(lsnr); boundAddresses++; + } catch (NumberFormatException nfe) { + System.err.println("Unable to bind routerconsole to " + host + " port " + _listenPort + ' ' + nfe); } 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); } @@ -221,6 +232,10 @@ public class RouterConsoleRunner { 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")); + ssll.setMinThreads(1); // default 2 + ssll.setMaxThreads(24); // default 256 + ssll.setMaxIdleTimeMs(90*1000); // default 10 sec + ssll.setName("ConsoleSocket"); // all with same name will use the same thread pool _server.addListener(ssll); boundAddresses++; } catch (Exception e) { // probably no exceptions at this point diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java index 69ace81a2833f19ebd84fc25f6caa172e69ab223..10ff38856435ed20d8da256bcf6b3b67028b5c12 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java @@ -40,7 +40,7 @@ class ConnectionManager { private int _maxConcurrentStreams; private ConnectionOptions _defaultOptions; private volatile int _numWaiting; - private long SoTimeout; + private long _soTimeout; private ConnThrottler _minuteThrottler; private ConnThrottler _hourThrottler; private ConnThrottler _dayThrottler; @@ -64,7 +64,7 @@ class ConnectionManager { _allowIncoming = false; _numWaiting = 0; /** Socket timeout for accept() */ - SoTimeout = -1; + _soTimeout = -1; _context.statManager().createRateStat("stream.con.lifetimeMessagesSent", "How many messages do we send on a stream?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 }); _context.statManager().createRateStat("stream.con.lifetimeMessagesReceived", "How many messages do we receive on a stream?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 }); @@ -97,16 +97,16 @@ class ConnectionManager { * Set the socket accept() timeout. * @param x */ - public void MsetSoTimeout(long x) { - SoTimeout = x; + public void setSoTimeout(long x) { + _soTimeout = x; } /** * Get the socket accept() timeout. * @return accept timeout in ms. */ - public long MgetSoTimeout() { - return SoTimeout; + public long getSoTimeout() { + return _soTimeout; } public void setAllowIncomingConnections(boolean allow) { diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java index acb58fe15fc7025f25bdf6882de2981b16706244..71e1dd3ac7e172e2ad5238e206006105939459ed 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java @@ -26,11 +26,11 @@ class I2PServerSocketFull implements I2PServerSocket { } public long getSoTimeout() { - return _socketManager.getConnectionManager().MgetSoTimeout(); + return _socketManager.getConnectionManager().getSoTimeout(); } public void setSoTimeout(long x) { - _socketManager.getConnectionManager().MsetSoTimeout(x); + _socketManager.getConnectionManager().setSoTimeout(x); } /** * Close the connection. diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketFull.java index dfcfacfa1f55f6e188c70cf8f9f42d8d676a4926..f8dbe74ea691d79241461b2dac53569a94bffebd 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketFull.java @@ -46,6 +46,10 @@ class I2PSocketFull implements I2PSocket { Connection getConnection() { return _connection; } + /** + * Warning, may return null instead of throwing IOE, + * which is not what the interface says. + */ public InputStream getInputStream() { Connection c = _connection; if (c != null) @@ -62,6 +66,10 @@ class I2PSocketFull implements I2PSocket { return null; } + /** + * Warning, may return null instead of throwing IOE, + * which is not what the interface says. + */ public OutputStream getOutputStream() throws IOException { Connection c = _connection; if (c != null) diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java index 03abafdda1163d6b9462696f1179a8c4355426a6..573354e8981f88840f85bee0f5aeda8082a2361a 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java @@ -1,6 +1,9 @@ package net.i2p.client.streaming; +import java.io.IOException; import java.net.NoRouteToHostException; +import java.net.ServerSocket; +import java.net.Socket; import java.net.SocketTimeoutException; import java.util.HashSet; import java.util.Iterator; @@ -30,6 +33,7 @@ public class I2PSocketManagerFull implements I2PSocketManager { private Log _log; private I2PSession _session; private I2PServerSocketFull _serverSocket; + private StandardServerSocket _realServerSocket; private ConnectionOptions _defaultOptions; private long _acceptTimeout; private String _name; @@ -44,8 +48,6 @@ public class I2PSocketManagerFull implements I2PSocketManager { private static final long ACCEPT_TIMEOUT_DEFAULT = 5*1000; public I2PSocketManagerFull() { - _context = null; - _session = null; } /** @@ -120,7 +122,7 @@ public class I2PSocketManagerFull implements I2PSocketManager { */ public I2PSocket receiveSocket() throws I2PException, SocketTimeoutException { verifySession(); - Connection con = _connectionManager.getConnectionHandler().accept(_connectionManager.MgetSoTimeout()); + Connection con = _connectionManager.getConnectionHandler().accept(_connectionManager.getSoTimeout()); if(_log.shouldLog(Log.DEBUG)) { _log.debug("receiveSocket() called: " + con); } @@ -129,7 +131,7 @@ public class I2PSocketManagerFull implements I2PSocketManager { con.setSocket(sock); return sock; } else { - if(_connectionManager.MgetSoTimeout() == -1) { + if(_connectionManager.getSoTimeout() == -1) { return null; } throw new SocketTimeoutException("I2PSocket timed out"); @@ -171,6 +173,17 @@ public class I2PSocketManagerFull implements I2PSocketManager { return _serverSocket; } + /** + * Like getServerSocket but returns a real ServerSocket for easier porting of apps. + * @since 0.8.4 + */ + public synchronized ServerSocket getStandardServerSocket() throws IOException { + if (_realServerSocket == null) + _realServerSocket = new StandardServerSocket(_serverSocket); + _connectionManager.setAllowIncomingConnections(true); + return _realServerSocket; + } + private void verifySession() throws I2PException { if (!_connectionManager.getSession().isClosed()) return; @@ -185,7 +198,7 @@ public class I2PSocketManagerFull implements I2PSocketManager { * this data will be bundled in the SYN packet. * * @param peer Destination to connect to - * @param options I2P socket options to be used for connecting + * @param options I2P socket options to be used for connecting, may be null * * @return I2PSocket if successful * @throws NoRouteToHostException if the peer is not found or not reachable @@ -235,6 +248,45 @@ public class I2PSocketManagerFull implements I2PSocketManager { return connect(peer, _defaultOptions); } + /** + * Like connect() but returns a real Socket, and throws only IOE, + * for easier porting of apps. + * @since 0.8.4 + */ + public Socket connectToSocket(Destination peer) throws IOException { + return connectToSocket(peer, _defaultOptions); + } + + /** + * Like connect() but returns a real Socket, and throws only IOE, + * for easier porting of apps. + * @param timeout ms if > 0, forces blocking (disables connectDelay) + * @since 0.8.4 + */ + public Socket connectToSocket(Destination peer, int timeout) throws IOException { + ConnectionOptions opts = new ConnectionOptions(_defaultOptions); + opts.setConnectTimeout(timeout); + if (timeout > 0) + opts.setConnectDelay(-1); + return connectToSocket(peer, opts); + } + + /** + * Like connect() but returns a real Socket, and throws only IOE, + * for easier porting of apps. + * @param options may be null + * @since 0.8.4 + */ + private Socket connectToSocket(Destination peer, I2PSocketOptions options) throws IOException { + try { + I2PSocket sock = connect(peer, options); + return new StandardSocket(sock); + } catch (I2PException i2pe) { + // fixme in 1.6 change to cause + throw new IOException(i2pe.toString()); + } + } + /** * Destroy the socket manager, freeing all the associated resources. This * method will block untill all the managed sockets are closed. @@ -259,11 +311,10 @@ public class I2PSocketManagerFull implements I2PSocketManager { * * @return set of currently connected I2PSockets */ - public Set listSockets() { - Set connections = _connectionManager.listConnections(); - Set rv = new HashSet(connections.size()); - for (Iterator iter = connections.iterator(); iter.hasNext(); ) { - Connection con = (Connection)iter.next(); + public Set<I2PSocket> listSockets() { + Set<Connection> connections = _connectionManager.listConnections(); + Set<I2PSocket> rv = new HashSet(connections.size()); + for (Connection con : connections) { if (con.getSocket() != null) rv.add(con.getSocket()); } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java index 4097c568f2c0ab6f918964e9dc121e8422f9ce93..a94b1a4da2c1bac8ac8781abfe99413ddc7c5fe5 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java @@ -2,13 +2,13 @@ package net.i2p.client.streaming; import java.util.Iterator; import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; import net.i2p.I2PAppContext; import net.i2p.client.I2PSession; import net.i2p.client.I2PSessionException; import net.i2p.client.I2PSessionListener; import net.i2p.util.Log; -import net.i2p.util.ConcurrentHashSet; /** * Receive raw information from the I2PSession and turn it into @@ -24,7 +24,7 @@ class MessageHandler implements I2PSessionListener { public MessageHandler(I2PAppContext ctx, ConnectionManager mgr) { _manager = mgr; _context = ctx; - _listeners = new ConcurrentHashSet(1); + _listeners = new CopyOnWriteArraySet(); _log = ctx.logManager().getLog(MessageHandler.class); _context.statManager().createRateStat("stream.packetReceiveFailure", "When do we fail to decrypt or otherwise receive a packet sent to us?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 }); } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/StandardServerSocket.java b/apps/streaming/java/src/net/i2p/client/streaming/StandardServerSocket.java new file mode 100644 index 0000000000000000000000000000000000000000..324d12687704dda3f139fb0c1a4e8580e06b9580 --- /dev/null +++ b/apps/streaming/java/src/net/i2p/client/streaming/StandardServerSocket.java @@ -0,0 +1,170 @@ +package net.i2p.client.streaming; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.channels.ServerSocketChannel; + +import net.i2p.I2PException; + +/** + * Bridge to I2PServerSocket. + * + * This extends ServerSocket to make porting apps easier. + * accept() returns a real Socket (a StandardSocket). + * accept() throws IOExceptions like ServerSockets do, rather than returning + * null or throwing I2PExceptions. + * + * StandardServerSockets are always bound. + * You may not create an unbound StandardServerSocket. + * Create this through the SocketManager. + * + * @author zzz + * @since 0.8.4 + */ +class StandardServerSocket extends ServerSocket { + private final I2PServerSocketFull _socket; + + /** + * Doesn't really throw IOE but super() does + */ + StandardServerSocket(I2PServerSocketFull socket) throws IOException { + _socket = socket; + } + + @Override + public Socket accept() throws IOException { + try { + I2PSocket sock = _socket.accept(); + if (sock == null) + throw new IOException("No socket"); + return new StandardSocket(sock); + } catch (I2PException i2pe) { + // fixme in 1.6 change to cause + throw new IOException(i2pe.toString()); + } + } + + /** + * @throws UnsupportedOperationException always + */ + @Override + public void bind(SocketAddress endpoint) { + throw new UnsupportedOperationException(); + } + + /** + * @throws UnsupportedOperationException always + */ + @Override + public void bind(SocketAddress endpoint, int backlog) { + throw new UnsupportedOperationException(); + } + + @Override + public void close() throws IOException { + if (isClosed()) + throw new IOException("Already closed"); + _socket.close(); + } + + /** + * @return null always + */ + @Override + public ServerSocketChannel getChannel() { + return null; + } + + /** + * @return null always + */ + @Override + public InetAddress getInetAddress() { + return null; + } + + /** + * @return -1 always + */ + @Override + public int getLocalPort() { + return -1; + } + + /** + * @return null always + */ + @Override + public SocketAddress getLocalSocketAddress() { + return null; + } + + @Override + public int getReceiveBufferSize() { + ConnectionOptions opts = (ConnectionOptions) ((I2PSocketManagerFull)_socket.getManager()).getDefaultOptions(); + if (opts == null) + return 64*1024; + return opts.getInboundBufferSize(); + } + + /** + * @return false always + */ + @Override + public boolean getReuseAddress() { + return false; + } + + @Override + public int getSoTimeout() { + return (int) _socket.getSoTimeout(); + } + + /** + * @return true always + */ + @Override + public boolean isBound() { + return true; + } + + @Override + public boolean isClosed() { + return ((I2PSocketManagerFull)_socket.getManager()).getConnectionManager().getAllowIncomingConnections(); + } + + /** + * Does nothing. + */ + @Override + public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { + } + + /** + * Does nothing. + */ + @Override + public void setReceiveBufferSize(int size) { + } + + /** + * Does nothing. + */ + @Override + public void setReuseAddress(boolean on) { + } + + @Override + public void setSoTimeout(int timeout) throws SocketException { + _socket.setSoTimeout(timeout); + } + + @Override + public String toString() { + return _socket.toString(); + } +} diff --git a/apps/streaming/java/src/net/i2p/client/streaming/StandardSocket.java b/apps/streaming/java/src/net/i2p/client/streaming/StandardSocket.java new file mode 100644 index 0000000000000000000000000000000000000000..6ba78bfd49eccb3341cd5ebf96da7c2d1fe32717 --- /dev/null +++ b/apps/streaming/java/src/net/i2p/client/streaming/StandardSocket.java @@ -0,0 +1,341 @@ +package net.i2p.client.streaming; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.channels.SocketChannel; + +import net.i2p.I2PException; + +/** + * Bridge to I2PSocket. + * + * This extends Socket to make porting apps easier. + * Methods throw IOExceptions like Sockets do, rather than returning + * null for some methods. + * + * StandardSockets are always bound, and always start out connected + * (unless connectDelay is > 0). + * You may not create an unbound StandardSocket. + * Create this through the SocketManager. + * + * Todo: Make public and add getPeerDestination() ? + * + * @author zzz + * @since 0.8.4 + */ +class StandardSocket extends Socket { + private final I2PSocket _socket; + + StandardSocket(I2PSocket socket) { + _socket = socket; + } + + /** + * @throws UnsupportedOperationException always + */ + @Override + public void bind(SocketAddress bindpoint) { + throw new UnsupportedOperationException(); + } + + @Override + public void close() throws IOException { + if (_socket.isClosed()) + throw new IOException("Already closed"); + _socket.close(); + } + + /** + * @throws UnsupportedOperationException always + */ + @Override + public void connect(SocketAddress endpoint) { + throw new UnsupportedOperationException(); + } + + /** + * @throws UnsupportedOperationException always + */ + @Override + public void connect(SocketAddress endpoint, int timeout) { + throw new UnsupportedOperationException(); + } + + /** + * @return null always + */ + @Override + public SocketChannel getChannel() { + return null; + } + + /** + * @return null always + */ + @Override + public InetAddress getInetAddress() { + return null; + } + + @Override + public InputStream getInputStream() throws IOException { + InputStream rv = _socket.getInputStream(); + if (rv != null) + return rv; + throw new IOException("No stream"); + } + + @Override + public boolean getKeepAlive() { + ConnectionOptions opts = (ConnectionOptions) _socket.getOptions(); + if (opts == null) + return false; + return opts.getInactivityAction() == ConnectionOptions.INACTIVITY_ACTION_SEND; + } + + /** + * @return null always + */ + @Override + public InetAddress getLocalAddress() { + return null; + } + + /** + * @return -1 always + */ + @Override + public int getLocalPort() { + return -1; + } + + /** + * @return null always + */ + @Override + public SocketAddress getLocalSocketAddress() { + return null; + } + + /** + * @return false always + */ + @Override + public boolean getOOBInline() { + return false; + } + + @Override + public OutputStream getOutputStream() throws IOException { + OutputStream rv = _socket.getOutputStream(); + if (rv != null) + return rv; + throw new IOException("No stream"); + } + + /** + * @return 0 always + */ + @Override + public int getPort() { + return 0; + } + + @Override + public int getReceiveBufferSize() { + ConnectionOptions opts = (ConnectionOptions) _socket.getOptions(); + if (opts == null) + return 64*1024; + return opts.getInboundBufferSize(); + } + + /** + * @throws UnsupportedOperationException always + */ + @Override + public SocketAddress getRemoteSocketAddress() { + throw new UnsupportedOperationException(); + } + + /** + * @return false always + */ + @Override + public boolean getReuseAddress() { + return false; + } + + @Override + public int getSendBufferSize() { + ConnectionOptions opts = (ConnectionOptions) _socket.getOptions(); + if (opts == null) + return 64*1024; + return opts.getInboundBufferSize(); + } + + @Override + public int getSoLinger() { + I2PSocketOptions opts = _socket.getOptions(); + if (opts == null) + return -1; + return -1; // fixme really? + } + + @Override + public int getSoTimeout() { + I2PSocketOptions opts = _socket.getOptions(); + if (opts == null) + return 0; + return (int) opts.getReadTimeout(); + } + + /** + * @return false always + */ + @Override + public boolean getTcpNoDelay() { + // No option yet. See ConnectionDataReceiver + return false; + } + + /** + * @return 0 always + */ + @Override + public int getTrafficClass() { + return 0; + } + + /** + * @return true always + */ + @Override + public boolean isBound() { + return true; + } + + @Override + public boolean isClosed() { + return _socket.isClosed(); + } + + @Override + public boolean isConnected() { + return !_socket.isClosed(); + } + + @Override + public boolean isInputShutdown() { + return _socket.isClosed(); + } + + @Override + public boolean isOutputShutdown() { + return _socket.isClosed(); + } + + /** + * @throws UnsupportedOperationException always + */ + @Override + public void sendUrgentData(int data) { + throw new UnsupportedOperationException(); + } + + @Override + public void setKeepAlive(boolean on) { + ConnectionOptions opts = (ConnectionOptions) _socket.getOptions(); + if (opts == null) + return; + if (on) + opts.setInactivityAction(ConnectionOptions.INACTIVITY_ACTION_SEND); + else + opts.setInactivityAction(ConnectionOptions.INACTIVITY_ACTION_NOOP); // DISCONNECT? + } + + /** + * @throws UnsupportedOperationException if on is true + */ + @Override + public void setOOBInline(boolean on) { + if (on) + throw new UnsupportedOperationException(); + } + + /** + * Does nothing. + */ + @Override + public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { + } + + /** + * Does nothing. + */ + @Override + public void setReceiveBufferSize(int size) { + } + + /** + * Does nothing. + */ + @Override + public void setReuseAddress(boolean on) { + } + + /** + * Does nothing. + */ + @Override + public void setSendBufferSize(int size) { + } + + /** + * Does nothing. + */ + @Override + public void setSoLinger(boolean on, int linger) { + } + + @Override + public void setSoTimeout(int timeout) throws SocketException { + I2PSocketOptions opts = _socket.getOptions(); + if (opts == null) + throw new SocketException("No options"); + opts.setReadTimeout(timeout); + } + + /** + * Does nothing. + */ + @Override + public void setTcpNoDelay(boolean on) { + } + + /** + * Does nothing. + */ + @Override + public void setTrafficClass(int tc) { + } + + @Override + public void shutdownInput() throws IOException { + close(); + } + + @Override + public void shutdownOutput() throws IOException { + close(); + } + + @Override + public String toString() { + return _socket.toString(); + } +} diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java index 5083865fa97ab5b6cd46ea99c0912070e8ada61a..d582109019a70d62fa145758a7a7c2380dcc4a96 100644 --- a/core/java/src/net/i2p/I2PAppContext.java +++ b/core/java/src/net/i2p/I2PAppContext.java @@ -65,7 +65,7 @@ import net.i2p.util.I2PProperties.I2PPropertyCallback; */ public class I2PAppContext { /** the context that components without explicit root are bound */ - protected static I2PAppContext _globalAppContext; + protected static volatile I2PAppContext _globalAppContext; protected I2PProperties _overrideProps; @@ -119,7 +119,8 @@ public class I2PAppContext { * */ public static I2PAppContext getGlobalContext() { - // skip the global lock + // skip the global lock - _gAC must be volatile + // http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html I2PAppContext rv = _globalAppContext; if (rv != null) return rv; @@ -476,6 +477,9 @@ public class I2PAppContext { * provided during the context construction, as well as the ones included in * System.getProperties. * + * WARNING - not overridden in RouterContext, doesn't contain router config settings, + * use getProperties() instead. + * * @return set of Strings containing the names of defined system properties */ public Set getPropertyNames() { @@ -485,6 +489,21 @@ public class I2PAppContext { return names; } + /** + * Access the configuration attributes of this context, listing the properties + * provided during the context construction, as well as the ones included in + * System.getProperties. + * + * @return new Properties with system and context properties + * @since 0.8.4 + */ + public Properties getProperties() { + Properties rv = new Properties(); + rv.putAll(System.getProperties()); + rv.putAll(_overrideProps); + return rv; + } + /** * Add a callback, which will fire upon changes in the property * given in the specific callback. @@ -767,7 +786,7 @@ public class I2PAppContext { * enable simulators to play with clock skew among different instances. * */ - public Clock clock() { // overridden in RouterContext + public Clock clock() { if (!_clockInitialized) initializeClock(); return _clock; diff --git a/core/java/src/net/i2p/client/I2CPMessageProducer.java b/core/java/src/net/i2p/client/I2CPMessageProducer.java index 0fa35d8cff10dc2904e2afe81fbc094d98be2d33..336c858cace3664d80ac0348d4024cd82a4df3d7 100644 --- a/core/java/src/net/i2p/client/I2CPMessageProducer.java +++ b/core/java/src/net/i2p/client/I2CPMessageProducer.java @@ -12,6 +12,8 @@ package net.i2p.client; import java.util.Date; import java.util.Properties; import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; import net.i2p.I2PAppContext; import net.i2p.data.DataFormatException; @@ -41,22 +43,51 @@ import net.i2p.util.Log; * @author jrandom */ class I2CPMessageProducer { - private final static Log _log = new Log(I2CPMessageProducer.class); + private final Log _log; private final I2PAppContext _context; - private int _sendBps; - private long _sendPeriodBytes; - private long _sendPeriodBeginTime; + private int _maxBytesPerSecond; + private volatile int _sendPeriodBytes; + private volatile long _sendPeriodBeginTime; + private final ReentrantLock _lock; + private static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond"; + /** see ConnectionOptions in streaming - MTU + streaming overhead + gzip overhead */ + private static final int TYP_SIZE = 1730 + 28 + 23; + private static final int MIN_RATE = 2 * TYP_SIZE; public I2CPMessageProducer(I2PAppContext context) { _context = context; - context.statManager().createRateStat("client.sendBpsRaw", "How fast we pump out I2CP data messages", "ClientMessages", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 }); + _log = context.logManager().getLog(I2CPMessageProducer.class); + _lock = new ReentrantLock(true); + context.statManager().createRateStat("client.sendThrottled", "Times waited for bandwidth", "ClientMessages", new long[] { 60*1000 }); + context.statManager().createRateStat("client.sendDropped", "Length of msg dropped waiting for bandwidth", "ClientMessages", new long[] { 60*1000 }); } + /** + * Update the bandwidth setting + * @since 0.8.4 + */ + public void updateBandwidth(I2PSessionImpl session) { + String max = session.getOptions().getProperty(PROP_MAX_BW); + if (max != null) { + try { + int iMax = Integer.parseInt(max); + if (iMax > 0) + // round up to next higher TYP_SIZE for efficiency, then add some fudge for small messages + _maxBytesPerSecond = 256 + Math.max(MIN_RATE, TYP_SIZE * ((iMax + TYP_SIZE - 1) / TYP_SIZE)); + else + _maxBytesPerSecond = 0; + } catch (NumberFormatException nfe) {} + } + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Setting " + _maxBytesPerSecond + " BPS max"); + } + /** * Send all the messages that a client needs to send to a router to establish * a new session. */ public void connect(I2PSessionImpl session) throws I2PSessionException { + updateBandwidth(session); CreateSessionMessage msg = new CreateSessionMessage(); SessionConfig cfg = new SessionConfig(session.getMyDestination()); cfg.setOptions(session.getOptions()); @@ -99,32 +130,135 @@ class I2CPMessageProducer { */ public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag, SessionKey key, Set tags, SessionKey newKey, long expires) throws I2PSessionException { + sendMessage(session, dest, nonce, payload, expires, 0); + } + + /** + * Package up and send the payload to the router for delivery + * @since 0.8.4 + */ + public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, + long expires, int flags) throws I2PSessionException { + + if (!updateBps(payload.length, expires)) + // drop the message... send fail notification? + return; SendMessageMessage msg; - if (expires > 0) { - msg = new SendMessageExpiresMessage(); - ((SendMessageExpiresMessage)msg).setExpiration(new Date(expires)); + if (expires > 0 || flags > 0) { + SendMessageExpiresMessage smsg = new SendMessageExpiresMessage(); + smsg.setExpiration(expires); + smsg.setFlags(flags); + msg = smsg; } else msg = new SendMessageMessage(); msg.setDestination(dest); msg.setSessionId(session.getSessionId()); msg.setNonce(nonce); - Payload data = createPayload(dest, payload, tag, key, tags, newKey); + Payload data = createPayload(dest, payload, null, null, null, null); msg.setPayload(data); session.sendMessage(msg); - updateBps(payload.length); } - private void updateBps(int len) { - long now = _context.clock().now(); - float period = ((float)now-_sendPeriodBeginTime)/1000f; - if (period >= 1f) { - // first term decays on slow transmission - _sendBps = (int)(((float)0.9f * (float)_sendBps) + ((float)0.1f*((float)_sendPeriodBytes)/period)); - _sendPeriodBytes = len; - _sendPeriodBeginTime = now; - _context.statManager().addRateData("client.sendBpsRaw", _sendBps, 0); - } else { - _sendPeriodBytes += len; + /** + * Super-simple bandwidth throttler. + * We only calculate on a one-second basis, so large messages + * (compared to the one-second limit) may exceed the limits. + * Tuned for streaming, may not work well for large datagrams. + * + * This does poorly with low rate limits since it doesn't credit + * bandwidth across two periods. So the limit is rounded up, + * and the min limit is set to 2x the typ size, above. + * + * Blocking so this could be very bad for retransmissions, + * as it could clog StreamingTimer. + * Waits are somewhat "fair" using ReentrantLock. + * While out-of-order transmission is acceptable, fairness + * reduces the chance of starvation. ReentrantLock does not + * guarantee in-order execution due to thread priority issues, + * so out-of-order may still occur. But shouldn't happen within + * the same thread anyway... Also note that small messages may + * go ahead of large ones that are waiting for the next window. + * Also, threads waiting a second time go to the back of the line. + * + * Since this is at the I2CP layer, it includes streaming overhead, + * streaming acks and retransmissions, + * gzip overhead (or "underhead" for compression), + * repliable datagram overhead, etc. + * However, it does not, of course, include the substantial overhead + * imposed by the router for the leaseset, tags, encryption, + * and fixed-size tunnel messages. + * + * @param expires if > 0, an expiration date + * @return true if we should send the message, false to drop it + */ + private boolean updateBps(int len, long expires) { + if (_maxBytesPerSecond <= 0) + return true; + //synchronized(this) { + _lock.lock(); + try { + int waitCount = 0; + while (true) { + long now = _context.clock().now(); + if (waitCount > 0 && expires > 0 && expires < now) { + // just say no to bufferbloat... drop the message right here + _context.statManager().addRateData("client.sendDropped", len, 0); + if (_log.shouldLog(Log.WARN)) + _log.warn("Dropping " + len + " byte msg expired in queue"); + return false; + } + + long period = now - _sendPeriodBeginTime; + if (period >= 2000) { + // start new period, always let it through no matter how big + _sendPeriodBytes = len; + _sendPeriodBeginTime = now; + if (_log.shouldLog(Log.DEBUG)) + _log.debug("New period after idle, " + len + " bytes"); + return true; + } + + if (period >= 1000) { + // start new period + // Allow burst within 2 sec, only advance window by 1 sec, and + // every other second give credit for unused bytes in previous period + if (_sendPeriodBytes > 0 && ((_sendPeriodBeginTime / 1000) & 0x01) == 0) + _sendPeriodBytes += len - _maxBytesPerSecond; + else + _sendPeriodBytes = len; + _sendPeriodBeginTime += 1000; + if (_log.shouldLog(Log.DEBUG)) + _log.debug("New period, " + len + " bytes"); + return true; + } + + if (_sendPeriodBytes + len <= _maxBytesPerSecond) { + // still bytes available in this period + _sendPeriodBytes += len; + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Sending " + len + ", Elapsed " + period + "ms, total " + _sendPeriodBytes + " bytes"); + return true; + } + + if (waitCount >= 2) { + // just say no to bufferbloat... drop the message right here + _context.statManager().addRateData("client.sendDropped", len, 0); + if (_log.shouldLog(Log.WARN)) + _log.warn("Dropping " + len + " byte msg after waiting " + waitCount + " times"); + return false; + } + + // wait until next period + _context.statManager().addRateData("client.sendThrottled", ++waitCount, 0); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Throttled " + len + " bytes, wait #" + waitCount + ' ' + (1000 - period) + "ms" /*, new Exception()*/); + try { + //this.wait(1000 - period); + _lock.newCondition().await(1000 - period, TimeUnit.MILLISECONDS); + } catch (InterruptedException ie) {} + } + } finally { + _lock.unlock(); } } diff --git a/core/java/src/net/i2p/client/I2PSession.java b/core/java/src/net/i2p/client/I2PSession.java index cd20cfc2d655ef86580e18c1faaf248421d5e33d..567c9e521dbfc5981c2e67407a38d35452ed40b6 100644 --- a/core/java/src/net/i2p/client/I2PSession.java +++ b/core/java/src/net/i2p/client/I2PSession.java @@ -9,6 +9,7 @@ package net.i2p.client; * */ +import java.util.Properties; import java.util.Set; import net.i2p.data.Destination; @@ -20,17 +21,20 @@ import net.i2p.data.SigningPrivateKey; /** * <p>Define the standard means of sending and receiving messages on the * I2P network by using the I2CP (the client protocol). This is done over a - * bidirectional TCP socket and never sends any private keys - all end to end - * encryption is done transparently within the client's I2PSession - * itself. Periodically the router will ask the client to authorize a new set of + * bidirectional TCP socket and never sends any private keys. + * + * End to end encryption in I2PSession was disabled in release 0.6. + * + * Periodically the router will ask the client to authorize a new set of * tunnels to be allocated to the client, which the client can accept by sending a * {@link net.i2p.data.LeaseSet} signed by the {@link net.i2p.data.Destination}. - * In addition, the router may on occation provide the client with an updated + * In addition, the router may on occasion provide the client with an updated * clock offset so that the client can stay in sync with the network (even if * the host computer's clock is off).</p> * */ public interface I2PSession { + /** Send a new message to the given destination, containing the specified * payload, returning true if the router feels confident that the message * was delivered. @@ -39,11 +43,18 @@ public interface I2PSession { * @return whether it was accepted by the router for delivery or not */ public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException; + public boolean sendMessage(Destination dest, byte[] payload, int offset, int size) throws I2PSessionException; - /** See I2PSessionMuxedImpl for details */ + + /** + * See I2PSessionMuxedImpl for proto/port details. + * @since 0.7.1 + */ public boolean sendMessage(Destination dest, byte[] payload, int proto, int fromport, int toport) throws I2PSessionException; /** + * End-to-End Crypto is disabled, tags and keys are ignored! + * * Like sendMessage above, except the key used and the tags sent are exposed to the * application. <p /> * @@ -61,25 +72,62 @@ public interface I2PSession { * * @param dest location to send the message * @param payload body of the message to be sent (unencrypted) - * @param keyUsed session key delivered to the destination for association with the tags sent. This is essentially + * @param keyUsed UNUSED, IGNORED. Session key delivered to the destination for association with the tags sent. This is essentially * an output parameter - keyUsed.getData() is ignored during this call, but after the call completes, * it will be filled with the bytes of the session key delivered. Typically the key delivered is the * same one as the key encrypted with, but not always. If this is null then the key data will not be * exposed. - * @param tagsSent set of tags delivered to the peer and associated with the keyUsed. This is also an output parameter - + * @param tagsSent UNUSED, IGNORED. Set of tags delivered to the peer and associated with the keyUsed. This is also an output parameter - * the contents of the set is ignored during the call, but afterwards it contains a set of SessionTag * objects that were sent along side the given keyUsed. */ public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException; + + /** + * End-to-End Crypto is disabled, tags and keys are ignored. + * @param keyUsed UNUSED, IGNORED. + * @param tagsSent UNUSED, IGNORED. + */ public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent) throws I2PSessionException; + + /** + * End-to-End Crypto is disabled, tags and keys are ignored. + * @param keyUsed UNUSED, IGNORED. + * @param tagsSent UNUSED, IGNORED. + * @since 0.7.1 + */ public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire) throws I2PSessionException; - /** See I2PSessionMuxedImpl for details */ + + /** + * See I2PSessionMuxedImpl for proto/port details. + * End-to-End Crypto is disabled, tags and keys are ignored. + * @param keyUsed UNUSED, IGNORED. + * @param tagsSent UNUSED, IGNORED. + * @since 0.7.1 + */ public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, int proto, int fromport, int toport) throws I2PSessionException; - /** See I2PSessionMuxedImpl for details */ + + /** + * See I2PSessionMuxedImpl for proto/port details. + * End-to-End Crypto is disabled, tags and keys are ignored. + * @param keyUsed UNUSED, IGNORED. + * @param tagsSent UNUSED, IGNORED. + * @since 0.7.1 + */ public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire, int proto, int fromport, int toport) throws I2PSessionException; + /** + * See I2PSessionMuxedImpl for proto/port details. + * End-to-End Crypto is disabled, tags and keys are ignored. + * @param keyUsed UNUSED, IGNORED. + * @param tagsSent UNUSED, IGNORED. + * @since 0.8.4 + */ + public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire, + int proto, int fromport, int toport, int flags) throws I2PSessionException; + /** Receive a message that the router has notified the client about, returning * the payload. * @param msgId message to fetch @@ -151,8 +199,16 @@ public interface I2PSession { */ public Destination lookupDest(Hash h, long maxWait) throws I2PSessionException; + /** + * Does not remove properties previously present but missing from this options parameter. + * @param options non-null + * @since 0.8.4 + */ + public void updateOptions(Properties options); + /** * Get the current bandwidth limits. Blocking. + * @since 0.8.3 */ public int[] bandwidthLimits() throws I2PSessionException; diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java index 8b4389e47da3a78db1799e995047e4516cc7bcc9..dc7e875d9cf4f6dd0798fbbb0bfb955b5244f4f5 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl.java @@ -221,20 +221,32 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa } } + /** save some memory, don't pass along the pointless properties */ private Properties filter(Properties options) { Properties rv = new Properties(); for (Iterator iter = options.keySet().iterator(); iter.hasNext();) { String key = (String) iter.next(); - String val = options.getProperty(key); - if (key.startsWith("java") || - key.startsWith("user") || - key.startsWith("os") || - key.startsWith("sun") || - key.startsWith("file") || - key.startsWith("line") || - key.startsWith("wrapper")) { + if (key.startsWith("java.") || + key.startsWith("user.") || + key.startsWith("os.") || + key.startsWith("sun.") || + key.startsWith("file.") || + key.equals("line.separator") || + key.equals("path.separator") || + key.equals("prng.buffers") || + key.equals("router.trustedUpdateKeys") || + key.startsWith("router.update") || + key.startsWith("routerconsole.") || + key.startsWith("time.") || + key.startsWith("stat.") || + key.startsWith("gnu.") || // gnu JVM + key.startsWith("net.i2p.router.web.") || // console nonces + key.startsWith("wrapper.")) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping property: " + key); - } else if ((key.length() > 255) || (val.length() > 255)) { + continue; + } + String val = options.getProperty(key); + if ((key.length() > 255) || (val.length() > 255)) { if (_log.shouldLog(Log.WARN)) _log.warn(getPrefix() + "Not passing on property [" + key @@ -247,6 +259,18 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa return rv; } + /** + * Update the tunnel and bandwidth settings + * @since 0.8.4 + */ + public void updateOptions(Properties options) { + _options.putAll(filter(options)); + _producer.updateBandwidth(this); + try { + _producer.updateTunnels(this, 0); + } catch (I2PSessionException ise) {} + } + void setLeaseSet(LeaseSet ls) { _leaseSet = ls; if (ls != null) { @@ -397,7 +421,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa * */ public byte[] receiveMessage(int msgId) throws I2PSessionException { - MessagePayloadMessage msg = _availableMessages.remove(new Long(msgId)); + MessagePayloadMessage msg = _availableMessages.remove(Long.valueOf(msgId)); if (msg == null) { _log.error("Receive message " + msgId + " had no matches"); return null; @@ -414,21 +438,6 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa _producer.reportAbuse(this, msgId, severity); } - /** - * Send the data to the destination. - * TODO: this currently always returns true, regardless of whether the message was - * delivered successfully. make this wait for at least ACCEPTED - * - */ - public abstract boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException; - - /** - * @param keyUsed unused - no end-to-end crypto - * @param tagsSent unused - no end-to-end crypto - */ - public abstract boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, - Set tagsSent) throws I2PSessionException; - public abstract void receiveStatus(int msgId, long nonce, int status); /****** no end-to-end crypto @@ -444,7 +453,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa * Recieve a payload message and let the app know its available */ public void addNewMessage(MessagePayloadMessage msg) { - Long mid = new Long(msg.getMessageId()); + Long mid = Long.valueOf(msg.getMessageId()); _availableMessages.put(mid, msg); long id = msg.getMessageId(); byte data[] = msg.getPayload().getUnencryptedData(); @@ -494,7 +503,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa public void available(long msgId, int size) { synchronized (AvailabilityNotifier.this) { - _pendingIds.add(new Long(msgId)); + _pendingIds.add(Long.valueOf(msgId)); _pendingSizes.add(Integer.valueOf(size)); AvailabilityNotifier.this.notifyAll(); } diff --git a/core/java/src/net/i2p/client/I2PSessionImpl2.java b/core/java/src/net/i2p/client/I2PSessionImpl2.java index f0000b68cb4302cdf079b01007ef7463f70b0743..3af551eaaf9c6fa96a7152f3f13c23e8f2696216 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl2.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl2.java @@ -130,6 +130,10 @@ class I2PSessionImpl2 extends I2PSessionImpl { int proto, int fromport, int toport) throws I2PSessionException { throw new IllegalArgumentException("Use MuxedImpl"); } + public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire, + int proto, int fromport, int toport, int flags) throws I2PSessionException { + throw new IllegalArgumentException("Use MuxedImpl"); + } @Override public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException { @@ -222,14 +226,23 @@ class I2PSessionImpl2 extends I2PSessionImpl { private static final int NUM_TAGS = 50; /** - * TODO - Don't need to save MessageState since actuallyWait is false... - * But for now just use sendNoEffort() instead. - * * @param keyUsed unused - no end-to-end crypto * @param tagsSent unused - no end-to-end crypto */ protected boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent, long expires) throws I2PSessionException { + return sendBestEffort(dest, payload, expires, 0); + } + + /** + * TODO - Don't need to save MessageState since actuallyWait is false... + * But for now just use sendNoEffort() instead. + * + * @param flags to be passed to the router + * @since 0.8.4 + */ + protected boolean sendBestEffort(Destination dest, byte payload[], long expires, int flags) + throws I2PSessionException { //SessionKey key = null; //SessionKey newKey = null; //SessionTag tag = null; @@ -324,7 +337,7 @@ class I2PSessionImpl2 extends I2PSessionImpl { + " sync took " + (inSendingSync-beforeSendingSync) + " add took " + (afterSendingSync-inSendingSync)); //_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey, expires); - _producer.sendMessage(this, dest, nonce, payload, null, null, null, null, expires); + _producer.sendMessage(this, dest, nonce, payload, expires, flags); // since this is 'best effort', all we're waiting for is a status update // saying that the router received it - in theory, that should come back diff --git a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java index 342b05d260e584e5e61e52685ea330210fe36ab8..fcf11d0dafab3e8fcb2d7d87a90c9dcdcc576b3d 100644 --- a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java +++ b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java @@ -162,12 +162,34 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession { * 255 disallowed * @param fromPort 1-65535 or 0 for unset * @param toPort 1-65535 or 0 for unset + * @since 0.7.1 */ @Override public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expires, int proto, int fromPort, int toPort) throws I2PSessionException { + return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0, proto, fromPort, toPort, 0); + } + + /** + * @param keyUsed unused - no end-to-end crypto + * @param tagsSent unused - no end-to-end crypto + * @param proto 1-254 or 0 for unset; recommended: + * I2PSession.PROTO_UNSPECIFIED + * I2PSession.PROTO_STREAMING + * I2PSession.PROTO_DATAGRAM + * 255 disallowed + * @param fromPort 1-65535 or 0 for unset + * @param toPort 1-65535 or 0 for unset + * @param flags to be passed to the router + * @since 0.8.4 + */ + @Override + public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, + SessionKey keyUsed, Set tagsSent, long expires, + int proto, int fromPort, int toPort, int flags) + throws I2PSessionException { if (isClosed()) throw new I2PSessionException("Already closed"); updateActivity(); @@ -183,7 +205,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession { _context.statManager().addRateData("i2cp.tx.msgCompressed", payload.length, 0); _context.statManager().addRateData("i2cp.tx.msgExpanded", size, 0); - return sendBestEffort(dest, payload, keyUsed, tagsSent, expires); + return sendBestEffort(dest, payload, expires, flags); } /** @@ -191,7 +213,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession { */ @Override public void addNewMessage(MessagePayloadMessage msg) { - Long mid = new Long(msg.getMessageId()); + Long mid = Long.valueOf(msg.getMessageId()); _availableMessages.put(mid, msg); long id = msg.getMessageId(); byte data[] = msg.getPayload().getUnencryptedData(); diff --git a/core/java/src/net/i2p/client/I2PSimpleSession.java b/core/java/src/net/i2p/client/I2PSimpleSession.java index e984b6d30712e00b93afd0d3542aac41f28634ef..e108fc008ba0e577d142f40fd0ca127adec1360d 100644 --- a/core/java/src/net/i2p/client/I2PSimpleSession.java +++ b/core/java/src/net/i2p/client/I2PSimpleSession.java @@ -98,10 +98,17 @@ class I2PSimpleSession extends I2PSessionImpl2 { } } + /** + * Ignore, does nothing + * @since 0.8.4 + */ + @Override + public void updateOptions(Properties options) {} + /** * Only map message handlers that we will use */ - class SimpleMessageHandlerMap extends I2PClientMessageHandlerMap { + private static class SimpleMessageHandlerMap extends I2PClientMessageHandlerMap { public SimpleMessageHandlerMap(I2PAppContext context) { int highest = Math.max(DestReplyMessage.MESSAGE_TYPE, BandwidthLimitsMessage.MESSAGE_TYPE); _handlers = new I2CPMessageHandler[highest+1]; diff --git a/core/java/src/net/i2p/crypto/CryptixAESEngine.java b/core/java/src/net/i2p/crypto/CryptixAESEngine.java index ea2338003ce4c9f80209a7f642f54405e440466c..350821e35558fe6324db6cbc538ce02ad53f6174 100644 --- a/core/java/src/net/i2p/crypto/CryptixAESEngine.java +++ b/core/java/src/net/i2p/crypto/CryptixAESEngine.java @@ -149,7 +149,7 @@ public class CryptixAESEngine extends AESEngine { @Override public final void decryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte rv[], int outIndex) { if ( (payload == null) || (rv == null) ) - throw new IllegalArgumentException("null block args [payload=" + payload + " rv="+rv); + throw new IllegalArgumentException("null block args"); if (payload.length - inIndex > rv.length - outIndex) throw new IllegalArgumentException("bad block args [payload.len=" + payload.length + " inIndex=" + inIndex + " rv.len=" + rv.length diff --git a/core/java/src/net/i2p/crypto/ElGamalAESEngine.java b/core/java/src/net/i2p/crypto/ElGamalAESEngine.java index 6160caf5b7a9243401ac5b6a166340649fcccb67..df4867574d7f52911025297a414592f2eec845bf 100644 --- a/core/java/src/net/i2p/crypto/ElGamalAESEngine.java +++ b/core/java/src/net/i2p/crypto/ElGamalAESEngine.java @@ -10,6 +10,7 @@ package net.i2p.crypto; */ import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -527,8 +528,6 @@ public class ElGamalAESEngine { return aesEncr; } - private final static Set EMPTY_SET = new HashSet(); - /** * For both scenarios, this method encrypts the AES area using the given key, iv * and making sure the resulting data is at least as long as the paddedSize and @@ -552,7 +551,7 @@ public class ElGamalAESEngine { long paddedSize, int prefixBytes) { //_log.debug("iv for encryption: " + DataHelper.toString(iv, 16)); //_log.debug("Encrypting AES"); - if (tagsForDelivery == null) tagsForDelivery = EMPTY_SET; + if (tagsForDelivery == null) tagsForDelivery = Collections.EMPTY_SET; int size = 2 // sizeof(tags) + tagsForDelivery.size() + SessionTag.BYTE_LENGTH*tagsForDelivery.size() diff --git a/core/java/src/net/i2p/data/Base32.java b/core/java/src/net/i2p/data/Base32.java index 3071e0bc013ca784b8264c37589fa04809a38c3e..cbcb93515f9f8dcb90e3eaa73fdf1da862c706f5 100644 --- a/core/java/src/net/i2p/data/Base32.java +++ b/core/java/src/net/i2p/data/Base32.java @@ -72,13 +72,13 @@ public class Base32 { } private static void runApp(String args[]) { + if ("encodestring".equalsIgnoreCase(args[0])) { + System.out.println(encode(args[1].getBytes())); + return; + } + InputStream in = System.in; + OutputStream out = System.out; try { - if ("encodestring".equalsIgnoreCase(args[0])) { - System.out.println(encode(args[1].getBytes())); - return; - } - InputStream in = System.in; - OutputStream out = System.out; if (args.length >= 3) { out = new FileOutputStream(args[2]); } @@ -95,6 +95,9 @@ public class Base32 { } } catch (IOException ioe) { ioe.printStackTrace(System.err); + } finally { + try { in.close(); } catch (IOException e) {} + try { out.close(); } catch (IOException e) {} } } diff --git a/core/java/src/net/i2p/data/Base64.java b/core/java/src/net/i2p/data/Base64.java index fe730af6b4a0349e527f7347d32e094c16aeaa6c..52d59002d8ef654bff0b9314b9d48b348895e0b7 100644 --- a/core/java/src/net/i2p/data/Base64.java +++ b/core/java/src/net/i2p/data/Base64.java @@ -178,13 +178,13 @@ public class Base64 { } private static void runApp(String args[]) { + if ("encodestring".equalsIgnoreCase(args[0])) { + System.out.println(encode(args[1].getBytes())); + return; + } + InputStream in = System.in; + OutputStream out = System.out; try { - if ("encodestring".equalsIgnoreCase(args[0])) { - System.out.println(encode(args[1].getBytes())); - return; - } - InputStream in = System.in; - OutputStream out = System.out; if (args.length >= 3) { out = new FileOutputStream(args[2]); } @@ -201,6 +201,9 @@ public class Base64 { } } catch (IOException ioe) { ioe.printStackTrace(System.err); + } finally { + try { in.close(); } catch (IOException e) {} + try { out.close(); } catch (IOException e) {} } } diff --git a/core/java/src/net/i2p/data/DataHelper.java b/core/java/src/net/i2p/data/DataHelper.java index 2e74adce3a94697f1fa3c3f2af226dc939871198..69b315a1a478be9ac84877478997734ab6d2e5b9 100644 --- a/core/java/src/net/i2p/data/DataHelper.java +++ b/core/java/src/net/i2p/data/DataHelper.java @@ -845,7 +845,7 @@ public class DataHelper { */ public final static void xor(byte lhs[], int startLeft, byte rhs[], int startRight, byte out[], int startOut, int len) { if ( (lhs == null) || (rhs == null) || (out == null) ) - throw new NullPointerException("Invalid params to xor (" + lhs + ", " + rhs + ", " + out + ")"); + throw new NullPointerException("Null params to xor"); if (lhs.length < startLeft + len) throw new IllegalArgumentException("Left hand side is too short"); if (rhs.length < startRight + len) diff --git a/core/java/src/net/i2p/data/DateAndFlags.java b/core/java/src/net/i2p/data/DateAndFlags.java new file mode 100644 index 0000000000000000000000000000000000000000..70f88ce655b27e4af5ca9bea42d624a5656f409f --- /dev/null +++ b/core/java/src/net/i2p/data/DateAndFlags.java @@ -0,0 +1,139 @@ +package net.i2p.data; + +/* + * free (adj.): unencumbered; not under the control of others + * Released into the public domain + * with no warranty of any kind, either expressed or implied. + * + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Date; + +/** + * A six-byte Date and 2 bytes of flags, since a Date won't encroach + * on the top two bytes until the year 10889. + * + * The flag format is not specified here. The bits may be used in + * an application-specific manner. The application should + * be designed so that a flags value of 0 is the default, for + * compatibility with an 8-byte Date. + * + * If we really need some more bits we could use the first few bits + * of the third byte. + * + * @author zzz + * @since 0.8.4 + */ +public class DateAndFlags extends DataStructureImpl { + private int _flags; + private long _date; + + public DateAndFlags() {} + + /** + * @param flags 0 - 65535 + */ + public DateAndFlags(int flags, long date) { + _flags = flags; + _date = date; + } + + /** + * @param flags 0 - 65535 + */ + public DateAndFlags(int flags, Date date) { + _flags = flags; + _date = date.getTime(); + } + + public int getFlags() { + return _flags; + } + + /** + * @param flags 0 - 65535 + */ + public void setFlags(int flags) { + _flags = flags; + } + + /** + * The Date object is created here, it is not cached. + * Use getTime() if you only need the long value. + */ + public Date getDate() { + return new Date(_date); + } + + public long getTime() { + return (_date); + } + + public void setDate(long date) { + _date = date; + } + + public void setDate(Date date) { + _date = date.getTime(); + } + + public void readBytes(InputStream in) throws DataFormatException, IOException { + _flags = (int) DataHelper.readLong(in, 2); + _date = DataHelper.readLong(in, 6); + } + + public void writeBytes(OutputStream out) throws DataFormatException, IOException { + DataHelper.writeLong(out, 2, _flags); + DataHelper.writeLong(out, 6, _date); + } + + /** + * Overridden for efficiency. + */ + @Override + public byte[] toByteArray() { + byte[] rv = DataHelper.toLong(8, _date); + rv[0] = (byte) ((_flags >> 8) & 0xff); + rv[1] = (byte) (_flags & 0xff); + return rv; + } + + /** + * Overridden for efficiency. + * @param data non-null + * @throws DataFormatException if null or wrong length + */ + @Override + public void fromByteArray(byte data[]) throws DataFormatException { + if (data == null) throw new DataFormatException("Null data passed in"); + if (data.length != 8) throw new DataFormatException("Bad data length"); + _flags = (int) DataHelper.fromLong(data, 0, 2); + _date = DataHelper.fromLong(data, 2, 6); + } + + @Override + public boolean equals(Object object) { + if ((object == null) || !(object instanceof DateAndFlags)) return false; + DateAndFlags daf = (DateAndFlags) object; + return _date == daf._date && _flags == daf._flags; + + } + + @Override + public int hashCode() { + return _flags + (int) _date; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(64); + buf.append("[DateAndFlags: "); + buf.append("\n\tDate: ").append((new Date(_date)).toString()); + buf.append("\n\tFlags: 0x").append(Integer.toHexString(_flags)); + buf.append("]"); + return buf.toString(); + } +} diff --git a/core/java/src/net/i2p/data/PrivateKeyFile.java b/core/java/src/net/i2p/data/PrivateKeyFile.java index 0c752f9d6a22c0eed86d5aeb97d99081997f8eb1..4f4af3cf9ae51a582bce40f0a7a0cb6c7a71c390 100644 --- a/core/java/src/net/i2p/data/PrivateKeyFile.java +++ b/core/java/src/net/i2p/data/PrivateKeyFile.java @@ -133,9 +133,15 @@ public class PrivateKeyFile { */ public Destination createIfAbsent() throws I2PException, IOException, DataFormatException { if(!this.file.exists()) { - FileOutputStream out = new FileOutputStream(this.file); - this.client.createDestination(out); - out.close(); + FileOutputStream out = null; + try { + out = new FileOutputStream(this.file); + this.client.createDestination(out); + } finally { + if (out != null) { + try { out.close(); } catch (IOException ioe) {} + } + } } return getDestination(); } @@ -243,29 +249,36 @@ public class PrivateKeyFile { public I2PSession open() throws I2PSessionException, IOException { return this.open(new Properties()); } + public I2PSession open(Properties opts) throws I2PSessionException, IOException { - // open input file - FileInputStream in = new FileInputStream(this.file); - - // create sesssion - I2PSession s = this.client.createSession(in, opts); - - // close file - in.close(); - - return s; + FileInputStream in = null; + try { + in = new FileInputStream(this.file); + I2PSession s = this.client.createSession(in, opts); + return s; + } finally { + if (in != null) { + try { in.close(); } catch (IOException ioe) {} + } + } } /** * Copied from I2PClientImpl.createDestination() */ public void write() throws IOException, DataFormatException { - FileOutputStream out = new FileOutputStream(this.file); - this.dest.writeBytes(out); - this.privKey.writeBytes(out); - this.signingPrivKey.writeBytes(out); - out.flush(); - out.close(); + FileOutputStream out = null; + try { + out = new FileOutputStream(this.file); + this.dest.writeBytes(out); + this.privKey.writeBytes(out); + this.signingPrivKey.writeBytes(out); + out.flush(); + } finally { + if (out != null) { + try { out.close(); } catch (IOException ioe) {} + } + } } @Override @@ -377,7 +390,8 @@ public class PrivateKeyFile { } } } - } catch (Exception ioe) { + } catch (DataFormatException dfe) { + } catch (IOException ioe) { } // not found, continue to the next file } diff --git a/core/java/src/net/i2p/data/TunnelId.java b/core/java/src/net/i2p/data/TunnelId.java index 2e625e606fbfb4f5c47f3082b5f4cee02eb7b45f..0e1c815f6005befb716fa5ca1d0ef3bb270ad063 100644 --- a/core/java/src/net/i2p/data/TunnelId.java +++ b/core/java/src/net/i2p/data/TunnelId.java @@ -50,6 +50,26 @@ public class TunnelId extends DataStructureImpl { DataHelper.writeLong(out, 4, _tunnelId); } + /** + * Overridden for efficiency. + */ + @Override + public byte[] toByteArray() { + return DataHelper.toLong(4, _tunnelId); + } + + /** + * Overridden for efficiency. + * @param data non-null + * @throws DataFormatException if null or wrong length + */ + @Override + public void fromByteArray(byte data[]) throws DataFormatException { + if (data == null) throw new DataFormatException("Null data passed in"); + if (data.length != 4) throw new DataFormatException("Bad data length"); + _tunnelId = (int) DataHelper.fromLong(data, 0, 4); + } + @Override public boolean equals(Object obj) { if ( (obj == null) || !(obj instanceof TunnelId)) diff --git a/core/java/src/net/i2p/data/i2cp/DestReplyMessage.java b/core/java/src/net/i2p/data/i2cp/DestReplyMessage.java index 788fb36a0d3930e45b7f2f98bef37790ab383f73..f4d44a3ae30103619d83147b5b3cb5f4d25f659a 100644 --- a/core/java/src/net/i2p/data/i2cp/DestReplyMessage.java +++ b/core/java/src/net/i2p/data/i2cp/DestReplyMessage.java @@ -74,10 +74,11 @@ public class DestReplyMessage extends I2CPMessageImpl { } protected byte[] doWriteMessage() throws I2CPMessageException, IOException { - if (_dest == null && _hash == null) - return new byte[0]; // null response allowed - if (_dest == null && _hash != null) + if (_dest == null) { + if (_hash == null) + return new byte[0]; // null response allowed return _hash.getData(); + } ByteArrayOutputStream os = new ByteArrayOutputStream(_dest.size()); try { _dest.writeBytes(os); diff --git a/core/java/src/net/i2p/data/i2cp/SendMessageExpiresMessage.java b/core/java/src/net/i2p/data/i2cp/SendMessageExpiresMessage.java index 9bcabe2fb77a4fb875d4bf0d38465a9409314008..08b31a6e759fb8c1b31242fbc89b25f127a2a2a3 100644 --- a/core/java/src/net/i2p/data/i2cp/SendMessageExpiresMessage.java +++ b/core/java/src/net/i2p/data/i2cp/SendMessageExpiresMessage.java @@ -16,32 +16,66 @@ import java.util.Date; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; +import net.i2p.data.DateAndFlags; import net.i2p.data.Destination; import net.i2p.data.Payload; /** * Same as SendMessageMessage, but with an expiration to be passed to the router * + * As of 0.8.4, retrofitted to use DateAndFlags. Backwards compatible. + * * @author zzz */ public class SendMessageExpiresMessage extends SendMessageMessage { /* FIXME hides another field FIXME */ public final static int MESSAGE_TYPE = 36; - private SessionId _sessionId; - private Destination _destination; - private Payload _payload; - private Date _expiration; + private final DateAndFlags _daf; public SendMessageExpiresMessage() { super(); + _daf = new DateAndFlags(); } + /** + * The Date object is created here, it is not cached. + * Use getExpirationTime() if you only need the long value. + */ public Date getExpiration() { - return _expiration; + return _daf.getDate(); + } + + /** + * Use this instead of getExpiration().getTime() + * @since 0.8.4 + */ + public long getExpirationTime() { + return _daf.getTime(); } public void setExpiration(Date d) { - _expiration = d; + _daf.setDate(d); + } + + /** + * @since 0.8.4 + */ + public void setExpiration(long d) { + _daf.setDate(d); + } + + /** + * @since 0.8.4 + */ + public int getFlags() { + return _daf.getFlags(); + } + + /** + * @since 0.8.4 + */ + public void setFlags(int f) { + _daf.setFlags(f); } /** @@ -54,7 +88,7 @@ public class SendMessageExpiresMessage extends SendMessageMessage { super.readMessage(in, length, type); try { - _expiration = DataHelper.readDate(in); + _daf.readBytes(in); } catch (DataFormatException dfe) { throw new I2CPMessageException("Unable to load the message data", dfe); } @@ -68,7 +102,7 @@ public class SendMessageExpiresMessage extends SendMessageMessage { */ @Override public void writeMessage(OutputStream out) throws I2CPMessageException, IOException { - if ((getSessionId() == null) || (getDestination() == null) || (getPayload() == null) || (getNonce() <= 0) || (_expiration == null)) + if ((getSessionId() == null) || (getDestination() == null) || (getPayload() == null) || (getNonce() <= 0)) throw new I2CPMessageException("Unable to write out the message as there is not enough data"); int len = 2 + getDestination().size() + getPayload().getSize() + 4 + 4 + DataHelper.DATE_LENGTH; @@ -79,7 +113,7 @@ public class SendMessageExpiresMessage extends SendMessageMessage { getDestination().writeBytes(out); getPayload().writeBytes(out); DataHelper.writeLong(out, 4, getNonce()); - DataHelper.writeDate(out, _expiration); + _daf.writeBytes(out); } catch (DataFormatException dfe) { throw new I2CPMessageException("Error writing the msg", dfe); } @@ -96,7 +130,7 @@ public class SendMessageExpiresMessage extends SendMessageMessage { if ((object != null) && (object instanceof SendMessageExpiresMessage)) { SendMessageExpiresMessage msg = (SendMessageExpiresMessage) object; return super.equals(object) - && DataHelper.eq(getExpiration(), msg.getExpiration()); + && _daf.equals(msg._daf); } return false; diff --git a/core/java/src/net/i2p/stat/FrequencyStat.java b/core/java/src/net/i2p/stat/FrequencyStat.java index dfa7bcfe675298c564d2169f4d8d95372c19e4ad..d18a469dbd3c921a640cf7e06d3141db968747be 100644 --- a/core/java/src/net/i2p/stat/FrequencyStat.java +++ b/core/java/src/net/i2p/stat/FrequencyStat.java @@ -89,7 +89,7 @@ public class FrequencyStat { /** @since 0.8.2 */ @Override public boolean equals(Object obj) { - if ((obj == null) || (obj.getClass() != FrequencyStat.class)) return false; + if ((obj == null) || !(obj instanceof FrequencyStat)) return false; return _statName.equals(((FrequencyStat)obj)._statName); } diff --git a/core/java/src/net/i2p/stat/Rate.java b/core/java/src/net/i2p/stat/Rate.java index fd0fae49f00d197c575c866c87cc21aca98bfdd2..8473d58ebbbfdcf2a2ca68e67980036a5db28a4f 100644 --- a/core/java/src/net/i2p/stat/Rate.java +++ b/core/java/src/net/i2p/stat/Rate.java @@ -473,7 +473,7 @@ public class Rate { @Override public boolean equals(Object obj) { - if ((obj == null) || (obj.getClass() != Rate.class)) return false; + if ((obj == null) || !(obj instanceof Rate)) return false; if (obj == this) return true; Rate r = (Rate) obj; return _period == r.getPeriod() && _creationDate == r.getCreationDate() && diff --git a/core/java/src/net/i2p/stat/RateStat.java b/core/java/src/net/i2p/stat/RateStat.java index 79ddec51980efdcf689f9f95b4cade1f25223d18..fded9b019f4665dd15dc15214f75d67bdf8c0618 100644 --- a/core/java/src/net/i2p/stat/RateStat.java +++ b/core/java/src/net/i2p/stat/RateStat.java @@ -108,7 +108,7 @@ public class RateStat { @Override public boolean equals(Object obj) { - if ((obj == null) || (obj.getClass() != RateStat.class)) return false; + if ((obj == null) || !(obj instanceof RateStat)) return false; RateStat rs = (RateStat) obj; if (DataHelper.eq(getGroupName(), rs.getGroupName()) && DataHelper.eq(getDescription(), rs.getDescription()) && DataHelper.eq(getName(), rs.getName())) { diff --git a/core/java/src/net/i2p/util/Clock.java b/core/java/src/net/i2p/util/Clock.java index 4fc5eaff075ba2e3d475fb9583f64117425d5ad2..80696fc957e187739ab40e38c049054aaad1f1c5 100644 --- a/core/java/src/net/i2p/util/Clock.java +++ b/core/java/src/net/i2p/util/Clock.java @@ -1,8 +1,8 @@ package net.i2p.util; -import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; import net.i2p.I2PAppContext; import net.i2p.time.Timestamper; @@ -19,19 +19,19 @@ import net.i2p.time.Timestamper; * */ public class Clock implements Timestamper.UpdateListener { - protected I2PAppContext _context; - private Timestamper _timestamper; - protected long _startedOn; + protected final I2PAppContext _context; + private final Timestamper _timestamper; + protected final long _startedOn; protected boolean _statCreated; + protected volatile long _offset; + protected boolean _alreadyChanged; + private final Set _listeners; public Clock(I2PAppContext context) { _context = context; - _offset = 0; - _alreadyChanged = false; - _listeners = new HashSet(1); + _listeners = new CopyOnWriteArraySet(); _timestamper = new Timestamper(context, this); _startedOn = System.currentTimeMillis(); - _statCreated = false; } public static Clock getInstance() { return I2PAppContext.getGlobalContext().clock(); @@ -41,10 +41,6 @@ public class Clock implements Timestamper.UpdateListener { /** we fetch it on demand to avoid circular dependencies (logging uses the clock) */ protected Log getLog() { return _context.logManager().getLog(Clock.class); } - - protected volatile long _offset; - protected boolean _alreadyChanged; - private final Set _listeners; /** if the clock is skewed by 3+ days, fuck 'em */ public final static long MAX_OFFSET = 3 * 24 * 60 * 60 * 1000; @@ -136,24 +132,18 @@ public class Clock implements Timestamper.UpdateListener { } public void addUpdateListener(ClockUpdateListener lsnr) { - synchronized (_listeners) { _listeners.add(lsnr); - } } public void removeUpdateListener(ClockUpdateListener lsnr) { - synchronized (_listeners) { _listeners.remove(lsnr); - } } protected void fireOffsetChanged(long delta) { - synchronized (_listeners) { for (Iterator iter = _listeners.iterator(); iter.hasNext();) { ClockUpdateListener lsnr = (ClockUpdateListener) iter.next(); lsnr.offsetChanged(delta); } - } } public static interface ClockUpdateListener { diff --git a/core/java/src/net/i2p/util/ConcurrentHashSet.java b/core/java/src/net/i2p/util/ConcurrentHashSet.java index 3610c54b939f3527648726d0e91fc0e84c057b49..6c6d8bfb675e1f63bc8b3aefeb0d9079b5f90806 100644 --- a/core/java/src/net/i2p/util/ConcurrentHashSet.java +++ b/core/java/src/net/i2p/util/ConcurrentHashSet.java @@ -14,7 +14,7 @@ import java.util.concurrent.ConcurrentHashMap; */ public class ConcurrentHashSet<E> extends AbstractSet<E> implements Set<E> { private static final Object DUMMY = new Object(); - private Map<E, Object> _map; + private final Map<E, Object> _map; public ConcurrentHashSet() { _map = new ConcurrentHashMap(); diff --git a/core/java/src/net/i2p/util/DecayingBloomFilter.java b/core/java/src/net/i2p/util/DecayingBloomFilter.java index 4179edd389625664757a36b3149c863b2928dffe..ff564b2656b659decc000a2a43d03732957d9d3f 100644 --- a/core/java/src/net/i2p/util/DecayingBloomFilter.java +++ b/core/java/src/net/i2p/util/DecayingBloomFilter.java @@ -20,28 +20,34 @@ import org.xlattice.crypto.filters.BloomSHA1; * Further analysis and tweaking for the tunnel IVV may be required. */ public class DecayingBloomFilter { - private I2PAppContext _context; - private Log _log; + protected final I2PAppContext _context; + protected final Log _log; private BloomSHA1 _current; private BloomSHA1 _previous; - private int _durationMs; - private int _entryBytes; + protected final int _durationMs; + protected final int _entryBytes; private byte _extenders[][]; private byte _extended[]; private byte _longToEntry[]; private long _longToEntryMask; protected long _currentDuplicates; - private boolean _keepDecaying; - private DecayEvent _decayEvent; + protected volatile boolean _keepDecaying; + protected SimpleTimer.TimedEvent _decayEvent; /** just for logging */ - private String _name; + protected final String _name; private static final int DEFAULT_M = 23; private static final int DEFAULT_K = 11; private static final boolean ALWAYS_MISS = false; - /** noop for DHS */ - public DecayingBloomFilter() {} + /** only for extension by DHS */ + protected DecayingBloomFilter(int durationMs, int entryBytes, String name, I2PAppContext context) { + _context = context; + _log = context.logManager().getLog(getClass()); + _entryBytes = entryBytes; + _name = name; + _durationMs = durationMs; + } /** * Create a bloom filter that will decay its entries over time. @@ -87,7 +93,6 @@ public class DecayingBloomFilter { _longToEntry = new byte[_entryBytes]; _longToEntryMask = (1l << (_entryBytes * 8l)) -1; } - _currentDuplicates = 0; _decayEvent = new DecayEvent(); _keepDecaying = true; SimpleTimer.getInstance().addEvent(_decayEvent, _durationMs); @@ -105,11 +110,13 @@ public class DecayingBloomFilter { } public long getCurrentDuplicateCount() { return _currentDuplicates; } + public int getInsertedCount() { synchronized (this) { return _current.size() + _previous.size(); } } + public double getFalsePositiveRate() { synchronized (this) { return _current.falsePositives(); @@ -117,12 +124,15 @@ public class DecayingBloomFilter { } /** - * return true if the entry added is a duplicate - * + * @return true if the entry added is a duplicate */ public boolean add(byte entry[]) { return add(entry, 0, entry.length); } + + /** + * @return true if the entry added is a duplicate + */ public boolean add(byte entry[], int off, int len) { if (ALWAYS_MISS) return false; if (entry == null) @@ -131,55 +141,52 @@ public class DecayingBloomFilter { throw new IllegalArgumentException("Bad entry [" + len + ", expected " + _entryBytes + "]"); synchronized (this) { - return locked_add(entry, off, len); + return locked_add(entry, off, len, true); } } /** - * return true if the entry added is a duplicate. the number of low order + * @return true if the entry added is a duplicate. the number of low order * bits used is determined by the entryBytes parameter used on creation of the * filter. * */ public boolean add(long entry) { if (ALWAYS_MISS) return false; + if (_entryBytes <= 7) + entry = ((entry ^ _longToEntryMask) & ((1 << 31)-1)) | (entry ^ _longToEntryMask); + //entry &= _longToEntryMask; + if (entry < 0) { + DataHelper.toLong(_longToEntry, 0, _entryBytes, 0-entry); + _longToEntry[0] |= (1 << 7); + } else { + DataHelper.toLong(_longToEntry, 0, _entryBytes, entry); + } synchronized (this) { - if (_entryBytes <= 7) - entry = ((entry ^ _longToEntryMask) & ((1 << 31)-1)) | (entry ^ _longToEntryMask); - //entry &= _longToEntryMask; - if (entry < 0) { - DataHelper.toLong(_longToEntry, 0, _entryBytes, 0-entry); - _longToEntry[0] |= (1 << 7); - } else { - DataHelper.toLong(_longToEntry, 0, _entryBytes, entry); - } - return locked_add(_longToEntry, 0, _longToEntry.length); + return locked_add(_longToEntry, 0, _longToEntry.length, true); } } /** - * return true if the entry is already known. this does NOT add the + * @return true if the entry is already known. this does NOT add the * entry however. * */ public boolean isKnown(long entry) { if (ALWAYS_MISS) return false; + if (_entryBytes <= 7) + entry = ((entry ^ _longToEntryMask) & ((1 << 31)-1)) | (entry ^ _longToEntryMask); + if (entry < 0) { + DataHelper.toLong(_longToEntry, 0, _entryBytes, 0-entry); + _longToEntry[0] |= (1 << 7); + } else { + DataHelper.toLong(_longToEntry, 0, _entryBytes, entry); + } synchronized (this) { - if (_entryBytes <= 7) - entry = ((entry ^ _longToEntryMask) & ((1 << 31)-1)) | (entry ^ _longToEntryMask); - if (entry < 0) { - DataHelper.toLong(_longToEntry, 0, _entryBytes, 0-entry); - _longToEntry[0] |= (1 << 7); - } else { - DataHelper.toLong(_longToEntry, 0, _entryBytes, entry); - } return locked_add(_longToEntry, 0, _longToEntry.length, false); } } - private boolean locked_add(byte entry[], int offset, int len) { - return locked_add(entry, offset, len, true); - } private boolean locked_add(byte entry[], int offset, int len, boolean addIfNew) { if (_extended != null) { // extend the entry to 32 bytes @@ -195,7 +202,6 @@ public class DecayingBloomFilter { } else { if (addIfNew) { _current.locked_insert(_extended); - _previous.locked_insert(_extended); } return false; } @@ -208,7 +214,6 @@ public class DecayingBloomFilter { } else { if (addIfNew) { _current.locked_insert(entry, offset, len); - _previous.locked_insert(entry, offset, len); } return false; } diff --git a/core/java/src/net/i2p/util/DecayingHashSet.java b/core/java/src/net/i2p/util/DecayingHashSet.java index a72b6b9e2e9a3fc01a92492514602d17d59b04cb..f090cf727b61989ca9bec1d4f205ba48767fba17 100644 --- a/core/java/src/net/i2p/util/DecayingHashSet.java +++ b/core/java/src/net/i2p/util/DecayingHashSet.java @@ -17,12 +17,15 @@ import net.i2p.data.DataHelper; * * ./router/java/src/net/i2p/router/tunnel/BuildMessageProcessor.java: * 32 bytes, peak 10 entries in 1m + * (320 peak entries seen on fast router) * * ./router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java: * 4 bytes, peak 150 entries in 10s + * (1600 peak entries seen on fast router) * * ./router/java/src/net/i2p/router/MessageValidator.java: * 8 bytes, peak 1K entries in 2m + * (36K peak entries seen on fast router) * * ./router/java/src/net/i2p/router/tunnel/BloomFilterIVValidator.java: * 16 bytes, peak 15K entries in 10m @@ -57,19 +60,10 @@ import net.i2p.data.DataHelper; * @author zzz */ public class DecayingHashSet extends DecayingBloomFilter { - private final I2PAppContext _context; - private final Log _log; private ConcurrentHashSet<ArrayWrapper> _current; private ConcurrentHashSet<ArrayWrapper> _previous; - private int _durationMs; - private int _entryBytes; - private volatile boolean _keepDecaying; - private final DecayEvent _decayEvent; - /** just for logging */ - private final String _name; /** synchronize against this lock when switching double buffers */ private final ReentrantReadWriteLock _reorganizeLock = new ReentrantReadWriteLock(true); - /** * Create a double-buffered hash set that will decay its entries over time. @@ -83,16 +77,11 @@ public class DecayingHashSet extends DecayingBloomFilter { /** @param name just for logging / debugging / stats */ public DecayingHashSet(I2PAppContext context, int durationMs, int entryBytes, String name) { + super(durationMs, entryBytes, name, context); if (entryBytes <= 0 || entryBytes > 32) throw new IllegalArgumentException("Bad size"); - _context = context; - _log = context.logManager().getLog(DecayingHashSet.class); - _entryBytes = entryBytes; - _name = name; _current = new ConcurrentHashSet(128); _previous = new ConcurrentHashSet(128); - _durationMs = durationMs; - _currentDuplicates = 0; _decayEvent = new DecayEvent(); _keepDecaying = true; SimpleScheduler.getInstance().addEvent(_decayEvent, _durationMs); @@ -111,6 +100,7 @@ public class DecayingHashSet extends DecayingBloomFilter { public int getInsertedCount() { return _current.size() + _previous.size(); } + /** pointless, only used for logging elsewhere */ @Override public double getFalsePositiveRate() { @@ -121,7 +111,6 @@ public class DecayingHashSet extends DecayingBloomFilter { /** * @return true if the entry added is a duplicate - * */ @Override public boolean add(byte entry[], int off, int len) { @@ -130,9 +119,10 @@ public class DecayingHashSet extends DecayingBloomFilter { if (len != _entryBytes) throw new IllegalArgumentException("Bad entry [" + len + ", expected " + _entryBytes + "]"); + ArrayWrapper w = new ArrayWrapper(entry, off, len); getReadLock(); try { - return locked_add(entry, off, len, true); + return locked_add(w, true); } finally { releaseReadLock(); } } @@ -158,35 +148,30 @@ public class DecayingHashSet extends DecayingBloomFilter { } private boolean add(long entry, boolean addIfNew) { - int len = Math.min(8, _entryBytes); - byte[] b = toLong(len, entry); + ArrayWrapper w = new ArrayWrapper(entry); getReadLock(); try { - return locked_add(b, 0, len, addIfNew); + return locked_add(w, addIfNew); } finally { releaseReadLock(); } } - /** from DataHelper, except negative values ok */ - private static byte[] toLong(int numBytes, long value) { - byte target[] = new byte[numBytes]; - for (int i = 0; i < numBytes; i++) - target[numBytes-i-1] = (byte)(value >>> (i*8)); - return target; - } - - /** so many questions... */ - private boolean locked_add(byte entry[], int offset, int len, boolean addIfNew) { - ArrayWrapper w = new ArrayWrapper(entry, offset, len); - boolean seen = _current.contains(w); - seen = seen || _previous.contains(w); + /** + * @param addIfNew if true, add the element to current if it is not already there; + * if false, only check + * @return if the element is in either the current or previous set + */ + private boolean locked_add(ArrayWrapper w, boolean addIfNew) { + boolean seen; + // only access _current once. This adds to _current even if seen in _previous. + if (addIfNew) + seen = !_current.add(w); + else + seen = _current.contains(w); + if (!seen) + seen = _previous.contains(w); if (seen) { - // why increment if addIfNew == false? - // why not add to current if only in previous? + // why increment if addIfNew == false? Only used for stats... _currentDuplicates++; - } else if (addIfNew) { - _current.add(w); - // why add to previous? - _previous.add(w); } return seen; } @@ -270,14 +255,22 @@ public class DecayingHashSet extends DecayingBloomFilter { * the maximum entropy given the length of the data. */ private static class ArrayWrapper { - private long _longhashcode; + private final long _longhashcode; + public ArrayWrapper(byte[] b, int offset, int len) { int idx = offset; int shift = Math.min(8, 64 / len); + long lhc = 0; for (int i = 0; i < len; i++) { // xor better than + in tests - _longhashcode ^= (((long) b[idx++]) << (i * shift)); + lhc ^= (((long) b[idx++]) << (i * shift)); } + _longhashcode = lhc; + } + + /** faster version for when storing <= 8 bytes */ + public ArrayWrapper(long b) { + _longhashcode = b; } public int hashCode() { diff --git a/core/java/src/net/i2p/util/EepGet.java b/core/java/src/net/i2p/util/EepGet.java index f055ce98cbbeb4594cc1a53d5de10bd4bf97a0a5..3ad84e6bbfc1226b970ad92bf65fb59615738b78 100644 --- a/core/java/src/net/i2p/util/EepGet.java +++ b/core/java/src/net/i2p/util/EepGet.java @@ -430,29 +430,33 @@ public class EepGet { _log.debug("Fetching (proxied? " + _shouldProxy + ") url=" + _actualURL); while (_keepFetching) { SocketTimeout timeout = null; - if (_fetchHeaderTimeout > 0) + if (_fetchHeaderTimeout > 0) { timeout = new SocketTimeout(_fetchHeaderTimeout); - final SocketTimeout stimeout = timeout; // ugly - why not use sotimeout? - timeout.setTimeoutCommand(new Runnable() { - public void run() { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("timeout reached on " + _url + ": " + stimeout); - _aborted = true; - } - }); - timeout.setTotalTimeoutPeriod(_fetchEndTime); + final SocketTimeout stimeout = timeout; // ugly - why not use sotimeout? + timeout.setTimeoutCommand(new Runnable() { + public void run() { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("timeout reached on " + _url + ": " + stimeout); + _aborted = true; + } + }); + timeout.setTotalTimeoutPeriod(_fetchEndTime); + } try { for (int i = 0; i < _listeners.size(); i++) _listeners.get(i).attempting(_url); sendRequest(timeout); - timeout.resetTimer(); + if (timeout != null) + timeout.resetTimer(); doFetch(timeout); - timeout.cancel(); + if (timeout != null) + timeout.cancel(); if (!_transferFailed) return true; break; } catch (IOException ioe) { - timeout.cancel(); + if (timeout != null) + timeout.cancel(); for (int i = 0; i < _listeners.size(); i++) _listeners.get(i).attemptFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt, _numRetries, ioe); if (_log.shouldLog(Log.WARN)) @@ -492,7 +496,10 @@ public class EepGet { return false; } - /** single fetch */ + /** + * single fetch + * @param timeout may be null + */ protected void doFetch(SocketTimeout timeout) throws IOException { _headersRead = false; _aborted = false; @@ -504,11 +511,13 @@ public class EepGet { if (_aborted) throw new IOException("Timed out reading the HTTP headers"); - timeout.resetTimer(); - if (_fetchInactivityTimeout > 0) - timeout.setInactivityTimeout(_fetchInactivityTimeout); - else - timeout.setInactivityTimeout(INACTIVITY_TIMEOUT); + if (timeout != null) { + timeout.resetTimer(); + if (_fetchInactivityTimeout > 0) + timeout.setInactivityTimeout(_fetchInactivityTimeout); + else + timeout.setInactivityTimeout(INACTIVITY_TIMEOUT); + } if (_redirectLocation != null) { //try { @@ -571,7 +580,8 @@ public class EepGet { int read = _proxyIn.read(buf, 0, toRead); if (read == -1) break; - timeout.resetTimer(); + if (timeout != null) + timeout.resetTimer(); _out.write(buf, 0, read); _bytesTransferred += read; if ((_maxSize > -1) && (_alreadyTransferred + read > _maxSize)) // could transfer a little over maxSize @@ -597,7 +607,8 @@ public class EepGet { read++; } } - timeout.resetTimer(); + if (timeout != null) + timeout.resetTimer(); if (_bytesRemaining >= read) // else chunked? _bytesRemaining -= read; if (read > 0) { @@ -622,7 +633,8 @@ public class EepGet { if (_aborted) throw new IOException("Timed out reading the HTTP data"); - timeout.cancel(); + if (timeout != null) + timeout.cancel(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Done transferring " + _bytesTransferred + " (ok? " + !_transferFailed + ")"); @@ -867,6 +879,9 @@ public class EepGet { private static final byte NL = '\n'; private static boolean isNL(byte b) { return (b == NL); } + /** + * @param timeout may be null + */ protected void sendRequest(SocketTimeout timeout) throws IOException { if (_outputStream != null) { // We are reading into a stream supplied by a caller, @@ -907,7 +922,8 @@ public class EepGet { _proxyIn = _proxy.getInputStream(); _proxyOut = _proxy.getOutputStream(); - timeout.setSocket(_proxy); + if (timeout != null) + timeout.setSocket(_proxy); _proxyOut.write(DataHelper.getUTF8(req)); _proxyOut.flush(); diff --git a/core/java/src/net/i2p/util/EepPost.java b/core/java/src/net/i2p/util/EepPost.java index 615361e2045ef3ed4742ec620a7fd68c8a029a1a..2e7836f62645a3576155604fcb4be902e389d566 100644 --- a/core/java/src/net/i2p/util/EepPost.java +++ b/core/java/src/net/i2p/util/EepPost.java @@ -31,6 +31,7 @@ public class EepPost { _log = ctx.logManager().getLog(EepPost.class); } +/***** public static void main(String args[]) { EepPost e = new EepPost(); Map fields = new HashMap(); @@ -47,6 +48,8 @@ public class EepPost { //e.postFiles("http://localhost/cgi-bin/read.pl", null, -1, fields, null); //e.postFiles("http://localhost:2001/import.jsp", null, -1, fields, null); } +*****/ + /** * Submit an HTTP POST to the given URL (using the proxy if specified), * uploading the given fields. If the field's value is a File object, then @@ -117,7 +120,7 @@ public class EepPost { } } out.close(); - } catch (Exception e) { + } catch (IOException e) { e.printStackTrace(); } finally { if (s != null) try { s.close(); } catch (IOException ioe) {} diff --git a/core/java/src/net/i2p/util/FileUtil.java b/core/java/src/net/i2p/util/FileUtil.java index b5bb48a4353fef584bc6088f0408df34d97f5568..68ce4e4f618099150b563864eb958fd4bc7908ce 100644 --- a/core/java/src/net/i2p/util/FileUtil.java +++ b/core/java/src/net/i2p/util/FileUtil.java @@ -122,24 +122,24 @@ public class FileUtil { } } } else { + InputStream in = null; + FileOutputStream fos = null; + JarOutputStream jos = null; try { - InputStream in = zip.getInputStream(entry); + in = zip.getInputStream(entry); if (entry.getName().endsWith(".jar.pack") || entry.getName().endsWith(".war.pack")) { target = new File(targetDir, entry.getName().substring(0, entry.getName().length() - ".pack".length())); - JarOutputStream fos = new JarOutputStream(new FileOutputStream(target)); - unpack(in, fos); - fos.close(); + jos = new JarOutputStream(new FileOutputStream(target)); + unpack(in, jos); System.err.println("INFO: File [" + entry.getName() + "] extracted and unpacked"); } else { - FileOutputStream fos = new FileOutputStream(target); + fos = new FileOutputStream(target); int read = 0; while ( (read = in.read(buf)) != -1) { fos.write(buf, 0, read); } - fos.close(); System.err.println("INFO: File [" + entry.getName() + "] extracted"); } - in.close(); } catch (IOException ioe) { System.err.println("ERROR: Error extracting the zip entry (" + entry.getName() + ')'); if (ioe.getMessage() != null && ioe.getMessage().indexOf("CAFED00D") >= 0) @@ -151,6 +151,10 @@ public class FileUtil { System.err.println("ERROR: Error unpacking the zip entry (" + entry.getName() + "), your JVM does not support unpack200"); return false; + } finally { + try { if (in != null) in.close(); } catch (IOException ioe) {} + try { if (fos != null) fos.close(); } catch (IOException ioe) {} + try { if (jos != null) jos.close(); } catch (IOException ioe) {} } } } @@ -401,21 +405,24 @@ public class FileUtil { if (dst.exists() && !overwriteExisting) return false; byte buf[] = new byte[4096]; + InputStream in = null; + OutputStream out = null; try { - FileInputStream in = new FileInputStream(src); - FileOutputStream out = new FileOutputStream(dst); + in = new FileInputStream(src); + out = new FileOutputStream(dst); int read = 0; while ( (read = in.read(buf)) != -1) out.write(buf, 0, read); - in.close(); - out.close(); return true; } catch (IOException ioe) { if (!quiet) ioe.printStackTrace(); return false; + } finally { + try { if (in != null) in.close(); } catch (IOException ioe) {} + try { if (out != null) out.close(); } catch (IOException ioe) {} } } diff --git a/core/java/src/net/i2p/util/I2PAppThread.java b/core/java/src/net/i2p/util/I2PAppThread.java index a9f027caeb15091cf17698b7d794fd9638b6a921..8111083551d93cfb1c6e75d5d93477a2a8aa204d 100644 --- a/core/java/src/net/i2p/util/I2PAppThread.java +++ b/core/java/src/net/i2p/util/I2PAppThread.java @@ -10,9 +10,9 @@ package net.i2p.util; */ -import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; /** * Like I2PThread but with per-thread OOM listeners, @@ -22,7 +22,7 @@ import java.util.Set; */ public class I2PAppThread extends I2PThread { - private Set _threadListeners = new HashSet(0); + private final Set _threadListeners = new CopyOnWriteArraySet(); public I2PAppThread() { super(); diff --git a/core/java/src/net/i2p/util/I2PThread.java b/core/java/src/net/i2p/util/I2PThread.java index 9b76b8fc9c92c9261c1e2338e3c5a05931f4e9fa..90c95d3818d70b3a3c17c566a5a6190cf5ade57d 100644 --- a/core/java/src/net/i2p/util/I2PThread.java +++ b/core/java/src/net/i2p/util/I2PThread.java @@ -10,9 +10,9 @@ package net.i2p.util; */ -import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; /** * In case its useful later... @@ -21,7 +21,7 @@ import java.util.Set; */ public class I2PThread extends Thread { private static volatile Log _log; - private static Set _listeners = new HashSet(4); + private static final Set _listeners = new CopyOnWriteArraySet(); private String _name; private Exception _createdBy; diff --git a/core/java/src/net/i2p/util/Log.java b/core/java/src/net/i2p/util/Log.java index b22325adb4d213f809f5f3d2d74824e097d6dfd8..6ce320c9d124beacdde2ae0a6a2eaf5942c77e8e 100644 --- a/core/java/src/net/i2p/util/Log.java +++ b/core/java/src/net/i2p/util/Log.java @@ -205,7 +205,8 @@ public class Log { } @Override public boolean equals(Object obj) { - if (obj == null) throw new NullPointerException("Null object scope?"); + if (obj == null) + return false; if (obj instanceof LogScope) { LogScope s = (LogScope)obj; return s._scopeCache.equals(_scopeCache); diff --git a/core/java/src/net/i2p/util/LogManager.java b/core/java/src/net/i2p/util/LogManager.java index 0ca7ef209c4ffe25a7223846c5cf1cda951bd569..a57512d4d33e9ac50225f311d8ba7a9ce4db26d5 100644 --- a/core/java/src/net/i2p/util/LogManager.java +++ b/core/java/src/net/i2p/util/LogManager.java @@ -166,8 +166,10 @@ public class LogManager { Log rv = _logs.get(scope); if (rv == null) { rv = new Log(this, cls, name); - _logs.putIfAbsent(scope, rv); - isNew = true; + Log old = _logs.putIfAbsent(scope, rv); + isNew = old == null; + if (!isNew) + rv = old; } if (isNew) updateLimit(rv); @@ -180,8 +182,9 @@ public class LogManager { } void addLog(Log log) { - _logs.putIfAbsent(log.getScope(), log); - updateLimit(log); + Log old = _logs.putIfAbsent(log.getScope(), log); + if (old == null) + updateLimit(log); } public LogConsoleBuffer getBuffer() { return _consoleBuffer; } @@ -636,6 +639,7 @@ public class LogManager { return _dateFormatPattern; } +/***** public static void main(String args[]) { I2PAppContext ctx = new I2PAppContext(); Log l1 = ctx.logManager().getLog("test.1"); @@ -656,6 +660,7 @@ public class LogManager { } System.exit(0); } +*****/ public void shutdown() { if (_writer != null) { diff --git a/core/java/src/net/i2p/util/LogRecordFormatter.java b/core/java/src/net/i2p/util/LogRecordFormatter.java index 52583b63c393290f4da98836635d731219d4cefc..58d08af74a789611ea1dd09aa3031f0af7943290 100644 --- a/core/java/src/net/i2p/util/LogRecordFormatter.java +++ b/core/java/src/net/i2p/util/LogRecordFormatter.java @@ -92,10 +92,13 @@ class LogRecordFormatter { } /** don't translate */ +/**** private static String getPriority(LogRecord rec) { return toString(Log.toLevelString(rec.getPriority()), MAX_PRIORITY_LENGTH); } +****/ + /** */ private static final String BUNDLE_NAME = "net.i2p.router.web.messages"; /** translate @since 0.7.14 */ diff --git a/core/java/src/net/i2p/util/LookaheadInputStream.java b/core/java/src/net/i2p/util/LookaheadInputStream.java index 7b23d26aeef4208cdc2659345a757e49ef60a148..d9a1d9f93e44a78025c1e7a37df98c8adf4c1946 100644 --- a/core/java/src/net/i2p/util/LookaheadInputStream.java +++ b/core/java/src/net/i2p/util/LookaheadInputStream.java @@ -78,6 +78,7 @@ public class LookaheadInputStream extends FilterInputStream { /** grab the lookahead footer */ public byte[] getFooter() { return _footerLookahead; } +/******* public static void main(String args[]) { byte buf[] = new byte[32]; for (int i = 0; i < 32; i++) @@ -128,4 +129,5 @@ public class LookaheadInputStream extends FilterInputStream { return false; } } +******/ } diff --git a/core/java/src/net/i2p/util/SSLEepGet.java b/core/java/src/net/i2p/util/SSLEepGet.java index e3ba43ae7a3ad12138f924749fbecfef74b188c9..d35545ae7e54bb039ce7e855eeec01b56034bd2b 100644 --- a/core/java/src/net/i2p/util/SSLEepGet.java +++ b/core/java/src/net/i2p/util/SSLEepGet.java @@ -482,12 +482,14 @@ public class SSLEepGet extends EepGet { if (_aborted) throw new IOException("Timed out reading the HTTP headers"); - timeout.resetTimer(); - if (_fetchInactivityTimeout > 0) - timeout.setInactivityTimeout(_fetchInactivityTimeout); - else - timeout.setInactivityTimeout(60*1000); - + if (timeout != null) { + timeout.resetTimer(); + if (_fetchInactivityTimeout > 0) + timeout.setInactivityTimeout(_fetchInactivityTimeout); + else + timeout.setInactivityTimeout(60*1000); + } + if (_redirectLocation != null) { throw new IOException("Server redirect to " + _redirectLocation + " not allowed"); } @@ -506,7 +508,8 @@ public class SSLEepGet extends EepGet { int read = _proxyIn.read(buf, 0, toRead); if (read == -1) break; - timeout.resetTimer(); + if (timeout != null) + timeout.resetTimer(); _out.write(buf, 0, read); _bytesTransferred += read; @@ -531,7 +534,8 @@ public class SSLEepGet extends EepGet { read++; } } - timeout.resetTimer(); + if (timeout != null) + timeout.resetTimer(); if (_bytesRemaining >= read) // else chunked? _bytesRemaining -= read; if (read > 0) { @@ -556,7 +560,8 @@ public class SSLEepGet extends EepGet { if (_aborted) throw new IOException("Timed out reading the HTTP data"); - timeout.cancel(); + if (timeout != null) + timeout.cancel(); if (_transferFailed) { // 404, etc - transferFailed is called after all attempts fail, by fetch() above diff --git a/core/java/src/net/i2p/util/ShellCommand.java b/core/java/src/net/i2p/util/ShellCommand.java index 12b668f67ee7b9345b4d3c301e3969b182c7f96c..bb0aec5f80b8bd420ece4131da52c9d28fdcae03 100644 --- a/core/java/src/net/i2p/util/ShellCommand.java +++ b/core/java/src/net/i2p/util/ShellCommand.java @@ -89,7 +89,7 @@ public class ShellCommand { * * @author hypercubus */ - private class StreamConsumer extends Thread { + private static class StreamConsumer extends Thread { private BufferedReader bufferedReader; private InputStreamReader inputStreamReader; @@ -123,7 +123,7 @@ public class ShellCommand { * * @author hypercubus */ - private class StreamReader extends Thread { + private static class StreamReader extends Thread { private BufferedReader bufferedReader; private InputStreamReader inputStreamReader; @@ -159,7 +159,7 @@ public class ShellCommand { * * @author hypercubus */ - private class StreamWriter extends Thread { + private static class StreamWriter extends Thread { private BufferedWriter bufferedWriter; private BufferedReader in; @@ -183,7 +183,7 @@ public class ShellCommand { bufferedWriter.write(input, 0, input.length()); bufferedWriter.flush(); } - } catch (Exception e) { + } catch (IOException e) { try { bufferedWriter.flush(); } catch (IOException e1) { diff --git a/core/java/src/net/i2p/util/SimpleTimer.java b/core/java/src/net/i2p/util/SimpleTimer.java index 0b543071199669c8ea3e802ead0b4aae82c7b254..f428afceb91be35badc734c848d48d3aa33123bd 100644 --- a/core/java/src/net/i2p/util/SimpleTimer.java +++ b/core/java/src/net/i2p/util/SimpleTimer.java @@ -90,7 +90,7 @@ public class SimpleTimer { int totalEvents = 0; long now = System.currentTimeMillis(); long eventTime = now + timeoutMs; - Long time = new Long(eventTime); + Long time = Long.valueOf(eventTime); synchronized (_events) { // remove the old scheduled position, then reinsert it Long oldTime = (Long)_eventTimes.get(event); diff --git a/core/java/src/net/i2p/util/SimpleTimer2.java b/core/java/src/net/i2p/util/SimpleTimer2.java index bda41e6211b5a58c8ebd483d4118094eaa9e4e60..44e405b2484bb1287f8154f93e114026f538aaa1 100644 --- a/core/java/src/net/i2p/util/SimpleTimer2.java +++ b/core/java/src/net/i2p/util/SimpleTimer2.java @@ -55,7 +55,7 @@ public class SimpleTimer2 { _executor.shutdownNow(); } - private class CustomScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { + private static class CustomScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor { public CustomScheduledThreadPoolExecutor(int threads, ThreadFactory factory) { super(threads, factory); } diff --git a/installer/resources/jetty.xml b/installer/resources/jetty.xml index c50679f054bc2a891255508864d157e0b0d21872..a5072d8d757d197a3a0891b827578cb0364ed67c 100644 --- a/installer/resources/jetty.xml +++ b/installer/resources/jetty.xml @@ -12,7 +12,9 @@ <!-- been patched to allow IPv6 addresses as well, --> <!-- enclosed in brackets e.g. [::1] --> <!-- * port: Default 7658 in the addListener section --> -<!-- * threads: Raise MaxThreads in the addListener section --> +<!-- * docroot: Change the ResourceBase in the addContext section --> +<!-- to serve files from a different location. --> +<!-- * threads: Raise MinThreads and/or MaxThreads in the addListener section --> <!-- if you have a high-traffic site and get a lot of warnings. --> <!-- * Uncomment the addWebApplications section to use to enable --> <!-- war files placed in the webapps/ dir. --> @@ -23,7 +25,7 @@ <!-- found in Jetty 5, you may install and run Jetty 6 in a different JVM, --> <!-- or run any other web server such as Apache. If you do run another --> <!-- web server instead, be sure and disable the Jetty 5 server for your --> -<!-- eepsite on http://127.0.0.1/configclients.jsp . --> +<!-- eepsite on http://127.0.0.1:7657/configclients.jsp . --> <!-- --> <!-- Jetty errors and warnings will appear in wrapper.log, check there --> <!-- to diagnose problems. --> @@ -57,13 +59,12 @@ <Set name="port">7658</Set> </New> </Arg> - <Set name="MinThreads">3</Set> - <Set name="MaxThreads">10</Set> + <Set name="MinThreads">1</Set> + <Set name="MaxThreads">16</Set> <Set name="MaxIdleTimeMs">60000</Set> <Set name="LowResourcePersistTimeMs">1000</Set> <Set name="ConfidentialPort">8443</Set> <Set name="IntegralPort">8443</Set> - <Set name="PoolName">main</Set> </New> </Arg> </Call> diff --git a/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java b/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java index 0a2ee498b7c163c13292cbe7cda582c0044bfe92..b0a4cfc6d1ddbfdf9b781f74ff3f9493e35d6b97 100644 --- a/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java +++ b/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java @@ -12,6 +12,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import net.i2p.I2PAppContext; +import net.i2p.data.DatabaseEntry; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.Hash; @@ -28,59 +29,39 @@ import net.i2p.data.TunnelId; public class DatabaseStoreMessage extends I2NPMessageImpl { public final static int MESSAGE_TYPE = 1; private Hash _key; - private int _type; - private LeaseSet _leaseSet; - private RouterInfo _info; - private byte[] _leaseSetCache; - private byte[] _routerInfoCache; + private DatabaseEntry _dbEntry; + private byte[] _byteCache; private long _replyToken; private TunnelId _replyTunnel; private Hash _replyGateway; - public final static int KEY_TYPE_ROUTERINFO = 0; - public final static int KEY_TYPE_LEASESET = 1; - public DatabaseStoreMessage(I2PAppContext context) { super(context); - setValueType(-1); } /** * Defines the key in the network database being stored * */ - public Hash getKey() { return _key; } - public void setKey(Hash key) { _key = key; } - - /** - * Defines the router info value in the network database being stored - * - */ - public RouterInfo getRouterInfo() { return _info; } - public void setRouterInfo(RouterInfo routerInfo) { - _info = routerInfo; - if (_info != null) - setValueType(KEY_TYPE_ROUTERINFO); + public Hash getKey() { + if (_key != null) + return _key; // receive + if (_dbEntry != null) + return _dbEntry.getHash(); // create + return null; } /** - * Defines the lease set value in the network database being stored - * + * Defines the entry in the network database being stored */ - public LeaseSet getLeaseSet() { return _leaseSet; } - public void setLeaseSet(LeaseSet leaseSet) { - _leaseSet = leaseSet; - if (_leaseSet != null) - setValueType(KEY_TYPE_LEASESET); - } - + public DatabaseEntry getEntry() { return _dbEntry; } + /** - * Defines type of key being stored in the network database - - * either KEY_TYPE_ROUTERINFO or KEY_TYPE_LEASESET - * + * This also sets the key */ - public int getValueType() { return _type; } - public void setValueType(int type) { _type = type; } + public void setEntry(DatabaseEntry entry) { + _dbEntry = entry; + } /** * If a reply is desired, this token specifies the message ID that should @@ -90,6 +71,7 @@ public class DatabaseStoreMessage extends I2NPMessageImpl { * @return positive reply token ID, or 0 if no reply is necessary. */ public long getReplyToken() { return _replyToken; } + /** * Update the reply token. * @@ -113,13 +95,10 @@ public class DatabaseStoreMessage extends I2NPMessageImpl { if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message"); int curIndex = offset; - //byte keyData[] = new byte[Hash.HASH_LENGTH]; - //System.arraycopy(data, curIndex, keyData, 0, Hash.HASH_LENGTH); _key = Hash.create(data, curIndex); curIndex += Hash.HASH_LENGTH; - //_key = new Hash(keyData); - _type = (int)DataHelper.fromLong(data, curIndex, 1); + type = (int)DataHelper.fromLong(data, curIndex, 1); curIndex++; _replyToken = DataHelper.fromLong(data, curIndex, 4); @@ -131,39 +110,38 @@ public class DatabaseStoreMessage extends I2NPMessageImpl { _replyTunnel = new TunnelId(tunnel); curIndex += 4; - //byte gw[] = new byte[Hash.HASH_LENGTH]; - //System.arraycopy(data, curIndex, gw, 0, Hash.HASH_LENGTH); _replyGateway = Hash.create(data, curIndex); curIndex += Hash.HASH_LENGTH; - //_replyGateway = new Hash(gw); } else { _replyTunnel = null; _replyGateway = null; } - if (_type == KEY_TYPE_LEASESET) { - _leaseSet = new LeaseSet(); + if (type == DatabaseEntry.KEY_TYPE_LEASESET) { + _dbEntry = new LeaseSet(); try { - _leaseSet.readBytes(new ByteArrayInputStream(data, curIndex, data.length-curIndex)); + _dbEntry.readBytes(new ByteArrayInputStream(data, curIndex, data.length-curIndex)); } catch (DataFormatException dfe) { throw new I2NPMessageException("Error reading the leaseSet", dfe); } - } else if (_type == KEY_TYPE_ROUTERINFO) { - _info = new RouterInfo(); + } else if (type == DatabaseEntry.KEY_TYPE_ROUTERINFO) { + _dbEntry = new RouterInfo(); int compressedSize = (int)DataHelper.fromLong(data, curIndex, 2); curIndex += 2; try { byte decompressed[] = DataHelper.decompress(data, curIndex, compressedSize); - _info.readBytes(new ByteArrayInputStream(decompressed)); + _dbEntry.readBytes(new ByteArrayInputStream(decompressed)); } catch (DataFormatException dfe) { throw new I2NPMessageException("Error reading the routerInfo", dfe); } catch (IOException ioe) { throw new I2NPMessageException("Compressed routerInfo was corrupt", ioe); } } else { - throw new I2NPMessageException("Invalid type of key read from the structure - " + _type); + throw new I2NPMessageException("Invalid type of key read from the structure - " + type); } + //if (!key.equals(_dbEntry.getHash())) + // throw new I2NPMessageException("Hash mismatch in DSM"); } @@ -172,28 +150,28 @@ public class DatabaseStoreMessage extends I2NPMessageImpl { int len = Hash.HASH_LENGTH + 1 + 4; // key+type+replyToken if (_replyToken > 0) len += 4 + Hash.HASH_LENGTH; // replyTunnel+replyGateway - if (_type == KEY_TYPE_LEASESET) { - _leaseSetCache = _leaseSet.toByteArray(); - len += _leaseSetCache.length; - } else if (_type == KEY_TYPE_ROUTERINFO) { - byte uncompressed[] = _info.toByteArray(); - byte compressed[] = DataHelper.compress(uncompressed); - _routerInfoCache = compressed; - len += compressed.length + 2; + if (_dbEntry.getType() == DatabaseEntry.KEY_TYPE_LEASESET) { + _byteCache = _dbEntry.toByteArray(); + } else if (_dbEntry.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { + byte uncompressed[] = _dbEntry.toByteArray(); + _byteCache = DataHelper.compress(uncompressed); + len += 2; } + len += _byteCache.length; return len; } + /** write the message body to the output array, starting at the given index */ protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException { - if (_key == null) throw new I2NPMessageException("Invalid key"); - if ( (_type != KEY_TYPE_LEASESET) && (_type != KEY_TYPE_ROUTERINFO) ) throw new I2NPMessageException("Invalid key type"); - if ( (_type == KEY_TYPE_LEASESET) && (_leaseSet == null) ) throw new I2NPMessageException("Missing lease set"); - if ( (_type == KEY_TYPE_ROUTERINFO) && (_info == null) ) throw new I2NPMessageException("Missing router info"); + if (_dbEntry == null) throw new I2NPMessageException("Missing entry"); + int type = _dbEntry.getType(); + if (type != DatabaseEntry.KEY_TYPE_LEASESET && type != DatabaseEntry.KEY_TYPE_ROUTERINFO) + throw new I2NPMessageException("Invalid key type"); - System.arraycopy(_key.getData(), 0, out, curIndex, Hash.HASH_LENGTH); + // Use the hash of the DatabaseEntry + System.arraycopy(getKey().getData(), 0, out, curIndex, Hash.HASH_LENGTH); curIndex += Hash.HASH_LENGTH; - byte type[] = DataHelper.toLong(1, _type); - out[curIndex++] = type[0]; + out[curIndex++] = (byte) type; byte tok[] = DataHelper.toLong(4, _replyToken); System.arraycopy(tok, 0, out, curIndex, 4); curIndex += 4; @@ -209,17 +187,14 @@ public class DatabaseStoreMessage extends I2NPMessageImpl { curIndex += Hash.HASH_LENGTH; } - if (_type == KEY_TYPE_LEASESET) { - // initialized in calculateWrittenLength - System.arraycopy(_leaseSetCache, 0, out, curIndex, _leaseSetCache.length); - curIndex += _leaseSetCache.length; - } else if (_type == KEY_TYPE_ROUTERINFO) { - byte len[] = DataHelper.toLong(2, _routerInfoCache.length); + // _byteCache initialized in calculateWrittenLength + if (type == DatabaseEntry.KEY_TYPE_ROUTERINFO) { + byte len[] = DataHelper.toLong(2, _byteCache.length); out[curIndex++] = len[0]; out[curIndex++] = len[1]; - System.arraycopy(_routerInfoCache, 0, out, curIndex, _routerInfoCache.length); - curIndex += _routerInfoCache.length; } + System.arraycopy(_byteCache, 0, out, curIndex, _byteCache.length); + curIndex += _byteCache.length; return curIndex; } @@ -228,9 +203,7 @@ public class DatabaseStoreMessage extends I2NPMessageImpl { @Override public int hashCode() { return DataHelper.hashCode(getKey()) + - DataHelper.hashCode(getLeaseSet()) + - DataHelper.hashCode(getRouterInfo()) + - getValueType() + + DataHelper.hashCode(_dbEntry) + (int)getReplyToken() + DataHelper.hashCode(getReplyTunnel()) + DataHelper.hashCode(getReplyGateway()); @@ -241,9 +214,7 @@ public class DatabaseStoreMessage extends I2NPMessageImpl { if ( (object != null) && (object instanceof DatabaseStoreMessage) ) { DatabaseStoreMessage msg = (DatabaseStoreMessage)object; return DataHelper.eq(getKey(),msg.getKey()) && - DataHelper.eq(getLeaseSet(),msg.getLeaseSet()) && - DataHelper.eq(getRouterInfo(),msg.getRouterInfo()) && - _type == msg.getValueType() && + DataHelper.eq(_dbEntry,msg.getEntry()) && getReplyToken() == msg.getReplyToken() && DataHelper.eq(getReplyTunnel(), msg.getReplyTunnel()) && DataHelper.eq(getReplyGateway(), msg.getReplyGateway()); @@ -259,9 +230,7 @@ public class DatabaseStoreMessage extends I2NPMessageImpl { buf.append("\n\tExpiration: ").append(getMessageExpiration()); buf.append("\n\tUnique ID: ").append(getUniqueId()); buf.append("\n\tKey: ").append(getKey()); - buf.append("\n\tValue Type: ").append(getValueType()); - buf.append("\n\tRouter Info: ").append(getRouterInfo()); - buf.append("\n\tLease Set: ").append(getLeaseSet()); + buf.append("\n\tEntry: ").append(_dbEntry); buf.append("\n\tReply token: ").append(getReplyToken()); buf.append("\n\tReply tunnel: ").append(getReplyTunnel()); buf.append("\n\tReply gateway: ").append(getReplyGateway()); diff --git a/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java b/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java index babf99dae2402a5450bf0b9f32adda30f6a723e8..eebb05f7e4ebdbf4c1fd618fdf7deaa76444535e 100644 --- a/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java +++ b/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java @@ -122,7 +122,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM if (!eq) throw new I2NPMessageException("Hash does not match for " + getClass().getName()); - long start = _context.clock().now(); + //long start = _context.clock().now(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Reading bytes: type = " + type + " / uniqueId : " + _uniqueId + " / expiration : " + _expiration); readMessage(buffer, 0, size, type); @@ -184,7 +184,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM if (!eq) throw new I2NPMessageException("Hash does not match for " + getClass().getName()); - long start = _context.clock().now(); + //long start = _context.clock().now(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Reading bytes: type = " + type + " / uniqueId : " + _uniqueId + " / expiration : " + _expiration); readMessage(data, cur, size, type); @@ -240,7 +240,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM } public int toByteArray(byte buffer[]) { - long start = _context.clock().now(); + //long start = _context.clock().now(); int prefixLen = 1 // type + 4 // uniqueId diff --git a/router/java/src/net/i2p/data/i2np/TunnelDataMessage.java b/router/java/src/net/i2p/data/i2np/TunnelDataMessage.java index 1ac01d0a6c87772a14604d72716473522ef07d2e..ef4930594f0f0491d17e18341dfb6803370b8d72 100644 --- a/router/java/src/net/i2p/data/i2np/TunnelDataMessage.java +++ b/router/java/src/net/i2p/data/i2np/TunnelDataMessage.java @@ -160,7 +160,7 @@ public class TunnelDataMessage extends I2NPMessageImpl { /** write the message body to the output array, starting at the given index */ protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException { if ( (_tunnelId <= 0) || (_data == null) ) - throw new I2NPMessageException("Not enough data to write out (id=" + _tunnelId + " data=" + _data + ")"); + throw new I2NPMessageException("Not enough data to write out (id=" + _tunnelId + ")"); if (_data.length <= 0) throw new I2NPMessageException("Not enough data to write out (data.length=" + _data.length + ")"); diff --git a/router/java/src/net/i2p/router/Blocklist.java b/router/java/src/net/i2p/router/Blocklist.java index d401f1532ee9352acc207664662ba1b44e71eaa5..8027ac007dbbb080495c93196032ad5fd25cf813 100644 --- a/router/java/src/net/i2p/router/Blocklist.java +++ b/router/java/src/net/i2p/router/Blocklist.java @@ -59,7 +59,7 @@ import net.i2p.util.Log; * */ public class Blocklist { - private Log _log; + private final Log _log; private RouterContext _context; private long _blocklist[]; private int _blocklistSize; @@ -72,15 +72,11 @@ public class Blocklist { public Blocklist(RouterContext context) { _context = context; _log = context.logManager().getLog(Blocklist.class); - _blocklist = null; - _blocklistSize = 0; - _wrapSave = null; } + /** only for testing with main() */ public Blocklist() { _log = new Log(Blocklist.class); - _blocklist = null; - _blocklistSize = 0; } static final String PROP_BLOCKLIST_ENABLED = "router.blocklist.enable"; @@ -683,7 +679,7 @@ public class Blocklist { return; Job job = new ShitlistJob(peer); if (number > 0) - job.getTiming().setStartAfter(_context.clock().now() + (number * 30*1000)); + job.getTiming().setStartAfter(_context.clock().now() + (30*1000l * number)); _context.jobQueue().addJob(job); } diff --git a/router/java/src/net/i2p/router/ClientMessage.java b/router/java/src/net/i2p/router/ClientMessage.java index ec7820d696a47e8ff065f216e025b85d208d5c91..5b5a228a199c2e0117afcca2937e68265ed405dc 100644 --- a/router/java/src/net/i2p/router/ClientMessage.java +++ b/router/java/src/net/i2p/router/ClientMessage.java @@ -28,16 +28,10 @@ public class ClientMessage { private Hash _destinationHash; private MessageId _messageId; private long _expiration; + /** only for outbound messages */ + private int _flags; public ClientMessage() { - setPayload(null); - setDestination(null); - setFromDestination(null); - setReceptionInfo(null); - setSenderConfig(null); - setDestinationHash(null); - setMessageId(null); - setExpiration(0); } /** @@ -101,4 +95,17 @@ public class ClientMessage { */ public long getExpiration() { return _expiration; } public void setExpiration(long e) { _expiration = e; } + + /** + * Flags requested by the client that sent the message. This will only be available + * for locally originated messages. + * + * @since 0.8.4 + */ + public int getFlags() { return _flags; } + + /** + * @since 0.8.4 + */ + public void setFlags(int f) { _flags = f; } } diff --git a/router/java/src/net/i2p/router/ClientMessagePool.java b/router/java/src/net/i2p/router/ClientMessagePool.java index f4dfd0e4402b1a4e802bff5fda1f2312a9d4e554..750c76915eea8188661749d542d7a8bebbe3f005 100644 --- a/router/java/src/net/i2p/router/ClientMessagePool.java +++ b/router/java/src/net/i2p/router/ClientMessagePool.java @@ -23,12 +23,13 @@ import net.i2p.util.Log; * */ public class ClientMessagePool { - private Log _log; - private RouterContext _context; + private final Log _log; + private final RouterContext _context; public ClientMessagePool(RouterContext context) { _context = context; _log = _context.logManager().getLog(ClientMessagePool.class); + OutboundClientMessageOneShotJob.init(_context); } /** @@ -65,6 +66,7 @@ public class ClientMessagePool { } } +/****** private boolean isGuaranteed(ClientMessage msg) { Properties opts = null; if (msg.getSenderConfig() != null) @@ -76,4 +78,5 @@ public class ClientMessagePool { return false; } } +******/ } diff --git a/router/java/src/net/i2p/router/DummyNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/DummyNetworkDatabaseFacade.java index 6031cf630e26c2a64f424c2c28ce593951e5e74e..0f06f507cc854b7fd1557f8c150af8f71052cbda 100644 --- a/router/java/src/net/i2p/router/DummyNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/DummyNetworkDatabaseFacade.java @@ -16,6 +16,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.RouterInfo; @@ -36,6 +37,7 @@ class DummyNetworkDatabaseFacade extends NetworkDatabaseFacade { _routers.put(info.getIdentity().getHash(), info); } + public DatabaseEntry lookupLocally(Hash key) { return null; } public void lookupLeaseSet(Hash key, Job onFindJob, Job onFailedLookupJob, long timeoutMs) {} public LeaseSet lookupLeaseSetLocally(Hash key) { return null; } public void lookupRouterInfo(Hash key, Job onFindJob, Job onFailedLookupJob, long timeoutMs) { diff --git a/router/java/src/net/i2p/router/InNetMessagePool.java b/router/java/src/net/i2p/router/InNetMessagePool.java index 47b799da5cc09c1d5a93836f866cc20eaeb2ff37..4e6846b461461b3666487f50d70203c5c58812d1 100644 --- a/router/java/src/net/i2p/router/InNetMessagePool.java +++ b/router/java/src/net/i2p/router/InNetMessagePool.java @@ -30,15 +30,17 @@ import net.i2p.util.Log; * */ public class InNetMessagePool implements Service { - private Log _log; - private RouterContext _context; - private HandlerJobBuilder _handlerJobBuilders[]; + private final Log _log; + private final RouterContext _context; + private final HandlerJobBuilder _handlerJobBuilders[]; + /** following 5 unused unless DISPATCH_DIRECT == false */ private final List _pendingDataMessages; private final List _pendingDataMessagesFrom; private final List _pendingGatewayMessages; private SharedShortCircuitDataJob _shortCircuitDataJob; private SharedShortCircuitGatewayJob _shortCircuitGatewayJob; + private boolean _alive; private boolean _dispatchThreaded; diff --git a/router/java/src/net/i2p/router/JobQueue.java b/router/java/src/net/i2p/router/JobQueue.java index f56b25f9edb35a091c33a6750165be187b8d52ad..86c050b0868c244ad190a48ebcbc04eb88661de9 100644 --- a/router/java/src/net/i2p/router/JobQueue.java +++ b/router/java/src/net/i2p/router/JobQueue.java @@ -34,22 +34,22 @@ import net.i2p.util.Log; * */ public class JobQueue { - private Log _log; - private RouterContext _context; + private final Log _log; + private final RouterContext _context; /** Integer (runnerId) to JobQueueRunner for created runners */ private final Map<Integer, JobQueueRunner> _queueRunners; /** a counter to identify a job runner */ private volatile static int _runnerId = 0; /** list of jobs that are ready to run ASAP */ - private BlockingQueue<Job> _readyJobs; + private final BlockingQueue<Job> _readyJobs; /** list of jobs that are scheduled for running in the future */ - private List<Job> _timedJobs; + private final List<Job> _timedJobs; /** job name to JobStat for that job */ private final Map<String, JobStats> _jobStats; /** how many job queue runners can go concurrently */ private int _maxRunners = 1; - private QueuePumper _pumper; + private final QueuePumper _pumper; /** will we allow the # job runners to grow beyond 1? */ private boolean _allowParallelOperation; /** have we been killed or are we alive? */ @@ -208,7 +208,7 @@ public class JobQueue { * <code>false</code> if the job is finished or doesn't exist in the queue. */ public boolean isJobActive(Job job) { - if (_readyJobs.contains(job) | _timedJobs.contains(job)) + if (_readyJobs.contains(job) || _timedJobs.contains(job)) return true; for (JobQueueRunner runner: _queueRunners.values()) if (runner.getCurrentJob() == job) @@ -689,7 +689,7 @@ public class JobQueue { TreeMap<Long, Job> ordered = new TreeMap(); for (int i = 0; i < timedJobs.size(); i++) { Job j = timedJobs.get(i); - ordered.put(new Long(j.getTiming().getStartAfter()), j); + ordered.put(Long.valueOf(j.getTiming().getStartAfter()), j); } for (Iterator<Job> iter = ordered.values().iterator(); iter.hasNext(); ) { Job j = iter.next(); diff --git a/router/java/src/net/i2p/router/JobQueueRunner.java b/router/java/src/net/i2p/router/JobQueueRunner.java index ca0ee3f1bccf7a71e80f1f4bedc438bf2dc1ef12..e9b0d08fb99d921a166be0c944e7d20be9d28eb0 100644 --- a/router/java/src/net/i2p/router/JobQueueRunner.java +++ b/router/java/src/net/i2p/router/JobQueueRunner.java @@ -4,10 +4,10 @@ import net.i2p.util.Log; /** a do run run run a do run run */ class JobQueueRunner implements Runnable { - private Log _log; - private RouterContext _context; + private final Log _log; + private final RouterContext _context; private boolean _keepRunning; - private int _id; + private final int _id; private long _numJobs; private Job _currentJob; private Job _lastJob; @@ -19,9 +19,6 @@ class JobQueueRunner implements Runnable { _context = context; _id = id; _keepRunning = true; - _numJobs = 0; - _currentJob = null; - _lastJob = null; _log = _context.logManager().getLog(JobQueueRunner.class); _context.statManager().createRateStat("jobQueue.jobRun", "How long jobs take", "JobQueue", new long[] { 60*60*1000l, 24*60*60*1000l }); _context.statManager().createRateStat("jobQueue.jobRunSlow", "How long jobs that take over a second take", "JobQueue", new long[] { 60*60*1000l, 24*60*60*1000l }); diff --git a/router/java/src/net/i2p/router/JobStats.java b/router/java/src/net/i2p/router/JobStats.java index 19362cff371a623abc7a16067756f965fda0c775..f9cfdc9cc753e40b42351456ddbdf3dcffa867d2 100644 --- a/router/java/src/net/i2p/router/JobStats.java +++ b/router/java/src/net/i2p/router/JobStats.java @@ -4,7 +4,7 @@ import net.i2p.data.DataHelper; /** glorified struct to contain basic job stats */ class JobStats { - private String _job; + private final String _job; private volatile long _numRuns; private volatile long _totalTime; private volatile long _maxTime; diff --git a/router/java/src/net/i2p/router/JobTiming.java b/router/java/src/net/i2p/router/JobTiming.java index d979074a6968c70f79e2d368caed3c9e565ba492..fd410f131d35dfd5a071a5aa4837860ba7045463 100644 --- a/router/java/src/net/i2p/router/JobTiming.java +++ b/router/java/src/net/i2p/router/JobTiming.java @@ -18,7 +18,7 @@ public class JobTiming implements Clock.ClockUpdateListener { private long _start; private long _actualStart; private long _actualEnd; - private RouterContext _context; + private final RouterContext _context; public JobTiming(RouterContext context) { _context = context; diff --git a/router/java/src/net/i2p/router/KeyManager.java b/router/java/src/net/i2p/router/KeyManager.java index fd0e85a9eb6f31cefeac96dbc5cff92e4bc22564..0e0b4e5d6faee67e8866690d33da1dc140be538c 100644 --- a/router/java/src/net/i2p/router/KeyManager.java +++ b/router/java/src/net/i2p/router/KeyManager.java @@ -12,9 +12,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import net.i2p.data.DataFormatException; @@ -37,14 +35,14 @@ import net.i2p.util.SecureFileOutputStream; * */ public class KeyManager { - private Log _log; - private RouterContext _context; + private final Log _log; + private final RouterContext _context; private PrivateKey _privateKey; private PublicKey _publicKey; private SigningPrivateKey _signingPrivateKey; private SigningPublicKey _signingPublicKey; private final Map<Hash, LeaseSetKeys> _leaseSetKeys; // Destination --> LeaseSetKeys - private SynchronizeKeysJob _synchronizeJob; + private final SynchronizeKeysJob _synchronizeJob; public final static String PROP_KEYDIR = "router.keyBackupDir"; public final static String DEFAULT_KEYDIR = "keyBackup"; @@ -61,10 +59,6 @@ public class KeyManager { _context = context; _log = _context.logManager().getLog(KeyManager.class); _synchronizeJob = new SynchronizeKeysJob(); - setPrivateKey(null); - setPublicKey(null); - setSigningPrivateKey(null); - setSigningPublicKey(null); _leaseSetKeys = new ConcurrentHashMap(); } @@ -132,12 +126,6 @@ public class KeyManager { return _leaseSetKeys.get(dest); } - public Set<LeaseSetKeys> getAllKeys() { - HashSet keys = new HashSet(); - keys.addAll(_leaseSetKeys.values()); - return keys; - } - private class SynchronizeKeysJob extends JobImpl { public SynchronizeKeysJob() { super(KeyManager.this._context); diff --git a/router/java/src/net/i2p/router/MessageValidator.java b/router/java/src/net/i2p/router/MessageValidator.java index b673702a1f777dc2adbb12d93d7ec1f0a269c227..b619166122885a9c4508584c6929be35a0537c5a 100644 --- a/router/java/src/net/i2p/router/MessageValidator.java +++ b/router/java/src/net/i2p/router/MessageValidator.java @@ -13,14 +13,13 @@ import net.i2p.util.Log; * */ public class MessageValidator { - private Log _log; - private RouterContext _context; + private final Log _log; + private final RouterContext _context; private DecayingBloomFilter _filter; public MessageValidator(RouterContext context) { _log = context.logManager().getLog(MessageValidator.class); - _filter = null; _context = context; context.statManager().createRateStat("router.duplicateMessageId", "Note that a duplicate messageId was received", "Router", new long[] { 10*60*1000l, 60*60*1000l, 3*60*60*1000l, 24*60*60*1000l }); diff --git a/router/java/src/net/i2p/router/MultiRouter.java b/router/java/src/net/i2p/router/MultiRouter.java index d8d316ae8aa0291d51e3ce370f0fb7aea4d553b6..43f618bc2209725b75e6edd356fcff49cd6f2c6f 100644 --- a/router/java/src/net/i2p/router/MultiRouter.java +++ b/router/java/src/net/i2p/router/MultiRouter.java @@ -90,13 +90,17 @@ public class MultiRouter { private static Properties getEnv(String filename) { Properties props = new Properties(); + FileInputStream in = null; try { - props.load(new FileInputStream(filename)); + in = new FileInputStream(filename); + props.load(in); props.setProperty("time.disabled", "true"); return props; } catch (IOException ioe) { ioe.printStackTrace(); return null; + } finally { + if (in != null) try { in.close(); } catch (IOException ioe) {} } } diff --git a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java index 50b9d5364cc88f5bab7ada626721217509fa6e6b..741f9495347dd04ef6bec10af6dc107863c9f77e 100644 --- a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java @@ -13,6 +13,7 @@ import java.io.Writer; import java.util.Collections; import java.util.Set; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.RouterInfo; @@ -32,6 +33,11 @@ public abstract class NetworkDatabaseFacade implements Service { */ public abstract Set<Hash> findNearestRouters(Hash key, int maxNumRouters, Set<Hash> peersToIgnore); + /** + * @return RouterInfo, LeaseSet, or null + * @since 0.8.3 + */ + public abstract DatabaseEntry lookupLocally(Hash key); public abstract void lookupLeaseSet(Hash key, Job onFindJob, Job onFailedLookupJob, long timeoutMs); public abstract LeaseSet lookupLeaseSetLocally(Hash key); public abstract void lookupRouterInfo(Hash key, Job onFindJob, Job onFailedLookupJob, long timeoutMs); diff --git a/router/java/src/net/i2p/router/OutNetMessage.java b/router/java/src/net/i2p/router/OutNetMessage.java index 72f262cc5613084f9a6ecbdd78d057757854f4a1..0c430b07e1d0317cc93fa164d8a28cad4773c2e1 100644 --- a/router/java/src/net/i2p/router/OutNetMessage.java +++ b/router/java/src/net/i2p/router/OutNetMessage.java @@ -89,15 +89,18 @@ public class OutNetMessage { // only timestamp if we are debugging synchronized (this) { locked_initTimestamps(); - while (_timestamps.containsKey(eventName)) { - eventName = eventName + '.'; - } - _timestamps.put(eventName, new Long(now)); + // ??? + //while (_timestamps.containsKey(eventName)) { + // eventName = eventName + '.'; + //} + _timestamps.put(eventName, Long.valueOf(now)); _timestampOrder.add(eventName); } } return now - _created; } + + /** @deprecated unused */ public Map<String, Long> getTimestamps() { if (_log.shouldLog(Log.INFO)) { synchronized (this) { @@ -107,6 +110,8 @@ public class OutNetMessage { } return Collections.EMPTY_MAP; } + + /** @deprecated unused */ public Long getTimestamp(String eventName) { if (_log.shouldLog(Log.INFO)) { synchronized (this) { @@ -220,7 +225,7 @@ public class OutNetMessage { public void transportFailed(String transportStyle) { if (_failedTransports == null) - _failedTransports = new HashSet(1); + _failedTransports = new HashSet(2); _failedTransports.add(transportStyle); } /** not thread safe - dont fail transports and iterate over this at the same time */ @@ -368,7 +373,7 @@ public class OutNetMessage { @Override public boolean equals(Object obj) { if(obj == null) return false; - if(obj.getClass() != OutNetMessage.class) return false; + if(!(obj instanceof OutNetMessage)) return false; return obj == this; // two OutNetMessages are different even if they contain the same message } } diff --git a/router/java/src/net/i2p/router/RouterClock.java b/router/java/src/net/i2p/router/RouterClock.java index 06337f5a6eb055856ae5f2793227162dc685a382..fd15d7db382c0f4cb92ebbae34c81a9204814761 100644 --- a/router/java/src/net/i2p/router/RouterClock.java +++ b/router/java/src/net/i2p/router/RouterClock.java @@ -34,7 +34,7 @@ public class RouterClock extends Clock { private long _lastChanged; private int _lastStratum; - RouterContext _contextRC; // LINT field hides another field + private final RouterContext _contextRC; public RouterClock(RouterContext context) { super(context); diff --git a/router/java/src/net/i2p/router/RouterContext.java b/router/java/src/net/i2p/router/RouterContext.java index 127603ea7d88503ab0c80694b0f1cc0937033141..d57c62635540f378657d2404be0ff4fdee477a14 100644 --- a/router/java/src/net/i2p/router/RouterContext.java +++ b/router/java/src/net/i2p/router/RouterContext.java @@ -9,13 +9,9 @@ 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; -import net.i2p.router.peermanager.CapacityCalculator; -import net.i2p.router.peermanager.IntegrationCalculator; import net.i2p.router.peermanager.PeerManagerFacadeImpl; import net.i2p.router.peermanager.ProfileManagerImpl; import net.i2p.router.peermanager.ProfileOrganizer; -import net.i2p.router.peermanager.SpeedCalculator; import net.i2p.router.transport.CommSystemFacadeImpl; import net.i2p.router.transport.FIFOBandwidthLimiter; import net.i2p.router.transport.OutboundMessageRegistry; @@ -58,11 +54,6 @@ public class RouterContext extends I2PAppContext { private MessageValidator _messageValidator; private MessageStateMonitor _messageStateMonitor; private RouterThrottle _throttle; - private RouterClock _clockX; // LINT field hides another field, hope rename won't break anything. - private Calculator _integrationCalc; - private Calculator _speedCalc; - private Calculator _capacityCalc; - private static List<RouterContext> _contexts = new ArrayList(1); @@ -166,9 +157,6 @@ public class RouterContext extends I2PAppContext { _messageValidator = new MessageValidator(this); _throttle = new RouterThrottleImpl(this); //_throttle = new RouterDoSThrottle(this); - _integrationCalc = new IntegrationCalculator(this); - _speedCalc = new SpeedCalculator(this); - _capacityCalc = new CapacityCalculator(this); } /** @@ -290,13 +278,6 @@ public class RouterContext extends I2PAppContext { */ public RouterThrottle throttle() { return _throttle; } - /** how do we rank the integration of profiles? */ - public Calculator integrationCalculator() { return _integrationCalc; } - /** how do we rank the speed of profiles? */ - public Calculator speedCalculator() { return _speedCalc; } - /** how do we rank the capacity of profiles? */ - public Calculator capacityCalculator() { return _capacityCalc; } - @Override public String toString() { StringBuilder buf = new StringBuilder(512); @@ -320,8 +301,6 @@ public class RouterContext extends I2PAppContext { buf.append(_statPublisher).append('\n'); buf.append(_shitlist).append('\n'); buf.append(_messageValidator).append('\n'); - buf.append(_integrationCalc).append('\n'); - buf.append(_speedCalc).append('\n'); return buf.toString(); } @@ -371,23 +350,22 @@ public class RouterContext extends I2PAppContext { } /** - * The context's synchronized clock, which is kept context specific only to - * enable simulators to play with clock skew among different instances. - * - * It wouldn't be necessary to override clock(), except for the reason - * that it triggers initializeClock() of which we definitely - * need the local version to run. + * @return new Properties with system and context properties + * @since 0.8.4 */ @Override - public Clock clock() { - if (!_clockInitialized) initializeClock(); - return _clockX; + public Properties getProperties() { + Properties rv = super.getProperties(); + if (_router != null) + rv.putAll(_router.getConfigMap()); + return rv; } + @Override protected void initializeClock() { synchronized (this) { - if (_clockX == null) - _clockX = new RouterClock(this); + if (_clock == null) + _clock = new RouterClock(this); _clockInitialized = true; } } diff --git a/router/java/src/net/i2p/router/RouterThrottleImpl.java b/router/java/src/net/i2p/router/RouterThrottleImpl.java index d3d3129af31a263dde6fc0aa88a1c4f8bf802d93..777cc1a6afd0439945ea9c4a5f763fc8be925608 100644 --- a/router/java/src/net/i2p/router/RouterThrottleImpl.java +++ b/router/java/src/net/i2p/router/RouterThrottleImpl.java @@ -12,12 +12,12 @@ import net.i2p.util.Log; * */ class RouterThrottleImpl implements RouterThrottle { - private RouterContext _context; - private Log _log; + private final RouterContext _context; + private final Log _log; private String _tunnelStatus; /** - * arbitrary hard limit of 10 seconds - if its taking this long to get + * arbitrary hard limit - if it's taking this long to get * to a job, we're congested. * */ @@ -98,7 +98,7 @@ class RouterThrottleImpl implements RouterThrottle { if (_context.router().getUptime() < 20*60*1000) return TunnelHistory.TUNNEL_REJECT_BANDWIDTH; - long lag = _context.jobQueue().getMaxLag(); + //long lag = _context.jobQueue().getMaxLag(); // reject here if lag too high??? RateStat rs = _context.statManager().getRate("transport.sendProcessingTime"); diff --git a/router/java/src/net/i2p/router/RouterWatchdog.java b/router/java/src/net/i2p/router/RouterWatchdog.java index 14dc01c1e65deb8735ff9b89c89d81a2ddce0d04..482f3b3edd7d95a308828568adfe803da4130f5d 100644 --- a/router/java/src/net/i2p/router/RouterWatchdog.java +++ b/router/java/src/net/i2p/router/RouterWatchdog.java @@ -12,8 +12,8 @@ import net.i2p.util.Log; * */ class RouterWatchdog implements Runnable { - private Log _log; - private RouterContext _context; + private final Log _log; + private final RouterContext _context; private int _consecutiveErrors; private static final long MAX_JOB_RUN_LAG = 60*1000; diff --git a/router/java/src/net/i2p/router/Shitlist.java b/router/java/src/net/i2p/router/Shitlist.java index f675d1056578151db6f8c55a560de209d1d9b793..a5c1c1c42c44efb557253ba52ddc6905ceac1c06 100644 --- a/router/java/src/net/i2p/router/Shitlist.java +++ b/router/java/src/net/i2p/router/Shitlist.java @@ -10,13 +10,13 @@ package net.i2p.router; import java.io.IOException; import java.io.Writer; -import java.util.concurrent.ConcurrentHashMap; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import net.i2p.data.DataHelper; import net.i2p.data.Hash; @@ -31,9 +31,9 @@ import net.i2p.util.Log; * shitlist. */ public class Shitlist { - private Log _log; - private RouterContext _context; - private Map<Hash, Entry> _entries; + private final Log _log; + private final RouterContext _context; + private final Map<Hash, Entry> _entries; public static class Entry { /** when it should expire, per the i2p clock */ @@ -156,7 +156,7 @@ public class Shitlist { e.causeCode = reasonCode; e.transports = null; if (transport != null) { - e.transports = new ConcurrentHashSet(1); + e.transports = new ConcurrentHashSet(2); e.transports.add(transport); } diff --git a/router/java/src/net/i2p/router/StatisticsManager.java b/router/java/src/net/i2p/router/StatisticsManager.java index 5a00f154c84bbb299408be373b5eb789532676b7..86cbef8fe8ec0a1f723ab34059bcc0a935073d20 100644 --- a/router/java/src/net/i2p/router/StatisticsManager.java +++ b/router/java/src/net/i2p/router/StatisticsManager.java @@ -157,9 +157,12 @@ public class StatisticsManager implements Service { return stats; } +/***** private void includeRate(String rateName, Properties stats, long selectedPeriods[]) { includeRate(rateName, stats, selectedPeriods, false); } +*****/ + /** * @param fudgeQuantity the data being published in this stat is too sensitive to, uh * publish, so we're kludge the quantity (allowing the fairly safe @@ -258,7 +261,6 @@ public class StatisticsManager implements Service { // bah saturation buf.append("0;0;0;0;"); } - long numPeriods = rate.getLifetimePeriods(); buf.append(num(fudgeQuantity)).append(';'); return buf.toString(); } diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java index 8bef2776d5a5a3687da21a6fe72eb35049adcddb..a73dcde83c363be8facf71c560466fd4a84b7350 100644 --- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java +++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java @@ -280,8 +280,12 @@ class ClientConnectionRunner { MessageId id = new MessageId(); id.setMessageId(getNextMessageId()); long expiration = 0; - if (message instanceof SendMessageExpiresMessage) - expiration = ((SendMessageExpiresMessage) message).getExpiration().getTime(); + int flags = 0; + if (message.getType() == SendMessageExpiresMessage.MESSAGE_TYPE) { + SendMessageExpiresMessage msg = (SendMessageExpiresMessage) message; + expiration = msg.getExpirationTime(); + flags = msg.getFlags(); + } if (!_dontSendMSM) _acceptedPending.add(id); @@ -289,16 +293,17 @@ class ClientConnectionRunner { _log.debug("** Receiving message [" + id.getMessageId() + "] with payload of size [" + payload.getSize() + "]" + " for session [" + _sessionId.getSessionId() + "]"); - long beforeDistribute = _context.clock().now(); + //long beforeDistribute = _context.clock().now(); // the following blocks as described above SessionConfig cfg = _config; if (cfg != null) - _manager.distributeMessage(cfg.getDestination(), dest, payload, id, expiration); - long timeToDistribute = _context.clock().now() - beforeDistribute; - if (_log.shouldLog(Log.DEBUG)) - _log.warn("Time to distribute in the manager to " - + dest.calculateHash().toBase64() + ": " - + timeToDistribute); + _manager.distributeMessage(cfg.getDestination(), dest, payload, id, expiration, flags); + // else log error? + //long timeToDistribute = _context.clock().now() - beforeDistribute; + //if (_log.shouldLog(Log.DEBUG)) + // _log.warn("Time to distribute in the manager to " + // + dest.calculateHash().toBase64() + ": " + // + timeToDistribute); return id; } diff --git a/router/java/src/net/i2p/router/client/ClientManager.java b/router/java/src/net/i2p/router/client/ClientManager.java index a534bdfb19627fb9b9acab93d67a061167159959..6f4d4414f5c2644e31afeb7fda5c3ae5cf590885 100644 --- a/router/java/src/net/i2p/router/client/ClientManager.java +++ b/router/java/src/net/i2p/router/client/ClientManager.java @@ -193,7 +193,11 @@ class ClientManager { } } - void distributeMessage(Destination fromDest, Destination toDest, Payload payload, MessageId msgId, long expiration) { + /** + * Distribute message to a local or remote destination. + * @param flags ignored for local + */ + void distributeMessage(Destination fromDest, Destination toDest, Payload payload, MessageId msgId, long expiration, int flags) { // check if there is a runner for it ClientConnectionRunner runner = getRunner(toDest); if (runner != null) { @@ -204,6 +208,7 @@ class ClientManager { // sender went away return; } + // TODO can we just run this inline instead? _ctx.jobQueue().addJob(new DistributeLocal(toDest, runner, sender, fromDest, payload, msgId)); } else { // remote. w00t @@ -217,22 +222,22 @@ class ClientManager { ClientMessage msg = new ClientMessage(); msg.setDestination(toDest); msg.setPayload(payload); - msg.setReceptionInfo(null); msg.setSenderConfig(runner.getConfig()); msg.setFromDestination(runner.getConfig().getDestination()); msg.setMessageId(msgId); msg.setExpiration(expiration); + msg.setFlags(flags); _ctx.clientMessagePool().add(msg, true); } } private class DistributeLocal extends JobImpl { - private Destination _toDest; - private ClientConnectionRunner _to; - private ClientConnectionRunner _from; - private Destination _fromDest; - private Payload _payload; - private MessageId _msgId; + private final Destination _toDest; + private final ClientConnectionRunner _to; + private final ClientConnectionRunner _from; + private final Destination _fromDest; + private final Payload _payload; + private final MessageId _msgId; public DistributeLocal(Destination toDest, ClientConnectionRunner to, ClientConnectionRunner from, Destination fromDest, Payload payload, MessageId id) { super(_ctx); @@ -433,7 +438,9 @@ class ClientManager { } } + /** @deprecated unused */ public void renderStatusHTML(Writer out) throws IOException { +/****** StringBuilder buf = new StringBuilder(8*1024); buf.append("<u><b>Local destinations</b></u><br>"); @@ -479,6 +486,7 @@ class ClientManager { buf.append("\n<hr>\n"); out.write(buf.toString()); out.flush(); +******/ } public void messageReceived(ClientMessage msg) { diff --git a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java index e4b6b2a36d38edf3a2560bd1cedf83b401f96c3b..35492936c4a9ef02a7f787458ce2670212ec0079 100644 --- a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java +++ b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java @@ -207,6 +207,7 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade implements Inte } } + /** @deprecated unused */ @Override public void renderStatusHTML(Writer out) throws IOException { if (_manager != null) diff --git a/router/java/src/net/i2p/router/client/SSLClientListenerRunner.java b/router/java/src/net/i2p/router/client/SSLClientListenerRunner.java index 0dc053a3361e3b57d2d6d7931712cbe10d307b8a..8d423ef0b31d2183f29bd0f830de84b8c54672fc 100644 --- a/router/java/src/net/i2p/router/client/SSLClientListenerRunner.java +++ b/router/java/src/net/i2p/router/client/SSLClientListenerRunner.java @@ -142,12 +142,12 @@ class SSLClientListenerRunner extends ClientListenerRunner { private void exportCert(File ks) { File sdir = new SecureDirectory(_context.getConfigDir(), "certificates"); if (sdir.exists() || sdir.mkdir()) { + InputStream fis = null; try { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - InputStream fis = new FileInputStream(ks); + fis = new FileInputStream(ks); String ksPass = _context.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD); keyStore.load(fis, ksPass.toCharArray()); - fis.close(); Certificate cert = keyStore.getCertificate(KEY_ALIAS); if (cert != null) { File certFile = new File(sdir, ASCII_KEYFILE); @@ -159,6 +159,8 @@ class SSLClientListenerRunner extends ClientListenerRunner { _log.error("Error saving ASCII SSL keys", gse); } catch (IOException ioe) { _log.error("Error saving ASCII SSL keys", ioe); + } finally { + if (fis != null) try { fis.close(); } catch (IOException ioe) {} } } else { _log.error("Error saving ASCII SSL keys"); @@ -208,12 +210,12 @@ class SSLClientListenerRunner extends ClientListenerRunner { " in " + (new File(_context.getConfigDir(), "router.config")).getAbsolutePath()); return false; } + InputStream fis = null; try { SSLContext sslc = SSLContext.getInstance("TLS"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - InputStream fis = new FileInputStream(ks); + fis = new FileInputStream(ks); keyStore.load(fis, ksPass.toCharArray()); - fis.close(); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, keyPass.toCharArray()); sslc.init(kmf.getKeyManagers(), null, _context.random()); @@ -223,6 +225,8 @@ class SSLClientListenerRunner extends ClientListenerRunner { _log.error("Error loading SSL keys", gse); } catch (IOException ioe) { _log.error("Error loading SSL keys", ioe); + } finally { + if (fis != null) try { fis.close(); } catch (IOException ioe) {} } return false; } diff --git a/router/java/src/net/i2p/router/message/CloveSet.java b/router/java/src/net/i2p/router/message/CloveSet.java index 8c414edcdedd861d9f2ef2fd3d348610a8b80819..5df37525688678b3e5b8d80bdc4dc659e00d6122 100644 --- a/router/java/src/net/i2p/router/message/CloveSet.java +++ b/router/java/src/net/i2p/router/message/CloveSet.java @@ -18,15 +18,14 @@ import net.i2p.data.i2np.GarlicClove; * Wrap up the data contained in a CloveMessage after being decrypted * */ -public class CloveSet { - private List _cloves; +class CloveSet { + private final List _cloves; private Certificate _cert; private long _msgId; private long _expiration; public CloveSet() { - _cloves = new ArrayList(); - _cert = null; + _cloves = new ArrayList(4); _msgId = -1; _expiration = -1; } diff --git a/router/java/src/net/i2p/router/message/GarlicConfig.java b/router/java/src/net/i2p/router/message/GarlicConfig.java index b23d4ac9a34836a5599991d9ae8cfa75debfb1eb..93af2714c21cad9d613c02174d32d4cf281dbaa6 100644 --- a/router/java/src/net/i2p/router/message/GarlicConfig.java +++ b/router/java/src/net/i2p/router/message/GarlicConfig.java @@ -21,13 +21,13 @@ import net.i2p.data.i2np.DeliveryInstructions; * Define the contents of a garlic chunk that contains 1 or more sub garlics * */ -public class GarlicConfig { +class GarlicConfig { private RouterInfo _recipient; private PublicKey _recipientPublicKey; private Certificate _cert; private long _id; private long _expiration; - private List _cloveConfigs; + private final List _cloveConfigs; private DeliveryInstructions _instructions; private boolean _requestAck; private RouterInfo _replyThroughRouter; // router through which any replies will be sent before delivery to us @@ -39,7 +39,7 @@ public class GarlicConfig { public GarlicConfig() { _id = -1; _expiration = -1; - _cloveConfigs = new ArrayList(); + _cloveConfigs = new ArrayList(4); _replyBlockMessageId = -1; _replyBlockExpiration = -1; } diff --git a/router/java/src/net/i2p/router/message/GarlicMessageHandler.java b/router/java/src/net/i2p/router/message/GarlicMessageHandler.java index 1db82eddbd7dce73f100f7291be054849ce04d1f..9f435c3bd55a4163da2c060827ea1bb4d9fde675 100644 --- a/router/java/src/net/i2p/router/message/GarlicMessageHandler.java +++ b/router/java/src/net/i2p/router/message/GarlicMessageHandler.java @@ -21,7 +21,7 @@ import net.i2p.router.RouterContext; * */ public class GarlicMessageHandler implements HandlerJobBuilder { - private RouterContext _context; + private final RouterContext _context; public GarlicMessageHandler(RouterContext context) { _context = context; diff --git a/router/java/src/net/i2p/router/message/GarlicMessageParser.java b/router/java/src/net/i2p/router/message/GarlicMessageParser.java index 31192dbcf113e08e236deaebe92f9733f8a1e584..b56ed291c1d842b0dea6b1032372218ebabba8c9 100644 --- a/router/java/src/net/i2p/router/message/GarlicMessageParser.java +++ b/router/java/src/net/i2p/router/message/GarlicMessageParser.java @@ -24,9 +24,9 @@ import net.i2p.util.Log; * Read a GarlicMessage, decrypt it, and return the resulting CloveSet * */ -public class GarlicMessageParser { - private Log _log; - private RouterContext _context; +class GarlicMessageParser { + private final Log _log; + private final RouterContext _context; public GarlicMessageParser(RouterContext context) { _context = context; @@ -47,7 +47,7 @@ public class GarlicMessageParser { } if (decrData == null) { if (_log.shouldLog(Log.WARN)) - _log.warn("Decryption of garlic message failed (data = " + encData + ")", new Exception("Decrypt fail")); + _log.warn("Decryption of garlic message failed", new Exception("Decrypt fail")); return null; } else { try { diff --git a/router/java/src/net/i2p/router/message/GarlicMessageReceiver.java b/router/java/src/net/i2p/router/message/GarlicMessageReceiver.java index 5013ab409efbdfe51ebabba97d0a2ffb375cf473..def373cf7a825d31cc0996a0994edef3be09f27a 100644 --- a/router/java/src/net/i2p/router/message/GarlicMessageReceiver.java +++ b/router/java/src/net/i2p/router/message/GarlicMessageReceiver.java @@ -26,11 +26,11 @@ import net.i2p.util.Log; * */ public class GarlicMessageReceiver { - private RouterContext _context; - private Log _log; - private CloveReceiver _receiver; - private Hash _clientDestination; - private GarlicMessageParser _parser; + private final RouterContext _context; + private final Log _log; + private final CloveReceiver _receiver; + private final Hash _clientDestination; + private final GarlicMessageParser _parser; private final static int FORWARD_PRIORITY = 50; diff --git a/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java b/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java index 32959b3a4107a1c55b8ac3719d0167a375ccaa51..d879cb5fc5bd52816cbbde948ddf2afc26de834d 100644 --- a/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java +++ b/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java @@ -28,9 +28,9 @@ import net.i2p.util.Log; * need to be. soon) * */ -public class HandleGarlicMessageJob extends JobImpl implements GarlicMessageReceiver.CloveReceiver { - private Log _log; - private GarlicMessage _message; +class HandleGarlicMessageJob extends JobImpl implements GarlicMessageReceiver.CloveReceiver { + private final Log _log; + private final GarlicMessage _message; //private RouterIdentity _from; //private Hash _fromHash; //private Map _cloves; // map of clove Id --> Expiration of cloves we've already seen diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java b/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java index 9025fd22b35a77b537411359d43a3f340ca540cc..0ba55213e07003edb2d4e634ead4ec06d0a3fada 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java @@ -227,9 +227,8 @@ class OutboundClientMessageJobHelper { clove.setExpiration(expiration); clove.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); DatabaseStoreMessage msg = new DatabaseStoreMessage(ctx); - msg.setLeaseSet(replyLeaseSet); + msg.setEntry(replyLeaseSet); msg.setMessageExpiration(expiration); - msg.setKey(replyLeaseSet.getDestination().calculateHash()); clove.setPayload(msg); clove.setRecipientPublicKey(null); clove.setRequestAck(false); diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java index bfa09419932f6ac049f5358597f54e7de4289c1e..fc22cadfacabc3d2d517c4571c696f0494d67fdc 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java @@ -47,21 +47,21 @@ import net.i2p.util.SimpleTimer; * */ public class OutboundClientMessageOneShotJob extends JobImpl { - private Log _log; + private final Log _log; private long _overallExpiration; private ClientMessage _clientMessage; - private MessageId _clientMessageId; - private int _clientMessageSize; - private Destination _from; - private Destination _to; - private String _toString; + private final MessageId _clientMessageId; + private final int _clientMessageSize; + private final Destination _from; + private final Destination _to; + private final String _toString; /** target destination's leaseSet, if known */ private LeaseSet _leaseSet; /** Actual lease the message is being routed through */ private Lease _lease; private PayloadGarlicConfig _clove; private long _cloveId; - private long _start; + private final long _start; private boolean _finished; private long _leaseSetLookupBegin; private TunnelInfo _outTunnel; @@ -103,8 +103,6 @@ public class OutboundClientMessageOneShotJob extends JobImpl { */ private static final int BUNDLE_PROBABILITY_DEFAULT = 100; - private static final Object _initializeLock = new Object(); - private static boolean _initialized = false; private static final int CLEAN_INTERVAL = 5*60*1000; private static final int REPLY_REQUEST_INTERVAL = 60*1000; @@ -115,26 +113,6 @@ public class OutboundClientMessageOneShotJob extends JobImpl { super(ctx); _log = ctx.logManager().getLog(OutboundClientMessageOneShotJob.class); - synchronized (_initializeLock) { - if (!_initialized) { - SimpleScheduler.getInstance().addPeriodicEvent(new OCMOSJCacheCleaner(ctx), CLEAN_INTERVAL, CLEAN_INTERVAL); - ctx.statManager().createFrequencyStat("client.sendMessageFailFrequency", "How often does a client fail to send a message?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.sendMessageSize", "How large are messages sent by the client?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.sendAckTime", "Message round trip time", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.timeoutCongestionTunnel", "How lagged our tunnels are when a send times out?", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.timeoutCongestionMessage", "How fast we process messages locally when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.timeoutCongestionInbound", "How much faster we are receiving data than our average bps when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.leaseSetFoundLocally", "How often we tried to look for a leaseSet and found it locally?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.leaseSetFoundRemoteTime", "How long we tried to look for a remote leaseSet (when we succeeded)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.leaseSetFailedRemoteTime", "How long we tried to look for a remote leaseSet (when we failed)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.dispatchPrepareTime", "How long until we've queued up the dispatch job (since we started)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.dispatchTime", "How long until we've dispatched the message (since we started)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.dispatchSendTime", "How long the actual dispatching takes?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.dispatchNoTunnels", "How long after start do we run out of tunnels to send/receive with?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); - ctx.statManager().createRateStat("client.dispatchNoACK", "Repeated message sends to a peer (no ack required)", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l }); - _initialized = true; - } - } long timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT; _clientMessage = msg; _clientMessageId = msg.getMessageId(); @@ -149,10 +127,17 @@ public class OutboundClientMessageOneShotJob extends JobImpl { // otherwise router config, otherwise default _overallExpiration = msg.getExpiration(); if (_overallExpiration > 0) { - _overallExpiration = Math.max(_overallExpiration, _start + OVERALL_TIMEOUT_MS_MIN); - _overallExpiration = Math.min(_overallExpiration, _start + OVERALL_TIMEOUT_MS_DEFAULT); - if (_log.shouldLog(Log.INFO)) - _log.info(getJobId() + ": Message Expiration (ms): " + (_overallExpiration - _start)); + // Unless it's already expired, set a min and max expiration + if (_overallExpiration <= _start) { + _overallExpiration = Math.max(_overallExpiration, _start + OVERALL_TIMEOUT_MS_MIN); + _overallExpiration = Math.min(_overallExpiration, _start + OVERALL_TIMEOUT_MS_DEFAULT); + if (_log.shouldLog(Log.INFO)) + _log.info(getJobId() + ": Message Expiration (ms): " + (_overallExpiration - _start)); + } else { + if (_log.shouldLog(Log.WARN)) + _log.warn(getJobId() + ": Expired before we got to it"); + // runJob() will call dieFatal() + } } else { String param = msg.getSenderConfig().getOptions().getProperty(OVERALL_TIMEOUT_MS_PARAM); if (param == null) @@ -171,15 +156,38 @@ public class OutboundClientMessageOneShotJob extends JobImpl { if (_log.shouldLog(Log.INFO)) _log.info(getJobId() + " Default Expiration (ms): " + timeoutMs); } - _finished = false; } + /** call once only */ + public static void init(RouterContext ctx) { + SimpleScheduler.getInstance().addPeriodicEvent(new OCMOSJCacheCleaner(ctx), CLEAN_INTERVAL, CLEAN_INTERVAL); + ctx.statManager().createFrequencyStat("client.sendMessageFailFrequency", "How often does a client fail to send a message?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.sendMessageSize", "How large are messages sent by the client?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.sendAckTime", "Message round trip time", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.timeoutCongestionTunnel", "How lagged our tunnels are when a send times out?", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.timeoutCongestionMessage", "How fast we process messages locally when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.timeoutCongestionInbound", "How much faster we are receiving data than our average bps when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.leaseSetFoundLocally", "How often we tried to look for a leaseSet and found it locally?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.leaseSetFoundRemoteTime", "How long we tried to look for a remote leaseSet (when we succeeded)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.leaseSetFailedRemoteTime", "How long we tried to look for a remote leaseSet (when we failed)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.dispatchPrepareTime", "How long until we've queued up the dispatch job (since we started)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.dispatchTime", "How long until we've dispatched the message (since we started)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.dispatchSendTime", "How long the actual dispatching takes?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.dispatchNoTunnels", "How long after start do we run out of tunnels to send/receive with?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l }); + ctx.statManager().createRateStat("client.dispatchNoACK", "Repeated message sends to a peer (no ack required)", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l }); + } + public String getName() { return "Outbound client message"; } public void runJob() { + long now = getContext().clock().now(); + if (now >= _overallExpiration) { + dieFatal(); + return; + } if (_log.shouldLog(Log.DEBUG)) _log.debug(getJobId() + ": Send outbound client message job beginning"); - long timeoutMs = _overallExpiration - getContext().clock().now(); + long timeoutMs = _overallExpiration - now; if (_log.shouldLog(Log.DEBUG)) _log.debug(getJobId() + ": preparing to search for the leaseSet for " + _toString); Hash key = _to.calculateHash(); @@ -249,7 +257,6 @@ public class OutboundClientMessageOneShotJob extends JobImpl { } // If the last leaseSet we sent him is still good, don't bother sending again - long now = getContext().clock().now(); synchronized (_leaseSetCache) { if (!force) { LeaseSet ls = _leaseSetCache.get(hashPair()); @@ -326,7 +333,6 @@ public class OutboundClientMessageOneShotJob extends JobImpl { _log.warn(getJobId() + ": Lookup locally didn't find the leaseSet for " + _toString); return false; } - long now = getContext().clock().now(); // Use the same lease if it's still good // Even if _leaseSet changed, _leaseSet.getEncryptionKey() didn't... @@ -373,7 +379,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { // randomize the ordering (so leases with equal # of failures per next // sort are randomly ordered) - Collections.shuffle(leases); + Collections.shuffle(leases, getContext().random()); /**** if (false) { @@ -793,7 +799,6 @@ public class OutboundClientMessageOneShotJob extends JobImpl { private TunnelInfo selectOutboundTunnel(Destination to) { TunnelInfo tunnel; - long now = getContext().clock().now(); synchronized (_tunnelCache) { /** * If old tunnel is valid and no longer backlogged, use it. diff --git a/router/java/src/net/i2p/router/message/PayloadGarlicConfig.java b/router/java/src/net/i2p/router/message/PayloadGarlicConfig.java index a5e8368d3438355c4773bd3c64f32e05f6657679..fb92898c816ec711c95c03ef6cfd1c7bb1558520 100644 --- a/router/java/src/net/i2p/router/message/PayloadGarlicConfig.java +++ b/router/java/src/net/i2p/router/message/PayloadGarlicConfig.java @@ -19,7 +19,6 @@ public class PayloadGarlicConfig extends GarlicConfig { public PayloadGarlicConfig() { super(); - _payload = null; } /** diff --git a/router/java/src/net/i2p/router/message/SendMessageDirectJob.java b/router/java/src/net/i2p/router/message/SendMessageDirectJob.java index c67eeaa4ad986d0fa6cd57f8a512c791176ae4f7..5ded790ab8150840ac4469b19932e25e2fd09d1b 100644 --- a/router/java/src/net/i2p/router/message/SendMessageDirectJob.java +++ b/router/java/src/net/i2p/router/message/SendMessageDirectJob.java @@ -22,16 +22,16 @@ import net.i2p.router.RouterContext; import net.i2p.util.Log; public class SendMessageDirectJob extends JobImpl { - private Log _log; - private I2NPMessage _message; - private Hash _targetHash; + private final Log _log; + private final I2NPMessage _message; + private final Hash _targetHash; private RouterInfo _router; - private long _expiration; - private int _priority; - private Job _onSend; - private ReplyJob _onSuccess; - private Job _onFail; - private MessageSelector _selector; + private final long _expiration; + private final int _priority; + private final Job _onSend; + private final ReplyJob _onSuccess; + private final Job _onFail; + private final MessageSelector _selector; private boolean _alreadySearched; private boolean _sent; private long _searchOn; @@ -47,7 +47,6 @@ public class SendMessageDirectJob extends JobImpl { _log = getContext().logManager().getLog(SendMessageDirectJob.class); _message = message; _targetHash = toPeer; - _router = null; if (timeoutMs < 10*1000) { if (_log.shouldLog(Log.WARN)) _log.warn("Very little time given [" + timeoutMs + "], resetting to 5s", new Exception("stingy bastard")); @@ -56,8 +55,6 @@ public class SendMessageDirectJob extends JobImpl { _expiration = timeoutMs + ctx.clock().now(); } _priority = priority; - _searchOn = 0; - _alreadySearched = false; _onSend = onSend; _onSuccess = onSuccess; _onFail = onFail; @@ -66,7 +63,6 @@ public class SendMessageDirectJob extends JobImpl { throw new IllegalArgumentException("Attempt to send a null message"); if (_targetHash == null) throw new IllegalArgumentException("Attempt to send a message to a null peer"); - _sent = false; } public String getName() { return "Send Message Direct"; } diff --git a/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java b/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java index ce907ae719a6101353db3467396cb8a4f8f7ae55..8cdc87e6f97eb7be0ae2bb4a40d09d29183e64d4 100644 --- a/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java +++ b/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java @@ -12,7 +12,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; -import net.i2p.data.DataStructure; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.RouterIdentity; @@ -227,20 +227,19 @@ public class HandleDatabaseLookupMessageJob extends JobImpl { return routerHashSet.contains(getContext().routerHash()); } - private void sendData(Hash key, DataStructure data, Hash toPeer, TunnelId replyTunnel) { + private void sendData(Hash key, DatabaseEntry data, Hash toPeer, TunnelId replyTunnel) { + if (!key.equals(data.getHash())) { + _log.error("Hash mismatch HDLMJ"); + return; + } if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending data matching key " + key.toBase64() + " to peer " + toPeer.toBase64() + " tunnel " + replyTunnel); DatabaseStoreMessage msg = new DatabaseStoreMessage(getContext()); - msg.setKey(key); - if (data instanceof LeaseSet) { - msg.setLeaseSet((LeaseSet)data); - msg.setValueType(DatabaseStoreMessage.KEY_TYPE_LEASESET); + if (data.getType() == DatabaseEntry.KEY_TYPE_LEASESET) { getContext().statManager().addRateData("netDb.lookupsMatchedLeaseSet", 1, 0); - } else if (data instanceof RouterInfo) { - msg.setRouterInfo((RouterInfo)data); - msg.setValueType(DatabaseStoreMessage.KEY_TYPE_ROUTERINFO); } + msg.setEntry(data); getContext().statManager().addRateData("netDb.lookupsMatched", 1, 0); getContext().statManager().addRateData("netDb.lookupsHandled", 1, 0); sendMessage(msg, toPeer, replyTunnel); diff --git a/router/java/src/net/i2p/router/networkdb/HandleDatabaseStoreMessageJob.java b/router/java/src/net/i2p/router/networkdb/HandleDatabaseStoreMessageJob.java index a61f947a59565ada3f5a83175f02b773f58c8e23..183d1da04754613c6c2054861ee00c580c8a3bf7 100644 --- a/router/java/src/net/i2p/router/networkdb/HandleDatabaseStoreMessageJob.java +++ b/router/java/src/net/i2p/router/networkdb/HandleDatabaseStoreMessageJob.java @@ -10,9 +10,11 @@ package net.i2p.router.networkdb; import java.util.Date; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.RouterIdentity; +import net.i2p.data.RouterInfo; import net.i2p.data.i2np.DatabaseStoreMessage; import net.i2p.data.i2np.DeliveryStatusMessage; import net.i2p.router.JobImpl; @@ -59,16 +61,17 @@ public class HandleDatabaseStoreMessageJob extends JobImpl { String invalidMessage = null; boolean wasNew = false; - if (_message.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET) { + DatabaseEntry entry = _message.getEntry(); + if (entry.getType() == DatabaseEntry.KEY_TYPE_LEASESET) { getContext().statManager().addRateData("netDb.storeLeaseSetHandled", 1, 0); try { - LeaseSet ls = _message.getLeaseSet(); + LeaseSet ls = (LeaseSet) entry; // mark it as something we received, so we'll answer queries // for it. this flag does NOT get set on entries that we // receive in response to our own lookups. ls.setReceivedAsPublished(true); - LeaseSet match = getContext().netDb().store(_message.getKey(), _message.getLeaseSet()); + LeaseSet match = getContext().netDb().store(_message.getKey(), ls); if (match == null) { wasNew = true; } else { @@ -78,13 +81,14 @@ public class HandleDatabaseStoreMessageJob extends JobImpl { } catch (IllegalArgumentException iae) { invalidMessage = iae.getMessage(); } - } else if (_message.getValueType() == DatabaseStoreMessage.KEY_TYPE_ROUTERINFO) { + } else if (entry.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { + RouterInfo ri = (RouterInfo) entry; getContext().statManager().addRateData("netDb.storeRouterInfoHandled", 1, 0); if (_log.shouldLog(Log.INFO)) _log.info("Handling dbStore of router " + _message.getKey() + " with publishDate of " - + new Date(_message.getRouterInfo().getPublished())); + + new Date(ri.getPublished())); try { - Object match = getContext().netDb().store(_message.getKey(), _message.getRouterInfo()); + Object match = getContext().netDb().store(_message.getKey(), ri); wasNew = (null == match); getContext().profileManager().heardAbout(_message.getKey()); } catch (IllegalArgumentException iae) { @@ -92,7 +96,7 @@ public class HandleDatabaseStoreMessageJob extends JobImpl { } } else { if (_log.shouldLog(Log.ERROR)) - _log.error("Invalid DatabaseStoreMessage data type - " + _message.getValueType() + _log.error("Invalid DatabaseStoreMessage data type - " + entry.getType() + ": " + _message); } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/DataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/DataStore.java index b4b55f3f8e77477cb805a71271b1ad28ccefae9c..ab064e2b0b1214565e3b696a0cadbd9570be7782 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/DataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/DataStore.java @@ -8,21 +8,27 @@ package net.i2p.router.networkdb.kademlia; * */ +import java.util.Collection; +import java.util.Map; import java.util.Set; -import net.i2p.data.DataStructure; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; public interface DataStore { public boolean isInitialized(); public boolean isKnown(Hash key); - public DataStructure get(Hash key); - public DataStructure get(Hash key, boolean persist); - public boolean put(Hash key, DataStructure data); - public boolean put(Hash key, DataStructure data, boolean persist); - public DataStructure remove(Hash key); - public DataStructure remove(Hash key, boolean persist); + public DatabaseEntry get(Hash key); + public DatabaseEntry get(Hash key, boolean persist); + public boolean put(Hash key, DatabaseEntry data); + public boolean put(Hash key, DatabaseEntry data, boolean persist); + public DatabaseEntry remove(Hash key); + public DatabaseEntry remove(Hash key, boolean persist); public Set<Hash> getKeys(); + /** @since 0.8.3 */ + public Collection<DatabaseEntry> getEntries(); + /** @since 0.8.3 */ + public Set<Map.Entry<Hash, DatabaseEntry>> getMapEntries(); public void stop(); public void restart(); public void rescan(); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/ExpireLeasesJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/ExpireLeasesJob.java index 7d4be13367b4f3678bff6817d9b29d58016290c0..59d8fc42919c9e16643605eb811776466ddcf989 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/ExpireLeasesJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/ExpireLeasesJob.java @@ -12,6 +12,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.router.JobImpl; @@ -61,8 +62,8 @@ class ExpireLeasesJob extends JobImpl { Set toExpire = new HashSet(128); for (Iterator iter = keys.iterator(); iter.hasNext(); ) { Hash key = (Hash)iter.next(); - Object obj = _facade.getDataStore().get(key); - if (obj instanceof LeaseSet) { + DatabaseEntry obj = _facade.getDataStore().get(key); + if (obj.getType() == DatabaseEntry.KEY_TYPE_LEASESET) { LeaseSet ls = (LeaseSet)obj; if (!ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) toExpire.add(key); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlyLookupMatchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlyLookupMatchJob.java index 20572667e7f3c3d1ecd8d3f2bb71a99fa5ad82bd..34b0e4e2b9c67f8621ac4ae09303df3a2f0e255b 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlyLookupMatchJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlyLookupMatchJob.java @@ -1,5 +1,8 @@ package net.i2p.router.networkdb.kademlia; +import net.i2p.data.DatabaseEntry; +import net.i2p.data.LeaseSet; +import net.i2p.data.RouterInfo; import net.i2p.data.i2np.DatabaseSearchReplyMessage; import net.i2p.data.i2np.DatabaseStoreMessage; import net.i2p.data.i2np.I2NPMessage; @@ -61,14 +64,15 @@ class FloodOnlyLookupMatchJob extends JobImpl implements ReplyJob { // We do it here first to make sure it is in the DB before // runJob() and search.success() is called??? // Should we just pass the DataStructure directly back to somebody? - if (dsm.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET) { + if (dsm.getEntry().getType() == DatabaseEntry.KEY_TYPE_LEASESET) { // Since HFDSMJ wants to setReceivedAsPublished(), we have to // set a flag saying this was really the result of a query, // so don't do that. - dsm.getLeaseSet().setReceivedAsReply(); - getContext().netDb().store(dsm.getKey(), dsm.getLeaseSet()); + LeaseSet ls = (LeaseSet) dsm.getEntry(); + ls.setReceivedAsReply(); + getContext().netDb().store(dsm.getKey(), ls); } else { - getContext().netDb().store(dsm.getKey(), dsm.getRouterInfo()); + getContext().netDb().store(dsm.getKey(), (RouterInfo) dsm.getEntry()); } } catch (IllegalArgumentException iae) { if (_log.shouldLog(Log.WARN)) diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodSearchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodSearchJob.java index bfa5c59ead02121e2f0a5c49870140b6ae7c6a58..1cecc2074137a32bf040825f19e34ab7cb6b0ce6 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodSearchJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodSearchJob.java @@ -182,8 +182,7 @@ public class FloodSearchJob extends JobImpl { _search = job; } public void runJob() { - if ( (getContext().netDb().lookupLeaseSetLocally(_search.getKey()) != null) || - (getContext().netDb().lookupRouterInfoLocally(_search.getKey()) != null) ) { + if (getContext().netDb().lookupLocally(_search.getKey()) != null) { _search.success(); } else { int remaining = _search.getLookupsRemaining(); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java index 540173ed63404457315894cf71bd784db7bc8788..5ba6701353326c768197adec60af97b8d0890d82 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java @@ -7,8 +7,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import net.i2p.data.DatabaseEntry; import net.i2p.data.DataFormatException; -import net.i2p.data.DataStructure; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.RouterInfo; @@ -93,11 +93,11 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad } @Override - public void sendStore(Hash key, DataStructure ds, Job onSuccess, Job onFailure, long sendTimeout, Set toIgnore) { + public void sendStore(Hash key, DatabaseEntry ds, Job onSuccess, Job onFailure, long sendTimeout, Set toIgnore) { // if we are a part of the floodfill netDb, don't send out our own leaseSets as part // of the flooding - instead, send them to a random floodfill peer so *they* can flood 'em out. // perhaps statistically adjust this so we are the source every 1/N times... or something. - if (floodfillEnabled() && (ds instanceof RouterInfo)) { + if (floodfillEnabled() && (ds.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO)) { flood(ds); if (onSuccess != null) _context.jobQueue().addJob(onSuccess); @@ -129,12 +129,8 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad * We do this to implement Kademlia within the floodfills, i.e. * we flood to those closest to the key. */ - public void flood(DataStructure ds) { - Hash key; - if (ds instanceof LeaseSet) - key = ((LeaseSet)ds).getDestination().calculateHash(); - else - key = ((RouterInfo)ds).getIdentity().calculateHash(); + public void flood(DatabaseEntry ds) { + Hash key = ds.getHash(); Hash rkey = _context.routingKeyGenerator().getRoutingKey(key); FloodfillPeerSelector sel = (FloodfillPeerSelector)getPeerSelector(); List peers = sel.selectFloodfillParticipants(rkey, MAX_TO_FLOOD, getKBuckets()); @@ -151,12 +147,7 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad if (peer.equals(_context.routerHash())) continue; DatabaseStoreMessage msg = new DatabaseStoreMessage(_context); - if (ds instanceof LeaseSet) { - msg.setLeaseSet((LeaseSet)ds); - } else { - msg.setRouterInfo((RouterInfo)ds); - } - msg.setKey(key); + msg.setEntry(ds); msg.setReplyGateway(null); msg.setReplyToken(0); msg.setReplyTunnel(null); @@ -242,13 +233,9 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad List<RouterInfo> rv = new ArrayList(); DataStore ds = getDataStore(); if (ds != null) { - Set keys = ds.getKeys(); - if (keys != null) { - for (Iterator iter = keys.iterator(); iter.hasNext(); ) { - Object o = ds.get((Hash)iter.next()); - if (o instanceof RouterInfo) - rv.add((RouterInfo)o); - } + for (DatabaseEntry o : ds.getEntries()) { + if (o.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) + rv.add((RouterInfo)o); } } return rv; diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java index 4eb4e482540127990ba30a6f407da9b0530974a9..9c4093899d2b15ba017629ee2ef50534bc02ac0e 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java @@ -11,6 +11,7 @@ package net.i2p.router.networkdb.kademlia; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; @@ -19,6 +20,7 @@ import net.i2p.data.Hash; import net.i2p.data.RouterInfo; import net.i2p.router.RouterContext; import net.i2p.router.peermanager.PeerProfile; +import net.i2p.router.util.RandomIterator; import net.i2p.stat.Rate; import net.i2p.util.Log; @@ -72,8 +74,10 @@ class FloodfillPeerSelector extends PeerSelector { */ List<Hash> selectNearestExplicitThin(Hash key, int maxNumRouters, Set<Hash> peersToIgnore, KBucketSet kbuckets, boolean preferConnected) { if (peersToIgnore == null) - peersToIgnore = new HashSet(1); - peersToIgnore.add(_context.routerHash()); + peersToIgnore = Collections.singleton(_context.routerHash()); + else + peersToIgnore.add(_context.routerHash()); + // TODO this is very slow FloodfillSelectionCollector matches = new FloodfillSelectionCollector(key, peersToIgnore, maxNumRouters); if (kbuckets == null) return new ArrayList(); kbuckets.getAll(matches); @@ -91,8 +95,7 @@ class FloodfillPeerSelector extends PeerSelector { * List is not sorted and not shuffled. */ List<Hash> selectFloodfillParticipants(KBucketSet kbuckets) { - Set<Hash> ignore = new HashSet(1); - ignore.add(_context.routerHash()); + Set<Hash> ignore = Collections.singleton(_context.routerHash()); return selectFloodfillParticipants(ignore, kbuckets); } @@ -104,6 +107,8 @@ class FloodfillPeerSelector extends PeerSelector { */ private List<Hash> selectFloodfillParticipants(Set<Hash> toIgnore, KBucketSet kbuckets) { if (kbuckets == null) return Collections.EMPTY_LIST; + // TODO this is very slow - use profile getPeersByCapability('f') instead + _context.statManager().addRateData("netDb.newFSC", 0, 0); FloodfillSelectionCollector matches = new FloodfillSelectionCollector(null, toIgnore, 0); kbuckets.getAll(matches); return matches.getFloodfillParticipants(); @@ -127,8 +132,7 @@ class FloodfillPeerSelector extends PeerSelector { * Group 3: All others */ List<Hash> selectFloodfillParticipants(Hash key, int maxNumRouters, KBucketSet kbuckets) { - Set<Hash> ignore = new HashSet(1); - ignore.add(_context.routerHash()); + Set<Hash> ignore = Collections.singleton(_context.routerHash()); return selectFloodfillParticipants(key, maxNumRouters, ignore, kbuckets); } @@ -147,8 +151,7 @@ class FloodfillPeerSelector extends PeerSelector { */ List<Hash> selectFloodfillParticipants(Hash key, int howMany, Set<Hash> toIgnore, KBucketSet kbuckets) { if (toIgnore == null) { - toIgnore = new HashSet(1); - toIgnore.add(_context.routerHash()); + toIgnore = Collections.singleton(_context.routerHash()); } else if (!toIgnore.contains(_context.routerHash())) { // copy the Set so we don't confuse StoreJob toIgnore = new HashSet(toIgnore); @@ -320,7 +323,6 @@ class FloodfillPeerSelector extends PeerSelector { * Group 4: Non-floodfills, sorted by closest-to-the-key */ public List<Hash> get(int howMany, boolean preferConnected) { - Collections.shuffle(_floodfillMatches, _context.random()); List<Hash> rv = new ArrayList(howMany); List<Hash> badff = new ArrayList(howMany); List<Hash> unconnectedff = new ArrayList(howMany); @@ -329,8 +331,8 @@ class FloodfillPeerSelector extends PeerSelector { // Only add in "good" floodfills here... // Let's say published in last 3h and no failed sends in last 30m // (Forever shitlisted ones are excluded in add() above) - for (int i = 0; found < howMany && i < _floodfillMatches.size(); i++) { - Hash entry = (Hash) _floodfillMatches.get(i); + for (Iterator<Hash> iter = new RandomIterator(_floodfillMatches); (found < howMany) && iter.hasNext(); ) { + Hash entry = iter.next(); RouterInfo info = _context.netDb().lookupRouterInfoLocally(entry); if (info != null && now - info.getPublished() > 3*60*60*1000) { badff.add(entry); @@ -391,6 +393,7 @@ class FloodfillPeerSelector extends PeerSelector { if (peersToIgnore != null && peersToIgnore.contains(Hash.FAKE_HASH)) { // return non-ff peersToIgnore.addAll(selectFloodfillParticipants(peersToIgnore, kbuckets)); + // TODO this is very slow FloodfillSelectionCollector matches = new FloodfillSelectionCollector(rkey, peersToIgnore, maxNumRouters); kbuckets.getAll(matches); return matches.get(maxNumRouters); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java index 14969df83a0ac39b86ac99f425a6166c594ddf46..babd35dd258497a6cb44c85db5c3ed2dac9b8b50 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java @@ -12,7 +12,7 @@ import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; -import net.i2p.data.DataStructure; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.RouterInfo; @@ -30,7 +30,7 @@ class FloodfillStoreJob extends StoreJob { * Send a data structure to the floodfills * */ - public FloodfillStoreJob(RouterContext context, FloodfillNetworkDatabaseFacade facade, Hash key, DataStructure data, Job onSuccess, Job onFailure, long timeoutMs) { + public FloodfillStoreJob(RouterContext context, FloodfillNetworkDatabaseFacade facade, Hash key, DatabaseEntry data, Job onSuccess, Job onFailure, long timeoutMs) { this(context, facade, key, data, onSuccess, onFailure, timeoutMs, null); } @@ -38,7 +38,7 @@ class FloodfillStoreJob extends StoreJob { * @param toSkip set of peer hashes of people we dont want to send the data to (e.g. we * already know they have it). This can be null. */ - public FloodfillStoreJob(RouterContext context, FloodfillNetworkDatabaseFacade facade, Hash key, DataStructure data, Job onSuccess, Job onFailure, long timeoutMs, Set<Hash> toSkip) { + public FloodfillStoreJob(RouterContext context, FloodfillNetworkDatabaseFacade facade, Hash key, DatabaseEntry data, Job onSuccess, Job onFailure, long timeoutMs, Set<Hash> toSkip) { super(context, facade, key, data, onSuccess, onFailure, timeoutMs, toSkip); _facade = facade; } @@ -63,15 +63,12 @@ class FloodfillStoreJob extends StoreJob { } // Get the time stamp from the data we sent, so the Verify job can meke sure that // it finds something stamped with that time or newer. - long published = 0; - DataStructure data = _state.getData(); - boolean isRouterInfo = data instanceof RouterInfo; + DatabaseEntry data = _state.getData(); + boolean isRouterInfo = data.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO; + long published = data.getDate(); if (isRouterInfo) { - published = ((RouterInfo) data).getPublished(); // Temporarily disable return; - } else if (data instanceof LeaseSet) { - published = ((LeaseSet) data).getEarliestLeaseDate(); } // we should always have exactly one successful entry Hash sentTo = null; diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java index fa944f384dafc0c53a653f3b593532597ac692bb..c7dca12718bb448d6d912d764c82edb6a2bfd63e 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillVerifyStoreJob.java @@ -4,7 +4,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import net.i2p.data.DataStructure; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.data.RouterInfo; import net.i2p.data.i2np.DatabaseLookupMessage; @@ -201,10 +201,7 @@ public class FloodfillVerifyStoreJob extends JobImpl { // Verify it's as recent as the one we sent boolean success = false; DatabaseStoreMessage dsm = (DatabaseStoreMessage)_message; - if (_isRouterInfo && dsm.getValueType() == DatabaseStoreMessage.KEY_TYPE_ROUTERINFO) - success = dsm.getRouterInfo().getPublished() >= _published; - else if ((!_isRouterInfo) && dsm.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET) - success = dsm.getLeaseSet().getEarliestLeaseDate() >= _published; + success = dsm.getEntry().getDate() >= _published; if (success) { // store ok, w00t! getContext().profileManager().dbLookupSuccessful(_target, delay); @@ -218,7 +215,7 @@ public class FloodfillVerifyStoreJob extends JobImpl { if (_log.shouldLog(Log.WARN)) _log.warn("Verify failed (older) for " + _key); if (_log.shouldLog(Log.INFO)) - _log.info("Rcvd older lease: " + dsm.getLeaseSet()); + _log.info("Rcvd older lease: " + dsm.getEntry()); } else if (_message instanceof DatabaseSearchReplyMessage) { // assume 0 old, all new, 0 invalid, 0 dup getContext().profileManager().dbLookupReply(_target, 0, @@ -245,11 +242,7 @@ public class FloodfillVerifyStoreJob extends JobImpl { * So at least we'll try THREE ffs round-robin if things continue to fail... */ private void resend() { - DataStructure ds; - if (_isRouterInfo) - ds = _facade.lookupRouterInfoLocally(_key); - else - ds = _facade.lookupLeaseSetLocally(_key); + DatabaseEntry ds = _facade.lookupLocally(_key); if (ds != null) { Set<Hash> toSkip = new HashSet(2); if (_sentTo != null) diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseLookupMessageJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseLookupMessageJob.java index 4e3c9c30ce377d539a505fd699df345d7aee8ea9..1409ecc2a153716777587490f0e21d49edbf600d 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseLookupMessageJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseLookupMessageJob.java @@ -57,9 +57,7 @@ public class HandleFloodfillDatabaseLookupMessageJob extends HandleDatabaseLooku // that would increment the netDb.lookupsHandled and netDb.lookupsMatched stats DatabaseStoreMessage msg = new DatabaseStoreMessage(getContext()); RouterInfo me = getContext().router().getRouterInfo(); - msg.setKey(me.getIdentity().getHash()); - msg.setRouterInfo(me); - msg.setValueType(DatabaseStoreMessage.KEY_TYPE_ROUTERINFO); + msg.setEntry(me); sendMessage(msg, toPeer, replyTunnel); } } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java index ea53c15667726b8dcf212e1fabcbfcee5d8921b1..a546ad7a27e2467322e1ce8eb500c644c3f6bed0 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java @@ -11,6 +11,7 @@ package net.i2p.router.networkdb.kademlia; import java.util.Date; import java.util.Set; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.RouterIdentity; @@ -55,7 +56,8 @@ public class HandleFloodfillDatabaseStoreMessageJob extends JobImpl { boolean wasNew = false; RouterInfo prevNetDb = null; Hash key = _message.getKey(); - if (_message.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET) { + DatabaseEntry entry = _message.getEntry(); + if (entry.getType() == DatabaseEntry.KEY_TYPE_LEASESET) { getContext().statManager().addRateData("netDb.storeLeaseSetHandled", 1, 0); if (_log.shouldLog(Log.INFO)) _log.info("Handling dbStore of leaseset " + _message); @@ -75,7 +77,7 @@ public class HandleFloodfillDatabaseStoreMessageJob extends JobImpl { throw new IllegalArgumentException("Peer attempted to store local leaseSet: " + key.toBase64().substring(0, 4)); } - LeaseSet ls = _message.getLeaseSet(); + LeaseSet ls = (LeaseSet) entry; //boolean oldrar = ls.getReceivedAsReply(); //boolean oldrap = ls.getReceivedAsPublished(); // If this was received as a response to a query, @@ -91,10 +93,10 @@ public class HandleFloodfillDatabaseStoreMessageJob extends JobImpl { //boolean rap = ls.getReceivedAsPublished(); //if (_log.shouldLog(Log.INFO)) // _log.info("oldrap? " + oldrap + " oldrar? " + oldrar + " newrap? " + rap); - LeaseSet match = getContext().netDb().store(key, _message.getLeaseSet()); + LeaseSet match = getContext().netDb().store(key, ls); if (match == null) { wasNew = true; - } else if (match.getEarliestLeaseDate() < _message.getLeaseSet().getEarliestLeaseDate()) { + } else if (match.getEarliestLeaseDate() < ls.getEarliestLeaseDate()) { wasNew = true; // If it is in our keyspace and we are talking to it @@ -117,11 +119,12 @@ public class HandleFloodfillDatabaseStoreMessageJob extends JobImpl { } catch (IllegalArgumentException iae) { invalidMessage = iae.getMessage(); } - } else if (_message.getValueType() == DatabaseStoreMessage.KEY_TYPE_ROUTERINFO) { + } else if (entry.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { + RouterInfo ri = (RouterInfo) entry; getContext().statManager().addRateData("netDb.storeRouterInfoHandled", 1, 0); if (_log.shouldLog(Log.INFO)) _log.info("Handling dbStore of router " + key + " with publishDate of " - + new Date(_message.getRouterInfo().getPublished())); + + new Date(ri.getPublished())); try { // Never store our RouterInfo received from somebody else. // This generally happens from a FloodfillVerifyStoreJob. @@ -132,8 +135,8 @@ public class HandleFloodfillDatabaseStoreMessageJob extends JobImpl { // throw rather than return, so that we send the ack below (prevent easy attack) throw new IllegalArgumentException("Peer attempted to store our RouterInfo"); } - prevNetDb = getContext().netDb().store(key, _message.getRouterInfo()); - wasNew = ((null == prevNetDb) || (prevNetDb.getPublished() < _message.getRouterInfo().getPublished())); + prevNetDb = getContext().netDb().store(key, ri); + wasNew = ((null == prevNetDb) || (prevNetDb.getPublished() < ri.getPublished())); // Check new routerinfo address against blocklist if (wasNew) { if (prevNetDb == null) { @@ -143,7 +146,7 @@ public class HandleFloodfillDatabaseStoreMessageJob extends JobImpl { _log.warn("Blocklisting new peer " + key); } else { Set oldAddr = prevNetDb.getAddresses(); - Set newAddr = _message.getRouterInfo().getAddresses(); + Set newAddr = ri.getAddresses(); if (newAddr != null && (!newAddr.equals(oldAddr)) && (!getContext().shitlist().isShitlistedForever(key)) && getContext().blocklist().isBlocklisted(key) && @@ -157,7 +160,7 @@ public class HandleFloodfillDatabaseStoreMessageJob extends JobImpl { } } else { if (_log.shouldLog(Log.ERROR)) - _log.error("Invalid DatabaseStoreMessage data type - " + _message.getValueType() + _log.error("Invalid DatabaseStoreMessage data type - " + entry.getType() + ": " + _message); } @@ -198,12 +201,9 @@ public class HandleFloodfillDatabaseStoreMessageJob extends JobImpl { return; } long floodBegin = System.currentTimeMillis(); - if (_message.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET) - _facade.flood(_message.getLeaseSet()); + _facade.flood(_message.getEntry()); // ERR: see comment in HandleDatabaseLookupMessageJob regarding hidden mode //else if (!_message.getRouterInfo().isHidden()) - else - _facade.flood(_message.getRouterInfo()); long floodEnd = System.currentTimeMillis(); getContext().statManager().addRateData("netDb.storeFloodNew", floodEnd-floodBegin, 0); } else { diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/HarvesterJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/HarvesterJob.java index 7ced65ed0fa8ca33e84462df758d0db23145b8bc..48ddc4f2c79fbd5e761944222e284181d72965a0 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/HarvesterJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/HarvesterJob.java @@ -85,9 +85,9 @@ class HarvesterJob extends JobImpl { long when = info.getPublished(); if (when + MIN_UPDATE_FREQUENCY > now) continue; - while (routersByAge.containsKey(new Long(when))) + while (routersByAge.containsKey(Long.valueOf(when))) when++; - routersByAge.put(new Long(when), info.getIdentity().getHash()); + routersByAge.put(Long.valueOf(when), info.getIdentity().getHash()); } } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KBucketImpl.java b/router/java/src/net/i2p/router/networkdb/kademlia/KBucketImpl.java index 568a283f9b17eabda7f203bf2a03ecdc81cb0b19..bd7c62fdeb5399644a99fc6da5b503df0514ca67 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KBucketImpl.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KBucketImpl.java @@ -387,15 +387,15 @@ class KBucketImpl implements KBucket { local.prepareCache(); KBucketImpl bucket = new KBucketImpl(I2PAppContext.getGlobalContext(), local); bucket.setRange(low, high); - Hash lowerBoundKey = bucket.getRangeBeginKey(); - Hash upperBoundKey = bucket.getRangeEndKey(); + //Hash lowerBoundKey = bucket.getRangeBeginKey(); + //Hash upperBoundKey = bucket.getRangeEndKey(); for (int i = 0; i < 100000; i++) { Hash rnd = bucket.generateRandomKey(); //buf.append(toString(rnd.getData())).append('\n'); boolean ok = bucket.shouldContain(rnd); if (!ok) { byte diff[] = bucket.getLocal().cachedXor(rnd); - BigInteger dv = new BigInteger(1, diff); + //BigInteger dv = new BigInteger(1, diff); //log.error("WTF! bucket doesn't want: \n" + toString(rnd.getData()) // + "\nDelta: \n" + toString(diff) + "\nDelta val: \n" + dv.toString(2) // + "\nBucket: \n"+bucket, new Exception("WTF")); @@ -403,7 +403,7 @@ class KBucketImpl implements KBucket { log.error("\nLow: " + DataHelper.toHexString(bucket.getRangeBeginKey().getData()) + "\nVal: " + DataHelper.toHexString(rnd.getData()) + "\nHigh:" + DataHelper.toHexString(bucket.getRangeEndKey().getData())); - try { Thread.sleep(1000); } catch (Exception e) {} + try { Thread.sleep(1000); } catch (InterruptedException e) {} System.exit(0); } else { //_log.debug("Ok, bucket wants: \n" + toString(rnd.getData())); @@ -415,7 +415,6 @@ class KBucketImpl implements KBucket { private static void testRand2() { Log log = I2PAppContext.getGlobalContext().logManager().getLog(KBucketImpl.class); - StringBuilder buf = new StringBuilder(1024*1024*16); int low = 1; int high = 200; byte hash[] = new byte[Hash.HASH_LENGTH]; @@ -424,15 +423,15 @@ class KBucketImpl implements KBucket { local.prepareCache(); KBucketImpl bucket = new KBucketImpl(I2PAppContext.getGlobalContext(), local); bucket.setRange(low, high); - Hash lowerBoundKey = bucket.getRangeBeginKey(); - Hash upperBoundKey = bucket.getRangeEndKey(); + //Hash lowerBoundKey = bucket.getRangeBeginKey(); + //Hash upperBoundKey = bucket.getRangeEndKey(); for (int i = 0; i < 100000; i++) { Hash rnd = bucket.generateRandomKey(); //buf.append(toString(rnd.getData())).append('\n'); boolean ok = bucket.shouldContain(rnd); if (!ok) { byte diff[] = bucket.getLocal().cachedXor(rnd); - BigInteger dv = new BigInteger(1, diff); + //BigInteger dv = new BigInteger(1, diff); //log.error("WTF! bucket doesn't want: \n" + toString(rnd.getData()) // + "\nDelta: \n" + toString(diff) + "\nDelta val: \n" + dv.toString(2) // + "\nBucket: \n"+bucket, new Exception("WTF")); @@ -440,13 +439,13 @@ class KBucketImpl implements KBucket { log.error("\nLow: " + DataHelper.toHexString(bucket.getRangeBeginKey().getData()) + "\nVal: " + DataHelper.toHexString(rnd.getData()) + "\nHigh:" + DataHelper.toHexString(bucket.getRangeEndKey().getData())); - try { Thread.sleep(1000); } catch (Exception e) {} + try { Thread.sleep(1000); } catch (InterruptedException e) {} System.exit(0); } else { //_log.debug("Ok, bucket wants: \n" + toString(rnd.getData())); } } - log.info("Passed 100,000 random key generations against a random hash\n" + buf.toString()); + log.info("Passed 100,000 random key generations against a random hash"); } private final static String toString(byte b[]) { diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KBucketSet.java b/router/java/src/net/i2p/router/networkdb/kademlia/KBucketSet.java index f576d967c7952b7f79a913fce4ddc0128016be6a..c630e2e9d1e4e27ac6216fbaaab89bc55cf8a099 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KBucketSet.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KBucketSet.java @@ -42,6 +42,7 @@ class KBucketSet { _context = context; _log = context.logManager().getLog(KBucketSet.class); createBuckets(); + context.statManager().createRateStat("netDb.KBSGetAllTime", "Time to add all Hashes to the Collector", "NetworkDatabase", new long[] { 60*60*1000 }); } /** @@ -99,8 +100,10 @@ class KBucketSet { } public void getAll(SelectionCollector collector) { + long start = _context.clock().now(); for (int i = 0; i < _buckets.length; i++) _buckets[i].getEntries(collector); + _context.statManager().addRateData("netDb.KBSGetAllTime", _context.clock().now() - start, 0); } public int pickBucket(Hash key) { diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index e9e81b542fa8424136ec1fd95540bb0f338fca7e..7409c8e76de638909a6efbf11b783e69b3837bfc 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -24,8 +24,8 @@ import java.util.Properties; import java.util.Set; import java.util.TreeSet; +import net.i2p.data.DatabaseEntry; import net.i2p.data.DataHelper; -import net.i2p.data.DataStructure; import net.i2p.data.Destination; import net.i2p.data.Hash; import net.i2p.data.Lease; @@ -88,9 +88,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { void searchComplete(Hash key) { if (_log.shouldLog(Log.DEBUG)) _log.debug("search Complete: " + key); - SearchJob removed = null; synchronized (_activeRequests) { - removed = (SearchJob)_activeRequests.remove(key); + _activeRequests.remove(key); } } @@ -235,11 +234,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { public void startup() { _log.info("Starting up the kademlia network database"); RouterInfo ri = _context.router().getRouterInfo(); - String dbDir = _context.router().getConfigSetting(PROP_DB_DIR); - if (dbDir == null) { - _log.info("No DB dir specified [" + PROP_DB_DIR + "], using [" + DEFAULT_DB_DIR + "]"); - dbDir = DEFAULT_DB_DIR; - } + String dbDir = _context.getProperty(PROP_DB_DIR, DEFAULT_DB_DIR); String enforce = _context.getProperty(PROP_ENFORCE_NETID); if (enforce != null) _enforceNetId = Boolean.valueOf(enforce).booleanValue(); @@ -354,21 +349,11 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { /** get the hashes for all known routers */ public Set<Hash> getAllRouters() { if (!_initialized) return Collections.EMPTY_SET; - Set<Hash> keys = _ds.getKeys(); - Set<Hash> rv = new HashSet(keys.size()); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("getAllRouters(): # keys in the datastore: " + keys.size()); - for (Hash key : keys) { - DataStructure ds = _ds.get(key); - if (ds == null) { - if (_log.shouldLog(Log.INFO)) - _log.info("Selected hash " + key.toBase64() + " is not stored locally"); - } else if ( !(ds instanceof RouterInfo) ) { - // leaseSet - } else { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("getAllRouters(): key is router: " + key.toBase64()); - rv.add(key); + Set<Map.Entry<Hash, DatabaseEntry>> entries = _ds.getMapEntries(); + Set<Hash> rv = new HashSet(entries.size()); + for (Map.Entry<Hash, DatabaseEntry> entry : entries) { + if (entry.getValue().getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { + rv.add(entry.getKey()); } } return rv; @@ -387,8 +372,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { public int size() { return _count; } public void add(Hash entry) { if (_ds == null) return; - Object o = _ds.get(entry); - if (o instanceof RouterInfo) + DatabaseEntry o = _ds.get(entry); + if (o != null && o.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) _count++; } } @@ -404,12 +389,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { public int getKnownLeaseSets() { if (_ds == null) return 0; //return _ds.countLeaseSets(); - Set<Hash> keys = _ds.getKeys(); int rv = 0; - for (Hash key : keys) { - DataStructure ds = _ds.get(key); - if (ds != null && - ds instanceof LeaseSet && + for (DatabaseEntry ds : _ds.getEntries()) { + if (ds.getType() == DatabaseEntry.KEY_TYPE_LEASESET && ((LeaseSet)ds).getReceivedAsPublished()) rv++; } @@ -422,8 +404,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { public int size() { return _count; } public void add(Hash entry) { if (_ds == null) return; - Object o = _ds.get(entry); - if (o instanceof LeaseSet) + DatabaseEntry o = _ds.get(entry); + if (o != null && o.getType() == DatabaseEntry.KEY_TYPE_LEASESET) _count++; } } @@ -438,6 +420,32 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { return _kb.size(); } + /** + * @return RouterInfo, LeaseSet, or null, validated + * @since 0.8.3 + */ + public DatabaseEntry lookupLocally(Hash key) { + if (!_initialized) + return null; + DatabaseEntry rv = _ds.get(key); + if (rv == null) + return null; + if (rv.getType() == DatabaseEntry.KEY_TYPE_LEASESET) { + LeaseSet ls = (LeaseSet)rv; + if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) + return rv; + else + fail(key); + } else if (rv.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { + try { + if (validate(key, (RouterInfo)rv) == null) + return rv; + } catch (IllegalArgumentException iae) {} + fail(key); + } + return null; + } + public void lookupLeaseSet(Hash key, Job onFindJob, Job onFailedLookupJob, long timeoutMs) { if (!_initialized) return; LeaseSet ls = lookupLeaseSetLocally(key); @@ -457,9 +465,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { public LeaseSet lookupLeaseSetLocally(Hash key) { if (!_initialized) return null; - if (_ds.isKnown(key)) { - DataStructure ds = _ds.get(key); - if (ds instanceof LeaseSet) { + DatabaseEntry ds = _ds.get(key); + if (ds != null) { + if (ds.getType() == DatabaseEntry.KEY_TYPE_LEASESET) { LeaseSet ls = (LeaseSet)ds; if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) { return ls; @@ -493,9 +501,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { public RouterInfo lookupRouterInfoLocally(Hash key) { if (!_initialized) return null; - DataStructure ds = _ds.get(key); + DatabaseEntry ds = _ds.get(key); if (ds != null) { - if (ds instanceof RouterInfo) { + if (ds.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { // more aggressive than perhaps is necessary, but makes sure we // drop old references that we had accepted on startup (since // startup allows some lax rules). @@ -614,6 +622,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { * Determine whether this leaseSet will be accepted as valid and current * given what we know now. * + * TODO this is called several times, only check the key and signature once + * * @return reason why the entry is not valid, or null if it is valid */ String validate(Hash key, LeaseSet leaseSet) { @@ -696,6 +706,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { * Determine whether this routerInfo will be accepted as valid and current * given what we know now. * + * TODO this is called several times, only check the key and signature once */ String validate(Hash key, RouterInfo routerInfo) throws IllegalArgumentException { long now = _context.clock().now(); @@ -811,30 +822,26 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { public void fail(Hash dbEntry) { if (!_initialized) return; - boolean isRouterInfo = false; - Object o = _ds.get(dbEntry); - if (o instanceof RouterInfo) - isRouterInfo = true; - - if (isRouterInfo) { - lookupBeforeDropping(dbEntry, (RouterInfo)o); - return; - } else { - // we always drop leaseSets that are failed [timed out], - // regardless of how many routers we have. this is called on a lease if - // it has expired *or* its tunnels are failing and we want to see if there - // are any updates - if (_log.shouldLog(Log.INFO)) - _log.info("Dropping a lease: " + dbEntry); - } - + DatabaseEntry o = _ds.get(dbEntry); if (o == null) { + // if we dont know the key, lets make sure it isn't a now-dead peer _kb.remove(dbEntry); _context.peerManager().removeCapabilities(dbEntry); - // if we dont know the key, lets make sure it isn't a now-dead peer + return; } - - _ds.remove(dbEntry, isRouterInfo); + + if (o.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { + lookupBeforeDropping(dbEntry, (RouterInfo)o); + return; + } + + // we always drop leaseSets that are failed [timed out], + // regardless of how many routers we have. this is called on a lease if + // it has expired *or* its tunnels are failing and we want to see if there + // are any updates + if (_log.shouldLog(Log.INFO)) + _log.info("Dropping a lease: " + dbEntry); + _ds.remove(dbEntry, false); } /** don't use directly - see F.N.D.F. override */ @@ -856,7 +863,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { public void unpublish(LeaseSet localLeaseSet) { if (!_initialized) return; Hash h = localLeaseSet.getDestination().calculateHash(); - DataStructure data = _ds.remove(h); + DatabaseEntry data = _ds.remove(h); if (data == null) { if (_log.shouldLog(Log.WARN)) @@ -910,8 +917,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { Set keys = getDataStore().getKeys(); for (Iterator iter = keys.iterator(); iter.hasNext(); ) { Hash key = (Hash)iter.next(); - Object o = getDataStore().get(key); - if (o instanceof LeaseSet) + DatabaseEntry o = getDataStore().get(key); + if (o.getType() == DatabaseEntry.KEY_TYPE_LEASESET) leases.add(o); } return leases; @@ -924,8 +931,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { Set keys = getDataStore().getKeys(); for (Iterator iter = keys.iterator(); iter.hasNext(); ) { Hash key = (Hash)iter.next(); - Object o = getDataStore().get(key); - if (o instanceof RouterInfo) + DatabaseEntry o = getDataStore().get(key); + if (o.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) routers.add(o); } return routers; @@ -957,7 +964,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { } /** unused (overridden in FNDF) */ - public void sendStore(Hash key, DataStructure ds, Job onSuccess, Job onFailure, long sendTimeout, Set toIgnore) { + public void sendStore(Hash key, DatabaseEntry ds, Job onSuccess, Job onFailure, long sendTimeout, Set toIgnore) { if ( (ds == null) || (key == null) ) { if (onFailure != null) _context.jobQueue().addJob(onFailure); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/LookupThrottler.java b/router/java/src/net/i2p/router/networkdb/kademlia/LookupThrottler.java index b4cef3621e5db85747462ed1281e38be05f0a3d1..3c82810e1f52fab03550ff70d6686ec3be355db4 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/LookupThrottler.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/LookupThrottler.java @@ -58,13 +58,15 @@ class LookupThrottler { @Override public boolean equals(Object obj) { + if (obj == null || !(obj instanceof ReplyTunnel)) + return false; return this.h.equals(((ReplyTunnel)obj).h) && this.id.equals(((ReplyTunnel)obj).id); } @Override public int hashCode() { - return this.h.hashCode() + this.id.hashCode(); + return this.h.hashCode() ^ this.id.hashCode(); } } } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PeerSelector.java b/router/java/src/net/i2p/router/networkdb/kademlia/PeerSelector.java index 2a6d889003b25054dfb02a6a86180ee05e3373b3..49dbc8b8fd7c2eadfdcd3c5460b26dc2a85b9660 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/PeerSelector.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/PeerSelector.java @@ -31,8 +31,8 @@ import net.i2p.util.Log; * Mostly unused, see overrides in FloodfillPeerSelector */ class PeerSelector { - protected Log _log; - protected RouterContext _context; + protected final Log _log; + protected final RouterContext _context; public PeerSelector(RouterContext ctx) { _context = ctx; diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java index 064aac5202873a7effc1f901c54e130faa7069cb..ad938e7ed37ba65a9df2c4e8a3f5939c973dc25a 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java @@ -19,8 +19,8 @@ import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentHashMap; import net.i2p.data.Base64; +import net.i2p.data.DatabaseEntry; import net.i2p.data.DataFormatException; -import net.i2p.data.DataStructure; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.RouterInfo; @@ -91,7 +91,7 @@ class PersistentDataStore extends TransientDataStore { } @Override - public DataStructure get(Hash key) { + public DatabaseEntry get(Hash key) { return get(key, true); } @@ -100,8 +100,8 @@ class PersistentDataStore extends TransientDataStore { * @param persist if false, call super only, don't access disk */ @Override - public DataStructure get(Hash key, boolean persist) { - DataStructure rv = super.get(key); + public DatabaseEntry get(Hash key, boolean persist) { + DatabaseEntry rv = super.get(key); /***** if (rv != null || !persist) return rv; @@ -116,7 +116,7 @@ class PersistentDataStore extends TransientDataStore { } @Override - public DataStructure remove(Hash key) { + public DatabaseEntry remove(Hash key) { return remove(key, true); } @@ -124,7 +124,7 @@ class PersistentDataStore extends TransientDataStore { * @param persist if false, call super only, don't access disk */ @Override - public DataStructure remove(Hash key, boolean persist) { + public DatabaseEntry remove(Hash key, boolean persist) { if (persist) { _writer.remove(key); _context.jobQueue().addJob(new RemoveJob(key)); @@ -133,7 +133,7 @@ class PersistentDataStore extends TransientDataStore { } @Override - public boolean put(Hash key, DataStructure data) { + public boolean put(Hash key, DatabaseEntry data) { return put(key, data, true); } @@ -142,11 +142,11 @@ class PersistentDataStore extends TransientDataStore { * @return success */ @Override - public boolean put(Hash key, DataStructure data, boolean persist) { + public boolean put(Hash key, DatabaseEntry data, boolean persist) { if ( (data == null) || (key == null) ) return false; boolean rv = super.put(key, data); // Don't bother writing LeaseSets to disk - if (rv && persist && data instanceof RouterInfo) + if (rv && persist && data.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) _writer.queue(key, data); return rv; } @@ -181,10 +181,10 @@ class PersistentDataStore extends TransientDataStore { * We store a reference to the data here too, * rather than simply pull it from super.get(), because * we will soon have to implement a scheme for keeping only - * a subset of all DataStructures in memory and keeping the rest on disk. + * a subset of all DatabaseEntrys in memory and keeping the rest on disk. */ private class Writer implements Runnable { - private final Map<Hash, DataStructure>_keys; + private final Map<Hash, DatabaseEntry>_keys; private final Object _waitLock; private volatile boolean _quit; @@ -193,7 +193,7 @@ class PersistentDataStore extends TransientDataStore { _waitLock = new Object(); } - public void queue(Hash key, DataStructure data) { + public void queue(Hash key, DatabaseEntry data) { int pending = _keys.size(); boolean exists = (null != _keys.put(key, data)); if (exists) @@ -202,7 +202,7 @@ class PersistentDataStore extends TransientDataStore { } /** check to see if it's in the write queue */ - public DataStructure get(Hash key) { + public DatabaseEntry get(Hash key) { return _keys.get(key); } @@ -213,16 +213,16 @@ class PersistentDataStore extends TransientDataStore { public void run() { _quit = false; Hash key = null; - DataStructure data = null; + DatabaseEntry data = null; int count = 0; int lastCount = 0; long startTime = 0; while (true) { // get a new iterator every time to get a random entry without // having concurrency issues or copying to a List or Array - Iterator<Map.Entry<Hash, DataStructure>> iter = _keys.entrySet().iterator(); + Iterator<Map.Entry<Hash, DatabaseEntry>> iter = _keys.entrySet().iterator(); try { - Map.Entry<Hash, DataStructure> entry = iter.next(); + Map.Entry<Hash, DatabaseEntry> entry = iter.next(); key = entry.getKey(); data = entry.getValue(); iter.remove(); @@ -275,7 +275,7 @@ class PersistentDataStore extends TransientDataStore { } } - private void write(Hash key, DataStructure data) { + private void write(Hash key, DatabaseEntry data) { if (_log.shouldLog(Log.INFO)) _log.info("Writing key " + key); FileOutputStream fos = null; @@ -283,9 +283,9 @@ class PersistentDataStore extends TransientDataStore { try { String filename = null; - if (data instanceof LeaseSet) + if (data.getType() == DatabaseEntry.KEY_TYPE_LEASESET) filename = getLeaseSetName(key); - else if (data instanceof RouterInfo) + else if (data.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) filename = getRouterInfoName(key); else throw new IOException("We don't know how to write objects of type " + data.getClass().getName()); @@ -316,14 +316,8 @@ class PersistentDataStore extends TransientDataStore { if (fos != null) try { fos.close(); } catch (IOException ioe) {} } } - private long getPublishDate(DataStructure data) { - if (data instanceof RouterInfo) { - return ((RouterInfo)data).getPublished(); - } else if (data instanceof LeaseSet) { - return ((LeaseSet)data).getEarliestLeaseDate(); - } else { - return -1; - } + private long getPublishDate(DatabaseEntry data) { + return data.getDate(); } /** This is only for manual reseeding? Why bother every 60 sec??? */ @@ -395,9 +389,9 @@ class PersistentDataStore extends TransientDataStore { private boolean shouldRead() { // persist = false to call only super.get() - DataStructure data = get(_key, false); + DatabaseEntry data = get(_key, false); if (data == null) return true; - if (data instanceof RouterInfo) { + if (data.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { long knownDate = ((RouterInfo)data).getPublished(); long fileDate = _routerFile.lastModified(); if (fileDate > knownDate) diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java index 223dd70f851faa8436891db3203685bc4e7f0bfd..239fd4772e5cfad9e7d5f8a2cf2e0453775ebc0c 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java @@ -89,7 +89,7 @@ public class RepublishLeaseSetJob extends JobImpl { return _lastPublished; } - class OnRepublishSuccess extends JobImpl { + private static class OnRepublishSuccess extends JobImpl { public OnRepublishSuccess(RouterContext ctx) { super(ctx); } public String getName() { return "Publish leaseSet successful"; } public void runJob() { @@ -98,7 +98,7 @@ public class RepublishLeaseSetJob extends JobImpl { } } - class OnRepublishFailure extends JobImpl { + private static class OnRepublishFailure extends JobImpl { private RepublishLeaseSetJob _job; public OnRepublishFailure(RouterContext ctx, RepublishLeaseSetJob job) { super(ctx); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/SearchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/SearchJob.java index 9400a17f696394d1d2be36ce0a432afc00c5f1cc..739a7c14827b4c5b9c969159e8284a4805775f3f 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/SearchJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/SearchJob.java @@ -9,13 +9,13 @@ package net.i2p.router.networkdb.kademlia; */ import java.util.ArrayList; -import java.util.HashSet; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; +import net.i2p.data.DatabaseEntry; import net.i2p.data.DataHelper; -import net.i2p.data.DataStructure; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.RouterInfo; @@ -293,12 +293,12 @@ class SearchJob extends JobImpl { attempted.addAll(closestHashes); for (Iterator iter = closestHashes.iterator(); iter.hasNext(); ) { Hash peer = (Hash)iter.next(); - DataStructure ds = _facade.getDataStore().get(peer); + DatabaseEntry ds = _facade.getDataStore().get(peer); if (ds == null) { if (_log.shouldLog(Log.INFO)) _log.info("Next closest peer " + peer + " was only recently referred to us, sending a search for them"); getContext().netDb().lookupRouterInfo(peer, null, null, _timeoutMs); - } else if (!(ds instanceof RouterInfo)) { + } else if (!(ds.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO)) { if (_log.shouldLog(Log.WARN)) _log.warn(getJobId() + ": Error selecting closest hash that wasnt a router! " + peer + " : " + ds.getClass().getName()); @@ -635,7 +635,7 @@ class SearchJob extends JobImpl { * */ private void resend() { - DataStructure ds = _facade.lookupLeaseSetLocally(_state.getTarget()); + DatabaseEntry ds = _facade.lookupLeaseSetLocally(_state.getTarget()); if (ds == null) { if (SHOULD_RESEND_ROUTERINFO) { ds = _facade.lookupRouterInfoLocally(_state.getTarget()); @@ -665,8 +665,7 @@ class SearchJob extends JobImpl { */ private boolean resend(RouterInfo toPeer, LeaseSet ls) { DatabaseStoreMessage msg = new DatabaseStoreMessage(getContext()); - msg.setKey(ls.getDestination().calculateHash()); - msg.setLeaseSet(ls); + msg.setEntry(ls); msg.setMessageExpiration(getContext().clock().now() + RESEND_TIMEOUT); TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundTunnel(); @@ -801,8 +800,7 @@ class SearchJob extends JobImpl { if (rv) { if (_log.shouldLog(Log.DEBUG)) _log.debug(getJobId() + ": Queueing up for next time: " + peer); - Set s = new HashSet(1); - s.add(peer); + Set<Hash> s = Collections.singleton(peer); _facade.queueForExploration(s); } return rv; diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/SearchState.java b/router/java/src/net/i2p/router/networkdb/kademlia/SearchState.java index 6fe5831ddfa2b83bf1e59342a0fd9a09a35cd3a7..25d86acf180af6be2f296e9fccff3402c097cc2d 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/SearchState.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/SearchState.java @@ -98,7 +98,7 @@ class SearchState { synchronized (_pendingPeers) { _pendingPeers.addAll(pending); for (Iterator iter = pending.iterator(); iter.hasNext(); ) - _pendingPeerTimes.put(iter.next(), new Long(_context.clock().now())); + _pendingPeerTimes.put(iter.next(), Long.valueOf(_context.clock().now())); } synchronized (_attemptedPeers) { _attemptedPeers.addAll(pending); @@ -107,7 +107,7 @@ class SearchState { public void addPending(Hash peer) { synchronized (_pendingPeers) { _pendingPeers.add(peer); - _pendingPeerTimes.put(peer, new Long(_context.clock().now())); + _pendingPeerTimes.put(peer, Long.valueOf(_context.clock().now())); } synchronized (_attemptedPeers) { _attemptedPeers.add(peer); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/SearchUpdateReplyFoundJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/SearchUpdateReplyFoundJob.java index b5a824472a8cd8fb0b59bcb9cd9fbd41fcfb03e4..22602b497a6d7d787dceab1f2145be80e6955a55 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/SearchUpdateReplyFoundJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/SearchUpdateReplyFoundJob.java @@ -2,7 +2,9 @@ package net.i2p.router.networkdb.kademlia; import java.util.Date; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; +import net.i2p.data.LeaseSet; import net.i2p.data.RouterInfo; import net.i2p.data.i2np.DatabaseSearchReplyMessage; import net.i2p.data.i2np.DatabaseStoreMessage; @@ -78,22 +80,23 @@ class SearchUpdateReplyFoundJob extends JobImpl implements ReplyJob { long timeToReply = _state.dataFound(_peer); DatabaseStoreMessage msg = (DatabaseStoreMessage)message; - if (msg.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET) { + DatabaseEntry entry = msg.getEntry(); + if (entry.getType() == DatabaseEntry.KEY_TYPE_LEASESET) { try { - _facade.store(msg.getKey(), msg.getLeaseSet()); + _facade.store(msg.getKey(), (LeaseSet) entry); getContext().profileManager().dbLookupSuccessful(_peer, timeToReply); } catch (IllegalArgumentException iae) { if (_log.shouldLog(Log.ERROR)) _log.warn("Peer " + _peer + " sent us an invalid leaseSet: " + iae.getMessage()); getContext().profileManager().dbLookupReply(_peer, 0, 0, 1, 0, timeToReply); } - } else if (msg.getValueType() == DatabaseStoreMessage.KEY_TYPE_ROUTERINFO) { + } else if (entry.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { if (_log.shouldLog(Log.INFO)) _log.info(getJobId() + ": dbStore received on search containing router " + msg.getKey() + " with publishDate of " - + new Date(msg.getRouterInfo().getPublished())); + + new Date(entry.getDate())); try { - _facade.store(msg.getKey(), msg.getRouterInfo()); + _facade.store(msg.getKey(), (RouterInfo) entry); getContext().profileManager().dbLookupSuccessful(_peer, timeToReply); } catch (IllegalArgumentException iae) { if (_log.shouldLog(Log.ERROR)) @@ -102,7 +105,7 @@ class SearchUpdateReplyFoundJob extends JobImpl implements ReplyJob { } } else { if (_log.shouldLog(Log.ERROR)) - _log.error(getJobId() + ": Unknown db store type?!@ " + msg.getValueType()); + _log.error(getJobId() + ": Unknown db store type?!@ " + entry.getType()); } } else if (message instanceof DatabaseSearchReplyMessage) { _job.replyFound((DatabaseSearchReplyMessage)message, _peer); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java index ad8217c6a7944319b2ee195015501445c4a456c8..c1a8494290d3be1bb1573b2d6bd3ebcc6eab8b51 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java @@ -13,7 +13,7 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -import net.i2p.data.DataStructure; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.RouterInfo; @@ -61,7 +61,7 @@ class StoreJob extends JobImpl { * */ public StoreJob(RouterContext context, KademliaNetworkDatabaseFacade facade, Hash key, - DataStructure data, Job onSuccess, Job onFailure, long timeoutMs) { + DatabaseEntry data, Job onSuccess, Job onFailure, long timeoutMs) { this(context, facade, key, data, onSuccess, onFailure, timeoutMs, null); } @@ -70,7 +70,7 @@ class StoreJob extends JobImpl { * already know they have it). This can be null. */ public StoreJob(RouterContext context, KademliaNetworkDatabaseFacade facade, Hash key, - DataStructure data, Job onSuccess, Job onFailure, long timeoutMs, Set<Hash> toSkip) { + DatabaseEntry data, Job onSuccess, Job onFailure, long timeoutMs, Set<Hash> toSkip) { super(context); _log = context.logManager().getLog(StoreJob.class); _facade = facade; @@ -167,8 +167,8 @@ class StoreJob extends JobImpl { _log.info(getJobId() + ": Continue sending key " + _state.getTarget() + " after " + _state.getAttempted().size() + " tries to " + closestHashes); for (Iterator<Hash> iter = closestHashes.iterator(); iter.hasNext(); ) { Hash peer = iter.next(); - DataStructure ds = _facade.getDataStore().get(peer); - if ( (ds == null) || !(ds instanceof RouterInfo) ) { + DatabaseEntry ds = _facade.getDataStore().get(peer); + if ( (ds == null) || !(ds.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) ) { if (_log.shouldLog(Log.INFO)) _log.info(getJobId() + ": Error selecting closest hash that wasnt a router! " + peer + " : " + ds); _state.addSkipped(peer); @@ -255,16 +255,19 @@ class StoreJob extends JobImpl { * */ private void sendStore(RouterInfo router, int responseTime) { + if (!_state.getTarget().equals(_state.getData().getHash())) { + _log.error("Hash mismatch StoreJob"); + return; + } DatabaseStoreMessage msg = new DatabaseStoreMessage(getContext()); - msg.setKey(_state.getTarget()); - if (_state.getData() instanceof RouterInfo) { - msg.setRouterInfo((RouterInfo)_state.getData()); + if (_state.getData().getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { if (responseTime > MAX_DIRECT_EXPIRATION) responseTime = MAX_DIRECT_EXPIRATION; - } else if (_state.getData() instanceof LeaseSet) - msg.setLeaseSet((LeaseSet)_state.getData()); - else + } else if (_state.getData().getType() == DatabaseEntry.KEY_TYPE_LEASESET) { + } else { throw new IllegalArgumentException("Storing an unknown data type! " + _state.getData()); + } + msg.setEntry(_state.getData()); msg.setMessageExpiration(getContext().clock().now() + _timeoutMs); if (router.getIdentity().equals(getContext().router().getRouterInfo().getIdentity())) { @@ -286,7 +289,7 @@ class StoreJob extends JobImpl { * */ private void sendStore(DatabaseStoreMessage msg, RouterInfo peer, long expiration) { - if (msg.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET) { + if (msg.getEntry().getType() == DatabaseEntry.KEY_TYPE_LEASESET) { getContext().statManager().addRateData("netDb.storeLeaseSetSent", 1, 0); // if it is an encrypted leaseset... if (getContext().keyRing().get(msg.getKey()) != null) @@ -440,7 +443,6 @@ class StoreJob extends JobImpl { sent = wm.getMessage(); _state.addPending(to, wm); } else { - sent = msg; _state.addPending(to); // now that almost all floodfills are at 0.7.10, // just refuse to store unencrypted to older ones. diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/StoreState.java b/router/java/src/net/i2p/router/networkdb/kademlia/StoreState.java index eb131fdfc5c5608bbe1e05aeb53cb58d498fec6a..9666c09be520533526c44e46a9621b9b6dce8f37 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/StoreState.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/StoreState.java @@ -9,7 +9,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import net.i2p.data.DataStructure; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.router.RouterContext; @@ -19,7 +19,7 @@ import net.i2p.router.RouterContext; class StoreState { private RouterContext _context; private Hash _key; - private DataStructure _data; + private DatabaseEntry _data; private final HashSet<Hash> _pendingPeers; private Map<Hash, Long> _pendingPeerTimes; private Map<Hash, MessageWrapper.WrappedMessage> _pendingMessages; @@ -31,10 +31,10 @@ class StoreState { private volatile long _completed; private volatile long _started; - public StoreState(RouterContext ctx, Hash key, DataStructure data) { + public StoreState(RouterContext ctx, Hash key, DatabaseEntry data) { this(ctx, key, data, null); } - public StoreState(RouterContext ctx, Hash key, DataStructure data, Set<Hash> toSkip) { + public StoreState(RouterContext ctx, Hash key, DatabaseEntry data, Set<Hash> toSkip) { _context = ctx; _key = key; _data = data; @@ -54,7 +54,7 @@ class StoreState { } public Hash getTarget() { return _key; } - public DataStructure getData() { return _data; } + public DatabaseEntry getData() { return _data; } public Set<Hash> getPending() { synchronized (_pendingPeers) { return (Set<Hash>)_pendingPeers.clone(); @@ -114,7 +114,7 @@ class StoreState { public void addPending(Hash peer) { synchronized (_pendingPeers) { _pendingPeers.add(peer); - _pendingPeerTimes.put(peer, new Long(_context.clock().now())); + _pendingPeerTimes.put(peer, Long.valueOf(_context.clock().now())); } synchronized (_attemptedPeers) { _attemptedPeers.add(peer); @@ -124,7 +124,7 @@ class StoreState { synchronized (_pendingPeers) { _pendingPeers.addAll(pending); for (Iterator<Hash> iter = pending.iterator(); iter.hasNext(); ) - _pendingPeerTimes.put(iter.next(), new Long(_context.clock().now())); + _pendingPeerTimes.put(iter.next(), Long.valueOf(_context.clock().now())); } synchronized (_attemptedPeers) { _attemptedPeers.addAll(pending); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java index d5e10738c26c560ed97169bf4d4d4f296336288b..e0a3a2a633762639891abf69b4697fb0143dafff 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java @@ -8,14 +8,15 @@ package net.i2p.router.networkdb.kademlia; * */ +import java.util.Collection; import java.util.Date; import java.util.concurrent.ConcurrentHashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import net.i2p.data.DatabaseEntry; import net.i2p.data.DataHelper; -import net.i2p.data.DataStructure; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.RouterInfo; @@ -24,7 +25,7 @@ import net.i2p.util.Log; class TransientDataStore implements DataStore { private Log _log; - private ConcurrentHashMap<Hash, DataStructure> _data; + private ConcurrentHashMap<Hash, DatabaseEntry> _data; protected RouterContext _context; public TransientDataStore(RouterContext ctx) { @@ -51,12 +52,28 @@ class TransientDataStore implements DataStore { return new HashSet(_data.keySet()); } + /** + * @return not a copy + * @since 0.8.3 + */ + public Collection<DatabaseEntry> getEntries() { + return _data.values(); + } + + /** + * @return not a copy + * @since 0.8.3 + */ + public Set<Map.Entry<Hash, DatabaseEntry>> getMapEntries() { + return _data.entrySet(); + } + /** for PersistentDataStore only - don't use here @throws IAE always */ - public DataStructure get(Hash key, boolean persist) { + public DatabaseEntry get(Hash key, boolean persist) { throw new IllegalArgumentException("no"); } - public DataStructure get(Hash key) { + public DatabaseEntry get(Hash key) { return _data.get(key); } @@ -66,15 +83,15 @@ class TransientDataStore implements DataStore { public int countLeaseSets() { int count = 0; - for (DataStructure d : _data.values()) { - if (d instanceof LeaseSet) + for (DatabaseEntry d : _data.values()) { + if (d.getType() == DatabaseEntry.KEY_TYPE_LEASESET) count++; } return count; } /** for PersistentDataStore only - don't use here @throws IAE always */ - public boolean put(Hash key, DataStructure data, boolean persist) { + public boolean put(Hash key, DatabaseEntry data, boolean persist) { throw new IllegalArgumentException("no"); } @@ -82,14 +99,14 @@ class TransientDataStore implements DataStore { * @param data must be validated before here * @return success */ - public boolean put(Hash key, DataStructure data) { + public boolean put(Hash key, DatabaseEntry data) { if (data == null) return false; if (_log.shouldLog(Log.DEBUG)) _log.debug("Storing key " + key); - DataStructure old = null; + DatabaseEntry old = null; old = _data.putIfAbsent(key, data); boolean rv = false; - if (data instanceof RouterInfo) { + if (data.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { // Don't do this here so we don't reset it at router startup; // the StoreMessageJob calls this //_context.profileManager().heardAbout(key); @@ -113,7 +130,7 @@ class TransientDataStore implements DataStore { _log.info("New router for " + key + ": published on " + new Date(ri.getPublished())); rv = true; } - } else if (data instanceof LeaseSet) { + } else if (data.getType() == DatabaseEntry.KEY_TYPE_LEASESET) { LeaseSet ls = (LeaseSet)data; if (old != null) { LeaseSet ols = (LeaseSet)old; @@ -144,23 +161,13 @@ class TransientDataStore implements DataStore { return rv; } - @Override - public int hashCode() { - return DataHelper.hashCode(_data); - } - @Override - public boolean equals(Object obj) { - if ( (obj == null) || (obj.getClass() != getClass()) ) return false; - TransientDataStore ds = (TransientDataStore)obj; - return DataHelper.eq(ds._data, _data); - } @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append("Transient DataStore: ").append(_data.size()).append("\nKeys: "); - for (Map.Entry<Hash, DataStructure> e : _data.entrySet()) { + for (Map.Entry<Hash, DatabaseEntry> e : _data.entrySet()) { Hash key = e.getKey(); - DataStructure dp = e.getValue(); + DatabaseEntry dp = e.getValue(); buf.append("\n\t*Key: ").append(key.toString()).append("\n\tContent: ").append(dp.toString()); } buf.append("\n"); @@ -168,11 +175,11 @@ class TransientDataStore implements DataStore { } /** for PersistentDataStore only - don't use here */ - public DataStructure remove(Hash key, boolean persist) { + public DatabaseEntry remove(Hash key, boolean persist) { throw new IllegalArgumentException("no"); } - public DataStructure remove(Hash key) { + public DatabaseEntry remove(Hash key) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Removing key " + key.toBase64()); return _data.remove(key); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/XORComparator.java b/router/java/src/net/i2p/router/networkdb/kademlia/XORComparator.java index 3c2b9d2d2c8c8f5b2e11909f2714077c9484f685..71dbefc6e2c4b1e53f6aac9e7b8311bd3efd2a71 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/XORComparator.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/XORComparator.java @@ -9,7 +9,7 @@ import net.i2p.data.Hash; * Help sort Hashes in relation to a base key using the XOR metric * */ -class XORComparator implements Comparator { +class XORComparator implements Comparator<Hash> { private Hash _base; /** * @param target key to compare distances with @@ -17,15 +17,11 @@ class XORComparator implements Comparator { public XORComparator(Hash target) { _base = target; } - public int compare(Object lhs, Object rhs) { + public int compare(Hash lhs, Hash rhs) { if (lhs == null) throw new NullPointerException("LHS is null"); if (rhs == null) throw new NullPointerException("RHS is null"); - if ( (lhs instanceof Hash) && (rhs instanceof Hash) ) { - byte lhsDelta[] = DataHelper.xor(((Hash)lhs).getData(), _base.getData()); - byte rhsDelta[] = DataHelper.xor(((Hash)rhs).getData(), _base.getData()); - return DataHelper.compareTo(lhsDelta, rhsDelta); - } else { - throw new ClassCastException(lhs.getClass().getName() + " / " + rhs.getClass().getName()); - } + byte lhsDelta[] = DataHelper.xor(lhs.getData(), _base.getData()); + byte rhsDelta[] = DataHelper.xor(rhs.getData(), _base.getData()); + return DataHelper.compareTo(lhsDelta, rhsDelta); } } diff --git a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java index 4f225ec977ddfc59cc2508744d9da3bcb3043f4d..8e874e9797d3f8f26b06847a0c6d1c0c6f559152 100644 --- a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java +++ b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java @@ -3,6 +3,7 @@ package net.i2p.router.networkdb.reseed; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; @@ -176,14 +177,14 @@ public class Reseeder { StringTokenizer tok = new StringTokenizer(URLs, " ,"); while (tok.hasMoreTokens()) URLList.add(tok.nextToken().trim()); - Collections.shuffle(URLList); + Collections.shuffle(URLList, _context.random()); if (defaulted && !SSLDisable) { // put the non-SSL at the end of the SSL List<String> URLList2 = new ArrayList(); tok = new StringTokenizer(DEFAULT_SEED_URL, " ,"); while (tok.hasMoreTokens()) URLList2.add(tok.nextToken().trim()); - Collections.shuffle(URLList2); + Collections.shuffle(URLList2, _context.random()); URLList.addAll(URLList2); } int total = 0; @@ -262,7 +263,7 @@ public class Reseeder { } List<String> urlList = new ArrayList(urls); - Collections.shuffle(urlList); + Collections.shuffle(urlList, _context.random()); int fetched = 0; int errors = 0; // 200 max from one URL @@ -278,7 +279,7 @@ public class Reseeder { if (fetched % 60 == 0) System.out.println(); } - } catch (Exception e) { + } catch (IOException e) { errors++; } } @@ -298,20 +299,20 @@ public class Reseeder { } /* Since we don't return a value, we should always throw an exception if something fails. */ - private void fetchSeed(String seedURL, String peer) throws Exception { + private void fetchSeed(String seedURL, String peer) throws IOException { URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + "routerInfo-" + peer + ".dat"); byte data[] = readURL(url); if (data == null) { // Logging deprecated here since attemptFailed() provides better info _log.debug("Failed fetching seed: " + url.toString()); - throw new Exception ("Failed fetching seed."); + throw new IOException("Failed fetching seed."); } //System.out.println("read: " + (data != null ? data.length : -1)); writeSeed(peer, data); } - private byte[] readURL(URL url) throws Exception { + private byte[] readURL(URL url) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024); EepGet get; @@ -338,15 +339,21 @@ public class Reseeder { return null; } - private void writeSeed(String name, byte data[]) throws Exception { + private void writeSeed(String name, byte data[]) throws IOException { String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb"); File netDbDir = new SecureDirectory(_context.getRouterDir(), dirName); if (!netDbDir.exists()) { boolean ok = netDbDir.mkdirs(); } - FileOutputStream fos = new SecureFileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat")); - fos.write(data); - fos.close(); + FileOutputStream fos = null; + try { + fos = new SecureFileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat")); + fos.write(data); + } finally { + try { + if (fos != null) fos.close(); + } catch (IOException ioe) {} + } } } diff --git a/router/java/src/net/i2p/router/peermanager/Calculator.java b/router/java/src/net/i2p/router/peermanager/Calculator.java deleted file mode 100644 index 99fb99fd8240a28600e2d96ddb5663c983bdc091..0000000000000000000000000000000000000000 --- a/router/java/src/net/i2p/router/peermanager/Calculator.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.i2p.router.peermanager; - - -/** - * Provide a means of quantifying a profiles fitness in some particular aspect, as well - * as to coordinate via statics the four known aspects. - * - */ -public class Calculator { - /** - * Evaluate the profile according to the current metric - */ - public double calc(PeerProfile profile) { return 0.0d; } - /** - * Evaluate the profile according to the current metric - */ - public boolean calcBoolean(PeerProfile profile) { return false; } -} diff --git a/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java b/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java index 891ca88c47596d167dfb19c44701a16e0f106335..9d23ceddf5349216fa082977b47ca235d6a0e3f1 100644 --- a/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java +++ b/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java @@ -1,21 +1,14 @@ package net.i2p.router.peermanager; -import net.i2p.router.RouterContext; +import net.i2p.I2PAppContext; import net.i2p.stat.Rate; import net.i2p.stat.RateStat; -import net.i2p.util.Log; /** * Estimate how many of our tunnels the peer can join per hour. */ -public class CapacityCalculator extends Calculator { - private Log _log; - private RouterContext _context; - - public CapacityCalculator(RouterContext context) { - _context = context; - _log = context.logManager().getLog(CapacityCalculator.class); - } +class CapacityCalculator { + private static final I2PAppContext _context = I2PAppContext.getGlobalContext(); /** used to adjust each period so that we keep trying to expand the peer's capacity */ static long GROWTH_FACTOR = 5; @@ -23,8 +16,7 @@ public class CapacityCalculator extends Calculator { /** the calculator estimates over a 1 hour period */ private static long ESTIMATE_PERIOD = 60*60*1000; - @Override - public double calc(PeerProfile profile) { + public static double calc(PeerProfile profile) { double capacity; if (tooOld(profile)) { diff --git a/router/java/src/net/i2p/router/peermanager/IntegrationCalculator.java b/router/java/src/net/i2p/router/peermanager/IntegrationCalculator.java index f306b199573ceead4a7c635d9953cc35d1dba7cf..14437e4bf01fddcbef6d64816d7c96da7564aea1 100644 --- a/router/java/src/net/i2p/router/peermanager/IntegrationCalculator.java +++ b/router/java/src/net/i2p/router/peermanager/IntegrationCalculator.java @@ -1,24 +1,13 @@ package net.i2p.router.peermanager; -import net.i2p.router.RouterContext; -import net.i2p.util.Log; - /** * Determine how well integrated the peer is - how likely they will be useful * to us if we are trying to get further connected. * */ -public class IntegrationCalculator extends Calculator { - private Log _log; - private RouterContext _context; - - public IntegrationCalculator(RouterContext context) { - _context = context; - _log = context.logManager().getLog(IntegrationCalculator.class); - } +class IntegrationCalculator { - @Override - public double calc(PeerProfile profile) { + public static double calc(PeerProfile profile) { long val = 0; if (profile.getIsExpandedDB()) { // give more weight to recent counts diff --git a/router/java/src/net/i2p/router/peermanager/PeerManager.java b/router/java/src/net/i2p/router/peermanager/PeerManager.java index bfe038e8c50266c7eaaab0d4ad11f5f1f4fe756d..a2c4327631698fd9bec5ce4b9fdf6e71b55da385 100644 --- a/router/java/src/net/i2p/router/peermanager/PeerManager.java +++ b/router/java/src/net/i2p/router/peermanager/PeerManager.java @@ -115,6 +115,7 @@ class PeerManager { */ List<Hash> selectPeers(PeerSelectionCriteria criteria) { Set<Hash> peers = new HashSet(criteria.getMinimumRequired()); + // not a singleton, SANFP adds to it Set<Hash> exclude = new HashSet(1); exclude.add(_context.routerHash()); switch (criteria.getPurpose()) { diff --git a/router/java/src/net/i2p/router/peermanager/PeerProfile.java b/router/java/src/net/i2p/router/peermanager/PeerProfile.java index 6f7fe54bbe8acd7bdddd5f0ddccb98a85c26ad67..6818abb9617dca9c80e5313b49048ac7b601e521 100644 --- a/router/java/src/net/i2p/router/peermanager/PeerProfile.java +++ b/router/java/src/net/i2p/router/peermanager/PeerProfile.java @@ -24,10 +24,10 @@ import net.i2p.util.Log; */ public class PeerProfile { - private Log _log; - private RouterContext _context; + private final Log _log; + private final RouterContext _context; // whoozaat? - private Hash _peer; + private final Hash _peer; // general peer stats private long _firstHeardAbout; private long _lastHeardAbout; @@ -65,13 +65,6 @@ public class PeerProfile { public PeerProfile(RouterContext context, Hash peer, boolean expand) { _context = context; _log = context.logManager().getLog(PeerProfile.class); - _expanded = false; - _speedValue = 0; - _capacityValue = 0; - _integrationValue = 0; - _isFailing = false; - _consecutiveShitlists = 0; - _tunnelTestResponseTimeAvg = 0.0d; _peer = peer; // this is always true, and there are several places in the router that will NPE // if it is false, so all need to be fixed before we can have non-expanded profiles @@ -81,7 +74,6 @@ public class PeerProfile { /** what peer is being profiled */ public Hash getPeer() { return _peer; } - public void setPeer(Hash peer) { _peer = peer; } /** * are we keeping an expanded profile on the peer, or just the bare minimum. @@ -474,9 +466,9 @@ public class PeerProfile { _log.debug("Coalesced: speed [" + _speedValue + "] capacity [" + _capacityValue + "] integration [" + _integrationValue + "] failing? [" + _isFailing + "]"); } - private double calculateSpeed() { return _context.speedCalculator().calc(this); } - private double calculateCapacity() { return _context.capacityCalculator().calc(this); } - private double calculateIntegration() { return _context.integrationCalculator().calc(this); } + private double calculateSpeed() { return SpeedCalculator.calc(this); } + private double calculateCapacity() { return CapacityCalculator.calc(this); } + private double calculateIntegration() { return IntegrationCalculator.calc(this); } /** deprecated - unused - always false */ private boolean calculateIsFailing() { return false; } /** deprecated - unused - always false */ @@ -484,14 +476,17 @@ public class PeerProfile { @Override public int hashCode() { return (_peer == null ? 0 : _peer.hashCode()); } + @Override public boolean equals(Object obj) { - if (obj == null) return false; - if (obj.getClass() != PeerProfile.class) return false; - if (_peer == null) return false; + if (obj == null || + (!(obj instanceof PeerProfile)) || + _peer == null) + return false; PeerProfile prof = (PeerProfile)obj; return _peer.equals(prof.getPeer()); } + @Override public String toString() { return "Profile: " + getPeer().toBase64(); } diff --git a/router/java/src/net/i2p/router/peermanager/PeerTestJob.java b/router/java/src/net/i2p/router/peermanager/PeerTestJob.java index 11e8cacb86c8d9137beeea0e495959b96a1b10e1..1d5fe3a849ac1dc098c434dc07ffe83ff316cc3f 100644 --- a/router/java/src/net/i2p/router/peermanager/PeerTestJob.java +++ b/router/java/src/net/i2p/router/peermanager/PeerTestJob.java @@ -178,8 +178,7 @@ public class PeerTestJob extends JobImpl { */ private DatabaseStoreMessage buildMessage(RouterInfo peer, TunnelId replyTunnel, Hash replyGateway, long nonce, long expiration) { DatabaseStoreMessage msg = new DatabaseStoreMessage(getContext()); - msg.setKey(peer.getIdentity().getHash()); - msg.setRouterInfo(peer); + msg.setEntry(peer); msg.setReplyGateway(replyGateway); msg.setReplyTunnel(replyTunnel); msg.setReplyToken(nonce); diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java index 03b49269f46b3813a05382d9ca2455bfef26baf7..fe33c3447f838d6ffe0a48a67b62c32b75465a34 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java @@ -28,6 +28,7 @@ import net.i2p.data.RouterInfo; import net.i2p.router.NetworkDatabaseFacade; import net.i2p.router.RouterContext; import net.i2p.router.tunnel.pool.TunnelPeerSelector; +import net.i2p.router.util.RandomIterator; import net.i2p.stat.Rate; import net.i2p.stat.RateStat; import net.i2p.util.Log; @@ -1043,17 +1044,19 @@ public class ProfileOrganizer { private void locked_selectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude, Set<Hash> matches) { locked_selectPeers(peers, howMany, toExclude, matches, 0); } + private void locked_selectPeers(Map<Hash, PeerProfile> peers, int howMany, Set<Hash> toExclude, Set<Hash> matches, int mask) { - List all = new ArrayList(peers.keySet()); - if (toExclude != null) - all.removeAll(toExclude); - - all.removeAll(matches); - all.remove(_us); - Collections.shuffle(all, _random); + List<Hash> all = new ArrayList(peers.keySet()); Set<Integer> IPSet = new HashSet(8); - for (int i = 0; (matches.size() < howMany) && (i < all.size()); i++) { - Hash peer = (Hash)all.get(i); + // use RandomIterator to avoid shuffling the whole thing + for (Iterator<Hash> iter = new RandomIterator(all); (matches.size() < howMany) && iter.hasNext(); ) { + Hash peer = iter.next(); + if (toExclude != null && toExclude.contains(peer)) + continue; + if (matches.contains(peer)) + continue; + if (_us.equals(peer)) + continue; boolean ok = isSelectable(peer); if (ok) { ok = mask <= 0 || notRestricted(peer, IPSet, mask); diff --git a/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java b/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java index e012fab4906c9a19694a6bd84710429f0028a6df..592c1f2677eefd544bf8624e941df9cb60479cc2 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java +++ b/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java @@ -1,9 +1,11 @@ package net.i2p.router.peermanager; +import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; +import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.util.HashSet; @@ -270,11 +272,12 @@ class ProfilePersistenceHelper { } private void loadProps(Properties props, File file) { + InputStream fin = null; try { - FileInputStream fin = new FileInputStream(file); + fin = new BufferedInputStream(new FileInputStream(file), 1); + fin.mark(1); int c = fin.read(); - fin.close(); - fin = new FileInputStream(file); // ghetto mark+reset + fin.reset(); if (c == '#') { // uncompressed if (_log.shouldLog(Log.INFO)) @@ -289,6 +292,10 @@ class ProfilePersistenceHelper { } catch (IOException ioe) { if (_log.shouldLog(Log.WARN)) _log.warn("Error loading properties from " + file.getName(), ioe); + } finally { + try { + if (fin != null) fin.close(); + } catch (IOException e) {} } } diff --git a/router/java/src/net/i2p/router/peermanager/SpeedCalculator.java b/router/java/src/net/i2p/router/peermanager/SpeedCalculator.java index c5ac1c3c56d26871dc33beec002063983fd5c70b..cf1b7f069d707db8ab8666ab404bbde97c2cf4cb 100644 --- a/router/java/src/net/i2p/router/peermanager/SpeedCalculator.java +++ b/router/java/src/net/i2p/router/peermanager/SpeedCalculator.java @@ -1,7 +1,5 @@ package net.i2p.router.peermanager; -import net.i2p.router.RouterContext; - /** * Quantify how fast the peer is - how fast they respond to our requests, how fast * they pass messages on, etc. This should be affected both by their bandwidth/latency, @@ -13,13 +11,9 @@ import net.i2p.router.RouterContext; * see the previous versions in change control to get 400+ lines of old code. * */ -public class SpeedCalculator extends Calculator { - - public SpeedCalculator(RouterContext context) { - } +class SpeedCalculator { - @Override - public double calc(PeerProfile profile) { + public static double calc(PeerProfile profile) { // measures 1 minute throughput of individual tunnels double d = (profile.getPeakTunnel1mThroughputKBps()*1024d) + profile.getSpeedBonus(); if (d >= 0) return d; diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java index 094f526c79913e433bfa53e178bee6d2f0b22725..d7849d5320695e38ad12043f817c1d3a3bb22357 100644 --- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java +++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java @@ -36,15 +36,14 @@ import net.i2p.util.SimpleTimer; import net.i2p.util.Translate; public class CommSystemFacadeImpl extends CommSystemFacade { - private Log _log; - private RouterContext _context; + private final Log _log; + private final RouterContext _context; private TransportManager _manager; private GeoIP _geoIP; public CommSystemFacadeImpl(RouterContext context) { _context = context; _log = _context.logManager().getLog(CommSystemFacadeImpl.class); - _manager = null; _context.statManager().createRateStat("transport.getBidsJobTime", "How long does it take?", "Transport", new long[] { 10*60*1000l }); startGeoIP(); } @@ -215,7 +214,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade { * This should really be moved to ntcp/NTCPTransport.java, why is it here? */ public static RouterAddress createNTCPAddress(RouterContext ctx) { - if (!TransportManager.enableNTCP(ctx)) return null; + if (!TransportManager.isNTCPEnabled(ctx)) return null; String name = ctx.router().getConfigSetting(PROP_I2NP_NTCP_HOSTNAME); String port = ctx.router().getConfigSetting(PROP_I2NP_NTCP_PORT); /* diff --git a/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java b/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java index 112d3660140285009f309d4c48794e7f02721472..c0245478314bde43ae73920ef5f8e91545b265a3 100644 --- a/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java +++ b/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java @@ -31,18 +31,18 @@ import net.i2p.util.Log; * */ public class FIFOBandwidthLimiter { - private Log _log; - private I2PAppContext _context; + private final Log _log; + private final I2PAppContext _context; private final List<Request> _pendingInboundRequests; private final List<Request> _pendingOutboundRequests; /** how many bytes we can consume for inbound transmission immediately */ - private AtomicInteger _availableInbound = new AtomicInteger(); + private final AtomicInteger _availableInbound = new AtomicInteger(); /** how many bytes we can consume for outbound transmission immediately */ - private AtomicInteger _availableOutbound = new AtomicInteger(); + private final AtomicInteger _availableOutbound = new AtomicInteger(); /** how many bytes we can queue up for bursting */ - private AtomicInteger _unavailableInboundBurst = new AtomicInteger(); + private final AtomicInteger _unavailableInboundBurst = new AtomicInteger(); /** how many bytes we can queue up for bursting */ - private AtomicInteger _unavailableOutboundBurst = new AtomicInteger(); + private final AtomicInteger _unavailableOutboundBurst = new AtomicInteger(); /** how large _unavailableInbound can get */ private int _maxInboundBurst; /** how large _unavailableInbound can get */ @@ -56,14 +56,14 @@ public class FIFOBandwidthLimiter { /** shortcut of whether our inbound rate is unlimited */ private boolean _inboundUnlimited; /** lifetime counter of bytes received */ - private AtomicLong _totalAllocatedInboundBytes = new AtomicLong(); + private final AtomicLong _totalAllocatedInboundBytes = new AtomicLong(); /** lifetime counter of bytes sent */ - private AtomicLong _totalAllocatedOutboundBytes = new AtomicLong(); + private final AtomicLong _totalAllocatedOutboundBytes = new AtomicLong(); /** lifetime counter of tokens available for use but exceeded our maxInboundBurst size */ - private AtomicLong _totalWastedInboundBytes = new AtomicLong(); + private final AtomicLong _totalWastedInboundBytes = new AtomicLong(); /** lifetime counter of tokens available for use but exceeded our maxOutboundBurst size */ - private AtomicLong _totalWastedOutboundBytes = new AtomicLong(); - private FIFOBandwidthRefiller _refiller; + private final AtomicLong _totalWastedOutboundBytes = new AtomicLong(); + private final FIFOBandwidthRefiller _refiller; private long _lastTotalSent; private long _lastTotalReceived; @@ -73,8 +73,6 @@ public class FIFOBandwidthLimiter { private float _sendBps15s; private float _recvBps15s; - private static int __id = 0; - public /* static */ long now() { // dont use the clock().now(), since that may jump return System.currentTimeMillis(); @@ -98,13 +96,9 @@ public class FIFOBandwidthLimiter { _pendingOutboundRequests = new ArrayList(16); _lastTotalSent = _totalAllocatedOutboundBytes.get(); _lastTotalReceived = _totalAllocatedInboundBytes.get(); - _sendBps = 0; - _recvBps = 0; _lastStatsUpdated = now(); _refiller = new FIFOBandwidthRefiller(_context, this); - I2PThread t = new I2PThread(_refiller); - t.setName("BWRefiller" + (++__id)); - t.setDaemon(true); + I2PThread t = new I2PThread(_refiller, "BWRefiller", true); t.setPriority(I2PThread.NORM_PRIORITY-1); t.start(); } @@ -753,7 +747,7 @@ public class FIFOBandwidthLimiter { private int _allocationsSinceWait; private boolean _aborted; private boolean _waited; - List<Request> satisfiedBuffer; + final List<Request> satisfiedBuffer; private CompleteListener _lsnr; private Object _attachment; diff --git a/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java b/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java index 2065cdc28ce0647cbc6f9b0569c80601807d20e2..bc8dd18520d070240311e869528fdb888f6b6b7f 100644 --- a/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java +++ b/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java @@ -7,9 +7,9 @@ import net.i2p.I2PAppContext; import net.i2p.util.Log; public class FIFOBandwidthRefiller implements Runnable { - private Log _log; - private I2PAppContext _context; - private FIFOBandwidthLimiter _limiter; + private final Log _log; + private final I2PAppContext _context; + private final FIFOBandwidthLimiter _limiter; /** how many KBps do we want to allow? */ private int _inboundKBytesPerSecond; /** how many KBps do we want to allow? */ diff --git a/router/java/src/net/i2p/router/transport/GeoIP.java b/router/java/src/net/i2p/router/transport/GeoIP.java index 85a89b3c6410b5eeaf471a37ace4812e5bc0fc58..1406460eece1bf08af0923d4b8ee200223d08236 100644 --- a/router/java/src/net/i2p/router/transport/GeoIP.java +++ b/router/java/src/net/i2p/router/transport/GeoIP.java @@ -35,7 +35,7 @@ import net.i2p.util.Log; * * @author zzz */ -public class GeoIP { +class GeoIP { private Log _log; private RouterContext _context; private final Map<String, String> _codeToName; diff --git a/router/java/src/net/i2p/router/transport/GetBidsJob.java b/router/java/src/net/i2p/router/transport/GetBidsJob.java index 55f8cdb7961bf1c4d27ea53abd5639c2a875d158..e014cc6411842a66f29305ee85f72004f74168ce 100644 --- a/router/java/src/net/i2p/router/transport/GetBidsJob.java +++ b/router/java/src/net/i2p/router/transport/GetBidsJob.java @@ -21,10 +21,10 @@ import net.i2p.util.Log; * pass it on to the transport for processing * */ -public class GetBidsJob extends JobImpl { - private Log _log; - private CommSystemFacadeImpl _facade; - private OutNetMessage _msg; +class GetBidsJob extends JobImpl { + private final Log _log; + private final CommSystemFacadeImpl _facade; + private final OutNetMessage _msg; public GetBidsJob(RouterContext ctx, CommSystemFacadeImpl facade, OutNetMessage msg) { super(ctx); diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java index 7ac7d05b2c7189742e5ebf39483614571bca4cdb..fef5850227f52736d06eebeb5c9217669ec8c149 100644 --- a/router/java/src/net/i2p/router/transport/TransportImpl.java +++ b/router/java/src/net/i2p/router/transport/TransportImpl.java @@ -512,7 +512,7 @@ public abstract class TransportImpl implements Transport { public void markUnreachable(Hash peer) { long now = _context.clock().now(); synchronized (_unreachableEntries) { - _unreachableEntries.put(peer, new Long(now)); + _unreachableEntries.put(peer, Long.valueOf(now)); } markWasUnreachable(peer, true); } diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java index a682840b1780fa28749d2e0d52870fa4f36983eb..266561cbce165695fdcc44ac305aa2ed8488c21f 100644 --- a/router/java/src/net/i2p/router/transport/TransportManager.java +++ b/router/java/src/net/i2p/router/transport/TransportManager.java @@ -37,19 +37,19 @@ import net.i2p.util.Log; import net.i2p.util.Translate; public class TransportManager implements TransportEventListener { - private Log _log; + private final Log _log; /** * Converted from List to prevent concurrent modification exceptions. * If we want more than one transport with the same style we will have to change this. */ - private Map<String, Transport> _transports; - private RouterContext _context; - private UPnPManager _upnpManager; + private final Map<String, Transport> _transports; + private final RouterContext _context; + private final UPnPManager _upnpManager; + /** default true */ public final static String PROP_ENABLE_UDP = "i2np.udp.enable"; + /** default true */ public final static String PROP_ENABLE_NTCP = "i2np.ntcp.enable"; - public final static String DEFAULT_ENABLE_NTCP = "true"; - public final static String DEFAULT_ENABLE_UDP = "true"; /** default true */ public final static String PROP_ENABLE_UPNP = "i2np.upnp.enable"; @@ -63,8 +63,10 @@ public class TransportManager implements TransportEventListener { _context.statManager().createRateStat("transport.bidFailNoTransports", "Could not attempt to bid on message, as none of the transports could attempt it", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); _context.statManager().createRateStat("transport.bidFailAllTransports", "Could not attempt to bid on message, as all of the transports had failed", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); _transports = new ConcurrentHashMap(2); - if (Boolean.valueOf(_context.getProperty(PROP_ENABLE_UPNP, "true")).booleanValue()) + if (_context.getBooleanPropertyDefaultTrue(PROP_ENABLE_UPNP)) _upnpManager = new UPnPManager(context, this); + else + _upnpManager = null; } public void addTransport(Transport transport) { @@ -80,37 +82,33 @@ public class TransportManager implements TransportEventListener { } private void configTransports() { - String enableUDP = _context.router().getConfigSetting(PROP_ENABLE_UDP); - if (enableUDP == null) - enableUDP = DEFAULT_ENABLE_UDP; - if ("true".equalsIgnoreCase(enableUDP)) { + boolean enableUDP = _context.getBooleanPropertyDefaultTrue(PROP_ENABLE_UDP); + if (enableUDP) { UDPTransport udp = new UDPTransport(_context); addTransport(udp); initializeAddress(udp); } - if (enableNTCP(_context)) + if (isNTCPEnabled(_context)) addTransport(new NTCPTransport(_context)); if (_transports.isEmpty()) _log.log(Log.CRIT, "No transports are enabled"); } - public static boolean enableNTCP(RouterContext ctx) { - String enableNTCP = ctx.router().getConfigSetting(PROP_ENABLE_NTCP); - if (enableNTCP == null) - enableNTCP = DEFAULT_ENABLE_NTCP; - return "true".equalsIgnoreCase(enableNTCP); + public static boolean isNTCPEnabled(RouterContext ctx) { + return ctx.getBooleanPropertyDefaultTrue(PROP_ENABLE_NTCP); } - private static void initializeAddress(Transport t) { + private void initializeAddress(Transport t) { String ips = Addresses.getAnyAddress(); if (ips == null) return; - InetAddress ia = null; + InetAddress ia; try { ia = InetAddress.getByName(ips); - } catch (UnknownHostException e) {return;} - if (ia == null) + } catch (UnknownHostException e) { + _log.error("UDP failed to bind to local address", e); return; + } byte[] ip = ia.getAddress(); t.externalAddressReceived(Transport.SOURCE_INTERFACE, ip, 0); } diff --git a/router/java/src/net/i2p/router/transport/UPnP.java b/router/java/src/net/i2p/router/transport/UPnP.java index d13a51cecdc97acdd9d9b1ff096492f250a0586d..05529935da87fdf654faef91b46ef284aae88786 100644 --- a/router/java/src/net/i2p/router/transport/UPnP.java +++ b/router/java/src/net/i2p/router/transport/UPnP.java @@ -52,7 +52,7 @@ import org.freenetproject.ForwardPortStatus; * TODO: Advertise the node like the MDNS plugin does * TODO: Implement EventListener and react on ip-change */ -public class UPnP extends ControlPoint implements DeviceChangeListener, EventListener { +class UPnP extends ControlPoint implements DeviceChangeListener, EventListener { private Log _log; private I2PAppContext _context; @@ -554,7 +554,10 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis return retval; } - /** non-blocking */ + /** + * non-blocking + * @param ports non-null + */ public void onChangePublicPorts(Set<ForwardPort> ports, ForwardPortCallback cb) { Set<ForwardPort> portsToDumpNow = null; Set<ForwardPort> portsToForwardNow = null; @@ -568,7 +571,7 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis portsToForward = ports; portsToForwardNow = ports; portsToDumpNow = null; - } else if(ports == null || ports.isEmpty()) { + } else if(ports.isEmpty()) { portsToDumpNow = portsToForward; portsToForward = ports; portsToForwardNow = null; diff --git a/router/java/src/net/i2p/router/transport/UPnPManager.java b/router/java/src/net/i2p/router/transport/UPnPManager.java index 7996a45562aa26021141f9ca916a19f2288d5ca0..ee8b60072323afa10a19eae5de3de72934c582d5 100644 --- a/router/java/src/net/i2p/router/transport/UPnPManager.java +++ b/router/java/src/net/i2p/router/transport/UPnPManager.java @@ -24,7 +24,7 @@ import org.freenetproject.ForwardPortStatus; * * @author zzz */ -public class UPnPManager { +class UPnPManager { private Log _log; private RouterContext _context; private UPnP _upnp; @@ -87,8 +87,9 @@ public class UPnPManager { if (!_isRunning) return; Set<ForwardPort> forwards = new HashSet(ports.size()); - for (String style : ports.keySet()) { - int port = ports.get(style).intValue(); + for (Map.Entry<String, Integer> entry : ports.entrySet()) { + String style = entry.getKey(); + int port = entry.getValue().intValue(); int protocol = -1; if ("SSU".equals(style)) protocol = ForwardPort.PROTOCOL_UDP_IPV4; @@ -136,8 +137,9 @@ public class UPnPManager { _log.debug("No external address returned"); } - for (ForwardPort fp : statuses.keySet()) { - ForwardPortStatus fps = statuses.get(fp); + for (Map.Entry<ForwardPort, ForwardPortStatus> entry : statuses.entrySet()) { + ForwardPort fp = entry.getKey(); + ForwardPortStatus fps = entry.getValue(); if (_log.shouldLog(Log.DEBUG)) _log.debug(fp.name + " " + fp.protocol + " " + fp.portNumber + " status: " + fps.status + " reason: " + fps.reasonString + " ext port: " + fps.externalPort); diff --git a/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java b/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java index 8de07fe6eac85eac1cef236f05c52b9370fc76ca..d6109c6406b10215379dde96ec3b9ae95d518275 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java +++ b/router/java/src/net/i2p/router/transport/ntcp/EstablishState.java @@ -69,7 +69,7 @@ import net.i2p.util.Log; * NOTE: Check info is unused. * */ -public class EstablishState { +class EstablishState { private RouterContext _context; private Log _log; diff --git a/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java b/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java index 29592bf524cba24bca0a45221e5a7a1c0ee4b2cc..eeeef56c1a3f723758bd149ea316cbd12f6c2e2a 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java +++ b/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java @@ -26,7 +26,7 @@ import net.i2p.util.Log; /** * */ -public class EventPumper implements Runnable { +class EventPumper implements Runnable { private RouterContext _context; private Log _log; private volatile boolean _alive; diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java index 3fb4c6b375de7908765779a87244a2c95947488f..a24b3d9728c0e0374b689f5ca0a3641a7175f3c9 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java @@ -53,9 +53,9 @@ import net.i2p.util.Log; *</pre> * */ -public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { - private RouterContext _context; - private Log _log; +class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { + private final RouterContext _context; + private final Log _log; private SocketChannel _chan; private SelectionKey _conKey; /** list of ByteBuffer containing data we have read and are ready to process, oldest first */ @@ -71,7 +71,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { private long _establishedOn; private EstablishState _establishState; private NTCPTransport _transport; - private boolean _isInbound; + private final boolean _isInbound; private boolean _closed; private NTCPAddress _remAddr; private RouterIdentity _remotePeer; @@ -87,17 +87,17 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { private byte _curReadBlock[]; /** next byte to which data should be placed in the _curReadBlock */ private int _curReadBlockIndex; - private byte _decryptBlockBuf[]; + private final byte _decryptBlockBuf[]; /** last AES block of the encrypted I2NP message (to serve as the next block's IV) */ private byte _prevReadBlock[]; private byte _prevWriteEnd[]; /** current partially read I2NP message */ - private ReadState _curReadState; + private final ReadState _curReadState; private long _messagesRead; private long _messagesWritten; private long _lastSendTime; private long _lastReceiveTime; - private long _created; + private final long _created; private long _nextMetaTime; /** unencrypted outbound metadata buffer */ private final byte _meta[] = new byte[16]; @@ -127,28 +127,21 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { _context = ctx; _log = ctx.logManager().getLog(getClass()); _created = System.currentTimeMillis(); - _lastSendTime = _created; - _lastReceiveTime = _created; _transport = transport; _chan = chan; _readBufs = new LinkedBlockingQueue(); _writeBufs = new LinkedBlockingQueue(); _bwRequests = new ConcurrentHashSet(2); _outbound = new LinkedBlockingQueue(); - _established = false; _isInbound = true; - _closed = false; _decryptBlockBuf = new byte[16]; - _curReadBlock = new byte[16]; - _prevReadBlock = new byte[16]; _curReadState = new ReadState(); _establishState = new EstablishState(ctx, transport, this); _conKey = key; _conKey.attach(this); - _sendingMeta = false; - _consecutiveBacklog = 0; - transport.establishing(this); + initialize(); } + /** * Create an outbound unconnected NTCP connection * @@ -157,26 +150,25 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { _context = ctx; _log = ctx.logManager().getLog(getClass()); _created = System.currentTimeMillis(); - _lastSendTime = _created; - _lastReceiveTime = _created; _transport = transport; + _remotePeer = remotePeer; _remAddr = remAddr; _readBufs = new LinkedBlockingQueue(); _writeBufs = new LinkedBlockingQueue(); _bwRequests = new ConcurrentHashSet(2); _outbound = new LinkedBlockingQueue(); - _established = false; _isInbound = false; - _closed = false; _decryptBlockBuf = new byte[16]; + _curReadState = new ReadState(); + initialize(); + } + + private void initialize() { + _lastSendTime = _created; + _lastReceiveTime = _created; _curReadBlock = new byte[16]; _prevReadBlock = new byte[16]; - _curReadState = new ReadState(); - _remotePeer = remotePeer; - _sendingMeta = false; - _consecutiveBacklog = 0; - //_establishState = new EstablishState(ctx, transport, this); - transport.establishing(this); + _transport.establishing(this); } public SocketChannel getChannel() { return _chan; } @@ -207,13 +199,18 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { _nextMetaTime = System.currentTimeMillis() + _context.random().nextInt(META_FREQUENCY); _nextInfoTime = System.currentTimeMillis() + (INFO_FREQUENCY / 2) + _context.random().nextInt(INFO_FREQUENCY); } + + /** @return seconds */ public long getClockSkew() { return _clockSkew; } + + /** @return milliseconds */ public long getUptime() { if (!_established) return getTimeSinceCreated(); else return System.currentTimeMillis()-_establishedOn; } + public long getMessagesSent() { return _messagesWritten; } public long getMessagesReceived() { return _messagesRead; } public long getOutboundQueueSize() { @@ -222,9 +219,16 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { queued++; return queued; } + + /** @return milliseconds */ public long getTimeSinceSend() { return System.currentTimeMillis()-_lastSendTime; } + + /** @return milliseconds */ public long getTimeSinceReceive() { return System.currentTimeMillis()-_lastReceiveTime; } + + /** @return milliseconds */ public long getTimeSinceCreated() { return System.currentTimeMillis()-_created; } + public int getConsecutiveBacklog() { return _consecutiveBacklog; } public boolean isClosed() { return _closed; } @@ -344,8 +348,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { OutNetMessage infoMsg = new OutNetMessage(_context); infoMsg.setExpiration(_context.clock().now()+10*1000); DatabaseStoreMessage dsm = new DatabaseStoreMessage(_context); - dsm.setKey(_context.routerHash()); - dsm.setRouterInfo(_context.router().getRouterInfo()); + dsm.setEntry(_context.router().getRouterInfo()); infoMsg.setMessage(dsm); infoMsg.setPriority(100); RouterInfo target = _context.netDb().lookupRouterInfoLocally(_remotePeer.calculateHash()); @@ -625,26 +628,26 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { } msg.beginTransmission(); - long begin = System.currentTimeMillis(); + //long begin = System.currentTimeMillis(); PrepBuffer buf = (PrepBuffer)msg.releasePreparationBuffer(); if (buf == null) throw new RuntimeException("buf is null for " + msg); _context.aes().encrypt(buf.unencrypted, 0, buf.encrypted, 0, _sessionKey, _prevWriteEnd, 0, buf.unencryptedLength); System.arraycopy(buf.encrypted, buf.encrypted.length-16, _prevWriteEnd, 0, _prevWriteEnd.length); - long encryptedTime = System.currentTimeMillis(); + //long encryptedTime = System.currentTimeMillis(); //if (_log.shouldLog(Log.DEBUG)) // _log.debug("Encrypting " + msg + " [" + System.identityHashCode(msg) + "] crc=" + crc.getValue() + "\nas: " // + Base64.encode(encrypted, 0, 16) + "...\ndecrypted: " // + Base64.encode(unencrypted, 0, 16) + "..." + "\nIV=" + Base64.encode(_prevWriteEnd, 0, 16)); _transport.getPumper().wantsWrite(this, buf.encrypted); - long wantsTime = System.currentTimeMillis(); + //long wantsTime = System.currentTimeMillis(); releaseBuf(buf); - long releaseTime = System.currentTimeMillis(); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("prepared outbound " + System.identityHashCode(msg) - + " encrypted=" + (encryptedTime-begin) - + " wantsWrite=" + (wantsTime-encryptedTime) - + " releaseBuf=" + (releaseTime-wantsTime)); + //long releaseTime = System.currentTimeMillis(); + //if (_log.shouldLog(Log.DEBUG)) + // _log.debug("prepared outbound " + System.identityHashCode(msg) + // + " encrypted=" + (encryptedTime-begin) + // + " wantsWrite=" + (wantsTime-encryptedTime) + // + " releaseBuf=" + (releaseTime-wantsTime)); // for every 6-12 hours that we are connected to a peer, send them // our updated netDb info (they may not accept it and instead query @@ -667,9 +670,9 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { //if (!_isInbound && !_established) // return; msg.beginPrepare(); - long begin = System.currentTimeMillis(); + //long begin = System.currentTimeMillis(); PrepBuffer buf = acquireBuf(); - long alloc = System.currentTimeMillis(); + //long alloc = System.currentTimeMillis(); I2NPMessage m = msg.getMessage(); buf.baseLength = m.toByteArray(buf.base); @@ -689,7 +692,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { _context.random().nextBytes(buf.pad); // maybe more than necessary, but its only the prng System.arraycopy(buf.pad, 0, buf.unencrypted, 2+sz, buf.padLength); - long serialized = System.currentTimeMillis(); + //long serialized = System.currentTimeMillis(); buf.crc.update(buf.unencrypted, 0, buf.unencryptedLength-4); long val = buf.crc.getValue(); @@ -700,11 +703,11 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { DataHelper.toLong(buf.unencrypted, buf.unencryptedLength-4, 4, val); buf.encrypted = new byte[buf.unencryptedLength]; - long crced = System.currentTimeMillis(); + //long crced = System.currentTimeMillis(); msg.prepared(buf); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Buffered prepare took " + (crced-begin) + ", alloc=" + (alloc-begin) - + " serialize=" + (serialized-alloc) + " crc=" + (crced-serialized)); + //if (_log.shouldLog(Log.DEBUG)) + // _log.debug("Buffered prepare took " + (crced-begin) + ", alloc=" + (alloc-begin) + // + " serialize=" + (serialized-alloc) + " crc=" + (crced-serialized)); } private static final int MIN_PREP_BUFS = 5; @@ -828,14 +831,16 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { * contents have been fully allocated */ public void queuedRecv(ByteBuffer buf, FIFOBandwidthLimiter.Request req) { - addRequest(req); req.attach(buf); req.setCompleteListener(this); + addRequest(req); } + + /** ditto for writes */ public void queuedWrite(ByteBuffer buf, FIFOBandwidthLimiter.Request req) { - addRequest(req); req.attach(buf); req.setCompleteListener(this); + addRequest(req); } /** @@ -1083,15 +1088,6 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener { // enqueueInfoMessage(); // this often? } - @Override - public int hashCode() { return System.identityHashCode(this); } - @Override - public boolean equals(Object obj) { - if(obj == null) return false; - if(obj.getClass() != NTCPConnection.class) return false; - return obj == this; - } - private static final int MAX_HANDLERS = 4; private final static LinkedBlockingQueue<I2NPMessageHandler> _i2npHandlers = new LinkedBlockingQueue(MAX_HANDLERS); diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPSendFinisher.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPSendFinisher.java index 374c7a5ba0dbf89fca890c991106a7209377476d..24b86fd5b30d17493b34b9e7a0a2208ec039f1e7 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPSendFinisher.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPSendFinisher.java @@ -23,7 +23,7 @@ import net.i2p.util.Log; * * @author zzz */ -public class NTCPSendFinisher { +class NTCPSendFinisher { private static final int MIN_THREADS = 1; private static final int MAX_THREADS = 4; private final I2PAppContext _context; @@ -31,7 +31,11 @@ public class NTCPSendFinisher { private final Log _log; private static int _count; private ThreadPoolExecutor _executor; - private static int _threads; + private static final int THREADS; + static { + long maxMemory = Runtime.getRuntime().maxMemory(); + THREADS = (int) Math.max(MIN_THREADS, Math.min(MAX_THREADS, 1 + (maxMemory / (32*1024*1024)))); + } public NTCPSendFinisher(I2PAppContext context, NTCPTransport transport) { _context = context; @@ -42,9 +46,7 @@ public class NTCPSendFinisher { public void start() { _count = 0; - long maxMemory = Runtime.getRuntime().maxMemory(); - _threads = (int) Math.max(MIN_THREADS, Math.min(MAX_THREADS, 1 + (maxMemory / (32*1024*1024)))); - _executor = new CustomThreadPoolExecutor(_threads); + _executor = new CustomThreadPoolExecutor(THREADS); } public void stop() { @@ -73,7 +75,7 @@ public class NTCPSendFinisher { private static class CustomThreadFactory implements ThreadFactory { public Thread newThread(Runnable r) { Thread rv = Executors.defaultThreadFactory().newThread(r); - rv.setName("NTCPSendFinisher " + (++_count) + '/' + _threads); + rv.setName("NTCPSendFinisher " + (++_count) + '/' + THREADS); rv.setDaemon(true); return rv; } diff --git a/router/java/src/net/i2p/router/transport/udp/ACKSender.java b/router/java/src/net/i2p/router/transport/udp/ACKSender.java index d6035766db1410fbf757b842ce3e52ce6bca2b02..33fd401df1d2b24f9370bd811e0a7bfb28604ced 100644 --- a/router/java/src/net/i2p/router/transport/udp/ACKSender.java +++ b/router/java/src/net/i2p/router/transport/udp/ACKSender.java @@ -2,6 +2,7 @@ package net.i2p.router.transport.udp; import java.util.HashSet; import java.util.List; +import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -120,7 +121,7 @@ class ACKSender implements Runnable { try { // bulk operations may throw an exception _peersToACK.addAll(notYet); - } catch (Exception e) {} + } catch (NoSuchElementException nsee) {} notYet.clear(); break; } else { diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java index d19f929c275b22df983e31aef2655689e376140f..24e0efe87720e18fa00108da7712ef1d153bc623 100644 --- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java +++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java @@ -577,8 +577,7 @@ class EstablishmentManager { (isInbound ? " inbound con from " + peer : "outbound con to " + peer)); DatabaseStoreMessage m = new DatabaseStoreMessage(_context); - m.setKey(_context.routerHash()); - m.setRouterInfo(_context.router().getRouterInfo()); + m.setEntry(_context.router().getRouterInfo()); m.setMessageExpiration(_context.clock().now() + 10*1000); _transport.send(m, peer); } @@ -642,7 +641,7 @@ class EstablishmentManager { private void handlePendingIntro(OutboundEstablishState state) { long nonce = _context.random().nextLong(MAX_NONCE); while (true) { - OutboundEstablishState old = _liveIntroductions.putIfAbsent(new Long(nonce), state); + OutboundEstablishState old = _liveIntroductions.putIfAbsent(Long.valueOf(nonce), state); if (old != null) { nonce = _context.random().nextLong(MAX_NONCE); } else { @@ -670,7 +669,7 @@ class EstablishmentManager { } public void timeReached() { // remove only if value equal to state - boolean removed = _liveIntroductions.remove(new Long(_nonce), _state); + boolean removed = _liveIntroductions.remove(Long.valueOf(_nonce), _state); if (removed) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Send intro for " + _state.getRemoteHostId().toString() + " timed out"); @@ -682,7 +681,7 @@ class EstablishmentManager { void receiveRelayResponse(RemoteHostId bob, UDPPacketReader reader) { long nonce = reader.getRelayResponseReader().readNonce(); - OutboundEstablishState state = _liveIntroductions.remove(new Long(nonce)); + OutboundEstablishState state = _liveIntroductions.remove(Long.valueOf(nonce)); if (state == null) return; // already established diff --git a/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java b/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java index bc0ddc5f5e1fdc0fabec9c8a0d520028b05e2617..f685c9e787e6a1dbc522ee8d9ff23757814f0353 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java @@ -100,7 +100,7 @@ class InboundMessageFragments /*implements UDPTransport.PartialACKSource */{ for (int i = 0; i < fragments; i++) { long mid = data.readMessageId(i); - Long messageId = new Long(mid); + Long messageId = Long.valueOf(mid); if (_recentlyCompletedMessages.isKnown(mid)) { _context.statManager().addRateData("udp.ignoreRecentDuplicate", 1, 0); diff --git a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java index 326c43f15d1b4721d641b7a9db81df3e5d6619ad..b39f44de9ad7f7803ee406e620a64f29e7d33a55 100644 --- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java +++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java @@ -53,7 +53,7 @@ class IntroductionManager { _log.debug("Adding peer " + peer.getRemoteHostId() + ", weRelayToThemAs " + peer.getWeRelayToThemAs() + ", theyRelayToUsAs " + peer.getTheyRelayToUsAs()); if (peer.getWeRelayToThemAs() > 0) - _outbound.put(new Long(peer.getWeRelayToThemAs()), peer); + _outbound.put(Long.valueOf(peer.getWeRelayToThemAs()), peer); if (peer.getTheyRelayToUsAs() > 0) { _inbound.add(peer); } @@ -65,14 +65,14 @@ class IntroductionManager { _log.debug("removing peer " + peer.getRemoteHostId() + ", weRelayToThemAs " + peer.getWeRelayToThemAs() + ", theyRelayToUsAs " + peer.getTheyRelayToUsAs()); if (peer.getWeRelayToThemAs() > 0) - _outbound.remove(new Long(peer.getWeRelayToThemAs())); + _outbound.remove(Long.valueOf(peer.getWeRelayToThemAs())); if (peer.getTheyRelayToUsAs() > 0) { _inbound.remove(peer); } } public PeerState get(long id) { - return _outbound.get(new Long(id)); + return _outbound.get(Long.valueOf(id)); } /** diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java index 98daaecf981e36220259e5a4ad482f5a841bdcfd..cc43c4b2e3cb2d6091cb7100e9514bc9f50b68c3 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java @@ -661,7 +661,6 @@ class PacketBuilder { */ public UDPPacket buildSessionDestroyPacket(PeerState peer) { UDPPacket packet = buildPacketHeader((byte)(UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY << 4)); - byte data[] = packet.getPacket().getData(); int off = HEADER_SIZE; StringBuilder msg = null; diff --git a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java index 2c0138228d728d69a854573718659232a03526bc..230c1b7921e1efd4a6b57320e5708d0398c33896 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java @@ -20,14 +20,14 @@ import net.i2p.util.Log; * */ class PacketHandler { - private RouterContext _context; - private Log _log; - private UDPTransport _transport; - private UDPEndpoint _endpoint; - private EstablishmentManager _establisher; - private InboundMessageFragments _inbound; - private PeerTestManager _testManager; - private IntroductionManager _introManager; + private final RouterContext _context; + private final Log _log; + private final UDPTransport _transport; + private final UDPEndpoint _endpoint; + private final EstablishmentManager _establisher; + private final InboundMessageFragments _inbound; + private final PeerTestManager _testManager; + private final IntroductionManager _introManager; private boolean _keepReading; private final Handler[] _handlers; @@ -337,6 +337,9 @@ class PacketHandler { _state = 30; } + /** + * @param state non-null + */ private void receivePacket(UDPPacketReader reader, UDPPacket packet, InboundEstablishState state) { receivePacket(reader, packet, state, true); } @@ -345,11 +348,12 @@ class PacketHandler { * Inbound establishing conn * Decrypt and validate the packet then call handlePacket() * + * @param state non-null * @param allowFallback if it isn't valid for this establishment state, try as a non-establishment packet */ private void receivePacket(UDPPacketReader reader, UDPPacket packet, InboundEstablishState state, boolean allowFallback) { _state = 31; - if ( (state != null) && (_log.shouldLog(Log.DEBUG)) ) { + if (_log.shouldLog(Log.DEBUG)) { StringBuilder buf = new StringBuilder(128); buf.append("Attempting to receive a packet on a known inbound state: "); buf.append(state); @@ -388,10 +392,12 @@ class PacketHandler { /** * Outbound establishing conn * Decrypt and validate the packet then call handlePacket() + * + * @param state non-null */ private void receivePacket(UDPPacketReader reader, UDPPacket packet, OutboundEstablishState state) { _state = 35; - if ( (state != null) && (_log.shouldLog(Log.DEBUG)) ) { + if (_log.shouldLog(Log.DEBUG)) { StringBuilder buf = new StringBuilder(128); buf.append("Attempting to receive a packet on a known outbound state: "); buf.append(state); diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java index a3211a5a620f065212fe56117fb747f59d889b78..46b1e4666884af6891487359a1f8bab4d41d8eb3 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java @@ -153,7 +153,7 @@ class PeerTestManager { _log.debug("Running test with bob = " + bobIP + ":" + bobPort + " " + test.getNonce()); while (_recentTests.size() > 16) _recentTests.poll(); - _recentTests.offer(new Long(test.getNonce())); + _recentTests.offer(Long.valueOf(test.getNonce())); sendTestToBob(); @@ -432,7 +432,7 @@ class PeerTestManager { testInfo.readIP(testIP, 0); } - PeerTestState state = _activeTests.get(new Long(nonce)); + PeerTestState state = _activeTests.get(Long.valueOf(nonce)); if (state == null) { if ( (testIP == null) || (testPort <= 0) ) { @@ -441,7 +441,7 @@ class PeerTestManager { _log.debug("test IP/port are blank coming from " + from + ", assuming we are Bob and they are alice"); receiveFromAliceAsBob(from, testInfo, nonce, null); } else { - if (_recentTests.contains(new Long(nonce))) { + if (_recentTests.contains(Long.valueOf(nonce))) { // ignore the packet, as its a holdover from a recently completed locally // initiated test } else { @@ -536,7 +536,7 @@ class PeerTestManager { _log.debug("Receive from bob (" + from + ") as charlie, sending back to bob and sending to alice @ " + aliceIP + ":" + alicePort); if (isNew) { - _activeTests.put(new Long(nonce), state); + _activeTests.put(Long.valueOf(nonce), state); SimpleScheduler.getInstance().addEvent(new RemoveTest(nonce), MAX_CHARLIE_LIFETIME); } @@ -615,7 +615,7 @@ class PeerTestManager { } if (isNew) { - _activeTests.put(new Long(nonce), state); + _activeTests.put(Long.valueOf(nonce), state); SimpleScheduler.getInstance().addEvent(new RemoveTest(nonce), MAX_CHARLIE_LIFETIME); } @@ -691,7 +691,7 @@ class PeerTestManager { _nonce = nonce; } public void timeReached() { - _activeTests.remove(new Long(_nonce)); + _activeTests.remove(Long.valueOf(_nonce)); } } } diff --git a/router/java/src/net/i2p/router/transport/udp/RemoteHostId.java b/router/java/src/net/i2p/router/transport/udp/RemoteHostId.java index 510d712a0bb478838e0e6a3977ca823b4ce6e9dc..837a50268e62d94a68855eb59bf7232498a4f57b 100644 --- a/router/java/src/net/i2p/router/transport/udp/RemoteHostId.java +++ b/router/java/src/net/i2p/router/transport/udp/RemoteHostId.java @@ -28,21 +28,15 @@ final class RemoteHostId { @Override public int hashCode() { - int rv = 0; - for (int i = 0; _ip != null && i < _ip.length; i++) - rv += _ip[i] << i; - for (int i = 0; _peerHash != null && i < _peerHash.length; i++) - rv += _peerHash[i] << i; - rv += _port; - return rv; + return DataHelper.hashCode(_ip) ^ DataHelper.hashCode(_peerHash) ^ _port; } @Override public boolean equals(Object obj) { if (obj == null) - throw new NullPointerException("obj is null"); + return false; if (!(obj instanceof RemoteHostId)) - throw new ClassCastException("obj is a " + obj.getClass().getName()); + return false; RemoteHostId id = (RemoteHostId)obj; return (_port == id.getPort()) && DataHelper.eq(_ip, id.getIP()) && DataHelper.eq(_peerHash, id.getPeerHash()); } diff --git a/router/java/src/net/i2p/router/transport/udp/UDPAddress.java b/router/java/src/net/i2p/router/transport/udp/UDPAddress.java index 6db0b1c6c2d840106582216896634306eeaff141..bcee07605e44a222faed26f1fc4c4b3b7f957d29 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPAddress.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPAddress.java @@ -10,6 +10,7 @@ import net.i2p.data.SessionKey; /** * basic helper to parse out peer info from a udp address + * FIXME public for ConfigNetHelper */ public class UDPAddress { private String _host; @@ -152,7 +153,7 @@ public class UDPAddress { } public String getHost() { return _host; } - public InetAddress getHostAddress() { + InetAddress getHostAddress() { if (_hostAddress == null) { try { _hostAddress = InetAddress.getByName(_host); @@ -163,10 +164,10 @@ public class UDPAddress { return _hostAddress; } public int getPort() { return _port; } - public byte[] getIntroKey() { return _introKey; } + byte[] getIntroKey() { return _introKey; } - public int getIntroducerCount() { return (_introAddresses == null ? 0 : _introAddresses.length); } - public InetAddress getIntroducerHost(int i) { + int getIntroducerCount() { return (_introAddresses == null ? 0 : _introAddresses.length); } + InetAddress getIntroducerHost(int i) { if (_introAddresses[i] == null) { try { _introAddresses[i] = InetAddress.getByName(_introHosts[i]); @@ -176,8 +177,8 @@ public class UDPAddress { } return _introAddresses[i]; } - public int getIntroducerPort(int i) { return _introPorts[i]; } - public byte[] getIntroducerKey(int i) { return _introKeys[i]; } - public long getIntroducerTag(int i) { return _introTags[i]; } + int getIntroducerPort(int i) { return _introPorts[i]; } + byte[] getIntroducerKey(int i) { return _introKeys[i]; } + long getIntroducerTag(int i) { return _introTags[i]; } } diff --git a/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java b/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java index 0a1937db10d8c7c59e9c21da37b49536e6e7a563..ded71f8a2abe350980aa06ca08cefa5bf2ca0ff6 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java @@ -20,16 +20,16 @@ import net.i2p.util.SimpleTimer; * */ class UDPReceiver { - private RouterContext _context; - private Log _log; + private final RouterContext _context; + private final Log _log; private DatagramSocket _socket; private String _name; private final BlockingQueue<UDPPacket> _inboundQueue; private boolean _keepRunning; - private Runner _runner; - private UDPTransport _transport; + private final Runner _runner; + private final UDPTransport _transport; private static int __id; - private int _id; + private final int _id; private static final int TYPE_POISON = -99999; public UDPReceiver(RouterContext ctx, UDPTransport transport, DatagramSocket socket, String name) { @@ -179,8 +179,7 @@ class UDPReceiver { msg.append(queueSize); msg.append(" queued for "); msg.append(headPeriod); - if (_transport != null) - msg.append(" packet handlers: ").append(_transport.getPacketHandlerStatus()); + msg.append(" packet handlers: ").append(_transport.getPacketHandlerStatus()); _log.warn(msg.toString()); } return queueSize; diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index b7b6f28da256a4ac4d1fffd15ab7d0f60f82c548..b8f1d35ee4b38d8268533f68164256be884e1f2f 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -18,6 +18,7 @@ import java.util.TreeSet; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; +import net.i2p.data.DatabaseEntry; import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.data.RouterAddress; @@ -33,6 +34,7 @@ import net.i2p.router.RouterContext; import net.i2p.router.transport.Transport; import net.i2p.router.transport.TransportBid; import net.i2p.router.transport.TransportImpl; +import net.i2p.router.util.RandomIterator; import net.i2p.util.ConcurrentHashSet; import net.i2p.util.Log; import net.i2p.util.SimpleScheduler; @@ -156,7 +158,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority public static final int DEFAULT_COST = 5; private static final int TEST_FREQUENCY = 13*60*1000; - public static final long[] RATES = { 10*60*1000 }; + static final long[] RATES = { 10*60*1000 }; private static final int[] BID_VALUES = { 15, 20, 50, 65, 80, 95, 100, 115, TransportBid.TRANSIENT_FAIL }; private static final int FAST_PREFERRED_BID = 0; @@ -769,8 +771,11 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority public void messageReceived(I2NPMessage inMsg, RouterIdentity remoteIdent, Hash remoteIdentHash, long msToReceive, int bytesReceived) { if (inMsg.getType() == DatabaseStoreMessage.MESSAGE_TYPE) { DatabaseStoreMessage dsm = (DatabaseStoreMessage)inMsg; - if ( (dsm.getRouterInfo() != null) && - (dsm.getRouterInfo().getNetworkId() != Router.NETWORK_ID) ) { + DatabaseEntry entry = dsm.getEntry(); + if (entry == null) + return; + if (entry.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO && + ((RouterInfo) entry).getNetworkId() != Router.NETWORK_ID) { // this is pre-0.6.1.10, so it isn't going to happen any more /* @@ -788,7 +793,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority + " because they are in the wrong net"); } */ - Hash peerHash = dsm.getRouterInfo().getIdentity().calculateHash(); + Hash peerHash = entry.getHash(); PeerState peer = getPeerState(peerHash); if (peer != null) { RemoteHostId remote = peer.getRemoteHostId(); @@ -797,14 +802,14 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority SimpleScheduler.getInstance().addEvent(new RemoveDropList(remote), DROPLIST_PERIOD); } markUnreachable(peerHash); - _context.shitlist().shitlistRouter(peerHash, "Part of the wrong network, version = " + dsm.getRouterInfo().getOption("router.version")); + _context.shitlist().shitlistRouter(peerHash, "Part of the wrong network, version = " + ((RouterInfo) entry).getOption("router.version")); //_context.shitlist().shitlistRouter(peerHash, "Part of the wrong network", STYLE); dropPeer(peerHash, false, "wrong network"); if (_log.shouldLog(Log.WARN)) - _log.warn("Dropping the peer " + peerHash.toBase64() + " because they are in the wrong net: " + dsm.getRouterInfo()); + _log.warn("Dropping the peer " + peerHash.toBase64() + " because they are in the wrong net: " + entry); return; } else { - if (dsm.getRouterInfo() != null) { + if (entry.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) { if (_log.shouldLog(Log.INFO)) _log.info("Received an RI from the same net"); } else { @@ -1634,7 +1639,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final IdleInComparator _instance = new IdleInComparator(); public static final IdleInComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = r.getLastReceiveTime() - l.getLastReceiveTime(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1646,7 +1651,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final IdleOutComparator _instance = new IdleOutComparator(); public static final IdleOutComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = r.getLastSendTime() - l.getLastSendTime(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1658,7 +1663,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final RateInComparator _instance = new RateInComparator(); public static final RateInComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = l.getReceiveBps() - r.getReceiveBps(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1670,7 +1675,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final RateOutComparator _instance = new RateOutComparator(); public static final RateOutComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = l.getSendBps() - r.getSendBps(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1682,7 +1687,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final UptimeComparator _instance = new UptimeComparator(); public static final UptimeComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = r.getKeyEstablishedTime() - l.getKeyEstablishedTime(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1694,7 +1699,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final SkewComparator _instance = new SkewComparator(); public static final SkewComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = Math.abs(l.getClockSkew()) - Math.abs(r.getClockSkew()); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1706,7 +1711,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final CwndComparator _instance = new CwndComparator(); public static final CwndComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = l.getSendWindowBytes() - r.getSendWindowBytes(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1718,7 +1723,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final SsthreshComparator _instance = new SsthreshComparator(); public static final SsthreshComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = l.getSlowStartThreshold() - r.getSlowStartThreshold(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1730,7 +1735,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final RTTComparator _instance = new RTTComparator(); public static final RTTComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = l.getRTT() - r.getRTT(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1742,7 +1747,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final DevComparator _instance = new DevComparator(); public static final DevComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = l.getRTTDeviation() - r.getRTTDeviation(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1754,7 +1759,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final RTOComparator _instance = new RTOComparator(); public static final RTOComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = l.getRTO() - r.getRTO(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1766,7 +1771,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final MTUComparator _instance = new MTUComparator(); public static final MTUComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = l.getMTU() - r.getMTU(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1778,7 +1783,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final SendCountComparator _instance = new SendCountComparator(); public static final SendCountComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = l.getPacketsTransmitted() - r.getPacketsTransmitted(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1790,7 +1795,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final RecvCountComparator _instance = new RecvCountComparator(); public static final RecvCountComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = l.getPacketsReceived() - r.getPacketsReceived(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1802,7 +1807,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final ResendComparator _instance = new ResendComparator(); public static final ResendComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = l.getPacketsRetransmitted() - r.getPacketsRetransmitted(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1814,7 +1819,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final DupComparator _instance = new DupComparator(); public static final DupComparator instance() { return _instance; } @Override - protected int compare(PeerState l, PeerState r) { + public int compare(PeerState l, PeerState r) { long rv = l.getPacketsReceivedDuplicate() - r.getPacketsReceivedDuplicate(); if (rv == 0) // fallback on alpha return super.compare(l, r); @@ -1823,17 +1828,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority } } - private static class PeerComparator implements Comparator { - public int compare(Object lhs, Object rhs) { - if ( (lhs == null) || (rhs == null) || !(lhs instanceof PeerState) || !(rhs instanceof PeerState)) - throw new IllegalArgumentException("rhs = " + rhs + " lhs = " + lhs); - return compare((PeerState)lhs, (PeerState)rhs); - } - protected int compare(PeerState l, PeerState r) { - // base64 retains binary ordering - return l.getRemotePeer().toBase64().compareTo(r.getRemotePeer().toBase64()); + private static class PeerComparator implements Comparator<PeerState> { + public int compare(PeerState l, PeerState r) { + return DataHelper.compareTo(l.getRemotePeer().getData(), r.getRemotePeer().getData()); } } + private static class InverseComparator implements Comparator { private Comparator _comp; public InverseComparator(Comparator comp) { _comp = comp; } @@ -2328,9 +2328,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority PeerState pickTestPeer(RemoteHostId dontInclude) { List<PeerState> peers = new ArrayList(_peersByIdent.values()); - Collections.shuffle(peers, _context.random()); - for (int i = 0; i < peers.size(); i++) { - PeerState peer = peers.get(i); + for (Iterator<PeerState> iter = new RandomIterator(peers); iter.hasNext(); ) { + PeerState peer = iter.next(); if ( (dontInclude != null) && (dontInclude.equals(peer.getRemoteHostId())) ) continue; RouterInfo peerInfo = _context.netDb().lookupRouterInfoLocally(peer.getRemotePeer()); diff --git a/router/java/src/net/i2p/router/tunnel/BuildMessageGenerator.java b/router/java/src/net/i2p/router/tunnel/BuildMessageGenerator.java index da169db7517f3125aa930af617244f605ac2dfbc..1790669890f1d1abea6c1d49a44f4e71bfdd2221 100644 --- a/router/java/src/net/i2p/router/tunnel/BuildMessageGenerator.java +++ b/router/java/src/net/i2p/router/tunnel/BuildMessageGenerator.java @@ -21,9 +21,6 @@ import net.i2p.util.Log; * */ public class BuildMessageGenerator { - // cached, rather than creating lots of temporary Integer objects whenever we build a tunnel - public static final Integer ORDER[] = new Integer[TunnelBuildMessage.MAX_RECORD_COUNT]; - static { for (int i = 0; i < ORDER.length; i++) ORDER[i] = Integer.valueOf(i); } /** return null if it is unable to find a router's public key (etc) */ /**** diff --git a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java index b21ecc48ce87f51884cb991c6874c19dc1654441..a0a2f2edacdf2fe8d93cf450001547614215c987 100644 --- a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java +++ b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java @@ -363,10 +363,10 @@ public class FragmentHandler { FragmentedMessage msg = null; if (fragmented) { synchronized (_fragmentedMessages) { - msg = _fragmentedMessages.get(new Long(messageId)); + msg = _fragmentedMessages.get(Long.valueOf(messageId)); if (msg == null) { msg = new FragmentedMessage(_context); - _fragmentedMessages.put(new Long(messageId), msg); + _fragmentedMessages.put(Long.valueOf(messageId), msg); } } } else { @@ -380,7 +380,7 @@ public class FragmentHandler { if (!ok) return -1; if (msg.isComplete()) { synchronized (_fragmentedMessages) { - _fragmentedMessages.remove(new Long(messageId)); + _fragmentedMessages.remove(Long.valueOf(messageId)); } if (msg.getExpireEvent() != null) SimpleTimer.getInstance().removeEvent(msg.getExpireEvent()); @@ -435,10 +435,10 @@ public class FragmentHandler { FragmentedMessage msg = null; synchronized (_fragmentedMessages) { - msg = _fragmentedMessages.get(new Long(messageId)); + msg = _fragmentedMessages.get(Long.valueOf(messageId)); if (msg == null) { msg = new FragmentedMessage(_context); - _fragmentedMessages.put(new Long(messageId), msg); + _fragmentedMessages.put(Long.valueOf(messageId), msg); } } @@ -449,7 +449,7 @@ public class FragmentHandler { if (msg.isComplete()) { synchronized (_fragmentedMessages) { - _fragmentedMessages.remove(new Long(messageId)); + _fragmentedMessages.remove(Long.valueOf(messageId)); } if (msg.getExpireEvent() != null) SimpleTimer.getInstance().removeEvent(msg.getExpireEvent()); @@ -532,7 +532,7 @@ public class FragmentHandler { public void timeReached() { boolean removed = false; synchronized (_fragmentedMessages) { - removed = (null != _fragmentedMessages.remove(new Long(_msg.getMessageId()))); + removed = (null != _fragmentedMessages.remove(Long.valueOf(_msg.getMessageId()))); } synchronized (_msg) { if (removed && !_msg.getReleased()) { diff --git a/router/java/src/net/i2p/router/tunnel/HopConfig.java b/router/java/src/net/i2p/router/tunnel/HopConfig.java index 532bdfe20c5157f28d16dcca4c9c47a871363862..3ac96bf280c78a2a45bea08e22a770cd81620f9c 100644 --- a/router/java/src/net/i2p/router/tunnel/HopConfig.java +++ b/router/java/src/net/i2p/router/tunnel/HopConfig.java @@ -73,7 +73,7 @@ public class HopConfig { } public void setSendTunnelId(byte id[]) { _sendTunnelId = id; } - private TunnelId getTunnel(byte id[]) { + private static TunnelId getTunnel(byte id[]) { if (id == null) return null; else diff --git a/router/java/src/net/i2p/router/tunnel/HopProcessor.java b/router/java/src/net/i2p/router/tunnel/HopProcessor.java index 205339d7f4076f1556aaf30178a9d91dfc3bd991..eac1f71dae8230111519d3c7110932a792acf481 100644 --- a/router/java/src/net/i2p/router/tunnel/HopProcessor.java +++ b/router/java/src/net/i2p/router/tunnel/HopProcessor.java @@ -77,7 +77,7 @@ public class HopProcessor { boolean okIV = _validator.receiveIV(orig, offset, orig, offset + IV_LENGTH); if (!okIV) { if (_log.shouldLog(Log.WARN)) - _log.warn("Invalid IV received on tunnel " + _config.getReceiveTunnelId()); + _log.warn("Invalid IV received on tunnel " + _config.getReceiveTunnel()); return false; } diff --git a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java index fcdb77ea9ea47868f0e23f65d9f4aec9ec68e171..82fdc47c60ef2bbd56c881a1d427deaa209e7495 100644 --- a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java +++ b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java @@ -1,8 +1,10 @@ package net.i2p.router.tunnel; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.Payload; +import net.i2p.data.RouterInfo; import net.i2p.data.TunnelId; import net.i2p.data.i2np.DataMessage; import net.i2p.data.i2np.DatabaseSearchReplyMessage; @@ -71,7 +73,7 @@ public class InboundMessageDistributor implements GarlicMessageReceiver.CloveRec msg = newMsg; } else if ( (_client != null) && (msg.getType() == DatabaseStoreMessage.MESSAGE_TYPE) && - (((DatabaseStoreMessage)msg).getValueType() == DatabaseStoreMessage.KEY_TYPE_ROUTERINFO)) { + (((DatabaseStoreMessage)msg).getEntry().getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO)) { // FVSJ may result in an unsolicited RI store if the peer went non-ff. // Maybe we can figure out a way to handle this safely, so we don't ask him again. // For now, just hope we eventually find out through other means. @@ -165,7 +167,7 @@ public class InboundMessageDistributor implements GarlicMessageReceiver.CloveRec // unnecessarily DatabaseStoreMessage dsm = (DatabaseStoreMessage)data; try { - if (dsm.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET) { + if (dsm.getEntry().getType() == DatabaseEntry.KEY_TYPE_LEASESET) { // If it was stored to us before, don't undo the // receivedAsPublished flag so we will continue to respond to requests // for the leaseset. That is, we don't want this to change the @@ -173,10 +175,11 @@ public class InboundMessageDistributor implements GarlicMessageReceiver.CloveRec // When the keyspace rotates at midnight, and this leaseset moves out // of our keyspace, maybe we shouldn't do this? // Should we do this whether ff or not? - LeaseSet old = _context.netDb().store(dsm.getKey(), dsm.getLeaseSet()); + LeaseSet ls = (LeaseSet) dsm.getEntry(); + LeaseSet old = _context.netDb().store(dsm.getKey(), ls); if (old != null && old.getReceivedAsPublished() /** && ((FloodfillNetworkDatabaseFacade)_context.netDb()).floodfillEnabled() **/ ) - dsm.getLeaseSet().setReceivedAsPublished(true); + ls.setReceivedAsPublished(true); if (_log.shouldLog(Log.INFO)) _log.info("Storing LS for: " + dsm.getKey() + " sent to: " + _client); } else { @@ -189,7 +192,7 @@ public class InboundMessageDistributor implements GarlicMessageReceiver.CloveRec _log.error("Dropped dangerous message down a tunnel for " + _client.toBase64() + ": " + dsm, new Exception("cause")); return; } - _context.netDb().store(dsm.getKey(), dsm.getRouterInfo()); + _context.netDb().store(dsm.getKey(), (RouterInfo) dsm.getEntry()); } } catch (IllegalArgumentException iae) { if (_log.shouldLog(Log.WARN)) diff --git a/router/java/src/net/i2p/router/tunnel/TrivialPreprocessor.java b/router/java/src/net/i2p/router/tunnel/TrivialPreprocessor.java index c9b28b85629fe8a448115d21b7da411deba14b95..6c85895c65948e40d8ccabe53e56a1c124f125df 100644 --- a/router/java/src/net/i2p/router/tunnel/TrivialPreprocessor.java +++ b/router/java/src/net/i2p/router/tunnel/TrivialPreprocessor.java @@ -52,92 +52,11 @@ public class TrivialPreprocessor implements TunnelGateway.QueuePreprocessor { * NOTE: Unused here, see BatchedPreprocessor override, super is not called. */ public boolean preprocessQueue(List<TunnelGateway.Pending> pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) { - if (true) throw new IllegalArgumentException("unused, right?"); - long begin = System.currentTimeMillis(); - StringBuilder buf = null; - if (_log.shouldLog(Log.DEBUG)) { - buf = new StringBuilder(256); - buf.append("Trivial preprocessing of ").append(pending.size()).append(" "); - } - while (!pending.isEmpty()) { - TunnelGateway.Pending msg = pending.remove(0); - long beforePreproc = System.currentTimeMillis(); - byte preprocessed[][] = preprocess(msg); - long afterPreproc = System.currentTimeMillis(); - if (buf != null) - buf.append("preprocessed into " + preprocessed.length + " fragments after " + (afterPreproc-beforePreproc) + ". "); - for (int i = 0; i < preprocessed.length; i++) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Preprocessed: fragment " + i + "/" + (preprocessed.length-1) + " in " - + msg.getMessageId() + ": " - + " send through " + sender + " receive with " + rec); - //Base64.encode(preprocessed[i])); - long beforeSend = System.currentTimeMillis(); - long id = sender.sendPreprocessed(preprocessed[i], rec); - long afterSend = System.currentTimeMillis(); - if (buf != null) - buf.append("send of " + msg.getMessageId() + " took " + (afterSend-beforeSend) + ". "); - msg.addMessageId(id); - } - notePreprocessing(msg.getMessageId(), msg.getFragmentNumber(), preprocessed.length, msg.getMessageIds(), null); - if (preprocessed.length != msg.getFragmentNumber() + 1) { - throw new RuntimeException("wtf, preprocessed " + msg.getMessageId() + " into " - + msg.getFragmentNumber() + "/" + preprocessed.length + " fragments, size = " - + msg.getData().length); - } - if (buf != null) - buf.append("all fragments sent after " + (System.currentTimeMillis()-afterPreproc) + ". "); - } - if (buf != null) { - buf.append("queue preprocessed after " + (System.currentTimeMillis()-begin) + "."); - _log.debug(buf.toString()); - } - return false; + throw new IllegalArgumentException("unused, right?"); } protected void notePreprocessing(long messageId, int numFragments, int totalLength, List<Long> messageIds, String msg) {} - /* - * @deprecated unused except by above - */ - private byte[][] preprocess(TunnelGateway.Pending msg) { - List<byte[]> fragments = new ArrayList(1); - - while (msg.getOffset() < msg.getData().length) { - fragments.add(preprocessFragment(msg)); - //if (_log.shouldLog(Log.DEBUG)) - // _log.debug("\n\nafter preprocessing fragment\n\n"); - } - - byte rv[][] = new byte[fragments.size()][]; - for (int i = 0; i < fragments.size(); i++) - rv[i] = fragments.get(i); - return rv; - } - - /** - * Preprocess the next available fragment off the given one in phases: - * First, write it out as { instructions + payload + random IV }, calculate the - * SHA256 of that, then move the instructions + payload to the end - * of the target, setting IV as the beginning. Then add the necessary random pad - * bytes after the IV, followed by the first 4 bytes of that SHA256, lining up - * exactly to meet the beginning of the instructions. (i hope) - * - * @deprecated unused except by above - */ - private byte[] preprocessFragment(TunnelGateway.Pending msg) { - byte target[] = _dataCache.acquire().getData(); - - int offset = 0; - if (msg.getOffset() <= 0) - offset = writeFirstFragment(msg, target, offset); - else - offset = writeSubsequentFragment(msg, target, offset); - - preprocess(target, offset); - return target; - } - /** * Wrap the preprocessed fragments with the necessary padding / checksums * to act as a tunnel message. diff --git a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java index c8424d8533807a09c6af276e6500b33c64280e62..48305513aafa5b4ddf1284134c52e96ef50a18c6 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java @@ -623,7 +623,7 @@ public class TunnelDispatcher implements Service { // drop in proportion to size w.r.t. a standard 1024-byte message // this is a little expensive but we want to adjust the curve between 0 and 1 // Most messages are 1024, only at the OBEP do we see other sizes - if (len != 1024d) + if ((int)len != 1024) pctDrop = (float) Math.pow(pctDrop, 1024d / len); float rand = _context.random().nextFloat(); boolean reject = rand <= pctDrop; @@ -743,7 +743,7 @@ public class TunnelDispatcher implements Service { private static final int LEAVE_BATCH_TIME = 10*1000; public void add(HopConfig cfg) { - Long dropTime = new Long(cfg.getExpiration() + 2*Router.CLOCK_FUDGE_FACTOR + LEAVE_BATCH_TIME); + Long dropTime = Long.valueOf(cfg.getExpiration() + 2*Router.CLOCK_FUDGE_FACTOR + LEAVE_BATCH_TIME); boolean noTunnels; synchronized (LeaveTunnel.this) { noTunnels = _configs.isEmpty(); diff --git a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java index 3ec567098a91de813dde6e564b73eab7e797564b..a7673f1b6df20276498193271de64252a3b193c8 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java @@ -229,7 +229,7 @@ public class TunnelGateway { synchronized (Pending.this) { if (_messageIds == null) _messageIds = new ArrayList(); - _messageIds.add(new Long(id)); + _messageIds.add(Long.valueOf(id)); } } /** diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java index 8b08b6ec8cf5ed3d4e296e42bc609d243f7950fb..19505c0c7e4bccea170da908e285df0b27a50399 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java +++ b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java @@ -294,7 +294,9 @@ class BuildExecutor implements Runnable { if (!_repoll) { if (_log.shouldLog(Log.DEBUG)) _log.debug("No tunnel to build with (allowed=" + allowed + ", wanted=" + wanted.size() + ", pending=" + pendingRemaining + "), wait for a while"); - _currentlyBuilding.wait(1*1000+_context.random().nextInt(1*1000)); + try { + _currentlyBuilding.wait(1*1000+_context.random().nextInt(1*1000)); + } catch (InterruptedException ie) {} } } } else { @@ -369,7 +371,7 @@ class BuildExecutor implements Runnable { wanted.clear(); pools.clear(); - } catch (Exception e) { + } catch (RuntimeException e) { if (_log.shouldLog(Log.CRIT)) _log.log(Log.CRIT, "B0rked in the tunnel builder", e); } @@ -459,7 +461,7 @@ class BuildExecutor implements Runnable { for (int i = 0; i < 32; i++) _recentBuildIds.remove(0); } - _recentBuildIds.add(new Long(id)); + _recentBuildIds.add(Long.valueOf(id)); } } } @@ -483,7 +485,7 @@ class BuildExecutor implements Runnable { public boolean wasRecentlyBuilding(long replyId) { synchronized (_recentBuildIds) { - return _recentBuildIds.contains(new Long(replyId)); + return _recentBuildIds.contains(Long.valueOf(replyId)); } } diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java b/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java index 7a79f927c658e35b5fd60551b8d9111276e5ed08..0ccb53fbdb77e5d806c6a646642adb123e3283ba 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java +++ b/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java @@ -25,9 +25,9 @@ import net.i2p.util.VersionComparator; * */ class BuildRequestor { - private static final List<Integer> ORDER = new ArrayList(BuildMessageGenerator.ORDER.length); + private static final List<Integer> ORDER = new ArrayList(TunnelBuildMessage.MAX_RECORD_COUNT); static { - for (int i = 0; i < BuildMessageGenerator.ORDER.length; i++) + for (int i = 0; i < TunnelBuildMessage.MAX_RECORD_COUNT; i++) ORDER.add(Integer.valueOf(i)); } private static final int PRIORITY = 500; diff --git a/router/java/src/net/i2p/router/tunnel/pool/TestJob.java b/router/java/src/net/i2p/router/tunnel/pool/TestJob.java index 74910cbcbbc805f0da9e31b7e5f80275c9ce1f3c..7c64df95845350ef5236e1cba4a479c9eac1a9f2 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TestJob.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TestJob.java @@ -144,6 +144,7 @@ class TestJob extends JobImpl { scheduleRetest(); return; } + // can't be a singleton, the SKM modifies it Set encryptTags = new HashSet(1); encryptTags.add(encryptTag); // Register the single tag with the appropriate SKM diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java index e6f3e599e8bf4c70b16c7f282529b748cdb476c5..03fa5bd719c54db8b9b52762f36be6493dedcd17 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java @@ -464,9 +464,9 @@ public class TunnelPool { * but we use latest expiration first, since we need to sort them by that anyway. * */ - class LeaseComparator implements Comparator { - public int compare(Object l, Object r) { - return ((Lease)r).getEndDate().compareTo(((Lease)l).getEndDate()); + private static class LeaseComparator implements Comparator<Lease> { + public int compare(Lease l, Lease r) { + return r.getEndDate().compareTo(l.getEndDate()); } } diff --git a/router/java/src/net/i2p/router/util/RandomIterator.java b/router/java/src/net/i2p/router/util/RandomIterator.java new file mode 100644 index 0000000000000000000000000000000000000000..db8a560b80a3125727953a9864a52f6edfa6dde0 --- /dev/null +++ b/router/java/src/net/i2p/router/util/RandomIterator.java @@ -0,0 +1,178 @@ +package net.i2p.router.util; + +/* + * Modified from: + * http://www.lockergnome.com/awarberg/2007/04/22/random-iterator-in-java/ + * No license, free to use + */ + +//import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Random; + +import net.i2p.util.RandomSource; + +/** + * + * + * This is some Java code I wrote for a school project to save some time when iterating in + * random order over a part of list (until some condition becomes true): + * + * Here is a sample on how to use the code: + * + <pre> + for(Iterator<Object> iter = new RandomIterator<Object>(myObjList); iter.hasNext();){ + Object o = iter.next(); + if(someCondition(o) ) + return o; // iteration stopped early + } + </pre> + * + * I wrote it to replace a Collection.shuffle call and this code gave us an overall increase in program execution speed of about 25%. + * As the javadoc description says, you are better off calling Collection.shuffle if you need to iterate over the entire list. But if you may stop early this class can save you some time, as it did in our case. + * + * Provides a random iteration over the given list. + * + * This effect can be achieved by using Collections.shuffle, + * which shuffles the entire collection in linear time. + * + * If the iteration process may end before all items + * are processed, this class may give a speed increase + * because the shuffling process is performed as items are requested + * rather than in the beginning. + * + * I2P changes: + *<pre> + * - Use BitSet instead of boolean[] + * - Use I2P RandomSource + * - Done check in next(), throw NSEE + * - Ensure lower and upper bounds are always clear + * - Replace unbounded loop in next(). It is now O(N) time, but now + * the iterator will tend to "clump" results and thus is not truly random. + * *** This class is not recommended for small Lists, + * *** or for iterating through a large portion of a List. + * *** Use Collections.shuffle() instead. + * - Add test code + *</pre> + * + */ +public class RandomIterator<E> implements Iterator<E> { + /** + * Mapping indicating which items were served (by index). + * if served[i] then the item with index i in the list + * has already been served. + * + * Note it is possible to save memory here by using + * BitSet rather than a boolean array, however it will + * increase the running time slightly. + */ + private final BitSet served; + + /** The amount of items served so far */ + private int servedCount = 0; + private final List<E> list; + private final int LIST_SIZE; + + /** + * The random number generator has a great influence + * on the running time of this iterator. + * + * See, for instance, + * <a href="http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation" title="http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation" target="_blank">http://www.qbrundage.com/michaelb/pubs/e…</a> + * for some implementations, which are faster than java.util.Random. + */ + private static final Random rand = RandomSource.getInstance(); + + /** Used to narrow the range to take random indexes from */ + private int lower, upper; + + public RandomIterator(List<E> list){ + this.list = list; + LIST_SIZE = list.size(); + served = new BitSet(LIST_SIZE); + upper = LIST_SIZE - 1; + } + + public boolean hasNext() { + return servedCount < LIST_SIZE; + } + + public E next() { + if (!hasNext()) + throw new NoSuchElementException(); + int range = upper - lower + 1; + + // This has unbounded behavior, even with lower/upper + //int index; + //do { + // index = lower + rand.nextInt(range); + //} while (served.get(index)); + + // This tends to "clump" results, escpecially toward the end of the iteration. + // It also tends to leave the first and last few elements until the end. + int start = lower + rand.nextInt(range); + int index; + if ((start % 2) == 0) // coin flip + index = served.nextClearBit(start); + else + index = previousClearBit(start); + if (index < 0) + throw new NoSuchElementException("shouldn't happen"); + servedCount++; + served.set(index); + + // check if the range from which random values + // are taken can be reduced + // I2P - ensure lower and upper are always clear + if (hasNext()) { + if (index == lower) + lower = served.nextClearBit(lower); + else if (index == upper) + upper = previousClearBit(upper - 1); + } + return list.get(index); + } + + /** just like nextClearBit() */ + private int previousClearBit(int n) { + for (int i = n; i >= lower; i--) { + if (!served.get(i)) { + return i; + } + } + return -1; + } + + /** + * @throws UnsupportedOperationException always + */ + public void remove() { + throw new UnsupportedOperationException(); + } + +/***** + public static void main(String[] args) { + System.out.println("\n testing with 0"); + test(0); + System.out.println("\n testing with 1"); + test(1); + System.out.println("\n testing with 2"); + test(2); + System.out.println("\n testing with 1000"); + test(1000); + } + + public static void test(int n) { + List<Integer> l = new ArrayList(n); + for (int i = 0; i < n; i++) { + l.add(Integer.valueOf(i)); + } + for (Iterator<Integer> iter = new RandomIterator(l); iter.hasNext(); ) { + System.out.println(iter.next().toString()); + } + } +*****/ +} diff --git a/router/java/src/net/i2p/router/util/package.html b/router/java/src/net/i2p/router/util/package.html new file mode 100644 index 0000000000000000000000000000000000000000..bf81eb3fafa814d31ee55ea1466b4a707f742fef --- /dev/null +++ b/router/java/src/net/i2p/router/util/package.html @@ -0,0 +1,6 @@ +<html><body> +<p> +These classes define the several useful utilities used +throughout the router. +</p> +</body></html>