/* * Created on 13-Dec-2004 * Created by Paul Gardner * Copyright (C) 2004 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, SARL au capital de 30,000 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. * */ package com.aelitis.azureus.plugins.networks.i2p; import java.io.*; import java.lang.reflect.*; import java.util.Properties; import java.util.StringTokenizer; import org.gudy.azureus2.core3.util.AEMonitor; import org.gudy.azureus2.core3.util.AESemaphore; import org.gudy.azureus2.core3.util.AEThread; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.plugins.logging.LoggerChannel; import org.gudy.azureus2.plugins.ui.config.BooleanParameter; import org.gudy.azureus2.plugins.ui.config.IntParameter; import org.gudy.azureus2.plugins.ui.config.StringParameter; import com.aelitis.azureus.core.proxy.AEProxyException; import com.aelitis.azureus.core.proxy.socks.*; /** * @author parg * */ public class I2PPluginConnectionManager implements AESocksProxyPlugableConnectionFactory { private ClassLoader class_loader; private LoggerChannel log; private volatile Object socket_manager; private Class i2p_Destination; private Method i2p_Destination_fromBase64; private Class i2p_I2PSocketManagerFactory; private Method i2p_I2PSocketManager_getSession; private Method i2p_I2PSocketManager_ping; private Method i2p_I2PSocketManager_connect; private Method i2p_I2PSession_getMyDestination; private Method i2p_I2PSocket_close; private Method i2p_I2PSocket_getInputStream; private Method i2p_I2PSocket_getOutputStream; //private Method i2p_I2PInputStream_read; //private Method i2p_I2POutputStream_write; private StringParameter router_host; private IntParameter router_port; private StringParameter router_options; // private BooleanParameter proxy_i2p_only; private BooleanParameter trace; private AESemaphore sem = new AESemaphore( "I2PInit" ); private AEMonitor this_mon = new AEMonitor( "I2PPluginConnectionManager" ); protected I2PPluginConnectionManager( ClassLoader _class_loader, LoggerChannel _log ) { class_loader = _class_loader; log = _log; } protected void initialise( StringParameter _router_host, IntParameter _router_port, StringParameter _router_options, BooleanParameter _trace ) // BooleanParameter _proxy_i2p_only ) { router_host = _router_host; router_port = _router_port; router_options = _router_options; trace = _trace; // proxy_i2p_only = _proxy_i2p_only; try{ log.log( "Initialising I2P Socket Manager" ); Class i2p_I2PContext = class_loader.loadClass( "net.i2p.I2PAppContext" ); i2p_I2PContext.newInstance(); i2p_Destination = class_loader.loadClass( "net.i2p.data.Destination" ); i2p_Destination_fromBase64 = i2p_Destination.getMethod( "fromBase64", new Class[]{ String.class }); Class i2p_I2PSession = class_loader.loadClass( "net.i2p.client.I2PSession" ); i2p_I2PSession_getMyDestination = i2p_I2PSession.getMethod( "getMyDestination", new Class[0]); i2p_I2PSocketManagerFactory = class_loader.loadClass( "net.i2p.client.streaming.I2PSocketManagerFactory" ); Class i2p_I2PSocketManager = class_loader.loadClass( "net.i2p.client.streaming.I2PSocketManager" ); i2p_I2PSocketManager_getSession = i2p_I2PSocketManager.getMethod( "getSession", new Class[0]); i2p_I2PSocketManager_ping = i2p_I2PSocketManager.getMethod( "ping", new Class[]{ i2p_Destination, long.class }); i2p_I2PSocketManager_connect = i2p_I2PSocketManager.getMethod( "connect", new Class[]{ i2p_Destination }); Class i2p_I2PSocket = class_loader.loadClass( "net.i2p.client.streaming.I2PSocket" ); i2p_I2PSocket_close = i2p_I2PSocket.getMethod( "close", new Class[0] ); i2p_I2PSocket_getInputStream = i2p_I2PSocket.getMethod( "getInputStream", new Class[0] ); i2p_I2PSocket_getOutputStream = i2p_I2PSocket.getMethod( "getOutputStream", new Class[0] ); /* Class i2p_I2PInputStream = class_loader.loadClass( "net.i2p.client.streaming.MessageInputStream" ); i2p_I2PInputStream_read = i2p_I2PInputStream.getMethod( "read", new Class[]{ byte[].class }); Class i2p_I2POutputStream = class_loader.loadClass( "net.i2p.client.streaming.MessageOutputStream" ); i2p_I2POutputStream_write = i2p_I2POutputStream.getMethod( "write", new Class[]{ byte[].class, int.class, int.class }); */ connectToRouter(); }catch( Throwable e ){ log.logAlert( "I2P Router initialisation failed", e ); } } protected void connectToRouter() { Thread t = new AEThread( "I2P Initialiser" ) { public void runSupport() { boolean error_reported = false; while( socket_manager == null ){ long last_create_time = System.currentTimeMillis(); try{ Properties opts = new Properties(); StringTokenizer tok = new StringTokenizer( router_options.getValue()); while (tok.hasMoreTokens()) { String pair = tok.nextToken(); int eq = pair.indexOf('='); if ( (eq <= 0) || (eq >= pair.length()) ){ continue; } String key = pair.substring(0, eq); String val = pair.substring(eq+1); opts.setProperty(key, val); } socket_manager = i2p_I2PSocketManagerFactory.getMethod( "createManager", new Class[] { String.class, int.class, Properties.class }).invoke( null, new Object[] { router_host.getValue(), new Integer(router_port.getValue()), opts }); if ( socket_manager != null ){ log.logAlertRepeatable( LoggerChannel.LT_INFORMATION, "I2P Router connection succeeded" ); Object session = i2p_I2PSocketManager_getSession.invoke( socket_manager, new Object[0] ); final Object my_dest = i2p_I2PSession_getMyDestination.invoke( session, new Object[0] ); final Object this_sm = socket_manager; AEThread pinger = new AEThread( "I2P Pinger" ) { public void runSupport() { int consecutive_fails = 0; while( socket_manager == this_sm ){ try{ Thread.sleep(60000); long start = System.currentTimeMillis(); boolean res = ((Boolean)i2p_I2PSocketManager_ping.invoke( this_sm, new Object[]{ my_dest, new Integer( 10000 )})).booleanValue(); log.log( "I2P router ping response = " + (res?"OK":"Failed") +", elapsed = " + (System.currentTimeMillis() - start )); if ( res){ consecutive_fails = 0; }else{ consecutive_fails++; if ( consecutive_fails == 3 ){ routerFailure( this_sm ); } } }catch( Throwable e ){ log.log( e ); routerFailure( this_sm ); } } } }; pinger.setDaemon( true ); pinger.start(); sem.releaseForever(); break; } }catch( Throwable e ){ log.log( "I2P Router connection failed", e ); } String msg = "I2P Router connection failed, check that the I2P router is running"; if ( error_reported ){ log.log( msg ); }else{ error_reported = true; log.logAlert( LoggerChannel.LT_ERROR, "I2P Router connection failed, check that the I2P router is running" ); } long sleep = 30000 - ( System.currentTimeMillis() - last_create_time ); if ( sleep > 0 ){ try{ Thread.sleep( sleep ); }catch( Throwable e ){ e.printStackTrace(); } } } } }; t.setDaemon( true ); t.start(); } protected void routerFailure( Object failed_socket_manager ) { try{ this_mon.enter(); if ( socket_manager == failed_socket_manager ){ log.logAlertRepeatable( LoggerChannel.LT_ERROR, "I2P Router connection failed" ); socket_manager = null; sem = new AESemaphore( "I2PInit" ); connectToRouter(); } }finally{ this_mon.exit(); } } protected void closedown() { } protected void log( String str ) { log.log( str ); } protected void trace( String str ) { if ( trace.getValue()){ log.log( str ); } } protected boolean proxyI2POnly() { return( false ); // proxy_i2p_only.getValue()); } public AESocksProxyPlugableConnection create( AESocksProxyConnection connection ) throws AEProxyException { return( new I2PPluginConnection( this, connection ) ); } protected Object i2pSocketManager_connect( String address ) throws IOException { sem.reserve(); Object current_socket_manager = socket_manager; if ( current_socket_manager == null || i2p_Destination == null ){ throw( new IOException( "I2P network unavailable" )); } try{ Object remote_dest = i2p_Destination.newInstance(); i2p_Destination_fromBase64.invoke( remote_dest, new Object[]{ address }); Object res = i2p_I2PSocketManager_connect.invoke( current_socket_manager, new Object[]{ remote_dest }); return( res ); }catch( Throwable e ){ if ( Debug.getNestedExceptionMessage(e).toLowerCase().indexOf( "session is closed" ) != -1 ){ routerFailure( current_socket_manager ); } log.log(e); throw( new IOException( e.getMessage())); } } protected void i2pSocket_close( Object socket ) throws IOException { try{ i2p_I2PSocket_close.invoke( socket, new Object[0]); }catch( Throwable e ){ if ( e instanceof IOException ){ throw((IOException)e); } log.log(e); throw( new IOException( e.getMessage())); } } protected InputStream i2pSocket_getInputStream( Object socket ) throws IOException { try{ return((InputStream)i2p_I2PSocket_getInputStream.invoke( socket, new Object[0])); }catch( Throwable e ){ if ( e instanceof IOException ){ throw((IOException)e); } log.log(e); throw( new IOException( e.getMessage())); } } protected OutputStream i2pSocket_getOutputStream( Object socket ) throws IOException { try{ return((OutputStream)i2p_I2PSocket_getOutputStream.invoke( socket, new Object[0])); }catch( Throwable e ){ if ( e instanceof IOException ){ throw((IOException)e); } log.log(e); throw( new IOException( e.getMessage())); } } /* protected int i2pInputStream_read( Object input_stream, byte[] buffer ) throws IOException { try{ Integer res = (Integer)i2p_I2PInputStream_read.invoke( input_stream, new Object[]{buffer}); return( res.intValue()); }catch( Throwable e ){ if ( e instanceof IOException ){ throw((IOException)e); } log.log(e); throw( new IOException( e.getMessage())); } } protected void i2pOutputStream_write( Object output_stream, byte[] buffer, int start, int len ) throws IOException { try{ i2p_I2POutputStream_write.invoke( output_stream, new Object[]{buffer, new Integer(start), new Integer(len)}); }catch( Throwable e ){ if ( e instanceof IOException ){ throw((IOException)e); } log.log(e); throw( new IOException( e.getMessage())); } } */ }