Skip to content
This repository
Browse code

Merge pull request #42 from tomamic/master

Integration with I2P
  • Loading branch information...
commit 5e9fc58ebe0b4d7c43fb2a0584104e944c9bde09 2 parents fd7df01 + 47f46e4
Enrico Franchi authored April 03, 2014
480  blogracy-vuze/src/main/java/com/aelitis/azureus/plugins/networks/i2p/I2PPlugin.java
... ...
@@ -0,0 +1,480 @@
  1
+/*
  2
+ * Created on 09-Dec-2004
  3
+ * Created by Paul Gardner
  4
+ * Copyright (C) 2004 Aelitis, All Rights Reserved.
  5
+ *
  6
+ * This program is free software; you can redistribute it and/or
  7
+ * modify it under the terms of the GNU General Public License
  8
+ * as published by the Free Software Foundation; either version 2
  9
+ * of the License, or (at your option) any later version.
  10
+ * This program is distributed in the hope that it will be useful,
  11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13
+ * GNU General Public License for more details.
  14
+ * You should have received a copy of the GNU General Public License
  15
+ * along with this program; if not, write to the Free Software
  16
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  17
+ * 
  18
+ * AELITIS, SARL au capital de 30,000 euros
  19
+ * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
  20
+ *
  21
+ */
  22
+
  23
+package com.aelitis.azureus.plugins.networks.i2p;
  24
+
  25
+
  26
+import java.io.*;
  27
+import java.net.URL;
  28
+import java.net.URLClassLoader;
  29
+import java.text.SimpleDateFormat;
  30
+import java.util.*;
  31
+
  32
+
  33
+import org.gudy.azureus2.core3.util.Debug;
  34
+import org.gudy.azureus2.plugins.*;
  35
+import org.gudy.azureus2.plugins.logging.LoggerChannel;
  36
+import org.gudy.azureus2.plugins.logging.LoggerChannelListener;
  37
+
  38
+import org.gudy.azureus2.plugins.ui.UIManager;
  39
+import org.gudy.azureus2.plugins.ui.config.*;
  40
+import org.gudy.azureus2.plugins.ui.model.BasicPluginConfigModel;
  41
+import org.gudy.azureus2.plugins.ui.model.BasicPluginViewModel;
  42
+import org.gudy.azureus2.plugins.utils.LocaleUtilities;
  43
+
  44
+import com.aelitis.azureus.core.proxy.socks.AESocksProxyFactory;
  45
+import com.aelitis.azureus.plugins.upnp.UPnPMapping;
  46
+import com.aelitis.azureus.plugins.upnp.UPnPPlugin;
  47
+
  48
+
  49
+/**
  50
+ * @author parg
  51
+ *
  52
+ */
  53
+public class I2PPlugin 
  54
+	implements Plugin
  55
