Package com.aelitis.azureus.core.proxy.socks.impl

Source Code of com.aelitis.azureus.core.proxy.socks.impl.AESocksProxyConnectionImpl

/*
* Created on 08-Dec-2004
* Created by Paul Gardner
* Copyright (C) 2004, 2005, 2006 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.proxy.socks.impl;

import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.HostNameToIPResolver;
import org.gudy.azureus2.core3.util.HostNameToIPResolverListener;

import com.aelitis.azureus.core.proxy.*;
import com.aelitis.azureus.core.proxy.socks.*;

/**
* @author parg
*
*/

public class
AESocksProxyConnectionImpl
  implements AESocksProxyConnection, AEProxyConnectionListener
{
  private static final LogIDs LOGID = LogIDs.NET;
  public static final boolean  TRACE  = false;
 
  protected AESocksProxyImpl    proxy;
  protected AEProxyConnection    connection;
  protected boolean        disable_dns_lookups;
 
  protected SocketChannel      source_channel;
 
  protected int          socks_version;
 
  protected AESocksProxyPlugableConnection  plugable_connection;
 
  protected
  AESocksProxyConnectionImpl(
    AESocksProxyImpl            _proxy,
    AESocksProxyPlugableConnectionFactory  _connection_factory,
    AEProxyConnection            _connection )
 
    throws IOException
  {
    proxy    = _proxy;
    connection  = _connection;
   
    connection.addListener( this );
   
    source_channel  = connection.getSourceChannel();
   
    try{
      plugable_connection  = _connection_factory.create( this );
   
      if ( TRACE ){
        Logger.log(new LogEvent(LOGID, "AESocksProxyProcessor: " + getName()));
      }
    }catch( AEProxyException e ){
     
      throw( new IOException( e.getMessage()));
    }
  }
 
  public AESocksProxy
  getProxy()
  {
    return( proxy );
  }
 
  public void
  setDelegate(
    AESocksProxyPlugableConnection  target )
  {
    plugable_connection = target;
  }
 
  protected String
  getName()
  {
    String  name = connection.getName() + ", ver = " + socks_version;
         
    name += plugable_connection.getName();
   
    return( name );
  }
 
  protected AEProxyState
  getInitialState()
  {
    return( new proxyStateVersion());
  }
 
  public void
  connectionClosed(
    AEProxyConnection  con )
  {
    try{
      if ( plugable_connection != null ){
       
        plugable_connection.close();
      }
     
    }catch( Throwable e ){
     
      Debug.printStackTrace( e );
    }
  }
 
  public boolean
  isClosed()
  {
    return( connection.isClosed());
  }
 
  public AEProxyConnection
  getConnection()
  {
    return( connection );
  }
 
  public void
  disableDNSLookups()
  {
    disable_dns_lookups  = true;
  }
 
  public void
  enableDNSLookups()
  {
    disable_dns_lookups = false;
  }
 
  public void
  close()
 
    throws IOException
  {
    new proxyStateClose();
  }
 
  protected class
  proxyStateVersion
    extends AESocksProxyState
  {
    protected
    proxyStateVersion()
    {
      super( AESocksProxyConnectionImpl.this );
     
      connection.setReadState( this );
     
      buffer  = ByteBuffer.allocate(1);
    }
   
    protected boolean
    readSupport(
      SocketChannel     sc )
   
      throws IOException
    {
      int  len = sc.read( buffer );
     
      if ( len == 0 ){
       
        return( false );
       
      }else if ( len == -1 ){
       
        throw( new IOException( "read channel shutdown" ));
      }
     
      if ( buffer.hasRemaining()){
       
        return( true );
      }
     
      buffer.flip();
     
      int  version  = buffer.get();
     
      if ( version == 5 ){
     
        new proxyStateV5MethodNumber();
       
      }else if ( version == 4 ){
       
        new proxyStateV4Request();
       
      }else{
       
        throw( new IOException( "Unsupported version " + version ));

      }
     
      return( true );
    }
  }
 
    // V4
 
  protected class
  proxyStateV4Request
    extends AESocksProxyState
  {
    boolean    got_header;
   
    protected int    port;
    protected byte[]  address;
   
    protected
    proxyStateV4Request()
    {
      super( AESocksProxyConnectionImpl.this );
     
      connection.setReadState( this );

      buffer  = ByteBuffer.allocate(7);
    }
 
    protected boolean
    readSupport(
      SocketChannel     sc )
   
      throws IOException
    {
      /*
      +----+----+----+----+----+----+----+----+----+----+....+----+
      | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
      +----+----+----+----+----+----+----+----+----+----+....+----+
      # of bytes:     1    1      2              4           variable       1
      */

      int  len = sc.read( buffer );
     
      if ( len == 0 ){
       
        return( false );
       
      }else if ( len == -1 ){
       
        throw( new IOException( "read channel shutdown" ));
      }
           
      if ( buffer.hasRemaining()){
       
        return( true );
      }
     
      buffer.flip();
     
      if ( got_header ){
       
        if ( buffer.get() == (byte)0){
       
            // end of play
   
          if address[0] == 0 &&
              address[1] == 0 &&
              address[2] == 0 &&
              address[3] != 0 ){
           
              // socks 4a
           
            new proxyStateV4aRequest( port );
           
          }else{
             
            socks_version  = 4;
           
            plugable_connection.connect(
                new AESocksProxyAddressImpl( "", InetAddress.getByAddress( address ), port ));
           
          }
        }else{
       
          // drop the user id byte
         
          buffer.flip();
         
        }
      }else{
       
        got_header  = true;
       
        byte  command  = buffer.get();
       
        if ( command != 1 ){
         
          throw( new IOException( "SocksV4: only CONNECT supported" ));
        }
       
        port = (((int)buffer.get() & 0xff) << 8 ) + ((int)buffer.get() & 0xff);

        address = new byte[4];
       
        for (int i=0;i<address.length;i++){
       
          address[i] = buffer.get();
        }
       
          // prepare for user id
       
        buffer = ByteBuffer.allocate(1);
      }
     
      return( true );
    }
  }
 
  protected class
  proxyStateV4aRequest
    extends AESocksProxyState
  {
    protected String  dns_address;
    protected int    port;
   
    protected
    proxyStateV4aRequest(
      int    _port )
    {
      super( AESocksProxyConnectionImpl.this );
     
      port    = _port;
      dns_address  = "";
     
      connection.setReadState( this );

      buffer  = ByteBuffer.allocate(1);
    }
 
    protected boolean
    readSupport(
      final SocketChannel     sc )
   
      throws IOException
    {
        // dns name follows, null terminated

      int  len = sc.read( buffer );
     
      if ( len == 0 ){
       
        return( false );
       
      }else if ( len == -1 ){
       
        throw( new IOException( "read channel shutdown" ));
      }
     
     
      if ( buffer.hasRemaining()){
       
        return( true );
      }
     
      buffer.flip();
             
      byte data = buffer.get();
     
      if ( data == 0 ){
       
        if ( disable_dns_lookups ){
         
          socks_version  = 4;
         
          plugable_connection.connect( new AESocksProxyAddressImpl( dns_address, null, port ));

        }else{
          final String  f_dns_address  = dns_address;
         
          connection.cancelReadSelect( sc );
         
          HostNameToIPResolver.addResolverRequest(
            dns_address,
            new HostNameToIPResolverListener()
            {
              public void
              hostNameResolutionComplete(
                InetAddress  address )
              {
                try{
                  socks_version  = 4;
                 
                  plugable_connection.connect( new AESocksProxyAddressImpl( f_dns_address, address, port ));
   
                    // re-activate the read select suspended while resolving
                 
                  connection.requestReadSelect( sc );
                 
                }catch ( IOException e ){
                 
                  connection.failed(e);
                }
              }
            });   
        }
      }else{
       
        dns_address += (char)data;
       
        if ( dns_address.length() > 4096 ){
         
          throw( new IOException( "DNS name too long" ));
        }
       
          // ready for next byte
       
        buffer.flip();
      }
     
      return( true );
    }
  }
 
  protected class
  proxyStateV4Reply
    extends AESocksProxyState
  {
    protected
    proxyStateV4Reply()
   
      throws IOException
    {   
      super( AESocksProxyConnectionImpl.this );
     
      /*
      +----+----+----+----+----+----+----+----+
      | VN | CD | DSTPORT |      DSTIP        |
      +----+----+----+----+----+----+----+----+
      # of bytes:     1    1      2              4
      */

      connection.setWriteState( this );
     
      byte[]  addr = plugable_connection.getLocalAddress().getAddress();
      int    port = plugable_connection.getLocalPort();
     
      buffer  = ByteBuffer.wrap(
          new byte[]{  (byte)0,(byte)90,
                (byte)((port>>8)&0xff), (byte)(port&0xff),
                addr[0],addr[1],addr[2],addr[3]});
         
     
      write( source_channel );
    }
   
    protected boolean
    writeSupport(
      SocketChannel     sc )
   
      throws IOException
    {
      int  len = sc.write( buffer );
     
      if ( buffer.hasRemaining()){
       
        connection.requestWriteSelect( sc );
       
      }else{
 
        plugable_connection.relayData();
      }
     
      return( len > 0 );
    }
  }
 
    // V5
 
  protected class
  proxyStateV5MethodNumber
    extends AESocksProxyState
  {
   
    protected
    proxyStateV5MethodNumber()
    {
      super( AESocksProxyConnectionImpl.this );
     
      connection.setReadState( this );

      buffer  = ByteBuffer.allocate(1);
    }
   
    protected boolean
    readSupport(
      SocketChannel     sc )
   
      throws IOException
    {
      int  len = sc.read( buffer );
     
      if ( len == 0 ){
       
        return( false );
       
      }else if ( len == -1 ){
       
        throw( new IOException( "read channel shutdown" ));
      }
     
     
      if ( buffer.hasRemaining()){
       
        return( true );
      }
     
      buffer.flip();
     
      int  num_methods  = buffer.get();
     
      new proxyStateV5Methods(num_methods);
     
      return( true );
    }
  }
 
  protected class
  proxyStateV5Methods
    extends AESocksProxyState
  {
   
    protected
    proxyStateV5Methods(
      int    methods )
    {
      super( AESocksProxyConnectionImpl.this );
     
      connection.setReadState( this );

      buffer  = ByteBuffer.allocate(methods);
    }
   
    protected boolean
    readSupport(
      SocketChannel     sc )
   
      throws IOException
    {
      int  len = sc.read( buffer );
     
      if ( len == 0 ){
       
        return( false );
       
      }else if ( len == -1 ){
       
        throw( new IOException( "read channel shutdown" ));
      }
     
     
      if ( buffer.hasRemaining()){
       
        return( true );
      }
     
        // we just ignore actual method values
     
      new proxyStateV5MethodsReply();
     
      return( true );
    }
  }
 
  protected class
  proxyStateV5MethodsReply
    extends AESocksProxyState
  {
   
    protected
    proxyStateV5MethodsReply()
   
      throws IOException
    {
      super( AESocksProxyConnectionImpl.this );
     
      new proxyStateV5Request();
     
      connection.setWriteState( this );
     
      buffer  = ByteBuffer.wrap(new byte[]{(byte)5,(byte)0});
     
      write( source_channel );
    }
   
    protected boolean
    writeSupport(
      SocketChannel     sc )
   
      throws IOException
    {
      int len = sc.write( buffer );
     
      if ( buffer.hasRemaining()){
       
        connection.requestWriteSelect( sc );
      }
     
      return( len > 0 );
    }
  }
 
  /*
    +----+-----+-------+------+----------+----------+
    |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
    +----+-----+-------+------+----------+----------+
    | 1  |  1  | X'00' |  1   | Variable |    2     |
    +----+-----+-------+------+----------+----------+

    Where:

            o  VER    protocol version: X'05'
            o  CMD
               o  CONNECT X'01'
               o  BIND X'02'
               o  UDP ASSOCIATE X'03'
            o  RSV    RESERVED
            o  ATYP   address type of following address
               o  IP V4 address: X'01'
               o  DOMAINNAME: X'03'
               o  IP V6 address: X'04'
            o  DST.ADDR       desired destination address
            o  DST.PORT desired destination port in network octet
               order
               */
 
  protected class
  proxyStateV5Request
    extends AESocksProxyState
  {
   
    protected
    proxyStateV5Request()
    {
      super( AESocksProxyConnectionImpl.this );
     
      connection.setReadState( this );
     
      buffer  = ByteBuffer.allocate(4);
    }
   
    protected boolean
    readSupport(
      SocketChannel     sc )
   
      throws IOException
    {
      int  len = sc.read( buffer );
     
      if ( len == 0 ){
       
        return( false );
       
      }else if ( len == -1 ){
       
        throw( new IOException( "read channel shutdown" ));
      }
     
     
      if ( buffer.hasRemaining()){
       
        return( true );
      }
     
      buffer.flip();
     
      buffer.get();    // version
     
      int  command      = buffer.get();
     
      buffer.get();    // reserved
     
      int address_type  = buffer.get();
     
      if ( command != 1 ){
       
        throw( new IOException( "V5: Only connect supported"));
      }
     
      if ( address_type == 1 ){
     
        new proxyStateV5RequestIP();
       
      }else if ( address_type == 3 ){
       
        new proxyStateV5RequestDNS();
       
      }else{
       
        throw( new IOException( "V5: Unsupported address type" ));
      }
     
      return( true );
    }
  }
 
  protected class
  proxyStateV5RequestIP
    extends AESocksProxyState
  {
   
    protected
    proxyStateV5RequestIP()
    {
      super( AESocksProxyConnectionImpl.this );
     
      connection.setReadState( this );
     
      buffer  = ByteBuffer.allocate(4);
    }
   
    protected boolean
    readSupport(
      SocketChannel     sc )
   
      throws IOException
    {
      int  len = sc.read( buffer );
     
      if ( len == 0 ){
       
        return( false );
       
      }else if ( len == -1 ){
       
        throw( new IOException( "read channel shutdown" ));
      }
     
     
      if ( buffer.hasRemaining()){
       
        return( true );
      }
     
      buffer.flip();
     
      byte[]  bytes = new byte[4];
     
      buffer.get( bytes );
     
      InetAddress inet_address = InetAddress.getByAddress( bytes );
     
      new proxyStateV5RequestPort( "", inet_address );
     
      return( true );
    }
  }
 
  protected class
  proxyStateV5RequestDNS
    extends AESocksProxyState
  {
    boolean  got_length  = false;
   
    protected
    proxyStateV5RequestDNS()
    {
      super( AESocksProxyConnectionImpl.this );
     
      connection.setReadState( this );
     
      buffer  = ByteBuffer.allocate(1);
    }
   
    protected boolean
    readSupport(
      final SocketChannel     sc )
   
      throws IOException
    {
      int  len = sc.read( buffer );
     
      if ( len == 0 ){
       
        return( false );
       
      }else if ( len == -1 ){
       
        throw( new IOException( "read channel shutdown" ));
      }
     
     
      if ( buffer.hasRemaining()){
       
        return( true );
      }
     
      buffer.flip();
     
      if ( !got_length){
       
        int  length = ((int)buffer.get()) & 0xff;
       
        buffer = ByteBuffer.allocate( length );
       
        got_length  = true;
       
      }else{
       
        String dns_address = "";
       
        while( buffer.hasRemaining()){
       
          dns_address += (char)buffer.get();
        }
         
        if ( disable_dns_lookups ){
       
          new proxyStateV5RequestPort( dns_address, null );
       
        }else{
         
          final String  f_dns_address  = dns_address;
         
          connection.cancelReadSelect( sc );
         
          HostNameToIPResolver.addResolverRequest(
            dns_address,
            new HostNameToIPResolverListener()
            {
              public void
              hostNameResolutionComplete(
                InetAddress  address )
              {
                new proxyStateV5RequestPort( f_dns_address, address);
                 
                connection.requestReadSelect( sc );
              }
            });
        }
      }
     
      return( true );
    }
  }
 
  protected class
  proxyStateV5RequestPort
    extends AESocksProxyState
  {
    protected String    unresolved_address;
    protected InetAddress  address;
   
    protected
    proxyStateV5RequestPort(
      String      _unresolved_address,
      InetAddress    _address )
    {
      super( AESocksProxyConnectionImpl.this );
     
      unresolved_address  = _unresolved_address;
      address        = _address;
     
      connection.setReadState( this );
     
      buffer  = ByteBuffer.allocate(2);
    }
   
    protected boolean
    readSupport(
      SocketChannel     sc )
   
      throws IOException
    {
      int  len = sc.read( buffer );
     
      if ( len == 0 ){
       
        return( false );
       
      }else if ( len == -1 ){
       
        throw( new IOException( "read channel shutdown" ));
      }
     
     
      if ( buffer.hasRemaining()){
       
        return( true );
      }
     
      buffer.flip();
           
      int  port = (((int)buffer.get() & 0xff) << 8 ) + ((int)buffer.get() & 0xff);
     
      socks_version  = 5;
     
      plugable_connection.connect( new AESocksProxyAddressImpl( unresolved_address, address, port ));
     
      return( true );
    }
  }
 
  /*
    +----+-----+-------+------+----------+----------+
    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
    +----+-----+-------+------+----------+----------+
    | 1  |  1  | X'00' |  1   | Variable |    2     |
    +----+-----+-------+------+----------+----------+

Where:

      o  VER    protocol version: X'05'
      o  REP    Reply field:
         o  X'00' succeeded
         o  X'01' general SOCKS server failure
         o  X'02' connection not allowed by ruleset
         o  X'03' Network unreachable
         o  X'04' Host unreachable
         o  X'05' Connection refused
         o  X'06' TTL expired
         o  X'07' Command not supported
         o  X'08' Address type not supported
         o  X'09' to X'FF' unassigned
      o  RSV    RESERVED
      o  ATYP   address type of following address

         o  IP V4 address: X'01'
         o  DOMAINNAME: X'03'
         o  IP V6 address: X'04'
      o  BND.ADDR       server bound address
      o  BND.PORT       server bound port in network octet order
      */
 
 
  protected class
  proxyStateV5Reply
    extends AESocksProxyState
  {
    protected
    proxyStateV5Reply()
   
      throws IOException
    {   
      super( AESocksProxyConnectionImpl.this );
     
      connection.setWriteState( this );
     
      byte[]  addr = plugable_connection.getLocalAddress().getAddress();
     
      int    port = plugable_connection.getLocalPort();
     
      buffer  = ByteBuffer.wrap(
          new byte[]{(byte)5,(byte)0,(byte)0,(byte)1,
                addr[0],addr[1],addr[2],addr[3],
                (byte)((port>>8)&0xff), (byte)(port&0xff)});
         
     
      write( source_channel );
    }
   
    protected boolean
    writeSupport(
      SocketChannel     sc )
   
      throws IOException
    {
      int  len = sc.write( buffer );
     
      if ( buffer.hasRemaining()){
       
        connection.requestWriteSelect( sc );
       
      }else{
 
        plugable_connection.relayData();
      }
     
      return( len > 0 );
    }
  }
 
  public void
  connected()
 
    throws IOException
  {
    if ( socks_version == 4 ){
     
      new proxyStateV4Reply();
     
    }else{
     
      new proxyStateV5Reply();
    }
  }

 
  protected class
  proxyStateClose
    extends AESocksProxyState
  {
    protected
    proxyStateClose()
   
      throws IOException
   
      super( AESocksProxyConnectionImpl.this );
     
      connection.close();
     
      connection.setReadState( null);
      connection.setWriteState( null);
      connection.setConnectState( null);
    }
  }
}
TOP

Related Classes of com.aelitis.azureus.core.proxy.socks.impl.AESocksProxyConnectionImpl

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.