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.RejectedExecutionException ;
import java.util.concurrent.ThreadPoolExecutor ;
2013-11-16 13:22:05 +00:00
import java.util.concurrent.atomic.AtomicLong ;
2004-04-08 04:41:54 +00:00
2014-10-15 20:44:23 +00:00
import javax.net.ssl.SSLServerSocket ;
2014-08-21 12:21:29 +00:00
import javax.net.ssl.SSLServerSocketFactory ;
2005-10-10 22:58:18 +00:00
import net.i2p.I2PAppContext ;
2008-07-16 13:42:54 +00:00
import net.i2p.I2PException ;
2015-04-18 19:50:14 +00:00
import net.i2p.client.I2PClient ;
2004-09-07 07:17:02 +00:00
import net.i2p.client.I2PSession ;
2015-03-27 14:16:41 +00:00
import net.i2p.client.I2PSessionException ;
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 ;
2015-04-18 19:01:23 +00:00
import net.i2p.crypto.SigType ;
2004-04-08 04:41:54 +00:00
import net.i2p.data.Destination ;
import net.i2p.util.EventDispatcher ;
2009-12-07 21:25:27 +00:00
import net.i2p.util.I2PAppThread ;
2014-10-15 20:44:23 +00:00
import net.i2p.util.I2PSSLSocketFactory ;
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
2013-11-16 13:22:05 +00:00
private static final AtomicLong __clientId = new AtomicLong ( ) ;
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
2013-11-20 23:36:39 +00:00
protected final List < I2PSocket > mySockets = new ArrayList < I2PSocket > ( ) ;
2009-06-04 15:14:41 +00:00
protected boolean _ownDest ;
2004-04-08 04:41:54 +00:00
2013-12-21 18:10:59 +00:00
protected Destination dest ;
2015-04-24 21:16:45 +00:00
private volatile int localPort ;
2015-03-27 14:16:41 +00:00
private final String _handlerName ;
2004-04-08 04:41:54 +00:00
2013-12-21 18:10:59 +00:00
/ * *
2014-02-08 18:09:46 +00:00
* Protected for I2Ping since 0 . 9 . 11 . Not for use outside package .
2013-12-21 18:10:59 +00:00
* /
protected boolean listenerReady ;
2004-04-08 04:41:54 +00:00
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 ( ) ;
2013-12-21 18:10:59 +00:00
private boolean startRunning ;
2004-04-08 04:41:54 +00:00
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.
2013-12-21 18:10:59 +00:00
private boolean chained ;
2010-01-10 11:23:20 +00:00
2014-12-05 15:12:51 +00:00
private volatile ThreadPoolExecutor _executor ;
2010-12-04 18:47:22 +00:00
2015-05-24 00:14:32 +00:00
/** this is ONLY for shared clients */
private static I2PSocketManager socketManager ;
/ * *
* Only destroy and replace a static shared client socket manager if it ' s been connected before
* @since 0 . 9 . 20
* /
private enum SocketManagerState { INIT , CONNECTED }
private static SocketManagerState _socketManagerState = SocketManagerState . INIT ;
2014-08-21 12:21:29 +00:00
public static final String PROP_USE_SSL = I2PTunnelServer . PROP_USE_SSL ;
2011-07-09 14:30:33 +00:00
/ * *
2015-06-04 22:34:13 +00:00
* This constructor is used to add a client to an existing socket manager .
* < p / >
* As of 0 . 9 . 21 this does NOT open the local socket . You MUST call
* { @link # startRunning ( ) } for that . The local socket will be opened
* immediately ( ignoring the < code > i2cp . delayOpen < / code > option ) .
2011-07-09 14:30:33 +00:00
*
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 ;
2015-03-27 14:16:41 +00:00
_handlerName = " chained " ;
2010-01-10 11:23:20 +00:00
this . localPort = localPort ;
this . l = l ;
_ownDest = true ; // == ! shared client
_context = tunnel . getContext ( ) ;
2015-06-04 22:36:45 +00:00
initStats ( ) ;
2010-12-03 15:20:00 +00:00
_log = _context . logManager ( ) . getLog ( getClass ( ) ) ;
2010-01-10 11:23:20 +00:00
}
2010-12-04 18:47:22 +00:00
2011-07-09 14:30:33 +00:00
/ * *
* The main constructor .
2015-06-04 22:25:44 +00:00
* < p / >
* As of 0 . 9 . 21 this is fast , and does NOT connect the manager to the router ,
2015-03-27 14:16:41 +00:00
* or open the local socket . You MUST call startRunning ( ) for that .
2015-06-04 22:25:44 +00:00
* < p / >
* ( 0 . 9 . 20 claimed to be fast , but due to a bug it DID connect the manager
* to the router . It did NOT open the local socket however , so it was still
* necessary to call startRunning ( ) for that . )
2011-07-09 14:30:33 +00:00
*
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 .
2015-06-04 22:25:44 +00:00
* < p / >
* As of 0 . 9 . 21 this is fast , and does NOT connect the manager to the router ,
2015-03-27 14:16:41 +00:00
* or open the local socket . You MUST call startRunning ( ) for that .
2015-06-04 22:25:44 +00:00
* < p / >
* ( 0 . 9 . 20 claimed to be fast , but due to a bug it DID connect the manager
* to the router . It did NOT open the local socket however , so it was still
* necessary to call startRunning ( ) for that . )
2011-07-09 14:30:33 +00:00
*
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 ) ;
2013-11-16 13:22:05 +00:00
_clientId = __clientId . incrementAndGet ( ) ;
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
2015-03-27 14:16:41 +00:00
_handlerName = handlerName ;
2004-04-10 11:45:02 +00:00
2005-10-10 22:58:18 +00:00
_context = tunnel . getContext ( ) ;
2015-06-04 22:36:45 +00:00
initStats ( ) ;
2010-12-03 15:20:00 +00:00
_log = _context . logManager ( ) . getLog ( getClass ( ) ) ;
2005-10-10 22:58:18 +00:00
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 " ) ;
2013-12-09 16:11:53 +00:00
if ( tunnel . getClientOptions ( ) . getProperty ( " i2p.streaming.answerPings " ) = = null )
tunnel . getClientOptions ( ) . setProperty ( " i2p.streaming.answerPings " , " false " ) ;
2004-04-08 04:41:54 +00:00
}
2015-06-04 22:36:45 +00:00
private void initStats ( ) {
_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 } ) ;
}
2009-06-04 15:14:41 +00:00
/ * *
2015-03-27 14:16:41 +00:00
* Create the manager if it doesn ' t exist , AND connect it to the router and
* build tunnels .
*
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 ;
2013-11-24 23:42:53 +00:00
// other shared client could have destroyed it
if ( this . sockMgr = = null | | this . sockMgr . isDestroyed ( ) ) {
2009-06-04 15:14:41 +00:00
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 " ) ;
2013-11-23 17:54:01 +00:00
// make sure the old one is closed
2013-11-24 23:42:53 +00:00
// if it's shared client, it will be destroyed in getSocketManager()
// with the correct locking
boolean shouldDestroy ;
synchronized ( I2PTunnelClientBase . class ) {
shouldDestroy = sockMgr ! = socketManager ;
}
if ( shouldDestroy )
sockMgr . destroySocketManager ( ) ;
2009-06-04 15:14:41 +00:00
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 ( ) ;
}
}
2015-03-27 14:16:41 +00:00
connectManager ( ) ;
2009-06-04 15:14:41 +00:00
}
2010-11-19 14:41:26 +00:00
/ * *
2011-07-09 14:30:33 +00:00
* This is ONLY for shared clients .
2015-03-27 14:16:41 +00:00
* As of 0 . 9 . 20 this is fast , and does NOT connect the manager to the router .
* Call verifySocketManager ( ) for that .
2011-07-09 14:30:33 +00:00
*
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 .
2015-03-27 14:16:41 +00:00
* As of 0 . 9 . 20 this is fast , and does NOT connect the manager to the router .
* Call verifySocketManager ( ) for that .
2011-07-09 14:30:33 +00:00
*
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 .
2015-03-27 14:16:41 +00:00
* As of 0 . 9 . 20 this is fast , and does NOT connect the manager to the router .
* Call verifySocketManager ( ) for that .
2011-07-09 14:30:33 +00:00
*
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 ) ;
2013-11-24 23:42:53 +00:00
if ( socketManager ! = null & & ! socketManager . isDestroyed ( ) ) {
2004-09-07 07:17:02 +00:00
I2PSession s = socketManager . getSession ( ) ;
2015-05-24 00:14:32 +00:00
if ( s . isClosed ( ) & & _socketManagerState ! = SocketManagerState . INIT ) {
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 ) ;
2013-11-23 17:54:01 +00:00
// make sure the old one is closed
socketManager . destroySocketManager ( ) ;
2015-05-24 00:14:32 +00:00
_socketManagerState = SocketManagerState . INIT ;
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 ) ;
2015-04-18 19:01:23 +00:00
// FIXME may not be the right place for this
I2PSession sub = addSubsession ( tunnel ) ;
if ( sub ! = null & & _log . shouldLog ( Log . WARN ) )
_log . warn ( " Added subsession " + sub ) ;
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 + " ] " ) ;
2014-09-13 14:49:38 +00:00
// If some other tunnel created the session, we need to add it
// as our session too.
// It's a Set in I2PTunnel
tunnel . addSession ( 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 ) ;
2015-04-18 19:01:23 +00:00
I2PSession sub = addSubsession ( tunnel ) ;
if ( sub ! = null & & _log . shouldLog ( Log . WARN ) )
_log . warn ( " Added subsession " + sub ) ;
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
2015-04-18 19:01:23 +00:00
/ * *
* Add a subsession to a shared client if necessary .
*
* @since 0 . 9 . 20
* /
protected static synchronized I2PSession addSubsession ( I2PTunnel tunnel ) {
I2PSession sess = socketManager . getSession ( ) ;
if ( sess . getMyDestination ( ) . getSigType ( ) = = SigType . DSA_SHA1 )
return null ;
Properties props = new Properties ( ) ;
props . putAll ( tunnel . getClientOptions ( ) ) ;
String name = props . getProperty ( " inbound.nickname " ) ;
if ( name ! = null )
props . setProperty ( " inbound.nickname " , name + " (DSA) " ) ;
name = props . getProperty ( " outbound.nickname " ) ;
if ( name ! = null )
props . setProperty ( " outbound.nickname " , name + " (DSA) " ) ;
2015-04-18 19:50:14 +00:00
props . setProperty ( I2PClient . PROP_SIGTYPE , " DSA_SHA1 " ) ;
2015-04-18 19:01:23 +00:00
try {
return socketManager . addSubsession ( null , props ) ;
} catch ( I2PSessionException ise ) {
Log log = tunnel . getContext ( ) . logManager ( ) . getLog ( I2PTunnelClientBase . class ) ;
if ( log . shouldLog ( Log . WARN ) )
log . warn ( " Failed to add subssession " , ise ) ;
return null ;
}
}
2014-12-05 15:12:51 +00:00
/ * *
* Kill the shared client , so that on restart in android
* we won ' t latch onto the old one
*
* @since 0 . 9 . 18
* /
protected static synchronized void killSharedClient ( ) {
socketManager = null ;
}
2010-11-19 14:41:26 +00:00
/ * *
2015-03-27 14:16:41 +00:00
* For NON - SHARED clients ( ownDest = true ) .
*
* As of 0 . 9 . 20 this is fast , and does NOT connect the manager to the router .
* Call verifySocketManager ( ) for that .
2011-07-09 14:30:33 +00:00
*
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
}
2015-03-27 14:47:49 +00:00
2010-11-19 14:41:26 +00:00
/ * *
2015-03-27 14:16:41 +00:00
* As of 0 . 9 . 20 this is fast , and does NOT connect the manager to the router .
* Call verifySocketManager ( ) for that .
2011-07-09 14:30:33 +00:00
*
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 ;
/ * *
2015-03-27 14:16:41 +00:00
* As of 0 . 9 . 20 this is fast , and does NOT connect the manager to the router .
* Call verifySocketManager ( ) for that .
2011-07-09 14:30:33 +00:00
*
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 ) ;
}
/ * *
2015-03-27 14:16:41 +00:00
* As of 0 . 9 . 20 this is fast , and does NOT connect the manager to the router .
* Call verifySocketManager ( ) for that .
2011-07-09 14:30:33 +00:00
*
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 ) {
2015-03-27 14:16:41 +00:00
throw new IllegalArgumentException ( " Invalid port specified [ " + tunnel . port + " ] " , nfe ) ;
2005-04-05 22:24:32 +00:00
}
}
I2PSocketManager sockManager = null ;
2015-03-27 14:16:41 +00:00
FileInputStream fis = null ;
try {
2009-03-01 23:14:38 +00:00
if ( pkf ! = null ) {
// Persistent client dest
2015-03-27 14:16:41 +00:00
fis = new FileInputStream ( pkf ) ;
sockManager = I2PSocketManagerFactory . createDisconnectedManager ( fis , tunnel . host , portNum , props ) ;
2009-03-01 23:14:38 +00:00
} else {
2015-03-27 14:16:41 +00:00
sockManager = I2PSocketManagerFactory . createDisconnectedManager ( null , tunnel . host , portNum , props ) ;
2009-03-01 23:14:38 +00:00
}
2015-03-27 14:16:41 +00:00
} catch ( I2PSessionException ise ) {
throw new IllegalArgumentException ( " Can't create socket manager " , ise ) ;
} catch ( IOException ioe ) {
if ( log ! = null )
log . log ( " Error opening key file " + ioe ) ;
_log . error ( " Error opening key file " , ioe ) ;
2015-04-02 20:52:40 +00:00
throw new IllegalArgumentException ( " Error opening key file " , ioe ) ;
2015-03-27 14:16:41 +00:00
} finally {
if ( fis ! = null )
try { fis . close ( ) ; } catch ( IOException ioe ) { }
}
sockManager . setName ( " Client " ) ;
if ( _log . shouldLog ( Log . INFO ) )
_log . info ( tunnel . getClientOptions ( ) . getProperty ( " inbound.nickname " ) + " : Built a new socket manager [s= " + sockManager . getSession ( ) + " ] " ) ;
tunnel . addSession ( sockManager . getSession ( ) ) ;
return sockManager ;
}
/ * *
* Warning , blocks while connecting to router and building tunnels ;
* This may take a LONG time .
*
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
* @since 0 . 9 . 20
* /
private void connectManager ( ) {
int retries = 0 ;
while ( sockMgr . getSession ( ) . isClosed ( ) ) {
try {
sockMgr . getSession ( ) . connect ( ) ;
2015-05-24 00:14:32 +00:00
synchronized ( I2PTunnelClientBase . class ) {
if ( sockMgr = = socketManager )
_socketManagerState = SocketManagerState . CONNECTED ;
}
2015-03-27 14:16:41 +00:00
} catch ( I2PSessionException ise ) {
2015-04-08 11:52:02 +00:00
// shadows instance _log
Log _log = getTunnel ( ) . getContext ( ) . logManager ( ) . getLog ( I2PTunnelClientBase . class ) ;
Logging log = this . l ;
2015-03-27 14:16:41 +00:00
// try to make this error sensible as it will happen...
String portNum = getTunnel ( ) . port ;
if ( portNum = = null )
portNum = " 7654 " ;
2015-04-08 11:52:02 +00:00
String msg ;
if ( getTunnel ( ) . getContext ( ) . isRouterContext ( ) )
msg = " Unable to connect to the router at " + getTunnel ( ) . host + ':' + portNum +
2010-11-19 14:41:26 +00:00
" and build tunnels for the client " ;
2015-04-08 11:52:02 +00:00
else
msg = " Unable to build tunnels for the client " ;
2010-11-19 14:41:26 +00:00
if ( + + retries < MAX_RETRIES ) {
if ( log ! = null )
log . log ( msg + " , retrying in " + ( RETRY_DELAY / 1000 ) + " seconds " ) ;
2015-04-08 11:52:02 +00:00
_log . error ( msg + " , retrying in " + ( RETRY_DELAY / 1000 ) + " seconds " , ise ) ;
2010-11-19 14:41:26 +00:00
} else {
if ( log ! = null )
log . log ( msg + " , giving up " ) ;
2015-04-08 11:52:02 +00:00
_log . log ( Log . CRIT , msg + " , giving up " , ise ) ;
throw new IllegalArgumentException ( msg , ise ) ;
2010-11-19 14:41:26 +00:00
}
try { Thread . sleep ( RETRY_DELAY ) ; } catch ( InterruptedException ie ) { }
2005-04-05 22:24:32 +00:00
}
}
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
/ * *
2015-03-27 14:16:41 +00:00
* Actually open the local socket and start working on incoming connections . * Must * be
2004-04-08 04:41:54 +00:00
* called by derived classes after initialization .
*
2015-03-27 14:16:41 +00:00
* ( this wasn ' t actually true until 0 . 9 . 20 )
*
* This will be fast if i2cp . delayOpen is true , but could take
* a LONG TIME if it is false , as it connects to the router and builds tunnels .
*
2015-04-24 21:16:45 +00:00
* Extending classes must check the value of boolean open after calling
* super . startRunning ( ) , if false then something went wrong .
*
2004-04-08 04:41:54 +00:00
* /
2009-12-07 21:25:27 +00:00
public void startRunning ( ) {
2015-03-27 14:16:41 +00:00
boolean openNow = ! Boolean . parseBoolean ( getTunnel ( ) . getClientOptions ( ) . getProperty ( " i2cp.delayOpen " ) ) ;
if ( openNow ) {
while ( sockMgr = = null ) {
verifySocketManager ( ) ;
if ( sockMgr = = null ) {
_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
try { Thread . sleep ( 10 * 1000 ) ; } catch ( InterruptedException ie ) { }
2015-04-24 21:37:22 +00:00
} else {
l . log ( " Tunnels ready for client: " + _handlerName ) ;
2015-03-27 14:16:41 +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");
//}
} // else delay creating session until createI2PSocket() is called
startup ( ) ;
}
private void startup ( ) {
2015-04-02 20:52:40 +00:00
if ( _log . shouldLog ( Log . DEBUG ) )
_log . debug ( " startup " + _clientId , new Exception ( " I did it " ) ) ;
2015-03-27 14:16:41 +00:00
// prevent JVM exit when running outside the router
boolean isDaemon = getTunnel ( ) . getContext ( ) . isRouterContext ( ) ;
2015-04-24 19:19:18 +00:00
open = true ;
2015-04-22 14:51:40 +00:00
Thread t = new I2PAppThread ( this , " I2PTunnel Client " + getTunnel ( ) . listenHost + ':' + localPort , isDaemon ) ;
2015-03-27 14:16:41 +00:00
t . start ( ) ;
synchronized ( this ) {
while ( ! listenerReady & & open ) {
try {
wait ( ) ;
} catch ( InterruptedException e ) {
// ignore
}
}
}
if ( open & & listenerReady ) {
boolean openNow = ! Boolean . parseBoolean ( getTunnel ( ) . getClientOptions ( ) . getProperty ( " i2cp.delayOpen " ) ) ;
2015-06-04 22:34:13 +00:00
if ( openNow | | chained )
2015-03-27 14:16:41 +00:00
l . log ( " Client ready, listening on " + getTunnel ( ) . listenHost + ':' + localPort ) ;
else
l . log ( " Client ready, listening on " + getTunnel ( ) . listenHost + ':' + localPort + " , delaying tunnel open until required " ) ;
notifyEvent ( " openBaseClientResult " , " ok " ) ;
} else {
l . log ( " Client error for " + getTunnel ( ) . listenHost + ':' + localPort + " , check logs " ) ;
notifyEvent ( " openBaseClientResult " , " error " ) ;
}
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
}
2013-12-21 18:10:59 +00:00
/ * *
2014-02-08 18:09:46 +00:00
* Non - final since 0 . 9 . 11 .
2015-04-24 19:19:18 +00:00
* open will be true before being called .
* Any overrides must set listenerReady = true and then notifyAll ( ) if setup is successful ,
* and must call close ( ) and then notifyAll ( ) on failure or termination .
2013-12-21 18:10:59 +00:00
* /
public void run ( ) {
2015-04-24 19:19:18 +00:00
InetAddress addr = getListenHost ( l ) ;
if ( addr = = null ) {
close ( true ) ;
open = false ;
synchronized ( this ) {
notifyAll ( ) ;
2004-11-21 22:31:33 +00:00
}
2015-04-24 19:19:18 +00:00
return ;
}
try {
2014-08-21 12:21:29 +00:00
Properties opts = getTunnel ( ) . getClientOptions ( ) ;
boolean useSSL = Boolean . parseBoolean ( opts . getProperty ( PROP_USE_SSL ) ) ;
if ( useSSL ) {
// was already done in web/IndexBean.java when saving the config
boolean wasCreated = SSLClientUtil . verifyKeyStore ( opts ) ;
if ( wasCreated ) {
// From here, we can't save the config.
// We shouldn't get here, as SSL isn't the default, so it would
// be enabled via the GUI only.
// If it was done manually, the keys will be regenerated at every startup,
// which is bad.
_log . logAlways ( Log . WARN , " Created new i2ptunnel SSL keys but can't save the config, disable and enable via i2ptunnel GUI " ) ;
}
SSLServerSocketFactory fact = SSLClientUtil . initializeFactory ( opts ) ;
ss = fact . createServerSocket ( localPort , 0 , addr ) ;
2014-10-15 20:44:23 +00:00
I2PSSLSocketFactory . setProtocolsAndCiphers ( ( SSLServerSocket ) ss ) ;
2014-08-21 12:21:29 +00:00
} else {
ss = new ServerSocket ( localPort , 0 , addr ) ;
}
2004-04-10 11:45:02 +00:00
// 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 ;
2015-04-24 19:19:18 +00:00
notifyAll ( ) ;
2004-04-10 11:45:02 +00:00
}
// Wait until we are authorized to process data
synchronized ( startLock ) {
while ( ! startRunning ) {
try {
startLock . wait ( ) ;
} catch ( InterruptedException ie ) {
}
}
}
2014-12-05 15:12:51 +00:00
TunnelControllerGroup tcg = TunnelControllerGroup . getInstance ( ) ;
if ( tcg ! = null ) {
_executor = tcg . getClientExecutor ( ) ;
} else {
// Fallback in case TCG.getInstance() is null, never instantiated
// and we were not started by TCG.
// Maybe a plugin loaded before TCG? Should be rare.
// Never shut down.
_executor = new TunnelControllerGroup . CustomThreadPoolExecutor ( ) ;
}
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 ) {
2004-11-15 14:35:16 +00:00
synchronized ( sockLock ) {
mySockets . clear ( ) ;
}
2015-04-24 19:19:18 +00:00
if ( open ) {
_log . error ( " Error listening for connections on " + addr + " port " + localPort , ex ) ;
2015-04-24 21:37:22 +00:00
l . log ( " Error listening for connections on " + addr + " port " + localPort + " : " + ex ) ;
2015-04-24 19:19:18 +00:00
notifyEvent ( " openBaseClientResult " , " error " ) ;
close ( true ) ;
}
2004-11-21 22:31:33 +00:00
synchronized ( this ) {
notifyAll ( ) ;
}
2004-04-10 11:45:02 +00:00
}
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
}
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 {
2015-04-21 20:37:59 +00:00
private final Socket _s ;
2004-12-30 22:51:16 +00:00
public BlockingRunner ( Socket s ) { _s = s ; }
public void run ( ) {
2015-04-21 20:37:59 +00:00
try {
clientConnectionRun ( _s ) ;
} catch ( Throwable t ) {
// probably an IllegalArgumentException from
// connecting to the router in a delay-open or
// close-on-idle tunnel (in connectManager() above)
_log . error ( " Uncaught error in i2ptunnel client " , t ) ;
}
2004-12-30 22:51:16 +00:00
}
}
2015-04-24 19:19:18 +00:00
/ * *
* Note that the tunnel can be reopened after this by calling startRunning ( ) .
* This may not release all resources . In particular , the I2PSocketManager remains
* and it may have timer threads that continue running .
*
* To release all resources permanently , call destroy ( ) .
*
* Does nothing if open is already false .
* Sets open = false but does not notifyAll ( ) .
*
* @return success
* /
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 ) ;
2014-09-13 14:49:38 +00:00
if ( _ownDest ) {
try {
session . destroySession ( ) ;
} catch ( I2PException ex ) { }
}
// TCG will try to destroy it too
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 ) {
2015-04-24 19:19:18 +00:00
if ( _log . shouldDebug ( ) )
_log . debug ( " 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
}
2014-11-13 20:12:55 +00:00
/ * *
* Note that the tunnel cannot be reopened after this by calling startRunning ( ) ,
* as it will destroy the underlying socket manager .
* This releases all resources if not a shared client .
* For shared client , the router will kill all the remaining streaming timers at shutdown .
*
* @since 0 . 9 . 17
* /
@Override
public synchronized boolean destroy ( ) {
close ( true ) ;
if ( _ownDest )
sockMgr . destroySocketManager ( ) ;
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
2014-12-05 15:12:51 +00:00
* you do not override manageConnection ( ) .
*
* This is run in a thread from an unlimited - size thread pool ,
* so it may block or run indefinitely .
2004-04-08 04:41:54 +00:00
* /
protected abstract void clientConnectionRun ( Socket s ) ;
2004-04-19 21:47:06 +00:00
}