+{
  56
+	private static final int	PROXY_CON_TIMEOUT		= 120*1000;
  57
+	private static final int	PROXY_READ_TIMEOUT		= 120*1000;
  58
+	
  59
+	protected PluginInterface	plugin_interface;
  60
+	
  61
+	public static final String[]	I2P_JARS = { "i2p.jar", "streaming.jar", "mstreaming.jar", "jbigi.jar" };
  62
+	
  63
+	public static final String 	CONFIG_ENABLE		= "enable";
  64
+	public static final boolean CONFIG_ENABLE_DEFAULT	= true;
  65
+	
  66
+	public static final String 	CONFIG_PROXY_PORT			= "proxy_port";
  67
+	public static final int 	CONFIG_PROXY_PORT_DEFAULT	= 0;
  68
+	
  69
+	//public static final String 	CONFIG_PROXY_I2P_ONLY			= "proxy_i2p_only";
  70
+	//public static final boolean CONFIG_PROXY_I2P_ONLY_DEFAULT	= false;
  71
+
  72
+	public static final String 	CONFIG_I2P_LOCATION				= "i2p_location";
  73
+	public static 		String 	CONFIG_I2P_LOCATION_DEFAULT		= "/home/mic/i2p";
  74
+	
  75
+	public static final String 	CONFIG_UPNP_ENABLE			= "upnp_enable";
  76
+	public static final boolean CONFIG_UPNP_ENABLE_DEFAULT	= false;
  77
+	
  78
+	public static final String 	CONFIG_UPNP_LEAVE_MAPPING			= "upnp_persistent_mapping";
  79
+	public static final boolean CONFIG_UPNP_LEAVE_MAPPING_DEFAULT	= true;
  80
+	
  81
+	public static final String 	CONFIG_UPNP_PORT			= "upnp_port";
  82
+	public static final int		CONFIG_UPNP_PORT_DEFAULT	= 8887;
  83
+
  84
+	public static final String 	CONFIG_I2P_ROUTER_HOST			= "i2p_router_host";
  85
+	public static final String 	CONFIG_I2P_ROUTER_HOST_DEFAULT	= "localhost";
  86
+
  87
+	public static final String 	CONFIG_I2P_ROUTER_PORT			= "i2p_router_port";
  88
+	public static final int 	CONFIG_I2P_ROUTER_PORT_DEFAULT	= 7654;
  89
+
  90
+	public static final String 	CONFIG_I2P_ROUTER_OPTIONS			= "i2p_router_options";
  91
+	public static final String 	CONFIG_I2P_ROUTER_OPTIONS_DEFAULT	= "";
  92
+
  93
+	public static final String 	CONFIG_TRACE					= "trace";
  94
+	public static final boolean CONFIG_TRACE_DEFAULT			= false;
  95
+
  96
+	protected LoggerChannel			log;
  97
+	protected LoggerChannelListener	log_temp;
  98
+	protected List					logs	= new ArrayList();
  99
+	
  100
+	protected BooleanParameter 		enable;
  101
+	protected IntParameter			proxy_port;
  102
+	//protected BooleanParameter 		proxy_i2p_only;
  103
+		
  104
+	protected BooleanParameter 		upnp_enable;
  105
+	protected IntParameter			upnp_port;
  106
+	protected BooleanParameter		upnp_persist;
  107
+
  108
+	protected StringParameter		i2p_router_host;
  109
+	protected IntParameter			i2p_router_port;
  110
+	protected StringParameter		i2p_router_options;
  111
+	
  112
+	protected BooleanParameter		trace;
  113
+
  114
+	public void 
  115
+	load(
  116
+		PluginInterface _plugin_interface )
  117
+	{		
  118
+		plugin_interface	= _plugin_interface;
  119
+		
  120
+		log	= plugin_interface.getLogger().getChannel( "I2P Network Plugin");
  121
+		
  122
+		if ( plugin_interface.getUtilities().isWindows()){
  123
+			
  124
+			String	win_def = "C:\\Program Files\\i2p";
  125
+			
  126
+			if ( new File(win_def).exists()){
  127
+				
  128
+				CONFIG_I2P_LOCATION_DEFAULT	= win_def;
  129
+			}
  130
+		}
  131
+		
  132
+		log_temp = 			
  133
+			new LoggerChannelListener()
  134
+			{
  135
+				public void
  136
+				messageLogged(
  137
+					int		type,
  138
+					String	content )
  139
+				{
  140
+					logs.add( content );
  141
+				}
  142
+				
  143
+				public void
  144
+				messageLogged(
  145
+					String		str,
  146
+					Throwable	error )
  147
+				{
  148
+					if ( str.length() > 0 ){
  149
+						logs.add( str );
  150
+					}
  151
+					http://rt.com/news/right-nationalists-storm-ukraine-701/
  152
+					logs.add( Debug.getNestedExceptionMessage( error ));
  153
+				}
  154
+			};
  155
+			
  156
+		log.addListener( log_temp );
  157
+		
  158
+		
  159
+		UIManager	ui_manager = plugin_interface.getUIManager();
  160
+
  161
+		BasicPluginConfigModel config_model = ui_manager.createBasicPluginConfigModel( "plugins", "i2pnet.name");
  162
+						
  163
+		enable 		= config_model.addBooleanParameter2( CONFIG_ENABLE, "i2pnet.enable", CONFIG_ENABLE_DEFAULT );
  164
+		
  165
+		LabelParameter lab1 = config_model.addLabelParameter2( "i2pnet.proxy_port.info" );
  166
+		
  167
+		proxy_port 	= config_model.addIntParameter2( CONFIG_PROXY_PORT, "i2pnet.proxy_port", CONFIG_PROXY_PORT_DEFAULT );
  168
+		
  169
+		/*
  170
+		 * this doesn't work as Java will revert to not using the proxy if it fails. GRRRRRRRRRRR
  171
+		 * 
  172
+		proxy_i2p_only 	= config_model.addBooleanParameter2( CONFIG_PROXY_I2P_ONLY, "i2pnet.proxy_i2p_only", CONFIG_PROXY_I2P_ONLY_DEFAULT );
  173
+
  174
+		LabelParameter lab2 = config_model.addLabelParameter2( "i2pnet.proxy_i2p_only.info" );
  175
+		
  176
+		config_model.createGroup(
  177
+				"i2pnet.i2p_only_group",
  178
+				new Parameter[]{ proxy_i2p_only, lab2 });
  179
+		*/
  180
+		
  181
+		DirectoryParameter	location = config_model.addDirectoryParameter2( CONFIG_I2P_LOCATION, "i2pnet.i2p_location", CONFIG_I2P_LOCATION_DEFAULT );
  182
+		
  183
+		upnp_enable 	= config_model.addBooleanParameter2( CONFIG_UPNP_ENABLE, "i2pnet.upnp_enable", CONFIG_UPNP_ENABLE_DEFAULT );
  184
+		
  185
+		upnp_persist 	= config_model.addBooleanParameter2( CONFIG_UPNP_LEAVE_MAPPING, "i2pnet.upnp_persist", CONFIG_UPNP_LEAVE_MAPPING_DEFAULT );
  186
+		
  187
+		upnp_port 		= config_model.addIntParameter2( CONFIG_UPNP_PORT, "i2pnet.upnp_port", CONFIG_UPNP_PORT_DEFAULT );
  188
+
  189
+		config_model.createGroup(
  190
+			"i2pnet.upnp_group",
  191
+			new Parameter[]{ upnp_enable, upnp_persist, upnp_port });
  192
+		
  193
+			// I2P Router stuff
  194
+		
  195
+		LabelParameter	i2p_lab = config_model.addLabelParameter2( "i2pnet.i2p_options.info" );
  196
+		
  197
+		i2p_router_host 		= config_model.addStringParameter2( CONFIG_I2P_ROUTER_HOST, "i2pnet.i2p_router_host", CONFIG_I2P_ROUTER_HOST_DEFAULT );
  198
+		
  199
+		i2p_router_port 		= config_model.addIntParameter2( CONFIG_I2P_ROUTER_PORT, "i2pnet.i2p_router_port", CONFIG_I2P_ROUTER_PORT_DEFAULT );
  200
+
  201
+		i2p_router_options 		= config_model.addStringParameter2( CONFIG_I2P_ROUTER_OPTIONS, "i2pnet.i2p_router_options", CONFIG_I2P_ROUTER_OPTIONS_DEFAULT );
  202
+
  203
+		config_model.createGroup(
  204
+				"i2pnet.i2p_router_group",
  205
+				new Parameter[]{ i2p_lab, i2p_router_host, i2p_router_port, i2p_router_options });
  206
+
  207
+			
  208
+		trace 	= config_model.addBooleanParameter2( CONFIG_TRACE, "i2pnet.trace_enable", CONFIG_TRACE_DEFAULT );
  209
+
  210
+		enable.addEnabledOnSelection( proxy_port );
  211
+		// enable.addEnabledOnSelection( proxy_i2p_only );
  212
+		enable.addEnabledOnSelection( lab1 );
  213
+		// enable.addEnabledOnSelection( lab2 );
  214
+		enable.addEnabledOnSelection( location );
  215
+		enable.addEnabledOnSelection( upnp_enable );
  216
+		
  217
+		ParameterListener	pl = 
  218
+			new ParameterListener()
  219
+			{
  220
+				public void
  221
+				parameterChanged(
  222
+					Parameter	param  )
  223
+				{
  224
+					upnp_port.setEnabled( enable.getValue() && upnp_enable.getValue());
  225
+					upnp_persist.setEnabled( enable.getValue() && upnp_enable.getValue());
  226
+				}
  227
+			};
  228
+		
  229
+		enable.addListener( pl );
  230
+		upnp_enable.addListener( pl );
  231
+		
  232
+		pl.parameterChanged(null);
  233
+		
  234
+		enable.addEnabledOnSelection( i2p_lab );
  235
+		enable.addEnabledOnSelection( i2p_router_host );
  236
+		enable.addEnabledOnSelection( i2p_router_port );
  237
+		enable.addEnabledOnSelection( i2p_router_options );
  238
+		enable.addEnabledOnSelection( trace );
  239
+
  240
+		if ( enable.getValue()){
  241
+			
  242
+			boolean	bad 	= false;
  243
+			boolean	alert	= false;
  244
+			
  245
+			try{
  246
+				String	loc = location.getValue();
  247
+				
  248
+				if ( loc.length() == 0 || !new File(loc).exists()){
  249
+					
  250
+					bad		= true;
  251
+					alert	= true;
  252
+					
  253
+					throw( new Exception( "I2P install location not defined, plugin initialisation failed." ));				
  254
+				}
  255
+					
  256
+				File	lib = new File( loc, "lib" );
  257
+				
  258
+		   		URL[]	jars = new URL[I2P_JARS.length];
  259
+		   	
  260
+		   		for (int i=0;i<jars.length;i++){
  261
+		   			
  262
+		   			File	jar = new File(lib,I2P_JARS[i] );
  263
+		   			
  264
+		   			if ( !jar.exists()){
  265
+		   				
  266
+		   				bad	= true;
  267
+		   				
  268
+		   				throw( new Exception( "I2P jar file '" + jar + "' not found" ));
  269
+		   			}
  270
+		   			
  271
+		   			jars[i] = jar.toURL();
  272
+		   		}
  273
+		   		
  274
+		   		ClassLoader	class_loader = getClass().getClassLoader();
  275
+    				    		
  276
+	    		if ( class_loader instanceof URLClassLoader ){
  277
+	    			
  278
+	    			URL[]	old = ((URLClassLoader)class_loader).getURLs();
  279
+	  
  280
+	    			URL[]	new_urls = new URL[old.length+jars.length];
  281
+	    			
  282
+	    			System.arraycopy( old, 0, new_urls, 0, old.length );
  283
+	    			
  284
+	    			System.arraycopy( jars, 0, new_urls, old.length, jars.length );
  285
+	    			
  286
+	    			class_loader = new URLClassLoader(
  287
+	    								new_urls,
  288
+	    								class_loader );
  289
+	    		}else{
  290
+	    			  		
  291
+	    			class_loader = new URLClassLoader(jars,class_loader);
  292
+	    		}
  293
+				
  294
+				int	port = proxy_port.getValue();
  295
+				
  296
+				if ( port == 0 ){
  297
+					
  298
+					bad	= true;
  299
+					
  300
+					throw( new Exception( "I2P poxy port not defined, can't initialise" ));
  301
+				}
  302
+				
  303
+				final I2PPluginConnectionManager	con_man = new I2PPluginConnectionManager( class_loader, log );
  304
+				
  305
+				AESocksProxyFactory.create(	port, PROXY_CON_TIMEOUT, PROXY_READ_TIMEOUT, con_man );
  306
+				
  307
+				log.log( "Established network proxy on port " + port );
  308
+				
  309
+				plugin_interface.addListener(
  310
+					new PluginListener()
  311
+					{
  312
+						public void
  313
+						initializationComplete()
  314
+						{
  315
+							con_man.initialise(
  316
+								i2p_router_host,
  317
+								i2p_router_port,
  318
+								i2p_router_options,
  319
+								trace );
  320
+								// proxy_i2p_only );
  321
+						}
  322
+						
  323
+						public void
  324
+						closedownInitiated()
  325
+						{
  326
+							con_man.closedown();
  327
+						}
  328
+						
  329
+						public void
  330
+						closedownComplete()
  331
+						{
  332
+							
  333
+						}
  334
+					});
  335
+	
  336
+			}catch( Throwable e ){
  337
+				
  338
+				if ( bad ){
  339
+					
  340
+					if ( alert ){
  341
+						
  342
+						log.logAlert( LoggerChannel.LT_ERROR, e.getMessage());
  343
+						
  344
+					}else{
  345
+						
  346
+						log.log( e.getMessage());
  347
+					}
  348
+				}else{
  349
+				
  350
+					log.log(e);
  351
+				}
  352
+			}
  353
+		}
  354
+	}
  355
+	
  356
+	public void 
  357
+	initialize(
  358
+		PluginInterface _plugin_interface )
  359
+	{	
  360
+		LocaleUtilities loc_utils = plugin_interface.getUtilities().getLocaleUtilities();
  361
+		
  362
+		UIManager	ui_manager = plugin_interface.getUIManager();
  363
+		
  364
+		final BasicPluginViewModel	view_model = 
  365
+			ui_manager.createBasicPluginViewModel( loc_utils.getLocalisedMessageText( "i2pnet.name" ));
  366
+
  367
+		view_model.getActivity().setVisible( false );
  368
+		view_model.getProgress().setVisible( false );
  369
+		
  370
+		log.removeListener( log_temp );
  371
+		
  372
+		log.addListener(
  373
+				new LoggerChannelListener()
  374
+				{
  375
+					public void
  376
+					messageLogged(
  377
+						int		type,
  378
+						String	content )
  379
+					{
  380
+						view_model.getLogArea().appendText( getTimeStamp() + content + "\n" );
  381
+					}
  382
+					
  383
+					public void
  384
+					messageLogged(
  385
+						String		str,
  386
+						Throwable	error )
  387
+					{
  388
+						if ( str.length() > 0 ){
  389
+							view_model.getLogArea().appendText( getTimeStamp() + str + "\n" );
  390
+						}
  391
+						view_model.getLogArea().appendText( getTimeStamp() + Debug.getNestedExceptionMessage(error) + "\n" );
  392
+					}
  393
+					
  394
+					protected String
  395
+					getTimeStamp()
  396
+					{
  397
+						return( "[" + new SimpleDateFormat( "HH:mm:ss" ).format(new Date()) + "] " );
  398
+					}
  399
+				});
  400
+		
  401
+		for (int i=0;i<logs.size();i++){
  402
+			
  403
+			log.log((String)logs.get(i));
  404
+		}
  405
+		
  406
+		logs.clear();
  407
+		
  408
+		view_model.getStatus().setText( enable.getValue()?"Enabled":"Disabled");
  409
+		
  410
+		enable.addListener(
  411
+			new ParameterListener()
  412
+			{
  413
+				public void
  414
+				parameterChanged(
  415
+					Parameter		p )
  416
+				{					
  417
+					view_model.getStatus().setText( enable.getValue()?"Enabled":"Disabled");
  418
+				}
  419
+			});	
  420
+		
  421
+		
  422
+		if ( enable.getValue()){
  423
+			
  424
+			if ( plugin_interface.getPluginconfig().getBooleanParameter(
  425
+					PluginConfig.CORE_PARAM_BOOLEAN_SOCKS_PROXY_NO_INWARD_CONNECTION )){
  426
+				
  427
+				log.logAlert( 
  428
+						LoggerChannel.LT_ERROR,
  429
+						"I2P Plugin requires that the 'inform tracker of limitation' setting for 'Connection' is deselected - please amend it" );
  430
+				
  431
+			}
  432
+			
  433
+			plugin_interface.addListener(
  434
+					new PluginListener()
  435
+					{
  436
+						public void
  437
+						initializationComplete()
  438
+						{
  439
+							PluginInterface pi_upnp = plugin_interface.getPluginManager().getPluginInterfaceByClass( UPnPPlugin.class );
  440
+							
  441
+							if ( pi_upnp == null ){
  442
+								
  443
+								log.log( "No UPnP plugin available, not attempting port mapping");
  444
+								
  445
+							}else{
  446
+								
  447
+								if ( upnp_enable.getValue()){
  448
+									
  449
+									UPnPMapping	mapping = 
  450
+										((UPnPPlugin)pi_upnp.getPlugin()).addMapping( 
  451
+											plugin_interface.getPluginName(), 
  452
+											true, upnp_port.getValue(), 
  453
+											true );
  454
+									
  455
+									if ( upnp_persist.getValue()){
  456
+										
  457
+										mapping.setPersistent( UPnPMapping.PT_PERSISTENT );
  458
+									}
  459
+									
  460
+								}else{
  461
+									
  462
+									log.log( "UPnP disabled for the plugin, not attempting port mapping");
  463
+									
  464
+								}
  465
+							}
  466
+						}
  467
+						
  468
+						public void
  469
+						closedownInitiated()
  470
+						{
  471
+						}
  472
+						
  473
+						public void
  474
+						closedownComplete()
  475
+						{	
  476
+						}
  477
+					});
  478
+		}
  479
+	}
  480
+}
556  blogracy-vuze/src/main/java/com/aelitis/azureus/plugins/networks/i2p/I2PPluginConnection.java
... ...
@@ -0,0 +1,556 @@
  1
