diff --git a/apps/i2psnark/java/src/net/i2p/kademlia/KBucketImpl.java b/apps/i2psnark/java/src/net/i2p/kademlia/KBucketImpl.java index 2653fe5d97ee4c853ac0c486439c3972fd5cdb3e..e99b51e84097a0360f67d35ef2a6f4fd8bcbc8b0 100644 --- a/apps/i2psnark/java/src/net/i2p/kademlia/KBucketImpl.java +++ b/apps/i2psnark/java/src/net/i2p/kademlia/KBucketImpl.java @@ -53,7 +53,7 @@ class KBucketImpl<T extends SimpleDataStructure> implements KBucket<T> { /** include if no bits higher than this bit (inclusive) are set */ private final int _end; private final int _max; - private final KBucketSet.KBucketTrimmer _trimmer; + private final KBucketTrimmer _trimmer; /** when did we last shake things up */ private long _lastChanged; private final I2PAppContext _context; @@ -62,7 +62,7 @@ class KBucketImpl<T extends SimpleDataStructure> implements KBucket<T> { * All entries in this bucket will have at least one bit different * from us in the range [begin, end] inclusive. */ - public KBucketImpl(I2PAppContext context, int begin, int end, int max, KBucketSet.KBucketTrimmer trimmer) { + public KBucketImpl(I2PAppContext context, int begin, int end, int max, KBucketTrimmer trimmer) { if (begin > end) throw new IllegalArgumentException(begin + " > " + end); _context = context; diff --git a/apps/i2psnark/java/src/net/i2p/kademlia/KBucketSet.java b/apps/i2psnark/java/src/net/i2p/kademlia/KBucketSet.java index b0b6d9055441dcdc7f24172178b0ff11cc076727..3b8b09db6c84919a4c6b269e09c765c5f4963be5 100644 --- a/apps/i2psnark/java/src/net/i2p/kademlia/KBucketSet.java +++ b/apps/i2psnark/java/src/net/i2p/kademlia/KBucketSet.java @@ -768,70 +768,6 @@ public class KBucketSet<T extends SimpleDataStructure> { } } - /** - * Called when a kbucket can no longer be split and is too big - */ - public interface KBucketTrimmer<K extends SimpleDataStructure> { - /** - * Called from add() just before adding the entry. - * You may call getEntries() and/or remove() from here. - * Do NOT call add(). - * To always discard a newer entry, always return false. - * - * @param kbucket the kbucket that is now too big - * @return true to actually add the entry. - */ - public boolean trim(KBucket<K> kbucket, K toAdd); - } - - /** - * Removes a random element. Not resistant to flooding. - */ - public static class RandomTrimmer<T extends SimpleDataStructure> implements KBucketTrimmer<T> { - protected final I2PAppContext _ctx; - private final int _max; - - public RandomTrimmer(I2PAppContext ctx, int max) { - _ctx = ctx; - _max = max; - } - - public boolean trim(KBucket<T> kbucket, T toAdd) { - List<T> e = new ArrayList(kbucket.getEntries()); - int sz = e.size(); - // concurrency - if (sz < _max) - return true; - T toRemove = e.get(_ctx.random().nextInt(sz)); - return kbucket.remove(toRemove); - } - } - - /** - * Removes a random element, but only if the bucket hasn't changed in 5 minutes. - */ - public static class RandomIfOldTrimmer<T extends SimpleDataStructure> extends RandomTrimmer<T> { - - public RandomIfOldTrimmer(I2PAppContext ctx, int max) { - super(ctx, max); - } - - public boolean trim(KBucket<T> kbucket, T toAdd) { - if (kbucket.getLastChanged() > _ctx.clock().now() - 5*60*1000) - return false; - return super.trim(kbucket, toAdd); - } - } - - /** - * Removes nothing and always rejects the add. Flood resistant.. - */ - public static class RejectTrimmer<T extends SimpleDataStructure> implements KBucketTrimmer<T> { - public boolean trim(KBucket<T> kbucket, T toAdd) { - return false; - } - } - @Override public String toString() { StringBuilder buf = new StringBuilder(1024); diff --git a/apps/i2psnark/java/src/net/i2p/kademlia/KBucketTrimmer.java b/apps/i2psnark/java/src/net/i2p/kademlia/KBucketTrimmer.java new file mode 100644 index 0000000000000000000000000000000000000000..b33f85ddb4c53217966e2338427c5e01666139e5 --- /dev/null +++ b/apps/i2psnark/java/src/net/i2p/kademlia/KBucketTrimmer.java @@ -0,0 +1,20 @@ +package net.i2p.kademlia; + +import net.i2p.data.SimpleDataStructure; + +/** + * Called when a kbucket can no longer be split and is too big + * @since 0.9.2 + */ +public interface KBucketTrimmer<K extends SimpleDataStructure> { + /** + * Called from add() just before adding the entry. + * You may call getEntries() and/or remove() from here. + * Do NOT call add(). + * To always discard a newer entry, always return false. + * + * @param kbucket the kbucket that is now too big + * @return true to actually add the entry. + */ + public boolean trim(KBucket<K> kbucket, K toAdd); +} diff --git a/apps/i2psnark/java/src/net/i2p/kademlia/RandomIfOldTrimmer.java b/apps/i2psnark/java/src/net/i2p/kademlia/RandomIfOldTrimmer.java new file mode 100644 index 0000000000000000000000000000000000000000..ade28ce5006938de29cd40a3d41fa2cad6a35a43 --- /dev/null +++ b/apps/i2psnark/java/src/net/i2p/kademlia/RandomIfOldTrimmer.java @@ -0,0 +1,21 @@ +package net.i2p.kademlia; + +import net.i2p.I2PAppContext; +import net.i2p.data.SimpleDataStructure; + +/** + * Removes a random element, but only if the bucket hasn't changed in 5 minutes. + * @since 0.9.2 + */ +public class RandomIfOldTrimmer<T extends SimpleDataStructure> extends RandomTrimmer<T> { + + public RandomIfOldTrimmer(I2PAppContext ctx, int max) { + super(ctx, max); + } + + public boolean trim(KBucket<T> kbucket, T toAdd) { + if (kbucket.getLastChanged() > _ctx.clock().now() - 5*60*1000) + return false; + return super.trim(kbucket, toAdd); + } +} diff --git a/apps/i2psnark/java/src/net/i2p/kademlia/RandomTrimmer.java b/apps/i2psnark/java/src/net/i2p/kademlia/RandomTrimmer.java new file mode 100644 index 0000000000000000000000000000000000000000..b0a802072b08dbfc2a84c1f0be161f79bbadff85 --- /dev/null +++ b/apps/i2psnark/java/src/net/i2p/kademlia/RandomTrimmer.java @@ -0,0 +1,31 @@ +package net.i2p.kademlia; + +import java.util.ArrayList; +import java.util.List; + +import net.i2p.I2PAppContext; +import net.i2p.data.SimpleDataStructure; + +/** + * Removes a random element. Not resistant to flooding. + * @since 0.9.2 + */ +public class RandomTrimmer<T extends SimpleDataStructure> implements KBucketTrimmer<T> { + protected final I2PAppContext _ctx; + private final int _max; + + public RandomTrimmer(I2PAppContext ctx, int max) { + _ctx = ctx; + _max = max; + } + + public boolean trim(KBucket<T> kbucket, T toAdd) { + List<T> e = new ArrayList(kbucket.getEntries()); + int sz = e.size(); + // concurrency + if (sz < _max) + return true; + T toRemove = e.get(_ctx.random().nextInt(sz)); + return kbucket.remove(toRemove); + } +} diff --git a/apps/i2psnark/java/src/net/i2p/kademlia/RejectTrimmer.java b/apps/i2psnark/java/src/net/i2p/kademlia/RejectTrimmer.java new file mode 100644 index 0000000000000000000000000000000000000000..2e29f28e27ca94070c262b581c809cd89d65972b --- /dev/null +++ b/apps/i2psnark/java/src/net/i2p/kademlia/RejectTrimmer.java @@ -0,0 +1,13 @@ +package net.i2p.kademlia; + +import net.i2p.data.SimpleDataStructure; + +/** + * Removes nothing and always rejects the add. Flood resistant.. + * @since 0.9.2 + */ +public class RejectTrimmer<T extends SimpleDataStructure> implements KBucketTrimmer<T> { + public boolean trim(KBucket<T> kbucket, T toAdd) { + return false; + } +} diff --git a/apps/i2psnark/java/src/org/klomp/snark/ExtensionHandler.java b/apps/i2psnark/java/src/org/klomp/snark/ExtensionHandler.java index c02b948fe3bcdfeb73ed7b0a315f959f1c465d27..8a49fc428477240a940c381c9402bc503e740979 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/ExtensionHandler.java +++ b/apps/i2psnark/java/src/org/klomp/snark/ExtensionHandler.java @@ -338,7 +338,7 @@ abstract class ExtensionHandler { System.arraycopy(ids, off, hash, 0, HASH_LENGTH); if (DataHelper.eq(hash, peer.getPeerID().getDestHash())) continue; - PeerID pID = new PeerID(hash); + PeerID pID = new PeerID(hash, listener.getUtil()); peers.add(pID); } // could include ourselves, listener must remove diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java index fd170586a398269e1420e73b0c5a52727975cfd4..5c4fdda399e5779556260e2b195d2d8165d00c6d 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java @@ -1435,5 +1435,13 @@ class PeerCoordinator implements PeerListener return listener.overUpBWLimit(total * 1000 / CHECK_PERIOD); return false; } + + /** + * Convenience + * @since 0.9.2 + */ + public I2PSnarkUtil getUtil() { + return _util; + } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerID.java b/apps/i2psnark/java/src/org/klomp/snark/PeerID.java index 37cf1a9b65ccbadad69299d2a783373ce665fc48..32f0d14bfaf2afe01c9996bdcc4a553e7d2ec611 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerID.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerID.java @@ -52,6 +52,7 @@ public class PeerID implements Comparable /** whether we have tried to get the dest from the hash - only do once */ private boolean triedDestLookup; private final int hash; + private final I2PSnarkUtil util; public PeerID(byte[] id, Destination address) { @@ -60,6 +61,7 @@ public class PeerID implements Comparable this.port = 6881; this.destHash = address.calculateHash().getData(); hash = calculateHash(); + util = null; } /** @@ -93,13 +95,15 @@ public class PeerID implements Comparable port = 6881; this.destHash = address.calculateHash().getData(); hash = calculateHash(); + util = null; } /** * Creates a PeerID from a destHash + * @param util for eventual destination lookup * @since 0.8.1 */ - public PeerID(byte[] dest_hash) throws InvalidBEncodingException + public PeerID(byte[] dest_hash, I2PSnarkUtil util) throws InvalidBEncodingException { // id and address remain null port = 6881; @@ -107,6 +111,7 @@ public class PeerID implements Comparable throw new InvalidBEncodingException("bad hash length"); destHash = dest_hash; hash = DataHelper.hashCode(dest_hash); + this.util = util; } public byte[] getID() @@ -131,7 +136,7 @@ public class PeerID implements Comparable { if (address == null && destHash != null && !triedDestLookup) { String b32 = Base32.encode(destHash) + ".b32.i2p"; - address = I2PAppContext.getGlobalContext().namingService().lookup(b32); + address = util.getDestination(b32); triedDestLookup = true; } return address; diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerListener.java b/apps/i2psnark/java/src/org/klomp/snark/PeerListener.java index 8b272bf81663f0f79e8c266250c0de3e2fcd35a1..892ba4ff3cddd6b250b1191a4282bf410259db33 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerListener.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerListener.java @@ -207,4 +207,10 @@ interface PeerListener * @since 0.8.4 */ void gotPeers(Peer peer, List<PeerID> pIDList); + + /** + * Convenience + * @since 0.9.2 + */ + public I2PSnarkUtil getUtil(); } diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 309d57c84a0db08551fb8d9ee91079cb2b269a0c..eed367e7a5fff955cdadffa4916bd88d628daa75 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -1484,7 +1484,7 @@ public class SnarkManager implements Snark.CompleteListener { byte[] ih = Base64.decode(b64); // ignore value - TODO put tracker URL in value if (ih != null && ih.length == 20) - addMagnet("* " + _("Magnet") + ' ' + I2PSnarkUtil.toHex(ih), ih, null, false); + addMagnet(_("Magnet") + ' ' + I2PSnarkUtil.toHex(ih), ih, null, false); // else remove from config? } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java index 487165ca97d6690888d43aaf19ec5d67c34bf378..dccfddbcd3513201e5c9fb6df89ede8b43c0c40b 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java @@ -487,7 +487,7 @@ public class TrackerClient implements Runnable { if ((!stop) && !hashes.isEmpty()) { List<Peer> peers = new ArrayList(hashes.size()); for (Hash h : hashes) { - PeerID pID = new PeerID(h.getData()); + PeerID pID = new PeerID(h.getData(), _util); peers.add(new Peer(pID, snark.getID(), snark.getInfoHash(), snark.getMetaInfo())); } Collections.shuffle(peers, r); @@ -651,7 +651,7 @@ public class TrackerClient implements Runnable { in = new FileInputStream(fetched); TrackerInfo info = new TrackerInfo(in, snark.getID(), - snark.getInfoHash(), snark.getMetaInfo()); + snark.getInfoHash(), snark.getMetaInfo(), _util); if (_log.shouldLog(Log.INFO)) _log.info("TrackerClient response: " + info); diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java index abcd61bf536be39b55a307d0c7e047417d6ac3aa..6399706fc9f4e35e8985a06ce165f99da0df3b9f 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java @@ -47,19 +47,19 @@ class TrackerInfo private int incomplete; /** @param metainfo may be null */ - public TrackerInfo(InputStream in, byte[] my_id, byte[] infohash, MetaInfo metainfo) + public TrackerInfo(InputStream in, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util) throws IOException { - this(new BDecoder(in), my_id, infohash, metainfo); + this(new BDecoder(in), my_id, infohash, metainfo, util); } - private TrackerInfo(BDecoder be, byte[] my_id, byte[] infohash, MetaInfo metainfo) + private TrackerInfo(BDecoder be, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util) throws IOException { - this(be.bdecodeMap().getMap(), my_id, infohash, metainfo); + this(be.bdecodeMap().getMap(), my_id, infohash, metainfo, util); } - private TrackerInfo(Map m, byte[] my_id, byte[] infohash, MetaInfo metainfo) + private TrackerInfo(Map m, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util) throws IOException { BEValue reason = (BEValue)m.get("failure reason"); @@ -85,10 +85,10 @@ class TrackerInfo Set<Peer> p; try { // One big string (the official compact format) - p = getPeers(bePeers.getBytes(), my_id, infohash, metainfo); + p = getPeers(bePeers.getBytes(), my_id, infohash, metainfo, util); } catch (InvalidBEncodingException ibe) { // List of Dictionaries or List of Strings - p = getPeers(bePeers.getList(), my_id, infohash, metainfo); + p = getPeers(bePeers.getList(), my_id, infohash, metainfo, util); } peers = p; } @@ -124,7 +124,7 @@ class TrackerInfo ******/ /** List of Dictionaries or List of Strings */ - private static Set<Peer> getPeers(List<BEValue> l, byte[] my_id, byte[] infohash, MetaInfo metainfo) + private static Set<Peer> getPeers(List<BEValue> l, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util) throws IOException { Set<Peer> peers = new HashSet(l.size()); @@ -138,7 +138,7 @@ class TrackerInfo try { // Case 2 - compact - A list of 32-byte binary strings (hashes) // This was just for testing and is not the official format - peerID = new PeerID(bev.getBytes()); + peerID = new PeerID(bev.getBytes(), util); } catch (InvalidBEncodingException ibe2) { // don't let one bad entry spoil the whole list //Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR); @@ -157,7 +157,7 @@ class TrackerInfo * One big string of concatenated 32-byte hashes * @since 0.8.1 */ - private static Set<Peer> getPeers(byte[] l, byte[] my_id, byte[] infohash, MetaInfo metainfo) + private static Set<Peer> getPeers(byte[] l, byte[] my_id, byte[] infohash, MetaInfo metainfo, I2PSnarkUtil util) throws IOException { int count = l.length / HASH_LENGTH; @@ -168,7 +168,7 @@ class TrackerInfo byte[] hash = new byte[HASH_LENGTH]; System.arraycopy(l, i * HASH_LENGTH, hash, 0, HASH_LENGTH); try { - peerID = new PeerID(hash); + peerID = new PeerID(hash, util); } catch (InvalidBEncodingException ibe) { // won't happen continue; diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/DHT.java b/apps/i2psnark/java/src/org/klomp/snark/dht/DHT.java index 9e629415b4ac87e7d66fd98b3cc697caaacdf0d8..3a32e1fa37912946496c2b108192b92bd221438b 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/DHT.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/DHT.java @@ -12,6 +12,7 @@ import net.i2p.data.Hash; /** * Stub for KRPC + * @since 0.8.4 */ public interface DHT { diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/DHTNodes.java b/apps/i2psnark/java/src/org/klomp/snark/dht/DHTNodes.java index f752efb17eef883ea3c53ff442c0a56c007a2c7b..5e2569e2d103e19df4cad09ed5f1eb83ec38c67c 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/DHTNodes.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/DHTNodes.java @@ -25,7 +25,7 @@ import net.i2p.util.SimpleTimer2; * * And a real Kademlia routing table, which stores node IDs only. * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ class DHTNodes { @@ -44,13 +44,17 @@ class DHTNodes { private static final long MIN_EXPIRE_TIME = 10*60*1000; private static final long DELTA_EXPIRE_TIME = 3*60*1000; private static final int MAX_PEERS = 799; + /** Buckets older than this are refreshed - BEP 5 says 15 minutes */ + private static final long MAX_BUCKET_AGE = 15*60*1000; + private static final int KAD_K = 8; + private static final int KAD_B = 1; public DHTNodes(I2PAppContext ctx, NID me) { _context = ctx; _expireTime = MAX_EXPIRE_TIME; _log = _context.logManager().getLog(DHTNodes.class); _nodeMap = new ConcurrentHashMap(); - _kad = new KBucketSet(ctx, me, 8, 1); + _kad = new KBucketSet(ctx, me, KAD_K, KAD_B, new KBTrimmer(ctx, KAD_K)); } public void start() { @@ -121,7 +125,7 @@ class DHTNodes { * DHT - get random keys to explore */ public List<NID> getExploreKeys() { - return _kad.getExploreKeys(15*60*1000); + return _kad.getExploreKeys(MAX_BUCKET_AGE); } /** */ diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/DHTTracker.java b/apps/i2psnark/java/src/org/klomp/snark/dht/DHTTracker.java index df2bf60782563c455a6c91854ac4b63a84ac2c05..5e8b3a9526043a1ee6a22bfcab8ba8cc5ce02ea1 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/DHTTracker.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/DHTTracker.java @@ -17,7 +17,7 @@ import net.i2p.util.SimpleTimer2; /** * The tracker stores peers, i.e. Dest hashes (not nodes). * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ class DHTTracker { diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/InfoHash.java b/apps/i2psnark/java/src/org/klomp/snark/dht/InfoHash.java index bc27ba9d935327ea50b835e8904f66a0e34b5df9..d8c285c3dc5351793c94f9c2b1771b4466977bcd 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/InfoHash.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/InfoHash.java @@ -9,7 +9,7 @@ import org.klomp.snark.I2PSnarkUtil; /** * A 20-byte SHA1 info hash * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ class InfoHash extends SHA1Hash { diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/KBTrimmer.java b/apps/i2psnark/java/src/org/klomp/snark/dht/KBTrimmer.java new file mode 100644 index 0000000000000000000000000000000000000000..6a36be69ef876ddc680c3ad88402115d076a82fa --- /dev/null +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/KBTrimmer.java @@ -0,0 +1,38 @@ +package org.klomp.snark.dht; + +import java.util.Set; + +import net.i2p.I2PAppContext; +import net.i2p.kademlia.KBucket; +import net.i2p.kademlia.KBucketTrimmer; + +/** + * Removes an element older than 15 minutes, but only if the bucket hasn't changed in 5 minutes. + * @since 0.9.2 + */ +class KBTrimmer implements KBucketTrimmer<NID> { + private final I2PAppContext _ctx; + private final int _max; + + private static final long MIN_BUCKET_AGE = 5*60*1000; + private static final long MAX_NODE_AGE = 15*60*1000; + + public KBTrimmer(I2PAppContext ctx, int max) { + _ctx = ctx; + _max = max; + } + + public boolean trim(KBucket<NID> kbucket, NID toAdd) { + long now = _ctx.clock().now(); + if (kbucket.getLastChanged() > now - MIN_BUCKET_AGE) + return false; + Set<NID> entries = kbucket.getEntries(); + for (NID nid : entries) { + if (nid.lastSeen() < now - MAX_NODE_AGE) { + if (kbucket.remove(nid)) + return true; + } + } + return entries.size() < _max; + } +} diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/KRPC.java b/apps/i2psnark/java/src/org/klomp/snark/dht/KRPC.java index b3fd95da2e39a73c189818524134d1c295dd4a85..bdf0891fc3ffea628aa1a51a6924de05661032dd 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/KRPC.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/KRPC.java @@ -79,7 +79,7 @@ import org.klomp.snark.bencode.InvalidBEncodingException; * - nodes (in the find_node and get_peers response) is one concatenated string, not a list of strings, right? * - Node ID enforcement, keyspace rotation? * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ public class KRPC implements I2PSessionMuxedListener, DHT { @@ -133,6 +133,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT { private static final int REPLY_PONG = 1; private static final int REPLY_PEERS = 2; private static final int REPLY_NODES = 3; + private static final int REPLY_NETWORK_FAIL = 4; public static final boolean SECURE_NID = true; @@ -272,6 +273,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT { if (! (ni.equals(_myNodeInfo) || (toTry.contains(ni) && tried.contains(ni)))) toTry.add(ni); } + } else if (replyType == REPLY_NETWORK_FAIL) { + break; } else { if (_log.shouldLog(Log.INFO)) _log.info("Got unexpected reply " + replyType + ": " + waiter.getReplyObject()); @@ -370,6 +373,8 @@ public class KRPC implements I2PSessionMuxedListener, DHT { if (! (ni.equals(_myNodeInfo) || tried.contains(ni) || toTry.contains(ni))) toTry.add(ni); } + } else if (replyType == REPLY_NETWORK_FAIL) { + break; } else { if (_log.shouldLog(Log.INFO)) _log.info("Got unexpected reply " + replyType + ": " + waiter.getReplyObject()); @@ -564,7 +569,11 @@ public class KRPC implements I2PSessionMuxedListener, DHT { _tracker.stop(); PersistDHT.saveDHT(_knownNodes, _dhtFile); _knownNodes.stop(); - _sentQueries.clear(); + for (Iterator<ReplyWaiter> iter = _sentQueries.values().iterator(); iter.hasNext(); ) { + ReplyWaiter waiter = iter.next(); + iter.remove(); + waiter.networkFail(); + } _outgoingTokens.clear(); _incomingTokens.clear(); } @@ -1317,7 +1326,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT { private final NodeInfo sentTo; private final Runnable onReply; private final Runnable onTimeout; - private int replyCode; + private volatile int replyCode; private Object sentObject; private Object replyObject; @@ -1400,6 +1409,18 @@ public class KRPC implements I2PSessionMuxedListener, DHT { this.notifyAll(); } } + + /** + * Will notify this but not run onReply or onTimeout, + * or remove from _sentQueries, or call heardFrom(). + */ + public void networkFail() { + cancel(); + replyCode = REPLY_NETWORK_FAIL; + synchronized(this) { + this.notifyAll(); + } + } } // I2PSessionMuxedListener interface ---------------- @@ -1532,22 +1553,24 @@ public class KRPC implements I2PSessionMuxedListener, DHT { return; if (!_hasBootstrapped) { if (_log.shouldLog(Log.INFO)) - _log.info("Bootstrap start size: " + _knownNodes.size()); + _log.info("Bootstrap start, size: " + _knownNodes.size()); explore(_myNID, 8, 60*1000, 1); if (_log.shouldLog(Log.INFO)) - _log.info("Bootstrap done size: " + _knownNodes.size()); + _log.info("Bootstrap done, size: " + _knownNodes.size()); _hasBootstrapped = true; } if (!_isRunning) return; if (_log.shouldLog(Log.INFO)) - _log.info("Explore start size: " + _knownNodes.size()); + _log.info("Explore start. size: " + _knownNodes.size()); List<NID> keys = _knownNodes.getExploreKeys(); for (NID nid : keys) { explore(nid, 8, 60*1000, 1); + if (!_isRunning) + return; } if (_log.shouldLog(Log.INFO)) - _log.info("Explore done size: " + _knownNodes.size()); + _log.info("Explore of " + keys.size() + " buckets done, new size: " + _knownNodes.size()); new Explorer(EXPLORE_TIME); } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/MsgID.java b/apps/i2psnark/java/src/org/klomp/snark/dht/MsgID.java index 6d53f7c8fb1a17bee2815aa54b6908f56766020e..fc98c6d428e5f884a3dc7d1d0f0e053b588e26b5 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/MsgID.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/MsgID.java @@ -9,12 +9,14 @@ import net.i2p.data.ByteArray; /** * Used for both incoming and outgoing message IDs * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ class MsgID extends ByteArray { + /** BEP 5: 2 bytes, incremented */ private static final int MY_TOK_LEN = 8; + private static final int MAX_TOK_LEN = 16; /** outgoing - generate a random ID */ public MsgID(I2PAppContext ctx) { @@ -28,5 +30,8 @@ class MsgID extends ByteArray { /** incoming - save the ID (arbitrary length) */ public MsgID(byte[] data) { super(data); + // lets not get carried away + if (data.length > MAX_TOK_LEN) + throw new IllegalArgumentException(); } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/NID.java b/apps/i2psnark/java/src/org/klomp/snark/dht/NID.java index f61c857d8ff42b75960b5d5af68ca40e1f578da4..44aa29c8560a31a32f352dcd34feaef803a06c82 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/NID.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/NID.java @@ -10,7 +10,7 @@ import net.i2p.util.Clock; * A 20-byte peer ID, used as a Map key in lots of places. * Must be public for constructor in KBucketSet.generateRandomKey() * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ public class NID extends SHA1Hash { diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/NodeInfo.java b/apps/i2psnark/java/src/org/klomp/snark/dht/NodeInfo.java index 9ed1a2912d428486652b4703804a77217de590fa..c6fce8f14461259a6b52739af3574e09cf9db27a 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/NodeInfo.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/NodeInfo.java @@ -20,7 +20,7 @@ import net.i2p.util.RandomSource; * always have the Destination. * The conpact info is immutable. The Destination may be added later. * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/NodeInfoComparator.java b/apps/i2psnark/java/src/org/klomp/snark/dht/NodeInfoComparator.java index 9995dfe57947f6843c5479a179a569ad011a2226..842853a99d91a2420d46d78a8374559b0a65f3d2 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/NodeInfoComparator.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/NodeInfoComparator.java @@ -12,7 +12,7 @@ import net.i2p.data.DataHelper; * Closest to a InfoHash or NID key. * Use for NodeInfos. * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ class NodeInfoComparator implements Comparator<NodeInfo> { diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/Peer.java b/apps/i2psnark/java/src/org/klomp/snark/dht/Peer.java index 84fc263a7d59711bcc000a875adbdc8301028225..536771d5bc3ccaf565372e3bf2359077fa1bd559 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/Peer.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/Peer.java @@ -9,7 +9,7 @@ import net.i2p.data.Hash; * A single peer for a single torrent. * This is what the DHT tracker remembers. * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ class Peer extends Hash { diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/Peers.java b/apps/i2psnark/java/src/org/klomp/snark/dht/Peers.java index 7fcc2b63e542f9b22bc13f306df734a190c2db44..cc8d16318a0288c85fd751f381e6587f7158908e 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/Peers.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/Peers.java @@ -10,7 +10,7 @@ import net.i2p.data.Hash; /** * All the peers for a single torrent * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ class Peers extends ConcurrentHashMap<Hash, Peer> { diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/PersistDHT.java b/apps/i2psnark/java/src/org/klomp/snark/dht/PersistDHT.java index aca3e5fd916dd39006d7eae8cd5f285a73f11fcb..f474e1dd06b7d75859fdbaa59e5fa431a1397392 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/PersistDHT.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/PersistDHT.java @@ -17,6 +17,7 @@ import net.i2p.util.SecureFileOutputStream; /** * Retrieve / Store the local DHT in a file * + * @since 0.9.2 */ abstract class PersistDHT { diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/Token.java b/apps/i2psnark/java/src/org/klomp/snark/dht/Token.java index 4995b48a9936e2317da5e1693c0ae0f048ce166b..14c87280a9655ffb6d62fb93b2f4f461652e71b4 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/Token.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/Token.java @@ -12,7 +12,7 @@ import net.i2p.data.DataHelper; /** * Used for Both outgoing and incoming tokens * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ class Token extends ByteArray { diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/TokenKey.java b/apps/i2psnark/java/src/org/klomp/snark/dht/TokenKey.java index 996d43351ea1594835bd0d1482fe1a67b258f028..c022f49bddf363cc9cb4f70230fde20149f05c3b 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/TokenKey.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/TokenKey.java @@ -9,7 +9,7 @@ import net.i2p.data.DataHelper; /** * Used to index incoming Tokens * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ class TokenKey extends SHA1Hash { diff --git a/apps/i2psnark/java/src/org/klomp/snark/dht/Torrents.java b/apps/i2psnark/java/src/org/klomp/snark/dht/Torrents.java index 304b7c9491a35e09d1225fdf92a141654e075bea..6adb36c527642793c2729e4a3320c8558d571511 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/dht/Torrents.java +++ b/apps/i2psnark/java/src/org/klomp/snark/dht/Torrents.java @@ -8,7 +8,7 @@ import java.util.concurrent.ConcurrentHashMap; /** * All the torrents * - * @since 0.8.4 + * @since 0.9.2 * @author zzz */ class Torrents extends ConcurrentHashMap<InfoHash, Peers> { diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java b/apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java index c5f31d8f52d97d135abd45787596aabfc819f5e3..98521e739fde33ae81ff88acea976b224ed86a60 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/FetchAndAdd.java @@ -72,7 +72,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl _log = ctx.logManager().getLog(FetchAndAdd.class); _mgr = mgr; _url = url; - _name = "* " + _("Download torrent file from {0}", url); + _name = _("Download torrent file from {0}", url); byte[] fake = null; try { fake = SHA1.getInstance().digest(url.getBytes("ISO-8859-1")); diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index 5a69a9c9032021984e9b4159cac27ba7020eafe0..731c43727798344f0afcf253caa8dbbb8d9a4325 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -89,7 +89,8 @@ public class I2PSnarkServlet extends DefaultServlet { @Override public void destroy() { - _manager.stop(); + if (_manager != null) + _manager.stop(); super.destroy(); } @@ -911,11 +912,17 @@ public class I2PSnarkServlet extends DefaultServlet { TreeSet<String> fileNames = new TreeSet(new TorrentNameComparator()); fileNames.addAll(files); ArrayList<Snark> rv = new ArrayList(fileNames.size()); + int magnet = 0; for (Iterator iter = fileNames.iterator(); iter.hasNext(); ) { String name = (String)iter.next(); Snark snark = _manager.getTorrent(name); - if (snark != null) - rv.add(snark); + if (snark != null) { + // put downloads and magnets first + if (snark.getStorage() == null) + rv.add(magnet++, snark); + else + rv.add(snark); + } } return rv; } @@ -1782,7 +1789,7 @@ public class I2PSnarkServlet extends DefaultServlet { } ihash = xt.substring("urn:btih:".length()); trackerURL = getTrackerParam(url); - name = "* " + _("Magnet") + ' ' + ihash; + name = _("Magnet") + ' ' + ihash; String dn = getParam("dn", url); if (dn != null) name += " (" + Storage.filterName(dn) + ')'; @@ -1792,7 +1799,7 @@ public class I2PSnarkServlet extends DefaultServlet { int col = ihash.indexOf(':'); if (col >= 0) ihash = ihash.substring(0, col); - name = "* " + _("Magnet") + ' ' + ihash; + name = _("Magnet") + ' ' + ihash; } else { return; } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java index f71a19d424fd2cdbedc9dedbfe8ea2ef4b236f47..95eef4687a81f862b87a6faaab32c753c21fd09c 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java @@ -100,6 +100,8 @@ class ConnectionOptions extends I2PSocketOptionsImpl { private static final boolean DEFAULT_ANSWER_PINGS = true; private static final int DEFAULT_INACTIVITY_TIMEOUT = 90*1000; private static final int DEFAULT_INACTIVITY_ACTION = INACTIVITY_ACTION_SEND; + private static final int DEFAULT_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR = 1; + private static final int DEFAULT_SLOW_START_GROWTH_RATE_FACTOR = 1; /** @@ -327,8 +329,10 @@ class ConnectionOptions extends I2PSocketOptionsImpl { setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, DEFAULT_INACTIVITY_TIMEOUT)); setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, DEFAULT_INACTIVITY_ACTION)); setInboundBufferSize(getMaxMessageSize() * (Connection.MAX_WINDOW_SIZE + 2)); - setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, 1)); - setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, 1)); + setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, + DEFAULT_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR)); + setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, + DEFAULT_SLOW_START_GROWTH_RATE_FACTOR)); // overrides default in super() setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT)); setAnswerPings(getBool(opts, PROP_ANSWER_PINGS, DEFAULT_ANSWER_PINGS)); @@ -378,9 +382,11 @@ class ConnectionOptions extends I2PSocketOptionsImpl { setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, DEFAULT_INACTIVITY_ACTION)); setInboundBufferSize(getMaxMessageSize() * (Connection.MAX_WINDOW_SIZE + 2)); if (opts.contains(PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR)) - setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, 2)); + setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, + DEFAULT_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR)); if (opts.contains(PROP_SLOW_START_GROWTH_RATE_FACTOR)) - setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, 2)); + setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, + DEFAULT_SLOW_START_GROWTH_RATE_FACTOR)); if (opts.containsKey(PROP_CONNECT_TIMEOUT)) // wow 5 minutes!!! FIXME!! // overrides default in super() diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java index 250dae808b256443e7a7574b03e77a3c06ff27c7..9d353075cb0c1fb79e5bb4225094b176edc95d1e 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java @@ -23,6 +23,8 @@ import net.i2p.util.SimpleTimer; class ConnectionPacketHandler { private final I2PAppContext _context; private final Log _log; + + public static final int MAX_SLOW_START_WINDOW = 24; public ConnectionPacketHandler(I2PAppContext context) { _context = context; @@ -367,9 +369,16 @@ class ConnectionPacketHandler { // grow acked/N times (where N = the slow start factor) // always grow at least 1 int factor = con.getOptions().getSlowStartGrowthRateFactor(); - if (factor <= 1) - newWindowSize += acked; - else if (acked < factor) + if (factor <= 1) { + // above a certain point, don't grow exponentially + // as it often leads to a big packet loss (30-50) all at once that + // takes quite a while (a minute or more) to recover from, + // especially if crypto tags are lost + if (newWindowSize >= MAX_SLOW_START_WINDOW) + newWindowSize++; + else + newWindowSize = Math.min(MAX_SLOW_START_WINDOW, newWindowSize + acked); + } else if (acked < factor) newWindowSize++; else newWindowSize += acked / factor; diff --git a/apps/streaming/java/src/net/i2p/client/streaming/TCBShare.java b/apps/streaming/java/src/net/i2p/client/streaming/TCBShare.java index 15b8e937e727dfd02ffa9c73e79d494b2001ae21..d412aa36a16e6bf9245578fc664e31481aea3c10 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/TCBShare.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/TCBShare.java @@ -31,7 +31,7 @@ class TCBShare { private static final double RTT_DAMPENING = 0.75; private static final double WDW_DAMPENING = 0.75; private static final int MAX_RTT = ((int) Connection.MAX_RESEND_DELAY) / 2; - private static final int MAX_WINDOW_SIZE = Connection.MAX_WINDOW_SIZE / 4; + private static final int MAX_WINDOW_SIZE = ConnectionPacketHandler.MAX_SLOW_START_WINDOW; public TCBShare(I2PAppContext ctx, SimpleTimer2 timer) { _context = ctx; @@ -45,6 +45,7 @@ class TCBShare { _cleaner.cancel(); } + /** retrieve from cache */ public void updateOptsFromShare(Connection con) { Destination dest = con.getRemotePeer(); if (dest == null) @@ -65,6 +66,7 @@ class TCBShare { opts.setWindowSize(e.getWindowSize()); } + /** store to cache */ public void updateShareOpts(Connection con) { Destination dest = con.getRemotePeer(); if (dest == null) diff --git a/history.txt b/history.txt index aef4f4962deea96f0e21f7c78349575259fa3cf5..6a030bb408a31b919458ef1f8d6f73b5702a0dc7 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,44 @@ +2012-08-31 zzz + * i2psnark: Remove * from magnet and download names + * Router: Lengthen shutdown spinner life + * Startup: Don't complain about clients.config missing on Android + +2012-08-29 zzz + * ClientManager: Cleanups + * i2psnark: + - Fix NPE on destroy() if init() failed + - Add new flood-resistant KBucket trim policy + - Limit received MsgID size + * NTCP: Reduce lock contention (ticket #697) + * RandomIterator: Workaround for Android bug (ticket #703) + +2012-08-27 zzz + * i2psnark: + - Notify threads awaiting DHT replies at shutdown + - Fix cases where we weren't using the session for b32 lookup + * Reseed: Remove forum.i2p2.de + * Streaming: Limit amount of slow-start exponential growth + * SSU: + - Limit UDPSender queue size + - Increase UDPSender max packet lifetime + - Clear UDPSender queue before sending destroys to all + - Increase PeerState queue size so large streaming windows + don't get dropped right away, especially at slow start + - Various improvements on iterating over pending outbound + messages in PeerState + * Wrapper: Update armv7 to 3.5.15 + +2012-08-27 kytv + * Update Java Service Wrapper to v3.5.15. + - Windows: Self-compiled with VS2010 in Windows 7. The icon has been + changed from Tanuki's default to Itoopie. + - FreeBSD: Self-compiled in FreeBSD 7.4 to eliminate the dependency on the + compat6x port. + - Linux ARMv5, Linux PPC32: Self-compiled in Debian Squeeze + - Linux x86, Linux x64, MacOSX & Solaris: Binares are from the "community + edition" deltapack offered by Tanuki. The x86 and x64 binaries for Linux + have been stripped. + 2012-08-26 zzz * DataHelper: Trim trailing whitespace when loading properties * NetDB: Increase floodfills, decrease flood redundancy diff --git a/installer/lib/wrapper/all/wrapper.jar b/installer/lib/wrapper/all/wrapper.jar index d41b3c995062117f557935a7d3ff23bb83de6c3f..717c7504afbe1d798c60b9d34239b7f2c540afb4 100644 Binary files a/installer/lib/wrapper/all/wrapper.jar and b/installer/lib/wrapper/all/wrapper.jar differ diff --git a/installer/lib/wrapper/freebsd/i2psvc b/installer/lib/wrapper/freebsd/i2psvc index 8169d4caf71b55e6b2cfd3d857a89d0fb84196a7..47f26b1f422b525061f68087a32d8a566069f866 100644 Binary files a/installer/lib/wrapper/freebsd/i2psvc and b/installer/lib/wrapper/freebsd/i2psvc differ diff --git a/installer/lib/wrapper/freebsd/libwrapper.so b/installer/lib/wrapper/freebsd/libwrapper.so index 99048a1a4ef19f991041a7f20b118db0242b04ad..f98aad7b31e38957267d26d29c6ef8081a0e7250 100644 Binary files a/installer/lib/wrapper/freebsd/libwrapper.so and b/installer/lib/wrapper/freebsd/libwrapper.so differ diff --git a/installer/lib/wrapper/freebsd64/i2psvc b/installer/lib/wrapper/freebsd64/i2psvc index aba0058822855be0ceab70b39f5fe3de6e43dc1c..3bc0ea5028b22919988ebe3f49001e9401f6fe9d 100644 Binary files a/installer/lib/wrapper/freebsd64/i2psvc and b/installer/lib/wrapper/freebsd64/i2psvc differ diff --git a/installer/lib/wrapper/freebsd64/libwrapper.so b/installer/lib/wrapper/freebsd64/libwrapper.so index c99d73a6334478484af7eb6233bec76e1232cea2..495db0c8977740ef83ea5da9e950272f4042c7d1 100644 Binary files a/installer/lib/wrapper/freebsd64/libwrapper.so and b/installer/lib/wrapper/freebsd64/libwrapper.so differ diff --git a/installer/lib/wrapper/linux-armv5/i2psvc b/installer/lib/wrapper/linux-armv5/i2psvc index d063e2b7103539a02fbb6af342d0d18925759d15..ba0eefdf8d4f5767941c6d751ebb6a0fbeb5a118 100644 Binary files a/installer/lib/wrapper/linux-armv5/i2psvc and b/installer/lib/wrapper/linux-armv5/i2psvc differ diff --git a/installer/lib/wrapper/linux-armv5/libwrapper.so b/installer/lib/wrapper/linux-armv5/libwrapper.so index a46bfc54a9dbc178c83933c5efbc1d9b73b95a12..8e1274e5560fca5f9ac003ad8df84d7599e5c20c 100644 Binary files a/installer/lib/wrapper/linux-armv5/libwrapper.so and b/installer/lib/wrapper/linux-armv5/libwrapper.so differ diff --git a/installer/lib/wrapper/linux-armv7/i2psvc b/installer/lib/wrapper/linux-armv7/i2psvc index e95ca3aebdec0dd3d15868b1426e94fcc6037f9f..61818afade85115f8836d243fa5a7dfef2b8178f 100644 Binary files a/installer/lib/wrapper/linux-armv7/i2psvc and b/installer/lib/wrapper/linux-armv7/i2psvc differ diff --git a/installer/lib/wrapper/linux-armv7/libwrapper.so b/installer/lib/wrapper/linux-armv7/libwrapper.so index 720b37556f7c52ea7967c5aa14e6c04fce4e4077..3fa8f8066182efcae67dcb348d1c99c1f41484a5 100644 Binary files a/installer/lib/wrapper/linux-armv7/libwrapper.so and b/installer/lib/wrapper/linux-armv7/libwrapper.so differ diff --git a/installer/lib/wrapper/linux-ppc/i2psvc b/installer/lib/wrapper/linux-ppc/i2psvc index f0e4a182876c4c0c254ed68307d12031ebb824ee..a2ab7512d5f5b42e5c3636a9825b64ae660c38e1 100644 Binary files a/installer/lib/wrapper/linux-ppc/i2psvc and b/installer/lib/wrapper/linux-ppc/i2psvc differ diff --git a/installer/lib/wrapper/linux-ppc/libwrapper.so b/installer/lib/wrapper/linux-ppc/libwrapper.so index ce13e97aed594da10aa6f208fe36aaded450be53..cbe517d0ade5ca0ca2117f8bf62ba290839ea951 100644 Binary files a/installer/lib/wrapper/linux-ppc/libwrapper.so and b/installer/lib/wrapper/linux-ppc/libwrapper.so differ diff --git a/installer/lib/wrapper/linux/i2psvc b/installer/lib/wrapper/linux/i2psvc index ffb6aca64ec7fd069d4774b152b446c67396d2e7..155b887b2d429b432878e101e8c704796c32cf2e 100644 Binary files a/installer/lib/wrapper/linux/i2psvc and b/installer/lib/wrapper/linux/i2psvc differ diff --git a/installer/lib/wrapper/linux/libwrapper.so b/installer/lib/wrapper/linux/libwrapper.so index 6c642c3d9decd7c5e8f9e56f584a820c41e8ecf2..4438a20ba668e9d316b124b0f2748ee756c98215 100644 Binary files a/installer/lib/wrapper/linux/libwrapper.so and b/installer/lib/wrapper/linux/libwrapper.so differ diff --git a/installer/lib/wrapper/linux64/i2psvc b/installer/lib/wrapper/linux64/i2psvc index ce1196d8a525f901dcbce61cfe5c305738b09a98..771285974f82995106fcc816bad301d067de55dc 100644 Binary files a/installer/lib/wrapper/linux64/i2psvc and b/installer/lib/wrapper/linux64/i2psvc differ diff --git a/installer/lib/wrapper/linux64/libwrapper.so b/installer/lib/wrapper/linux64/libwrapper.so index ce5e78646a8bb6cb74f98027c2857b1c4b8ff134..b3c58161e42b69cf80160f034e85ebaa962ccec8 100644 Binary files a/installer/lib/wrapper/linux64/libwrapper.so and b/installer/lib/wrapper/linux64/libwrapper.so differ diff --git a/installer/lib/wrapper/macosx/i2psvc-macosx-universal-32 b/installer/lib/wrapper/macosx/i2psvc-macosx-universal-32 index 726a0d53f812a760487c1a40ae86f27073c31bb7..16b1479ba14018097a4d9e62ee03453910fdd133 100644 Binary files a/installer/lib/wrapper/macosx/i2psvc-macosx-universal-32 and b/installer/lib/wrapper/macosx/i2psvc-macosx-universal-32 differ diff --git a/installer/lib/wrapper/macosx/i2psvc-macosx-universal-64 b/installer/lib/wrapper/macosx/i2psvc-macosx-universal-64 index 52d866ce035409375546fee2e685a73dab2a0cb5..4669457cc8d7ef70e60a383e5372e1ca5ce573f7 100644 Binary files a/installer/lib/wrapper/macosx/i2psvc-macosx-universal-64 and b/installer/lib/wrapper/macosx/i2psvc-macosx-universal-64 differ diff --git a/installer/lib/wrapper/macosx/libwrapper-macosx-universal-32.jnilib b/installer/lib/wrapper/macosx/libwrapper-macosx-universal-32.jnilib index e407394ca03cc3090a0bbd52255df626320d4955..4042180716b362caa6e7c166881963a425bafa0a 100644 Binary files a/installer/lib/wrapper/macosx/libwrapper-macosx-universal-32.jnilib and b/installer/lib/wrapper/macosx/libwrapper-macosx-universal-32.jnilib differ diff --git a/installer/lib/wrapper/macosx/libwrapper-macosx-universal-64.jnilib b/installer/lib/wrapper/macosx/libwrapper-macosx-universal-64.jnilib index 560e0b232fdb6dfccc202bee17c08d8b16630998..8bdcf096800f1f41dff9f6afa8289e40af86e3ab 100644 Binary files a/installer/lib/wrapper/macosx/libwrapper-macosx-universal-64.jnilib and b/installer/lib/wrapper/macosx/libwrapper-macosx-universal-64.jnilib differ diff --git a/installer/lib/wrapper/solaris/i2psvc b/installer/lib/wrapper/solaris/i2psvc index 6cecf4d3c5e1d21d69a40e1719d14a7e701e1f6b..e18d52b675b00ec22170602a48e8e3c9fb35e03f 100644 Binary files a/installer/lib/wrapper/solaris/i2psvc and b/installer/lib/wrapper/solaris/i2psvc differ diff --git a/installer/lib/wrapper/solaris/libwrapper.so b/installer/lib/wrapper/solaris/libwrapper.so index e1e97d353345cf6f0409516a7fdcb3405c0aa59c..d5f6795effe021b3c50fbce101534e6cde29f539 100644 Binary files a/installer/lib/wrapper/solaris/libwrapper.so and b/installer/lib/wrapper/solaris/libwrapper.so differ diff --git a/installer/lib/wrapper/win32/I2Psvc.exe b/installer/lib/wrapper/win32/I2Psvc.exe index 5da0233dbaf189f95cccfca64e7ebf913097e4fb..e7d3530e114af868c68d64d60ad249c07eae6f23 100644 Binary files a/installer/lib/wrapper/win32/I2Psvc.exe and b/installer/lib/wrapper/win32/I2Psvc.exe differ diff --git a/installer/lib/wrapper/win32/wrapper.dll b/installer/lib/wrapper/win32/wrapper.dll index 6902a103f267c274c0946efcc5adaa28621c9627..26a8d64aed715dfc57dcded0a6a8a5b755938fad 100644 Binary files a/installer/lib/wrapper/win32/wrapper.dll and b/installer/lib/wrapper/win32/wrapper.dll differ diff --git a/installer/lib/wrapper/win64/I2Psvc.exe b/installer/lib/wrapper/win64/I2Psvc.exe index 9674753cda462b515ca778971d531d816fb7d9d7..5730b35d82b3256629d0086cd67373bfa9502200 100644 Binary files a/installer/lib/wrapper/win64/I2Psvc.exe and b/installer/lib/wrapper/win64/I2Psvc.exe differ diff --git a/installer/lib/wrapper/win64/wrapper.dll b/installer/lib/wrapper/win64/wrapper.dll index 0b6052e2db258287af1cc83a76c1f758ce6985b2..d9d526b3d9d5ed2e8a4fc3d7a14c9bff768fbb39 100644 Binary files a/installer/lib/wrapper/win64/wrapper.dll and b/installer/lib/wrapper/win64/wrapper.dll differ diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 5b1ba79e398c2a84e1450e314aedb3fba726d6dd..9319fc01c919050d6e37a985b498a0942594c8e1 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 19; + public final static long BUILD = 22; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java index 600539a5c1150caef149f169ef3b144092d9aa18..b50d0d1953adf5b22c9e8e1a4dee250b543a63c3 100644 --- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java +++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java @@ -86,7 +86,7 @@ class ClientConnectionRunner { private ClientWriterRunner _writer; private Hash _destHashCache; /** are we, uh, dead */ - private boolean _dead; + private volatile boolean _dead; /** For outbound traffic. true if i2cp.messageReliability = "none"; @since 0.8.1 */ private boolean _dontSendMSM; private final AtomicInteger _messageId; // messageId counter @@ -463,10 +463,10 @@ class ClientConnectionRunner { } private class Rerequest implements SimpleTimer.TimedEvent { - private LeaseSet _ls; - private long _expirationTime; - private Job _onCreate; - private Job _onFailed; + private final LeaseSet _ls; + private final long _expirationTime; + private final Job _onCreate; + private final Job _onFailed; public Rerequest(LeaseSet ls, long expirationTime, Job onCreate, Job onFailed) { _ls = ls; _expirationTime = expirationTime; diff --git a/router/java/src/net/i2p/router/client/ClientListenerRunner.java b/router/java/src/net/i2p/router/client/ClientListenerRunner.java index 5dc5c650686d47b7425e403614f3e2f4d0c7e848..9b169ca757b820fa648e1dd1a5d5ec98f5fcfbf3 100644 --- a/router/java/src/net/i2p/router/client/ClientListenerRunner.java +++ b/router/java/src/net/i2p/router/client/ClientListenerRunner.java @@ -31,8 +31,8 @@ class ClientListenerRunner implements Runnable { protected ServerSocket _socket; protected final int _port; protected final boolean _bindAllInterfaces; - protected boolean _running; - protected boolean _listening; + protected volatile boolean _running; + protected volatile boolean _listening; public static final String BIND_ALL_INTERFACES = "i2cp.tcp.bindAllInterfaces"; diff --git a/router/java/src/net/i2p/router/client/ClientManager.java b/router/java/src/net/i2p/router/client/ClientManager.java index 681527ae3a0ee833c52b88178b1f2dca2470319d..da55abe2713dfa79137b70b797a461754f16be41 100644 --- a/router/java/src/net/i2p/router/client/ClientManager.java +++ b/router/java/src/net/i2p/router/client/ClientManager.java @@ -45,7 +45,7 @@ class ClientManager { private final HashMap<Destination, ClientConnectionRunner> _runners; // Destination --> ClientConnectionRunner private final Set<ClientConnectionRunner> _pendingRunners; // ClientConnectionRunner for clients w/out a Dest yet private final RouterContext _ctx; - private boolean _isStarted; + private volatile boolean _isStarted; /** Disable external interface, allow internal clients only @since 0.8.3 */ private static final String PROP_DISABLE_EXTERNAL = "i2cp.disableInterface"; diff --git a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java index 022326cb6d263b9e8be097bbc8ec1119cf31e819..5045c6c8b3dd2745fb70585260832ae9806c7ec0 100644 --- a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java +++ b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java @@ -38,7 +38,7 @@ import net.i2p.util.Log; public class ClientManagerFacadeImpl extends ClientManagerFacade implements InternalClientManager { private final Log _log; private ClientManager _manager; - private RouterContext _context; + private final RouterContext _context; /** note that this is different than the property the client side uses, i2cp.tcp.port */ public final static String PROP_CLIENT_PORT = "i2cp.port"; public final static int DEFAULT_PORT = 7654; diff --git a/router/java/src/net/i2p/router/client/ClientWriterRunner.java b/router/java/src/net/i2p/router/client/ClientWriterRunner.java index 86c391ee49538e37dd34d403809c88032b0d3eaf..759e1ef21a14fe7644c97aec8e47af3a094af4e7 100644 --- a/router/java/src/net/i2p/router/client/ClientWriterRunner.java +++ b/router/java/src/net/i2p/router/client/ClientWriterRunner.java @@ -17,10 +17,10 @@ import net.i2p.util.Log; * @author zzz modded to use concurrent */ class ClientWriterRunner implements Runnable { - private BlockingQueue<I2CPMessage> _messagesToWrite; - private ClientConnectionRunner _runner; - private Log _log; - private long _id; + private final BlockingQueue<I2CPMessage> _messagesToWrite; + private final ClientConnectionRunner _runner; + private final Log _log; + private final long _id; private static long __id = 0; public ClientWriterRunner(RouterContext context, ClientConnectionRunner runner) { diff --git a/router/java/src/net/i2p/router/client/CreateSessionJob.java b/router/java/src/net/i2p/router/client/CreateSessionJob.java index 0ee1f177f7dc5e9b434c8998d1bec54cb212f4a9..5b0c103b122b663e351f37791a3c1947d7ab563b 100644 --- a/router/java/src/net/i2p/router/client/CreateSessionJob.java +++ b/router/java/src/net/i2p/router/client/CreateSessionJob.java @@ -24,8 +24,8 @@ import net.i2p.util.Log; * */ class CreateSessionJob extends JobImpl { - private Log _log; - private ClientConnectionRunner _runner; + private final Log _log; + private final ClientConnectionRunner _runner; public CreateSessionJob(RouterContext context, ClientConnectionRunner runner) { super(context); diff --git a/router/java/src/net/i2p/router/client/LeaseRequestState.java b/router/java/src/net/i2p/router/client/LeaseRequestState.java index 7e2a248ac948481129ceedd6890755f6f22b33ec..50f4003ca1c9a36d93ce8dbbe5990fee2af0ee73 100644 --- a/router/java/src/net/i2p/router/client/LeaseRequestState.java +++ b/router/java/src/net/i2p/router/client/LeaseRequestState.java @@ -20,12 +20,12 @@ import net.i2p.router.Job; */ class LeaseRequestState { private LeaseSet _grantedLeaseSet; - private LeaseSet _requestedLeaseSet; - private PrivateKey _leaseSetPrivateKey; - private SigningPrivateKey _leaseSetSigningPrivateKey; - private Job _onGranted; - private Job _onFailed; - private long _expiration; + private final LeaseSet _requestedLeaseSet; + //private PrivateKey _leaseSetPrivateKey; + //private SigningPrivateKey _leaseSetSigningPrivateKey; + private final Job _onGranted; + private final Job _onFailed; + private final long _expiration; private boolean _successful; public LeaseRequestState(Job onGranted, Job onFailed, long expiration, LeaseSet requested) { @@ -35,26 +35,34 @@ class LeaseRequestState { _requestedLeaseSet = requested; } - /** created lease set from client */ + /** created lease set from client - FIXME always null */ public LeaseSet getGranted() { return _grantedLeaseSet; } + /** FIXME unused - why? */ public void setGranted(LeaseSet ls) { _grantedLeaseSet = ls; } + /** lease set that is being requested */ public LeaseSet getRequested() { return _requestedLeaseSet; } - public void setRequested(LeaseSet ls) { _requestedLeaseSet = ls; } + //public void setRequested(LeaseSet ls) { _requestedLeaseSet = ls; } + /** the private encryption key received regarding the lease set */ - public PrivateKey getPrivateKey() { return _leaseSetPrivateKey; } - public void getPrivateKey(PrivateKey pk) { _leaseSetPrivateKey = pk; } + //public PrivateKey getPrivateKey() { return _leaseSetPrivateKey; } + //public void setPrivateKey(PrivateKey pk) { _leaseSetPrivateKey = pk; } + /** the private signing key received regarding the lease set (for revocation) */ - public SigningPrivateKey getSigningPrivateKey() { return _leaseSetSigningPrivateKey; } - public void getSigningPrivateKey(SigningPrivateKey spk) { _leaseSetSigningPrivateKey = spk; } + //public SigningPrivateKey getSigningPrivateKey() { return _leaseSetSigningPrivateKey; } + //public void setSigningPrivateKey(SigningPrivateKey spk) { _leaseSetSigningPrivateKey = spk; } + /** what to do once the lease set is created */ public Job getOnGranted() { return _onGranted; } - public void setOnGranted(Job jb) { _onGranted = jb; } + //public void setOnGranted(Job jb) { _onGranted = jb; } + /** what to do if the lease set create fails / times out */ public Job getOnFailed() { return _onFailed; } - public void setOnFailed(Job jb) { _onFailed = jb; } + //public void setOnFailed(Job jb) { _onFailed = jb; } + /** when the request for the lease set expires */ public long getExpiration() { return _expiration; } + /** whether the request was successful in the time allotted */ public boolean getIsSuccessful() { return _successful; } public void setIsSuccessful(boolean is) { _successful = is; } diff --git a/router/java/src/net/i2p/router/client/LookupDestJob.java b/router/java/src/net/i2p/router/client/LookupDestJob.java index 7db546e91a11489fbe951781eb9c9e8451d36a4f..017401569a0346c5cdeb762ec9b7d4289d44bc6a 100644 --- a/router/java/src/net/i2p/router/client/LookupDestJob.java +++ b/router/java/src/net/i2p/router/client/LookupDestJob.java @@ -16,8 +16,8 @@ import net.i2p.router.RouterContext; * Look up the lease of a hash, to convert it to a Destination for the client */ class LookupDestJob extends JobImpl { - private ClientConnectionRunner _runner; - private Hash _hash; + private final ClientConnectionRunner _runner; + private final Hash _hash; public LookupDestJob(RouterContext context, ClientConnectionRunner runner, Hash h) { super(context); diff --git a/router/java/src/net/i2p/router/client/MessageReceivedJob.java b/router/java/src/net/i2p/router/client/MessageReceivedJob.java index 843ebfaceacf7b276840ca95acce2cb566069866..92d4cb5355042d16110d246c84fab2403a7fde85 100644 --- a/router/java/src/net/i2p/router/client/MessageReceivedJob.java +++ b/router/java/src/net/i2p/router/client/MessageReceivedJob.java @@ -22,9 +22,9 @@ import net.i2p.util.Log; * */ class MessageReceivedJob extends JobImpl { - private Log _log; - private ClientConnectionRunner _runner; - private Payload _payload; + private final Log _log; + private final ClientConnectionRunner _runner; + private final Payload _payload; public MessageReceivedJob(RouterContext ctx, ClientConnectionRunner runner, Destination toDest, Destination fromDest, Payload payload) { super(ctx); _log = ctx.logManager().getLog(MessageReceivedJob.class); diff --git a/router/java/src/net/i2p/router/client/ReportAbuseJob.java b/router/java/src/net/i2p/router/client/ReportAbuseJob.java index abf57e2152e0ba0f751d14c3050a260474375939..8dd36ac3cad98fb19261f39a63696d5f0372be6f 100644 --- a/router/java/src/net/i2p/router/client/ReportAbuseJob.java +++ b/router/java/src/net/i2p/router/client/ReportAbuseJob.java @@ -21,10 +21,10 @@ import net.i2p.util.Log; * */ class ReportAbuseJob extends JobImpl { - private Log _log; - private ClientConnectionRunner _runner; - private String _reason; - private int _severity; + private final Log _log; + private final ClientConnectionRunner _runner; + private final String _reason; + private final int _severity; public ReportAbuseJob(RouterContext context, ClientConnectionRunner runner, String reason, int severity) { super(context); _log = context.logManager().getLog(ReportAbuseJob.class); 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 e392e557480ca9d65cf615f36a0a6330b7b7ee21..7fa12be10149bb398233a78d201204f6b7ed1d83 100644 --- a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java +++ b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java @@ -62,7 +62,7 @@ public class Reseeder { public static final String DEFAULT_SEED_URL = "http://netdb.i2p2.de/" + "," + "http://reseed.i2p-projekt.de/" + "," + - "http://forum.i2p2.de/netdb/" + "," + + // "http://forum.i2p2.de/netdb/" + "," + "http://euve5653.vserver.de/netDb/" + "," + // "http://r31453.ovh.net/static_media/files/netDb/" + "," + "http://cowpuncher.drollette.com/netdb/" + "," + @@ -73,7 +73,7 @@ public class Reseeder { /** @since 0.8.2 */ public static final String DEFAULT_SSL_SEED_URL = "https://netdb.i2p2.de/" + "," + - "https://forum.i2p2.de/netdb/" + "," + + // "https://forum.i2p2.de/netdb/" + "," + "https://euve5653.vserver.de/netDb/" + "," + "https://reseed.i2p-projekt.de/" + "," + // "https://r31453.ovh.net/static_media/files/netDb/" + "," + diff --git a/router/java/src/net/i2p/router/startup/StartupJob.java b/router/java/src/net/i2p/router/startup/StartupJob.java index f8344ccd33531f7e2fb2da15f8c328aca7ab0ec2..ec7f47dc6ce975083c41e1ee306a711bf6220407 100644 --- a/router/java/src/net/i2p/router/startup/StartupJob.java +++ b/router/java/src/net/i2p/router/startup/StartupJob.java @@ -33,7 +33,8 @@ public class StartupJob extends JobImpl { public String getName() { return "Startup Router"; } public void runJob() { - getContext().jobQueue().addJob(new LoadClientAppsJob(getContext())); + if (!System.getProperty("java.vendor").contains("Android")) + getContext().jobQueue().addJob(new LoadClientAppsJob(getContext())); getContext().statPublisher().startup(); getContext().jobQueue().addJob(new LoadRouterInfoJob(getContext())); } diff --git a/router/java/src/net/i2p/router/tasks/Spinner.java b/router/java/src/net/i2p/router/tasks/Spinner.java index 99c4f9ef3055cd6fb224d5081ab644937985cd16..f4dfe32171369ff6ae64a6183ec0d78b1ed0a8a7 100644 --- a/router/java/src/net/i2p/router/tasks/Spinner.java +++ b/router/java/src/net/i2p/router/tasks/Spinner.java @@ -17,7 +17,7 @@ public class Spinner extends Thread { @Override public void run() { try { - sleep(60*1000); + sleep(5*60*1000); } catch (InterruptedException ie) {} } } diff --git a/router/java/src/net/i2p/router/transport/ntcp/Reader.java b/router/java/src/net/i2p/router/transport/ntcp/Reader.java index 68480538aaacedfed4676edccbef0600d0cc4e8e..13a4632df846553d18f7214cff6bcf8ddd6b0675 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/Reader.java +++ b/router/java/src/net/i2p/router/transport/ntcp/Reader.java @@ -64,8 +64,9 @@ class Reader { already = true; } else { _pendingConnections.add(con); + // only notify here if added? } - _pendingConnections.notifyAll(); + _pendingConnections.notify(); } if (_log.shouldLog(Log.DEBUG)) _log.debug("wantsRead: " + con + " already live? " + already); @@ -75,7 +76,8 @@ class Reader { synchronized (_pendingConnections) { _readAfterLive.remove(con); _pendingConnections.remove(con); - _pendingConnections.notifyAll(); + // necessary? + _pendingConnections.notify(); } } diff --git a/router/java/src/net/i2p/router/transport/ntcp/Writer.java b/router/java/src/net/i2p/router/transport/ntcp/Writer.java index 80bb26631f2d3c41abdfc92b8c3bcc1495657825..1ed1e7c2199730a17fbf3d6d1b189188f03f040d 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/Writer.java +++ b/router/java/src/net/i2p/router/transport/ntcp/Writer.java @@ -62,8 +62,9 @@ class Writer { already = true; } else { pending = _pendingConnections.add(con); + // only notify here if added? } - _pendingConnections.notifyAll(); + _pendingConnections.notify(); } if (_log.shouldLog(Log.DEBUG)) _log.debug("wantsWrite: " + con + " already live? " + already + " added to pending? " + pending + ": " + source); @@ -73,7 +74,8 @@ class Writer { synchronized (_pendingConnections) { _writeAfterLive.remove(con); _pendingConnections.remove(con); - _pendingConnections.notifyAll(); + // necessary? + _pendingConnections.notify(); } } diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java b/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java index 1a637695dad35d17b86f5bda4b54ec7369b0ac04..4dc7c87faf3aefdc5c5e3926086a07c2204ab039 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java @@ -42,6 +42,8 @@ class OutboundMessageState { private static final int MAX_ENTRIES = 64; /** would two caches, one for small and one for large messages, be better? */ private static final ByteCache _cache = ByteCache.getInstance(MAX_ENTRIES, MAX_MSG_SIZE); + + private static final long EXPIRATION = 10*1000; public OutboundMessageState(I2PAppContext context) { _context = context; @@ -64,6 +66,7 @@ class OutboundMessageState { /** * Called from UDPTransport + * TODO make two constructors, remove this, and make more things final * @return success */ public boolean initialize(I2NPMessage msg, PeerState peer) { @@ -82,6 +85,7 @@ class OutboundMessageState { /** * Called from OutboundMessageFragments + * TODO make two constructors, remove this, and make more things final * @return success */ public boolean initialize(OutNetMessage m, I2NPMessage msg) { @@ -121,7 +125,7 @@ class OutboundMessageState { _startedOn = _context.clock().now(); _nextSendTime = _startedOn; - _expiration = _startedOn + 10*1000; + _expiration = _startedOn + EXPIRATION; //_expiration = msg.getExpiration(); if (_log.shouldLog(Log.DEBUG)) diff --git a/router/java/src/net/i2p/router/transport/udp/PacketPusher.java b/router/java/src/net/i2p/router/transport/udp/PacketPusher.java index 7b4d7f3e24b94ed52cb22499a60a2e99e97fba24..959c7a07c5f0d7b7ecb03469bc9080b3e7b9cebf 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketPusher.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketPusher.java @@ -38,7 +38,7 @@ class PacketPusher implements Runnable { if (packets != null) { for (int i = 0; i < packets.length; i++) { if (packets[i] != null) // null for ACKed fragments - //_sender.add(packets[i], 0); // 0 does not block //100); // blocks for up to 100ms + // BLOCKING if queue is full _sender.add(packets[i]); } } diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState.java b/router/java/src/net/i2p/router/transport/udp/PeerState.java index 96b40db43191a1ba8eb4a70dbe9fe4a3368eba4b..e938599499857ad12a6e2845c346cfdfd938ae5e 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerState.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerState.java @@ -217,6 +217,13 @@ class PeerState { private static final int MINIMUM_WINDOW_BYTES = DEFAULT_SEND_WINDOW_BYTES; private static final int MAX_SEND_WINDOW_BYTES = 1024*1024; + /** + * Was 32 before 0.9.2, but since the streaming lib goes up to 128, + * we would just drop our own msgs right away during slow start. + * May need to adjust based on memory. + */ + private static final int MAX_SEND_MSGS_PENDING = 128; + /* * 596 gives us 588 IP byes, 568 UDP bytes, and with an SSU data message, * 522 fragment bytes, which is enough to send a tunnel data message in 2 @@ -1181,6 +1188,14 @@ class PeerState { RemoteHostId getRemoteHostId() { return _remoteHostId; } + /** + * TODO should this use a queue, separate from the list of msgs pending an ack? + * TODO bring back tail drop? + * TODO priority queue? (we don't implement priorities in SSU now) + * TODO backlog / pushback / block instead of dropping? Can't really block here. + * TODO SSU does not support isBacklogged() now + * @return total pending messages + */ public int add(OutboundMessageState state) { if (_dead) { _transport.failed(state, false); @@ -1193,8 +1208,8 @@ class PeerState { boolean fail = false; synchronized (_outboundMessages) { rv = _outboundMessages.size() + 1; - if (rv > 32) { - // 32 queued messages? to *one* peer? nuh uh. + if (rv > MAX_SEND_MSGS_PENDING) { + // too many queued messages to one peer? nuh uh. fail = true; rv--; @@ -1240,8 +1255,11 @@ class PeerState { _outboundMessages.add(state); } } - if (fail) + if (fail) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Dropping msg, OB queue full for " + toString()); _transport.failed(state, false); + } return rv; } @@ -1278,6 +1296,10 @@ class PeerState { /** * Expire / complete any outbound messages + * High usage - + * OutboundMessageFragments.getNextVolley() calls this 1st. + * TODO combine finishMessages(), allocateSend(), and getNextDelay() so we don't iterate 3 times. + * * @return number of active outbound messages remaining */ public int finishMessages() { @@ -1350,14 +1372,20 @@ class PeerState { /** * Pick a message we want to send and allocate it out of our window - * @return allocated message to send, or null if no messages or no resources + * High usage - + * OutboundMessageFragments.getNextVolley() calls this 2nd, if finishMessages() returned > 0. + * TODO combine finishMessages(), allocateSend(), and getNextDelay() so we don't iterate 3 times. * + * @return allocated message to send, or null if no messages or no resources */ public OutboundMessageState allocateSend() { if (_dead) return null; synchronized (_outboundMessages) { for (OutboundMessageState state : _outboundMessages) { - if (locked_shouldSend(state)) { + // We have 3 return values, because if allocateSendingBytes() returns false, + // then we can stop iterating. + ShouldSend should = locked_shouldSend(state); + if (should == ShouldSend.YES) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Allocate sending to " + _remotePeer + ": " + state.getMessageId()); /* @@ -1369,6 +1397,12 @@ class PeerState { } */ return state; + } else if (should == ShouldSend.NO_BW) { + // no more bandwidth available + // we don't bother looking for a smaller msg that would fit. + // By not looking further, we keep strict sending order, and that allows + // some efficiency in acked() below. + break; } /* else { OutNetMessage msg = state.getMessage(); if (msg != null) @@ -1382,6 +1416,10 @@ class PeerState { } /** + * High usage - + * OutboundMessageFragments.getNextVolley() calls this 3rd, if allocateSend() returned null. + * TODO combine finishMessages(), allocateSend(), and getNextDelay() so we don't iterate 3 times. + * * @return how long to wait before sending, or Integer.MAX_VALUE if we have nothing to send. * If ready now, will return 0 or a negative value. */ @@ -1396,6 +1434,9 @@ class PeerState { } for (OutboundMessageState state : _outboundMessages) { int delay = (int)(state.getNextSendTime() - now); + // short circuit once we hit something ready to go + if (delay <= 0) + return delay; if (delay < rv) rv = delay; } @@ -1435,7 +1476,13 @@ class PeerState { return mtu - (PacketBuilder.MIN_DATA_PACKET_OVERHEAD + MIN_ACK_SIZE); } - private boolean locked_shouldSend(OutboundMessageState state) { + private enum ShouldSend { YES, NO, NO_BW }; + + /** + * Have 3 return values, because if allocateSendingBytes() returns false, + * then allocateSend() can stop iterating + */ + private ShouldSend locked_shouldSend(OutboundMessageState state) { long now = _context.clock().now(); if (state.getNextSendTime() <= now) { if (!state.isFragmented()) { @@ -1465,7 +1512,7 @@ class PeerState { } else if ( (max <= 0) || (THROTTLE_RESENDS) ) { //if (state.getMessage() != null) // state.getMessage().timestamp("choked, with another message retransmitting"); - return false; + return ShouldSend.NO; } else { //if (state.getMessage() != null) // state.getMessage().timestamp("another message is retransmitting, but since we've already begun sending..."); @@ -1491,7 +1538,7 @@ class PeerState { //if (peer.getSendWindowBytesRemaining() > 0) // _throttle.unchoke(peer.getRemotePeer()); - return true; + return ShouldSend.YES; } else { _context.statManager().addRateData("udp.sendRejected", state.getPushCount(), state.getLifetime()); //if (state.getMessage() != null) @@ -1510,15 +1557,16 @@ class PeerState { // state.getMessage().timestamp("choked, not enough available, wsize=" // + getSendWindowBytes() + " available=" // + getSendWindowBytesRemaining()); - return false; + return ShouldSend.NO_BW; } } // nextTime <= now - return false; + return ShouldSend.NO; } /** * A full ACK was received. + * TODO if messages awaiting ack were a HashSet this would be faster. * * @return true if the message was acked for the first time */ @@ -1531,6 +1579,11 @@ class PeerState { if (state.getMessageId() == messageId) { iter.remove(); break; + } else if (state.getPushCount() <= 0) { + // _outboundMessages is ordered, so once we get to a msg that + // hasn't been transmitted yet, we can stop + state = null; + break; } else { state = null; } @@ -1600,6 +1653,11 @@ class PeerState { _retransmitter = null; } break; + } else if (state.getPushCount() <= 0) { + // _outboundMessages is ordered, so once we get to a msg that + // hasn't been transmitted yet, we can stop + state = null; + break; } else { state = null; } diff --git a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java index 9531335b42a19172d0c2a0cbc966f21994c40979..7bdf756f40333b5ee3e2a6a300c7a38790593c7a 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java @@ -136,13 +136,10 @@ class UDPEndpoint { /** * Add the packet to the outobund queue to be sent ASAP (as allowed by * the bandwidth limiter) - * - * @return ZERO (used to be number of packets in the queue) + * BLOCKING if queue is full. */ - public int send(UDPPacket packet) { - if (_sender == null) - return 0; - return _sender.add(packet); + public void send(UDPPacket packet) { + _sender.add(packet); } /** @@ -154,4 +151,12 @@ class UDPEndpoint { return null; return _receiver.receiveNext(); } + + /** + * Clear outbound queue, probably in preparation for sending destroy() to everybody. + * @since 0.9.2 + */ + public void clearOutbound() { + _sender.clear(); + } } diff --git a/router/java/src/net/i2p/router/transport/udp/UDPSender.java b/router/java/src/net/i2p/router/transport/udp/UDPSender.java index df017fedfc85c0f399da1e08a67013522a6d2cbd..f8eaaf5f85c939b0ba8fe29bbcfe9bc33a346827 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPSender.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPSender.java @@ -25,12 +25,17 @@ class UDPSender { private final Runner _runner; private static final int TYPE_POISON = 99999; - //private static final int MAX_QUEUED = 4; + private static final int MIN_QUEUE_SIZE = 64; + private static final int MAX_QUEUE_SIZE = 384; public UDPSender(RouterContext ctx, DatagramSocket socket, String name) { _context = ctx; _log = ctx.logManager().getLog(UDPSender.class); - _outboundQueue = new LinkedBlockingQueue(); + long maxMemory = Runtime.getRuntime().maxMemory(); + if (maxMemory == Long.MAX_VALUE) + maxMemory = 96*1024*1024l; + int qsize = (int) Math.max(MIN_QUEUE_SIZE, Math.min(MAX_QUEUE_SIZE, maxMemory / (1024*1024))); + _outboundQueue = new LinkedBlockingQueue(qsize); _socket = socket; _runner = new Runner(); _name = name; @@ -81,6 +86,14 @@ class UDPSender { _outboundQueue.clear(); } + /** + * Clear outbound queue, probably in preparation for sending destroy() to everybody. + * @since 0.9.2 + */ + public void clear() { + _outboundQueue.clear(); + } + /********* public DatagramSocket updateListeningPort(DatagramSocket socket, int newPort) { return _runner.updateListeningPort(socket, newPort); @@ -93,10 +106,9 @@ class UDPSender { * available, if requested, otherwise it returns immediately * * @param blockTime how long to block IGNORED - * @return ZERO (used to be number of packets in the queue) * @deprecated use add(packet) */ - public int add(UDPPacket packet, int blockTime) { + public void add(UDPPacket packet, int blockTime) { /******** //long expiration = _context.clock().now() + blockTime; int remaining = -1; @@ -148,31 +160,32 @@ class UDPSender { _log.debug("Added the packet onto the queue with " + remaining + " remaining and a lifetime of " + lifetime); return remaining; ********/ - return add(packet); + add(packet); } - private static final int MAX_HEAD_LIFETIME = 1000; + private static final int MAX_HEAD_LIFETIME = 3*1000; /** - * Put it on the queue - * @return ZERO (used to be number of packets in the queue) + * Put it on the queue. + * BLOCKING if queue is full (backs up PacketPusher thread) */ - public int add(UDPPacket packet) { - if (packet == null || !_keepRunning) return 0; - int size = 0; + public void add(UDPPacket packet) { + if (packet == null || !_keepRunning) return; int psz = packet.getPacket().getLength(); if (psz > PeerState.LARGE_MTU) { _log.error("Dropping large UDP packet " + psz + " bytes: " + packet); - return 0; + return; + } + try { + _outboundQueue.put(packet); + } catch (InterruptedException ie) { + return; } - _outboundQueue.offer(packet); //size = _outboundQueue.size(); //_context.statManager().addRateData("udp.sendQueueSize", size, lifetime); if (_log.shouldLog(Log.DEBUG)) { - size = _outboundQueue.size(); - _log.debug("Added the packet onto the queue with " + size + " remaining and a lifetime of " + packet.getLifetime()); + _log.debug("Added the packet onto the queue with a lifetime of " + packet.getLifetime()); } - return size; } private class Runner implements Runnable { 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 970801cb6f2d3cccbf5f80c00afbb07060d7bede..20f08eb412a7c8f6aaa8823eadcc8bcb9598870c 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -1119,17 +1119,17 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority /** * This sends it directly out, bypassing OutboundMessageFragments * and the PacketPusher. The only queueing is for the bandwidth limiter. - * - * @return ZERO (used to be number of packets in the queue) + * BLOCKING if OB queue is full. */ - int send(UDPPacket packet) { + void send(UDPPacket packet) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending packet " + packet); - return _endpoint.send(packet); + _endpoint.send(packet); } /** * Send a session destroy message, bypassing OMF and PacketPusher. + * BLOCKING if OB queue is full. * * @since 0.8.9 */ @@ -1145,10 +1145,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority /** * Send a session destroy message to everybody + * BLOCKING if OB queue is full. * * @since 0.8.9 */ private void destroyAll() { + _endpoint.clearOutbound(); int howMany = _peersByIdent.size(); if (_log.shouldLog(Log.WARN)) _log.warn("Sending destroy to : " + howMany + " peers"); diff --git a/router/java/src/net/i2p/router/util/RandomIterator.java b/router/java/src/net/i2p/router/util/RandomIterator.java index b7da15e9b779d590daaac4118842c4a2e36aa456..87ca7c61066519771e96dfcca7b041068f3a950f 100644 --- a/router/java/src/net/i2p/router/util/RandomIterator.java +++ b/router/java/src/net/i2p/router/util/RandomIterator.java @@ -6,7 +6,7 @@ package net.i2p.router.util; * No license, free to use */ -//import java.util.ArrayList; +import java.util.ArrayList; import java.util.BitSet; import java.util.List; import java.util.Iterator; @@ -89,6 +89,13 @@ public class RandomIterator<E> implements Iterator<E> { /** Used to narrow the range to take random indexes from */ private int lower, upper; + private static final boolean isAndroid = System.getProperty("java.vendor").contains("Android"); + + static { + if (isAndroid) + testAndroid(); + } + public RandomIterator(List<E> list){ this.list = list; LIST_SIZE = list.size(); @@ -129,9 +136,10 @@ public class RandomIterator<E> implements Iterator<E> { // I2P - ensure lower and upper are always clear if (hasNext()) { if (index == lower) - lower = served.nextClearBit(lower); + // workaround for Android ICS bug - see below + lower = isAndroid ? nextClearBit(index) : served.nextClearBit(index); else if (index == upper) - upper = previousClearBit(upper - 1); + upper = previousClearBit(index - 1); } return list.get(index); } @@ -146,6 +154,20 @@ public class RandomIterator<E> implements Iterator<E> { return -1; } + /** + * Workaround for bug in Android (ICS only?) + * http://code.google.com/p/android/issues/detail?id=31036 + * @since 0.9.2 + */ + private int nextClearBit(int n) { + for (int i = n; i <= upper; i++) { + if (!served.get(i)) { + return i; + } + } + return -1; + } + /** * @throws UnsupportedOperationException always */ @@ -153,19 +175,16 @@ public class RandomIterator<E> implements Iterator<E> { throw new UnsupportedOperationException(); } -/***** public static void main(String[] args) { - System.out.println("\n testing with 0"); + testAndroid(); 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) { + private static void test(int n) { + System.out.println("testing with " + n); List<Integer> l = new ArrayList(n); for (int i = 0; i < n; i++) { l.add(Integer.valueOf(i)); @@ -174,5 +193,23 @@ public class RandomIterator<E> implements Iterator<E> { System.out.println(iter.next().toString()); } } -*****/ + + /** + * Test case from android ticket above + * @since 0.9.2 + */ + private static void testAndroid() { + System.out.println("checking for Android bug"); + BitSet theBitSet = new BitSet(864); + for (int exp =0; exp < 864; exp++) { + int act = theBitSet.nextClearBit(0); + if (exp != act) { + System.err.println(String.format("Test failed for: exp=%d, act=%d", exp, act)); + System.err.println("Android BitSet bug detected, workaround implemented!"); + return; + } + theBitSet.set(exp); + } + System.err.println("Android BitSet bug NOT detected, no workaround needed!"); + } }