(8);
loadKeys();
try {
if ( (listenHost != null) && !("0.0.0.0".equals(listenHost)) ) {
- serverSocket = new ServerSocket(listenPort, 0, InetAddress.getByName(listenHost));
+ serverSocket = ServerSocketChannel.open();
+ serverSocket.socket().bind(new InetSocketAddress(listenHost, listenPort));
if (_log.shouldLog(Log.DEBUG))
_log.debug("SAM bridge listening on "
+ listenHost + ":" + listenPort);
} else {
- serverSocket = new ServerSocket(listenPort);
+ serverSocket = ServerSocketChannel.open();
+ serverSocket.socket().bind(new InetSocketAddress(listenPort));
if (_log.shouldLog(Log.DEBUG))
_log.debug("SAM bridge listening on 0.0.0.0:" + listenPort);
}
@@ -191,36 +205,50 @@ public class SAMBridge implements Runnable {
}
}
+ static class HelpRequested extends Exception {static final long serialVersionUID=0x1;}
+
/**
* Usage:
- * SAMBridge [[listenHost ]listenPort[ name=val]*]
- *
+ * SAMBridge [ keyfile [listenHost ] listenPort [ name=val ]* ]
+ * or:
+ * SAMBridge [ name=val ]*
+ *
* name=val options are passed to the I2CP code to build a session,
* allowing the bridge to specify an alternate I2CP host and port, tunnel
* depth, etc.
- * @param args [[listenHost ]listenPort[ name=val]*]
+ * @param args [ keyfile [ listenHost ] listenPort [ name=val ]* ]
*/
public static void main(String args[]) {
String keyfile = DEFAULT_SAM_KEYFILE;
int port = SAM_LISTENPORT;
- String host = "0.0.0.0";
+ String host = DEFAULT_TCP_HOST;
Properties opts = null;
if (args.length > 0) {
- keyfile = args[0];
- int portIndex = 1;
- try {
- port = Integer.parseInt(args[portIndex]);
- } catch (NumberFormatException nfe) {
- host = args[1];
- portIndex++;
- try {
- port = Integer.parseInt(args[portIndex]);
- } catch (NumberFormatException nfe1) {
- usage();
- return;
- }
- }
- opts = parseOptions(args, portIndex+1);
+ try {
+ opts = parseOptions(args, 0);
+ keyfile = args[0];
+ int portIndex = 1;
+ try {
+ if (args.length>portIndex) port = Integer.parseInt(args[portIndex]);
+ } catch (NumberFormatException nfe) {
+ host = args[portIndex];
+ portIndex++;
+ try {
+ if (args.length>portIndex) port = Integer.parseInt(args[portIndex]);
+ } catch (NumberFormatException nfe1) {
+ try {
+ port = Integer.parseInt(opts.getProperty(SAMBridge.PROP_TCP_PORT, SAMBridge.DEFAULT_TCP_PORT));
+ host = opts.getProperty(SAMBridge.PROP_TCP_HOST, SAMBridge.DEFAULT_TCP_HOST);
+ } catch (NumberFormatException e) {
+ usage();
+ return;
+ }
+ }
+ }
+ } catch (HelpRequested e) {
+ usage();
+ return;
+ }
}
SAMBridge bridge = new SAMBridge(host, port, opts, keyfile);
I2PAppThread t = new I2PAppThread(bridge, "SAMListener");
@@ -236,10 +264,11 @@ public class SAMBridge implements Runnable {
t.start();
}
- private static Properties parseOptions(String args[], int startArgs) {
+ private static Properties parseOptions(String args[], int startArgs) throws HelpRequested {
Properties props = new Properties();
// skip over first few options
for (int i = startArgs; i < args.length; i++) {
+ if (args[i].equals("-h")) throw new HelpRequested();
int eq = args[i].indexOf('=');
if (eq <= 0) continue;
if (eq >= args[i].length()-1) continue;
@@ -255,50 +284,76 @@ public class SAMBridge implements Runnable {
private static void usage() {
System.err.println("Usage: SAMBridge [keyfile [listenHost] listenPortNum[ name=val]*]");
+ System.err.println("or:");
+ System.err.println(" SAMBridge [ name=val ]*");
System.err.println(" keyfile: location to persist private keys (default sam.keys)");
System.err.println(" listenHost: interface to listen on (0.0.0.0 for all interfaces)");
System.err.println(" listenPort: port to listen for SAM connections on (default 7656)");
System.err.println(" name=val: options to pass when connecting via I2CP, such as ");
System.err.println(" i2cp.host=localhost and i2cp.port=7654");
+ System.err.println("");
+ System.err.println("Host and ports of the SAM bridge can be specified with the alternate");
+ System.err.println("form by specifying options "+SAMBridge.PROP_TCP_HOST+" and/or "+
+ SAMBridge.PROP_TCP_PORT);
+ System.err.println("");
+ System.err.println("Options "+SAMBridge.PROP_DATAGRAM_HOST+" and "+SAMBridge.PROP_DATAGRAM_PORT+
+ " specify the listening ip");
+ System.err.println("range and the port of SAM datagram server. This server is");
+ System.err.println("only launched after a client creates the first SAM datagram");
+ System.err.println("or raw session, after a handshake with SAM version >= 3.0.");
+ System.err.println("");
+ System.err.println("The option loglevel=[DEBUG|WARN|ERROR|CRIT] can be used");
+ System.err.println("for tuning the log verbosity.\n");
}
public void run() {
if (serverSocket == null) return;
try {
while (acceptConnections) {
- Socket s = serverSocket.accept();
+ SocketChannel s = serverSocket.accept();
if (_log.shouldLog(Log.DEBUG))
_log.debug("New connection from "
- + s.getInetAddress().toString() + ":"
- + s.getPort());
+ + s.socket().getInetAddress().toString() + ":"
+ + s.socket().getPort());
- try {
- SAMHandler handler = SAMHandlerFactory.createSAMHandler(s, i2cpProps);
- if (handler == null) {
- if (_log.shouldLog(Log.DEBUG))
- _log.debug("SAM handler has not been instantiated");
+ class HelloHandler implements Runnable {
+ SocketChannel s ;
+ SAMBridge parent ;
+ HelloHandler(SocketChannel s, SAMBridge parent) {
+ this.s = s ;
+ this.parent = parent ;
+ }
+ public void run() {
try {
- s.close();
- } catch (IOException e) {}
- continue;
- }
- handler.setBridge(this);
- handler.startHandling();
- } catch (SAMException e) {
- if (_log.shouldLog(Log.ERROR))
- _log.error("SAM error: " + e.getMessage(), e);
- try {
- String reply = "HELLO REPLY RESULT=I2P_ERROR MESSAGE=\"" + e.getMessage() + "\"\n";
- s.getOutputStream().write(reply.getBytes("ISO-8859-1"));
- } catch (IOException ioe) {
- if (_log.shouldLog(Log.ERROR))
- _log.error("SAM Error sending error reply", ioe);
- }
- try { s.close(); } catch (IOException ioe) {}
- } catch (Exception ee) {
- try { s.close(); } catch (IOException ioe) {}
- _log.log(Log.CRIT, "Unexpected error handling SAM connection", ee);
- }
+ SAMHandler handler = SAMHandlerFactory.createSAMHandler(s, i2cpProps);
+ if (handler == null) {
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("SAM handler has not been instantiated");
+ try {
+ s.close();
+ } catch (IOException e) {}
+ return;
+ }
+ handler.setBridge(parent);
+ handler.startHandling();
+ } catch (SAMException e) {
+ if (_log.shouldLog(Log.ERROR))
+ _log.error("SAM error: " + e.getMessage(), e);
+ try {
+ String reply = "HELLO REPLY RESULT=I2P_ERROR MESSAGE=\"" + e.getMessage() + "\"\n";
+ s.write(ByteBuffer.wrap(reply.getBytes("ISO-8859-1")));
+ } catch (IOException ioe) {
+ if (_log.shouldLog(Log.ERROR))
+ _log.error("SAM Error sending error reply", ioe);
+ }
+ try { s.close(); } catch (IOException ioe) {}
+ } catch (Exception ee) {
+ try { s.close(); } catch (IOException ioe) {}
+ _log.log(Log.CRIT, "Unexpected error handling SAM connection", ee);
+ }
+ }
+ }
+ new I2PAppThread(new HelloHandler(s,this), "HelloHandler").start();
}
} catch (Exception e) {
if (_log.shouldLog(Log.ERROR))
diff --git a/apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java b/apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java
index a3e20f7df..c8d31b489 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMDatagramSession.java
@@ -30,7 +30,7 @@ public class SAMDatagramSession extends SAMMessageSession {
private final static Log _log = new Log(SAMDatagramSession.class);
public static int DGRAM_SIZE_MAX = 31*1024;
- private SAMDatagramReceiver recv = null;
+ protected SAMDatagramReceiver recv = null;
private I2PDatagramMaker dgramMaker;
private I2PDatagramDissector dgramDissector = new I2PDatagramDissector();
@@ -84,9 +84,10 @@ public class SAMDatagramSession extends SAMMessageSession {
public boolean sendBytes(String dest, byte[] data) throws DataFormatException {
if (data.length > DGRAM_SIZE_MAX)
throw new DataFormatException("Datagram size exceeded (" + data.length + ")");
-
- byte[] dgram = dgramMaker.makeI2PDatagram(data);
-
+ byte[] dgram ;
+ synchronized (dgramMaker) {
+ dgram = dgramMaker.makeI2PDatagram(data);
+ }
return sendBytesThroughMessageSession(dest, dgram);
}
diff --git a/apps/sam/java/src/net/i2p/sam/SAMException.java b/apps/sam/java/src/net/i2p/sam/SAMException.java
index e51e35ea4..ae965a4c8 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMException.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMException.java
@@ -15,11 +15,13 @@ package net.i2p.sam;
*/
public class SAMException extends Exception {
+ static final long serialVersionUID = 1 ;
+
public SAMException() {
- super();
+ super();
}
public SAMException(String s) {
- super(s);
+ super(s);
}
}
diff --git a/apps/sam/java/src/net/i2p/sam/SAMHandler.java b/apps/sam/java/src/net/i2p/sam/SAMHandler.java
index 64d824a57..d53a5a662 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMHandler.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMHandler.java
@@ -9,9 +9,8 @@ package net.i2p.sam;
*/
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
+import java.nio.channels.SocketChannel;
+import java.nio.ByteBuffer;
import java.util.Properties;
import net.i2p.util.I2PAppThread;
@@ -32,8 +31,7 @@ public abstract class SAMHandler implements Runnable {
protected SAMBridge bridge = null;
private Object socketWLock = new Object(); // Guards writings on socket
- private Socket socket = null;
- private OutputStream socketOS = null; // Stream associated to socket
+ protected SocketChannel socket = null;
protected int verMajor = 0;
protected int verMinor = 0;
@@ -53,10 +51,9 @@ public abstract class SAMHandler implements Runnable {
* @param i2cpProps properties to configure the I2CP connection (host, port, etc)
* @throws IOException
*/
- protected SAMHandler(Socket s,
+ protected SAMHandler(SocketChannel s,
int verMajor, int verMinor, Properties i2cpProps) throws IOException {
socket = s;
- socketOS = socket.getOutputStream();
this.verMajor = verMajor;
this.verMinor = verMinor;
@@ -86,8 +83,8 @@ public abstract class SAMHandler implements Runnable {
* @return input stream
* @throws IOException
*/
- protected final InputStream getClientSocketInputStream() throws IOException {
- return socket.getInputStream();
+ protected final SocketChannel getClientSocket() {
+ return socket ;
}
/**
@@ -98,13 +95,17 @@ public abstract class SAMHandler implements Runnable {
* @param data A byte array to be written
* @throws IOException
*/
- protected final void writeBytes(byte[] data) throws IOException {
+ protected final void writeBytes(ByteBuffer data) throws IOException {
synchronized (socketWLock) {
- socketOS.write(data);
- socketOS.flush();
+ writeBytes(data, socket);
}
}
+ static public void writeBytes(ByteBuffer data, SocketChannel out) throws IOException {
+ while (data.hasRemaining()) out.write(data);
+ out.socket().getOutputStream().flush();
+ }
+
/**
* If you're crazy enough to write to the raw socket, grab the write lock
* with getWriteLock(), synchronize against it, and write to the getOut()
@@ -112,7 +113,6 @@ public abstract class SAMHandler implements Runnable {
* @return socket Write lock object
*/
protected Object getWriteLock() { return socketWLock; }
- protected OutputStream getOut() { return socketOS; }
/**
* Write a string to the handler's socket. This method must
@@ -121,21 +121,25 @@ public abstract class SAMHandler implements Runnable {
*
* @param str A byte array to be written
*
- * @return True is the string was successfully written, false otherwise
+ * @return True if the string was successfully written, false otherwise
*/
protected final boolean writeString(String str) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending the client: [" + str + "]");
- try {
- writeBytes(str.getBytes("ISO-8859-1"));
+ return writeString(str, socket);
+ }
+
+ public static boolean writeString(String str, SocketChannel out)
+ {
+ try {
+ writeBytes(ByteBuffer.wrap(str.getBytes("ISO-8859-1")), out);
} catch (IOException e) {
_log.debug("Caught IOException", e);
return false;
}
-
- return true;
+ return true ;
}
-
+
/**
* Close the socket connected to the SAM client.
*
@@ -178,8 +182,8 @@ public abstract class SAMHandler implements Runnable {
return ("SAM handler (class: " + this.getClass().getName()
+ "; SAM version: " + verMajor + "." + verMinor
+ "; client: "
- + this.socket.getInetAddress().toString() + ":"
- + this.socket.getPort() + ")");
+ + this.socket.socket().getInetAddress().toString() + ":"
+ + this.socket.socket().getPort() + ")");
}
public final void run() {
diff --git a/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java b/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java
index 21a0e97d2..e9a51214b 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java
@@ -9,9 +9,9 @@ package net.i2p.sam;
*/
import java.io.IOException;
-import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
-import java.net.Socket;
+import java.nio.channels.SocketChannel;
+import java.nio.ByteBuffer;
import java.util.Properties;
import java.util.StringTokenizer;
@@ -34,17 +34,17 @@ public class SAMHandlerFactory {
* @throws SAMException if the connection handshake (HELLO message) was malformed
* @return A SAM protocol handler, or null if the client closed before the handshake
*/
- public static SAMHandler createSAMHandler(Socket s, Properties i2cpProps) throws SAMException {
+ public static SAMHandler createSAMHandler(SocketChannel s, Properties i2cpProps) throws SAMException {
String line;
StringTokenizer tok;
try {
- line = DataHelper.readLine(s.getInputStream());
+ line = DataHelper.readLine(s.socket().getInputStream());
if (line == null) {
_log.debug("Connection closed by client");
return null;
}
- tok = new StringTokenizer(line, " ");
+ tok = new StringTokenizer(line.trim(), " ");
} catch (IOException e) {
throw new SAMException("Error reading from socket: "
+ e.getMessage());
@@ -84,14 +84,15 @@ public class SAMHandlerFactory {
}
String ver = chooseBestVersion(minVer, maxVer);
- if (ver == null)
- throw new SAMException("No version specified");
- // Let's answer positively
try {
- OutputStream out = s.getOutputStream();
- out.write(("HELLO REPLY RESULT=OK VERSION="
- + ver + "\n").getBytes("ISO-8859-1"));
+ if (ver == null) {
+ s.write(ByteBuffer.wrap(("HELLO REPLY RESULT=NOVERSION\n").getBytes("ISO-8859-1")));
+ return null ;
+ }
+ // Let's answer positively
+ s.write(ByteBuffer.wrap(("HELLO REPLY RESULT=OK VERSION="
+ + ver + "\n").getBytes("ISO-8859-1")));
} catch (UnsupportedEncodingException e) {
_log.error("Caught UnsupportedEncodingException ("
+ e.getMessage() + ")");
@@ -115,6 +116,9 @@ public class SAMHandlerFactory {
case 2:
handler = new SAMv2Handler(s, verMajor, verMinor, i2cpProps);
break;
+ case 3:
+ handler = new SAMv3Handler(s, verMajor, verMinor, i2cpProps);
+ break;
default:
_log.error("BUG! Trying to initialize the wrong SAM version!");
throw new SAMException("BUG! (in handler instantiation)");
@@ -128,6 +132,7 @@ public class SAMHandlerFactory {
/* Return the best version we can use, or null on failure */
private static String chooseBestVersion(String minVer, String maxVer) {
+
int minMajor = getMajor(minVer), minMinor = getMinor(minVer);
int maxMajor = getMajor(maxVer), maxMinor = getMinor(maxVer);
@@ -143,6 +148,8 @@ public class SAMHandlerFactory {
float fmaxVer = (float) maxMajor + (float) maxMinor / 10 ;
+ if ( ( fminVer <= 3.0 ) && ( fmaxVer >= 3.0 ) ) return "3.0" ;
+
if ( ( fminVer <= 2.0 ) && ( fmaxVer >= 2.0 ) ) return "2.0" ;
if ( ( fminVer <= 1.0 ) && ( fmaxVer >= 1.0 ) ) return "1.0" ;
diff --git a/apps/sam/java/src/net/i2p/sam/SAMInvalidDirectionException.java b/apps/sam/java/src/net/i2p/sam/SAMInvalidDirectionException.java
index b52ecda65..cd1c6b1a5 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMInvalidDirectionException.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMInvalidDirectionException.java
@@ -15,7 +15,8 @@ package net.i2p.sam;
* @author human
*/
public class SAMInvalidDirectionException extends Exception {
-
+ static final long serialVersionUID = 1 ;
+
public SAMInvalidDirectionException() {
super();
}
diff --git a/apps/sam/java/src/net/i2p/sam/SAMMessageSession.java b/apps/sam/java/src/net/i2p/sam/SAMMessageSession.java
index b29b2f84c..2c8ed2756 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMMessageSession.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMMessageSession.java
@@ -109,8 +109,7 @@ public abstract class SAMMessageSession {
* @throws DataFormatException
*/
protected boolean sendBytesThroughMessageSession(String dest, byte[] data) throws DataFormatException {
- Destination d = new Destination();
- d.fromBase64(dest);
+ Destination d = SAMUtils.getDest(dest);
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Sending " + data.length + " bytes to " + dest);
diff --git a/apps/sam/java/src/net/i2p/sam/SAMRawSession.java b/apps/sam/java/src/net/i2p/sam/SAMRawSession.java
index 7f56066b1..92bf4960d 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMRawSession.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMRawSession.java
@@ -26,7 +26,7 @@ public class SAMRawSession extends SAMMessageSession {
private final static Log _log = new Log(SAMRawSession.class);
public static final int RAW_SIZE_MAX = 32*1024;
- private SAMRawReceiver recv = null;
+ protected SAMRawReceiver recv = null;
/**
* Create a new SAM RAW session.
*
diff --git a/apps/sam/java/src/net/i2p/sam/SAMStreamReceiver.java b/apps/sam/java/src/net/i2p/sam/SAMStreamReceiver.java
index 6d6d824b5..a57ddd681 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMStreamReceiver.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMStreamReceiver.java
@@ -9,6 +9,7 @@ package net.i2p.sam;
*/
import java.io.IOException;
+import java.nio.ByteBuffer;
import net.i2p.data.Destination;
@@ -53,14 +54,13 @@ public interface SAMStreamReceiver {
public void notifyStreamOutgoingConnection(int id, String result, String msg) throws IOException;
/**
- * Send a byte array to a SAM client.
+ * Transmit a byte array from I2P to a SAM client.
*
* @param id Connection id
* @param data Byte array to be received
- * @param len Number of bytes in data
* @throws IOException
*/
- public void receiveStreamBytes(int id, byte data[], int len) throws IOException;
+ public void receiveStreamBytes(int id, ByteBuffer data) throws IOException;
/**
* Notify that a connection has been closed
diff --git a/apps/sam/java/src/net/i2p/sam/SAMStreamSession.java b/apps/sam/java/src/net/i2p/sam/SAMStreamSession.java
index 280562e48..aef2802bd 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMStreamSession.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMStreamSession.java
@@ -13,6 +13,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.ArrayList;
@@ -51,15 +53,15 @@ public class SAMStreamSession {
protected SAMStreamReceiver recv = null;
- private SAMStreamSessionServer server = null;
+ protected SAMStreamSessionServer server = null;
protected I2PSocketManager socketMgr = null;
private Object handlersMapLock = new Object();
/** stream id (Long) to SAMStreamSessionSocketReader */
- private HashMap handlersMap = new HashMap();
+ private HashMap handlersMap = new HashMap();
/** stream id (Long) to StreamSender */
- private HashMap sendersMap = new HashMap();
+ private HashMap sendersMap = new HashMap();
private Object idLock = new Object();
private int lastNegativeId = 0;
@@ -76,6 +78,10 @@ public class SAMStreamSession {
public static String PROP_FORCE_FLUSH = "sam.forceFlush";
public static String DEFAULT_FORCE_FLUSH = "false";
+ public SAMStreamSession() {
+
+ }
+
/**
* Create a new SAM STREAM session.
*
@@ -166,7 +172,7 @@ public class SAMStreamSession {
}
}
- private class DisconnectListener implements I2PSocketManager.DisconnectListener {
+ protected class DisconnectListener implements I2PSocketManager.DisconnectListener {
public void sessionDisconnected() {
close();
}
@@ -572,19 +578,20 @@ public class SAMStreamSession {
_log.debug("run() called for socket reader " + id);
int read = -1;
- byte[] data = new byte[SOCKET_HANDLER_BUF_SIZE];
+ ByteBuffer data = ByteBuffer.allocateDirect(SOCKET_HANDLER_BUF_SIZE);
try {
InputStream in = i2pSocket.getInputStream();
while (stillRunning) {
- read = in.read(data);
+ data.clear();
+ read = Channels.newChannel(in).read(data);
if (read == -1) {
_log.debug("Handler " + id + ": connection closed");
break;
}
-
- recv.receiveStreamBytes(id, data, read);
+ data.flip();
+ recv.receiveStreamBytes(id, data);
}
} catch (IOException e) {
_log.debug("Caught IOException", e);
@@ -650,7 +657,7 @@ public class SAMStreamSession {
protected class v1StreamSender extends StreamSender
{
- private List _data;
+ private List _data;
private int _id;
private ByteCache _cache;
private OutputStream _out = null;
@@ -660,7 +667,7 @@ public class SAMStreamSession {
public v1StreamSender ( I2PSocket s, int id ) throws IOException {
super ( s, id );
- _data = new ArrayList(1);
+ _data = new ArrayList(1);
_id = id;
_cache = ByteCache.getInstance(4, 32*1024);
_out = s.getOutputStream();
diff --git a/apps/sam/java/src/net/i2p/sam/SAMUtils.java b/apps/sam/java/src/net/i2p/sam/SAMUtils.java
index 8bb3fac30..61578e333 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMUtils.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMUtils.java
@@ -8,6 +8,7 @@ package net.i2p.sam;
*
*/
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
@@ -19,8 +20,11 @@ import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.naming.NamingService;
+import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
+import net.i2p.data.PrivateKey;
+import net.i2p.data.SigningPrivateKey;
import net.i2p.util.Log;
/**
@@ -73,6 +77,22 @@ public class SAMUtils {
return false;
}
}
+
+ public static class InvalidDestination extends Exception {
+ static final long serialVersionUID = 0x1 ;
+ }
+ public static void checkPrivateDestination(String dest) throws InvalidDestination {
+ ByteArrayInputStream destKeyStream = new ByteArrayInputStream(Base64.decode(dest));
+
+ try {
+ new Destination().readBytes(destKeyStream);
+ new PrivateKey().readBytes(destKeyStream);
+ new SigningPrivateKey().readBytes(destKeyStream);
+ } catch (Exception e) {
+ throw new InvalidDestination();
+ }
+ }
+
/**
* Resolved the specified hostname.
@@ -101,6 +121,27 @@ public class SAMUtils {
return dest;
}
+ /**
+ * Resolve the destination from a key or a hostname
+ *
+ * @param s Hostname or key to be resolved
+ *
+ * @return the Destination for the specified hostname, or null if not found
+ */
+ public static Destination getDest(String s) throws DataFormatException
+ {
+ Destination d = new Destination() ;
+ try {
+ d.fromBase64(s);
+ } catch (DataFormatException e) {
+ d = lookupHost(s, null);
+ if ( d==null ) {
+ throw e ;
+ }
+ }
+ return d ;
+ }
+
/**
* Parse SAM parameters, and put them into a Propetries object
*
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java
index 93a9a8d66..4e6bd5dc7 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java
@@ -12,12 +12,11 @@ import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
-import java.io.InputStream;
import java.io.InterruptedIOException;
-import java.io.OutputStream;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
-import java.net.Socket;
+import java.nio.channels.SocketChannel;
+import java.nio.ByteBuffer;
import java.util.Properties;
import java.util.StringTokenizer;
@@ -40,14 +39,15 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
private final static Log _log = new Log(SAMv1Handler.class);
- private final static int IN_BUFSIZE = 2048;
+ protected SAMRawSession rawSession = null;
+ protected SAMDatagramSession datagramSession = null;
+ protected SAMStreamSession streamSession = null;
+ protected SAMRawSession getRawSession() {return rawSession ;}
+ protected SAMDatagramSession getDatagramSession() {return datagramSession ;}
+ protected SAMStreamSession getStreamSession() {return streamSession ;}
- private SAMRawSession rawSession = null;
- private SAMDatagramSession datagramSession = null;
- protected SAMStreamSession streamSession = null;
-
- private long _id;
- private static volatile long __id = 0;
+ protected long _id;
+ protected static volatile long __id = 0;
/**
* Create a new SAM version 1 handler. This constructor expects
@@ -60,7 +60,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
* @throws SAMException
* @throws IOException
*/
- public SAMv1Handler(Socket s, int verMajor, int verMinor) throws SAMException, IOException {
+ public SAMv1Handler(SocketChannel s, int verMajor, int verMinor) throws SAMException, IOException {
this(s, verMajor, verMinor, new Properties());
}
/**
@@ -75,7 +75,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
* @throws SAMException
* @throws IOException
*/
- public SAMv1Handler(Socket s, int verMajor, int verMinor, Properties i2cpProps) throws SAMException, IOException {
+ public SAMv1Handler(SocketChannel s, int verMajor, int verMinor, Properties i2cpProps) throws SAMException, IOException {
super(s, verMajor, verMinor, i2cpProps);
_id = ++__id;
_log.debug("SAM version 1 handler instantiated");
@@ -101,16 +101,13 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
_log.debug("SAM handling started");
try {
- InputStream in = getClientSocketInputStream();
- int b = -1;
-
while (true) {
if (shouldStop()) {
_log.debug("Stop request found");
break;
}
- msg = DataHelper.readLine(in);
+ msg = DataHelper.readLine(getClientSocket().socket().getInputStream()).trim();
if (msg == null) {
_log.debug("Connection closed by client");
break;
@@ -175,27 +172,27 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
} catch (IOException e) {
_log.error("Error closing socket: " + e.getMessage());
}
- if (rawSession != null) {
- rawSession.close();
+ if (getRawSession() != null) {
+ getRawSession().close();
}
- if (datagramSession != null) {
- datagramSession.close();
+ if (getDatagramSession() != null) {
+ getDatagramSession().close();
}
- if (streamSession != null) {
- streamSession.close();
+ if (getStreamSession() != null) {
+ getStreamSession().close();
}
}
}
/* Parse and execute a SESSION message */
- private boolean execSessionMessage(String opcode, Properties props) {
+ protected boolean execSessionMessage(String opcode, Properties props) {
String dest = "BUG!";
try{
if (opcode.equals("CREATE")) {
- if ((rawSession != null) || (datagramSession != null)
- || (streamSession != null)) {
+ if ((getRawSession() != null) || (getDatagramSession() != null)
+ || (getStreamSession() != null)) {
_log.debug("Trying to create a session, but one still exists");
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Session already exists\"\n");
}
@@ -293,7 +290,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
}
/* Parse and execute a DEST message*/
- private boolean execDestMessage(String opcode, Properties props) {
+ protected boolean execDestMessage(String opcode, Properties props) {
if (opcode.equals("GENERATE")) {
if (props.size() > 0) {
@@ -318,7 +315,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
}
/* Parse and execute a NAMING message */
- private boolean execNamingMessage(String opcode, Properties props) {
+ protected boolean execNamingMessage(String opcode, Properties props) {
if (opcode.equals("LOOKUP")) {
if (props == null) {
_log.debug("No parameters specified in NAMING LOOKUP message");
@@ -331,20 +328,23 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
return false;
}
- Destination dest;
+ Destination dest = null ;
if (name.equals("ME")) {
- if (rawSession != null) {
- dest = rawSession.getDestination();
- } else if (streamSession != null) {
- dest = streamSession.getDestination();
- } else if (datagramSession != null) {
- dest = datagramSession.getDestination();
+ if (getRawSession() != null) {
+ dest = getRawSession().getDestination();
+ } else if (getStreamSession() != null) {
+ dest = getStreamSession().getDestination();
+ } else if (getDatagramSession() != null) {
+ dest = getDatagramSession().getDestination();
} else {
_log.debug("Lookup for SESSION destination, but session is null");
return false;
}
} else {
- dest = SAMUtils.lookupHost(name, null);
+ try {
+ dest = SAMUtils.getDest(name);
+ } catch (DataFormatException e) {
+ }
}
if (dest == null) {
@@ -364,8 +364,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
/* Parse and execute a DATAGRAM message */
- private boolean execDatagramMessage(String opcode, Properties props) {
- if (datagramSession == null) {
+ protected boolean execDatagramMessage(String opcode, Properties props) {
+ if (getDatagramSession() == null) {
_log.error("DATAGRAM message received, but no DATAGRAM session exists");
return false;
}
@@ -403,12 +403,12 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
}
try {
- DataInputStream in = new DataInputStream(getClientSocketInputStream());
+ DataInputStream in = new DataInputStream(getClientSocket().socket().getInputStream());
byte[] data = new byte[size];
in.readFully(data);
- if (!datagramSession.sendBytes(dest, data)) {
+ if (!getDatagramSession().sendBytes(dest, data)) {
_log.error("DATAGRAM SEND failed");
return true;
}
@@ -435,8 +435,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
}
/* Parse and execute a RAW message */
- private boolean execRawMessage(String opcode, Properties props) {
- if (rawSession == null) {
+ protected boolean execRawMessage(String opcode, Properties props) {
+ if (getRawSession() == null) {
_log.error("RAW message received, but no RAW session exists");
return false;
}
@@ -474,12 +474,12 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
}
try {
- DataInputStream in = new DataInputStream(getClientSocketInputStream());
+ DataInputStream in = new DataInputStream(getClientSocket().socket().getInputStream());
byte[] data = new byte[size];
in.readFully(data);
- if (!rawSession.sendBytes(dest, data)) {
+ if (!getRawSession().sendBytes(dest, data)) {
_log.error("RAW SEND failed");
return true;
}
@@ -507,7 +507,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
/* Parse and execute a STREAM message */
protected boolean execStreamMessage(String opcode, Properties props) {
- if (streamSession == null) {
+ if (getStreamSession() == null) {
_log.error("STREAM message received, but no STREAM session exists");
return false;
}
@@ -567,11 +567,11 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
}
try {
- if (!streamSession.sendBytes(id, getClientSocketInputStream(), size)) { // data)) {
+ if (!getStreamSession().sendBytes(id, getClientSocket().socket().getInputStream(), size)) { // data)) {
if (_log.shouldLog(Log.WARN))
_log.warn("STREAM SEND [" + size + "] failed");
boolean rv = writeString("STREAM CLOSED RESULT=CANT_REACH_PEER ID=" + id + " MESSAGE=\"Send of " + size + " bytes failed\"\n");
- streamSession.closeConnection(id);
+ getStreamSession().closeConnection(id);
return rv;
}
@@ -622,7 +622,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
try {
try {
- if (!streamSession.connect(id, dest, props)) {
+ if (!getStreamSession().connect(id, dest, props)) {
_log.debug("STREAM connection failed");
return false;
}
@@ -673,7 +673,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
}
}
- boolean closed = streamSession.closeConnection(id);
+ boolean closed = getStreamSession().closeConnection(id);
if ( (!closed) && (_log.shouldLog(Log.WARN)) )
_log.warn("Stream unable to be closed, but this is non fatal");
return true;
@@ -691,7 +691,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
// SAMRawReceiver implementation
public void receiveRawBytes(byte data[]) throws IOException {
- if (rawSession == null) {
+ if (getRawSession() == null) {
_log.error("BUG! Received raw bytes, but session is null!");
throw new NullPointerException("BUG! RAW session is null!");
}
@@ -701,17 +701,18 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
String msgText = "RAW RECEIVED SIZE=" + data.length + "\n";
msg.write(msgText.getBytes("ISO-8859-1"));
msg.write(data);
+ msg.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("sending to client: " + msgText);
- writeBytes(msg.toByteArray());
+ writeBytes(ByteBuffer.wrap(msg.toByteArray()));
}
public void stopRawReceiving() {
_log.debug("stopRawReceiving() invoked");
- if (rawSession == null) {
+ if (getRawSession() == null) {
_log.error("BUG! Got raw receiving stop, but session is null!");
throw new NullPointerException("BUG! RAW session is null!");
}
@@ -726,7 +727,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
// SAMDatagramReceiver implementation
public void receiveDatagramBytes(Destination sender, byte data[]) throws IOException {
- if (datagramSession == null) {
+ if (getDatagramSession() == null) {
_log.error("BUG! Received datagram bytes, but session is null!");
throw new NullPointerException("BUG! DATAGRAM session is null!");
}
@@ -740,14 +741,14 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
if (_log.shouldLog(Log.DEBUG))
_log.debug("sending to client: " + msgText);
msg.write(data);
-
- writeBytes(msg.toByteArray());
+ msg.flush();
+ writeBytes(ByteBuffer.wrap(msg.toByteArray()));
}
public void stopDatagramReceiving() {
_log.debug("stopDatagramReceiving() invoked");
- if (datagramSession == null) {
+ if (getDatagramSession() == null) {
_log.error("BUG! Got datagram receiving stop, but session is null!");
throw new NullPointerException("BUG! DATAGRAM session is null!");
}
@@ -764,7 +765,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
public void streamSendAnswer( int id, String result, String bufferState ) throws IOException
{
- if ( streamSession == null )
+ if ( getStreamSession() == null )
{
_log.error ( "BUG! Want to answer to stream SEND, but session is null!" );
throw new NullPointerException ( "BUG! STREAM session is null!" );
@@ -782,7 +783,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
public void notifyStreamSendBufferFree( int id ) throws IOException
{
- if ( streamSession == null )
+ if ( getStreamSession() == null )
{
_log.error ( "BUG! Stream outgoing buffer is free, but session is null!" );
throw new NullPointerException ( "BUG! STREAM session is null!" );
@@ -796,7 +797,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
public void notifyStreamIncomingConnection(int id, Destination d) throws IOException {
- if (streamSession == null) {
+ if (getStreamSession() == null) {
_log.error("BUG! Received stream connection, but session is null!");
throw new NullPointerException("BUG! STREAM session is null!");
}
@@ -810,7 +811,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
public void notifyStreamOutgoingConnection ( int id, String result, String msg ) throws IOException
{
- if ( streamSession == null )
+ if ( getStreamSession() == null )
{
_log.error ( "BUG! Received stream connection, but session is null!" );
throw new NullPointerException ( "BUG! STREAM session is null!" );
@@ -830,34 +831,28 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
}
}
- public void receiveStreamBytes(int id, byte data[], int len) throws IOException {
- if (streamSession == null) {
+ public void receiveStreamBytes(int id, ByteBuffer data) throws IOException {
+ if (getStreamSession() == null) {
_log.error("Received stream bytes, but session is null!");
throw new NullPointerException("BUG! STREAM session is null!");
}
- String msgText = "STREAM RECEIVED ID=" + id +" SIZE=" + len + "\n";
+ String msgText = "STREAM RECEIVED ID=" + id +" SIZE=" + data.remaining() + "\n";
if (_log.shouldLog(Log.DEBUG))
_log.debug("sending to client: " + msgText);
- byte prefix[] = msgText.getBytes("ISO-8859-1");
+ ByteBuffer prefix = ByteBuffer.wrap(msgText.getBytes("ISO-8859-1"));
- // dont waste so much memory
- //ByteArrayOutputStream msg = new ByteArrayOutputStream();
- //msg.write(msgText.getBytes("ISO-8859-1"));
- //msg.write(data, 0, len);
- // writeBytes(msg.toByteArray());
Object writeLock = getWriteLock();
- OutputStream out = getOut();
synchronized (writeLock) {
- out.write(prefix);
- out.write(data, 0, len);
- out.flush();
+ while (prefix.hasRemaining()) socket.write(prefix);
+ while (data.hasRemaining()) socket.write(data);
+ socket.socket().getOutputStream().flush();
}
}
public void notifyStreamDisconnection(int id, String result, String msg) throws IOException {
- if (streamSession == null) {
+ if (getStreamSession() == null) {
_log.error("BUG! Received stream disconnection, but session is null!");
throw new NullPointerException("BUG! STREAM session is null!");
}
@@ -873,7 +868,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
public void stopStreamReceiving() {
_log.debug("stopStreamReceiving() invoked", new Exception("stopped"));
- if (streamSession == null) {
+ if (getStreamSession() == null) {
_log.error("BUG! Got stream receiving stop, but session is null!");
throw new NullPointerException("BUG! STREAM session is null!");
}
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv2Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv2Handler.java
index 75f1bd4b4..fe1f379b7 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMv2Handler.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMv2Handler.java
@@ -9,7 +9,7 @@ package net.i2p.sam;
*/
import java.io.IOException;
-import java.net.Socket;
+import java.nio.channels.SocketChannel;
import java.util.Properties;
import net.i2p.data.DataFormatException;
@@ -36,7 +36,7 @@ public class SAMv2Handler extends SAMv1Handler implements SAMRawReceiver, SAMDat
* @param verMajor SAM major version to manage (should be 2)
* @param verMinor SAM minor version to manage
*/
- public SAMv2Handler ( Socket s, int verMajor, int verMinor ) throws SAMException, IOException
+ public SAMv2Handler ( SocketChannel s, int verMajor, int verMinor ) throws SAMException, IOException
{
this ( s, verMajor, verMinor, new Properties() );
}
@@ -52,7 +52,7 @@ public class SAMv2Handler extends SAMv1Handler implements SAMRawReceiver, SAMDat
* @param i2cpProps properties to configure the I2CP connection (host, port, etc)
*/
- public SAMv2Handler ( Socket s, int verMajor, int verMinor, Properties i2cpProps ) throws SAMException, IOException
+ public SAMv2Handler ( SocketChannel s, int verMajor, int verMinor, Properties i2cpProps ) throws SAMException, IOException
{
super ( s, verMajor, verMinor, i2cpProps );
}
@@ -72,7 +72,7 @@ public class SAMv2Handler extends SAMv1Handler implements SAMRawReceiver, SAMDat
/* Parse and execute a STREAM message */
protected boolean execStreamMessage ( String opcode, Properties props )
{
- if ( streamSession == null )
+ if ( getStreamSession() == null )
{
_log.error ( "STREAM message received, but no STREAM session exists" );
return false;
@@ -173,7 +173,7 @@ public class SAMv2Handler extends SAMv1Handler implements SAMRawReceiver, SAMDat
}
}
- streamSession.setReceiveLimit ( id, limit, nolimit ) ;
+ getStreamSession().setReceiveLimit ( id, limit, nolimit ) ;
return true;
}
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv2StreamSession.java b/apps/sam/java/src/net/i2p/sam/SAMv2StreamSession.java
index de5b7851b..4197597eb 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMv2StreamSession.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMv2StreamSession.java
@@ -12,6 +12,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
+import java.nio.channels.Channels;
+import java.nio.ByteBuffer;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.ArrayList;
@@ -140,9 +142,6 @@ public class SAMv2StreamSession extends SAMStreamSession
public class StreamConnector implements Runnable
{
- private Object runningLock = new Object();
- private boolean stillRunning = true;
-
private int id;
private Destination dest ;
private I2PSocketOptions opts ;
@@ -245,7 +244,7 @@ public class SAMv2StreamSession extends SAMStreamSession
protected class v2StreamSender extends StreamSender
{
- private List _data;
+ private List _data;
private int _dataSize;
private int _id;
private ByteCache _cache;
@@ -257,7 +256,7 @@ public class SAMv2StreamSession extends SAMStreamSession
public v2StreamSender ( I2PSocket s, int id ) throws IOException
{
super ( s, id );
- _data = new ArrayList ( 1 );
+ _data = new ArrayList ( 1 );
_dataSize = 0;
_id = id;
_cache = ByteCache.getInstance ( 10, 32 * 1024 );
@@ -511,7 +510,7 @@ public class SAMv2StreamSession extends SAMStreamSession
_log.debug ( "run() called for socket reader " + id );
int read = -1;
- byte[] data = new byte[SOCKET_HANDLER_BUF_SIZE];
+ ByteBuffer data = ByteBuffer.allocateDirect(SOCKET_HANDLER_BUF_SIZE);
try
{
@@ -533,7 +532,8 @@ public class SAMv2StreamSession extends SAMStreamSession
break ;
}
- read = in.read ( data );
+ data.clear();
+ read = Channels.newChannel(in).read ( data );
if ( read == -1 )
{
@@ -542,8 +542,8 @@ public class SAMv2StreamSession extends SAMStreamSession
}
totalReceived += read ;
-
- recv.receiveStreamBytes ( id, data, read );
+ data.flip();
+ recv.receiveStreamBytes ( id, data );
}
}
catch ( IOException e )
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java
new file mode 100644
index 000000000..69f14430d
--- /dev/null
+++ b/apps/sam/java/src/net/i2p/sam/SAMv3DatagramSession.java
@@ -0,0 +1,90 @@
+/**
+ * @author MKVore
+ *
+ */
+
+package net.i2p.sam;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.Properties;
+
+import net.i2p.client.I2PSessionException;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.Destination;
+import net.i2p.util.Log;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress ;
+import java.nio.ByteBuffer;
+
+public class SAMv3DatagramSession extends SAMDatagramSession implements SAMv3Handler.Session, SAMDatagramReceiver {
+
+ private final static Log _log = new Log ( SAMv3DatagramSession.class );
+
+ SAMv3Handler handler = null ;
+ SAMv3Handler.DatagramServer server = null ;
+ String nick = null ;
+ SocketAddress clientAddress = null ;
+
+ public String getNick() { return nick; }
+
+ /**
+ * build a DatagramSession according to informations registered
+ * with the given nickname
+ * @param nick nickname of the session
+ * @throws IOException
+ * @throws DataFormatException
+ * @throws I2PSessionException
+ */
+ public SAMv3DatagramSession(String nick)
+ throws IOException, DataFormatException, I2PSessionException {
+
+ super(SAMv3Handler.sSessionsHash.get(nick).getDest(),
+ SAMv3Handler.sSessionsHash.get(nick).getProps(),
+ null
+ );
+ this.nick = nick ;
+ this.recv = this ;
+ this.server = SAMv3Handler.DatagramServer.getInstance() ;
+
+ SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick);
+ if ( rec==null ) throw new InterruptedIOException() ;
+
+ this.handler = rec.getHandler();
+
+ Properties props = rec.getProps();
+ String portStr = props.getProperty("PORT") ;
+ if ( portStr==null ) {
+ _log.debug("receiver port not specified. Current socket will be used.");
+ }
+ else {
+ int port = Integer.parseInt(portStr);
+
+ String host = props.getProperty("HOST");
+ if ( host==null ) {
+ host = rec.getHandler().getClientIP();
+ _log.debug("no host specified. Taken from the client socket : " + host+':'+port);
+ }
+
+
+ this.clientAddress = new InetSocketAddress(host,port);
+ }
+ }
+
+ public void receiveDatagramBytes(Destination sender, byte[] data) throws IOException {
+ if (this.clientAddress==null) {
+ this.handler.receiveDatagramBytes(sender, data);
+ } else {
+ String msg = sender.toBase64()+"\n";
+ ByteBuffer msgBuf = ByteBuffer.allocate(msg.length()+data.length);
+ msgBuf.put(msg.getBytes("ISO-8859-1"));
+ msgBuf.put(data);
+ msgBuf.flip();
+ this.server.send(this.clientAddress, msgBuf);
+ }
+ }
+
+ public void stopDatagramReceiving() {
+ }
+}
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java
new file mode 100644
index 000000000..7a64d8441
--- /dev/null
+++ b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java
@@ -0,0 +1,783 @@
+package net.i2p.sam;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by human in 2004 and released into the public domain
+ * with no warranty of any kind, either expressed or implied.
+ * It probably won't make your computer catch on fire, or eat
+ * your children, but it might. Use at your own risk.
+ *
+ */
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.net.ConnectException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.NoRouteToHostException;
+import java.nio.channels.DatagramChannel;
+import java.nio.channels.SocketChannel;
+import java.nio.ByteBuffer;
+import java.util.Properties;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+import net.i2p.I2PException;
+import net.i2p.client.I2PSessionException;
+import net.i2p.data.Base64;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
+import net.i2p.data.Destination;
+import net.i2p.util.Log;
+import net.i2p.util.I2PAppThread;
+
+/**
+ * Class able to handle a SAM version 3 client connection.
+ *
+ * @author mkvore
+ */
+
+public class SAMv3Handler extends SAMv1Handler
+{
+ private final static Log _log = new Log ( SAMv3Handler.class );
+
+ protected SAMv3RawSession rawSession = null ;
+ protected SAMv3DatagramSession datagramSession = null ;
+ protected SAMv3StreamSession streamSession = null ;
+
+ protected SAMRawSession getRawSession() {
+ return rawSession ;
+ }
+ protected SAMDatagramSession getDatagramSession() {
+ return datagramSession ;
+ }
+ protected SAMStreamSession getStreamSession() {
+ return streamSession ;
+ }
+
+ protected Session session = null ;
+
+ interface Session {
+ String getNick();
+ void close();
+ boolean sendBytes(String dest, byte[] data) throws DataFormatException;
+ }
+
+ /**
+ * Create a new SAM version 3 handler. This constructor expects
+ * that the SAM HELLO message has been still answered (and
+ * stripped) from the socket input stream.
+ *
+ * @param s Socket attached to a SAM client
+ * @param verMajor SAM major version to manage (should be 3)
+ * @param verMinor SAM minor version to manage
+ */
+ public SAMv3Handler ( SocketChannel s, int verMajor, int verMinor ) throws SAMException, IOException
+ {
+ this ( s, verMajor, verMinor, new Properties() );
+ }
+
+ /**
+ * Create a new SAM version 3 handler. This constructor expects
+ * that the SAM HELLO message has been still answered (and
+ * stripped) from the socket input stream.
+ *
+ * @param s Socket attached to a SAM client
+ * @param verMajor SAM major version to manage (should be 3)
+ * @param verMinor SAM minor version to manage
+ * @param i2cpProps properties to configure the I2CP connection (host, port, etc)
+ */
+
+ public SAMv3Handler ( SocketChannel s, int verMajor, int verMinor, Properties i2cpProps ) throws SAMException, IOException
+ {
+ super ( s, verMajor, verMinor, i2cpProps );
+ _log.debug("SAM version 3 handler instantiated");
+ }
+
+ public boolean verifVersion()
+ {
+ return (verMajor == 3 && verMinor == 0) ;
+ }
+
+ static public class DatagramServer {
+
+ private static DatagramServer _instance = null ;
+ private static DatagramChannel server = null ;
+
+ public static DatagramServer getInstance() throws IOException {
+ return getInstance(new Properties());
+ }
+
+ public static DatagramServer getInstance(Properties props) throws IOException {
+ if (_instance==null) {
+ _instance = new DatagramServer(props);
+ }
+ return _instance ;
+ }
+
+ public DatagramServer(Properties props) throws IOException {
+ if (server==null) {
+ server = DatagramChannel.open();
+ }
+
+ String host = props.getProperty(SAMBridge.PROP_DATAGRAM_HOST, SAMBridge.DEFAULT_DATAGRAM_HOST);
+ String portStr = props.getProperty(SAMBridge.PROP_DATAGRAM_PORT, SAMBridge.DEFAULT_DATAGRAM_PORT);
+ int port ;
+ try {
+ port = Integer.parseInt(portStr);
+ } catch (NumberFormatException e) {
+ port = Integer.parseInt(SAMBridge.DEFAULT_DATAGRAM_PORT);
+ }
+
+ server.socket().bind(new InetSocketAddress(host, port));
+ new I2PAppThread(new Listener(server), "DatagramListener").start();
+ }
+
+ public void send(SocketAddress addr, ByteBuffer msg) throws IOException {
+ server.send(msg, addr);
+ }
+
+ class Listener implements Runnable {
+
+ DatagramChannel server = null;
+
+ public Listener(DatagramChannel server)
+ {
+ this.server = server ;
+ }
+ public void run()
+ {
+ ByteBuffer inBuf = ByteBuffer.allocateDirect(SAMRawSession.RAW_SIZE_MAX+1024);
+
+ while (!Thread.interrupted())
+ {
+ inBuf.clear();
+ try {
+ server.receive(inBuf);
+ } catch (IOException e) {
+ break ;
+ }
+ inBuf.flip();
+ ByteBuffer outBuf = ByteBuffer.wrap(new byte[inBuf.remaining()]);
+ outBuf.put(inBuf);
+ outBuf.flip();
+ new I2PAppThread(new MessageDispatcher(outBuf.array()), "MessageDispatcher").start();
+ }
+ }
+ }
+ }
+
+ public static class MessageDispatcher implements Runnable
+ {
+ ByteArrayInputStream is = null ;
+
+ public MessageDispatcher(byte[] buf)
+ {
+ this.is = new java.io.ByteArrayInputStream(buf) ;
+ }
+
+ public void run() {
+ String header = null ;
+ String nick ;
+ String dest ;
+ String version ;
+
+ try {
+ header = DataHelper.readLine(is).trim();
+ StringTokenizer tok = new StringTokenizer(header, " ");
+ if (tok.countTokens() != 3) {
+ // This is not a correct message, for sure
+ _log.debug("Error in message format");
+ return;
+ }
+ version = tok.nextToken();
+ if (!"3.0".equals(version)) return ;
+ nick = tok.nextToken();
+ dest = tok.nextToken();
+
+ byte[] data = new byte[is.available()];
+ is.read(data);
+ SessionRecord rec = sSessionsHash.get(nick);
+ if (rec!=null) {
+ rec.getHandler().session.sendBytes(dest,data);
+ }
+ } catch (Exception e) {}
+ }
+ }
+
+ public class SessionRecord
+ {
+ protected String m_dest ;
+ protected Properties m_props ;
+ protected ThreadGroup m_threadgroup ;
+ protected SAMv3Handler m_handler ;
+
+ public SessionRecord( String dest, Properties props, SAMv3Handler handler )
+ {
+ m_dest = new String(dest) ;
+ m_props = new Properties() ;
+ m_props.putAll(props);
+ m_threadgroup = null ;
+ m_handler = handler ;
+ }
+
+ public SessionRecord( SessionRecord in )
+ {
+ m_dest = in.getDest();
+ m_props = in.getProps();
+ m_threadgroup = in.getThreadGroup();
+ m_handler = in.getHandler();
+ }
+
+ synchronized public String getDest()
+ {
+ return new String(m_dest) ;
+ }
+ synchronized public Properties getProps()
+ {
+ Properties p = new Properties();
+ p.putAll(m_props);
+ return m_props;
+ }
+ synchronized public SAMv3Handler getHandler()
+ {
+ return m_handler ;
+ }
+ synchronized public ThreadGroup getThreadGroup()
+ {
+ return m_threadgroup ;
+ }
+ synchronized public void createThreadGroup(String name)
+ {
+ if (m_threadgroup == null)
+ m_threadgroup = new ThreadGroup(name);
+ }
+ }
+
+ public static class SessionsDB
+ {
+ static final long serialVersionUID = 0x1 ;
+
+ class ExistingId extends Exception {
+ static final long serialVersionUID = 0x1 ;
+ }
+ class ExistingDest extends Exception {
+ static final long serialVersionUID = 0x1 ;
+ }
+
+ HashMap map ;
+
+ public SessionsDB() {
+ map = new HashMap() ;
+ }
+
+ synchronized public boolean put( String nick, SessionRecord session ) throws ExistingId, ExistingDest
+ {
+ if ( map.containsKey(nick) ) {
+ throw new ExistingId();
+ }
+ for ( SessionRecord r : map.values() ) {
+ if (r.getDest().equals(session.getDest())) {
+ throw new ExistingDest();
+ }
+ }
+
+ if ( !map.containsKey(nick) ) {
+ session.createThreadGroup("SAM session "+nick);
+ map.put(nick, session) ;
+ return true ;
+ }
+ else
+ return false ;
+ }
+ synchronized public boolean del( String nick )
+ {
+ SessionRecord rec = map.get(nick);
+
+ if ( rec!=null ) {
+ map.remove(nick);
+ return true ;
+ }
+ else
+ return false ;
+ }
+ synchronized public SessionRecord get(String nick)
+ {
+ return map.get(nick);
+ }
+ synchronized public boolean containsKey( String nick )
+ {
+ return map.containsKey(nick);
+ }
+ }
+
+ public static SessionsDB sSessionsHash = new SessionsDB() ;
+
+ public String getClientIP()
+ {
+ return this.socket.socket().getInetAddress().getHostAddress();
+ }
+
+ boolean stolenSocket = false ;
+
+ boolean streamForwardingSocket = false ;
+
+ public void stealSocket()
+ {
+ stolenSocket = true ;
+ this.stopHandling();
+ }
+
+ public void handle() {
+ String msg = null;
+ String domain = null;
+ String opcode = null;
+ boolean canContinue = false;
+ StringTokenizer tok;
+ Properties props;
+
+ this.thread.setName("SAMv3Handler " + _id);
+ _log.debug("SAM handling started");
+
+ try {
+ InputStream in = getClientSocket().socket().getInputStream();
+
+ while (true) {
+ if (shouldStop()) {
+ _log.debug("Stop request found");
+ break;
+ }
+ String line = DataHelper.readLine(in) ;
+ if (line==null) {
+ _log.debug("Connection closed by client (line read : null)");
+ break;
+ }
+ msg = line.trim();
+
+ if (_log.shouldLog(Log.DEBUG)) {
+ _log.debug("New message received: [" + msg + "]");
+ }
+
+ if(msg.equals("")) {
+ _log.debug("Ignoring newline");
+ continue;
+ }
+
+ tok = new StringTokenizer(msg, " ");
+ if (tok.countTokens() < 2) {
+ // This is not a correct message, for sure
+ _log.debug("Error in message format");
+ break;
+ }
+ domain = tok.nextToken();
+ opcode = tok.nextToken();
+ if (_log.shouldLog(Log.DEBUG)) {
+ _log.debug("Parsing (domain: \"" + domain
+ + "\"; opcode: \"" + opcode + "\")");
+ }
+ props = SAMUtils.parseParams(tok);
+
+ if (domain.equals("STREAM")) {
+ canContinue = execStreamMessage(opcode, props);
+ } else if (domain.equals("SESSION")) {
+ if (i2cpProps != null)
+ props.putAll(i2cpProps); // make sure we've got the i2cp settings
+ canContinue = execSessionMessage(opcode, props);
+ } else if (domain.equals("DEST")) {
+ canContinue = execDestMessage(opcode, props);
+ } else if (domain.equals("NAMING")) {
+ canContinue = execNamingMessage(opcode, props);
+ } else if (domain.equals("DATAGRAM")) {
+ canContinue = execDatagramMessage(opcode, props);
+ } else {
+ _log.debug("Unrecognized message domain: \""
+ + domain + "\"");
+ break;
+ }
+
+ if (!canContinue) {
+ break;
+ }
+ }
+ } catch (IOException e) {
+ _log.debug("Caught IOException ("
+ + e.getMessage() + ") for message [" + msg + "]", e);
+ } catch (Exception e) {
+ _log.error("Unexpected exception for message [" + msg + "]", e);
+ } finally {
+ _log.debug("Stopping handler");
+
+ if (!this.stolenSocket)
+ {
+ try {
+ closeClientSocket();
+ } catch (IOException e) {
+ _log.error("Error closing socket: " + e.getMessage());
+ }
+ }
+ if (streamForwardingSocket)
+ {
+ if (this.getStreamSession()!=null) {
+ try {
+ this.streamSession.stopForwardingIncoming();
+ } catch (SAMException e) {
+ _log.error("Error while stopping forwarding connections: " + e.getMessage());
+ } catch (InterruptedIOException e) {
+ _log.error("Interrupted while stopping forwarding connections: " + e.getMessage());
+ }
+ }
+ }
+
+
+
+ die();
+ }
+ }
+
+ protected void die() {
+ SessionRecord rec = null ;
+
+ if (session!=null) {
+ session.close();
+ rec = sSessionsHash.get(session.getNick());
+ }
+ if (rec!=null) {
+ rec.getThreadGroup().interrupt() ;
+ while (rec.getThreadGroup().activeCount()>0)
+ try {
+ Thread.sleep(1000);
+ } catch ( InterruptedException e) {}
+ rec.getThreadGroup().destroy();
+ sSessionsHash.del(session.getNick());
+ }
+ }
+
+ /* Parse and execute a SESSION message */
+ @Override
+ protected boolean execSessionMessage(String opcode, Properties props) {
+
+ String dest = "BUG!";
+ String nick = null ;
+ boolean ok = false ;
+
+ try{
+ if (opcode.equals("CREATE")) {
+ if ((this.getRawSession()!= null) || (this.getDatagramSession() != null)
+ || (this.getStreamSession() != null)) {
+ _log.debug("Trying to create a session, but one still exists");
+ return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Session already exists\"\n");
+ }
+ if (props == null) {
+ _log.debug("No parameters specified in SESSION CREATE message");
+ return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No parameters for SESSION CREATE\"\n");
+ }
+
+ dest = props.getProperty("DESTINATION");
+ if (dest == null) {
+ _log.debug("SESSION DESTINATION parameter not specified");
+ return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"DESTINATION not specified\"\n");
+ }
+ props.remove("DESTINATION");
+
+
+ if (dest.equals("TRANSIENT")) {
+ _log.debug("TRANSIENT destination requested");
+ ByteArrayOutputStream priv = new ByteArrayOutputStream(640);
+ SAMUtils.genRandomKey(priv, null);
+
+ dest = Base64.encode(priv.toByteArray());
+ } else {
+ _log.debug("Custom destination specified [" + dest + "]");
+ }
+
+ try {
+ SAMUtils.checkPrivateDestination(dest);
+ } catch ( SAMUtils.InvalidDestination e ) {
+ return writeString("SESSION STATUS RESULT=INVALID_KEY\n");
+ }
+
+ nick = props.getProperty("ID");
+ if (nick == null) {
+ _log.debug("SESSION ID parameter not specified");
+ return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"ID not specified\"\n");
+ }
+ props.remove("ID");
+
+
+ String style = props.getProperty("STYLE");
+ if (style == null) {
+ _log.debug("SESSION STYLE parameter not specified");
+ return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No SESSION STYLE specified\"\n");
+ }
+ props.remove("STYLE");
+
+
+
+ // Record the session in the database sSessionsHash
+ Properties allProps = new Properties();
+ allProps.putAll(i2cpProps);
+ allProps.putAll(props);
+
+
+ try {
+ sSessionsHash.put( nick, new SessionRecord(dest, allProps, this) ) ;
+ } catch (SessionsDB.ExistingId e) {
+ _log.debug("SESSION ID parameter already in use");
+ return writeString("SESSION STATUS RESULT=DUPLICATED_ID\n");
+ } catch (SessionsDB.ExistingDest e) {
+ return writeString("SESSION STATUS RESULT=DUPLICATED_DEST\n");
+ }
+
+
+ // Create the session
+
+ if (style.equals("RAW")) {
+ DatagramServer.getInstance(i2cpProps);
+ rawSession = newSAMRawSession(nick);
+ this.session = rawSession ;
+ } else if (style.equals("DATAGRAM")) {
+ DatagramServer.getInstance(i2cpProps);
+ datagramSession = newSAMDatagramSession(nick);
+ this.session = datagramSession ;
+ } else if (style.equals("STREAM")) {
+ streamSession = newSAMStreamSession(nick);
+ this.session = streamSession ;
+ } else {
+ _log.debug("Unrecognized SESSION STYLE: \"" + style +"\"");
+ return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized SESSION STYLE\"\n");
+ }
+ ok = true ;
+ return writeString("SESSION STATUS RESULT=OK DESTINATION="
+ + dest + "\n");
+ } else {
+ _log.debug("Unrecognized SESSION message opcode: \""
+ + opcode + "\"");
+ return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized opcode\"\n");
+ }
+ } catch (DataFormatException e) {
+ _log.debug("Invalid destination specified");
+ return writeString("SESSION STATUS RESULT=INVALID_KEY DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
+ } catch (I2PSessionException e) {
+ _log.debug("I2P error when instantiating session", e);
+ return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
+ } catch (SAMException e) {
+ _log.error("Unexpected SAM error", e);
+ return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
+ } catch (IOException e) {
+ _log.error("Unexpected IOException", e);
+ return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
+ } finally {
+ // unregister the session if it has not been created
+ if ( !ok && nick!=null ) {
+ sSessionsHash.del(nick) ;
+ session = null ;
+ }
+ }
+ }
+
+ SAMv3StreamSession newSAMStreamSession(String login )
+ throws IOException, DataFormatException, SAMException
+ {
+ return new SAMv3StreamSession( login ) ;
+ }
+
+ SAMv3RawSession newSAMRawSession(String login )
+ throws IOException, DataFormatException, SAMException, I2PSessionException
+ {
+ return new SAMv3RawSession( login ) ;
+ }
+
+ SAMv3DatagramSession newSAMDatagramSession(String login )
+ throws IOException, DataFormatException, SAMException, I2PSessionException
+ {
+ return new SAMv3DatagramSession( login ) ;
+ }
+
+ /* Parse and execute a STREAM message */
+ protected boolean execStreamMessage ( String opcode, Properties props )
+ {
+ String nick = null ;
+ SessionRecord rec = null ;
+
+ if ( session != null )
+ {
+ _log.error ( "STREAM message received, but this session is a master session" );
+
+ try {
+ notifyStreamResult(true, "I2P_ERROR", "master session cannot be used for streams");
+ } catch (IOException e) {}
+ return false;
+ }
+
+ nick = props.getProperty("ID");
+ if (nick == null) {
+ _log.debug("SESSION ID parameter not specified");
+ try {
+ notifyStreamResult(true, "I2P_ERROR", "ID not specified");
+ } catch (IOException e) {}
+ return false ;
+ }
+ props.remove("ID");
+
+ rec = sSessionsHash.get(nick);
+
+ if ( rec==null ) {
+ _log.debug("STREAM SESSION ID does not exist");
+ try {
+ notifyStreamResult(true, "INVALID_ID", "STREAM SESSION ID does not exist");
+ } catch (IOException e) {}
+ return false ;
+ }
+
+ streamSession = rec.getHandler().streamSession ;
+
+ if (streamSession==null) {
+ _log.debug("specified ID is not a stream session");
+ try {
+ notifyStreamResult(true, "I2P_ERROR", "specified ID is not a STREAM session");
+ } catch (IOException e) {}
+ return false ;
+ }
+
+ if ( opcode.equals ( "CONNECT" ) )
+ {
+ return execStreamConnect ( props );
+ }
+ else if ( opcode.equals ( "ACCEPT" ) )
+ {
+ return execStreamAccept ( props );
+ }
+ else if ( opcode.equals ( "FORWARD") )
+ {
+ return execStreamForwardIncoming( props );
+ }
+ else
+ {
+ _log.debug ( "Unrecognized RAW message opcode: \""
+ + opcode + "\"" );
+ try {
+ notifyStreamResult(true, "I2P_ERROR", "Unrecognized RAW message opcode: "+opcode );
+ } catch (IOException e) {}
+ return false;
+ }
+ }
+
+
+ protected boolean execStreamConnect( Properties props) {
+ try {
+ if (props == null) {
+ notifyStreamResult(true,"I2P_ERROR","No parameters specified in STREAM CONNECT message");
+ _log.debug("No parameters specified in STREAM CONNECT message");
+ return false;
+ }
+ boolean verbose = props.getProperty("SILENT","false").equals("false");
+
+ String dest = props.getProperty("DESTINATION");
+ if (dest == null) {
+ notifyStreamResult(verbose, "I2P_ERROR", "Destination not specified in RAW SEND message");
+ _log.debug("Destination not specified in RAW SEND message");
+ return false;
+ }
+ props.remove("DESTINATION");
+
+ try {
+ streamSession.connect( this, dest, props );
+ return true ;
+ } catch (DataFormatException e) {
+ _log.debug("Invalid destination in STREAM CONNECT message");
+ notifyStreamResult ( verbose, "INVALID_KEY", null );
+ } catch (ConnectException e) {
+ _log.debug("STREAM CONNECT failed: " + e.getMessage());
+ notifyStreamResult ( verbose, "CONNECTION_REFUSED", null );
+ } catch (NoRouteToHostException e) {
+ _log.debug("STREAM CONNECT failed: " + e.getMessage());
+ notifyStreamResult ( verbose, "CANT_REACH_PEER", null );
+ } catch (InterruptedIOException e) {
+ _log.debug("STREAM CONNECT failed: " + e.getMessage());
+ notifyStreamResult ( verbose, "TIMEOUT", null );
+ } catch (I2PException e) {
+ _log.debug("STREAM CONNECT failed: " + e.getMessage());
+ notifyStreamResult ( verbose, "I2P_ERROR", e.getMessage() );
+ }
+ } catch (IOException e) {
+ }
+ return false ;
+ }
+
+ protected boolean execStreamForwardIncoming( Properties props ) {
+ try {
+ try {
+ streamForwardingSocket = true ;
+ streamSession.startForwardingIncoming(props);
+ notifyStreamResult( true, "OK", null );
+ return true ;
+ } catch (SAMException e) {
+ _log.debug("Forwarding STREAM connections failed: " + e.getMessage());
+ notifyStreamResult ( true, "I2P_ERROR", "Forwarding failed : " + e.getMessage() );
+ }
+ } catch (IOException e) {
+ }
+ return false ;
+ }
+
+ protected boolean execStreamAccept( Properties props )
+ {
+ boolean verbose = props.getProperty( "SILENT", "false").equals("false");
+ try {
+ try {
+ notifyStreamResult(verbose, "OK", null);
+ streamSession.accept(this, verbose);
+ return true ;
+ } catch (InterruptedIOException e) {
+ _log.debug("STREAM ACCEPT failed: " + e.getMessage());
+ notifyStreamResult( verbose, "TIMEOUT", e.getMessage() );
+ } catch (I2PException e) {
+ _log.debug("STREAM ACCEPT failed: " + e.getMessage());
+ notifyStreamResult ( verbose, "I2P_ERROR", e.getMessage() );
+ } catch (SAMException e) {
+ _log.debug("STREAM ACCEPT failed: " + e.getMessage());
+ notifyStreamResult ( verbose, "ALREADY_ACCEPTING", null );
+ }
+ } catch (IOException e) {
+ }
+ return false ;
+ }
+
+
+ public void notifyStreamResult(boolean verbose, String result, String message) throws IOException
+ {
+ if (!verbose) return ;
+
+ String out = "STREAM STATUS RESULT="+result;
+ if (message!=null)
+ 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 {
+ if (getStreamSession() == null) {
+ _log.error("BUG! Received stream connection, but session is null!");
+ throw new NullPointerException("BUG! STREAM session is null!");
+ }
+
+ if (!writeString(d.toBase64() + "\n")) {
+ throw new IOException("Error notifying connection to SAM client");
+ }
+ }
+
+ public static void notifyStreamIncomingConnection(SocketChannel client, Destination d) throws IOException {
+ if (!writeString(d.toBase64() + "\n", client)) {
+ throw new IOException("Error notifying connection to SAM client");
+ }
+ }
+
+}
+
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java
new file mode 100644
index 000000000..3695bf3dd
--- /dev/null
+++ b/apps/sam/java/src/net/i2p/sam/SAMv3RawSession.java
@@ -0,0 +1,90 @@
+/**
+ *
+ */
+package net.i2p.sam;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.util.Properties;
+
+import net.i2p.client.I2PSessionException;
+import net.i2p.data.DataFormatException;
+import net.i2p.util.Log;
+
+/**
+ * @author MKVore
+ *
+ */
+public class SAMv3RawSession extends SAMRawSession implements SAMv3Handler.Session, SAMRawReceiver {
+
+ String nick = null ;
+ SAMv3Handler handler = null ;
+ SAMv3Handler.DatagramServer server ;
+ private final static Log _log = new Log ( SAMv3DatagramSession.class );
+ SocketAddress clientAddress = null ;
+
+ public String getNick() { return nick; }
+
+ /**
+ * Build a Raw Datagram Session according to information
+ * registered with the given nickname
+ *
+ * @param nick nickname of the session
+ * @throws IOException
+ * @throws DataFormatException
+ * @throws I2PSessionException
+ */
+ public SAMv3RawSession(String nick)
+ throws IOException, DataFormatException, I2PSessionException {
+
+ super(SAMv3Handler.sSessionsHash.get(nick).getDest(),
+ SAMv3Handler.sSessionsHash.get(nick).getProps(),
+ SAMv3Handler.sSessionsHash.get(nick).getHandler()
+ );
+ this.nick = nick ;
+ this.recv = this ;
+ this.server = SAMv3Handler.DatagramServer.getInstance() ;
+
+ SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick);
+ if ( rec==null ) throw new InterruptedIOException() ;
+
+ this.handler = rec.getHandler();
+
+ Properties props = rec.getProps();
+
+
+ String portStr = props.getProperty("PORT") ;
+ if ( portStr==null ) {
+ _log.debug("receiver port not specified. Current socket will be used.");
+ }
+ else {
+ int port = Integer.parseInt(portStr);
+
+ String host = props.getProperty("HOST");
+ if ( host==null ) {
+ host = rec.getHandler().getClientIP();
+
+ _log.debug("no host specified. Taken from the client socket : " + host +':'+port);
+ }
+
+
+ this.clientAddress = new InetSocketAddress(host,port);
+ }
+ }
+
+ public void receiveRawBytes(byte[] data) throws IOException {
+ if (this.clientAddress==null) {
+ this.handler.receiveRawBytes(data);
+ } else {
+ ByteBuffer msgBuf = ByteBuffer.allocate(data.length);
+ msgBuf.put(data);
+ msgBuf.flip();
+ this.server.send(this.clientAddress, msgBuf);
+ }
+ }
+
+ public void stopRawReceiving() {}
+}
diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java b/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java
new file mode 100644
index 000000000..26d99fa01
--- /dev/null
+++ b/apps/sam/java/src/net/i2p/sam/SAMv3StreamSession.java
@@ -0,0 +1,408 @@
+package net.i2p.sam;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by human in 2004 and released into the public domain
+ * with no warranty of any kind, either expressed or implied.
+ * It probably won't make your computer catch on fire, or eat
+ * your children, but it might. Use at your own risk.
+ *
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.ConnectException;
+import java.net.NoRouteToHostException;
+import java.util.Properties;
+
+import net.i2p.I2PException;
+import net.i2p.client.I2PClient;
+import net.i2p.client.streaming.I2PServerSocket;
+import net.i2p.client.streaming.I2PSocket;
+import net.i2p.client.streaming.I2PSocketManagerFactory;
+import net.i2p.client.streaming.I2PSocketOptions;
+import net.i2p.data.Base64;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.Destination;
+import net.i2p.util.I2PAppThread;
+import net.i2p.util.Log;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.nio.ByteBuffer ;
+import java.nio.channels.SocketChannel;
+
+/**
+ * SAMv3 STREAM session class.
+ *
+ * @author mkvore
+ */
+
+public class SAMv3StreamSession extends SAMStreamSession implements SAMv3Handler.Session
+{
+
+ private final static Log _log = new Log ( SAMv3StreamSession.class );
+
+ protected final int BUFFER_SIZE = 1024 ;
+
+ protected Object socketServerLock = new Object();
+ protected I2PServerSocket socketServer = null;
+
+ protected String nick ;
+
+ public String getNick() {
+ return nick ;
+ }
+
+ /**
+ * Create a new SAM STREAM session, according to information
+ * registered with the given nickname
+ *
+ * @param login The nickname
+ * @throws IOException
+ * @throws DataFormatException
+ * @throws SAMException
+ */
+ public SAMv3StreamSession(String login)
+ throws IOException, DataFormatException, SAMException
+ {
+ initSAMStreamSession(login);
+ }
+
+ public static SAMv3Handler.SessionsDB getDB()
+ {
+ return SAMv3Handler.sSessionsHash ;
+ }
+
+ private void initSAMStreamSession(String login)
+ throws IOException, DataFormatException, SAMException {
+
+ SAMv3Handler.SessionRecord rec = getDB().get(login);
+ String dest = rec.getDest() ;
+ ByteArrayInputStream ba_dest = new ByteArrayInputStream(Base64.decode(dest));
+
+ this.recv = rec.getHandler();
+
+ _log.debug("SAM STREAM session instantiated");
+
+ Properties allprops = new Properties();
+ allprops.putAll(System.getProperties());
+ allprops.putAll(rec.getProps());
+
+ String i2cpHost = allprops.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
+ int i2cpPort ;
+ String port = allprops.getProperty(I2PClient.PROP_TCP_PORT, "7654");
+ try {
+ i2cpPort = Integer.parseInt(port);
+ } catch (NumberFormatException nfe) {
+ throw new SAMException("Invalid I2CP port specified [" + port + "]");
+ }
+
+ _log.debug("Creating I2PSocketManager...");
+ socketMgr = I2PSocketManagerFactory.createManager(ba_dest,
+ i2cpHost,
+ i2cpPort,
+ allprops);
+ if (socketMgr == null) {
+ throw new SAMException("Error creating I2PSocketManager towards "+i2cpHost+":"+i2cpPort);
+ }
+
+ socketMgr.addDisconnectListener(new DisconnectListener());
+ this.nick = login ;
+ }
+
+ /**
+ * Connect the SAM STREAM session to the specified Destination
+ *
+ * @param handler The handler that communicates with the requesting client
+ * @param dest Base64-encoded Destination to connect to
+ * @param props Options to be used for connection
+ *
+ * @throws DataFormatException if the destination is not valid
+ * @throws ConnectException if the destination refuses connections
+ * @throws NoRouteToHostException if the destination can't be reached
+ * @throws InterruptedIOException if the connection timeouts
+ * @throws I2PException if there's another I2P-related error
+ * @throws IOException
+ */
+ public void connect ( SAMv3Handler handler, String dest, Properties props )
+ throws I2PException, ConnectException, NoRouteToHostException,
+ DataFormatException, InterruptedIOException, IOException {
+
+ boolean verbose = (props.getProperty("SILENT", "false").equals("false"));
+ Destination d = SAMUtils.getDest(dest);
+
+ I2PSocketOptions opts = socketMgr.buildOptions(props);
+ if (props.getProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT) == null)
+ opts.setConnectTimeout(60 * 1000);
+
+ _log.debug("Connecting new I2PSocket...");
+
+ // blocking connection (SAMv3)
+
+ I2PSocket i2ps = socketMgr.connect(d, opts);
+
+ SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick);
+
+ if ( rec==null ) throw new InterruptedIOException() ;
+
+ handler.notifyStreamResult(verbose, "OK", null) ;
+
+ handler.stealSocket() ;
+
+ ReadableByteChannel fromClient = handler.getClientSocket();
+ ReadableByteChannel fromI2P = Channels.newChannel(i2ps.getInputStream());
+ WritableByteChannel toClient = handler.getClientSocket();
+ WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream());
+
+ (new Thread(rec.getThreadGroup(), new I2PAppThread(new Pipe(fromClient,toI2P, "SAMPipeClientToI2P"), "SAMPipeClientToI2P"), "SAMPipeClientToI2P")).start();
+ (new Thread(rec.getThreadGroup(), new I2PAppThread(new Pipe(fromI2P,toClient, "SAMPipeI2PToClient"), "SAMPipeI2PToClient"), "SAMPipeI2PToClient")).start();
+
+ }
+
+ /**
+ * Accept an incoming STREAM
+ *
+ * @param handler The handler that communicates with the requesting client
+ * @param verbose If true, SAM will send the Base64-encoded peer Destination of an
+ * incoming socket as the first line of data sent to its client
+ * on the handler socket
+ *
+ * @throws DataFormatException if the destination is not valid
+ * @throws ConnectException if the destination refuses connections
+ * @throws NoRouteToHostException if the destination can't be reached
+ * @throws InterruptedIOException if the connection timeouts
+ * @throws I2PException if there's another I2P-related error
+ * @throws IOException
+ */
+ public void accept(SAMv3Handler handler, boolean verbose)
+ throws I2PException, InterruptedIOException, IOException, SAMException {
+
+ synchronized( this.socketServerLock )
+ {
+ if (this.socketServer!=null) {
+ _log.debug("a socket server is already defined for this destination");
+ throw new SAMException("a socket server is already defined for this destination");
+ }
+ this.socketServer = this.socketMgr.getServerSocket();
+ }
+
+ I2PSocket i2ps;
+ i2ps = this.socketServer.accept();
+
+ synchronized( this.socketServerLock )
+ {
+ this.socketServer = null ;
+ }
+
+ SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick);
+
+ if ( rec==null ) throw new InterruptedIOException() ;
+
+ if (verbose)
+ handler.notifyStreamIncomingConnection(i2ps.getPeerDestination()) ;
+
+ handler.stealSocket() ;
+ ReadableByteChannel fromClient = handler.getClientSocket();
+ ReadableByteChannel fromI2P = Channels.newChannel(i2ps.getInputStream());
+ WritableByteChannel toClient = handler.getClientSocket();
+ WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream());
+
+ (new Thread(rec.getThreadGroup(), new I2PAppThread(new Pipe(fromClient,toI2P, "SAMPipeClientToI2P"), "SAMPipeClientToI2P"), "SAMPipeClientToI2P")).start();
+ (new Thread(rec.getThreadGroup(), new I2PAppThread(new Pipe(fromI2P,toClient, "SAMPipeI2PToClient"), "SAMPipeI2PToClient"), "SAMPipeI2PToClient")).start();
+ }
+
+
+ public void startForwardingIncoming( Properties props ) throws SAMException, InterruptedIOException
+ {
+ SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick);
+ boolean verbose = props.getProperty("SILENT", "false").equals("false");
+
+ if ( rec==null ) throw new InterruptedIOException() ;
+
+ String portStr = props.getProperty("PORT") ;
+ if ( portStr==null ) {
+ _log.debug("receiver port not specified");
+ throw new SAMException("receiver port not specified");
+ }
+ int port = Integer.parseInt(portStr);
+
+ String host = props.getProperty("HOST");
+ if ( host==null ) {
+ host = rec.getHandler().getClientIP();
+ _log.debug("no host specified. Taken from the client socket : " + host +':'+port);
+ }
+
+
+ synchronized( this.socketServerLock )
+ {
+ if (this.socketServer!=null) {
+ _log.debug("a socket server is already defined for this destination");
+ throw new SAMException("a socket server is already defined for this destination");
+ }
+ this.socketServer = this.socketMgr.getServerSocket();
+ }
+
+ SocketForwarder forwarder = new SocketForwarder(host, port, this, verbose);
+ (new Thread(rec.getThreadGroup(), new I2PAppThread(forwarder, "SAMStreamForwarder"), "SAMStreamForwarder")).start();
+ }
+
+ public class SocketForwarder extends Thread
+ {
+ String host = null ;
+ int port = 0 ;
+ SAMv3StreamSession session;
+ boolean verbose;
+
+ SocketForwarder(String host, int port, SAMv3StreamSession session, boolean verbose) {
+ this.host = host ;
+ this.port = port ;
+ this.session = session ;
+ this.verbose = verbose ;
+ }
+
+ public void run()
+ {
+ while (session.getSocketServer()!=null) {
+
+ // wait and accept a connection from I2P side
+ I2PSocket i2ps = null ;
+ try {
+ i2ps = session.getSocketServer().accept();
+ } catch (Exception e) {}
+
+ if (i2ps==null) {
+ continue ;
+ }
+
+ // open a socket towards client
+ java.net.InetSocketAddress addr = new java.net.InetSocketAddress(host,port);
+
+ SocketChannel clientServerSock = null ;
+ try {
+ clientServerSock = SocketChannel.open(addr) ;
+ }
+ catch ( IOException e ) {
+ continue ;
+ }
+ if (clientServerSock==null) {
+ try {
+ i2ps.close();
+ } catch (IOException ee) {}
+ continue ;
+ }
+
+ // build pipes between both sockets
+ try {
+ if (this.verbose)
+ SAMv3Handler.notifyStreamIncomingConnection(
+ clientServerSock, i2ps.getPeerDestination());
+ ReadableByteChannel fromClient = clientServerSock ;
+ ReadableByteChannel fromI2P = Channels.newChannel(i2ps.getInputStream());
+ WritableByteChannel toClient = clientServerSock ;
+ WritableByteChannel toI2P = Channels.newChannel(i2ps.getOutputStream());
+ (new I2PAppThread(new Pipe(fromClient,toI2P, "SAMPipeClientToI2P"), "SAMPipeClientToI2P")).start();
+ (new I2PAppThread(new Pipe(fromI2P,toClient, "SAMPipeI2PToClient"), "SAMPipeI2PToClient")).start();
+
+ } catch (IOException e) {
+ try {
+ clientServerSock.close();
+ } catch (IOException ee) {}
+ try {
+ i2ps.close();
+ } catch (IOException ee) {}
+ continue ;
+ }
+ }
+ }
+ }
+ public class Pipe extends Thread
+ {
+ ReadableByteChannel in ;
+ WritableByteChannel out ;
+ ByteBuffer buf ;
+
+ public Pipe(ReadableByteChannel in, WritableByteChannel out, String name)
+ {
+ super(name);
+ this.in = in ;
+ this.out = out ;
+ this.buf = ByteBuffer.allocate(BUFFER_SIZE) ;
+ }
+
+ public void run()
+ {
+ try {
+ while (!Thread.interrupted() && (in.read(buf)>=0 || buf.position() != 0)) {
+ buf.flip();
+ out.write(buf);
+ buf.compact();
+ }
+ }
+ catch (IOException e)
+ {
+ this.interrupt();
+ }
+ try {
+ in.close();
+ }
+ catch (IOException e) {}
+ try {
+ buf.flip();
+ while (buf.hasRemaining())
+ out.write(buf);
+ }
+ catch (IOException e) {}
+ try {
+ out.close();
+ }
+ catch (IOException e) {}
+ }
+ }
+
+ public I2PServerSocket getSocketServer()
+ {
+ synchronized ( this.socketServerLock ) {
+ return this.socketServer ;
+ }
+ }
+ /**
+ * stop Forwarding Incoming connection coming from I2P
+ * @throws SAMException
+ * @throws InterruptedIOException
+ */
+ public void stopForwardingIncoming() throws SAMException, InterruptedIOException
+ {
+ SAMv3Handler.SessionRecord rec = SAMv3Handler.sSessionsHash.get(nick);
+
+ if ( rec==null ) throw new InterruptedIOException() ;
+
+ I2PServerSocket server = null ;
+ synchronized( this.socketServerLock )
+ {
+ if (this.socketServer==null) {
+ _log.debug("no socket server is defined for this destination");
+ throw new SAMException("no socket server is defined for this destination");
+ }
+ server = this.socketServer ;
+ this.socketServer = null ;
+ _log.debug("nulling socketServer in stopForwardingIncoming. Object " + this );
+ }
+ try {
+ server.close();
+ } catch ( I2PException e) {}
+ }
+
+ /**
+ * Close the stream session
+ */
+ @Override
+ public void close() {
+ socketMgr.destroySocketManager();
+ }
+
+ public boolean sendBytes(String s, byte[] b) throws DataFormatException
+ {
+ throw new DataFormatException(null);
+ }
+}
diff --git a/apps/sam/java/src/net/i2p/sam/client/SAMEventHandler.java b/apps/sam/java/src/net/i2p/sam/client/SAMEventHandler.java
index 7df1a2324..9df867aa5 100644
--- a/apps/sam/java/src/net/i2p/sam/client/SAMEventHandler.java
+++ b/apps/sam/java/src/net/i2p/sam/client/SAMEventHandler.java
@@ -12,17 +12,17 @@ import net.i2p.util.Log;
*
*/
public class SAMEventHandler extends SAMClientEventListenerImpl {
- private I2PAppContext _context;
+ //private I2PAppContext _context;
private Log _log;
private Boolean _helloOk;
private Object _helloLock = new Object();
private Boolean _sessionCreateOk;
private Object _sessionCreateLock = new Object();
private Object _namingReplyLock = new Object();
- private Map _namingReplies = new HashMap();
+ private Map _namingReplies = new HashMap();
public SAMEventHandler(I2PAppContext ctx) {
- _context = ctx;
+ //_context = ctx;
_log = ctx.logManager().getLog(getClass());
}
diff --git a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java
index 4e9d1133b..80db744a3 100644
--- a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java
+++ b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSend.java
@@ -31,10 +31,10 @@ public class SAMStreamSend {
private OutputStream _samOut;
private InputStream _samIn;
private SAMReader _reader;
- private boolean _dead;
+ //private boolean _dead;
private SAMEventHandler _eventHandler;
/** Connection id (Integer) to peer (Flooder) */
- private Map _remotePeers;
+ private Map _remotePeers;
public static void main(String args[]) {
if (args.length < 4) {
@@ -42,7 +42,7 @@ public class SAMStreamSend {
return;
}
I2PAppContext ctx = new I2PAppContext();
- String files[] = new String[args.length - 3];
+ //String files[] = new String[args.length - 3];
SAMStreamSend sender = new SAMStreamSend(ctx, args[0], args[1], args[2], args[3]);
sender.startup();
}
@@ -50,14 +50,14 @@ public class SAMStreamSend {
public SAMStreamSend(I2PAppContext ctx, String samHost, String samPort, String destFile, String dataFile) {
_context = ctx;
_log = ctx.logManager().getLog(SAMStreamSend.class);
- _dead = false;
+ //_dead = false;
_samHost = samHost;
_samPort = samPort;
_destFile = destFile;
_dataFile = dataFile;
_conOptions = "";
_eventHandler = new SendEventHandler(_context);
- _remotePeers = new HashMap();
+ _remotePeers = new HashMap();
}
public void startup() {
@@ -207,7 +207,6 @@ public class SAMStreamSend {
_started = _context.clock().now();
_context.statManager().addRateData("send." + _connectionId + ".started", 1, 0);
byte data[] = new byte[1024];
- long value = 0;
long lastSend = _context.clock().now();
while (!_closed) {
try {
diff --git a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java
index 8d29e3799..406150b36 100644
--- a/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java
+++ b/apps/sam/java/src/net/i2p/sam/client/SAMStreamSink.java
@@ -31,10 +31,10 @@ public class SAMStreamSink {
private OutputStream _samOut;
private InputStream _samIn;
private SAMReader _reader;
- private boolean _dead;
+ //private boolean _dead;
private SAMEventHandler _eventHandler;
/** Connection id (Integer) to peer (Flooder) */
- private Map _remotePeers;
+ private Map _remotePeers;
public static void main(String args[]) {
if (args.length < 4) {
@@ -49,14 +49,14 @@ public class SAMStreamSink {
public SAMStreamSink(I2PAppContext ctx, String samHost, String samPort, String destFile, String sinkDir) {
_context = ctx;
_log = ctx.logManager().getLog(SAMStreamSink.class);
- _dead = false;
+ //_dead = false;
_samHost = samHost;
_samPort = samPort;
_destFile = destFile;
_sinkDir = sinkDir;
_conOptions = "";
_eventHandler = new SinkEventHandler(_context);
- _remotePeers = new HashMap();
+ _remotePeers = new HashMap();
}
public void startup() {
@@ -70,7 +70,8 @@ public class SAMStreamSink {
String ourDest = handshake();
_log.debug("Handshake complete. we are " + ourDest);
if (ourDest != null) {
- boolean written = writeDest(ourDest);
+ //boolean written =
+ writeDest(ourDest);
_log.debug("Dest written");
}
}
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java
index 2d198ad66..30d849ba4 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java
@@ -2,8 +2,6 @@ package net.i2p.client.streaming;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
-import java.util.ArrayList;
-import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
@@ -41,7 +39,7 @@ class ConnectionHandler {
_context = context;
_log = context.logManager().getLog(ConnectionHandler.class);
_manager = mgr;
- _synQueue = new LinkedBlockingQueue(MAX_QUEUE_SIZE);
+ _synQueue = new LinkedBlockingQueue(MAX_QUEUE_SIZE);
_active = false;
_acceptTimeout = DEFAULT_ACCEPT_TIMEOUT;
}
@@ -126,7 +124,7 @@ class ConnectionHandler {
if (timeoutMs <= 0) {
try {
syn = _synQueue.take(); // waits forever
- } catch (InterruptedException ie) { break;}
+ } catch (InterruptedException ie) { } // { break;}
} else {
long remaining = expiration - _context.clock().now();
// (dont think this applies anymore for LinkedBlockingQueue)
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java
index 75adf6e59..1ff65248d 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/MessageHandler.java
@@ -73,8 +73,8 @@ public class MessageHandler implements I2PSessionListener {
* @param session that has been terminated
*/
public void disconnected(I2PSession session) {
- if (_log.shouldLog(Log.ERROR))
- _log.error("I2PSession disconnected");
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("I2PSession disconnected");
_manager.disconnectAllHard();
List listeners = null;
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java
index 197b92754..827be5b04 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java
@@ -213,7 +213,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
timeRemaining = 10*1000;
wait(timeRemaining);
}
- } catch (InterruptedException ie) { break; }
+ } catch (InterruptedException ie) { }//{ break; }
}
if (!writeSuccessful())
releasePayload();
diff --git a/build.xml b/build.xml
index 6ed84ca73..fc251a40a 100644
--- a/build.xml
+++ b/build.xml
@@ -25,6 +25,10 @@
+
+
+
+
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java
index 9e42eef5f..a5d8ed94d 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl.java
@@ -16,7 +16,6 @@ import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -90,7 +89,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
protected I2PAppContext _context;
/** monitor for waiting until a lease set has been granted */
- private Object _leaseSetWait = new Object();
+ private final Object _leaseSetWait = new Object();
/** whether the session connection has already been closed (or not yet opened) */
protected boolean _closed;
@@ -101,7 +100,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
/** have we received the current date from the router yet? */
private boolean _dateReceived;
/** lock that we wait upon, that the SetDateMessageHandler notifies */
- private Object _dateReceivedLock = new Object();
+ private final Object _dateReceivedLock = new Object();
/**
* thread that we tell when new messages are available who then tells us
@@ -253,6 +252,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
try {
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "connect begin to " + _hostname + ":" + _portNum);
_socket = new Socket(_hostname, _portNum);
+ // _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
_out = _socket.getOutputStream();
synchronized (_out) {
_out.write(I2PClient.PROTOCOL_BYTE);
diff --git a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java
index a3e5e57a7..58b5cae9f 100644
--- a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java
@@ -4,18 +4,16 @@ package net.i2p.client;
* public domain
*/
-import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.LinkedBlockingQueue;
-import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
-import net.i2p.data.SessionTag;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.util.Log;
import net.i2p.util.SimpleScheduler;
@@ -97,6 +95,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
* 255 disallowed
* @param port 1-65535 or PORT_ANY for all
*/
+ @Override
public void addSessionListener(I2PSessionListener lsnr, int proto, int port) {
_demultiplexer.addListener(lsnr, proto, port);
}
@@ -107,11 +106,13 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
* @param proto 1-254 or 0 for all; 255 disallowed
* @param port 1-65535 or 0 for all
*/
+ @Override
public void addMuxedSessionListener(I2PSessionMuxedListener l, int proto, int port) {
_demultiplexer.addMuxedListener(l, proto, port);
}
/** removes the specified listener (only) */
+ @Override
public void removeListener(int proto, int port) {
_demultiplexer.removeListener(proto, port);
}
@@ -149,6 +150,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
* @param fromPort 1-65535 or 0 for unset
* @param toPort 1-65535 or 0 for unset
*/
+ @Override
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size,
SessionKey keyUsed, Set tagsSent, long expires,
int proto, int fromPort, int toPort)
@@ -198,24 +200,40 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
protected class MuxedAvailabilityNotifier extends AvailabilityNotifier {
private LinkedBlockingQueue _msgs;
- private boolean _alive;
+ private volatile boolean _alive = false;
private static final int POISON_SIZE = -99999;
-
+ private final AtomicBoolean stopping = new AtomicBoolean(false);
+
public MuxedAvailabilityNotifier() {
_msgs = new LinkedBlockingQueue();
}
-
- public void stopNotifying() {
- _msgs.clear();
- if (_alive) {
- _alive = false;
- try {
- _msgs.put(new MsgData(0, POISON_SIZE, 0, 0, 0));
- } catch (InterruptedException ie) {}
+
+ @Override
+ public void stopNotifying() {
+ boolean again = true;
+ synchronized (stopping) {
+ if( !stopping.getAndSet(true)) {
+ if (_alive == true) {
+ // System.out.println("I2PSessionMuxedImpl.stopNotifying()");
+ _msgs.clear();
+ while(again) {
+ try {
+ _msgs.put(new MsgData(0, POISON_SIZE, 0, 0, 0));
+ again = false;
+ // System.out.println("I2PSessionMuxedImpl.stopNotifying() success.");
+ } catch (InterruptedException ie) {
+ continue;
+ }
+ }
+ }
+ _alive = false;
+ stopping.set(false);
+ }
+ // stopping.notifyAll();
}
}
-
/** unused */
+ @Override
public void available(long msgId, int size) { throw new IllegalArgumentException("no"); }
public void available(long msgId, int size, int proto, int fromPort, int toPort) {
@@ -224,20 +242,24 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
} catch (InterruptedException ie) {}
}
+ @Override
public void run() {
- _alive = true;
- while (true) {
- MsgData msg;
+ MsgData msg;
+ _alive=true;
+ while (_alive) {
try {
msg = _msgs.take();
} catch (InterruptedException ie) {
+ _log.debug("I2PSessionMuxedImpl.run() InterruptedException " + String.valueOf(_msgs.size()) + " Messages, Alive " + _alive);
continue;
}
- if (msg.size == POISON_SIZE)
+ if (msg.size == POISON_SIZE) {
+ // System.out.println("I2PSessionMuxedImpl.run() POISONED");
break;
+ }
try {
- _demultiplexer.messageAvailable(I2PSessionMuxedImpl.this, msg.id,
- msg.size, msg.proto, msg.fromPort, msg.toPort);
+ _demultiplexer.messageAvailable(I2PSessionMuxedImpl.this,
+ msg.id, msg.size, msg.proto, msg.fromPort, msg.toPort);
} catch (Exception e) {
_log.error("Error notifying app of message availability");
}
diff --git a/core/java/src/net/i2p/data/i2cp/I2CPMessageReader.java b/core/java/src/net/i2p/data/i2cp/I2CPMessageReader.java
index 5c79d94f3..461c4b08a 100644
--- a/core/java/src/net/i2p/data/i2cp/I2CPMessageReader.java
+++ b/core/java/src/net/i2p/data/i2cp/I2CPMessageReader.java
@@ -115,8 +115,8 @@ public class I2CPMessageReader {
}
private class I2CPMessageReaderRunner implements Runnable {
- private boolean _doRun;
- private boolean _stayAlive;
+ private volatile boolean _doRun;
+ private volatile boolean _stayAlive;
public I2CPMessageReaderRunner() {
_doRun = true;
diff --git a/history.txt b/history.txt
index 50315edcd..e802d6da4 100644
--- a/history.txt
+++ b/history.txt
@@ -1,5 +1,135 @@
+2009-05-12 sponge
+ * BOB clean up, change println's to _log.warn, bump BOB version
+ * I2PSessionMuxedImpl.java changes as per zzz, and they test OK for me.
+
+2009-05-12 mkvore
+ * SAM: fix: warnings when generating javadoc
+
+2009-05-11 zzz
+ * Connect client: Fix NPE when used with advanced i2ptunnel features
+ * Context: Don't instantiate unused AdminManager
+ * logs.jsp: Put critical log at the top
+ * NetDb: Don't accept stores of our own LeaseSets or RouterInfo
+
+2009-05-11 mkvore
+ * SAM: fix: removed ERROR level logging when a client disconnects
+
+2009-05-09 sponge
+ * merge
+
+2009-05-09 sponge
+ * fixed OOM on lock (woops! my bad!)
+
+2009-05-08 Mathiasdm
+ * desktopgui: moved files to stop polluting the namespace
+ (everything now in net.i2p.desktopgui)
+ * desktopgui: some variable renaming in general configuration
+
+2009-05-07 mkvore
+ * SAM: version 3 added
+ * SAM: blocking case corrected on simultaneous client connection (v.1-3)
+
+2009-05-07 zzz
+ * Add nibble.i2p to proxy list and hosts.txt
+
+2009-05-07 zzz
+ * Addressbook: Name the thread
+ * Console:
+ - More IE button fixes, try harder to not refresh the iframe after shutdown
+ - Disable idle options for streamr client, it will never be
+ idle because it pings the server
+ * Floodfill Monitor: Slow down the volunteers
+ * Throttle: Throttle at 90% so we throttle before we WRED
+
+2009-05-06 Mathiasdm
+ * Improvements to popup menu rightclick action
+ * Added general configuration options (still not available by default)
+ * General fixes
+ * Added ant build options (irc says eche|on would like that ;))
+
+2009-05-06 sponge
+ * Hopefully the last fixes for BOB.
+ * Fixes to prevent race in client-side I2CP and Notifier.
+
+2009-05-03 sponge
+ * More hopeful fixes for BOB.
+ * Added new Robert ID to snark
+
+2009-05-01 zzz
+ * Build files:
+ - Fix up susidns build file so it will work with gcj
+ - Add consoleDocs target
+ * Client: Fix race NPE (thanks sponge)
+ * Console: fix ERR-UDP Disabled and Inbound TCP host/port not set
+ * I2CP: Fix race NPE
+ * I2PTunnel:
+ - Try to fix locking to prevent duplicate destinations when using
+ the new option new-dest-on-resume. Still not right for shared clients
+ but should be better for non-shared.
+ * Router console:
+ - Add jbigi and cpu info to logs.jsp
+ * Session key manager:
+ - Log before a hang maybe
+ * URL Launcher:
+ - Launcher on linux was stopping after trying opera, whether it succeeded or failed.
+ Now it keeps going to try firefox, etc. as designed.
+ - Extend default delay from 5s to 15s so it will reliably start
+
+2009-04-27 sponge
+ * more BOB fixes, complete with warnings when things go wrong, and
+ success messages when things turn around and go right. Terminates
+ early so that applications wait no more than 10 seconds or so.
+ * Reversed a few earlier patches that caused some odd behavior.
+ * Changed some core println()'s to debugging messages.
+
+2009-04-27 zzz
+ * Build files:
+ - New updaterWithJettyFixes target, build it for pkg
+ - Pass compiler args down from top build.xml
+ * GarlicMessageBuilder: Reduce bundled tags to 40 (was 100)
+ * i2psnark: Add Postman2 tracker
+ * I2PTunnel: Allow spaces in dest and proxy lists
+ * NetDb:
+ - Adjust RouterInfo expiration down to control memory usage
+ - Display LeaseSets and RouterInfos on separate console pages
+ * NTCP:
+ - Correct the meanings of the i2np.ntcp.autoip and i2np.ntcp.autoport
+ advanced config. If you have one of these set but not the other, you
+ will have to adjust your configuration on config.jsp.
+ * RouterConsole: iframe tweaks
+ * StatisticsManager: Cleanup
+ * Streaming: Don't let jrandom yell so loud
+ * Tunnel Pool: Don't self-destruct if more than 6 IB tunnels configured
+
+2009-04-25 sponge
+ * I2PSessionMuxedImpl atomic fixes
+ * BOB fixes. This should be the final bug wack. Good Luck to everybody!
+
+2009-04-23 zzz
+ * Blocklist: cleanup
+ * eepget: handle -h, --help, bad options, etc.
+ (http://forum.i2p/viewtopic.php?p=16261#16261)
+ * Fragmenter: don't re-throw the corrupt fragment IllegalStateException,
+ to limit the damage - root cause still not found
+ * i2psnark: (http://forum.i2p/viewtopic.php?t=3317)
+ - Change file limit to 512 (was 256)
+ - Change size limit to 10GB (was 5GB)
+ - Change request size to 16KB (was 32KB)
+ - Change pipeline to 5 (was 3)
+ * logs.jsp: Move version info to the top
+ * Jetty: Fix temp dir name handling on windows, which was
+ causing susidns not to start
+ (http://forum.i2p/viewtopic.php?t=3364)
+ * NTCP: Prevent IllegalStateException
+ * PeerProfile:
+ - Replace a hot lock with concurrent RW lock
+ - Rewrite ugly IP Restriction code
+ - Also use transport IP in restriction code
+ * RouterConsole: Make summary bar a refreshing iframe
+ * Transport: Start the previously unused CleanupUnreachable
+
2009-04-21 sponge
- * Code janator work, basic corrections involving @Override, and
+ * Code janitor work, basic corrections involving @Override, and
appling final where it is important. Also fixed some equals methods
and commented places that need fixing.
diff --git a/hosts.txt b/hosts.txt
index 95ca28af6..44cdacd75 100644
--- a/hosts.txt
+++ b/hosts.txt
@@ -313,3 +313,5 @@ tracker.mastertracker.i2p=VzXD~stRKbL3MOmeTn1iaCQ0CFyTmuFHiKYyo0Rd~dFPZFCYH-22rT
codevoid.i2p=tV-4GJjgYIoCDTTJ91nfDbhSnT8B2o3v-TUfHtiAAjJJdroCAEDbmJWFPUQJEEispvrjNe~fP7VAYkk9fAhSrmdBLtEGB3NUESdiZEPsDtKJBdxijPGb1erZF2Z6eYHoK-t5g7MWWTsgLz~4xn211Jpfa-T4pqL2tcjsa7ixsaMpHF8NXFrITdyxSJRPz8OnHYgDR~ULFyzroi255MpiSUBzGcUZEiQSFLHLhjT5D5tP~gfJirFnfgOHvzWBK9L7y91qY~gYvM2eDcxMxq4Ac1gw0JeahkzAk3j6Spco3LHW3bJvELopf1QmLFu3nfPaegH1Hejt9AhXEH~FV-~M9F1BePipcIYlm7nKyre3aVPLYDZSCvkUx~8nnD3HEpMijD8fdfqSFPU7aZQe19a7rZJUbX~a4M3rBDO-C4uAid6Uznb1tLu2XR1GVVITGHaLwmumImXjlU~1nEnluBQB6iBQPZ9xJccArlYgWSooR9gpyN93PwTPsPe5cPkxCFuxAAAA
echelon.i2p=w6zK9m4fqSfvJck9EGIR1wRIbWsEQ2DkjZ-VI57ESFqLqbTIA1cD5nOfSSbpELqPyhjifdrNiBNAsSdyil3C0a2B7CGtwUcTS2dCG0tKf2nAbvpsbcCK17nI4Xbu5KqZU0y3hJ~l7rcJqQBR0nfV5cU30ZDrpQV6VL875cihGlnmwLFq6qSzNcEb88Nw6wFG~FIgB2PJ6A3jJyuTnLrdiMvwqgD6nSyeOylOgBCsNxXh8-drrhASjladfNrwjlGRCZTiQ~H92HIyOwiabDiG3TUugMaFWs87yuXnZ~ni9jgjoAMFo8xV8Od2BiRgCxkZoMU07FhgUjew9qtXNa04wkexf3gx77nVPhqE0GHqCuwHwmBVf92RdYEys76u~akaOMq5UhayDpCBCaHiYLkKDNqmh47tfMCwxf6z8VIcR4zv25QfJDIWPs~RA~9U7m4raytiAs5PvYZBn4B3SqOL8XdkL9sDT54sQXbsYCJr3olu6ieMtNWlmos0uohYXNUyAAAA
crstrack.i2p=b4G9sCdtfvccMAXh~SaZrPqVQNyGQbhbYMbw6supq2XGzbjU4NcOmjFI0vxQ8w1L05twmkOvg5QERcX6Mi8NQrWnR0stLExu2LucUXg1aYjnggxIR8TIOGygZVIMV3STKH4UQXD--wz0BUrqaLxPhrm2Eh9Hwc8TdB6Na4ShQUq5Xm8D4elzNUVdpM~RtChEyJWuQvoGAHY3ppX-EJJLkiSr1t77neS4Lc-KofMVmgI9a2tSSpNAagBiNI6Ak9L1T0F9uxeDfEG9bBSQPNMOSUbAoEcNxtt7xOW~cNOAyMyGydwPMnrQ5kIYPY8Pd3XudEko970vE0D6gO19yoBMJpKx6Dh50DGgybLQ9CpRaynh2zPULTHxm8rneOGRcQo8D3mE7FQ92m54~SvfjXjD2TwAVGI~ae~n9HDxt8uxOecAAvjjJ3TD4XM63Q9TmB38RmGNzNLDBQMEmJFpqQU8YeuhnS54IVdUoVQFqui5SfDeLXlSkh4vYoMU66pvBfWbAAAA
+tracker2.postman.i2p=lnQ6yoBTxQuQU8EQ1FlF395ITIQF-HGJxUeFvzETLFnoczNjQvKDbtSB7aHhn853zjVXrJBgwlB9sO57KakBDaJ50lUZgVPhjlI19TgJ-CxyHhHSCeKx5JzURdEW-ucdONMynr-b2zwhsx8VQCJwCEkARvt21YkOyQDaB9IdV8aTAmP~PUJQxRwceaTMn96FcVenwdXqleE16fI8CVFOV18jbJKrhTOYpTtcZKV4l1wNYBDwKgwPx5c0kcrRzFyw5~bjuAKO~GJ5dR7BQsL7AwBoQUS4k1lwoYrG1kOIBeDD3XF8BWb6K3GOOoyjc1umYKpur3G~FxBuqtHAsDRICkEbKUqJ9mPYQlTSujhNxiRIW-oLwMtvayCFci99oX8MvazPS7~97x0Gsm-onEK1Td9nBdmq30OqDxpRtXBimbzkLbR1IKObbg9HvrKs3L-kSyGwTUmHG9rSQSoZEvFMA-S0EXO~o4g21q1oikmxPMhkeVwQ22VHB0-LZJfmLr4SAAAA
+nibble.i2p=V2XQ31BQWcwLcBNz2ywb4xy0Q1GMjdziQyjKql-lGdYPOX7w9g3j8IkA1jfW6YYwNi5QZc0JurjrSNH1yx6Y1goI8SB1l-yWdzst73fGWo6B1UtL45XrfXPg5k34RpktCNa4KoeIsUnGnxHQESSj5hw389hvexKXlkAHXQg9eUfbBYyzZc~~Kt4YdYX4cfMpXXjg443kyEiwKisOaRuiEN-YjqZ8pJTyAQsOKNg8hL3e15XFNPfAAkCSsALPAqj0~HZDwCZDeV0Cp4iaCGjw8tsNQ7xBeSjnhOeMoZKtrPAbbK4vNh7OIcakcVu16ykfEf-FcqbPQQe9rjilMy8V-BcjhggjUcZmtWj9qE7RMfUFpbAIfNHgWXTl5yR5V~brqxxuBxHQWn4oyB5NpY02dBkvvxXwdk~XFzXlSz~uEZKVswvI8rUHR4a2N3YDss5iQ~uscvKwNvsTZiDUaN66~CacZLYU9BtDBNnAxClz9LSu5b9CiunKeacbH6l5qrPpAAAA
diff --git a/installer/resources/clients.config b/installer/resources/clients.config
index 170f4c8fe..88bba5f9a 100644
--- a/installer/resources/clients.config
+++ b/installer/resources/clients.config
@@ -1,32 +1,32 @@
# fire up the web console
clientApp.0.args=7657 ::1,127.0.0.1 ./webapps/
clientApp.0.main=net.i2p.router.web.RouterConsoleRunner
-clientApp.0.name=webConsole
+clientApp.0.name=Web console
clientApp.0.onBoot=true
clientApp.0.startOnLoad=true
# SAM bridge
clientApp.1.main=net.i2p.sam.SAMBridge
-clientApp.1.name=SAMBridge
+clientApp.1.name=SAM application bridge
clientApp.1.args=sam.keys 127.0.0.1 7656 i2cp.tcp.host=127.0.0.1 i2cp.tcp.port=7654
clientApp.1.startOnLoad=false
# poke the i2ptunnels defined in i2ptunnel.config
clientApp.2.main=net.i2p.i2ptunnel.TunnelControllerGroup
-clientApp.2.name=Tunnels
+clientApp.2.name=Application tunnels
clientApp.2.args=i2ptunnel.config
clientApp.2.startOnLoad=true
# run our own eepsite with a seperate jetty instance
clientApp.3.main=org.mortbay.jetty.Server
-clientApp.3.name=eepsite
+clientApp.3.name=My eepsite web server
clientApp.3.args=eepsite/jetty.xml
clientApp.3.delay=30
clientApp.3.startOnLoad=true
# load a browser pointing at the web console whenever we start up
clientApp.4.main=net.i2p.apps.systray.UrlLauncher
-clientApp.4.name=consoleBrowser
+clientApp.4.name=Browser launch at startup
clientApp.4.args=http://127.0.0.1:7657/index.jsp
clientApp.4.delay=15
clientApp.4.startOnLoad=true
@@ -35,5 +35,5 @@ clientApp.4.startOnLoad=true
clientApp.5.args=
clientApp.5.delay=10
clientApp.5.main=net.i2p.BOB.BOB
-clientApp.5.name=BOB
+clientApp.5.name=BOB application bridge
clientApp.5.startOnLoad=false
diff --git a/installer/resources/i2ptunnel.config b/installer/resources/i2ptunnel.config
index 48d18b95e..c0dd002db 100644
--- a/installer/resources/i2ptunnel.config
+++ b/installer/resources/i2ptunnel.config
@@ -5,7 +5,7 @@ tunnel.0.type=httpclient
tunnel.0.sharedClient=true
tunnel.0.interface=127.0.0.1
tunnel.0.listenPort=4444
-tunnel.0.proxyList=false.i2p
+tunnel.0.proxyList=false.i2p,nibble.i2p
tunnel.0.i2cpHost=127.0.0.1
tunnel.0.i2cpPort=7654
tunnel.0.option.inbound.nickname=shared clients
diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java
index c9a95dd55..2c9a1319e 100644
--- a/router/java/src/net/i2p/router/Router.java
+++ b/router/java/src/net/i2p/router/Router.java
@@ -999,8 +999,7 @@ public class Router {
}
public static void main(String args[]) {
- System.out.println("Starting I2P " + RouterVersion.VERSION + "-" + RouterVersion.BUILD);
- System.out.println(RouterVersion.ID);
+ System.out.println("Starting I2P " + RouterVersion.FULL_VERSION);
installUpdates();
verifyWrapperConfig();
Router r = new Router();
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 1d4797d6f..e4b934bb2 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -15,11 +15,15 @@ import net.i2p.CoreVersion;
*
*/
public class RouterVersion {
- public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $";
+ /** deprecated */
+ public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
- public final static long BUILD = 1;
+ public final static long BUILD = 14;
+ /** for example "-test" */
+ public final static String EXTRA = "";
+ public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA;
public static void main(String args[]) {
- System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
+ System.out.println("I2P Router version: " + FULL_VERSION);
System.out.println("Router ID: " + RouterVersion.ID);
System.out.println("I2P Core version: " + CoreVersion.VERSION);
System.out.println("Core ID: " + CoreVersion.ID);