FileDocCategorySizeDatePackage
ClientIDManagerImpl.javaAPI DocAzureus 3.0.3.416856Tue Jul 24 13:28:04 BST 2007org.gudy.azureus2.pluginsimpl.local.clientid

ClientIDManagerImpl

public class ClientIDManagerImpl extends Object implements org.gudy.azureus2.plugins.clientid.ClientIDManager
author
parg

Fields Summary
private static final LogIDs
LOGID
protected static ClientIDManagerImpl
singleton
protected static final char
CR
protected static final char
FF
protected static final String
NL
private org.gudy.azureus2.plugins.clientid.ClientIDGenerator
generator_user_accessor
private boolean
use_filter
private boolean
filter_override
private org.gudy.azureus2.core3.util.ThreadPool
thread_pool
private int
filter_port
Constructors Summary
Methods Summary
protected voidcheckGenerator(org.gudy.azureus2.plugins.clientid.ClientIDGenerator gen)

		ClassLoader	cl = gen.getClass().getClassLoader();
		
		if ( cl != null && cl != ClientIDManager.class.getClassLoader()){
			
				// if early in the day we can try to get the default one working here
			
			PluginManager.getDefaults().setDefaultPluginEnabled( PluginManagerDefaults.PID_CLIENT_ID, true );
			
			Debug.out( "Generator isn't trusted - " + gen );
			
			throw( new RuntimeException( "Generator isn't trusted" ));
		}
	
public voidgenerateHTTPProperties(java.util.Properties properties)

		if ( use_filter ){
		
				// to support SSL here we would need to substitute the https url with an https one
				// and then drive the SSL in the filter appropriately
			
			URL	url = (URL)properties.get( ClientIDGenerator.PR_URL );
			
			if ( !url.getProtocol().toLowerCase().equals( "http" )){
				
				Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR,
						"ClientIDManager only supports filtering of http, not https"));
				
				return;
			}
			
			try{
				String	url_str = url.toString();
				
				String	target_host = url.getHost();
				int		target_port	= url.getPort();
				
				if ( target_port == -1 ){
					
					target_port = url.getDefaultPort();
				}
				
				int host_pos = url_str.indexOf( target_host );
				
				String	new_url = url_str.substring(0,host_pos) + "127.0.0.1:" + filter_port;
				
				String	rem = url_str.substring( host_pos + target_host.length());
				
				if ( rem.charAt(0) == ':" ){
					
					rem = rem.substring( (""+ target_port ).length() + 1 );
				}
				
				int q_pos = rem.indexOf( '?" );
				
				new_url += rem.substring(0,q_pos+1) + "cid=" + target_host + ":" + target_port + "&" + rem.substring(q_pos+1);
				
				properties.put( ClientIDGenerator.PR_URL, new URL( new_url ));
				
			}catch( Throwable e ){
				
				Debug.printStackTrace(e);
			}
		}else{
			
			getGenerator().generateHTTPProperties( properties );
		}
	
public byte[]generatePeerID(org.gudy.azureus2.core3.torrent.TOTorrent torrent, boolean for_tracker)

		return( getGenerator().generatePeerID( new TorrentImpl( torrent ), for_tracker ));
	
public org.gudy.azureus2.plugins.clientid.ClientIDGeneratorgetGenerator()

		checkGenerator( generator_user_accessor );
		
		return( generator_user_accessor );
	
public static org.gudy.azureus2.pluginsimpl.local.clientid.ClientIDManagerImplgetSingleton()

	
	  
	
	
		return( singleton );
	
