Package com.aelitis.azureus.core.peermanager

Source Code of com.aelitis.azureus.core.peermanager.PeerManager$PeerManagerRegistrationImpl

/*
* Created on Jan 20, 2005
* Created by Alon Rohter
* Copyright (C) 2004-2005 Aelitis, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/

package com.aelitis.azureus.core.peermanager;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.*;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.peer.PEPeer;
import org.gudy.azureus2.core3.peer.PEPeerListener;
import org.gudy.azureus2.core3.peer.PEPeerSource;
import org.gudy.azureus2.core3.peer.impl.*;
import org.gudy.azureus2.core3.peer.util.PeerIdentityManager;
import org.gudy.azureus2.core3.torrent.TOTorrentFile;
import org.gudy.azureus2.core3.util.AEGenericCallback;
import org.gudy.azureus2.core3.util.AEMonitor;
import org.gudy.azureus2.core3.util.AEThread2;
import org.gudy.azureus2.core3.util.ByteFormatter;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.HashWrapper;
import org.gudy.azureus2.core3.util.SystemTime;

import com.aelitis.azureus.core.networkmanager.*;
import com.aelitis.azureus.core.networkmanager.impl.IncomingConnectionManager;
import com.aelitis.azureus.core.networkmanager.impl.TransportHelper;
import com.aelitis.azureus.core.peermanager.messaging.*;
import com.aelitis.azureus.core.peermanager.messaging.bittorrent.*;
import com.aelitis.azureus.core.peermanager.piecepicker.util.BitFlags;
import com.aelitis.azureus.core.stats.AzureusCoreStats;
import com.aelitis.azureus.core.stats.AzureusCoreStatsProvider;
import com.aelitis.azureus.core.util.bloom.BloomFilter;
import com.aelitis.azureus.core.util.bloom.BloomFilterFactory;

/**
*
*/
public class PeerManager implements AzureusCoreStatsProvider{
  private static final LogIDs LOGID = LogIDs.PEER;

  private static final PeerManager instance = new PeerManager();

  private static final int  PENDING_TIMEOUT  = 10*1000;

  private static final AEMonitor  timer_mon = new AEMonitor( "PeerManager:timeouts" );
  private static AEThread2  timer_thread;
  private static Set  timer_targets = new HashSet();

  protected static void
  registerForTimeouts(
      PeerManagerRegistrationImpl    reg )
  {
    try{
      timer_mon.enter();

      timer_targets.add( reg );

      if ( timer_thread == null ){

        timer_thread =
          new AEThread2( "PeerManager:timeouts", true )
        {
          public void
          run()
          {
            int  idle_time  = 0;

            while( true ){

              try{
                Thread.sleep( PENDING_TIMEOUT / 2 );

              }catch( Throwable e ){ 
              }

              try{
                timer_mon.enter();

                if ( timer_targets.size() == 0 ){

                  idle_time += PENDING_TIMEOUT / 2;

                  if ( idle_time >= 30*1000 ){

                    timer_thread = null;

                    break;
                  }
                }else{

                  idle_time = 0;

                  Iterator  it = timer_targets.iterator();

                  while( it.hasNext()){

                    PeerManagerRegistrationImpl  registration = (PeerManagerRegistrationImpl)it.next();

                    if ( !registration.timeoutCheck()){

                      it.remove();
                    }
                  }
                }
              }finally{

                timer_mon.exit();
              }
            }
          }
        };

        timer_thread.start();
      }
    }finally{

      timer_mon.exit();
    }
  }

  /**
   * Get the singleton instance of the peer manager.
   * @return the peer manager
   */
  public static PeerManager getSingleton() {  return instance;  }



  private final Map registered_legacy_managers   = new HashMap();
  private final Map  registered_links      = new HashMap();

  private final ByteBuffer legacy_handshake_header;

  private final AEMonitor  managers_mon = new AEMonitor( "PeerManager:managers" );