+/*
  2
+ * Created on 13-Dec-2004
  3
+ * Created by Paul Gardner
  4
+ * Copyright (C) 2004 Aelitis, All Rights Reserved.
  5
+ *
  6
+ * This program is free software; you can redistribute it and/or
  7
+ * modify it under the terms of the GNU General Public License
  8
+ * as published by the Free Software Foundation; either version 2
  9
+ * of the License, or (at your option) any later version.
  10
+ * This program is distributed in the hope that it will be useful,
  11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13
+ * GNU General Public License for more details.
  14
+ * You should have received a copy of the GNU General Public License
  15
+ * along with this program; if not, write to the Free Software
  16
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  17
+ * 
  18
+ * AELITIS, SARL au capital de 30,000 euros
  19
+ * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
  20
+ *
  21
+ */
  22
+
  23
+package com.aelitis.azureus.plugins.networks.i2p;
  24
+
  25
+import java.io.*;
  26
+import java.net.InetAddress;
  27
+import java.nio.ByteBuffer;
  28
+import java.nio.channels.SocketChannel;
  29
+
  30
+import org.gudy.azureus2.core3.util.AEMonitor;
  31
+import org.gudy.azureus2.core3.util.AERunnable;
  32
+import org.gudy.azureus2.core3.util.AESemaphore;
  33