public voidsetGenerator(org.gudy.azureus2.plugins.clientid.ClientIDGenerator _generator, boolean _use_filter)

			// I wanted to allow signed plugins the ability to do this but given that a malicious 
			// plugin can use reflection to get access to fields (such as the URL field of a 
			// URLClassLoader) I can't see a way to enforce this. That is, how can you verify
			// that the class was loaded from a signed jar? you can get the jar that the URLClassLoader
			// claims it was loaded from and verify that, but this jar location may have been changed
			// by the plugin. you can look inside the signed jar and check that there's a class in 
			// there with the right name, implementing ClientIDGenerator, but this doesn't prove
			// that the implementation passed to this method is the same as once an offical signed
			// plugin is released that uses this feature (with, say, a class called a.b.c.X as the
			// generator), a malicious plugin can simply also implement a class a.b.c.X, ship 
			// along with a copy of the official jar, hack the class-loader after loading to make
			// the class-loader point to the official jar. The only things that can't be changed
			// by reflection are static final fields which don't seem to help. We could modify
			// our security manager to trap a checkAccess perm check but we don't have access to
			// the thing being modified and this is used in various other places to work around bugs.
			// So we only accept generators loaded by non-plugin loaders. Note that you can't
			// change a class's class loader so this works.
			// we might be able to fix things by using some native storage that can't be modified
			// by a plugin, or by getting this code to load/instantiate the class, but you still
			// have the problem that the plugin can directly modify the "generator" field. Another
			// fix would be to enhance the security manager and provide methods to wrap the
			// setAccessible operations so we can control which objects are accessible
		
		checkGenerator( _generator );
		
		generator_user_accessor	= _generator;
		use_filter				= _use_filter;
		
			// we override the filter parameter here if we have a local bind IP set as
			// this is the only simple solution to enforcing the local bind (Sun's
			// HTTPConnection doesn't allow the network interface to be bound)
		

		if ( !use_filter ){
			
				// another reason for NOT doing this is if the user has a defined proxy
				// in this case the assumption is that they know what they're doing and
				// the proxy will be bound correctly to ensure that things work...
			
			String	http_proxy 	= System.getProperty( "http.proxyHost" );
			String	socks_proxy = System.getProperty( "socksProxyHost" );
			
		    InetAddress bindIP = NetworkAdmin.getSingleton().getDefaultBindAddress();
		    
	        if (	( http_proxy == null || http_proxy.trim().length() == 0 ) &&
	        		( socks_proxy == null || socks_proxy.trim().length() == 0 ) &&
	        		bindIP != null ){

	        	int		ips = 0;
	        	
	        		// seeing as this is a bit of a crappy way to enforce binding, add one more check to make
	        		// sure that the machine has multiple ips before going ahead in case user has set it
	        		// incorrectly
	        	
	        	try{
	        		Enumeration nis = NetworkInterface.getNetworkInterfaces();
	        			        		
	        		while( nis.hasMoreElements()){
	        			
	        			NetworkInterface ni = (NetworkInterface)nis.nextElement();
	        			
	        			Enumeration addresses = ni.getInetAddresses();
	        			
	        			while( addresses.hasMoreElements()){
	        				
	        				InetAddress address = (InetAddress)addresses.nextElement();
	        				
	        				if ( !address.isLoopbackAddress()){
	        					
	        					ips++;
	        				}
	        			}        			
	        		}
	        	}catch( Throwable e ){
	        		Logger.log(new LogEvent(LOGID, "", e));
	        	}
	        	
	        	if ( ips > 1 ){
	        		
		        	filter_override	= true;
		        	
		        	use_filter	= true;
		        	
		        	if (Logger.isEnabled())
		        		Logger.log(new LogEvent(LOGID,
		        				"ClientIDManager: overriding filter "
		        				+ "option to support local bind IP"));
	        	}
	        }
		}
		
		if ( use_filter ){
			
			try{
				thread_pool = new ThreadPool( "ClientIDManager", 32 );
				
			  	String	connect_timeout = System.getProperty("sun.net.client.defaultConnectTimeout"); 
			  	String	read_timeout 	= System.getProperty("sun.net.client.defaultReadTimeout"); 
			  			
			  	int	timeout = Integer.parseInt( connect_timeout ) + Integer.parseInt( read_timeout );
				
				thread_pool.setExecutionLimit( timeout );
			
				final ServerSocket ss = new ServerSocket( 0, 1024, InetAddress.getByName("127.0.0.1"));
				
				filter_port	= ss.getLocalPort();
				
				ss.setReuseAddress(true);
								
				Thread accept_thread = 
						new AEThread("ClientIDManager::filterloop")
						{
							public void
							runSupport()
							{
								long	successfull_accepts = 0;
								long	failed_accepts		= 0;

								while(true){
									
									try{				
										Socket socket = ss.accept();
							
										successfull_accepts++;
							
										thread_pool.run( new httpFilter( socket ));
										
									}catch( Throwable e ){
										
										failed_accepts++;
										
                    if (Logger.isEnabled())
                      Logger.log(new LogEvent(LOGID, 
                                              "ClientIDManager: listener failed on port "
                                              + filter_port, e )); 
										
										if ( failed_accepts > 100 && successfull_accepts == 0 ){

												// looks like its not going to work...
												// some kind of socket problem
															
											Logger.logTextResource(new LogAlert(LogAlert.UNREPEATABLE,
												LogAlert.AT_ERROR, "Network.alert.acceptfail"),
												new String[] { "" + filter_port, "TCP" });
									
											use_filter	= false;
											
											break;
										}
									}
								}
							}
						};
			
				accept_thread.setDaemon( true );
			
				accept_thread.start();									
			
				if (Logger.isEnabled())
					Logger.log(new LogEvent(LOGID,
							"ClientIDManager: listener established on port " + filter_port)); 
				
			}catch( Throwable e){
			
				Logger.logTextResource(new LogAlert(LogAlert.UNREPEATABLE,
						LogAlert.AT_ERROR, "Tracker.alert.listenfail"), new String[] { ""
						+ filter_port });
		
				if (Logger.isEnabled())
					Logger.log(new LogEvent(LOGID,
							"ClientIDManager: listener failed on port " + filter_port, e)); 
				
				use_filter	= false;
			}		
		}