  private PeerManager() {
    legacy_handshake_header = ByteBuffer.allocate( 20 );
    legacy_handshake_header.put( (byte)BTHandshake.PROTOCOL.length() );
    legacy_handshake_header.put( BTHandshake.PROTOCOL.getBytes() );
    legacy_handshake_header.flip();

    Set  types = new HashSet();

    types.add( AzureusCoreStats.ST_PEER_MANAGER_COUNT );
    types.add( AzureusCoreStats.ST_PEER_MANAGER_PEER_COUNT );
    types.add( AzureusCoreStats.ST_PEER_MANAGER_PEER_SNUBBED_COUNT );
    types.add( AzureusCoreStats.ST_PEER_MANAGER_PEER_STALLED_DISK_COUNT );

    AzureusCoreStats.registerProvider( types, this );

    init();
  }

  public void
  updateStats(
      Set    types,
      Map    values )
  {
    if ( types.contains( AzureusCoreStats.ST_PEER_MANAGER_COUNT )){

      values.put( AzureusCoreStats.ST_PEER_MANAGER_COUNT, new Long( registered_legacy_managers.size()));
    }

    if (   types.contains( AzureusCoreStats.ST_PEER_MANAGER_PEER_COUNT ) ||
        types.contains( AzureusCoreStats.ST_PEER_MANAGER_PEER_SNUBBED_COUNT ) ||
        types.contains( AzureusCoreStats.ST_PEER_MANAGER_PEER_STALLED_DISK_COUNT )){

      long  total_peers         = 0;
      long  total_snubbed_peers      = 0;
      long  total_stalled_pending_load  = 0;

      try{

        managers_mon.enter();

        Iterator  it = registered_legacy_managers.values().iterator();

        while( it.hasNext()){

          List  registrations = (List)it.next();

          Iterator  it2 = registrations.iterator();

          while( it2.hasNext()){

            PeerManagerRegistrationImpl reg = (PeerManagerRegistrationImpl)it2.next();

            PEPeerControl control = reg.getActiveControl();

            if ( control != null ){

              total_peers         += control.getNbPeers();
              total_snubbed_peers      += control.getNbPeersSnubbed();
              total_stalled_pending_load  += control.getNbPeersStalledPendingLoad();
            }
          }
        }
      }finally{

        managers_mon.exit();
      }
      if ( types.contains( AzureusCoreStats.ST_PEER_MANAGER_PEER_COUNT )){

        values.put( AzureusCoreStats.ST_PEER_MANAGER_PEER_COUNT, new Long( total_peers ));
      }
      if ( types.contains( AzureusCoreStats.ST_PEER_MANAGER_PEER_SNUBBED_COUNT )){

        values.put( AzureusCoreStats.ST_PEER_MANAGER_PEER_SNUBBED_COUNT, new Long( total_snubbed_peers ));
      }
      if ( types.contains( AzureusCoreStats.ST_PEER_MANAGER_PEER_STALLED_DISK_COUNT )){

        values.put( AzureusCoreStats.ST_PEER_MANAGER_PEER_STALLED_DISK_COUNT, new Long( total_stalled_pending_load ));
      }
    }
  }

