2004-04-08 04:41:54 +00:00
/ * I2PTunnel is GPL ' ed ( with the exception mentioned in I2PTunnel . java )
* ( c ) 2003 - 2004 mihi
* /
package net.i2p.i2ptunnel ;
2009-10-09 13:56:34 +00:00
import java.io.File ;
2009-03-01 23:14:38 +00:00
import java.io.FileInputStream ;
2004-04-08 04:41:54 +00:00
import java.io.IOException ;
2004-04-20 15:43:04 +00:00
import java.io.InterruptedIOException ;
2004-04-19 21:47:06 +00:00
import java.net.ConnectException ;
2004-04-08 04:41:54 +00:00
import java.net.InetAddress ;
2004-04-19 21:47:06 +00:00
import java.net.NoRouteToHostException ;
2004-04-08 04:41:54 +00:00
import java.net.ServerSocket ;
import java.net.Socket ;
import java.net.UnknownHostException ;
import java.util.ArrayList ;
import java.util.List ;
import java.util.Properties ;
2010-12-04 18:47:22 +00:00
import java.util.concurrent.Executors ;
import java.util.concurrent.SynchronousQueue ;
import java.util.concurrent.RejectedExecutionException ;
import java.util.concurrent.ThreadPoolExecutor ;
import java.util.concurrent.TimeUnit ;
import java.util.concurrent.ThreadFactory ;
2004-04-08 04:41:54 +00:00
2005-10-10 22:58:18 +00:00
import net.i2p.I2PAppContext ;
2008-07-16 13:42:54 +00:00
import net.i2p.I2PException ;
2004-09-07 07:17:02 +00:00
import net.i2p.client.I2PSession ;
2004-04-08 04:41:54 +00:00
import net.i2p.client.streaming.I2PSocket ;
import net.i2p.client.streaming.I2PSocketManager ;
import net.i2p.client.streaming.I2PSocketManagerFactory ;
import net.i2p.client.streaming.I2PSocketOptions ;
import net.i2p.data.Destination ;
import net.i2p.util.EventDispatcher ;
2009-12-07 21:25:27 +00:00
import net.i2p.util.I2PAppThread ;
2004-04-10 11:39:00 +00:00
import net.i2p.util.Log ;
2004-04-08 04:41:54 +00:00
2004-04-10 11:45:02 +00:00
public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runnable {
2010-12-03 15:20:00 +00:00
protected final Log _log ;
protected final I2PAppContext _context ;
protected final Logging l ;
2004-04-10 11:45:02 +00:00
2004-12-02 00:35:17 +00:00
static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000 ;
2004-04-08 04:41:54 +00:00
2004-05-04 08:14:19 +00:00
private static volatile long __clientId = 0 ;
2004-05-19 15:20:55 +00:00
protected long _clientId ;
2009-04-11 13:55:38 +00:00
protected final Object sockLock = new Object ( ) ; // Guards sockMgr and mySockets
protected I2PSocketManager sockMgr ; // should be final and use a factory. LINT
2011-07-16 20:44:40 +00:00
protected final List < I2PSocket > mySockets = new ArrayList ( ) ;
2009-06-04 15:14:41 +00:00
protected boolean _ownDest ;
2004-04-08 04:41:54 +00:00
protected Destination dest = null ;
private int localPort ;
private boolean listenerReady = false ;
2011-07-15 20:52:18 +00:00
protected ServerSocket ss ;
2004-04-10 11:45:02 +00:00
2009-04-11 13:55:38 +00:00
private final Object startLock = new Object ( ) ;
2004-04-08 04:41:54 +00:00
private boolean startRunning = false ;
2009-04-11 13:55:38 +00:00
// private Object closeLock = new Object();
2004-04-10 11:45:02 +00:00
2009-04-11 13:55:38 +00:00
// private byte[] pubkey;
2004-04-08 04:41:54 +00:00
2009-03-01 23:14:38 +00:00
private String privKeyFile ;
2004-04-08 04:41:54 +00:00
2010-01-10 11:23:20 +00:00
// true if we are chained from a server.
private boolean chained = false ;
2010-12-04 18:47:22 +00:00
/** how long to wait before dropping an idle thread */
private static final long HANDLER_KEEPALIVE_MS = 2 * 60 * 1000 ;
/ * *
* We keep a static pool of socket handlers for all clients ,
* as there is no need for isolation on the client side .
* Extending classes may use it for other purposes .
* Not for use by servers , as there is no limit on threads .
* /
2011-06-18 19:42:08 +00:00
private static volatile ThreadPoolExecutor _executor ;
2010-12-04 18:47:22 +00:00
private static int _executorThreadCount ;
2011-07-09 14:30:33 +00:00
private static final Object _executorLock = new Object ( ) ;
2010-12-04 18:47:22 +00:00
2011-07-09 14:30:33 +00:00
/ * *
* This constructor always starts the tunnel ( ignoring the i2cp . delayOpen option ) .
* It is used to add a client to an existing socket manager .
*
2011-07-13 14:20:20 +00:00
* @param localPort if 0 , use any port , get actual port selected with getLocalPort ( )
2011-07-09 14:30:33 +00:00
* @param sktMgr the existing socket manager
* /
2010-01-15 03:32:35 +00:00
public I2PTunnelClientBase ( int localPort , Logging l , I2PSocketManager sktMgr ,
2010-01-10 11:23:20 +00:00
I2PTunnel tunnel , EventDispatcher notifyThis , long clientId )
throws IllegalArgumentException {
super ( localPort + " (uninitialized) " , notifyThis , tunnel ) ;
chained = true ;
2010-01-15 03:32:35 +00:00
sockMgr = sktMgr ;
2010-01-10 11:23:20 +00:00
_clientId = clientId ;
this . localPort = localPort ;
this . l = l ;
_ownDest = true ; // == ! shared client
_context = tunnel . getContext ( ) ;
_context . statManager ( ) . createRateStat ( " i2ptunnel.client.closeBacklog " , " How many pending sockets remain when we close one due to backlog? " , " I2PTunnel " , new long [ ] { 60 * 1000 , 10 * 60 * 1000 , 60 * 60 * 1000 } ) ;
_context . statManager ( ) . createRateStat ( " i2ptunnel.client.closeNoBacklog " , " How many pending sockets remain when it was removed prior to backlog timeout? " , " I2PTunnel " , new long [ ] { 60 * 1000 , 10 * 60 * 1000 , 60 * 60 * 1000 } ) ;
_context . statManager ( ) . createRateStat ( " i2ptunnel.client.manageTime " , " How long it takes to accept a socket and fire it into an i2ptunnel runner (or queue it for the pool)? " , " I2PTunnel " , new long [ ] { 60 * 1000 , 10 * 60 * 1000 , 60 * 60 * 1000 } ) ;
_context . statManager ( ) . createRateStat ( " i2ptunnel.client.buildRunTime " , " How long it takes to run a queued socket into an i2ptunnel runner? " , " I2PTunnel " , new long [ ] { 60 * 1000 , 10 * 60 * 1000 , 60 * 60 * 1000 } ) ;
2010-12-03 15:20:00 +00:00
_log = _context . logManager ( ) . getLog ( getClass ( ) ) ;
2010-01-10 11:23:20 +00:00
2011-07-09 14:30:33 +00:00
synchronized ( _executorLock ) {
2011-06-17 00:09:05 +00:00
if ( _executor = = null )
_executor = new CustomThreadPoolExecutor ( ) ;
}
2011-06-18 19:42:08 +00:00
2010-12-04 18:47:22 +00:00
Thread t = new I2PAppThread ( this , " Client " + tunnel . listenHost + ':' + localPort ) ;
2010-01-10 11:23:20 +00:00
t . start ( ) ;
open = true ;
synchronized ( this ) {
while ( ! listenerReady & & open ) {
try {
wait ( ) ;
} catch ( InterruptedException e ) {
// ignore
}
}
}
if ( open & & listenerReady ) {
2010-11-19 14:41:26 +00:00
l . log ( " Client ready, listening on " + tunnel . listenHost + ':' + localPort ) ;
2010-01-10 11:23:20 +00:00
notifyEvent ( " openBaseClientResult " , " ok " ) ;
} else {
2010-11-19 14:41:26 +00:00
l . log ( " Client error for " + tunnel . listenHost + ':' + localPort + " , check logs " ) ;
2010-01-10 11:23:20 +00:00
notifyEvent ( " openBaseClientResult " , " error " ) ;
}
}
2010-12-04 18:47:22 +00:00
2011-07-09 14:30:33 +00:00
/ * *
* The main constructor .
* This may take a LONG time if building and starting a new manager .
*
2011-07-13 14:20:20 +00:00
* @param localPort if 0 , use any port , get actual port selected with getLocalPort ( )
2011-07-09 14:30:33 +00:00
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we can ' t create a socketManager
* /
2009-03-01 23:14:38 +00:00
public I2PTunnelClientBase ( int localPort , boolean ownDest , Logging l ,
EventDispatcher notifyThis , String handlerName ,
I2PTunnel tunnel ) throws IllegalArgumentException {
this ( localPort , ownDest , l , notifyThis , handlerName , tunnel , null ) ;
}
2004-08-03 08:18:10 +00:00
/ * *
2011-07-09 14:30:33 +00:00
* Use this to build a client with a persistent private key .
* This may take a LONG time if building and starting a new manager .
*
2011-07-13 14:20:20 +00:00
* @param localPort if 0 , use any port , get actual port selected with getLocalPort ( )
2011-07-09 14:30:33 +00:00
* @param pkf Path to the private key file , or null to generate a transient key
2009-03-01 23:14:38 +00:00
*
2004-08-03 08:18:10 +00:00
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
2011-07-09 14:30:33 +00:00
* badly that we can ' t create a socketManager
2004-08-03 08:18:10 +00:00
* /
public I2PTunnelClientBase ( int localPort , boolean ownDest , Logging l ,
EventDispatcher notifyThis , String handlerName ,
2009-03-01 23:14:38 +00:00
I2PTunnel tunnel , String pkf ) throws IllegalArgumentException {
2004-07-10 16:59:49 +00:00
super ( localPort + " (uninitialized) " , notifyThis , tunnel ) ;
2004-05-04 08:14:19 +00:00
_clientId = + + __clientId ;
2004-04-10 11:45:02 +00:00
this . localPort = localPort ;
this . l = l ;
2009-06-04 15:14:41 +00:00
_ownDest = ownDest ; // == ! shared client
2009-03-01 23:14:38 +00:00
2004-04-10 11:45:02 +00:00
2005-10-10 22:58:18 +00:00
_context = tunnel . getContext ( ) ;
_context . statManager ( ) . createRateStat ( " i2ptunnel.client.closeBacklog " , " How many pending sockets remain when we close one due to backlog? " , " I2PTunnel " , new long [ ] { 60 * 1000 , 10 * 60 * 1000 , 60 * 60 * 1000 } ) ;
_context . statManager ( ) . createRateStat ( " i2ptunnel.client.closeNoBacklog " , " How many pending sockets remain when it was removed prior to backlog timeout? " , " I2PTunnel " , new long [ ] { 60 * 1000 , 10 * 60 * 1000 , 60 * 60 * 1000 } ) ;
_context . statManager ( ) . createRateStat ( " i2ptunnel.client.manageTime " , " How long it takes to accept a socket and fire it into an i2ptunnel runner (or queue it for the pool)? " , " I2PTunnel " , new long [ ] { 60 * 1000 , 10 * 60 * 1000 , 60 * 60 * 1000 } ) ;
_context . statManager ( ) . createRateStat ( " i2ptunnel.client.buildRunTime " , " How long it takes to run a queued socket into an i2ptunnel runner? " , " I2PTunnel " , new long [ ] { 60 * 1000 , 10 * 60 * 1000 , 60 * 60 * 1000 } ) ;
2010-12-03 15:20:00 +00:00
_log = _context . logManager ( ) . getLog ( getClass ( ) ) ;
2005-10-10 22:58:18 +00:00
2011-07-09 14:30:33 +00:00
synchronized ( _executorLock ) {
2011-06-18 19:42:08 +00:00
if ( _executor = = null )
_executor = new CustomThreadPoolExecutor ( ) ;
}
2009-10-09 13:56:34 +00:00
// normalize path so we can find it
if ( pkf ! = null ) {
File keyFile = new File ( pkf ) ;
if ( ! keyFile . isAbsolute ( ) )
keyFile = new File ( _context . getConfigDir ( ) , pkf ) ;
this . privKeyFile = keyFile . getAbsolutePath ( ) ;
}
2005-09-01 00:20:16 +00:00
// no need to load the netDb with leaseSets for destinations that will never
// be looked up
2011-07-14 20:06:31 +00:00
boolean dccEnabled = ( this instanceof I2PTunnelIRCClient ) & &
2012-09-28 17:50:41 +00:00
Boolean . parseBoolean ( tunnel . getClientOptions ( ) . getProperty ( I2PTunnelIRCClient . PROP_DCC ) ) ;
2011-07-14 20:06:31 +00:00
if ( ! dccEnabled )
tunnel . getClientOptions ( ) . setProperty ( " i2cp.dontPublishLeaseSet " , " true " ) ;
2005-09-01 00:20:16 +00:00
2012-09-28 17:50:41 +00:00
boolean openNow = ! Boolean . parseBoolean ( tunnel . getClientOptions ( ) . getProperty ( " i2cp.delayOpen " ) ) ;
2009-03-09 15:11:45 +00:00
if ( openNow ) {
while ( sockMgr = = null ) {
2009-06-04 15:14:41 +00:00
verifySocketManager ( ) ;
2009-03-09 15:11:45 +00:00
if ( sockMgr = = null ) {
2010-11-19 14:41:26 +00:00
_log . error ( " Unable to connect to router and build tunnels for " + handlerName ) ;
// FIXME there is a loop in buildSocketManager(), do we really need another one here?
// no matter, buildSocketManager() now throws an IllegalArgumentException
2009-03-09 15:11:45 +00:00
try { Thread . sleep ( 10 * 1000 ) ; } catch ( InterruptedException ie ) { }
2005-03-04 06:09:20 +00:00
}
}
2011-09-28 17:05:38 +00:00
// can't be null unless we limit the loop above
//if (sockMgr == null) {
// l.log("Invalid I2CP configuration");
// throw new IllegalArgumentException("Socket manager could not be created");
//}
2010-12-05 04:05:20 +00:00
l . log ( " Tunnels ready for client: " + handlerName ) ;
2004-04-10 11:45:02 +00:00
2009-03-09 15:11:45 +00:00
} // else delay creating session until createI2PSocket() is called
2004-08-19 17:36:27 +00:00
2009-12-07 21:25:27 +00:00
Thread t = new I2PAppThread ( this ) ;
2004-05-04 08:14:19 +00:00
t . setName ( " Client " + _clientId ) ;
2004-04-10 11:45:02 +00:00
t . start ( ) ;
open = true ;
synchronized ( this ) {
2004-11-21 22:31:33 +00:00
while ( ! listenerReady & & open ) {
2004-04-10 11:45:02 +00:00
try {
wait ( ) ;
} catch ( InterruptedException e ) {
// ignore
}
}
}
if ( open & & listenerReady ) {
2009-03-09 15:11:45 +00:00
if ( openNow )
2010-11-19 14:41:26 +00:00
l . log ( " Client ready, listening on " + tunnel . listenHost + ':' + localPort ) ;
2009-03-09 15:11:45 +00:00
else
2010-11-19 14:41:26 +00:00
l . log ( " Client ready, listening on " + tunnel . listenHost + ':' + localPort + " , delaying tunnel open until required " ) ;
2004-04-10 11:45:02 +00:00
notifyEvent ( " openBaseClientResult " , " ok " ) ;
} else {
2010-11-19 14:41:26 +00:00
l . log ( " Client error for " + tunnel . listenHost + ':' + localPort + " , check logs " ) ;
2004-04-10 11:45:02 +00:00
notifyEvent ( " openBaseClientResult " , " error " ) ;
}
2004-04-08 04:41:54 +00:00
}
2004-12-30 22:51:16 +00:00
2009-06-04 15:14:41 +00:00
/ * *
2011-07-09 14:30:33 +00:00
* Sets the this . sockMgr field if it is null , or if we want a new one .
* This may take a LONG time if building a new manager .
2009-06-04 15:14:41 +00:00
*
* We need a socket manager before getDefaultOptions ( ) and most other things
2010-11-19 14:41:26 +00:00
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
2009-06-04 15:14:41 +00:00
* /
protected void verifySocketManager ( ) {
synchronized ( sockLock ) {
boolean newManager = false ;
if ( this . sockMgr = = null ) {
newManager = true ;
} else {
I2PSession sess = sockMgr . getSession ( ) ;
2013-06-30 17:00:14 +00:00
if ( sess . isClosed ( ) & &
2012-09-28 17:50:41 +00:00
Boolean . parseBoolean ( getTunnel ( ) . getClientOptions ( ) . getProperty ( " i2cp.closeOnIdle " ) ) & &
Boolean . parseBoolean ( getTunnel ( ) . getClientOptions ( ) . getProperty ( " i2cp.newDestOnResume " ) ) ) {
2009-06-04 15:14:41 +00:00
// build a new socket manager and a new dest if the session is closed.
getTunnel ( ) . removeSession ( sess ) ;
if ( _log . shouldLog ( Log . WARN ) )
_log . warn ( getTunnel ( ) . getClientOptions ( ) . getProperty ( " inbound.nickname " ) + " : Built a new destination on resume " ) ;
newManager = true ;
} // else the old socket manager will reconnect the old session if necessary
}
if ( newManager ) {
if ( _ownDest )
this . sockMgr = buildSocketManager ( ) ;
else
this . sockMgr = getSocketManager ( ) ;
}
}
}
/** this is ONLY for shared clients */
2004-04-08 04:41:54 +00:00
private static I2PSocketManager socketManager ;
2004-04-10 11:45:02 +00:00
2010-11-19 14:41:26 +00:00
/ * *
2011-07-09 14:30:33 +00:00
* This is ONLY for shared clients .
* This may take a LONG time if building a new manager .
*
2010-11-19 14:41:26 +00:00
* @return non - null
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
* /
2011-07-09 14:30:33 +00:00
protected I2PSocketManager getSocketManager ( ) {
2009-03-01 23:14:38 +00:00
return getSocketManager ( getTunnel ( ) , this . privKeyFile ) ;
2004-07-10 16:59:49 +00:00
}
2010-11-19 14:41:26 +00:00
/ * *
2011-07-09 14:30:33 +00:00
* This is ONLY for shared clients .
* This may take a LONG time if building a new manager .
*
2010-11-19 14:41:26 +00:00
* @return non - null
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
* /
2011-07-09 14:30:33 +00:00
protected static I2PSocketManager getSocketManager ( I2PTunnel tunnel ) {
2009-03-01 23:14:38 +00:00
return getSocketManager ( tunnel , null ) ;
}
2010-11-19 14:41:26 +00:00
/ * *
2011-07-09 14:30:33 +00:00
* This is ONLY for shared clients .
* This may take a LONG time if building a new manager .
*
2010-11-19 14:41:26 +00:00
* @return non - null
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
* /
2009-03-01 23:14:38 +00:00
protected static synchronized I2PSocketManager getSocketManager ( I2PTunnel tunnel , String pkf ) {
2010-12-03 15:20:00 +00:00
// shadows instance _log
Log _log = tunnel . getContext ( ) . logManager ( ) . getLog ( I2PTunnelClientBase . class ) ;
2004-09-07 07:17:02 +00:00
if ( socketManager ! = null ) {
I2PSession s = socketManager . getSession ( ) ;
2013-06-30 17:00:14 +00:00
if ( s . isClosed ( ) ) {
2009-06-04 15:14:41 +00:00
if ( _log . shouldLog ( Log . INFO ) )
_log . info ( tunnel . getClientOptions ( ) . getProperty ( " inbound.nickname " ) + " : Building a new socket manager since the old one closed [s= " + s + " ] " ) ;
2013-11-07 19:20:25 +00:00
tunnel . removeSession ( s ) ;
2011-07-09 14:30:33 +00:00
// We could be here a LONG time, holding the lock
2009-03-01 23:14:38 +00:00
socketManager = buildSocketManager ( tunnel , pkf ) ;
2004-09-07 07:17:02 +00:00
} else {
2009-06-04 15:14:41 +00:00
if ( _log . shouldLog ( Log . INFO ) )
_log . info ( tunnel . getClientOptions ( ) . getProperty ( " inbound.nickname " ) + " : Not building a new socket manager since the old one is open [s= " + s + " ] " ) ;
2004-09-07 07:17:02 +00:00
}
} else {
2009-06-04 15:14:41 +00:00
if ( _log . shouldLog ( Log . INFO ) )
_log . info ( tunnel . getClientOptions ( ) . getProperty ( " inbound.nickname " ) + " : Building a new socket manager since there is no other one " ) ;
2009-03-01 23:14:38 +00:00
socketManager = buildSocketManager ( tunnel , pkf ) ;
2004-04-10 11:45:02 +00:00
}
return socketManager ;
2004-04-08 04:41:54 +00:00
}
2004-04-10 11:45:02 +00:00
2010-11-19 14:41:26 +00:00
/ * *
2011-07-09 14:30:33 +00:00
* This may take a LONG time .
*
2010-11-19 14:41:26 +00:00
* @return non - null
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
* /
2004-07-10 16:59:49 +00:00
protected I2PSocketManager buildSocketManager ( ) {
2010-11-19 14:41:26 +00:00
return buildSocketManager ( getTunnel ( ) , this . privKeyFile , this . l ) ;
2004-07-10 16:59:49 +00:00
}
2010-11-19 14:41:26 +00:00
/ * *
2011-07-09 14:30:33 +00:00
* This may take a LONG time .
*
2010-11-19 14:41:26 +00:00
* @return non - null
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
* /
2004-07-10 16:59:49 +00:00
protected static I2PSocketManager buildSocketManager ( I2PTunnel tunnel ) {
2009-03-01 23:14:38 +00:00
return buildSocketManager ( tunnel , null ) ;
}
2009-10-09 13:56:34 +00:00
2010-11-19 14:41:26 +00:00
private static final int RETRY_DELAY = 20 * 1000 ;
private static final int MAX_RETRIES = 4 ;
/ * *
2011-07-09 14:30:33 +00:00
* This may take a LONG time .
*
2010-11-19 14:41:26 +00:00
* @param pkf absolute path or null
* @return non - null
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
* /
2009-03-01 23:14:38 +00:00
protected static I2PSocketManager buildSocketManager ( I2PTunnel tunnel , String pkf ) {
2010-11-19 14:41:26 +00:00
return buildSocketManager ( tunnel , pkf , null ) ;
}
/ * *
2011-07-09 14:30:33 +00:00
* This may take a LONG time .
*
2010-11-19 14:41:26 +00:00
* @param pkf absolute path or null
* @return non - null
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
* /
protected static I2PSocketManager buildSocketManager ( I2PTunnel tunnel , String pkf , Logging log ) {
2010-12-03 15:20:00 +00:00
// shadows instance _log
Log _log = tunnel . getContext ( ) . logManager ( ) . getLog ( I2PTunnelClientBase . class ) ;
2004-04-10 11:45:02 +00:00
Properties props = new Properties ( ) ;
2008-10-19 22:09:14 +00:00
props . putAll ( tunnel . getClientOptions ( ) ) ;
2005-04-05 22:24:32 +00:00
int portNum = 7654 ;
if ( tunnel . port ! = null ) {
try {
portNum = Integer . parseInt ( tunnel . port ) ;
} catch ( NumberFormatException nfe ) {
_log . log ( Log . CRIT , " Invalid port specified [ " + tunnel . port + " ], reverting to " + portNum ) ;
}
}
I2PSocketManager sockManager = null ;
2011-07-09 14:30:33 +00:00
// FIXME: Can't stop a tunnel from the UI while it's in this loop (no session yet)
2010-11-19 14:41:26 +00:00
int retries = 0 ;
2005-04-05 22:24:32 +00:00
while ( sockManager = = null ) {
2009-03-01 23:14:38 +00:00
if ( pkf ! = null ) {
// Persistent client dest
FileInputStream fis = null ;
try {
fis = new FileInputStream ( pkf ) ;
sockManager = I2PSocketManagerFactory . createManager ( fis , tunnel . host , portNum , props ) ;
} catch ( IOException ioe ) {
2010-11-19 14:41:26 +00:00
if ( log ! = null )
log . log ( " Error opening key file " + ioe ) ;
2009-03-01 23:14:38 +00:00
_log . error ( " Error opening key file " , ioe ) ;
2010-11-19 14:41:26 +00:00
throw new IllegalArgumentException ( " Error opening key file " + ioe ) ;
2009-03-01 23:14:38 +00:00
} finally {
if ( fis ! = null )
try { fis . close ( ) ; } catch ( IOException ioe ) { }
}
} else {
sockManager = I2PSocketManagerFactory . createManager ( tunnel . host , portNum , props ) ;
}
2005-04-05 22:24:32 +00:00
if ( sockManager = = null ) {
2010-11-19 14:41:26 +00:00
// try to make this error sensible as it will happen... sadly we can't get to the listenPort, only the listenHost
String msg = " Unable to connect to the router at " + tunnel . host + ':' + portNum +
" and build tunnels for the client " ;
if ( + + retries < MAX_RETRIES ) {
if ( log ! = null )
log . log ( msg + " , retrying in " + ( RETRY_DELAY / 1000 ) + " seconds " ) ;
_log . error ( msg + " , retrying in " + ( RETRY_DELAY / 1000 ) + " seconds " ) ;
} else {
if ( log ! = null )
log . log ( msg + " , giving up " ) ;
_log . log ( Log . CRIT , msg + " , giving up " ) ;
// not clear if callers can handle null
//return null;
throw new IllegalArgumentException ( msg ) ;
}
try { Thread . sleep ( RETRY_DELAY ) ; } catch ( InterruptedException ie ) { }
2005-04-05 22:24:32 +00:00
}
}
2004-06-28 13:23:24 +00:00
sockManager . setName ( " Client " ) ;
2009-06-04 15:14:41 +00:00
if ( _log . shouldLog ( Log . INFO ) )
_log . info ( tunnel . getClientOptions ( ) . getProperty ( " inbound.nickname " ) + " : Built a new socket manager [s= " + sockManager . getSession ( ) + " ] " ) ;
2009-04-01 04:54:49 +00:00
tunnel . addSession ( sockManager . getSession ( ) ) ;
2004-06-28 13:23:24 +00:00
return sockManager ;
2004-04-08 04:41:54 +00:00
}
2004-04-10 11:45:02 +00:00
2004-04-08 04:41:54 +00:00
public final int getLocalPort ( ) {
return localPort ;
}
protected final InetAddress getListenHost ( Logging l ) {
2004-04-10 11:45:02 +00:00
try {
2004-08-03 08:18:10 +00:00
return InetAddress . getByName ( getTunnel ( ) . listenHost ) ;
2004-04-10 11:45:02 +00:00
} catch ( UnknownHostException uhe ) {
2004-08-03 08:18:10 +00:00
l . log ( " Could not find listen host to bind to [ " + getTunnel ( ) . host + " ] " ) ;
2004-04-10 11:45:02 +00:00
_log . error ( " Error finding host to bind " , uhe ) ;
notifyEvent ( " openBaseClientResult " , " error " ) ;
return null ;
}
2004-04-08 04:41:54 +00:00
}
2004-04-10 11:45:02 +00:00
2004-04-08 04:41:54 +00:00
/ * *
* Actually start working on incoming connections . * Must * be
* called by derived classes after initialization .
*
* /
2009-12-07 21:25:27 +00:00
public void startRunning ( ) {
2004-04-10 11:45:02 +00:00
synchronized ( startLock ) {
startRunning = true ;
startLock . notify ( ) ;
}
2004-04-08 04:41:54 +00:00
}
2004-04-10 11:45:02 +00:00
2004-04-08 04:41:54 +00:00
/ * *
2012-06-19 21:48:31 +00:00
* Create the default options ( using the default timeout , etc ) .
* Warning , this does not make a copy of I2PTunnel ' s client options ,
* it modifies them directly .
2004-04-08 04:41:54 +00:00
* /
2004-11-27 05:17:06 +00:00
protected I2PSocketOptions getDefaultOptions ( ) {
2004-11-16 22:11:11 +00:00
Properties defaultOpts = getTunnel ( ) . getClientOptions ( ) ;
I2PSocketOptions opts = sockMgr . buildOptions ( defaultOpts ) ;
if ( ! defaultOpts . containsKey ( I2PSocketOptions . PROP_CONNECT_TIMEOUT ) )
opts . setConnectTimeout ( DEFAULT_CONNECT_TIMEOUT ) ;
2004-04-10 11:45:02 +00:00
return opts ;
2004-04-08 04:41:54 +00:00
}
2004-11-27 05:17:06 +00:00
/ * *
2012-06-19 21:48:31 +00:00
* Create the default options ( using the default timeout , etc ) .
* Warning , this does not make a copy of I2PTunnel ' s client options ,
* it modifies them directly .
* Do not use overrides for per - socket options .
2004-11-27 05:17:06 +00:00
* /
protected I2PSocketOptions getDefaultOptions ( Properties overrides ) {
Properties defaultOpts = getTunnel ( ) . getClientOptions ( ) ;
defaultOpts . putAll ( overrides ) ;
I2PSocketOptions opts = sockMgr . buildOptions ( defaultOpts ) ;
if ( ! defaultOpts . containsKey ( I2PSocketOptions . PROP_CONNECT_TIMEOUT ) )
opts . setConnectTimeout ( DEFAULT_CONNECT_TIMEOUT ) ;
return opts ;
}
2004-04-08 04:41:54 +00:00
2012-06-14 19:44:47 +00:00
/ * *
* Update the I2PSocketManager .
*
* @since 0 . 9 . 1
* /
@Override
public void optionsUpdated ( I2PTunnel tunnel ) {
if ( getTunnel ( ) ! = tunnel )
return ;
I2PSocketManager sm = _ownDest ? sockMgr : socketManager ;
if ( sm = = null )
return ;
Properties props = tunnel . getClientOptions ( ) ;
2012-06-24 11:38:37 +00:00
sm . setDefaultOptions ( sm . buildOptions ( props ) ) ;
2012-06-14 19:44:47 +00:00
}
2004-04-08 04:41:54 +00:00
/ * *
* Create a new I2PSocket towards to the specified destination ,
* adding it to the list of connections actually managed by this
* tunnel .
*
* @param dest The destination to connect to
* @return a new I2PSocket
* /
2004-04-20 15:43:04 +00:00
public I2PSocket createI2PSocket ( Destination dest ) throws I2PException , ConnectException , NoRouteToHostException , InterruptedIOException {
2013-10-23 20:20:54 +00:00
return createI2PSocket ( dest , 0 ) ;
}
/ * *
* Create a new I2PSocket towards to the specified destination ,
* adding it to the list of connections actually managed by this
* tunnel .
*
* @param dest The destination to connect to
* @param port The destination port to connect to 0 - 65535
* @return a new I2PSocket
* @since 0 . 9 . 9
* /
public I2PSocket createI2PSocket ( Destination dest , int port )
throws I2PException , ConnectException , NoRouteToHostException , InterruptedIOException {
2009-06-04 15:14:41 +00:00
verifySocketManager ( ) ;
2013-10-23 20:20:54 +00:00
I2PSocketOptions opts = getDefaultOptions ( ) ;
opts . setPort ( port ) ;
return createI2PSocket ( dest , opts ) ;
2004-04-08 04:41:54 +00:00
}
/ * *
* Create a new I2PSocket towards to the specified destination ,
* adding it to the list of connections actually managed by this
* tunnel .
*
* @param dest The destination to connect to
* @param opt Option to be used to open when opening the socket
* @return a new I2PSocket
2004-04-19 21:47:06 +00:00
*
* @throws ConnectException if the peer refuses the connection
* @throws NoRouteToHostException if the peer is not found or not reachable
2004-04-20 15:43:04 +00:00
* @throws InterruptedIOException if the connection timeouts
2004-04-19 21:47:06 +00:00
* @throws I2PException if there is some other I2P - related problem
2004-04-08 04:41:54 +00:00
* /
2004-04-20 15:43:04 +00:00
public I2PSocket createI2PSocket ( Destination dest , I2PSocketOptions opt ) throws I2PException , ConnectException , NoRouteToHostException , InterruptedIOException {
2004-04-10 11:45:02 +00:00
I2PSocket i2ps ;
2004-04-08 04:41:54 +00:00
2009-06-04 15:14:41 +00:00
verifySocketManager ( ) ;
2004-05-04 08:14:19 +00:00
i2ps = sockMgr . connect ( dest , opt ) ;
2004-04-10 11:45:02 +00:00
synchronized ( sockLock ) {
mySockets . add ( i2ps ) ;
}
2004-04-08 04:41:54 +00:00
2004-04-10 11:45:02 +00:00
return i2ps ;
2004-04-08 04:41:54 +00:00
}
public final void run ( ) {
2004-04-10 11:45:02 +00:00
try {
InetAddress addr = getListenHost ( l ) ;
2004-11-21 22:31:33 +00:00
if ( addr = = null ) {
open = false ;
synchronized ( this ) {
notifyAll ( ) ;
}
return ;
}
2004-04-10 11:45:02 +00:00
ss = new ServerSocket ( localPort , 0 , addr ) ;
// If a free port was requested, find out what we got
if ( localPort = = 0 ) {
localPort = ss . getLocalPort ( ) ;
}
2011-09-28 17:05:38 +00:00
notifyEvent ( " clientLocalPort " , Integer . valueOf ( ss . getLocalPort ( ) ) ) ;
2010-11-19 14:41:26 +00:00
// duplicates message in constructor
//l.log("Listening for clients on port " + localPort + " of " + getTunnel().listenHost);
2004-04-10 11:45:02 +00:00
// Notify constructor that port is ready
synchronized ( this ) {
listenerReady = true ;
notify ( ) ;
}
// Wait until we are authorized to process data
synchronized ( startLock ) {
while ( ! startRunning ) {
try {
startLock . wait ( ) ;
} catch ( InterruptedException ie ) {
}
}
}
2010-12-04 18:47:22 +00:00
while ( open ) {
2004-04-10 11:45:02 +00:00
Socket s = ss . accept ( ) ;
manageConnection ( s ) ;
}
} catch ( IOException ex ) {
2009-03-24 20:24:20 +00:00
if ( open ) {
_log . error ( " Error listening for connections on " + localPort , ex ) ;
notifyEvent ( " openBaseClientResult " , " error " ) ;
}
2004-11-15 14:35:16 +00:00
synchronized ( sockLock ) {
mySockets . clear ( ) ;
}
2004-11-21 22:31:33 +00:00
open = false ;
synchronized ( this ) {
notifyAll ( ) ;
}
2004-04-10 11:45:02 +00:00
}
2004-04-08 04:41:54 +00:00
}
2011-06-17 00:09:05 +00:00
/ * *
* @return may be null if no class has been instantiated
* @since 0 . 8 . 8
* /
static ThreadPoolExecutor getClientExecutor ( ) {
return _executor ;
}
/ * *
* @since 0 . 8 . 8
* /
static void killClientExecutor ( ) {
2011-07-09 14:30:33 +00:00
synchronized ( _executorLock ) {
2011-06-17 03:19:57 +00:00
if ( _executor ! = null ) {
2011-06-17 00:09:05 +00:00
_executor . setRejectedExecutionHandler ( new ThreadPoolExecutor . DiscardPolicy ( ) ) ;
_executor . shutdownNow ( ) ;
_executor = null ;
}
// kill the shared client, so that on restart in android
// we won't latch onto the old one
socketManager = null ;
}
}
2004-04-08 04:41:54 +00:00
/ * *
* Manage the connection just opened on the specified socket
*
* @param s Socket to take care of
* /
protected void manageConnection ( Socket s ) {
2004-12-30 22:51:16 +00:00
if ( s = = null ) return ;
2011-06-18 19:42:08 +00:00
ThreadPoolExecutor tpe = _executor ;
if ( tpe = = null ) {
_log . error ( " No executor for socket! " ) ;
try {
s . close ( ) ;
} catch ( IOException ioe ) { }
return ;
}
2010-12-04 18:47:22 +00:00
try {
2011-06-18 19:42:08 +00:00
tpe . execute ( new BlockingRunner ( s ) ) ;
2010-12-04 18:47:22 +00:00
} catch ( RejectedExecutionException ree ) {
// should never happen, we have an unbounded pool and never stop the executor
try {
s . close ( ) ;
} catch ( IOException ioe ) { }
2004-12-30 22:51:16 +00:00
}
2010-12-04 18:47:22 +00:00
}
/ * *
* Not really needed for now but in case we want to add some hooks like afterExecute ( ) .
* /
private static class CustomThreadPoolExecutor extends ThreadPoolExecutor {
public CustomThreadPoolExecutor ( ) {
super ( 0 , Integer . MAX_VALUE , HANDLER_KEEPALIVE_MS , TimeUnit . MILLISECONDS ,
new SynchronousQueue ( ) , new CustomThreadFactory ( ) ) ;
}
}
2004-12-30 22:51:16 +00:00
2010-12-04 18:47:22 +00:00
/** just to set the name and set Daemon */
private static class CustomThreadFactory implements ThreadFactory {
public Thread newThread ( Runnable r ) {
Thread rv = Executors . defaultThreadFactory ( ) . newThread ( r ) ;
rv . setName ( " I2PTunnel Client Runner " + ( + + _executorThreadCount ) ) ;
rv . setDaemon ( true ) ;
return rv ;
2004-12-30 22:51:16 +00:00
}
}
/ * *
2010-12-04 18:47:22 +00:00
* Blocking runner , used during the connection establishment
2004-12-30 22:51:16 +00:00
* /
private class BlockingRunner implements Runnable {
private Socket _s ;
public BlockingRunner ( Socket s ) { _s = s ; }
public void run ( ) {
clientConnectionRun ( _s ) ;
}
}
2004-04-08 04:41:54 +00:00
public boolean close ( boolean forced ) {
2010-06-30 23:37:25 +00:00
if ( _log . shouldLog ( Log . INFO ) )
_log . info ( " close() called: forced = " + forced + " open = " + open + " sockMgr = " + sockMgr ) ;
2004-04-10 11:45:02 +00:00
if ( ! open ) return true ;
// FIXME: here we might have to wait quite a long time if
// there is a connection attempt atm. But without waiting we
// might risk to create an orphan socket. Would be better
// to return with an error in that situation quickly.
synchronized ( sockLock ) {
2009-03-24 20:24:20 +00:00
if ( sockMgr ! = null ) {
mySockets . retainAll ( sockMgr . listSockets ( ) ) ;
2011-07-19 21:17:52 +00:00
if ( ( ! forced ) & & ( ! mySockets . isEmpty ( ) ) ) {
l . log ( " Not closing, there are still active connections! " ) ;
2009-03-24 20:24:20 +00:00
_log . debug ( " can't close: there are still active connections! " ) ;
2011-07-19 21:17:52 +00:00
for ( I2PSocket s : mySockets ) {
l . log ( " -> " + s . toString ( ) ) ;
2009-03-24 20:24:20 +00:00
}
return false ;
}
2010-01-10 11:23:20 +00:00
if ( ! chained ) {
I2PSession session = sockMgr . getSession ( ) ;
2013-06-30 17:00:14 +00:00
getTunnel ( ) . removeSession ( session ) ;
2010-01-10 11:23:20 +00:00
} // else the app chaining to this one closes it!
2004-09-07 07:17:02 +00:00
}
2010-11-19 14:41:26 +00:00
l . log ( " Stopping client " + toString ( ) ) ;
2009-03-24 20:24:20 +00:00
open = false ;
2004-04-10 11:45:02 +00:00
try {
if ( ss ! = null ) ss . close ( ) ;
} catch ( IOException ex ) {
2011-07-19 21:17:52 +00:00
if ( _log . shouldLog ( Log . WARN ) )
_log . warn ( " error closing " , ex ) ;
2004-04-10 11:45:02 +00:00
return false ;
}
2010-11-19 14:41:26 +00:00
//l.log("Client closed.");
2004-04-10 11:45:02 +00:00
}
2004-12-30 22:51:16 +00:00
return true ;
2004-04-08 04:41:54 +00:00
}
public static void closeSocket ( Socket s ) {
2004-04-10 11:45:02 +00:00
try {
s . close ( ) ;
} catch ( IOException ex ) {
2010-12-03 15:20:00 +00:00
//_log.error("Could not close socket", ex);
2004-04-10 11:45:02 +00:00
}
2004-04-08 04:41:54 +00:00
}
2004-05-07 03:33:23 +00:00
2004-04-08 04:41:54 +00:00
/ * *
* Manage a connection in a separate thread . This only works if
* you do not override manageConnection ( )
* /
protected abstract void clientConnectionRun ( Socket s ) ;
2004-04-19 21:47:06 +00:00
}