diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java index 924104d886..97397c3d38 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java @@ -37,6 +37,8 @@ public class ConfigNetHandler extends FormHandler { private boolean _requireIntroductions; private boolean _hiddenMode; private boolean _dynamicKeys; + private String _ntcpHostname; + private String _ntcpPort; private String _tcpPort; private String _udpPort; private String _inboundRate; @@ -78,6 +80,12 @@ public class ConfigNetHandler extends FormHandler { public void setTcpPort(String port) { _tcpPort = (port != null ? port.trim() : null); } + public void setNtcphost(String host) { + _ntcpHostname = (host != null ? host.trim() : null); + } + public void setNtcpport(String port) { + _ntcpPort = (port != null ? port.trim() : null); + } public void setUdpPort(String port) { _udpPort = (port != null ? port.trim() : null); } @@ -222,6 +230,30 @@ public class ConfigNetHandler extends FormHandler { restartRequired = true; } } + + if ( (_ntcpHostname != null) && (_ntcpHostname.length() > 0) && (_ntcpPort != null) && (_ntcpPort.length() > 0) ) { + String oldHost = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME); + String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_PORT); + if ( (oldHost == null) || (!oldHost.equalsIgnoreCase(_ntcpHostname)) || + (oldPort == null) || (!oldPort.equalsIgnoreCase(_ntcpPort)) ) { + _context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME, _ntcpHostname); + _context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_PORT, _ntcpPort); + addFormNotice("Updating inbound TCP settings from " + oldHost + ":" + oldPort + + " to " + _ntcpHostname + ":" + _ntcpPort); + restartRequired = true; + } + } else { + String oldHost = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME); + String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_PORT); + if ( (oldHost != null) || (oldPort != null) ) { + _context.router().removeConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME); + _context.router().removeConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_PORT); + addFormNotice("Updating inbound TCP settings from " + oldHost + ":" + oldPort + + " so that we no longer receive inbound TCP connections"); + restartRequired = true; + } + } + if ( (_udpPort != null) && (_udpPort.length() > 0) ) { String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_UDP_PORT); if ( (oldPort == null) && (_udpPort.equals("8887")) ) { 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 d47ae5caf6..5c9015dee0 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java @@ -48,6 +48,10 @@ public class ConfigNetHelper { } return "" + port; } + public final static String PROP_I2NP_NTCP_HOSTNAME = "i2np.ntcp.hostname"; + public final static String PROP_I2NP_NTCP_PORT = "i2np.ntcp.port"; + public String getNtcphostname() { return _context.getProperty(PROP_I2NP_NTCP_HOSTNAME); } + public String getNtcpport() { return _context.getProperty(PROP_I2NP_NTCP_PORT); } public String getUdpAddress() { RouterAddress addr = _context.router().getRouterInfo().getTargetAddress("SSU"); diff --git a/apps/routerconsole/jsp/config.jsp b/apps/routerconsole/jsp/config.jsp index b5d50ca745..fb39dda677 100644 --- a/apps/routerconsole/jsp/config.jsp +++ b/apps/routerconsole/jsp/config.jsp @@ -66,6 +66,20 @@ Users behind symmetric NATs, such as OpenBSD's pf, are not currently supported.


+ Inbound TCP connection configuration:
+ Externally reachable hostname or IP address: + " /> + (dyndns and the like are fine)
+ Externally reachable TCP port: + " />
+

You do not need to allow inbound TCP connections - outbound connections work with no + configuration. However, if you want to receive inbound TCP connections, you must poke a hole + in your NAT or firewall for unsolicited TCP connections. If you specify the wrong IP address or + hostname, or do not properly configure your NAT or firewall, your network performance will degrade + substantially. When in doubt, leave the hostname and port number blank.

+

Note: changing this setting will terminate all of your connections and effectively + restart your router. +


Dynamic Router Keys: />

diff --git a/core/java/src/gnu/crypto/hash/BaseHashStandalone.java b/core/java/src/gnu/crypto/hash/BaseHashStandalone.java new file mode 100644 index 0000000000..6cfab4cc0f --- /dev/null +++ b/core/java/src/gnu/crypto/hash/BaseHashStandalone.java @@ -0,0 +1,198 @@ +package gnu.crypto.hash; + +// ---------------------------------------------------------------------------- +// $Id: BaseHashStandalone.java,v 1.1 2006/02/26 16:30:59 jrandom Exp $ +// +// Copyright (C) 2001, 2002, Free Software Foundation, Inc. +// +// This file is part of GNU Crypto. +// +// GNU Crypto is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or (at your option) +// any later version. +// +// GNU Crypto is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to the +// +// Free Software Foundation Inc., +// 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301 +// USA +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give +// you permission to link this library with independent modules to +// produce an executable, regardless of the license terms of these +// independent modules, and to copy and distribute the resulting +// executable under terms of your choice, provided that you also meet, +// for each linked independent module, the terms and conditions of the +// license of that module. An independent module is a module which is +// not derived from or based on this library. If you modify this +// library, you may extend this exception to your version of the +// library, but you are not obligated to do so. If you do not wish to +// do so, delete this exception statement from your version. +// ---------------------------------------------------------------------------- + +/** + *

A base abstract class to facilitate hash implementations.

+ * + * @version $Revision: 1.1 $ + */ +public abstract class BaseHashStandalone implements IMessageDigestStandalone { + + // Constants and variables + // ------------------------------------------------------------------------- + + /** The canonical name prefix of the hash. */ + protected String name; + + /** The hash (output) size in bytes. */ + protected int hashSize; + + /** The hash (inner) block size in bytes. */ + protected int blockSize; + + /** Number of bytes processed so far. */ + protected long count; + + /** Temporary input buffer. */ + protected byte[] buffer; + + // Constructor(s) + // ------------------------------------------------------------------------- + + /** + *

Trivial constructor for use by concrete subclasses.

+ * + * @param name the canonical name prefix of this instance. + * @param hashSize the block size of the output in bytes. + * @param blockSize the block size of the internal transform. + */ + protected BaseHashStandalone(String name, int hashSize, int blockSize) { + super(); + + this.name = name; + this.hashSize = hashSize; + this.blockSize = blockSize; + this.buffer = new byte[blockSize]; + + resetContext(); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // IMessageDigestStandalone interface implementation --------------------------------- + + public String name() { + return name; + } + + public int hashSize() { + return hashSize; + } + + public int blockSize() { + return blockSize; + } + + public void update(byte b) { + // compute number of bytes still unhashed; ie. present in buffer + int i = (int)(count % blockSize); + count++; + buffer[i] = b; + if (i == (blockSize - 1)) { + transform(buffer, 0); + } + } + + public void update(byte[] b) { + update(b, 0, b.length); + } + + public void update(byte[] b, int offset, int len) { + int n = (int)(count % blockSize); + count += len; + int partLen = blockSize - n; + int i = 0; + + if (len >= partLen) { + System.arraycopy(b, offset, buffer, n, partLen); + transform(buffer, 0); + for (i = partLen; i + blockSize - 1 < len; i+= blockSize) { + transform(b, offset + i); + } + n = 0; + } + + if (i < len) { + System.arraycopy(b, offset + i, buffer, n, len - i); + } + } + + public byte[] digest() { + byte[] tail = padBuffer(); // pad remaining bytes in buffer + update(tail, 0, tail.length); // last transform of a message + byte[] result = getResult(); // make a result out of context + + reset(); // reset this instance for future re-use + + return result; + } + + public void reset() { // reset this instance for future re-use + count = 0L; + for (int i = 0; i < blockSize; ) { + buffer[i++] = 0; + } + + resetContext(); + } + + // methods to be implemented by concrete subclasses ------------------------ + + public abstract Object clone(); + + public abstract boolean selfTest(); + + /** + *

Returns the byte array to use as padding before completing a hash + * operation.

+ * + * @return the bytes to pad the remaining bytes in the buffer before + * completing a hash operation. + */ + protected abstract byte[] padBuffer(); + + /** + *

Constructs the result from the contents of the current context.

+ * + * @return the output of the completed hash operation. + */ + protected abstract byte[] getResult(); + + /** Resets the instance for future re-use. */ + protected abstract void resetContext(); + + /** + *

The block digest transformation per se.

+ * + * @param in the blockSize long block, as an array of bytes to digest. + * @param offset the index where the data to digest is located within the + * input buffer. + */ + protected abstract void transform(byte[] in, int offset); +} diff --git a/core/java/src/gnu/crypto/hash/IMessageDigestStandalone.java b/core/java/src/gnu/crypto/hash/IMessageDigestStandalone.java new file mode 100644 index 0000000000..79fbab2020 --- /dev/null +++ b/core/java/src/gnu/crypto/hash/IMessageDigestStandalone.java @@ -0,0 +1,141 @@ +package gnu.crypto.hash; + +// ---------------------------------------------------------------------------- +// $Id: IMessageDigestStandalone.java,v 1.1 2006/02/26 16:30:59 jrandom Exp $ +// +// Copyright (C) 2001, 2002, Free Software Foundation, Inc. +// +// This file is part of GNU Crypto. +// +// GNU Crypto is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or (at your option) +// any later version. +// +// GNU Crypto is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to the +// +// Free Software Foundation Inc., +// 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301 +// USA +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give +// you permission to link this library with independent modules to +// produce an executable, regardless of the license terms of these +// independent modules, and to copy and distribute the resulting +// executable under terms of your choice, provided that you also meet, +// for each linked independent module, the terms and conditions of the +// license of that module. An independent module is a module which is +// not derived from or based on this library. If you modify this +// library, you may extend this exception to your version of the +// library, but you are not obligated to do so. If you do not wish to +// do so, delete this exception statement from your version. +// ---------------------------------------------------------------------------- + +/** + *

The basic visible methods of any hash algorithm.

+ * + *

A hash (or message digest) algorithm produces its output by iterating a + * basic compression function on blocks of data.

+ * + * @version $Revision: 1.1 $ + */ +public interface IMessageDigestStandalone extends Cloneable { + + // Constants + // ------------------------------------------------------------------------- + + // Methods + // ------------------------------------------------------------------------- + + /** + *

Returns the canonical name of this algorithm.

+ * + * @return the canonical name of this instance. + */ + String name(); + + /** + *

Returns the output length in bytes of this message digest algorithm.

+ * + * @return the output length in bytes of this message digest algorithm. + */ + int hashSize(); + + /** + *

Returns the algorithm's (inner) block size in bytes.

+ * + * @return the algorithm's inner block size in bytes. + */ + int blockSize(); + + /** + *

Continues a message digest operation using the input byte.

+ * + * @param b the input byte to digest. + */ + void update(byte b); + + /** + *

Continues a message digest operation, by filling the buffer, processing + * data in the algorithm's HASH_SIZE-bit block(s), updating the context and + * count, and buffering the remaining bytes in buffer for the next + * operation.

+ * + * @param in the input block. + */ + void update(byte[] in); + + /** + *

Continues a message digest operation, by filling the buffer, processing + * data in the algorithm's HASH_SIZE-bit block(s), updating the context and + * count, and buffering the remaining bytes in buffer for the next + * operation.

+ * + * @param in the input block. + * @param offset start of meaningful bytes in input block. + * @param length number of bytes, in input block, to consider. + */ + void update(byte[] in, int offset, int length); + + /** + *

Completes the message digest by performing final operations such as + * padding and resetting the instance.

+ * + * @return the array of bytes representing the hash value. + */ + byte[] digest(); + + /** + *

Resets the current context of this instance clearing any eventually cached + * intermediary values.

+ */ + void reset(); + + /** + *

A basic test. Ensures that the digest of a pre-determined message is equal + * to a known pre-computed value.

+ * + * @return true if the implementation passes a basic self-test. + * Returns false otherwise. + */ + boolean selfTest(); + + /** + *

Returns a clone copy of this instance.

+ * + * @return a clone copy of this instance. + */ + Object clone(); +} diff --git a/core/java/src/gnu/crypto/hash/Sha256Standalone.java b/core/java/src/gnu/crypto/hash/Sha256Standalone.java index a42742e43e..85d5cb97e0 100644 --- a/core/java/src/gnu/crypto/hash/Sha256Standalone.java +++ b/core/java/src/gnu/crypto/hash/Sha256Standalone.java @@ -1,7 +1,7 @@ package gnu.crypto.hash; // ---------------------------------------------------------------------------- -// $Id: Sha256Standalone.java,v 1.1 2006/02/26 16:30:59 jrandom Exp $ +// $Id: Sha256Standalone.java,v 1.2 2006/03/16 16:45:19 jrandom Exp $ // // Copyright (C) 2003 Free Software Foundation, Inc. // @@ -59,9 +59,9 @@ package gnu.crypto.hash; * renamed from Sha256 to avoid conflicts with JVMs using gnu-crypto as their JCE * provider. * - * @version $Revision: 1.1 $ + * @version $Revision: 1.2 $ */ -public class Sha256Standalone extends BaseHash { +public class Sha256Standalone extends BaseHashStandalone { // Constants and variables // ------------------------------------------------------------------------- private static final int[] k = { @@ -143,7 +143,7 @@ public class Sha256Standalone extends BaseHash { return new Sha256Standalone(this); } - // Implementation of concrete methods in BaseHash -------------------------- + // Implementation of concrete methods in BaseHashStandalone -------------------------- private int transformResult[] = new int[8]; protected void transform(byte[] in, int offset) { diff --git a/core/java/src/gnu/crypto/prng/AsyncFortunaStandalone.java b/core/java/src/gnu/crypto/prng/AsyncFortunaStandalone.java new file mode 100644 index 0000000000..9b3031e8c9 --- /dev/null +++ b/core/java/src/gnu/crypto/prng/AsyncFortunaStandalone.java @@ -0,0 +1,172 @@ +package gnu.crypto.prng; + +import java.util.*; + +/** + * fortuna instance that tries to avoid blocking if at all possible by using separate + * filled buffer segments rather than one buffer (and blocking when that buffer's data + * has been eaten) + */ +public class AsyncFortunaStandalone extends FortunaStandalone implements Runnable { + private static final int BUFFERS = 16; + private static final int BUFSIZE = 256*1024; + private final byte asyncBuffers[][] = new byte[BUFFERS][BUFSIZE]; + private final int status[] = new int[BUFFERS]; + private int nextBuf = 0; + + private static final int STATUS_NEED_FILL = 0; + private static final int STATUS_FILLING = 1; + private static final int STATUS_FILLED = 2; + private static final int STATUS_LIVE = 3; + + public AsyncFortunaStandalone() { + super(); + for (int i = 0; i < BUFFERS; i++) + status[i] = STATUS_NEED_FILL; + } + + public void startup() { + Thread refillThread = new Thread(this, "PRNG"); + refillThread.setDaemon(true); + refillThread.setPriority(Thread.MIN_PRIORITY+1); + refillThread.start(); + } + + /** the seed is only propogated once the prng is started with startup() */ + public void seed(byte val[]) { + Map props = new HashMap(1); + props.put(SEED, (Object)val); + init(props); + //fillBlock(); + } + + protected void allocBuffer() {} + + /** + * make the next available filled buffer current, scheduling any unfilled + * buffers for refill, and blocking until at least one buffer is ready + */ + protected void rotateBuffer() { + synchronized (asyncBuffers) { + // wait until we get some filled + long before = System.currentTimeMillis(); + long waited = 0; + while (status[nextBuf] != STATUS_FILLED) { + System.out.println(Thread.currentThread().getName() + ": Next PRNG buffer " + + nextBuf + " isn't ready (" + status[nextBuf] + ")"); + //new Exception("source").printStackTrace(); + asyncBuffers.notifyAll(); + try { + asyncBuffers.wait(); + } catch (InterruptedException ie) {} + waited = System.currentTimeMillis()-before; + } + if (waited > 0) + System.out.println(Thread.currentThread().getName() + ": Took " + waited + + "ms for a full PRNG buffer to be found"); + //System.out.println(Thread.currentThread().getName() + ": Switching to prng buffer " + nextBuf); + buffer = asyncBuffers[nextBuf]; + status[nextBuf] = STATUS_LIVE; + int prev=nextBuf-1; + if (prev<0) + prev = BUFFERS-1; + if (status[prev] == STATUS_LIVE) + status[prev] = STATUS_NEED_FILL; + nextBuf++; + if (nextBuf >= BUFFERS) + nextBuf = 0; + asyncBuffers.notify(); + } + } + + public void run() { + while (true) { + int toFill = -1; + try { + synchronized (asyncBuffers) { + for (int i = 0; i < BUFFERS; i++) { + if (status[i] == STATUS_NEED_FILL) { + status[i] = STATUS_FILLING; + toFill = i; + break; + } + } + if (toFill == -1) { + //System.out.println(Thread.currentThread().getName() + ": All pending buffers full"); + asyncBuffers.wait(); + } + } + } catch (InterruptedException ie) {} + + if (toFill != -1) { + //System.out.println(Thread.currentThread().getName() + ": Filling prng buffer " + toFill); + long before = System.currentTimeMillis(); + doFill(asyncBuffers[toFill]); + long after = System.currentTimeMillis(); + synchronized (asyncBuffers) { + status[toFill] = STATUS_FILLED; + //System.out.println(Thread.currentThread().getName() + ": Prng buffer " + toFill + " filled after " + (after-before)); + asyncBuffers.notifyAll(); + } + Thread.yield(); + try { Thread.sleep((after-before)*5); } catch (InterruptedException ie) {} + } + } + } + + public void fillBlock() + { + rotateBuffer(); + } + + private void doFill(byte buf[]) { + long start = System.currentTimeMillis(); + if (pool0Count >= MIN_POOL_SIZE + && System.currentTimeMillis() - lastReseed > 100) + { + reseedCount++; + //byte[] seed = new byte[0]; + for (int i = 0; i < NUM_POOLS; i++) + { + if (reseedCount % (1 << i) == 0) { + generator.addRandomBytes(pools[i].digest()); + } + } + lastReseed = System.currentTimeMillis(); + } + generator.nextBytes(buf); + long now = System.currentTimeMillis(); + long diff = now-lastRefill; + lastRefill = now; + long refillTime = now-start; + //System.out.println("Refilling " + (++refillCount) + " after " + diff + " for the PRNG took " + refillTime); + } + + public static void main(String args[]) { + try { + AsyncFortunaStandalone rand = new AsyncFortunaStandalone(); + + byte seed[] = new byte[1024]; + rand.seed(seed); + System.out.println("Before starting prng"); + rand.startup(); + System.out.println("Starting prng, waiting 1 minute"); + try { Thread.sleep(60*1000); } catch (InterruptedException ie) {} + System.out.println("PRNG started, beginning test"); + + long before = System.currentTimeMillis(); + byte buf[] = new byte[1024]; + java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); + java.util.zip.GZIPOutputStream gos = new java.util.zip.GZIPOutputStream(baos); + for (int i = 0; i < 1024; i++) { + rand.nextBytes(buf); + gos.write(buf); + } + long after = System.currentTimeMillis(); + gos.finish(); + byte compressed[] = baos.toByteArray(); + System.out.println("Compressed size of 1MB: " + compressed.length + " took " + (after-before)); + } catch (Exception e) { e.printStackTrace(); } + try { Thread.sleep(5*60*1000); } catch (InterruptedException ie) {} + } +} diff --git a/core/java/src/gnu/crypto/prng/BasePRNGStandalone.java b/core/java/src/gnu/crypto/prng/BasePRNGStandalone.java index c52f5cae71..f306c45780 100644 --- a/core/java/src/gnu/crypto/prng/BasePRNGStandalone.java +++ b/core/java/src/gnu/crypto/prng/BasePRNGStandalone.java @@ -49,7 +49,7 @@ import java.util.Map; * Modified slightly by jrandom for I2P (removing unneeded exceptions) * @version $Revision: 1.1 $ */ -public abstract class BasePRNGStandalone implements IRandom { +public abstract class BasePRNGStandalone implements IRandomStandalone { // Constants and variables // ------------------------------------------------------------------------- @@ -88,7 +88,7 @@ public abstract class BasePRNGStandalone implements IRandom { // Instance methods // ------------------------------------------------------------------------- - // IRandom interface implementation ---------------------------------------- + // IRandomStandalone interface implementation ---------------------------------------- public String name() { return name; diff --git a/core/java/src/gnu/crypto/prng/FortunaStandalone.java b/core/java/src/gnu/crypto/prng/FortunaStandalone.java index d6e2675865..764c042b43 100644 --- a/core/java/src/gnu/crypto/prng/FortunaStandalone.java +++ b/core/java/src/gnu/crypto/prng/FortunaStandalone.java @@ -97,20 +97,22 @@ import net.i2p.crypto.CryptixAESKeyCache; * gnu-crypto implementation, which has been imported into GNU/classpath * */ -public class FortunaStandalone extends BasePRNGStandalone implements Serializable, RandomEventListener +public class FortunaStandalone extends BasePRNGStandalone implements Serializable, RandomEventListenerStandalone { private static final long serialVersionUID = 0xFACADE; private static final int SEED_FILE_SIZE = 64; - private static final int NUM_POOLS = 32; - private static final int MIN_POOL_SIZE = 64; - private final Generator generator; - private final Sha256Standalone[] pools; - private long lastReseed; - private int pool; - private int pool0Count; - private int reseedCount; + static final int NUM_POOLS = 32; + static final int MIN_POOL_SIZE = 64; + final Generator generator; + final Sha256Standalone[] pools; + long lastReseed; + int pool; + int pool0Count; + int reseedCount; + static long refillCount = 0; + static long lastRefill = System.currentTimeMillis(); public static final String SEED = "gnu.crypto.prng.fortuna.seed"; @@ -124,6 +126,9 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl lastReseed = 0; pool = 0; pool0Count = 0; + allocBuffer(); + } + protected void allocBuffer() { buffer = new byte[4*1024*1024]; //256]; // larger buffer to reduce churn } @@ -145,6 +150,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl public void fillBlock() { + long start = System.currentTimeMillis(); if (pool0Count >= MIN_POOL_SIZE && System.currentTimeMillis() - lastReseed > 100) { @@ -159,6 +165,11 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl lastReseed = System.currentTimeMillis(); } generator.nextBytes(buffer); + long now = System.currentTimeMillis(); + long diff = now-lastRefill; + lastRefill = now; + long refillTime = now-start; + System.out.println("Refilling " + (++refillCount) + " after " + diff + " for the PRNG took " + refillTime); } public void addRandomByte(byte b) @@ -177,7 +188,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl pool = (pool + 1) % NUM_POOLS; } - public void addRandomEvent(RandomEvent event) + public void addRandomEvent(RandomEventStandalone event) { if (event.getPoolNumber() < 0 || event.getPoolNumber() >= pools.length) throw new IllegalArgumentException("pool number out of range: " @@ -338,6 +349,34 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl } public static void main(String args[]) { + byte in[] = new byte[16]; + byte out[] = new byte[16]; + byte key[] = new byte[32]; + try { + CryptixAESKeyCache.KeyCacheEntry buf = CryptixAESKeyCache.createNew(); + Object cryptixKey = CryptixRijndael_Algorithm.makeKey(key, 16, buf); + long beforeAll = System.currentTimeMillis(); + for (int i = 0; i < 256; i++) { + //long before =System.currentTimeMillis(); + for (int j = 0; j < 1024; j++) + CryptixRijndael_Algorithm.blockEncrypt(in, out, 0, 0, cryptixKey); + //long after = System.currentTimeMillis(); + //System.out.println("encrypting 16KB took " + (after-before)); + } + long after = System.currentTimeMillis(); + System.out.println("encrypting 4MB took " + (after-beforeAll)); + } catch (Exception e) { e.printStackTrace(); } + + try { + CryptixAESKeyCache.KeyCacheEntry buf = CryptixAESKeyCache.createNew(); + Object cryptixKey = CryptixRijndael_Algorithm.makeKey(key, 16, buf); + byte data[] = new byte[4*1024*1024]; + long beforeAll = System.currentTimeMillis(); + CryptixRijndael_Algorithm.ecbBulkEncrypt(data, data, cryptixKey); + long after = System.currentTimeMillis(); + System.out.println("encrypting 4MB took " + (after-beforeAll)); + } catch (Exception e) { e.printStackTrace(); } + /* FortunaStandalone f = new FortunaStandalone(); java.util.HashMap props = new java.util.HashMap(); byte initSeed[] = new byte[1234]; @@ -351,5 +390,6 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl } long time = System.currentTimeMillis() - before; System.out.println("512MB took " + time + ", or " + (8*64d)/((double)time/1000d) +"MBps"); + */ } } diff --git a/core/java/src/gnu/crypto/prng/IRandom.java b/core/java/src/gnu/crypto/prng/IRandomStandalone.java similarity index 81% rename from core/java/src/gnu/crypto/prng/IRandom.java rename to core/java/src/gnu/crypto/prng/IRandomStandalone.java index 9dc2109cdb..a7f343378d 100644 --- a/core/java/src/gnu/crypto/prng/IRandom.java +++ b/core/java/src/gnu/crypto/prng/IRandomStandalone.java @@ -1,7 +1,7 @@ package gnu.crypto.prng; // ---------------------------------------------------------------------------- -// $Id: IRandom.java,v 1.12 2005/10/06 04:24:17 rsdio Exp $ +// $Id: IRandomStandalone.java,v 1.1 2005/10/22 13:10:00 jrandom Exp $ // // Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. // @@ -82,9 +82,9 @@ import java.util.Map; * Menezes, A., van Oorschot, P. and S. Vanstone. * * - * @version $Revision: 1.12 $ + * @version $Revision: 1.1 $ */ -public interface IRandom extends Cloneable { +public interface IRandomStandalone extends Cloneable { // Constants // ------------------------------------------------------------------------- @@ -110,32 +110,32 @@ public interface IRandom extends Cloneable { void init(Map attributes); /** - *

Returns the next 8 bits of random data generated from this instance.

- * - * @return the next 8 bits of random data generated from this instance. - * @exception IllegalStateException if the instance is not yet initialised. - * @exception LimitReachedException if this instance has reached its - * theoretical limit for generating non-repetitive pseudo-random data. - */ - byte nextByte() throws IllegalStateException, LimitReachedException; + *

Returns the next 8 bits of random data generated from this instance.

+ * + * @return the next 8 bits of random data generated from this instance. + * @exception IllegalStateException if the instance is not yet initialised. + * @exception LimLimitReachedExceptionStandalone this instance has reached its + * theoretical limit for generating non-repetitive pseudo-random data. + */ + byte nextByte() throws IllegalStateException, LimitReachedExceptionStandalone; /** - *

Fills the designated byte array, starting from byte at index - * offset, for a maximum of length bytes with the - * output of this generator instance. - * - * @param out the placeholder to contain the generated random bytes. - * @param offset the starting index in out to consider. This method - * does nothing if this parameter is not within 0 and - * out.length. - * @param length the maximum number of required random bytes. This method - * does nothing if this parameter is less than 1. - * @exception IllegalStateException if the instance is not yet initialised. - * @exception LimitReachedException if this instance has reached its - * theoretical limit for generating non-repetitive pseudo-random data. - */ + *

Fills the designated byte array, starting from byte at index + * offset, for a maximum of length bytes with the + * output of this generator instance. + * + * @param out the placeholder to contain the generated random bytes. + * @param offset the starting index in out to consider. This method + * does nothing if this parameter is not within 0 and + * out.length. + * @param length the maximum number of required random bytes. This method + * does nothing if this parameter is less than 1. + * @exception IllegalStateException if the instance is not yet initialised. + * @exception LimitLimitReachedExceptionStandalonehis instance has reached its + * theoretical limit for generating non-repetitive pseudo-random data. + */ void nextBytes(byte[] out, int offset, int length) - throws IllegalStateException, LimitReachedException; + throws IllegalStateException, LimitReachedExceptionStandalone; /** *

Supplement, or possibly replace, the random state of this PRNG with diff --git a/core/java/src/gnu/crypto/prng/LimitReachedException.java b/core/java/src/gnu/crypto/prng/LimitReachedExceptionStandalone.java similarity index 90% rename from core/java/src/gnu/crypto/prng/LimitReachedException.java rename to core/java/src/gnu/crypto/prng/LimitReachedExceptionStandalone.java index 4e5f26f3f0..509a2d9713 100644 --- a/core/java/src/gnu/crypto/prng/LimitReachedException.java +++ b/core/java/src/gnu/crypto/prng/LimitReachedExceptionStandalone.java @@ -1,7 +1,7 @@ package gnu.crypto.prng; // ---------------------------------------------------------------------------- -// $Id: LimitReachedException.java,v 1.5 2005/10/06 04:24:17 rsdio Exp $ +// $Id: LimitReachedExceptionStandalone.java,v 1.1 2005/10/22 13:10:00 jrandom Exp $ // // Copyright (C) 2001, 2002, Free Software Foundation, Inc. // @@ -47,9 +47,9 @@ package gnu.crypto.prng; * A checked exception that indicates that a pseudo random number generated has * reached its theoretical limit in generating random bytes. * - * @version $Revision: 1.5 $ + * @version $Revision: 1.1 $ */ -public class LimitReachedException extends Exception { +public class LimitReachedExceptionStandalone extends Exception { // Constants and variables // ------------------------------------------------------------------------- @@ -57,11 +57,11 @@ public class LimitReachedException extends Exception { // Constructor(s) // ------------------------------------------------------------------------- - public LimitReachedException() { + public LimitReachedExceptionStandalone() { super(); } - public LimitReachedException(String msg) { + public LimitReachedExceptionStandalone(String msg) { super(msg); } diff --git a/core/java/src/gnu/crypto/prng/RandomEventListener.java b/core/java/src/gnu/crypto/prng/RandomEventListenerStandalone.java similarity index 91% rename from core/java/src/gnu/crypto/prng/RandomEventListener.java rename to core/java/src/gnu/crypto/prng/RandomEventListenerStandalone.java index 0c4542217f..dee897bfd0 100644 --- a/core/java/src/gnu/crypto/prng/RandomEventListener.java +++ b/core/java/src/gnu/crypto/prng/RandomEventListenerStandalone.java @@ -1,4 +1,4 @@ -/* RandomEventListener.java -- event listener +/* RandomEventListenerStandalone.java -- event listener Copyright (C) 2004 Free Software Foundation, Inc. This file is part of GNU Crypto. @@ -47,7 +47,7 @@ import java.util.EventListener; * An interface for entropy accumulators that will be notified of random * events. */ -public interface RandomEventListener extends EventListener +public interface RandomEventListenerStandalone extends EventListener { - void addRandomEvent(RandomEvent event); + void addRandomEvent(RandomEventStandalone event); } diff --git a/core/java/src/gnu/crypto/prng/RandomEvent.java b/core/java/src/gnu/crypto/prng/RandomEventStandalone.java similarity index 92% rename from core/java/src/gnu/crypto/prng/RandomEvent.java rename to core/java/src/gnu/crypto/prng/RandomEventStandalone.java index f9a678f951..666439df2a 100644 --- a/core/java/src/gnu/crypto/prng/RandomEvent.java +++ b/core/java/src/gnu/crypto/prng/RandomEventStandalone.java @@ -1,4 +1,4 @@ -/* RandomEvent.java -- a random event. +/* RandomEventStandalone.java -- a random event. Copyright (C) 2004 Free Software Foundation, Inc. This file is part of GNU Crypto. @@ -47,14 +47,14 @@ import java.util.EventObject; * An interface for entropy accumulators that will be notified of random * events. */ -public class RandomEvent extends EventObject +public class RandomEventStandalone extends EventObject { private final byte sourceNumber; private final byte poolNumber; private final byte[] data; - public RandomEvent(Object source, byte sourceNumber, byte poolNumber, + public RandomEventStandalone(Object source, byte sourceNumber, byte poolNumber, byte[] data) { super(source); diff --git a/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java b/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java index a1754a3ad2..949d3a70b2 100644 --- a/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java +++ b/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java @@ -320,9 +320,9 @@ public class DHSessionKeyBuilder { if (_myPrivateValue == null) generateMyValue(); _sessionKey = calculateSessionKey(_myPrivateValue, _peerValue); } else { - System.err.println("Not ready yet.. privateValue and peerValue must be set (" - + (_myPrivateValue != null ? "set" : "null") + "," - + (_peerValue != null ? "set" : "null") + ")"); + //System.err.println("Not ready yet.. privateValue and peerValue must be set (" + // + (_myPrivateValue != null ? "set" : "null") + "," + // + (_peerValue != null ? "set" : "null") + ")"); } return _sessionKey; } diff --git a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java index f7a2579ce9..fbe0448b46 100644 --- a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java +++ b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java @@ -67,7 +67,7 @@ class TransientSessionKeyManager extends SessionKeyManager { _log = context.logManager().getLog(TransientSessionKeyManager.class); _context = context; _outboundSessions = new HashMap(1024); - _inboundTagSets = new HashMap(64*1024); + _inboundTagSets = new HashMap(1024); context.statManager().createRateStat("crypto.sessionTagsExpired", "How many tags/sessions are expired?", "Encryption", new long[] { 10*60*1000, 60*60*1000, 3*60*60*1000 }); context.statManager().createRateStat("crypto.sessionTagsRemaining", "How many tags/sessions are remaining after a cleanup?", "Encryption", new long[] { 10*60*1000, 60*60*1000, 3*60*60*1000 }); SimpleTimer.getInstance().addEvent(new CleanupEvent(), 60*1000); diff --git a/core/java/src/net/i2p/data/DataHelper.java b/core/java/src/net/i2p/data/DataHelper.java index b66749c0d9..ea84546112 100644 --- a/core/java/src/net/i2p/data/DataHelper.java +++ b/core/java/src/net/i2p/data/DataHelper.java @@ -822,6 +822,8 @@ public class DataHelper { return (ms / (60 * 1000)) + "m"; } else if (ms < 3 * 24 * 60 * 60 * 1000) { return (ms / (60 * 60 * 1000)) + "h"; + } else if (ms > 365 * 24 * 60 * 60 * 1000) { + return "n/a"; } else { return (ms / (24 * 60 * 60 * 1000)) + "d"; } diff --git a/core/java/src/net/i2p/util/FortunaRandomSource.java b/core/java/src/net/i2p/util/FortunaRandomSource.java index 74eb4938cc..2d1a691965 100644 --- a/core/java/src/net/i2p/util/FortunaRandomSource.java +++ b/core/java/src/net/i2p/util/FortunaRandomSource.java @@ -14,7 +14,7 @@ import java.security.SecureRandom; import net.i2p.I2PAppContext; import net.i2p.crypto.EntropyHarvester; -import gnu.crypto.prng.FortunaStandalone; +import gnu.crypto.prng.AsyncFortunaStandalone; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -26,13 +26,13 @@ import java.io.IOException; * */ public class FortunaRandomSource extends RandomSource implements EntropyHarvester { - private FortunaStandalone _fortuna; + private AsyncFortunaStandalone _fortuna; private double _nextGaussian; private boolean _haveNextGaussian; public FortunaRandomSource(I2PAppContext context) { super(context); - _fortuna = new FortunaStandalone(); + _fortuna = new AsyncFortunaStandalone(); byte seed[] = new byte[1024]; if (initSeed(seed)) { _fortuna.seed(seed); @@ -41,6 +41,7 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste sr.nextBytes(seed); _fortuna.seed(seed); } + _fortuna.startup(); // kickstart it _fortuna.nextBytes(seed); _haveNextGaussian = false; @@ -202,6 +203,13 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste public static void main(String args[]) { try { RandomSource rand = I2PAppContext.getGlobalContext().random(); + if (true) { + for (int i = 0; i < 1000; i++) + if (rand.nextFloat() < 0) + throw new RuntimeException("negative!"); + System.out.println("All positive"); + return; + } java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); java.util.zip.GZIPOutputStream gos = new java.util.zip.GZIPOutputStream(baos); for (int i = 0; i < 1024*1024; i++) { diff --git a/core/java/src/net/i2p/util/I2PThread.java b/core/java/src/net/i2p/util/I2PThread.java index b4bd8d740c..f33d6f08d5 100644 --- a/core/java/src/net/i2p/util/I2PThread.java +++ b/core/java/src/net/i2p/util/I2PThread.java @@ -48,6 +48,12 @@ public class I2PThread extends Thread { if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) ) _createdBy = new Exception("Created by"); } + public I2PThread(Runnable r, String name, boolean isDaemon) { + super(r, name); + setDaemon(isDaemon); + if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) ) + _createdBy = new Exception("Created by"); + } private void log(int level, String msg) { log(level, msg, null); } private void log(int level, String msg, Throwable t) { @@ -113,4 +119,4 @@ public class I2PThread extends Thread { } catch (Throwable tt) { // nop } } -} \ No newline at end of file +} diff --git a/core/java/src/net/i2p/util/SimpleTimer.java b/core/java/src/net/i2p/util/SimpleTimer.java index 9cde50032f..c86b1379b9 100644 --- a/core/java/src/net/i2p/util/SimpleTimer.java +++ b/core/java/src/net/i2p/util/SimpleTimer.java @@ -31,14 +31,14 @@ public class SimpleTimer { _context = I2PAppContext.getGlobalContext(); _log = _context.logManager().getLog(SimpleTimer.class); _events = new TreeMap(); - _eventTimes = new HashMap(1024); + _eventTimes = new HashMap(256); _readyEvents = new ArrayList(4); I2PThread runner = new I2PThread(new SimpleTimerRunner()); runner.setName(name); runner.setDaemon(true); runner.start(); for (int i = 0; i < 3; i++) { - I2PThread executor = new I2PThread(new Executor()); + I2PThread executor = new I2PThread(new Executor(_context, _log, _readyEvents)); executor.setName(name + "Executor " + i); executor.setDaemon(true); executor.start(); @@ -114,7 +114,7 @@ public class SimpleTimer { long timeToAdd = System.currentTimeMillis() - now; if (timeToAdd > 50) { if (_log.shouldLog(Log.WARN)) - _log.warn("timer contention: took " + timeToAdd + "ms to add a job"); + _log.warn("timer contention: took " + timeToAdd + "ms to add a job with " + totalEvents + " queued"); } } @@ -141,14 +141,6 @@ public class SimpleTimer { public void timeReached(); } - private void log(String msg, Throwable t) { - synchronized (this) { - if (_log == null) - _log = I2PAppContext.getGlobalContext().logManager().getLog(SimpleTimer.class); - } - _log.log(Log.CRIT, msg, t); - } - private long _occurredTime; private long _occurredEventCount; private TimedEvent _recentEvents[] = new TimedEvent[5]; @@ -228,30 +220,45 @@ public class SimpleTimer { } } } - - private class Executor implements Runnable { - public void run() { - while (true) { - TimedEvent evt = null; - synchronized (_readyEvents) { - if (_readyEvents.size() <= 0) - try { _readyEvents.wait(); } catch (InterruptedException ie) {} - if (_readyEvents.size() > 0) - evt = (TimedEvent)_readyEvents.remove(0); - } - - if (evt != null) { - long before = _context.clock().now(); - try { - evt.timeReached(); - } catch (Throwable t) { - log("wtf, event borked: " + evt, t); - } - long time = _context.clock().now() - before; - if ( (time > 1000) && (_log != null) && (_log.shouldLog(Log.WARN)) ) - _log.warn("wtf, event execution took " + time + ": " + evt); +} + +class Executor implements Runnable { + private I2PAppContext _context; + private Log _log; + private List _readyEvents; + public Executor(I2PAppContext ctx, Log log, List events) { + _context = ctx; + _readyEvents = events; + } + public void run() { + while (true) { + SimpleTimer.TimedEvent evt = null; + synchronized (_readyEvents) { + if (_readyEvents.size() <= 0) + try { _readyEvents.wait(); } catch (InterruptedException ie) {} + if (_readyEvents.size() > 0) + evt = (SimpleTimer.TimedEvent)_readyEvents.remove(0); + } + + if (evt != null) { + long before = _context.clock().now(); + try { + evt.timeReached(); + } catch (Throwable t) { + log("wtf, event borked: " + evt, t); } + long time = _context.clock().now() - before; + if ( (time > 1000) && (_log != null) && (_log.shouldLog(Log.WARN)) ) + _log.warn("wtf, event execution took " + time + ": " + evt); } } } + + private void log(String msg, Throwable t) { + synchronized (this) { + if (_log == null) + _log = I2PAppContext.getGlobalContext().logManager().getLog(SimpleTimer.class); + } + _log.log(Log.CRIT, msg, t); + } } diff --git a/history.txt b/history.txt index 7c77dfe93a..bfc35f21ff 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,20 @@ -$Id: history.txt,v 1.490 2006-06-14 00:24:35 cervantes Exp $ +$Id: history.txt,v 1.491 2006-07-01 17:44:34 complication Exp $ + +2006-07-04 jrandom + * New NIO-based tcp transport (NTCP), enabled by default for outbound + connections only. Those who configure their NAT/firewall to allow + inbound connections and specify the external host and port + (dyndns/etc is ok) on /config.jsp can receive inbound connections. + SSU is still enabled for use by default for all users as a fallback. + * Substantial bugfix to the tunnel gateway processing to transfer + messages sequentially instead of interleaved + * Renamed GNU/crypto classes to avoid name clashes with kaffe and other + GNU/Classpath based JVMs + * Adjust the Fortuna PRNG's pooling system to reduce contention on + refill with a background thread to refill the output buffer + * Add per-transport support for the shitlist + * Add a new async pumped tunnel gateway to reduce tunnel dispatcher + contention 2006-07-01 Complication * Ensure that the I2PTunnel web interface won't update tunnel settings diff --git a/router/java/src/net/i2p/router/OutNetMessage.java b/router/java/src/net/i2p/router/OutNetMessage.java index c19ce5f181..2240af423c 100644 --- a/router/java/src/net/i2p/router/OutNetMessage.java +++ b/router/java/src/net/i2p/router/OutNetMessage.java @@ -48,6 +48,7 @@ public class OutNetMessage { private MessageSelector _replySelector; private Set _failedTransports; private long _sendBegin; + private long _transmitBegin; private Exception _createdBy; private long _created; /** for debugging, contains a mapping of even name to Long (e.g. "begin sending", "handleOutbound", etc) */ @@ -57,6 +58,10 @@ public class OutNetMessage { * (some JVMs have less than 10ms resolution, so the Long above doesn't guarantee order) */ private List _timestampOrder; + private int _queueSize; + private long _prepareBegin; + private long _prepareEnd; + private Object _preparationBuf; public OutNetMessage(RouterContext context) { _context = context; @@ -148,6 +153,7 @@ public class OutNetMessage { _messageType = msg.getClass().getName(); _messageTypeId = msg.getType(); _messageId = msg.getUniqueId(); + _messageSize = _message.getMessageSize(); } } @@ -235,9 +241,31 @@ public class OutNetMessage { /** when did the sending process begin */ public long getSendBegin() { return _sendBegin; } public void beginSend() { _sendBegin = _context.clock().now(); } + public void beginTransmission() { _transmitBegin = _context.clock().now(); } + public void beginPrepare() { _prepareBegin = _context.clock().now(); } + public void prepared() { prepared(null); } + public void prepared(Object buf) { + _prepareEnd = _context.clock().now(); + _preparationBuf = buf; + } + public Object releasePreparationBuffer() { + Object rv = _preparationBuf; + _preparationBuf = null; + return rv; + } public long getCreated() { return _created; } + /** time since the message was created */ public long getLifetime() { return _context.clock().now() - _created; } + /** time the transport tries to send the message (including any queueing) */ + public long getSendTime() { return _context.clock().now() - _sendBegin; } + /** time during which the i2np message is actually in flight */ + public long getTransmissionTime() { return _context.clock().now() - _transmitBegin; } + /** how long it took to prepare the i2np message for transmission (including serialization and transport layer encryption) */ + public long getPreparationTime() { return _prepareEnd - _prepareBegin; } + /** number of messages ahead of this one going to the targetted peer when it is first enqueued */ + public int getQueueSize() { return _queueSize; } + public void setQueueSize(int size) { _queueSize = size; } /** * We've done what we need to do with the data from this message, though @@ -245,6 +273,8 @@ public class OutNetMessage { */ public void discardData() { long timeToDiscard = _context.clock().now() - _created; + if ( (_message != null) && (_messageSize <= 0) ) + _messageSize = _message.getMessageSize(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Discard " + _messageSize + "byte " + _messageType + " message after " + timeToDiscard); diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index d208f3b7a3..e75936cc3b 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -15,9 +15,9 @@ import net.i2p.CoreVersion; * */ public class RouterVersion { - public final static String ID = "$Revision: 1.429 $ $Date: 2006-06-13 21:17:48 $"; + public final static String ID = "$Revision: 1.430 $ $Date: 2006-07-01 17:44:36 $"; public final static String VERSION = "0.6.1.21"; - public final static long BUILD = 1; + public final static long BUILD = 2; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); diff --git a/router/java/src/net/i2p/router/Shitlist.java b/router/java/src/net/i2p/router/Shitlist.java index e46b545a89..ea3adf761a 100644 --- a/router/java/src/net/i2p/router/Shitlist.java +++ b/router/java/src/net/i2p/router/Shitlist.java @@ -10,10 +10,8 @@ package net.i2p.router; import java.io.IOException; import java.io.Writer; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; +import java.util.*; +import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.router.peermanager.PeerProfile; @@ -28,29 +26,36 @@ import net.i2p.util.Log; public class Shitlist { private Log _log; private RouterContext _context; - private Map _shitlist; // H(routerIdent) --> Date - private Map _shitlistCause; // H(routerIdent) --> String + private Map _entries; + + private class Entry { + /** when it should expire, per the i2p clock */ + long expireOn; + /** why they were shitlisted */ + String cause; + /** what transports they were shitlisted for (String), or null for all transports */ + Set transports; + } public final static long SHITLIST_DURATION_MS = 4*60*1000; // 4 minute shitlist public Shitlist(RouterContext context) { _context = context; _log = context.logManager().getLog(Shitlist.class); - _shitlist = new HashMap(5); - _shitlistCause = new HashMap(5); + _entries = new HashMap(32); } public int getRouterCount() { - purge(); - synchronized (_shitlist) { - return _shitlist.size(); + synchronized (_entries) { + return _entries.size(); } } public boolean shitlistRouter(Hash peer) { return shitlistRouter(peer, null); } - public boolean shitlistRouter(Hash peer, String reason) { + public boolean shitlistRouter(Hash peer, String reason) { return shitlistRouter(peer, reason, null); } + public boolean shitlistRouter(Hash peer, String reason, String transport) { if (peer == null) { _log.error("wtf, why did we try to shitlist null?", new Exception("shitfaced")); return false; @@ -73,14 +78,26 @@ public class Shitlist { if (period > 60*60*1000) period = 60*60*1000; - synchronized (_shitlist) { - Date oldDate = (Date)_shitlist.put(peer, new Date(_context.clock().now() + period)); - wasAlready = (null == oldDate); - if (reason != null) { - if (!wasAlready || (!_shitlistCause.containsKey(peer)) ) - _shitlistCause.put(peer, reason); - } else { - _shitlistCause.remove(peer); + Entry e = new Entry(); + e.expireOn = _context.clock().now() + period; + e.cause = reason; + e.transports = null; + if (transport != null) { + e.transports = new HashSet(1); + e.transports.add(transport); + } + + synchronized (_entries) { + Entry old = (Entry)_entries.put(peer, e); + if (old != null) { + wasAlready = true; + _entries.put(peer, old); + if (e.transports == null) { + old.transports = null; + } else if (old.transports != null) { + old.transports.addAll(e.transports); + } + e = old; } } @@ -95,87 +112,89 @@ public class Shitlist { public void unshitlistRouter(Hash peer) { unshitlistRouter(peer, true); } - private void unshitlistRouter(Hash peer, boolean realUnshitlist) { + private void unshitlistRouter(Hash peer, boolean realUnshitlist) { unshitlistRouter(peer, realUnshitlist, null); } + public void unshitlistRouter(Hash peer, String transport) { unshitlistRouter(peer, true, transport); } + private void unshitlistRouter(Hash peer, boolean realUnshitlist, String transport) { if (peer == null) return; if (_log.shouldLog(Log.INFO)) - _log.info("Unshitlisting router " + peer.toBase64()); - synchronized (_shitlist) { - _shitlist.remove(peer); - _shitlistCause.remove(peer); + _log.info("Unshitlisting router " + peer.toBase64() + + (transport != null ? "/" + transport : "")); + boolean fully = false; + synchronized (_entries) { + Entry e = (Entry)_entries.remove(peer); + if ( (e == null) || (e.transports == null) || (transport == null) || (e.transports.size() <= 1) ) { + // fully unshitlisted + fully = true; + } else { + e.transports.remove(transport); + _entries.put(peer, e); + } } - if (realUnshitlist) { + if (fully) { + if (realUnshitlist) { + PeerProfile prof = _context.profileOrganizer().getProfile(peer); + if (prof != null) + prof.unshitlist(); + } + _context.messageHistory().unshitlist(peer); + } + } + + public boolean isShitlisted(Hash peer) { return isShitlisted(peer, null); } + public boolean isShitlisted(Hash peer, String transport) { + boolean rv = false; + boolean unshitlist = false; + synchronized (_entries) { + Entry entry = (Entry)_entries.get(peer); + if (entry == null) { + rv = false; + } else { + if (entry.expireOn <= _context.clock().now()) { + _entries.remove(peer); + unshitlist = true; + rv = false; + } else { + if (entry.transports == null) { + rv = true; + } else if (entry.transports.contains(transport)) { + rv = true; + } else { + rv = false; + } + } + } + } + + if (unshitlist) { PeerProfile prof = _context.profileOrganizer().getProfile(peer); if (prof != null) prof.unshitlist(); - } - _context.messageHistory().unshitlist(peer); - } - - public boolean isShitlisted(Hash peer) { - Date shitlistDate = null; - synchronized (_shitlist) { - shitlistDate = (Date)_shitlist.get(peer); - } - if (shitlistDate == null) return false; - - // check validity - if (shitlistDate.getTime() > _context.clock().now()) { - return true; - } else { - unshitlistRouter(peer, false); - return false; - } - } - - /** - * We already unshitlist on isShitlisted, but this purge - * lets us get the correct value when rendering the HTML or - * getting the shitlist count. wheee - * - */ - private void purge() { - Map shitlist = null; - synchronized (_shitlist) { - shitlist = new HashMap(_shitlist); + _context.messageHistory().unshitlist(peer); } - long limit = _context.clock().now(); - - for (Iterator iter = shitlist.keySet().iterator(); iter.hasNext(); ) { - Hash key = (Hash)iter.next(); - Date shitDate = (Date)shitlist.get(key); - if (shitDate.getTime() < limit) { - unshitlistRouter(key, false); - } - } + return rv; } - public void renderStatusHTML(Writer out) throws IOException { StringBuffer buf = new StringBuffer(1024); buf.append("

Shitlist

"); - Map shitlist = null; - Map causes = null; + Map entries = null; - purge(); - - synchronized (_shitlist) { - shitlist = new HashMap(_shitlist); - causes = new HashMap(_shitlistCause); + synchronized (_entries) { + entries = new HashMap(_entries); } buf.append("