  protected void
  init()
  {
    MessageManager.getSingleton().initialize()//ensure it gets initialized

    NetworkManager.ByteMatcher matcher =
      new NetworkManager.ByteMatcher()
    {
      public int matchThisSizeOrBigger(){  return( 48 );}
      public int maxSize() {  return 48}
      public int minSize() { return 20; }

      public Object
      matches(
          TransportHelper    transport,
          ByteBuffer       to_compare,
          int         port )
      {
        InetSocketAddress  address = transport.getAddress();

        int old_limit = to_compare.limit();
        int old_position = to_compare.position();

        to_compare.limit( old_position + 20 );

        PeerManagerRegistrationImpl  routing_data = null;

        if( to_compare.equals( legacy_handshake_header ) ) {  //compare header
          to_compare.limit( old_position + 48 );
          to_compare.position( old_position + 28 );

          byte[]  hash = new byte[to_compare.remaining()];

          to_compare.get( hash );

          try{
            managers_mon.enter();

            List  registrations = (List)registered_legacy_managers.get( new HashWrapper( hash ));

            if ( registrations != null ){

              routing_data = (PeerManagerRegistrationImpl)registrations.get(0);
            }
          }finally{

            managers_mon.exit();
          }
        }

        //restore buffer structure
        to_compare.limit( old_limit );
        to_compare.position( old_position );

        if ( routing_data != null ){

          if ( !routing_data.isActive()){

            if ( routing_data.isKnownSeed( address )){

              String reason = "Activation request from " + address + " denied as known seed";
             
              if (Logger.isEnabled()){
                Logger.log(new LogEvent(LOGID, reason  ));
              }

              transport.close( reason );
             
              routing_data = null;

            }else{

              if ( !routing_data.getAdapter().activateRequest( address )){

                String reason = "Activation request from " + address + " denied by rules";
               
                if (Logger.isEnabled()){
                  Logger.log(new LogEvent(LOGID, reason ));
                }

                transport.close( reason );
               
                routing_data = null
              }
            }
          }
        }

        return routing_data;
      }

      public Object
      minMatches(
          TransportHelper    transport,
          ByteBuffer       to_compare,
          int         port )
      {
        boolean matches = false;

        int old_limit = to_compare.limit();
        int old_position = to_compare.position();

        to_compare.limit( old_position + 20 );

        if( to_compare.equals( legacy_handshake_header ) ) {
          matches = true;
        }

        //restore buffer structure

        to_compare.limit( old_limit );
        to_compare.position( old_position );

        return matches?"":null;
      }

      public byte[][]
                    getSharedSecrets()
      {
        return( null )// registered manually above
      }

      public int
      getSpecificPort()
      {
        return( -1 );
      }
    };

    // register for incoming connection routing
    NetworkManager.getSingleton().requestIncomingConnectionRouting(
        matcher,
        new NetworkManager.RoutingListener()
        {
          public void
          connectionRouted(
              NetworkConnection   connection,
              Object         routing_data )
          {
            PeerManagerRegistrationImpl  registration = (PeerManagerRegistrationImpl)routing_data;

            registration.route( connection, null );
          }

          public boolean
          autoCryptoFallback()
          {
            return( false );
          }
        },
        new MessageStreamFactory() {
          public MessageStreamEncoder createEncoder() {  return new BTMessageEncoder()}
          public MessageStreamDecoder createDecoder() {  return new BTMessageDecoder()}
        });
  }

