forked from I2P_Developers/i2p.i2p
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:
@ -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;
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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";
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user