Methods Summary |
---|
public com.aelitis.azureus.core.dht.router.DHTRouterContact | addContact(byte[] node_id, com.aelitis.azureus.core.dht.router.DHTRouterContactAttachment attachment, boolean known_to_be_alive)
try{
try{
this_mon.enter();
if ( known_to_be_alive ){
consecutive_dead = 0;
}
return( addContactSupport( node_id, attachment, known_to_be_alive ));
}finally{
this_mon.exit();
}
}finally{
dispatchPings();
dispatchNodeAdds();
}
|
protected com.aelitis.azureus.core.dht.router.DHTRouterContact | addContactSupport(byte[] node_id, com.aelitis.azureus.core.dht.router.DHTRouterContactAttachment attachment, boolean known_to_be_alive)
DHTRouterNodeImpl current_node = root;
boolean part_of_smallest_subtree = false;
for (int i=0;i<node_id.length;i++){
byte b = node_id[i];
int j = 7;
while( j >= 0 ){
if ( current_node == smallest_subtree ){
part_of_smallest_subtree = true;
}
boolean bit = ((b>>j)&0x01)==1?true:false;
DHTRouterNodeImpl next_node;
if ( bit ){
next_node = current_node.getLeft();
}else{
next_node = current_node.getRight();
}
if ( next_node == null ){
DHTRouterContact existing_contact = current_node.updateExistingNode( node_id, attachment, known_to_be_alive );
if ( existing_contact != null ){
return( existing_contact );
}
List buckets = current_node.getBuckets();
if ( buckets.size() == K ){
// split if either
// 1) this list contains router_node_id or
// 2) depth % B is not 0
// 3) this is part of the smallest subtree
boolean contains_router_node_id = current_node.containsRouterNodeID();
int depth = current_node.getDepth();
boolean too_deep_to_split = depth % B == 0; // note this will be true for 0 but other
// conditions will allow the split
if ( contains_router_node_id ||
(!too_deep_to_split) ||
part_of_smallest_subtree ){
// the smallest-subtree bit is to ensure that we remember all of
// our closest neighbours as ultimately they are the ones responsible
// for returning our identity to queries (due to binary choppery in
// general the query will home in on our neighbours before
// hitting us. It is therefore important that we keep ourselves live
// in their tree by refreshing. If we blindly chopped at K entries
// (down to B levels) then a highly unbalanced tree would result in
// us dropping some of them and therefore not refreshing them and
// therefore dropping out of their trees. There are also other benefits
// of maintaining this tree regarding stored value refresh
// Note that it is rare for such an unbalanced tree.
// However, a possible DOS here would be for a rogue node to
// deliberately try and create such a tree with a large number
// of entries.
if ( part_of_smallest_subtree &&
too_deep_to_split &&
( !contains_router_node_id ) &&
getContactCount( smallest_subtree ) > smallest_subtree_max ){
Debug.out( "DHTRouter: smallest subtree max size violation" );
return( null );
}
// split!!!!
List left_buckets = new ArrayList();
List right_buckets = new ArrayList();
for (int k=0;k<buckets.size();k++){
DHTRouterContactImpl contact = (DHTRouterContactImpl)buckets.get(k);
byte[] bucket_id = contact.getID();
if (((bucket_id[depth/8]>>(7-(depth%8)))&0x01 ) == 0 ){
right_buckets.add( contact );
}else{
left_buckets.add( contact );
}
}
boolean right_contains_rid = false;
boolean left_contains_rid = false;
if ( contains_router_node_id ){
right_contains_rid =
((router_node_id[depth/8]>>(7-(depth%8)))&0x01 ) == 0;
left_contains_rid = !right_contains_rid;
}
DHTRouterNodeImpl new_left = new DHTRouterNodeImpl( this, depth+1, left_contains_rid, left_buckets );
DHTRouterNodeImpl new_right = new DHTRouterNodeImpl( this, depth+1, right_contains_rid, right_buckets );
current_node.split( new_left, new_right );
if ( right_contains_rid ){
// we've created a new smallest subtree
// TODO: tidy up old smallest subtree - remember to factor in B...
smallest_subtree = new_left;
}else if ( left_contains_rid ){
// TODO: tidy up old smallest subtree - remember to factor in B...
smallest_subtree = new_right;
}
// not complete, retry addition
}else{
// split not appropriate, add as a replacemnet
DHTRouterContactImpl new_contact = new DHTRouterContactImpl( node_id, attachment, known_to_be_alive );
return( current_node.addReplacement( new_contact, max_rep_per_node ));
}
}else{
// bucket space free, just add it
DHTRouterContactImpl new_contact = new DHTRouterContactImpl( node_id, attachment, known_to_be_alive );
current_node.addNode( new_contact ); // complete - added to bucket
return( new_contact );
}
}else{
current_node = next_node;
j--;
}
}
}
Debug.out( "DHTRouter inconsistency" );
return( null );
|
public boolean | addObserver(com.aelitis.azureus.core.dht.router.DHTRouterObserver rto)
if ((rto != null) && !observers.contains(rto)) {
observers.add(rto);
return true;
}
return false;
|
public com.aelitis.azureus.core.dht.router.DHTRouterContact | contactAlive(byte[] node_id, com.aelitis.azureus.core.dht.router.DHTRouterContactAttachment attachment)
return( addContact( node_id, attachment, true ));
|
public com.aelitis.azureus.core.dht.router.DHTRouterContact | contactDead(byte[] node_id, boolean force)
if ( Arrays.equals( router_node_id, node_id )){
// we should never become dead ourselves as this screws up things like
// checking that stored values are close enough to the K livest nodes (as if we are
// dead we don't return ourselves and it all goes doo daa )
Debug.out( "DHTRouter: contactDead called on router node!" );
return( local_contact );
}
try{
try{
this_mon.enter();
consecutive_dead++;
/*
if ( consecutive_dead != 0 && consecutive_dead % 10 == 0 ){
System.out.println( "consecutive_dead: " + consecutive_dead );
}
*/
Object[] res = findContactSupport( node_id );
DHTRouterNodeImpl node = (DHTRouterNodeImpl)res[0];
DHTRouterContactImpl contact = (DHTRouterContactImpl)res[1];
if ( contact != null ){
// some protection against network drop outs - start ignoring dead
// notifications if we're getting significant continous fails
if ( consecutive_dead < 100 || force ){
node.dead( contact, force );
}
}
return( contact );
}finally{
this_mon.exit();
}
}finally{
dispatchPings();
dispatchNodeAdds();
}
|
public com.aelitis.azureus.core.dht.router.DHTRouterContact | contactKnown(byte[] node_id, com.aelitis.azureus.core.dht.router.DHTRouterContactAttachment attachment)
return( addContact( node_id, attachment, false ));
|
public void | contactRemoved(byte[] node_id)
|
public boolean | containsObserver(com.aelitis.azureus.core.dht.router.DHTRouterObserver rto)
return ((rto != null) && observers.contains(rto));
|
public void | destroy()
notifyDead();
|
protected void | dispatchNodeAdds()
if ( outstanding_adds.size() == 0 ){
return;
}
List adds;
try{
this_mon.enter();
adds = outstanding_adds;
outstanding_adds = new ArrayList();
}finally{
this_mon.exit();
}
for (int i=0;i<adds.size();i++){
adapter.requestAdd((DHTRouterContactImpl)adds.get(i));
}
|
protected void | dispatchPings()
if ( outstanding_pings.size() == 0 ){
return;
}
List pings;
try{
this_mon.enter();
pings = outstanding_pings;
outstanding_pings = new ArrayList();
}finally{
this_mon.exit();
}
for (int i=0;i<pings.size();i++){
adapter.requestPing((DHTRouterContactImpl)pings.get(i));
}
|
protected void | findAllContacts(java.util.Set set, DHTRouterNodeImpl node)
List buckets = node.getBuckets();
if ( buckets == null ){
findAllContacts( set, node.getLeft());
findAllContacts( set, node.getRight());
}else{
for (int i=0;i<buckets.size();i++){
DHTRouterContactImpl contact = (DHTRouterContactImpl)buckets.get(i);
set.add( contact );
}
}
|
protected void | findAllContacts(java.util.List list, DHTRouterNodeImpl node)
List buckets = node.getBuckets();
if ( buckets == null ){
findAllContacts( list, node.getLeft());
findAllContacts( list, node.getRight());
}else{
for (int i=0;i<buckets.size();i++){
DHTRouterContactImpl contact = (DHTRouterContactImpl)buckets.get(i);
list.add( contact );
}
}
|
public java.util.List | findBestContacts(int max)
Set set =
new TreeSet(
new Comparator()
{
public int
compare(
Object o1,
Object o2 )
{
DHTRouterContactImpl c1 = (DHTRouterContactImpl)o1;
DHTRouterContactImpl c2 = (DHTRouterContactImpl)o2;
return((int)( c2.getTimeAlive() - c1.getTimeAlive()));
}
});
try{
this_mon.enter();
findAllContacts( set, root );
}finally{
this_mon.exit();
}
List result = new ArrayList( max );
Iterator it = set.iterator();
while( it.hasNext() && (max <= 0 || result.size() < max )){
result.add( it.next());
}
return( result );
|
public java.util.List | findClosestContacts(byte[] node_id, boolean live_only)
// find the K-ish closest nodes - consider all buckets, not just the closest
try{
this_mon.enter();
List res = new ArrayList();
findClosestContacts( node_id, 0, root, live_only, res );
return( res );
}finally{
this_mon.exit();
}
|
protected void | findClosestContacts(byte[] node_id, int depth, DHTRouterNodeImpl current_node, boolean live_only, java.util.List res)
List buckets = current_node.getBuckets();
if ( buckets != null ){
// add everything from the buckets - caller will sort and select
// the best ones as required
for (int i=0;i<buckets.size();i++){
DHTRouterContactImpl contact = (DHTRouterContactImpl)buckets.get(i);
// use !failing at the moment to include unknown ones
if ( ! ( live_only && contact.isFailing())){
res.add( contact );
}
}
}else{
boolean bit = ((node_id[depth/8]>>(7-(depth%8)))&0x01 ) == 1;
DHTRouterNodeImpl best_node;
DHTRouterNodeImpl worse_node;
if ( bit ){
best_node = current_node.getLeft();
worse_node = current_node.getRight();
}else{
best_node = current_node.getRight();
worse_node = current_node.getLeft();
}
findClosestContacts( node_id, depth+1, best_node, live_only, res );
if ( res.size() < K ){
findClosestContacts( node_id, depth+1, worse_node, live_only, res );
}
}
|
public com.aelitis.azureus.core.dht.router.DHTRouterContact | findContact(byte[] node_id)
Object[] res = findContactSupport( node_id );
return((DHTRouterContact)res[1]);
|
protected java.lang.Object[] | findContactSupport(byte[] node_id)
try{
this_mon.enter();
DHTRouterNodeImpl current_node = root;
for (int i=0;i<node_id.length;i++){
if ( current_node.getBuckets() != null ){
break;
}
byte b = node_id[i];
int j = 7;
while( j >= 0 ){
boolean bit = ((b>>j)&0x01)==1?true:false;
if ( current_node.getBuckets() != null ){
break;
}
if ( bit ){
current_node = current_node.getLeft();
}else{
current_node = current_node.getRight();
}
j--;
}
}
List buckets = current_node.getBuckets();
for (int k=0;k<buckets.size();k++){
DHTRouterContactImpl contact = (DHTRouterContactImpl)buckets.get(k);
if ( Arrays.equals(node_id, contact.getID())){
return( new Object[]{ current_node, contact });
}
}
return( new Object[]{ current_node, null });
}finally{
this_mon.exit();
}
|
protected DHTRouterNodeImpl | findNode(byte[] node_id)
Object[] res = findContactSupport( node_id );
return((DHTRouterNodeImpl)res[0]);
|
public java.util.List | getAllContacts()
try{
this_mon.enter();
List l = new ArrayList();
findAllContacts( l, root );
return( l );
}finally{
this_mon.exit();
}
|
protected long | getContactCount()
return( getContactCount( root ));
|
protected long | getContactCount(DHTRouterNodeImpl node)
if ( node.getBuckets() != null ){
return( node.getBuckets().size());
}else{
return( getContactCount( node.getLeft())) + getContactCount( node.getRight());
}
|
public byte[] | getID()
return( router_node_id );
|
public int | getK()
return( K );
|
public com.aelitis.azureus.core.dht.router.DHTRouterContact | getLocalContact()
return( local_contact );
|
protected long | getNodeCount()
return( getNodeCount( root ));
|
protected long | getNodeCount(DHTRouterNodeImpl node)
if ( node.getBuckets() != null ){
return( 1 );
}else{
return( 1 + getNodeCount( node.getLeft())) + getNodeCount( node.getRight());
}
|
protected DHTRouterNodeImpl | getSmallestSubtree()
return( smallest_subtree );
|
public com.aelitis.azureus.core.dht.router.DHTRouterStats | getStats()
return( stats );
|
protected void | getStatsSupport(long[] stats_array, DHTRouterNodeImpl node)
stats_array[DHTRouterStats.ST_NODES]++;
List buckets = node.getBuckets();
if ( buckets == null ){
getStatsSupport( stats_array, node.getLeft());
getStatsSupport( stats_array, node.getRight());
}else{
stats_array[DHTRouterStats.ST_LEAVES]++;
stats_array[DHTRouterStats.ST_CONTACTS] += buckets.size();
for (int i=0;i<buckets.size();i++){
DHTRouterContactImpl contact = (DHTRouterContactImpl)buckets.get(i);
if ( contact.getFirstFailTime() > 0 ){
stats_array[DHTRouterStats.ST_CONTACTS_DEAD]++;
}else if ( contact.hasBeenAlive()){
stats_array[DHTRouterStats.ST_CONTACTS_LIVE]++;
}else{
stats_array[DHTRouterStats.ST_CONTACTS_UNKNOWN]++;
}
}
List rep = node.getReplacements();
if ( rep != null ){
stats_array[DHTRouterStats.ST_REPLACEMENTS] += rep.size();
}
}
|
protected long[] | getStatsSupport()
/* number of nodes
* number of leaves
* number of contacts
* number of replacements
* number of live contacts
* number of unknown contacts
* number of dying contacts
*/
try{
this_mon.enter();
long[] res = new long[7];
getStatsSupport( res, root );
return( res );
}finally{
this_mon.exit();
}
|
public boolean | isID(byte[] id)
return( Arrays.equals( id, router_node_id ));
|
protected void | log(java.lang.String str)
logger.log( str );
|
protected void | notifyAdded(com.aelitis.azureus.core.dht.router.DHTRouterContact contact)
for (Iterator i = observers.iterator(); i.hasNext(); ) {
DHTRouterObserver rto = (DHTRouterObserver) i.next();
try{
rto.added(contact);
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
|
protected void | notifyDead()
for (Iterator i = observers.iterator(); i.hasNext(); ) {
DHTRouterObserver rto = (DHTRouterObserver) i.next();
try{
rto.destroyed(this);
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
|
protected void | notifyLocationChanged(com.aelitis.azureus.core.dht.router.DHTRouterContact contact)
for (Iterator i = observers.iterator(); i.hasNext(); ) {
DHTRouterObserver rto = (DHTRouterObserver) i.next();
try{
rto.locationChanged(contact);
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
|
protected void | notifyNowAlive(com.aelitis.azureus.core.dht.router.DHTRouterContact contact)
for (Iterator i = observers.iterator(); i.hasNext(); ) {
DHTRouterObserver rto = (DHTRouterObserver) i.next();
try{
rto.nowAlive(contact);
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
|
protected void | notifyNowFailing(com.aelitis.azureus.core.dht.router.DHTRouterContact contact)
for (Iterator i = observers.iterator(); i.hasNext(); ) {
DHTRouterObserver rto = (DHTRouterObserver) i.next();
try{
rto.nowFailing(contact);
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
|
protected void | notifyRemoved(com.aelitis.azureus.core.dht.router.DHTRouterContact contact)
for (Iterator i = observers.iterator(); i.hasNext(); ) {
DHTRouterObserver rto = (DHTRouterObserver) i.next();
try{
rto.removed(contact);
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
|
public void | print()
try{
this_mon.enter();
log( "DHT: " + DHTLog.getString2(router_node_id) + ", node count = " + getNodeCount()+ ", contacts =" + getContactCount());
root.print( "", "" );
}finally{
this_mon.exit();
}
|
public void | recordLookup(byte[] node_id)
findNode( node_id ).setLastLookupTime();
|
public void | refreshIdleLeaves(long idle_max)
// while we are synchronously refreshing the smallest subtree the tree can mutate underneath us
// as new contacts are discovered. We NEVER merge things back together
byte[] path = new byte[router_node_id.length];
List ids = new ArrayList();
try{
this_mon.enter();
refreshNodes( ids, root, path, false, idle_max );
}finally{
this_mon.exit();
}
for (int i=0;i<ids.size();i++){
requestLookup((byte[])ids.get(i), "Idle leaf refresh" );
}
|
protected void | refreshNode(java.util.List nodes_to_refresh, DHTRouterNodeImpl node, byte[] path)
// pick a random id in the node's range.
byte[] id = new byte[router_node_id.length];
random.nextBytes( id );
int depth = node.getDepth();
for (int i=0;i<depth;i++){
byte mask = (byte)( 0x01<<(7-(i%8)));
boolean bit = ((path[i/8]>>(7-(i%8)))&0x01 ) == 1;
if ( bit ){
id[i/8] = (byte)( id[i/8] | mask );
}else{
id[i/8] = (byte)( id[i/8] & ~mask );
}
}
nodes_to_refresh.add( id );
|
protected void | refreshNodes(java.util.List nodes_to_refresh, DHTRouterNodeImpl node, byte[] path, boolean seeding, long max_permitted_idle)
// when seeding we don't do the smallest subtree
if ( seeding && node == smallest_subtree ){
return;
}
if ( max_permitted_idle != 0 ){
if ( node.getTimeSinceLastLookup() <= max_permitted_idle ){
return;
}
}
if ( node.getBuckets() != null ){
// and we also don't refresh the bucket containing the router id when seeding
if ( seeding && node.containsRouterNodeID()){
return;
}
refreshNode( nodes_to_refresh, node, path );
}
// synchronous refresh may result in this bucket being split
// so we retest here to refresh sub-buckets as required
if ( node.getBuckets() == null ){
int depth = node.getDepth();
byte mask = (byte)( 0x01<<(7-(depth%8)));
path[depth/8] = (byte)( path[depth/8] | mask );
refreshNodes( nodes_to_refresh, node.getLeft(), path,seeding, max_permitted_idle );
path[depth/8] = (byte)( path[depth/8] & ~mask );
refreshNodes( nodes_to_refresh, node.getRight(), path,seeding, max_permitted_idle );
}
|
public byte[] | refreshRandom()
byte[] id = new byte[router_node_id.length];
random.nextBytes( id );
requestLookup( id, "Random Refresh" );
return( id );
|
public boolean | removeObserver(com.aelitis.azureus.core.dht.router.DHTRouterObserver rto)
return ((rto != null) && observers.remove(rto));
|
protected void | requestLookup(byte[] id, java.lang.String description)
DHTLog.log( "DHTRouter: requestLookup:" + DHTLog.getString( id ));
adapter.requestLookup( id, description );
|
protected void | requestNodeAdd(DHTRouterContactImpl contact)
// make sure we don't do the addition when synchronised
DHTLog.log( "DHTRouter: requestNodeAdd:" + DHTLog.getString( contact.getID()));
if ( contact == local_contact ){
Debug.out( "adding local contact" );
}
try{
this_mon.enter();
if ( !outstanding_adds.contains( contact )){
outstanding_adds.add( contact );
}
}finally{
this_mon.exit();
}
|
public boolean | requestPing(byte[] node_id)
Object[] res = findContactSupport( node_id );
DHTRouterContactImpl contact = (DHTRouterContactImpl)res[1];
if ( contact != null ){
adapter.requestPing( contact );
return( true );
}
return( false );
|
protected void | requestPing(DHTRouterContactImpl contact)
// make sure we don't do the ping when synchronised
DHTLog.log( "DHTRouter: requestPing:" + DHTLog.getString( contact.getID()));
if ( contact == local_contact ){
Debug.out( "pinging local contact" );
}
try{
this_mon.enter();
if ( !outstanding_pings.contains( contact )){
outstanding_pings.add( contact );
}
}finally{
this_mon.exit();
}
|
public void | seed()
// refresh all buckets apart from closest neighbour
byte[] path = new byte[router_node_id.length];
List ids = new ArrayList();
try{
this_mon.enter();
refreshNodes( ids, root, path, true, 0 );
}finally{
this_mon.exit();
}
for (int i=0;i<ids.size();i++){
requestLookup((byte[])ids.get(i), "Seeding DHT" );
}
|
public void | setAdapter(com.aelitis.azureus.core.dht.router.DHTRouterAdapter _adapter)
adapter = _adapter;
|