  public PeerManagerRegistration
  manualMatchHash(
      InetSocketAddress  address,
      byte[]        hash )
  {     
    PeerManagerRegistrationImpl  routing_data = null;

    try{

      managers_mon.enter();

      List  registrations = (List)registered_legacy_managers.get( new HashWrapper( hash ));

      if ( registrations != null ){

        routing_data = (PeerManagerRegistrationImpl)registrations.get(0);
      }
    }finally{

      managers_mon.exit();
    }

    if ( routing_data != null ){

      if ( !routing_data.isActive()){

        if ( routing_data.isKnownSeed( address )){

          if (Logger.isEnabled()){
            Logger.log(new LogEvent(LOGID, "Activation request from " + address + " denied as known seed" ));
          }

          routing_data = null;

        }else{

          if ( !routing_data.getAdapter().activateRequest( address )){

            if (Logger.isEnabled()){
              Logger.log(new LogEvent(LOGID, "Activation request from " + address + " denied by rules" ));
            }

            routing_data = null
          }
        }
      }
    }

    return routing_data;
 

  public PeerManagerRegistration
  manualMatchLink(
      InetSocketAddress  address,
      String        link )
  { 
    byte[]  hash;

    try{
      managers_mon.enter();

      PeerManagerRegistrationImpl  registration = (PeerManagerRegistrationImpl)registered_links.get( link );

      if ( registration == null ){

        return( null );
      }

      hash = registration.getHash();

    }finally{

      managers_mon.exit();
    }

    return( manualMatchHash( address, hash ));
  }

  public void
  manualRoute(
      PeerManagerRegistration    _registration,
      NetworkConnection      _connection,
      PeerManagerRoutingListener  _listener )
  {
    PeerManagerRegistrationImpl  registration = (PeerManagerRegistrationImpl)_registration;

    registration.route( _connection, _listener );
  }

  public PeerManagerRegistration
  registerLegacyManager(
      HashWrapper            hash,
      PeerManagerRegistrationAdapter  adapter )
  {
    try{
      managers_mon.enter();

      // normally we only get a max of 1 of these. However, due to DownloadManager crazyness
      // we can get an extra one when adding a download that already exists...

      List  registrations = (List)registered_legacy_managers.get( hash );

      byte[][]  secrets = adapter.getSecrets();

      if ( registrations == null ){

        registrations = new ArrayList(1);

        registered_legacy_managers.put( hash, registrations );

        IncomingConnectionManager.getSingleton().addSharedSecrets( secrets );
      }

      PeerManagerRegistration  registration = new PeerManagerRegistrationImpl( hash, adapter );

      registrations.add( registration );

      return( registration );

    }finally{

      managers_mon.exit();
    }
  }



  private class
  PeerManagerRegistrationImpl
  implements PeerManagerRegistration
  {
    private HashWrapper           hash;
    private PeerManagerRegistrationAdapter  adapter;

    private PEPeerControl          download;

    private volatile PEPeerControl      active_control;

    private List  pending_connections;

    private BloomFilter    known_seeds;

    private Map        links;

    protected
    PeerManagerRegistrationImpl(
        HashWrapper            _hash,
        PeerManagerRegistrationAdapter  _adapter )
    {
      hash  = _hash;
      adapter  = _adapter;
    }

    protected PeerManagerRegistrationAdapter
    getAdapter()
    {
      return( adapter );
    }

    protected byte[]
                   getHash()
    {
      return( hash.getBytes() );
    }

    public synchronized TOTorrentFile
    getLink(
        String    target )
    {
      if ( links == null ){

        return( null );
      }

      return((TOTorrentFile)links.get(target));
    }

    public void
    addLink(
        String      link,
        TOTorrentFile  target )

    throws Exception
    {
      try{
        managers_mon.enter();

        if ( registered_links.get( link ) != null ){

          throw( new Exception( "Duplicate link '" + link + "'" ));
        }

        registered_links.put( link, this );

        System.out.println( "Added link '" + link + "'" );

      }finally{

        managers_mon.exit();
      }

      synchronized( this ){

        if ( links == null ){

          links = new HashMap();
        }

        links.put( link, target );
      }
    }

    public void
    removeLink(
        String      link )
    {
      try{
        managers_mon.enter();

        registered_links.remove( link );

      }finally{

        managers_mon.exit();
      }

      synchronized( this ){

        if ( links != null ){

          links.remove( link );
        }
      }
    }

    public boolean
    isActive()
    {
      return( active_control != null );
    }

    public void
    activate(
        PEPeerControl  _active_control )
    {
      List  connections = null;

      try{
        managers_mon.enter();

        active_control = _active_control;

        if ( download != null ){

          Debug.out( "Already activated" );
        }

        download = _active_control;

        connections = pending_connections;

        pending_connections = null;

      }finally{

        managers_mon.exit();
      }

      if ( connections != null ){

        for (int i=0;i<connections.size();i++){

          Object[]  entry = (Object[])connections.get(i);

          NetworkConnection  nc = (NetworkConnection)entry[0];

          PeerManagerRoutingListener  listener = (PeerManagerRoutingListener)entry[2];

          route( _active_control, nc, true, listener );
        }
      }
    }

    public void
    deactivate()
    {
      try{
        managers_mon.enter();

        if ( download == null ){

          Debug.out( "Already deactivated" );

        }else{

          download  = null;
        }

        active_control = null;

        if ( pending_connections != null ){

          for (int i=0;i<pending_connections.size();i++){

            Object[]  entry = (Object[])pending_connections.get(i);

            NetworkConnection  connection = (NetworkConnection)entry[0];

            if (Logger.isEnabled())
              Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING,
                  "Incoming connection from [" + connection
                                             + "] closed due to deactivation" ));

            connection.close( "deactivated" );
          }

          pending_connections = null;
        }

      }finally{

        managers_mon.exit();
      }
    }

