- Change secure Node ID requirements again

- Protect against null DHT races
- Add message about restarting tunnel when DHT config changes
- Add DHT size to table totals
This commit is contained in:
zzz
2012-08-04 17:11:11 +00:00
parent 4715dbdbd0
commit 280a708afe
7 changed files with 63 additions and 24 deletions

View File

@ -28,6 +28,8 @@ import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
import org.klomp.snark.dht.DHT;
/**
* TimerTask that checks for good/bad up/downloader. Works together
* with the PeerCoordinator to select which Peers get (un)choked.
@ -74,6 +76,7 @@ class PeerCheckerTask implements Runnable
List<Peer> removed = new ArrayList();
int uploadLimit = coordinator.allowedUploaders();
boolean overBWLimit = coordinator.overUpBWLimit();
DHT dht = _util.getDHT();
for (Peer peer : peerList) {
// Remove dying peers
@ -218,8 +221,8 @@ class PeerCheckerTask implements Runnable
if (coordinator.getNeededLength() > 0 || !peer.isCompleted())
peer.keepAlive();
// announce them to local tracker (TrackerClient does this too)
if (_util.getDHT() != null && (_runCount % 5) == 0) {
_util.getDHT().announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash());
if (dht != null && (_runCount % 5) == 0) {
dht.announce(coordinator.getInfoHash(), peer.getPeerID().getDestHash());
}
}
@ -267,8 +270,8 @@ class PeerCheckerTask implements Runnable
}
// announce ourselves to local tracker (TrackerClient does this too)
if (_util.getDHT() != null && (_runCount % 16) == 0) {
_util.getDHT().announce(coordinator.getInfoHash());
if (dht != null && (_runCount % 16) == 0) {
dht.announce(coordinator.getInfoHash());
}
}
}

View File

@ -577,6 +577,8 @@ public class SnarkManager implements Snark.CompleteListener {
addMessage(_("Enabled DHT."));
else
addMessage(_("Disabled DHT."));
if (_util.connected())
addMessage(_("DHT change requires tunnel shutdown and reopen"));
_util.setUseDHT(useDHT);
changed = true;
}

View File

@ -43,6 +43,8 @@ import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer2;
import org.klomp.snark.dht.DHT;
/**
* Informs metainfo tracker of events and gets new peers for peer
* coordinator.
@ -323,8 +325,9 @@ public class TrackerClient implements Runnable {
}
// Local DHT tracker announce
if (_util.getDHT() != null)
_util.getDHT().announce(snark.getInfoHash());
DHT dht = _util.getDHT();
if (dht != null)
dht.announce(snark.getInfoHash());
long uploaded = coordinator.getUploaded();
long downloaded = coordinator.getDownloaded();
@ -372,9 +375,10 @@ public class TrackerClient implements Runnable {
snark.setTrackerSeenPeers(tr.seenPeers);
// pass everybody over to our tracker
if (_util.getDHT() != null) {
dht = _util.getDHT();
if (dht != null) {
for (Peer peer : peers) {
_util.getDHT().announce(snark.getInfoHash(), peer.getPeerID().getDestHash());
dht.announce(snark.getInfoHash(), peer.getPeerID().getDestHash());
}
}
@ -458,20 +462,21 @@ public class TrackerClient implements Runnable {
// Get peers from DHT
// FIXME this needs to be in its own thread
if (_util.getDHT() != null && (meta == null || !meta.isPrivate()) && !stop) {
dht = _util.getDHT();
if (dht != null && (meta == null || !meta.isPrivate()) && !stop) {
int numwant;
if (event.equals(STOPPED_EVENT) || !coordinator.needOutboundPeers())
numwant = 1;
else
numwant = _util.getMaxConnections();
List<Hash> hashes = _util.getDHT().getPeers(snark.getInfoHash(), numwant, 2*60*1000);
List<Hash> hashes = dht.getPeers(snark.getInfoHash(), numwant, 2*60*1000);
if (_log.shouldLog(Log.INFO))
_log.info("Got " + hashes + " from DHT");
// announce ourselves while the token is still good
// FIXME this needs to be in its own thread
if (!stop) {
// announce only to the 1 closest
int good = _util.getDHT().announce(snark.getInfoHash(), 1, 5*60*1000);
int good = dht.announce(snark.getInfoHash(), 1, 5*60*1000);
if (_log.shouldLog(Log.INFO))
_log.info("Sent " + good + " good announces to DHT");
}
@ -548,8 +553,9 @@ public class TrackerClient implements Runnable {
*/
private void unannounce() {
// Local DHT tracker unannounce
if (_util.getDHT() != null)
_util.getDHT().unannounce(snark.getInfoHash());
DHT dht = _util.getDHT();
if (dht != null)
dht.unannounce(snark.getInfoHash());
int i = 0;
for (Tracker tr : trackers) {
if (_util.connected() &&

View File

@ -89,4 +89,9 @@ public interface DHT {
* Stop everything.
*/
public void stop();
/**
* Known nodes, not estimated total network size.
*/
public int size();
}

View File

@ -158,7 +158,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
_qPort = 2555 + ctx.random().nextInt(61111);
_rPort = _qPort + 1;
if (SECURE_NID) {
_myNID = NodeInfo.generateNID(session.getMyDestination().calculateHash(), _qPort);
_myNID = NodeInfo.generateNID(session.getMyDestination().calculateHash(), _qPort, _context.random());
_myID = _myNID.getData();
} else {
_myID = new byte[NID.HASH_LENGTH];
@ -176,6 +176,13 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
///////////////// Public methods
/**
* Known nodes, not estimated total network size.
*/
public int size() {
return _knownNodes.size();
}
/**
* @return The UDP query port
*/

View File

@ -9,6 +9,7 @@ import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.SimpleDataStructure;
import net.i2p.util.RandomSource;
/*
* A Node ID, Hash, and port, and an optional Destination.
@ -139,29 +140,35 @@ class NodeInfo extends SimpleDataStructure {
}
/**
* Generate a secure NID that matches the Hash and port
* Generate a secure NID that matches the Hash and port.
* Rules: First 4 bytes must match Hash.
* Next 2 bytes must match Hash ^ port.
* Remaining bytes may be random.
*
* @throws IllegalArgumentException
*/
public static NID generateNID(Hash h, int p) {
public static NID generateNID(Hash h, int p, RandomSource random) {
byte[] n = new byte[NID.HASH_LENGTH];
System.arraycopy(h.getData(), 0, n, 0, NID.HASH_LENGTH);
n[0] ^= (byte) (p >> 8);
n[1] ^= (byte) p;
System.arraycopy(h.getData(), 0, n, 0, 6);
n[4] ^= (byte) (p >> 8);
n[5] ^= (byte) p;
random.nextBytes(n, 6, NID.HASH_LENGTH - 6);
return new NID(n);
}
/**
* Verify the NID matches the Hash
* @throws IllegalArgumentException
* Verify the NID matches the Hash.
* See generateNID() for requirements.
* @throws IllegalArgumentException on mismatch
*/
private void verify() {
if (!KRPC.SECURE_NID)
return;
byte[] nb = nID.getData();
byte[] hb = hash.getData();
if ((!DataHelper.eq(nb, 2, hb, 2, NID.HASH_LENGTH - 2)) ||
((nb[0] ^ (port >> 8)) & 0xff) != (hb[0] & 0xff) ||
((nb[1] ^ port) & 0xff) != (hb[1] & 0xff))
if ((!DataHelper.eq(nb, 0, hb, 0, 4)) ||
((nb[4] ^ (port >> 8)) & 0xff) != (hb[4] & 0xff) ||
((nb[5] ^ port) & 0xff) != (hb[5] & 0xff))
throw new IllegalArgumentException("NID/Hash mismatch");
}

View File

@ -42,6 +42,7 @@ import org.klomp.snark.SnarkManager;
import org.klomp.snark.Storage;
import org.klomp.snark.Tracker;
import org.klomp.snark.TrackerClient;
import org.klomp.snark.dht.DHT;
import org.mortbay.jetty.servlet.DefaultServlet;
import org.mortbay.resource.Resource;
@ -470,6 +471,14 @@ public class I2PSnarkServlet extends DefaultServlet {
out.write(", ");
out.write(DataHelper.formatSize2(stats[5]) + "B, ");
out.write(ngettext("1 connected peer", "{0} connected peers", (int) stats[4]));
DHT dht = _manager.util().getDHT();
if (dht != null) {
int dhts = dht.size();
if (dhts > 0) {
out.write(", ");
out.write(ngettext("1 DHT peer", "{0} DHT peers", dhts));
}
}
out.write("</th>\n");
if (_manager.util().connected()) {
out.write(" <th align=\"right\">" + formatSize(stats[0]) + "</th>\n" +