i2psnark:

- Rework PeerID class so it depends only on peer desthash, since
    peer ID is not available with compact format. Implement deferred
    lookup of destination.
  - Implement compact tracker requests and response handling
    (may not be the final format)
  - Fix Peer hashCode()
  - Java 5
This commit is contained in:
zzz
2010-07-09 16:32:31 +00:00
parent 01ef6baa53
commit c19af4dbcf
7 changed files with 111 additions and 42 deletions

View File

@ -197,11 +197,14 @@ public class I2PSnarkUtil {
/** connect to the given destination */ /** connect to the given destination */
I2PSocket connect(PeerID peer) throws IOException { I2PSocket connect(PeerID peer) throws IOException {
Hash dest = peer.getAddress().calculateHash(); Destination addr = peer.getAddress();
if (addr == null)
throw new IOException("Null address");
Hash dest = addr.calculateHash();
if (_shitlist.contains(dest)) if (_shitlist.contains(dest))
throw new IOException("Not trying to contact " + dest.toBase64() + ", as they are shitlisted"); throw new IOException("Not trying to contact " + dest.toBase64() + ", as they are shitlisted");
try { try {
I2PSocket rv = _manager.connect(peer.getAddress()); I2PSocket rv = _manager.connect(addr);
if (rv != null) if (rv != null)
_shitlist.remove(dest); _shitlist.remove(dest);
return rv; return rv;

View File

@ -129,7 +129,7 @@ public class Peer implements Comparable
@Override @Override
public int hashCode() public int hashCode()
{ {
return peerID.hashCode() ^ (2 << _id); return peerID.hashCode() ^ (7777 * (int)_id);
} }
/** /**
@ -150,6 +150,7 @@ public class Peer implements Comparable
/** /**
* Compares the PeerIDs. * Compares the PeerIDs.
* @deprecated unused?
*/ */
public int compareTo(Object o) public int compareTo(Object o)
{ {
@ -207,12 +208,15 @@ public class Peer implements Comparable
// = new BufferedOutputStream(sock.getOutputStream()); // = new BufferedOutputStream(sock.getOutputStream());
byte [] id = handshake(in, out); //handshake(bis, bos); byte [] id = handshake(in, out); //handshake(bis, bos);
byte [] expected_id = peerID.getID(); byte [] expected_id = peerID.getID();
if (!Arrays.equals(expected_id, id)) if (expected_id == null)
throw new IOException("Unexpected peerID '" peerID.setID(id);
else if (Arrays.equals(expected_id, id))
_log.debug("Handshake got matching IDs with " + toString());
else
throw new IOException("Unexpected peerID '"
+ PeerID.idencode(id) + PeerID.idencode(id)
+ "' expected '" + "' expected '"
+ PeerID.idencode(expected_id) + "'"); + PeerID.idencode(expected_id) + "'");
_log.debug("Handshake got matching IDs with " + toString());
} else { } else {
_log.debug("Already have din [" + sock + "] with " + toString()); _log.debug("Already have din [" + sock + "] with " + toString());
} }

View File

@ -373,7 +373,8 @@ public class PeerCoordinator implements PeerListener
if (need_more) if (need_more)
{ {
_log.debug("Adding a peer " + peer.getPeerID().getAddress().calculateHash().toBase64() + " for " + metainfo.getName(), new Exception("add/run")); if (_log.shouldLog(Log.DEBUG))
_log.debug("Adding a peer " + peer.getPeerID().toString() + " for " + metainfo.getName(), new Exception("add/run"));
// Run the peer with us as listener and the current bitfield. // Run the peer with us as listener and the current bitfield.
final PeerListener listener = this; final PeerListener listener = this;

View File

@ -24,19 +24,33 @@ import java.io.IOException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Map; import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.data.Base32;
import net.i2p.data.Base64; import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import org.klomp.snark.bencode.BDecoder; import org.klomp.snark.bencode.BDecoder;
import org.klomp.snark.bencode.BEValue; import org.klomp.snark.bencode.BEValue;
import org.klomp.snark.bencode.InvalidBEncodingException; import org.klomp.snark.bencode.InvalidBEncodingException;
/**
* Store the address information about a peer.
* Prior to 0.8.1, an instantiation required a peer ID, and full Destination address.
* Starting with 0.8.1, to support compact tracker responses,
* a PeerID can be instantiated with a Destination Hash alone.
* The full destination lookup is deferred until getAddress() is called,
* and the PeerID is not required.
* Equality is now determined solely by the dest hash.
*/
public class PeerID implements Comparable public class PeerID implements Comparable
{ {
private final byte[] id; private byte[] id;
private final Destination address; private Destination address;
private final int port; private final int port;
private byte[] destHash;
/** whether we have tried to get the dest from the hash - only do once */
private boolean triedDestLookup;
private final int hash; private final int hash;
public PeerID(byte[] id, Destination address) public PeerID(byte[] id, Destination address)
@ -44,7 +58,7 @@ public class PeerID implements Comparable
this.id = id; this.id = id;
this.address = address; this.address = address;
this.port = 6881; this.port = 6881;
this.destHash = address.calculateHash().getData();
hash = calculateHash(); hash = calculateHash();
} }
@ -77,17 +91,49 @@ public class PeerID implements Comparable
throw new InvalidBEncodingException("Invalid destination [" + bevalue.getString() + "]"); throw new InvalidBEncodingException("Invalid destination [" + bevalue.getString() + "]");
port = 6881; port = 6881;
this.destHash = address.calculateHash().getData();
hash = calculateHash(); hash = calculateHash();
} }
/**
* Creates a PeerID from a destHash
* @since 0.8.1
*/
public PeerID(byte[] dest_hash) throws InvalidBEncodingException
{
// id and address remain null
port = 6881;
if (dest_hash.length != 32)
throw new InvalidBEncodingException("bad hash length");
destHash = dest_hash;
hash = DataHelper.hashCode(dest_hash);
}
public byte[] getID() public byte[] getID()
{ {
return id; return id;
} }
public Destination getAddress() /** for connecting out to peer based on desthash @since 0.8.1 */
public void setID(byte[] xid)
{ {
id = xid;
}
/**
* Get the destination.
* If this PeerId was instantiated with a destHash,
* and we have not yet done so, lookup the full destination, which may take
* up to 10 seconds.
* @return Dest or null if unknown
*/
public synchronized Destination getAddress()
{
if (address == null && destHash != null && !triedDestLookup) {
String b32 = Base32.encode(destHash) + ".b32.i2p";
address = I2PAppContext.getGlobalContext().namingService().lookup(b32);
triedDestLookup = true;
}
return address; return address;
} }
@ -96,16 +142,19 @@ public class PeerID implements Comparable
return port; return port;
} }
/** @since 0.8.1 */
public byte[] getDestHash()
{
return destHash;
}
private int calculateHash() private int calculateHash()
{ {
int b = 0; return DataHelper.hashCode(destHash);
for (int i = 0; i < id.length; i++)
b ^= id[i];
return (b ^ address.hashCode()) ^ port;
} }
/** /**
* The hash code of a PeerID is the exclusive or of all id bytes. * The hash code of a PeerID is the hashcode of the desthash
*/ */
@Override @Override
public int hashCode() public int hashCode()
@ -115,18 +164,15 @@ public class PeerID implements Comparable
/** /**
* Returns true if and only if this peerID and the given peerID have * Returns true if and only if this peerID and the given peerID have
* the same 20 bytes as ID. * the same destination hash
*/ */
public boolean sameID(PeerID pid) public boolean sameID(PeerID pid)
{ {
boolean equal = true; return DataHelper.eq(destHash, pid.getDestHash());
for (int i = 0; equal && i < id.length; i++)
equal = id[i] == pid.id[i];
return equal;
} }
/** /**
* Two PeerIDs are equal when they have the same id, address and port. * Two PeerIDs are equal when they have the same dest hash
*/ */
@Override @Override
public boolean equals(Object o) public boolean equals(Object o)
@ -135,9 +181,7 @@ public class PeerID implements Comparable
{ {
PeerID pid = (PeerID)o; PeerID pid = (PeerID)o;
return port == pid.port return sameID(pid);
&& address.equals(pid.address)
&& sameID(pid);
} }
else else
return false; return false;
@ -145,6 +189,7 @@ public class PeerID implements Comparable
/** /**
* Compares port, address and id. * Compares port, address and id.
* @deprecated unused? and will NPE now that address can be null?
*/ */
public int compareTo(Object o) public int compareTo(Object o)
{ {
@ -176,6 +221,8 @@ public class PeerID implements Comparable
@Override @Override
public String toString() public String toString()
{ {
if (id == null || address == null)
return "unkn@" + Base64.encode(destHash).substring(0, 6);
int nonZero = 0; int nonZero = 0;
for (int i = 0; i < id.length; i++) { for (int i = 0; i < id.length; i++) {
if (id[i] != 0) { if (id[i] != 0) {

View File

@ -357,6 +357,7 @@ public class TrackerClient extends I2PAppThread
+ "&uploaded=" + uploaded + "&uploaded=" + uploaded
+ "&downloaded=" + downloaded + "&downloaded=" + downloaded
+ "&left=" + left + "&left=" + left
+ "&compact"
+ ((! event.equals(NO_EVENT)) ? ("&event=" + event) : ""); + ((! event.equals(NO_EVENT)) ? ("&event=" + event) : "");
if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers()) if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
s += "&numwant=0"; s += "&numwant=0";

View File

@ -33,11 +33,17 @@ import org.klomp.snark.bencode.BDecoder;
import org.klomp.snark.bencode.BEValue; import org.klomp.snark.bencode.BEValue;
import org.klomp.snark.bencode.InvalidBEncodingException; import org.klomp.snark.bencode.InvalidBEncodingException;
/**
* The data structure for the tracker response.
* Handles both traditional and compact formats.
* Compact format 1 - a list of hashes - implemented
* Compact format 2 - One big string of concatenated hashes - unimplemented
*/
public class TrackerInfo public class TrackerInfo
{ {
private final String failure_reason; private final String failure_reason;
private final int interval; private final int interval;
private final Set peers; private final Set<Peer> peers;
private int complete; private int complete;
private int incomplete; private int incomplete;
@ -76,6 +82,7 @@ public class TrackerInfo
if (bePeers == null) if (bePeers == null)
peers = Collections.EMPTY_SET; peers = Collections.EMPTY_SET;
else else
// if compact is going to be one big string instead, try/catch here
peers = getPeers(bePeers.getList(), my_id, metainfo); peers = getPeers(bePeers.getList(), my_id, metainfo);
BEValue bev = (BEValue)m.get("complete"); BEValue bev = (BEValue)m.get("complete");
@ -94,33 +101,39 @@ public class TrackerInfo
} }
} }
public static Set getPeers(InputStream in, byte[] my_id, MetaInfo metainfo) /******
public static Set<Peer> getPeers(InputStream in, byte[] my_id, MetaInfo metainfo)
throws IOException throws IOException
{ {
return getPeers(new BDecoder(in), my_id, metainfo); return getPeers(new BDecoder(in), my_id, metainfo);
} }
public static Set getPeers(BDecoder be, byte[] my_id, MetaInfo metainfo) public static Set<Peer> getPeers(BDecoder be, byte[] my_id, MetaInfo metainfo)
throws IOException throws IOException
{ {
return getPeers(be.bdecodeList().getList(), my_id, metainfo); return getPeers(be.bdecodeList().getList(), my_id, metainfo);
} }
******/
public static Set getPeers(List l, byte[] my_id, MetaInfo metainfo) private static Set<Peer> getPeers(List<BEValue> l, byte[] my_id, MetaInfo metainfo)
throws IOException throws IOException
{ {
Set peers = new HashSet(l.size()); Set<Peer> peers = new HashSet(l.size());
Iterator it = l.iterator(); for (BEValue bev : l) {
while (it.hasNext())
{
PeerID peerID; PeerID peerID;
try { try {
peerID = new PeerID(((BEValue)it.next()).getMap()); // Case 1 - non-compact - A list of dictionaries (maps)
peerID = new PeerID(bev.getMap());
} catch (InvalidBEncodingException ibe) { } catch (InvalidBEncodingException ibe) {
// don't let one bad entry spoil the whole list try {
//Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR); // Case 2 - compact - A list of 32-byte binary strings (hashes)
continue; peerID = new PeerID(bev.getBytes());
} catch (InvalidBEncodingException ibe2) {
// don't let one bad entry spoil the whole list
//Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR);
continue;
}
} }
peers.add(new Peer(peerID, my_id, metainfo)); peers.add(new Peer(peerID, my_id, metainfo));
} }
@ -128,7 +141,7 @@ public class TrackerInfo
return peers; return peers;
} }
public Set getPeers() public Set<Peer> getPeers()
{ {
return peers; return peers;
} }

View File

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