    public void
    unregister()
    {
      try{
        managers_mon.enter();

        if ( active_control != null ){

          Debug.out( "Not deactivated" );

          deactivate();
        }

        List  registrations = (List)registered_legacy_managers.get( hash );

        if ( registrations == null ){

          Debug.out( "manager already deregistered" );

        }else{

          if ( registrations.remove( this )){

            if ( registrations.size() == 0 ){

              IncomingConnectionManager.getSingleton().removeSharedSecrets( adapter.getSecrets());

              registered_legacy_managers.remove( hash );
            }
          }else{

            Debug.out( "manager already deregistered" );
          }
        }

        if ( links != null ){

          Iterator  it = links.keySet().iterator();

          while( it.hasNext()){

            registered_links.remove( it.next());
          }
        }
      }finally{

        managers_mon.exit();
      }
    }

    protected boolean
    isKnownSeed(
      InetSocketAddress    address )
    {
      try{
        managers_mon.enter();

        if ( known_seeds == null ){

          return( false );
        }

        return( known_seeds.contains( address.getAddress().getAddress()));

      }finally{

        managers_mon.exit();
      }
    }

    protected void
    setKnownSeed(
        InetSocketAddress    address )
    {
      try{
        managers_mon.enter();

        if ( known_seeds == null ){

          known_seeds = BloomFilterFactory.createAddOnly( 1024 );
        }

        // can't include port as it will be a randomly allocated one in general. two people behind the
        // same NAT will have to connect to each other using LAN peer finder

        known_seeds.add( address.getAddress().getAddress() );

      }finally{

        managers_mon.exit();
      }
    }

    protected PEPeerControl
    getActiveControl()
    {
      return( active_control );
    }

    protected void
    route(
      NetworkConnection       connection,
      PeerManagerRoutingListener  listener )
    { 
      if ( adapter.manualRoute( connection )){
       
        return;
      }
     
      if ( !adapter.isPeerSourceEnabled( PEPeerSource.PS_INCOMING )){
     
        if (Logger.isEnabled())
          Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING,
              "Incoming connection from [" + connection
              + "] to " + adapter.getDescription() + " dropped as peer source disabled" ));
       
        connection.close( "peer source disabled" );

