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.Iterator ;
import java.util.List ;
import java.util.Properties ;
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 ;
2009-01-31 14:22:07 +00:00
import net.i2p.util.SimpleScheduler ;
2008-07-16 13:42:54 +00:00
import net.i2p.util.SimpleTimer ;
2004-04-08 04:41:54 +00:00
2004-04-10 11:45:02 +00:00
public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runnable {
2004-04-08 04:41:54 +00:00
private static final Log _log = new Log ( I2PTunnelClientBase . class ) ;
2005-10-10 22:58:18 +00:00
protected I2PAppContext _context ;
2004-04-08 04:41:54 +00:00
protected 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
2004-11-15 14:35:16 +00:00
protected List 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 ;
private 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
private String handlerName ;
2009-03-01 23:14:38 +00:00
private String privKeyFile ;
2004-04-08 04:41:54 +00:00
2009-04-11 13:55:38 +00:00
// private Object conLock = new Object();
2004-12-30 22:51:16 +00:00
/** List of Socket for those accept()ed but not yet started up */
2010-01-15 03:32:35 +00:00
protected final List _waitingSockets = new ArrayList ( 4 ) ; // FIXME should be final and use a factory. FIXME
2004-12-30 22:51:16 +00:00
/** How many connections will we allow to be in the process of being built at once? */
private int _numConnectionBuilders ;
/** How long will we allow sockets to sit in the _waitingSockets map before killing them? */
private int _maxWaitTime ;
/ * *
* How many concurrent connections this I2PTunnel instance will allow to be
* in the process of connecting ( or if less than 1 , there is no limit ) ?
* /
public static final String PROP_NUM_CONNECTION_BUILDERS = " i2ptunnel.numConnectionBuilders " ;
/ * *
* How long will we let a socket wait after being accept ( ) ed without getting
* pumped through a connection builder ( in milliseconds ) . If this time is
* reached , the socket is unceremoniously closed and discarded . If the max
* wait time is less than 1 , there is no limit .
*
* /
public static final String PROP_MAX_WAIT_TIME = " i2ptunnel.maxWaitTime " ;
private static final int DEFAULT_NUM_CONNECTION_BUILDERS = 5 ;
private static final int DEFAULT_MAX_WAIT_TIME = 30 * 1000 ;
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-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 ;
this . handlerName = handlerName + _clientId ;
_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 } ) ;
Thread t = new I2PAppThread ( this ) ;
t . setName ( " Client " + _clientId ) ;
listenerReady = false ;
t . start ( ) ;
open = true ;
synchronized ( this ) {
while ( ! listenerReady & & open ) {
try {
wait ( ) ;
} catch ( InterruptedException e ) {
// ignore
}
}
}
configurePool ( tunnel ) ;
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 " ) ;
}
}
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
/ * *
2009-04-10 23:12:41 +00:00
* @param pkf 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
* badly that we cant create a socketManager
* /
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 ;
2004-05-04 08:14:19 +00:00
this . handlerName = handlerName + _clientId ;
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 } ) ;
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
tunnel . getClientOptions ( ) . setProperty ( " i2cp.dontPublishLeaseSet " , " true " ) ;
2009-03-09 15:11:45 +00:00
boolean openNow = ! Boolean . valueOf ( tunnel . getClientOptions ( ) . getProperty ( " i2cp.delayOpen " ) ) . booleanValue ( ) ;
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
}
}
if ( sockMgr = = null ) {
2009-03-09 15:11:45 +00:00
l . log ( " Invalid I2CP configuration " ) ;
throw new IllegalArgumentException ( " Socket manager could not be created " ) ;
2004-04-10 11:45:02 +00:00
}
2009-03-09 15:11:45 +00:00
l . log ( " I2P session created " ) ;
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
listenerReady = false ;
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
}
}
}
2004-12-30 22:51:16 +00:00
configurePool ( tunnel ) ;
2004-04-10 11:45:02 +00:00
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
/ * *
* build and configure the pool handling accept ( ) ed but not yet
* established connections
*
* /
private void configurePool ( I2PTunnel tunnel ) {
2010-01-15 03:32:35 +00:00
//_waitingSockets = new ArrayList(4);
2004-12-30 22:51:16 +00:00
Properties opts = tunnel . getClientOptions ( ) ;
String maxWait = opts . getProperty ( PROP_MAX_WAIT_TIME , DEFAULT_MAX_WAIT_TIME + " " ) ;
try {
_maxWaitTime = Integer . parseInt ( maxWait ) ;
} catch ( NumberFormatException nfe ) {
_maxWaitTime = DEFAULT_MAX_WAIT_TIME ;
}
String numBuild = opts . getProperty ( PROP_NUM_CONNECTION_BUILDERS , DEFAULT_NUM_CONNECTION_BUILDERS + " " ) ;
try {
_numConnectionBuilders = Integer . parseInt ( numBuild ) ;
} catch ( NumberFormatException nfe ) {
_numConnectionBuilders = DEFAULT_NUM_CONNECTION_BUILDERS ;
}
for ( int i = 0 ; i < _numConnectionBuilders ; i + + ) {
String name = " ClientBuilder " + _clientId + '.' + i ;
2009-12-07 21:25:27 +00:00
I2PAppThread b = new I2PAppThread ( new TunnelConnectionBuilder ( ) , name ) ;
2004-12-30 22:51:16 +00:00
b . setDaemon ( true ) ;
b . start ( ) ;
}
}
2004-04-08 04:41:54 +00:00
2009-06-04 15:14:41 +00:00
/ * *
* Sets the this . sockMgr field if it is null , or if we want a new one
*
* 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 ( ) ;
if ( sess = = null ) {
newManager = true ;
} else if ( sess . isClosed ( ) & &
Boolean . valueOf ( getTunnel ( ) . getClientOptions ( ) . getProperty ( " i2cp.closeOnIdle " ) ) . booleanValue ( ) & &
Boolean . valueOf ( getTunnel ( ) . getClientOptions ( ) . getProperty ( " i2cp.newDestOnResume " ) ) . booleanValue ( ) ) {
// 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
/ * *
* this is ONLY for shared clients
* @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 synchronized 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
/ * *
* this is ONLY for shared clients
* @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 synchronized I2PSocketManager getSocketManager ( I2PTunnel tunnel ) {
2009-03-01 23:14:38 +00:00
return getSocketManager ( tunnel , null ) ;
}
2010-11-19 14:41:26 +00:00
/ * *
* this is ONLY for shared clients
* @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 ) {
2004-09-07 07:17:02 +00:00
if ( socketManager ! = null ) {
I2PSession s = socketManager . getSession ( ) ;
if ( ( s = = null ) | | ( 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 + " ] " ) ;
2009-03-09 15:11:45 +00:00
if ( s ! = null )
tunnel . removeSession ( s ) ;
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
/ * *
* @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
/ * *
* @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 ;
/ * *
* @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 ) ;
}
/ * *
* @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 ) {
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 ;
2010-11-19 14:41:26 +00:00
// Todo: Can't stop a tunnel from the UI while it's in this loop (no session yet)
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
/ * *
* create the default options ( using the default timeout , etc )
*
* /
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
/ * *
* create the default options ( using the default timeout , etc )
*
* /
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
/ * *
* 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 {
2009-06-04 15:14:41 +00:00
verifySocketManager ( ) ;
2004-04-10 11:45:02 +00:00
return createI2PSocket ( dest , getDefaultOptions ( ) ) ;
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 ( ) ;
}
2004-12-30 22:51:16 +00:00
synchronized ( _waitingSockets ) { _waitingSockets . notifyAll ( ) ; }
2004-11-21 22:31:33 +00:00
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 ( ) ;
}
notifyEvent ( " clientLocalPort " , new Integer ( 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 ) {
}
}
}
while ( true ) {
Socket s = ss . accept ( ) ;
2005-10-10 22:58:18 +00:00
long before = System . currentTimeMillis ( ) ;
2004-04-10 11:45:02 +00:00
manageConnection ( s ) ;
2005-10-10 22:58:18 +00:00
long total = System . currentTimeMillis ( ) - before ;
_context . statManager ( ) . addRateData ( " i2ptunnel.client.manageTime " , total , total ) ;
2004-04-10 11:45:02 +00:00
}
} 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-12-30 22:51:16 +00:00
synchronized ( _waitingSockets ) {
_waitingSockets . notifyAll ( ) ;
}
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 ;
if ( _numConnectionBuilders < = 0 ) {
2009-12-07 21:25:27 +00:00
new I2PAppThread ( new BlockingRunner ( s ) , " Clinet run " ) . start ( ) ;
2004-12-30 22:51:16 +00:00
return ;
}
if ( _maxWaitTime > 0 )
2009-01-31 14:22:07 +00:00
SimpleScheduler . getInstance ( ) . addEvent ( new CloseEvent ( s ) , _maxWaitTime ) ;
2004-12-30 22:51:16 +00:00
synchronized ( _waitingSockets ) {
_waitingSockets . add ( s ) ;
_waitingSockets . notifyAll ( ) ;
}
}
/ * *
* Blocking runner , used during the connection establishment whenever we
* are not using the queued builders .
*
* /
private class BlockingRunner implements Runnable {
private Socket _s ;
public BlockingRunner ( Socket s ) { _s = s ; }
public void run ( ) {
clientConnectionRun ( _s ) ;
}
}
/ * *
* Remove and close the socket from the waiting list , if it is still there .
*
* /
private class CloseEvent implements SimpleTimer . TimedEvent {
private Socket _s ;
public CloseEvent ( Socket s ) { _s = s ; }
public void timeReached ( ) {
2005-10-10 22:58:18 +00:00
int remaining = 0 ;
2004-12-30 22:51:16 +00:00
boolean stillWaiting = false ;
synchronized ( _waitingSockets ) {
stillWaiting = _waitingSockets . remove ( _s ) ;
2005-10-10 22:58:18 +00:00
remaining = _waitingSockets . size ( ) ;
2004-12-30 22:51:16 +00:00
}
if ( stillWaiting ) {
try { _s . close ( ) ; } catch ( IOException ioe ) { }
2005-10-10 22:58:18 +00:00
if ( _log . shouldLog ( Log . INFO ) ) {
_context . statManager ( ) . addRateData ( " i2ptunnel.client.closeBacklog " , remaining , 0 ) ;
2004-12-30 22:51:16 +00:00
_log . info ( " Closed a waiting socket because of backlog " ) ;
2005-10-10 22:58:18 +00:00
}
} else {
_context . statManager ( ) . addRateData ( " i2ptunnel.client.closeNoBacklog " , remaining , 0 ) ;
2004-12-30 22:51:16 +00:00
}
2004-11-13 09:59:37 +00:00
}
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 ( ) ) ;
if ( ! forced & & mySockets . size ( ) ! = 0 ) {
l . log ( " There are still active connections! " ) ;
_log . debug ( " can't close: there are still active connections! " ) ;
for ( Iterator it = mySockets . iterator ( ) ; it . hasNext ( ) ; ) {
l . log ( " -> " + it . next ( ) ) ;
}
return false ;
}
2010-01-10 11:23:20 +00:00
if ( ! chained ) {
I2PSession session = sockMgr . getSession ( ) ;
if ( session ! = null ) {
getTunnel ( ) . removeSession ( session ) ;
}
} // 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 ) {
ex . printStackTrace ( ) ;
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
synchronized ( _waitingSockets ) { _waitingSockets . notifyAll ( ) ; }
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 ) {
_log . error ( " Could not close socket " , ex ) ;
}
2004-04-08 04:41:54 +00:00
}
2004-05-07 03:33:23 +00:00
2004-12-30 22:51:16 +00:00
/ * *
* Pool runner pulling sockets off the waiting list and pushing them
* through clientConnectionRun . This dies when the I2PTunnel instance
* is closed .
*
* /
private class TunnelConnectionBuilder implements Runnable {
public void run ( ) {
Socket s = null ;
while ( open ) {
try {
synchronized ( _waitingSockets ) {
2010-05-05 16:51:54 +00:00
if ( _waitingSockets . isEmpty ( ) )
2004-12-30 22:51:16 +00:00
_waitingSockets . wait ( ) ;
else
s = ( Socket ) _waitingSockets . remove ( 0 ) ;
}
} catch ( InterruptedException ie ) { }
2005-10-10 22:58:18 +00:00
if ( s ! = null ) {
long before = System . currentTimeMillis ( ) ;
2004-12-30 22:51:16 +00:00
clientConnectionRun ( s ) ;
2005-10-10 22:58:18 +00:00
long total = System . currentTimeMillis ( ) - before ;
_context . statManager ( ) . addRateData ( " i2ptunnel.client.buildRunTime " , total , 0 ) ;
}
2004-12-30 22:51:16 +00:00
s = null ;
2004-11-13 09:59:37 +00:00
}
2004-04-10 11:45:02 +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
}