forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p.zzz.sam' (head 68de14d0053dea374413f9e0419b1c0f7e9ec3af)
to branch 'i2p.i2p' (head 54f5dd288f7c0c5a50f7f63f911aec4008be27e2)
This commit is contained in:
14
apps/sam/java/src/net/i2p/sam/Handler.java
Normal file
14
apps/sam/java/src/net/i2p/sam/Handler.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package net.i2p.sam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Something that can be stopped by the SAMBridge.
|
||||||
|
*
|
||||||
|
* @since 0.9.20
|
||||||
|
*/
|
||||||
|
public interface Handler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the handler
|
||||||
|
*/
|
||||||
|
public void stopHandling();
|
||||||
|
}
|
||||||
@@ -19,9 +19,13 @@ import java.nio.channels.ServerSocketChannel;
|
|||||||
import java.nio.channels.SocketChannel;
|
import java.nio.channels.SocketChannel;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.app.*;
|
import net.i2p.app.*;
|
||||||
@@ -30,6 +34,7 @@ import net.i2p.data.DataFormatException;
|
|||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.util.I2PAppThread;
|
import net.i2p.util.I2PAppThread;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.PortMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SAM bridge implementation.
|
* SAM bridge implementation.
|
||||||
@@ -55,6 +60,7 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
* destination keys (Destination+PrivateKey+SigningPrivateKey)
|
* destination keys (Destination+PrivateKey+SigningPrivateKey)
|
||||||
*/
|
*/
|
||||||
private final Map<String,String> nameToPrivKeys;
|
private final Map<String,String> nameToPrivKeys;
|
||||||
|
private final Set<Handler> _handlers;
|
||||||
|
|
||||||
private volatile boolean acceptConnections = true;
|
private volatile boolean acceptConnections = true;
|
||||||
|
|
||||||
@@ -95,6 +101,7 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
_listenPort = options.port;
|
_listenPort = options.port;
|
||||||
persistFilename = options.keyFile;
|
persistFilename = options.keyFile;
|
||||||
nameToPrivKeys = new HashMap<String,String>(8);
|
nameToPrivKeys = new HashMap<String,String>(8);
|
||||||
|
_handlers = new HashSet<Handler>(8);
|
||||||
this.i2cpProps = options.opts;
|
this.i2cpProps = options.opts;
|
||||||
_state = INITIALIZED;
|
_state = INITIALIZED;
|
||||||
}
|
}
|
||||||
@@ -124,6 +131,7 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
_listenPort = listenPort;
|
_listenPort = listenPort;
|
||||||
persistFilename = persistFile;
|
persistFilename = persistFile;
|
||||||
nameToPrivKeys = new HashMap<String,String>(8);
|
nameToPrivKeys = new HashMap<String,String>(8);
|
||||||
|
_handlers = new HashSet<Handler>(8);
|
||||||
loadKeys();
|
loadKeys();
|
||||||
try {
|
try {
|
||||||
openSocket();
|
openSocket();
|
||||||
@@ -209,8 +217,9 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load up the keys from the persistFilename
|
* Load up the keys from the persistFilename.
|
||||||
*
|
* TODO use DataHelper
|
||||||
|
* TODO store in config dir, not base dir
|
||||||
*/
|
*/
|
||||||
private void loadKeys() {
|
private void loadKeys() {
|
||||||
synchronized (nameToPrivKeys) {
|
synchronized (nameToPrivKeys) {
|
||||||
@@ -218,7 +227,7 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
BufferedReader br = null;
|
BufferedReader br = null;
|
||||||
try {
|
try {
|
||||||
br = new BufferedReader(new InputStreamReader(
|
br = new BufferedReader(new InputStreamReader(
|
||||||
new FileInputStream(persistFilename)));
|
new FileInputStream(persistFilename), "UTF-8"));
|
||||||
String line = null;
|
String line = null;
|
||||||
while ( (line = br.readLine()) != null) {
|
while ( (line = br.readLine()) != null) {
|
||||||
int eq = line.indexOf('=');
|
int eq = line.indexOf('=');
|
||||||
@@ -226,6 +235,8 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
String privKeys = line.substring(eq+1);
|
String privKeys = line.substring(eq+1);
|
||||||
nameToPrivKeys.put(name, privKeys);
|
nameToPrivKeys.put(name, privKeys);
|
||||||
}
|
}
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("Loaded " + nameToPrivKeys.size() + " private keys from " + persistFilename);
|
||||||
} catch (FileNotFoundException fnfe) {
|
} catch (FileNotFoundException fnfe) {
|
||||||
_log.warn("Key file does not exist at " + persistFilename);
|
_log.warn("Key file does not exist at " + persistFilename);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
@@ -237,8 +248,9 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the current keys to disk in the location specified on creation
|
* Store the current keys to disk in the location specified on creation.
|
||||||
*
|
* TODO use DataHelper
|
||||||
|
* TODO store in config dir, not base dir
|
||||||
*/
|
*/
|
||||||
private void storeKeys() {
|
private void storeKeys() {
|
||||||
synchronized (nameToPrivKeys) {
|
synchronized (nameToPrivKeys) {
|
||||||
@@ -248,11 +260,13 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
for (Map.Entry<String, String> entry : nameToPrivKeys.entrySet()) {
|
for (Map.Entry<String, String> entry : nameToPrivKeys.entrySet()) {
|
||||||
String name = entry.getKey();
|
String name = entry.getKey();
|
||||||
String privKeys = entry.getValue();
|
String privKeys = entry.getValue();
|
||||||
out.write(name.getBytes());
|
out.write(name.getBytes("UTF-8"));
|
||||||
out.write('=');
|
out.write('=');
|
||||||
out.write(privKeys.getBytes());
|
out.write(privKeys.getBytes("UTF-8"));
|
||||||
out.write('\n');
|
out.write('\n');
|
||||||
}
|
}
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("Saved " + nameToPrivKeys.size() + " private keys to " + persistFilename);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
_log.error("Error writing out the SAM keys to " + persistFilename, ioe);
|
_log.error("Error writing out the SAM keys to " + persistFilename, ioe);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -261,6 +275,51 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handlers must call on startup
|
||||||
|
* @since 0.9.20
|
||||||
|
*/
|
||||||
|
public void register(Handler handler) {
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("Register " + handler);
|
||||||
|
synchronized (_handlers) {
|
||||||
|
_handlers.add(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handlers must call on stop
|
||||||
|
* @since 0.9.20
|
||||||
|
*/
|
||||||
|
public void unregister(Handler handler) {
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("Unregister " + handler);
|
||||||
|
synchronized (_handlers) {
|
||||||
|
_handlers.remove(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop all the handlers.
|
||||||
|
* @since 0.9.20
|
||||||
|
*/
|
||||||
|
private void stopHandlers() {
|
||||||
|
List<Handler> handlers = null;
|
||||||
|
synchronized (_handlers) {
|
||||||
|
if (!_handlers.isEmpty()) {
|
||||||
|
handlers = new ArrayList<Handler>(_handlers);
|
||||||
|
_handlers.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (handlers != null) {
|
||||||
|
for (Handler handler : handlers) {
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("Stopping " + handler);
|
||||||
|
handler.stopHandling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////// begin ClientApp interface, use only if using correct construtor
|
////// begin ClientApp interface, use only if using correct construtor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,6 +329,9 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
if (_state != INITIALIZED)
|
if (_state != INITIALIZED)
|
||||||
return;
|
return;
|
||||||
changeState(STARTING);
|
changeState(STARTING);
|
||||||
|
synchronized (_handlers) {
|
||||||
|
_handlers.clear();
|
||||||
|
}
|
||||||
loadKeys();
|
loadKeys();
|
||||||
try {
|
try {
|
||||||
openSocket();
|
openSocket();
|
||||||
@@ -285,7 +347,8 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does NOT stop existing sessions.
|
* As of 0.9.20, stops running handlers and sessions.
|
||||||
|
*
|
||||||
* @since 0.9.6
|
* @since 0.9.6
|
||||||
*/
|
*/
|
||||||
public synchronized void shutdown(String[] args) {
|
public synchronized void shutdown(String[] args) {
|
||||||
@@ -293,11 +356,11 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
return;
|
return;
|
||||||
changeState(STOPPING);
|
changeState(STOPPING);
|
||||||
acceptConnections = false;
|
acceptConnections = false;
|
||||||
|
stopHandlers();
|
||||||
if (_runner != null)
|
if (_runner != null)
|
||||||
_runner.interrupt();
|
_runner.interrupt();
|
||||||
else
|
else
|
||||||
changeState(STOPPED);
|
changeState(STOPPED);
|
||||||
// TODO does not stop active connections / sessions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -375,7 +438,7 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
* @since 0.9.6
|
* @since 0.9.6
|
||||||
*/
|
*/
|
||||||
private void startThread() {
|
private void startThread() {
|
||||||
I2PAppThread t = new I2PAppThread(this, "SAMListener");
|
I2PAppThread t = new I2PAppThread(this, "SAMListener " + _listenPort);
|
||||||
if (Boolean.parseBoolean(System.getProperty("sam.shutdownOnOOM"))) {
|
if (Boolean.parseBoolean(System.getProperty("sam.shutdownOnOOM"))) {
|
||||||
t.addOOMEventThreadListener(new I2PAppThread.OOMEventListener() {
|
t.addOOMEventThreadListener(new I2PAppThread.OOMEventListener() {
|
||||||
public void outOfMemory(OutOfMemoryError err) {
|
public void outOfMemory(OutOfMemoryError err) {
|
||||||
@@ -487,6 +550,7 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
changeState(RUNNING);
|
changeState(RUNNING);
|
||||||
if (_mgr != null)
|
if (_mgr != null)
|
||||||
_mgr.register(this);
|
_mgr.register(this);
|
||||||
|
I2PAppContext.getGlobalContext().portMapper().register(PortMapper.SVC_SAM, _listenPort);
|
||||||
try {
|
try {
|
||||||
while (acceptConnections) {
|
while (acceptConnections) {
|
||||||
SocketChannel s = serverSocket.accept();
|
SocketChannel s = serverSocket.accept();
|
||||||
@@ -495,18 +559,19 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
+ s.socket().getInetAddress().toString() + ":"
|
+ s.socket().getInetAddress().toString() + ":"
|
||||||
+ s.socket().getPort());
|
+ s.socket().getPort());
|
||||||
|
|
||||||
class HelloHandler implements Runnable {
|
class HelloHandler implements Runnable, Handler {
|
||||||
private final SocketChannel s;
|
private final SocketChannel s;
|
||||||
private final SAMBridge parent;
|
private final SAMBridge parent;
|
||||||
|
|
||||||
HelloHandler(SocketChannel s, SAMBridge parent) {
|
HelloHandler(SocketChannel s, SAMBridge parent) {
|
||||||
this.s = s ;
|
this.s = s ;
|
||||||
this.parent = parent ;
|
this.parent = parent ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
|
parent.register(this);
|
||||||
try {
|
try {
|
||||||
SAMHandler handler = SAMHandlerFactory.createSAMHandler(s, i2cpProps);
|
SAMHandler handler = SAMHandlerFactory.createSAMHandler(s, i2cpProps, parent);
|
||||||
if (handler == null) {
|
if (handler == null) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("SAM handler has not been instantiated");
|
_log.debug("SAM handler has not been instantiated");
|
||||||
@@ -515,7 +580,6 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
} catch (IOException e) {}
|
} catch (IOException e) {}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handler.setBridge(parent);
|
|
||||||
handler.startHandling();
|
handler.startHandling();
|
||||||
} catch (SAMException e) {
|
} catch (SAMException e) {
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
@@ -526,11 +590,17 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
} catch (Exception ee) {
|
} catch (Exception ee) {
|
||||||
try { s.close(); } catch (IOException ioe) {}
|
try { s.close(); } catch (IOException ioe) {}
|
||||||
_log.log(Log.CRIT, "Unexpected error handling SAM connection", ee);
|
_log.log(Log.CRIT, "Unexpected error handling SAM connection", ee);
|
||||||
|
} finally {
|
||||||
|
parent.unregister(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.20 */
|
||||||
|
public void stopHandling() {
|
||||||
|
try { s.close(); } catch (IOException ioe) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// TODO: Handler threads are not saved or tracked and cannot be stopped
|
new I2PAppThread(new HelloHandler(s,this), "SAM HelloHandler").start();
|
||||||
new I2PAppThread(new HelloHandler(s,this), "HelloHandler").start();
|
|
||||||
}
|
}
|
||||||
changeState(STOPPING);
|
changeState(STOPPING);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -546,6 +616,8 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
if (serverSocket != null)
|
if (serverSocket != null)
|
||||||
serverSocket.close();
|
serverSocket.close();
|
||||||
} catch (IOException e) {}
|
} catch (IOException e) {}
|
||||||
|
I2PAppContext.getGlobalContext().portMapper().unregister(PortMapper.SVC_SAM);
|
||||||
|
stopHandlers();
|
||||||
changeState(STOPPED);
|
changeState(STOPPED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,12 +25,12 @@ import net.i2p.util.Log;
|
|||||||
*
|
*
|
||||||
* @author human
|
* @author human
|
||||||
*/
|
*/
|
||||||
abstract class SAMHandler implements Runnable {
|
abstract class SAMHandler implements Runnable, Handler {
|
||||||
|
|
||||||
protected final Log _log;
|
protected final Log _log;
|
||||||
|
|
||||||
protected I2PAppThread thread = null;
|
protected I2PAppThread thread;
|
||||||
protected SAMBridge bridge = null;
|
protected final SAMBridge bridge;
|
||||||
|
|
||||||
private final Object socketWLock = new Object(); // Guards writings on socket
|
private final Object socketWLock = new Object(); // Guards writings on socket
|
||||||
protected final SocketChannel socket;
|
protected final SocketChannel socket;
|
||||||
@@ -41,8 +41,8 @@ abstract class SAMHandler implements Runnable {
|
|||||||
/** I2CP options configuring the I2CP connection (port, host, numHops, etc) */
|
/** I2CP options configuring the I2CP connection (port, host, numHops, etc) */
|
||||||
protected final Properties i2cpProps;
|
protected final Properties i2cpProps;
|
||||||
|
|
||||||
private final Object stopLock = new Object();
|
protected final Object stopLock = new Object();
|
||||||
private volatile boolean stopHandler;
|
protected boolean stopHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SAMHandler constructor (to be called by subclasses)
|
* SAMHandler constructor (to be called by subclasses)
|
||||||
@@ -53,14 +53,15 @@ abstract class SAMHandler implements Runnable {
|
|||||||
* @param i2cpProps properties to configure the I2CP connection (host, port, etc)
|
* @param i2cpProps properties to configure the I2CP connection (host, port, etc)
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
protected SAMHandler(SocketChannel s,
|
protected SAMHandler(SocketChannel s, int verMajor, int verMinor,
|
||||||
int verMajor, int verMinor, Properties i2cpProps) throws IOException {
|
Properties i2cpProps, SAMBridge parent) throws IOException {
|
||||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
|
_log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
|
||||||
socket = s;
|
socket = s;
|
||||||
|
|
||||||
this.verMajor = verMajor;
|
this.verMajor = verMajor;
|
||||||
this.verMinor = verMinor;
|
this.verMinor = verMinor;
|
||||||
this.i2cpProps = i2cpProps;
|
this.i2cpProps = i2cpProps;
|
||||||
|
bridge = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,12 +69,10 @@ abstract class SAMHandler implements Runnable {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public final void startHandling() {
|
public final void startHandling() {
|
||||||
thread = new I2PAppThread(this, "SAMHandler");
|
thread = new I2PAppThread(this, getClass().getSimpleName());
|
||||||
thread.start();
|
thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBridge(SAMBridge bridge) { this.bridge = bridge; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actually handle the SAM protocol.
|
* Actually handle the SAM protocol.
|
||||||
*
|
*
|
||||||
@@ -81,10 +80,9 @@ abstract class SAMHandler implements Runnable {
|
|||||||
protected abstract void handle();
|
protected abstract void handle();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the input stream of the socket connected to the SAM client
|
* Get the channel of the socket connected to the SAM client
|
||||||
*
|
*
|
||||||
* @return input stream
|
* @return channel
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
protected final SocketChannel getClientSocket() {
|
protected final SocketChannel getClientSocket() {
|
||||||
return socket ;
|
return socket ;
|
||||||
@@ -156,13 +154,17 @@ abstract class SAMHandler implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the SAM handler
|
* Stop the SAM handler, close the client socket,
|
||||||
*
|
* unregister with the bridge.
|
||||||
*/
|
*/
|
||||||
public final void stopHandling() {
|
public void stopHandling() {
|
||||||
synchronized (stopLock) {
|
synchronized (stopLock) {
|
||||||
stopHandler = true;
|
stopHandler = true;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
closeClientSocket();
|
||||||
|
} catch (IOException e) {}
|
||||||
|
bridge.unregister(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -183,14 +185,23 @@ abstract class SAMHandler implements Runnable {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final String toString() {
|
public final String toString() {
|
||||||
return ("SAM handler (class: " + this.getClass().getName()
|
return (this.getClass().getSimpleName()
|
||||||
+ "; SAM version: " + verMajor + "." + verMinor
|
+ "; SAM version: " + verMajor + "." + verMinor
|
||||||
+ "; client: "
|
+ "; client: "
|
||||||
+ this.socket.socket().getInetAddress().toString() + ":"
|
+ this.socket.socket().getInetAddress().toString() + ":"
|
||||||
+ this.socket.socket().getPort() + ")");
|
+ this.socket.socket().getPort() + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register with the bridge, call handle(),
|
||||||
|
* unregister with the bridge.
|
||||||
|
*/
|
||||||
public final void run() {
|
public final void run() {
|
||||||
handle();
|
bridge.register(this);
|
||||||
|
try {
|
||||||
|
handle();
|
||||||
|
} finally {
|
||||||
|
bridge.unregister(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,13 +38,15 @@ class SAMHandlerFactory {
|
|||||||
* @throws SAMException if the connection handshake (HELLO message) was malformed
|
* @throws SAMException if the connection handshake (HELLO message) was malformed
|
||||||
* @return A SAM protocol handler, or null if the client closed before the handshake
|
* @return A SAM protocol handler, or null if the client closed before the handshake
|
||||||
*/
|
*/
|
||||||
public static SAMHandler createSAMHandler(SocketChannel s, Properties i2cpProps) throws SAMException {
|
public static SAMHandler createSAMHandler(SocketChannel s, Properties i2cpProps,
|
||||||
|
SAMBridge parent) throws SAMException {
|
||||||
StringTokenizer tok;
|
StringTokenizer tok;
|
||||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(SAMHandlerFactory.class);
|
Log log = I2PAppContext.getGlobalContext().logManager().getLog(SAMHandlerFactory.class);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Socket sock = s.socket();
|
Socket sock = s.socket();
|
||||||
sock.setSoTimeout(HELLO_TIMEOUT);
|
sock.setSoTimeout(HELLO_TIMEOUT);
|
||||||
|
sock.setKeepAlive(true);
|
||||||
String line = DataHelper.readLine(sock.getInputStream());
|
String line = DataHelper.readLine(sock.getInputStream());
|
||||||
sock.setSoTimeout(0);
|
sock.setSoTimeout(0);
|
||||||
if (line == null) {
|
if (line == null) {
|
||||||
@@ -103,13 +105,13 @@ class SAMHandlerFactory {
|
|||||||
try {
|
try {
|
||||||
switch (verMajor) {
|
switch (verMajor) {
|
||||||
case 1:
|
case 1:
|
||||||
handler = new SAMv1Handler(s, verMajor, verMinor, i2cpProps);
|
handler = new SAMv1Handler(s, verMajor, verMinor, i2cpProps, parent);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
handler = new SAMv2Handler(s, verMajor, verMinor, i2cpProps);
|
handler = new SAMv2Handler(s, verMajor, verMinor, i2cpProps, parent);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
handler = new SAMv3Handler(s, verMajor, verMinor, i2cpProps);
|
handler = new SAMv3Handler(s, verMajor, verMinor, i2cpProps, parent);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
log.error("BUG! Trying to initialize the wrong SAM version!");
|
log.error("BUG! Trying to initialize the wrong SAM version!");
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ abstract class SAMMessageSession {
|
|||||||
|
|
||||||
handler = new SAMMessageSessionHandler(destStream, props);
|
handler = new SAMMessageSessionHandler(destStream, props);
|
||||||
|
|
||||||
|
// FIXME don't start threads in constructors
|
||||||
Thread t = new I2PAppThread(handler, "SAMMessageSessionHandler");
|
Thread t = new I2PAppThread(handler, "SAMMessageSessionHandler");
|
||||||
t.start();
|
t.start();
|
||||||
}
|
}
|
||||||
@@ -125,7 +126,6 @@ abstract class SAMMessageSession {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Close a SAM message-based session.
|
* Close a SAM message-based session.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public void close() {
|
public void close() {
|
||||||
handler.stopRunning();
|
handler.stopRunning();
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ class SAMUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Dump a Properties object in an human-readable form */
|
/* Dump a Properties object in an human-readable form */
|
||||||
|
/****
|
||||||
private static String dumpProperties(Properties props) {
|
private static String dumpProperties(Properties props) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
String key, val;
|
String key, val;
|
||||||
@@ -231,6 +232,7 @@ class SAMUtils {
|
|||||||
|
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
****/
|
||||||
|
|
||||||
/****
|
/****
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
|
|||||||
@@ -60,8 +60,9 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
* @throws SAMException
|
* @throws SAMException
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public SAMv1Handler(SocketChannel s, int verMajor, int verMinor) throws SAMException, IOException {
|
public SAMv1Handler(SocketChannel s, int verMajor, int verMinor,
|
||||||
this(s, verMajor, verMinor, new Properties());
|
SAMBridge parent) throws SAMException, IOException {
|
||||||
|
this(s, verMajor, verMinor, new Properties(), parent);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Create a new SAM version 1 handler. This constructor expects
|
* Create a new SAM version 1 handler. This constructor expects
|
||||||
@@ -75,13 +76,14 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
* @throws SAMException
|
* @throws SAMException
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public SAMv1Handler(SocketChannel s, int verMajor, int verMinor, Properties i2cpProps) throws SAMException, IOException {
|
public SAMv1Handler(SocketChannel s, int verMajor, int verMinor,
|
||||||
super(s, verMajor, verMinor, i2cpProps);
|
Properties i2cpProps, SAMBridge parent) throws SAMException, IOException {
|
||||||
|
super(s, verMajor, verMinor, i2cpProps, parent);
|
||||||
_id = __id.incrementAndGet();
|
_id = __id.incrementAndGet();
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("SAM version 1 handler instantiated");
|
_log.debug("SAM version 1 handler instantiated");
|
||||||
|
|
||||||
if ( ! verifVersion() ) {
|
if ( ! verifVersion() ) {
|
||||||
throw new SAMException("BUG! Wrong protocol version!");
|
throw new SAMException("BUG! Wrong protocol version!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,8 +185,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Caught IOException ("
|
_log.debug("Caught IOException for message [" + msg + "]", e);
|
||||||
+ e.getMessage() + ") for message [" + msg + "]", e);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
_log.error("Unexpected exception for message [" + msg + "]", e);
|
_log.error("Unexpected exception for message [" + msg + "]", e);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -193,7 +194,8 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
try {
|
try {
|
||||||
closeClientSocket();
|
closeClientSocket();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
_log.error("Error closing socket: " + e.getMessage());
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Error closing socket", e);
|
||||||
}
|
}
|
||||||
if (getRawSession() != null) {
|
if (getRawSession() != null) {
|
||||||
getRawSession().close();
|
getRawSession().close();
|
||||||
@@ -797,7 +799,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
public void receiveRawBytes(byte data[]) throws IOException {
|
public void receiveRawBytes(byte data[]) throws IOException {
|
||||||
if (getRawSession() == null) {
|
if (getRawSession() == null) {
|
||||||
_log.error("BUG! Received raw bytes, but session is null!");
|
_log.error("BUG! Received raw bytes, but session is null!");
|
||||||
throw new NullPointerException("BUG! RAW session is null!");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteArrayOutputStream msg = new ByteArrayOutputStream();
|
ByteArrayOutputStream msg = new ByteArrayOutputStream();
|
||||||
@@ -818,7 +820,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
|
|
||||||
if (getRawSession() == null) {
|
if (getRawSession() == null) {
|
||||||
_log.error("BUG! Got raw receiving stop, but session is null!");
|
_log.error("BUG! Got raw receiving stop, but session is null!");
|
||||||
throw new NullPointerException("BUG! RAW session is null!");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -833,7 +835,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
public void receiveDatagramBytes(Destination sender, byte data[]) throws IOException {
|
public void receiveDatagramBytes(Destination sender, byte data[]) throws IOException {
|
||||||
if (getDatagramSession() == null) {
|
if (getDatagramSession() == null) {
|
||||||
_log.error("BUG! Received datagram bytes, but session is null!");
|
_log.error("BUG! Received datagram bytes, but session is null!");
|
||||||
throw new NullPointerException("BUG! DATAGRAM session is null!");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteArrayOutputStream msg = new ByteArrayOutputStream();
|
ByteArrayOutputStream msg = new ByteArrayOutputStream();
|
||||||
@@ -855,7 +857,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
|
|
||||||
if (getDatagramSession() == null) {
|
if (getDatagramSession() == null) {
|
||||||
_log.error("BUG! Got datagram receiving stop, but session is null!");
|
_log.error("BUG! Got datagram receiving stop, but session is null!");
|
||||||
throw new NullPointerException("BUG! DATAGRAM session is null!");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -873,7 +875,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
if ( getStreamSession() == null )
|
if ( getStreamSession() == null )
|
||||||
{
|
{
|
||||||
_log.error ( "BUG! Want to answer to stream SEND, but session is null!" );
|
_log.error ( "BUG! Want to answer to stream SEND, but session is null!" );
|
||||||
throw new NullPointerException ( "BUG! STREAM session is null!" );
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !writeString ( "STREAM SEND ID=" + id
|
if ( !writeString ( "STREAM SEND ID=" + id
|
||||||
@@ -891,7 +893,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
if ( getStreamSession() == null )
|
if ( getStreamSession() == null )
|
||||||
{
|
{
|
||||||
_log.error ( "BUG! Stream outgoing buffer is free, but session is null!" );
|
_log.error ( "BUG! Stream outgoing buffer is free, but session is null!" );
|
||||||
throw new NullPointerException ( "BUG! STREAM session is null!" );
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !writeString ( "STREAM READY_TO_SEND ID=" + id + "\n" ) )
|
if ( !writeString ( "STREAM READY_TO_SEND ID=" + id + "\n" ) )
|
||||||
@@ -904,7 +906,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
public void notifyStreamIncomingConnection(int id, Destination d) throws IOException {
|
public void notifyStreamIncomingConnection(int id, Destination d) throws IOException {
|
||||||
if (getStreamSession() == null) {
|
if (getStreamSession() == null) {
|
||||||
_log.error("BUG! Received stream connection, but session is null!");
|
_log.error("BUG! Received stream connection, but session is null!");
|
||||||
throw new NullPointerException("BUG! STREAM session is null!");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!writeString("STREAM CONNECTED DESTINATION="
|
if (!writeString("STREAM CONNECTED DESTINATION="
|
||||||
@@ -914,18 +916,16 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param msg may be null */
|
||||||
public void notifyStreamOutgoingConnection ( int id, String result, String msg ) throws IOException
|
public void notifyStreamOutgoingConnection ( int id, String result, String msg ) throws IOException
|
||||||
{
|
{
|
||||||
if ( getStreamSession() == null )
|
if ( getStreamSession() == null )
|
||||||
{
|
{
|
||||||
_log.error ( "BUG! Received stream connection, but session is null!" );
|
_log.error ( "BUG! Received stream connection, but session is null!" );
|
||||||
throw new NullPointerException ( "BUG! STREAM session is null!" );
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String msgString = "" ;
|
String msgString = createMessageString(msg);
|
||||||
|
|
||||||
if ( msg != null ) msgString = " MESSAGE=\"" + msg + "\"";
|
|
||||||
|
|
||||||
if ( !writeString ( "STREAM STATUS RESULT="
|
if ( !writeString ( "STREAM STATUS RESULT="
|
||||||
+ result
|
+ result
|
||||||
+ " ID=" + id
|
+ " ID=" + id
|
||||||
@@ -936,10 +936,35 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a string to be appended to a status.
|
||||||
|
*
|
||||||
|
* @param msg may be null
|
||||||
|
* @return non-null, "" if msg is null, MESSAGE=msg or MESSAGE="msg a b c"
|
||||||
|
* with leading space if msg is non-null
|
||||||
|
* @since 0.9.20
|
||||||
|
*/
|
||||||
|
protected static String createMessageString(String msg) {
|
||||||
|
String rv;
|
||||||
|
if ( msg != null ) {
|
||||||
|
msg = msg.replace("\n", " ");
|
||||||
|
msg = msg.replace("\r", " ");
|
||||||
|
if (!msg.startsWith("\"")) {
|
||||||
|
msg = msg.replace("\"", "");
|
||||||
|
if (msg.contains("\"") || msg.contains("\t"))
|
||||||
|
msg = '"' + msg + '"';
|
||||||
|
}
|
||||||
|
rv = " MESSAGE=\"" + msg + "\"";
|
||||||
|
} else {
|
||||||
|
rv = "";
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
public void receiveStreamBytes(int id, ByteBuffer data) throws IOException {
|
public void receiveStreamBytes(int id, ByteBuffer data) throws IOException {
|
||||||
if (getStreamSession() == null) {
|
if (getStreamSession() == null) {
|
||||||
_log.error("Received stream bytes, but session is null!");
|
_log.error("Received stream bytes, but session is null!");
|
||||||
throw new NullPointerException("BUG! STREAM session is null!");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String msgText = "STREAM RECEIVED ID=" + id +" SIZE=" + data.remaining() + "\n";
|
String msgText = "STREAM RECEIVED ID=" + id +" SIZE=" + data.remaining() + "\n";
|
||||||
@@ -956,16 +981,15 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param msg may be null */
|
||||||
public void notifyStreamDisconnection(int id, String result, String msg) throws IOException {
|
public void notifyStreamDisconnection(int id, String result, String msg) throws IOException {
|
||||||
if (getStreamSession() == null) {
|
if (getStreamSession() == null) {
|
||||||
_log.error("BUG! Received stream disconnection, but session is null!");
|
_log.error("BUG! Received stream disconnection, but session is null!");
|
||||||
throw new NullPointerException("BUG! STREAM session is null!");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: msg should be escaped!
|
String msgString = createMessageString(msg);
|
||||||
if (!writeString("STREAM CLOSED ID=" + id + " RESULT=" + result
|
if (!writeString("STREAM CLOSED ID=" + id + " RESULT=" + result + msgString + '\n')) {
|
||||||
+ (msg == null ? "" : (" MESSAGE=" + msg))
|
|
||||||
+ "\n")) {
|
|
||||||
throw new IOException("Error notifying disconnection to SAM client");
|
throw new IOException("Error notifying disconnection to SAM client");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -976,7 +1000,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
|
|
||||||
if (getStreamSession() == null) {
|
if (getStreamSession() == null) {
|
||||||
_log.error("BUG! Got stream receiving stop, but session is null!");
|
_log.error("BUG! Got stream receiving stop, but session is null!");
|
||||||
throw new NullPointerException("BUG! STREAM session is null!");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -34,9 +34,10 @@ class SAMv2Handler extends SAMv1Handler implements SAMRawReceiver, SAMDatagramRe
|
|||||||
* @param verMajor SAM major version to manage (should be 2)
|
* @param verMajor SAM major version to manage (should be 2)
|
||||||
* @param verMinor SAM minor version to manage
|
* @param verMinor SAM minor version to manage
|
||||||
*/
|
*/
|
||||||
public SAMv2Handler ( SocketChannel s, int verMajor, int verMinor ) throws SAMException, IOException
|
public SAMv2Handler(SocketChannel s, int verMajor, int verMinor,
|
||||||
|
SAMBridge parent) throws SAMException, IOException
|
||||||
{
|
{
|
||||||
this ( s, verMajor, verMinor, new Properties() );
|
this(s, verMajor, verMinor, new Properties(), parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,9 +51,10 @@ class SAMv2Handler extends SAMv1Handler implements SAMRawReceiver, SAMDatagramRe
|
|||||||
* @param i2cpProps properties to configure the I2CP connection (host, port, etc)
|
* @param i2cpProps properties to configure the I2CP connection (host, port, etc)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public SAMv2Handler ( SocketChannel s, int verMajor, int verMinor, Properties i2cpProps ) throws SAMException, IOException
|
public SAMv2Handler(SocketChannel s, int verMajor, int verMinor,
|
||||||
|
Properties i2cpProps, SAMBridge parent) throws SAMException, IOException
|
||||||
{
|
{
|
||||||
super ( s, verMajor, verMinor, i2cpProps );
|
super(s, verMajor, verMinor, i2cpProps, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
|
|
||||||
private Session session;
|
private Session session;
|
||||||
public static final SessionsDB sSessionsHash = new SessionsDB();
|
public static final SessionsDB sSessionsHash = new SessionsDB();
|
||||||
private boolean stolenSocket;
|
private volatile boolean stolenSocket;
|
||||||
private boolean streamForwardingSocket;
|
private volatile boolean streamForwardingSocket;
|
||||||
|
|
||||||
|
|
||||||
interface Session {
|
interface Session {
|
||||||
@@ -67,9 +67,10 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
* @param verMajor SAM major version to manage (should be 3)
|
* @param verMajor SAM major version to manage (should be 3)
|
||||||
* @param verMinor SAM minor version to manage
|
* @param verMinor SAM minor version to manage
|
||||||
*/
|
*/
|
||||||
public SAMv3Handler ( SocketChannel s, int verMajor, int verMinor ) throws SAMException, IOException
|
public SAMv3Handler(SocketChannel s, int verMajor, int verMinor,
|
||||||
|
SAMBridge parent) throws SAMException, IOException
|
||||||
{
|
{
|
||||||
this ( s, verMajor, verMinor, new Properties() );
|
this(s, verMajor, verMinor, new Properties(), parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,9 +84,10 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
* @param i2cpProps properties to configure the I2CP connection (host, port, etc)
|
* @param i2cpProps properties to configure the I2CP connection (host, port, etc)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public SAMv3Handler ( SocketChannel s, int verMajor, int verMinor, Properties i2cpProps ) throws SAMException, IOException
|
public SAMv3Handler(SocketChannel s, int verMajor, int verMinor,
|
||||||
|
Properties i2cpProps, SAMBridge parent) throws SAMException, IOException
|
||||||
{
|
{
|
||||||
super ( s, verMajor, verMinor, i2cpProps );
|
super(s, verMajor, verMinor, i2cpProps, parent);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("SAM version 3 handler instantiated");
|
_log.debug("SAM version 3 handler instantiated");
|
||||||
}
|
}
|
||||||
@@ -214,6 +216,9 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The values in the SessionsDB
|
||||||
|
*/
|
||||||
public static class SessionRecord
|
public static class SessionRecord
|
||||||
{
|
{
|
||||||
private final String m_dest ;
|
private final String m_dest ;
|
||||||
@@ -266,11 +271,14 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* basically a HashMap from String to SessionRecord
|
||||||
|
*/
|
||||||
public static class SessionsDB
|
public static class SessionsDB
|
||||||
{
|
{
|
||||||
private static final long serialVersionUID = 0x1;
|
private static final long serialVersionUID = 0x1;
|
||||||
|
|
||||||
static class ExistingIdException extends Exception {
|
static class ExistingIdException extends Exception {
|
||||||
private static final long serialVersionUID = 0x1;
|
private static final long serialVersionUID = 0x1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,6 +292,7 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
map = new HashMap<String, SessionRecord>() ;
|
map = new HashMap<String, SessionRecord>() ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return success */
|
||||||
synchronized public boolean put( String nick, SessionRecord session )
|
synchronized public boolean put( String nick, SessionRecord session )
|
||||||
throws ExistingIdException, ExistingDestException
|
throws ExistingIdException, ExistingDestException
|
||||||
{
|
{
|
||||||
@@ -305,17 +314,12 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
return false ;
|
return false ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return true if removed */
|
||||||
synchronized public boolean del( String nick )
|
synchronized public boolean del( String nick )
|
||||||
{
|
{
|
||||||
SessionRecord rec = map.get(nick);
|
return map.remove(nick) != null;
|
||||||
|
|
||||||
if ( rec!=null ) {
|
|
||||||
map.remove(nick);
|
|
||||||
return true ;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized public SessionRecord get(String nick)
|
synchronized public SessionRecord get(String nick)
|
||||||
{
|
{
|
||||||
return map.get(nick);
|
return map.get(nick);
|
||||||
@@ -332,12 +336,23 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
return this.socket.socket().getInetAddress().getHostAddress();
|
return this.socket.socket().getInetAddress().getHostAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For SAMv3StreamSession connect and accept
|
||||||
|
*/
|
||||||
public void stealSocket()
|
public void stealSocket()
|
||||||
{
|
{
|
||||||
stolenSocket = true ;
|
stolenSocket = true ;
|
||||||
this.stopHandling();
|
this.stopHandling();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For SAMv3StreamSession
|
||||||
|
* @since 0.9.20
|
||||||
|
*/
|
||||||
|
SAMBridge getBridge() {
|
||||||
|
return bridge;
|
||||||
|
}
|
||||||
|
|
||||||
public void handle() {
|
public void handle() {
|
||||||
String msg = null;
|
String msg = null;
|
||||||
String domain = null;
|
String domain = null;
|
||||||
@@ -348,7 +363,7 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
|
|
||||||
this.thread.setName("SAMv3Handler " + _id);
|
this.thread.setName("SAMv3Handler " + _id);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("SAM handling started");
|
_log.debug("SAMv3 handling started");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InputStream in = getClientSocket().socket().getInputStream();
|
InputStream in = getClientSocket().socket().getInputStream();
|
||||||
@@ -422,8 +437,7 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Caught IOException ("
|
_log.debug("Caught IOException for message [" + msg + "]", e);
|
||||||
+ e.getMessage() + ") for message [" + msg + "]", e);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
_log.error("Unexpected exception for message [" + msg + "]", e);
|
_log.error("Unexpected exception for message [" + msg + "]", e);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -435,7 +449,8 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
try {
|
try {
|
||||||
closeClientSocket();
|
closeClientSocket();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
_log.error("Error closing socket: " + e.getMessage());
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Error closing socket", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (streamForwardingSocket)
|
if (streamForwardingSocket)
|
||||||
@@ -444,20 +459,40 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
try {
|
try {
|
||||||
((SAMv3StreamSession)streamSession).stopForwardingIncoming();
|
((SAMv3StreamSession)streamSession).stopForwardingIncoming();
|
||||||
} catch (SAMException e) {
|
} catch (SAMException e) {
|
||||||
_log.error("Error while stopping forwarding connections: " + e.getMessage());
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Error while stopping forwarding connections", e);
|
||||||
} catch (InterruptedIOException e) {
|
} catch (InterruptedIOException e) {
|
||||||
_log.error("Interrupted while stopping forwarding connections: " + e.getMessage());
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Interrupted while stopping forwarding connections", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void die() {
|
/**
|
||||||
|
* Stop the SAM handler, close the socket,
|
||||||
|
* unregister with the bridge.
|
||||||
|
*
|
||||||
|
* Overridden to not close the client socket if stolen.
|
||||||
|
*
|
||||||
|
* @since 0.9.20
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void stopHandling() {
|
||||||
|
synchronized (stopLock) {
|
||||||
|
stopHandler = true;
|
||||||
|
}
|
||||||
|
if (!stolenSocket) {
|
||||||
|
try {
|
||||||
|
closeClientSocket();
|
||||||
|
} catch (IOException e) {}
|
||||||
|
}
|
||||||
|
bridge.unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void die() {
|
||||||
SessionRecord rec = null ;
|
SessionRecord rec = null ;
|
||||||
|
|
||||||
if (session!=null) {
|
if (session!=null) {
|
||||||
@@ -813,20 +848,15 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void notifyStreamResult(boolean verbose, String result, String message) throws IOException
|
public void notifyStreamResult(boolean verbose, String result, String message) throws IOException {
|
||||||
{
|
|
||||||
if (!verbose) return ;
|
if (!verbose) return ;
|
||||||
|
String msgString = createMessageString(message);
|
||||||
|
String out = "STREAM STATUS RESULT=" + result + msgString + '\n';
|
||||||
|
|
||||||
String out = "STREAM STATUS RESULT="+result;
|
if (!writeString(out)) {
|
||||||
if (message!=null)
|
throw new IOException ( "Error notifying connection to SAM client" );
|
||||||
out = out + " MESSAGE=\"" + message + "\"";
|
}
|
||||||
out = out + '\n';
|
}
|
||||||
|
|
||||||
if ( !writeString ( out ) )
|
|
||||||
{
|
|
||||||
throw new IOException ( "Error notifying connection to SAM client" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void notifyStreamIncomingConnection(Destination d) throws IOException {
|
public void notifyStreamIncomingConnection(Destination d) throws IOException {
|
||||||
if (getStreamSession() == null) {
|
if (getStreamSession() == null) {
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect the SAM STREAM session to the specified Destination
|
* Connect the SAM STREAM session to the specified Destination
|
||||||
|
* for a single connection, using the socket stolen from the handler.
|
||||||
*
|
*
|
||||||
* @param handler The handler that communicates with the requesting client
|
* @param handler The handler that communicates with the requesting client
|
||||||
* @param dest Base64-encoded Destination to connect to
|
* @param dest Base64-encoded Destination to connect to
|
||||||
@@ -87,7 +88,7 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public void connect ( SAMv3Handler handler, String dest, Properties props )
|
public void connect ( SAMv3Handler handler, String dest, Properties props )
|
||||||
throws I2PException, ConnectException, NoRouteToHostException,
|
throws I2PException, ConnectException, NoRouteToHostException,
|
||||||
DataFormatException, InterruptedIOException, IOException {
|
DataFormatException, InterruptedIOException, IOException {
|
||||||
|
|
||||||
boolean verbose = (props.getProperty("SILENT", "false").equals("false"));
|
boolean verbose = (props.getProperty("SILENT", "false").equals("false"));
|
||||||
@@ -117,13 +118,17 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
|
|||||||
WritableByteChannel toClient = handler.getClientSocket();
|
WritableByteChannel toClient = handler.getClientSocket();
|
||||||
WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream());
|
WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream());
|
||||||
|
|
||||||
(new Thread(rec.getThreadGroup(), new I2PAppThread(new Pipe(fromClient,toI2P, "SAMPipeClientToI2P"), "SAMPipeClientToI2P"), "SAMPipeClientToI2P")).start();
|
SAMBridge bridge = handler.getBridge();
|
||||||
(new Thread(rec.getThreadGroup(), new I2PAppThread(new Pipe(fromI2P,toClient, "SAMPipeI2PToClient"), "SAMPipeI2PToClient"), "SAMPipeI2PToClient")).start();
|
(new Thread(rec.getThreadGroup(),
|
||||||
|
new Pipe(fromClient, toI2P, bridge),
|
||||||
|
"ConnectV3 SAMPipeClientToI2P")).start();
|
||||||
|
(new Thread(rec.getThreadGroup(),
|
||||||
|
new Pipe(fromI2P, toClient, bridge),
|
||||||
|
"ConnectV3 SAMPipeI2PToClient")).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accept an incoming STREAM
|
* Accept a single incoming STREAM on the socket stolen from the handler.
|
||||||
*
|
*
|
||||||
* @param handler The handler that communicates with the requesting client
|
* @param handler The handler that communicates with the requesting client
|
||||||
* @param verbose If true, SAM will send the Base64-encoded peer Destination of an
|
* @param verbose If true, SAM will send the Base64-encoded peer Destination of an
|
||||||
@@ -150,8 +155,7 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
|
|||||||
this.socketServer = this.socketMgr.getServerSocket();
|
this.socketServer = this.socketMgr.getServerSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
I2PSocket i2ps;
|
I2PSocket i2ps = this.socketServer.accept();
|
||||||
i2ps = this.socketServer.accept();
|
|
||||||
|
|
||||||
synchronized( this.socketServerLock )
|
synchronized( this.socketServerLock )
|
||||||
{
|
{
|
||||||
@@ -160,10 +164,10 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
|
|||||||
|
|
||||||
SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick);
|
SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick);
|
||||||
|
|
||||||
if ( rec==null || i2ps==null ) throw new InterruptedIOException() ;
|
if ( rec==null || i2ps==null ) throw new InterruptedIOException() ;
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
handler.notifyStreamIncomingConnection(i2ps.getPeerDestination()) ;
|
handler.notifyStreamIncomingConnection(i2ps.getPeerDestination()) ;
|
||||||
|
|
||||||
handler.stealSocket() ;
|
handler.stealSocket() ;
|
||||||
ReadableByteChannel fromClient = handler.getClientSocket();
|
ReadableByteChannel fromClient = handler.getClientSocket();
|
||||||
@@ -171,8 +175,13 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
|
|||||||
WritableByteChannel toClient = handler.getClientSocket();
|
WritableByteChannel toClient = handler.getClientSocket();
|
||||||
WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream());
|
WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream());
|
||||||
|
|
||||||
(new Thread(rec.getThreadGroup(), new I2PAppThread(new Pipe(fromClient,toI2P, "SAMPipeClientToI2P"), "SAMPipeClientToI2P"), "SAMPipeClientToI2P")).start();
|
SAMBridge bridge = handler.getBridge();
|
||||||
(new Thread(rec.getThreadGroup(), new I2PAppThread(new Pipe(fromI2P,toClient, "SAMPipeI2PToClient"), "SAMPipeI2PToClient"), "SAMPipeI2PToClient")).start();
|
(new Thread(rec.getThreadGroup(),
|
||||||
|
new Pipe(fromClient, toI2P, bridge),
|
||||||
|
"AcceptV3 SAMPipeClientToI2P")).start();
|
||||||
|
(new Thread(rec.getThreadGroup(),
|
||||||
|
new Pipe(fromI2P, toClient, bridge),
|
||||||
|
"AcceptV3 SAMPipeI2PToClient")).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -210,10 +219,10 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
|
|||||||
}
|
}
|
||||||
|
|
||||||
SocketForwarder forwarder = new SocketForwarder(host, port, this, verbose);
|
SocketForwarder forwarder = new SocketForwarder(host, port, this, verbose);
|
||||||
(new Thread(rec.getThreadGroup(), new I2PAppThread(forwarder, "SAMStreamForwarder"), "SAMStreamForwarder")).start();
|
(new Thread(rec.getThreadGroup(), forwarder, "SAMV3StreamForwarder")).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SocketForwarder extends Thread
|
private static class SocketForwarder implements Runnable
|
||||||
{
|
{
|
||||||
private final String host;
|
private final String host;
|
||||||
private final int port;
|
private final int port;
|
||||||
@@ -254,6 +263,7 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
|
|||||||
|
|
||||||
// build pipes between both sockets
|
// build pipes between both sockets
|
||||||
try {
|
try {
|
||||||
|
clientServerSock.socket().setKeepAlive(true);
|
||||||
if (this.verbose)
|
if (this.verbose)
|
||||||
SAMv3Handler.notifyStreamIncomingConnection(
|
SAMv3Handler.notifyStreamIncomingConnection(
|
||||||
clientServerSock, i2ps.getPeerDestination());
|
clientServerSock, i2ps.getPeerDestination());
|
||||||
@@ -261,8 +271,10 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
|
|||||||
ReadableByteChannel fromI2P = Channels.newChannel(i2ps.getInputStream());
|
ReadableByteChannel fromI2P = Channels.newChannel(i2ps.getInputStream());
|
||||||
WritableByteChannel toClient = clientServerSock ;
|
WritableByteChannel toClient = clientServerSock ;
|
||||||
WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream());
|
WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream());
|
||||||
(new I2PAppThread(new Pipe(fromClient,toI2P, "SAMPipeClientToI2P"), "SAMPipeClientToI2P")).start();
|
(new I2PAppThread(new Pipe(fromClient, toI2P, null),
|
||||||
(new I2PAppThread(new Pipe(fromI2P,toClient, "SAMPipeI2PToClient"), "SAMPipeI2PToClient")).start();
|
"ForwardV3 SAMPipeClientToI2P")).start();
|
||||||
|
(new I2PAppThread(new Pipe(fromI2P,toClient, null),
|
||||||
|
"ForwardV3 SAMPipeI2PToClient")).start();
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
try {
|
try {
|
||||||
@@ -277,48 +289,62 @@ class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Sessi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Pipe extends Thread
|
private static class Pipe implements Runnable, Handler
|
||||||
{
|
{
|
||||||
private final ReadableByteChannel in ;
|
private final ReadableByteChannel in ;
|
||||||
private final WritableByteChannel out ;
|
private final WritableByteChannel out ;
|
||||||
private final ByteBuffer buf ;
|
private final ByteBuffer buf ;
|
||||||
|
private final SAMBridge bridge;
|
||||||
|
|
||||||
public Pipe(ReadableByteChannel in, WritableByteChannel out, String name)
|
/**
|
||||||
|
* @param bridge may be null
|
||||||
|
*/
|
||||||
|
public Pipe(ReadableByteChannel in, WritableByteChannel out, SAMBridge bridge)
|
||||||
{
|
{
|
||||||
super(name);
|
|
||||||
this.in = in ;
|
this.in = in ;
|
||||||
this.out = out ;
|
this.out = out ;
|
||||||
this.buf = ByteBuffer.allocate(BUFFER_SIZE) ;
|
this.buf = ByteBuffer.allocate(BUFFER_SIZE) ;
|
||||||
|
this.bridge = bridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run()
|
public void run() {
|
||||||
{
|
if (bridge != null)
|
||||||
try {
|
bridge.register(this);
|
||||||
while (!Thread.interrupted() && (in.read(buf)>=0 || buf.position() != 0)) {
|
try {
|
||||||
buf.flip();
|
while (!Thread.interrupted() && (in.read(buf)>=0 || buf.position() != 0)) {
|
||||||
out.write(buf);
|
buf.flip();
|
||||||
buf.compact();
|
out.write(buf);
|
||||||
}
|
buf.compact();
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
this.interrupt();
|
|
||||||
}
|
}
|
||||||
try {
|
} catch (IOException ioe) {
|
||||||
in.close();
|
// ignore
|
||||||
}
|
} finally {
|
||||||
catch (IOException e) {}
|
try {
|
||||||
try {
|
in.close();
|
||||||
buf.flip();
|
} catch (IOException e) {}
|
||||||
while (buf.hasRemaining())
|
try {
|
||||||
out.write(buf);
|
buf.flip();
|
||||||
}
|
while (buf.hasRemaining()) {
|
||||||
catch (IOException e) {}
|
out.write(buf);
|
||||||
try {
|
}
|
||||||
out.close();
|
} catch (IOException e) {}
|
||||||
}
|
try {
|
||||||
catch (IOException e) {}
|
out.close();
|
||||||
}
|
} catch (IOException e) {}
|
||||||
|
if (bridge != null)
|
||||||
|
bridge.unregister(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler interface
|
||||||
|
* @since 0.9.20
|
||||||
|
*/
|
||||||
|
public void stopHandling() {
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public I2PServerSocket getSocketServer()
|
public I2PServerSocket getSocketServer()
|
||||||
|
|||||||
@@ -493,6 +493,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
try {
|
try {
|
||||||
I2PSSLSocketFactory fact = new I2PSSLSocketFactory(_context, false, "certificates/i2cp");
|
I2PSSLSocketFactory fact = new I2PSSLSocketFactory(_context, false, "certificates/i2cp");
|
||||||
_socket = fact.createSocket(_hostname, _portNum);
|
_socket = fact.createSocket(_hostname, _portNum);
|
||||||
|
_socket.setKeepAlive(true);
|
||||||
} catch (GeneralSecurityException gse) {
|
} catch (GeneralSecurityException gse) {
|
||||||
IOException ioe = new IOException("SSL Fail");
|
IOException ioe = new IOException("SSL Fail");
|
||||||
ioe.initCause(gse);
|
ioe.initCause(gse);
|
||||||
@@ -500,6 +501,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_socket = new Socket(_hostname, _portNum);
|
_socket = new Socket(_hostname, _portNum);
|
||||||
|
_socket.setKeepAlive(true);
|
||||||
}
|
}
|
||||||
// _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
|
// _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
|
||||||
OutputStream out = _socket.getOutputStream();
|
OutputStream out = _socket.getOutputStream();
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ class I2PSimpleSession extends I2PSessionImpl2 {
|
|||||||
} else {
|
} else {
|
||||||
_socket = new Socket(_hostname, _portNum);
|
_socket = new Socket(_hostname, _portNum);
|
||||||
}
|
}
|
||||||
|
_socket.setKeepAlive(true);
|
||||||
OutputStream out = _socket.getOutputStream();
|
OutputStream out = _socket.getOutputStream();
|
||||||
out.write(I2PClient.PROTOCOL_BYTE);
|
out.write(I2PClient.PROTOCOL_BYTE);
|
||||||
out.flush();
|
out.flush();
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ class ClientListenerRunner implements Runnable {
|
|||||||
if (validate(socket)) {
|
if (validate(socket)) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Connection received");
|
_log.debug("Connection received");
|
||||||
|
socket.setKeepAlive(true);
|
||||||
runConnection(socket);
|
runConnection(socket);
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
|||||||
Reference in New Issue
Block a user