+import org.gudy.azureus2.core3.util.AEThread;
  34
+import org.gudy.azureus2.core3.util.Debug;
  35
+import org.gudy.azureus2.core3.util.ThreadPool;
  36
+
  37
+import com.aelitis.azureus.core.proxy.*;
  38
+import com.aelitis.azureus.core.proxy.socks.*;
  39
+
  40
+/**
  41
+ * @author parg
  42
+ *
  43
+ */
  44
+
  45
+public class 
  46
+I2PPluginConnection
  47
+	implements AESocksProxyPlugableConnection
  48
+{
  49
+	private static final boolean TRACE = true;
  50
+	
  51
+		// try to buffer at least a whole block
  52
+	
  53
+	public static final int RELAY_BUFFER_SIZE	= 64*1024 + 256;
  54
+	
  55
+	protected I2PPluginConnectionManager	con_man;
  56
+	protected Object						socket;
  57
+	protected boolean						socket_closed;
  58
+	
  59
+	protected AESocksProxyConnection		proxy_connection;
  60
+	
  61
+	protected proxyStateRelayData			relay_state;
  62
+	
  63
+	protected AEMonitor			this_mon	= new AEMonitor( "I2PPluginConnection" );
  64
+
  65
+	protected
  66
+	I2PPluginConnection(
  67
+		I2PPluginConnectionManager		_con_man,
  68
+		AESocksProxyConnection			_proxy_connection )
  69
+	{
  70
+		con_man				= _con_man;
  71
+		proxy_connection	= _proxy_connection;
  72
+		
  73
+		proxy_connection.disableDNSLookups();
  74
+	}
  75
+	
  76
+	public String
  77
+	getName()
  78
+	{
  79
+		return( "I2PPluginConnection" );
  80
+	}
  81
+	
  82
+	public InetAddress
  83
+	getLocalAddress()
  84
+	{
  85
+		try{
  86
+			return( InetAddress.getLocalHost() );
  87
+			
  88
+		}catch( Throwable e ){
  89
+			
  90
+			Debug.printStackTrace(e);
  91
+			
  92
+			return( null );
  93
+		}
  94
+	}
  95
+	
  96
+	public int
  97
+	getLocalPort()
  98
+	{
  99
+		return( -1 );
  100
+	}
  101
+
  102
+	public void
  103
+	connect(
  104
+		AESocksProxyAddress		_address )
  105
+		
  106
+		throws IOException
  107
+	{
  108
+		if ( TRACE ){
  109
+			
  110
+			con_man.log( "connect request to " + _address.getUnresolvedAddress() + "/" + _address.getAddress() + "/" + _address.getPort());
  111
+		}
  112
+		
  113
+		if ( _address.getAddress() != null ){
  114
+		
  115
+			if ( con_man.proxyI2POnly()){
  116
+				
  117
+				if ( TRACE ){
  118
+					
  119
+					con_man.log( "    NOT delegating resolved, non-I2P delegation disabled" );
  120
+				}
  121
+				
  122
+				throw( new IOException( "delegation to non-I2P locations disabled" ));
  123
+			}
  124
+			
  125
+			if ( TRACE ){
  126
+				
  127
+				con_man.log( "    delegating resolved" );
  128
+			}
  129
+			
  130
+			AESocksProxyPlugableConnection	delegate = proxy_connection.getProxy().getDefaultPlugableConnection( proxy_connection );
  131
+			
  132
+			proxy_connection.setDelegate( delegate );
  133
+			
  134
+			delegate.connect( _address );
  135
+			
  136
+		}else{ 
  137
+			
  138
+			final String	externalised_address = AEProxyFactory.getAddressMapper().externalise(_address.getUnresolvedAddress());
  139
+		
  140
+			if ( !externalised_address.toLowerCase().endsWith(".i2p")){
  141
+				
  142
+				if ( con_man.proxyI2POnly()){
  143
+					
  144
+					if ( TRACE ){
  145
+						
  146
+						con_man.log( "    NOT delegating unresolved, non-I2P delegation disabled" );
  147
+					}
  148
+					
  149
+					throw( new IOException( "delegation to non-I2P locations disabled" ));
  150
+				}
  151
+				
  152
+				if ( TRACE ){
  153
+					
  154
+					con_man.log( "    delegating unresolved" );
  155
+				}
  156
+				
  157
+				AESocksProxyPlugableConnection	delegate = proxy_connection.getProxy().getDefaultPlugableConnection( proxy_connection );
  158
+				
  159
+				proxy_connection.enableDNSLookups();
  160
+				
  161
+				proxy_connection.setDelegate( delegate );
  162
+				
  163
+				delegate.connect( _address );
  164
+
  165
+			}else{
  166
+
  167
+				AEThread	connect_thread = 
  168
+					new AEThread( "I2PConnect")
  169
+					{
  170
+						public void
  171
+						runSupport()
  172
+						{
  173
+							if ( TRACE ){
  174
+								
  175
+								con_man.log( "    delegating to I2P" );
  176
+							}
  177
+							
  178
+							try{
  179
+								
  180
+									// remove the .i2p
  181
+								
  182
+								String new_externalised_address = externalised_address.substring( 0, externalised_address.length() - 4 );
  183
+								
  184
+						        socket = con_man.i2pSocketManager_connect( new_externalised_address );
  185
+						       	
  186
+						        proxy_connection.connected();
  187
+						        
  188
+						        
  189
+							}catch( Throwable e ){
  190
+								
  191
+								try{
  192
+									proxy_connection.close();
  193
+									
  194
+								}catch( Throwable f ){
  195
+									
  196
+									f.printStackTrace();
  197
+								}
  198
+								
  199
+								e.printStackTrace();
  200
+								
  201
+								con_man.log( "I2PSocket creation fails: " + Debug.getNestedExceptionMessage(e) );
  202
+							}
  203
+						}
  204
+					};
  205
+					
  206
+				connect_thread.setDaemon( true );
  207
+				
  208
+				connect_thread.start();
  209
+			}
  210
+		}
  211
+	}
  212
+	
  213
+	public void
  214
+	relayData()
  215
+	
  216
+		throws IOException
  217
+	{
  218
+		try{
  219
+			this_mon.enter();
  220
+		
  221
+			if ( socket_closed ){
  222
+			
  223
+				throw( new IOException( "I2PPluginConnection::relayData: socket already closed"));
  224
+			}
  225
+		
  226
+			relay_state = new proxyStateRelayData( proxy_connection.getConnection());
  227
+			
  228
+		}finally{
  229
+			
  230
+			this_mon.exit();
  231
+		}
  232
+	}
  233
+	
  234
+	public void
  235
+	close()
  236
+	
  237
+		throws IOException
  238
+	{
  239
+		try{
  240
+			this_mon.enter();
  241
+		
  242
+			if ( socket != null && !socket_closed ){
  243
+				
  244
+				socket_closed	= true;
  245
+			
  246
+				if ( relay_state != null ){
  247
+					
  248
+					relay_state.close();
  249
+				}
  250
+				
  251
+				final Object	f_socket	= socket;
  252
+				
  253
+				socket	= null;
  254
+				
  255
+				AEThread t = 
  256
+					new AEThread( "I2P SocketCloser" )
  257
+					{
  258
+						public void
  259
+						runSupport()
  260
+						{
  261
+							try{
  262
+								con_man.i2pSocket_close( f_socket );
  263
+								
  264
+							}catch( Throwable e ){
  265
+								
  266
+							}
  267
+						}
  268
+					};
  269
+					
  270
+				t.setDaemon(true);
  271
+				
  272
+				t.start();
  273
+			}
  274
+		}finally{
  275
+			
  276
+			this_mon.exit();
  277
+		}
  278
+	}
  279
+	
  280
+	protected class
  281
+	proxyStateRelayData
  282
+		implements AEProxyState
  283
+	{
  284
+		protected AEProxyConnection		connection;
  285
+		protected ByteBuffer			source_buffer;
  286
+		protected ByteBuffer			target_buffer;
  287
+		
  288
+		protected SocketChannel			source_channel;
  289
+		
  290
+		protected InputStream			input_stream;
  291
+		protected OutputStream			output_stream;
  292
+		
  293
+		protected long					outward_bytes	= 0;
  294
+		protected long					inward_bytes	= 0;
  295
+		
  296
+		protected AESemaphore			write_sem = new AESemaphore( "I2PSocket write sem" );
  297
+		
  298
+		protected ThreadPool			async_pool = new ThreadPool( "I2PSocket async", 2 );
  299
+		
  300
+		protected
  301
+		proxyStateRelayData(
  302
+			AEProxyConnection	_connection )
  303
+		
  304
+			throws IOException
  305
+		{		
  306
+			connection	= _connection;
  307
+			
  308
+			source_channel	= connection.getSourceChannel();
  309
+			
  310
+			source_buffer	= ByteBuffer.allocate( RELAY_BUFFER_SIZE );
  311
+			
  312
+			connection.setReadState( this );
  313
+			
  314
+			connection.setWriteState( this );
  315
+			
  316
+			connection.requestReadSelect( source_channel );
  317
+						
  318
+			connection.setConnected();
  319
+			
  320
+			input_stream 	= con_man.i2pSocket_getInputStream( socket );
  321
+			output_stream 	= con_man.i2pSocket_getOutputStream( socket );
  322
+
  323
+			async_pool.run(
  324
+				new AERunnable()
  325
+				{
  326
+					public void
  327
+					runSupport()
  328
+					{
  329
+						byte[]	buffer = new byte[RELAY_BUFFER_SIZE];
  330
+						
  331
+						
  332
+						while( !connection.isClosed()){
  333
+						
  334
+							try{
  335
+								con_man.trace( "I2PCon: " + getStateName() + " : read Starts <- I2P " );
  336
+
  337
+								long	start = System.currentTimeMillis();
  338
+								
  339
+								int	len = input_stream.read( buffer );
  340
+								
  341
+								if ( len <= 0 ){
  342
+									
  343
+									break;
  344
+								}
  345
+															
  346
+								con_man.trace( "I2PCon: " + getStateName() + " : read Done <- I2P - " + len + ", elapsed = " + ( System.currentTimeMillis() - start ));
  347
+								
  348
+								if ( target_buffer != null ){
  349
+									
  350
+									Debug.out("I2PluginConnection: target buffer should be null" );
  351
+								}
  352
+								
  353
+								target_buffer = ByteBuffer.wrap( buffer, 0, len );
  354
+								
  355
+								read();
  356
+								
  357
+							}catch( Throwable e ){
  358
+								
  359
+								if ( 	e instanceof IOException &&
  360
+										e.getMessage() != null &&
  361
+										e.getMessage().startsWith( "Already closed" )){
  362
+								
  363
+										// ignore this one
  364
+									
  365
+								}else{
  366
+										
  367
+									Debug.printStackTrace(e);
  368
+								}
  369
+								
  370
+								break;
  371
+							}
  372
+						}
  373
+						
  374
+						if ( !proxy_connection.isClosed()){
  375
+							
  376
+							try{
  377
+								proxy_connection.close();
  378
+								
  379
+							}catch( IOException e ){
  380
+								
  381
+								Debug.printStackTrace(e);
  382
+							}
  383
+						}
  384
+					}
  385
+				});
  386
+		}
  387
+		
  388
+		protected void
  389
+		close()
  390
+		{						
  391
+			con_man.trace( "I2PCon: " + getStateName() + " close" );
  392
+			
  393
+			write_sem.releaseForever();
  394
+		}
  395
+		
  396
+		protected void
  397
+		read()
  398
+		
  399
+			throws IOException
  400
+		{
  401
+				// data from I2P
  402
+			
  403
+			connection.setTimeStamp();
  404
+		
  405
+			int written = source_channel.write( target_buffer );
  406
+				
  407
+			con_man.trace( "I2PCon: " + getStateName() + " : write -> AZ - " + written );
  408
+			
  409
+			inward_bytes += written;
  410
+			
  411
+			if ( target_buffer.hasRemaining()){
  412
+			
  413
+				connection.requestWriteSelect( source_channel );
  414
+				
  415
+				write_sem.reserve();
  416
+			}
  417
+			
  418
+			target_buffer	= null;
  419
+		}
  420
+		
  421
+		public boolean
  422
+		read(
  423
+			SocketChannel 		sc )
  424
+		
  425
+			throws IOException
  426
+		{
  427
+			if ( source_buffer.position() != 0 ){
  428
+				
  429
+				Debug.out( "I2PluginConnection: source buffer position invalid" );
  430
+			}
  431
+			
  432
+				// data read from source
  433
+			
  434
+			connection.setTimeStamp();
  435
+															
  436
+			final int	len = sc.read( source_buffer );
  437
+	
  438
+			if ( len == 0 ){
  439
+				
  440
+				return( false );
  441
+			}
  442
+			
  443
+			if ( len == -1 ){
  444
+				
  445
+				throw( new IOException( "read channel shutdown" ));
  446
+				
  447
+			}else{
  448
+				
  449
+				if ( source_buffer.position() > 0 ){
  450
+					
  451
+					connection.cancelReadSelect( source_channel );
  452
+					
  453
+					con_man.trace( "I2PCon: " + getStateName() + " : read <- AZ - " + len );
  454
+					
  455
+						// offload the write to separate thread as can't afford to block the
  456
+						// proxy
  457
+				
  458
+					async_pool.run(
  459
+						new AERunnable()
  460
+						{
  461
+							public void
  462
+							runSupport()
  463
+							{
  464
+								try{					
  465
+									source_buffer.flip();
  466
+									
  467
+									long	start = System.currentTimeMillis();
  468
+									
  469
+									con_man.trace( "I2PCon: " + getStateName() + " : write Starts -> I2P - " + len );
  470
+									
  471
+									output_stream.write( source_buffer.array(), 0, len );
  472
+					
  473
+									source_buffer.position( 0 );
  474
+									
  475
+									source_buffer.limit( source_buffer.capacity());
  476
+									
  477
+									// output_stream.flush();
  478
+									
  479
+									con_man.trace( "I2PCon: " + getStateName() + " : write done -> I2P - " + len + ", elapsed = " + ( System.currentTimeMillis() - start ));
  480
+									
  481
+									outward_bytes += len;
  482
+									
  483
+									connection.requestReadSelect( source_channel );								
  484
+
  485
+								}catch( Throwable e ){
  486
+									
  487
+									connection.failed( e );
  488
+								}