        return;
      }
     
      PEPeerControl  control;

      boolean  register_for_timeouts = false;

      try{
        managers_mon.enter();

        control = active_control;

        if ( control == null ){

          // not yet activated, queue connection for use on activation

          if ( pending_connections != null && pending_connections.size() > 10 ){

            if (Logger.isEnabled())
              Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING,
                  "Incoming connection from [" + connection
                  + "] to " + adapter.getDescription() + " dropped too many pending activations" ));
           
            connection.close( "too many pending activations" );

            return;
           
          }else{

            if ( pending_connections == null ){

              pending_connections = new ArrayList();
            }

            pending_connections.add( new Object[]{ connection, new Long( SystemTime.getCurrentTime()), listener });

            if ( pending_connections.size() == 1 ){

              register_for_timeouts  = true;
            }
          }
        } 
      }finally{

        managers_mon.exit();
      }

      // do this outside the monitor as the timeout code calls us back holding the timeout monitor
      // and we need to grab managers_mon inside this to run timeouts

      if ( register_for_timeouts ){

        registerForTimeouts( this );
      }

      if ( control != null ){

        route( control, connection, false, listener );
      }
    }

    protected boolean
    timeoutCheck()
    {
      try{
        managers_mon.enter();

        if ( pending_connections == null ){

          return( false );
        }

        Iterator it = pending_connections.iterator();

        long  now = SystemTime.getCurrentTime();

        while( it.hasNext()){

          Object[]  entry = (Object[])it.next();

          long  start_time = ((Long)entry[1]).longValue();

          if ( now < start_time ){

            entry[1] = new Long( now );

          }else if ( now - start_time > PENDING_TIMEOUT ){

            it.remove();

            NetworkConnection  connection = (NetworkConnection)entry[0];

            if (Logger.isEnabled())
              Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING,
                  "Incoming connection from [" + connection
                  + "] to " + adapter.getDescription() + " closed due to activation timeout" ));

            connection.close( "activation timeout" );   
          }
        }

        if ( pending_connections.size() == 0 ){

          pending_connections = null;
        }

        return( pending_connections != null );

      }finally{

        managers_mon.exit();
      }
    }

    protected void
    route(
        PEPeerControl        control, 
        final NetworkConnection   connection,
        boolean            is_activation,
        PeerManagerRoutingListener  listener )
    {
      // make sure not already connected to the same IP address; allow
      // loopback connects for co-located proxy-based connections and
      // testing

      Object callback = connection.getUserData( "RoutedCallback" );
     
      if ( callback instanceof AEGenericCallback ){
       
        try{
          ((AEGenericCallback)callback).invoke( control );
         
        }catch( Throwable e ){
         
          Debug.out( e );
        }
      }
     
      String host_address = connection.getEndpoint().getNotionalAddress().getAddress().getHostAddress();

      boolean same_allowed = COConfigurationManager.getBooleanParameter( "Allow Same IP Peers" ) || host_address.equals( "127.0.0.1" );

      if( !same_allowed && PeerIdentityManager.containsIPAddress( control.getPeerIdentityDataID(), host_address ) ){

        if (Logger.isEnabled())
          Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING,
              "Incoming connection from [" + connection
              + "] dropped as IP address already "
              + "connected for ["
              + control.getDisplayName() + "]"));
        connection.close( "already connected to peer");

        return;
      }

      if (Logger.isEnabled())
        Logger.log(new LogEvent(LOGID, "Incoming connection from ["
            + connection + "] routed to legacy download ["
            + control.getDisplayName() + "]"));

      PEPeerTransport  pt = PEPeerTransportFactory.createTransport( control, PEPeerSource.PS_INCOMING, connection, null );

      if ( listener != null ){

        boolean  ok = false;

        try{
          if ( listener.routed( pt )){

            ok  = true;
          }

        }catch( Throwable e ){

          Debug.printStackTrace(e);
        }

        if ( !ok ){

          connection.close( "routing denied" );

          return;
        }
      }

      pt.start();

      if ( is_activation ){

        pt.addListener(
            new PEPeerListener()
            {
              public void
              stateChanged(
                  PEPeer     peer,
                  int     new_state )
              {
                if ( new_state == PEPeer.CLOSING ){

                  if ( peer.isSeed()){

                    InetSocketAddress  address = connection.getEndpoint().getNotionalAddress();

                    setKnownSeed( address );

                    // this is mainly to deal with seeds that incorrectly connect to us

                    adapter.deactivateRequest( address );
                 
                }
              }

              public void sentBadChunk(PEPeer peer, int piece_num, int total_bad_chunks ){}

              public void addAvailability(final PEPeer peer, final BitFlags peerHavePieces){}

              public void removeAvailability(final PEPeer peer, final BitFlags peerHavePieces){} 
            });
      }

      control.addPeerTransport( pt );
    }

    public String
    getDescription()
    {
      PEPeerControl  control = active_control;

      return( ByteFormatter.encodeString( hash.getBytes()) + ", control=" + (control==null?null:control.getDisplayName()) + ": " + adapter.getDescription());
    }
  }
}
TOP

Related Classes of com.aelitis.azureus.core.peermanager.PeerManager$PeerManagerRegistrationImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.