* more verbose errors (include MESSAGE data on the I2P_ERROR reply, not just in the log)
* don't create excess I2PAppContexts (if any old context will do, use the global) keep track of keys per spec (when DESTINATION=blah, create (or reuse) the destination private keys). we still need to persist this data though. * the DESTINATION in the SESSION STATUS is now the same as the one sent in the SESSION CREATE, /not/ the base64 of the private key, per spec * enum is a reserved word in 1.5, so s/enum/names/ for future compatability * logging
This commit is contained in:
@ -13,6 +13,14 @@ import java.net.InetAddress;
|
|||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.PrivateKey;
|
||||||
|
import net.i2p.data.SigningPrivateKey;
|
||||||
|
|
||||||
import net.i2p.util.I2PThread;
|
import net.i2p.util.I2PThread;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
@ -27,11 +35,16 @@ public class SAMBridge implements Runnable {
|
|||||||
private final static Log _log = new Log(SAMBridge.class);
|
private final static Log _log = new Log(SAMBridge.class);
|
||||||
private ServerSocket serverSocket;
|
private ServerSocket serverSocket;
|
||||||
private Properties i2cpProps;
|
private Properties i2cpProps;
|
||||||
|
/**
|
||||||
|
* app designated destination name to the base64 of the I2P formatted
|
||||||
|
* destination keys (Destination+PrivateKey+SigningPrivateKey)
|
||||||
|
*/
|
||||||
|
private Map nameToPrivKeys = Collections.synchronizedMap(new HashMap(8));
|
||||||
|
|
||||||
private boolean acceptConnections = true;
|
private boolean acceptConnections = true;
|
||||||
|
|
||||||
private final static int SAM_LISTENPORT = 7656;
|
private final static int SAM_LISTENPORT = 7656;
|
||||||
|
|
||||||
private SAMBridge() {}
|
private SAMBridge() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,6 +76,46 @@ public class SAMBridge implements Runnable {
|
|||||||
this.i2cpProps = i2cpProps;
|
this.i2cpProps = i2cpProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the destination associated with the given name
|
||||||
|
*
|
||||||
|
* @return null if the name does not exist, or if it is improperly formatted
|
||||||
|
*/
|
||||||
|
public Destination getDestination(String name) {
|
||||||
|
String val = (String)nameToPrivKeys.get(name);
|
||||||
|
if (val == null) return null;
|
||||||
|
try {
|
||||||
|
Destination d = new Destination();
|
||||||
|
d.fromBase64(val);
|
||||||
|
return d;
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
_log.error("Error retrieving the destination from " + name, dfe);
|
||||||
|
nameToPrivKeys.remove(name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the I2P private keystream for the given name, formatted
|
||||||
|
* as a base64 string (Destination+PrivateKey+SessionPrivateKey, as I2CP
|
||||||
|
* stores it).
|
||||||
|
*
|
||||||
|
* @return null if the name does not exist, else the stream
|
||||||
|
*/
|
||||||
|
public String getKeystream(String name) {
|
||||||
|
String val = (String)nameToPrivKeys.get(name);
|
||||||
|
if (val == null) return null;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify that the given keystream should be used for the given name
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void addKeystream(String name, String stream) {
|
||||||
|
nameToPrivKeys.put(name, stream);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Usage:
|
* Usage:
|
||||||
* <pre>SAMBridge [[listenHost ]listenPort[ name=val]*]</pre>
|
* <pre>SAMBridge [[listenHost ]listenPort[ name=val]*]</pre>
|
||||||
@ -140,10 +193,18 @@ public class SAMBridge implements Runnable {
|
|||||||
} catch (IOException e) {}
|
} catch (IOException e) {}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
handler.setBridge(this);
|
||||||
handler.startHandling();
|
handler.startHandling();
|
||||||
} catch (SAMException e) {
|
} catch (SAMException e) {
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
_log.error("SAM error: " + e.getMessage(), e);
|
_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);
|
||||||
|
}
|
||||||
s.close();
|
s.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import net.i2p.util.Log;
|
|||||||
public class SAMDatagramSession extends SAMMessageSession {
|
public class SAMDatagramSession extends SAMMessageSession {
|
||||||
|
|
||||||
private final static Log _log = new Log(SAMDatagramSession.class);
|
private final static Log _log = new Log(SAMDatagramSession.class);
|
||||||
|
public static int DGRAM_SIZE_MAX = 31*1024;
|
||||||
|
|
||||||
private SAMDatagramReceiver recv = null;
|
private SAMDatagramReceiver recv = null;
|
||||||
|
|
||||||
@ -43,7 +44,8 @@ public class SAMDatagramSession extends SAMMessageSession {
|
|||||||
* @param recv Object that will receive incoming data
|
* @param recv Object that will receive incoming data
|
||||||
*/
|
*/
|
||||||
public SAMDatagramSession(String dest, Properties props,
|
public SAMDatagramSession(String dest, Properties props,
|
||||||
SAMDatagramReceiver recv) throws IOException, DataFormatException, I2PSessionException {
|
SAMDatagramReceiver recv) throws IOException,
|
||||||
|
DataFormatException, I2PSessionException {
|
||||||
super(dest, props);
|
super(dest, props);
|
||||||
|
|
||||||
this.recv = recv;
|
this.recv = recv;
|
||||||
@ -58,7 +60,8 @@ public class SAMDatagramSession extends SAMMessageSession {
|
|||||||
* @param recv Object that will receive incoming data
|
* @param recv Object that will receive incoming data
|
||||||
*/
|
*/
|
||||||
public SAMDatagramSession(InputStream destStream, Properties props,
|
public SAMDatagramSession(InputStream destStream, Properties props,
|
||||||
SAMDatagramReceiver recv) throws IOException, DataFormatException, I2PSessionException {
|
SAMDatagramReceiver recv) throws IOException,
|
||||||
|
DataFormatException, I2PSessionException {
|
||||||
super(destStream, props);
|
super(destStream, props);
|
||||||
|
|
||||||
this.recv = recv;
|
this.recv = recv;
|
||||||
@ -73,6 +76,9 @@ public class SAMDatagramSession extends SAMMessageSession {
|
|||||||
* @return True if the data was sent, false otherwise
|
* @return True if the data was sent, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean sendBytes(String dest, byte[] data) throws DataFormatException {
|
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 = dgramMaker.makeI2PDatagram(data);
|
||||||
|
|
||||||
return sendBytesThroughMessageSession(dest, dgram);
|
return sendBytesThroughMessageSession(dest, dgram);
|
||||||
|
@ -30,6 +30,7 @@ public abstract class SAMHandler implements Runnable {
|
|||||||
private final static Log _log = new Log(SAMHandler.class);
|
private final static Log _log = new Log(SAMHandler.class);
|
||||||
|
|
||||||
protected I2PThread thread = null;
|
protected I2PThread thread = null;
|
||||||
|
protected SAMBridge bridge = null;
|
||||||
|
|
||||||
private Object socketWLock = new Object(); // Guards writings on socket
|
private Object socketWLock = new Object(); // Guards writings on socket
|
||||||
private Socket socket = null;
|
private Socket socket = null;
|
||||||
@ -71,6 +72,8 @@ public abstract class SAMHandler implements Runnable {
|
|||||||
thread.start();
|
thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBridge(SAMBridge bridge) { this.bridge = bridge; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actually handle the SAM protocol.
|
* Actually handle the SAM protocol.
|
||||||
*
|
*
|
||||||
@ -124,7 +127,9 @@ public abstract class SAMHandler implements Runnable {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected final void closeClientSocket() throws IOException {
|
protected final void closeClientSocket() throws IOException {
|
||||||
socket.close();
|
if (socket != null)
|
||||||
|
socket.close();
|
||||||
|
socket = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,8 +32,8 @@ public class SAMHandlerFactory {
|
|||||||
*
|
*
|
||||||
* @param s Socket attached to SAM client
|
* @param s Socket attached to SAM client
|
||||||
* @param i2cpProps config options for our i2cp connection
|
* @param i2cpProps config options for our i2cp connection
|
||||||
*
|
* @throws SAMException if the connection handshake (HELLO message) was malformed
|
||||||
* @return A SAM protocol handler
|
* @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(Socket s, Properties i2cpProps) throws SAMException {
|
||||||
BufferedReader br;
|
BufferedReader br;
|
||||||
@ -66,8 +66,8 @@ public class SAMHandlerFactory {
|
|||||||
{
|
{
|
||||||
String opcode;
|
String opcode;
|
||||||
if (!(opcode = tok.nextToken()).equals("VERSION")) {
|
if (!(opcode = tok.nextToken()).equals("VERSION")) {
|
||||||
throw new SAMException("Unrecognized HELLO message opcode: \""
|
throw new SAMException("Unrecognized HELLO message opcode: '"
|
||||||
+ opcode + "\"");
|
+ opcode + "'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,22 +88,8 @@ public class SAMHandlerFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String ver = chooseBestVersion(minVer, maxVer);
|
String ver = chooseBestVersion(minVer, maxVer);
|
||||||
if (ver == null) {
|
if (ver == null)
|
||||||
// Let's answer negatively
|
throw new SAMException("No version specified");
|
||||||
try {
|
|
||||||
OutputStream out = s.getOutputStream();
|
|
||||||
out.write("HELLO REPLY RESULT=NOVERSION\n".getBytes("ISO-8859-1"));
|
|
||||||
return null;
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
_log.error("Caught UnsupportedEncodingException ("
|
|
||||||
+ e.getMessage() + ")");
|
|
||||||
throw new SAMException("Character encoding error: "
|
|
||||||
+ e.getMessage());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new SAMException("Error reading from socket: "
|
|
||||||
+ e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let's answer positively
|
// Let's answer positively
|
||||||
try {
|
try {
|
||||||
@ -135,8 +121,8 @@ public class SAMHandlerFactory {
|
|||||||
throw new SAMException("BUG! (in handler instantiation)");
|
throw new SAMException("BUG! (in handler instantiation)");
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
_log.error("IOException caught during SAM handler instantiation");
|
_log.error("Error creating the v1 handler", e);
|
||||||
return null;
|
throw new SAMException("IOException caught during SAM handler instantiation");
|
||||||
}
|
}
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import net.i2p.util.Log;
|
|||||||
public class SAMRawSession extends SAMMessageSession {
|
public class SAMRawSession extends SAMMessageSession {
|
||||||
|
|
||||||
private final static Log _log = new Log(SAMRawSession.class);
|
private final static Log _log = new Log(SAMRawSession.class);
|
||||||
|
public static final int RAW_SIZE_MAX = 32*1024;
|
||||||
|
|
||||||
private SAMRawReceiver recv = null;
|
private SAMRawReceiver recv = null;
|
||||||
/**
|
/**
|
||||||
@ -64,6 +65,8 @@ public class SAMRawSession extends SAMMessageSession {
|
|||||||
* @return True if the data was sent, false otherwise
|
* @return True if the data was sent, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean sendBytes(String dest, byte[] data) throws DataFormatException {
|
public boolean sendBytes(String dest, byte[] data) throws DataFormatException {
|
||||||
|
if (data.length > RAW_SIZE_MAX)
|
||||||
|
throw new DataFormatException("Data size limit exceeded (" + data.length + ")");
|
||||||
return sendBytesThroughMessageSession(dest, data);
|
return sendBytesThroughMessageSession(dest, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,6 @@ public class SAMStreamSession {
|
|||||||
allprops.putAll(System.getProperties());
|
allprops.putAll(System.getProperties());
|
||||||
allprops.putAll(props);
|
allprops.putAll(props);
|
||||||
|
|
||||||
// FIXME: we should setup I2CP host and port, too
|
|
||||||
String i2cpHost = allprops.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
String i2cpHost = allprops.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
||||||
int i2cpPort = 7654;
|
int i2cpPort = 7654;
|
||||||
String port = allprops.getProperty(I2PClient.PROP_TCP_PORT, "7654");
|
String port = allprops.getProperty(I2PClient.PROP_TCP_PORT, "7654");
|
||||||
@ -113,6 +112,7 @@ public class SAMStreamSession {
|
|||||||
// streams MUST be mode=guaranteed (though i think the socket manager
|
// streams MUST be mode=guaranteed (though i think the socket manager
|
||||||
// enforces this anyway...
|
// enforces this anyway...
|
||||||
allprops.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
|
allprops.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
|
||||||
|
|
||||||
_log.debug("Creating I2PSocketManager...");
|
_log.debug("Creating I2PSocketManager...");
|
||||||
socketMgr = I2PSocketManagerFactory.createManager(destStream,
|
socketMgr = I2PSocketManagerFactory.createManager(destStream,
|
||||||
i2cpHost,
|
i2cpHost,
|
||||||
|
@ -33,7 +33,6 @@ import net.i2p.I2PAppContext;
|
|||||||
public class SAMUtils {
|
public class SAMUtils {
|
||||||
|
|
||||||
private final static Log _log = new Log(SAMUtils.class);
|
private final static Log _log = new Log(SAMUtils.class);
|
||||||
private static I2PAppContext _context = new I2PAppContext();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a random destination key
|
* Generate a random destination key
|
||||||
@ -86,7 +85,7 @@ public class SAMUtils {
|
|||||||
* @return the Destination for the specified hostname, or null if not found
|
* @return the Destination for the specified hostname, or null if not found
|
||||||
*/
|
*/
|
||||||
public static Destination lookupHost(String name, OutputStream pubKey) {
|
public static Destination lookupHost(String name, OutputStream pubKey) {
|
||||||
NamingService ns = _context.namingService();
|
NamingService ns = I2PAppContext.getGlobalContext().namingService();
|
||||||
Destination dest = ns.lookup(name);
|
Destination dest = ns.lookup(name);
|
||||||
|
|
||||||
if ((pubKey != null) && (dest != null)) {
|
if ((pubKey != null) && (dest != null)) {
|
||||||
@ -144,13 +143,13 @@ public class SAMUtils {
|
|||||||
|
|
||||||
/* Dump a Properties object in an human-readable form */
|
/* Dump a Properties object in an human-readable form */
|
||||||
private static String dumpProperties(Properties props) {
|
private static String dumpProperties(Properties props) {
|
||||||
Enumeration enum = props.propertyNames();
|
Enumeration names = props.propertyNames();
|
||||||
String msg = "";
|
String msg = "";
|
||||||
String key, val;
|
String key, val;
|
||||||
boolean firstIter = true;
|
boolean firstIter = true;
|
||||||
|
|
||||||
while (enum.hasMoreElements()) {
|
while (names.hasMoreElements()) {
|
||||||
key = (String)enum.nextElement();
|
key = (String)names.nextElement();
|
||||||
val = props.getProperty(key);
|
val = props.getProperty(key);
|
||||||
|
|
||||||
if (!firstIter) {
|
if (!firstIter) {
|
||||||
|
@ -154,10 +154,10 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
|||||||
}
|
}
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
_log.error("Caught UnsupportedEncodingException ("
|
_log.error("Caught UnsupportedEncodingException ("
|
||||||
+ e.getMessage() + ")");
|
+ e.getMessage() + ")", e);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
_log.debug("Caught IOException ("
|
_log.debug("Caught IOException ("
|
||||||
+ e.getMessage() + ")");
|
+ e.getMessage() + ")", e);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
_log.error("Unexpected exception", e);
|
_log.error("Unexpected exception", e);
|
||||||
} finally {
|
} finally {
|
||||||
@ -189,32 +189,47 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
|||||||
if ((rawSession != null) || (datagramSession != null)
|
if ((rawSession != null) || (datagramSession != null)
|
||||||
|| (streamSession != null)) {
|
|| (streamSession != null)) {
|
||||||
_log.debug("Trying to create a session, but one still exists");
|
_log.debug("Trying to create a session, but one still exists");
|
||||||
return false;
|
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Session already exists\"\n");
|
||||||
}
|
}
|
||||||
if (props == null) {
|
if (props == null) {
|
||||||
_log.debug("No parameters specified in SESSION CREATE message");
|
_log.debug("No parameters specified in SESSION CREATE message");
|
||||||
return false;
|
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No parameters for SESSION CREATE\"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
dest = props.getProperty("DESTINATION");
|
dest = props.getProperty("DESTINATION");
|
||||||
if (dest == null) {
|
if (dest == null) {
|
||||||
_log.debug("SESSION DESTINATION parameter not specified");
|
_log.debug("SESSION DESTINATION parameter not specified");
|
||||||
return false;
|
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"DESTINATION not specified\"\n");
|
||||||
}
|
}
|
||||||
props.remove("DESTINATION");
|
props.remove("DESTINATION");
|
||||||
|
|
||||||
|
String destKeystream = null;
|
||||||
|
|
||||||
if (dest.equals("TRANSIENT")) {
|
if (dest.equals("TRANSIENT")) {
|
||||||
_log.debug("TRANSIENT destination requested");
|
_log.debug("TRANSIENT destination requested");
|
||||||
ByteArrayOutputStream priv = new ByteArrayOutputStream();
|
ByteArrayOutputStream priv = new ByteArrayOutputStream(640);
|
||||||
SAMUtils.genRandomKey(priv, null);
|
SAMUtils.genRandomKey(priv, null);
|
||||||
|
|
||||||
dest = Base64.encode(priv.toByteArray());
|
destKeystream = Base64.encode(priv.toByteArray());
|
||||||
|
} else {
|
||||||
|
destKeystream = bridge.getKeystream(dest);
|
||||||
|
if (destKeystream == null) {
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Custom destination specified [" + dest + "] but it isnt know, creating a new one");
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(640);
|
||||||
|
SAMUtils.genRandomKey(baos, null);
|
||||||
|
destKeystream = Base64.encode(baos.toByteArray());
|
||||||
|
bridge.addKeystream(dest, destKeystream);
|
||||||
|
} else {
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Custom destination specified [" + dest + "] and it is already known");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String style = props.getProperty("STYLE");
|
String style = props.getProperty("STYLE");
|
||||||
if (style == null) {
|
if (style == null) {
|
||||||
_log.debug("SESSION STYLE parameter not specified");
|
_log.debug("SESSION STYLE parameter not specified");
|
||||||
return false;
|
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No SESSION STYLE specified\"\n");
|
||||||
}
|
}
|
||||||
props.remove("STYLE");
|
props.remove("STYLE");
|
||||||
|
|
||||||
@ -231,34 +246,34 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
|||||||
if (!dir.equals("CREATE") && !dir.equals("RECEIVE")
|
if (!dir.equals("CREATE") && !dir.equals("RECEIVE")
|
||||||
&& !dir.equals("BOTH")) {
|
&& !dir.equals("BOTH")) {
|
||||||
_log.debug("Unknow DIRECTION parameter value: [" + dir + "]");
|
_log.debug("Unknow DIRECTION parameter value: [" + dir + "]");
|
||||||
return false;
|
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unknown DIRECTION parameter\"\n");
|
||||||
}
|
}
|
||||||
props.remove("DIRECTION");
|
props.remove("DIRECTION");
|
||||||
|
|
||||||
streamSession = new SAMStreamSession(dest, dir,props,this);
|
streamSession = new SAMStreamSession(destKeystream, dir,props,this);
|
||||||
} else {
|
} else {
|
||||||
_log.debug("Unrecognized SESSION STYLE: \"" + style +"\"");
|
_log.debug("Unrecognized SESSION STYLE: \"" + style +"\"");
|
||||||
return false;
|
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized SESSION STYLE\"\n");
|
||||||
}
|
}
|
||||||
return writeString("SESSION STATUS RESULT=OK DESTINATION="
|
return writeString("SESSION STATUS RESULT=OK DESTINATION="
|
||||||
+ dest + "\n");
|
+ dest + "\n");
|
||||||
} else {
|
} else {
|
||||||
_log.debug("Unrecognized SESSION message opcode: \""
|
_log.debug("Unrecognized SESSION message opcode: \""
|
||||||
+ opcode + "\"");
|
+ opcode + "\"");
|
||||||
return false;
|
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized opcode\"\n");
|
||||||
}
|
}
|
||||||
} catch (DataFormatException e) {
|
} catch (DataFormatException e) {
|
||||||
_log.debug("Invalid destination specified");
|
_log.debug("Invalid destination specified");
|
||||||
return writeString("SESSION STATUS RESULT=INVALID_KEY DESTINATION=" + dest + "\n");
|
return writeString("SESSION STATUS RESULT=INVALID_KEY DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
|
||||||
} catch (I2PSessionException e) {
|
} catch (I2PSessionException e) {
|
||||||
_log.debug("I2P error when instantiating session", e);
|
_log.debug("I2P error when instantiating session", e);
|
||||||
return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + "\n");
|
return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
|
||||||
} catch (SAMException e) {
|
} catch (SAMException e) {
|
||||||
_log.error("Unexpected SAM error", e);
|
_log.error("Unexpected SAM error", e);
|
||||||
return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + "\n");
|
return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
_log.error("Unexpected IOException", e);
|
_log.error("Unexpected IOException", e);
|
||||||
return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + "\n");
|
return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,159 +498,171 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opcode.equals("SEND")) {
|
if (opcode.equals("SEND")) {
|
||||||
if (props == null) {
|
return execStreamSend(props);
|
||||||
_log.debug("No parameters specified in STREAM SEND message");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int id;
|
|
||||||
{
|
|
||||||
String strid = props.getProperty("ID");
|
|
||||||
if (strid == null) {
|
|
||||||
_log.debug("ID not specified in STREAM SEND message");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
id = Integer.parseInt(strid);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
_log.debug("Invalid STREAM SEND ID specified: " + strid);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int size;
|
|
||||||
{
|
|
||||||
String strsize = props.getProperty("SIZE");
|
|
||||||
if (strsize == null) {
|
|
||||||
_log.debug("Size not specified in STREAM SEND message");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
size = Integer.parseInt(strsize);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
_log.debug("Invalid STREAM SEND size specified: "+strsize);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!checkSize(size)) {
|
|
||||||
_log.debug("Specified size (" + size
|
|
||||||
+ ") is out of protocol limits");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
DataInputStream in = new DataInputStream(getClientSocketInputStream());
|
|
||||||
byte[] data = new byte[size];
|
|
||||||
|
|
||||||
in.readFully(data);
|
|
||||||
|
|
||||||
if (!streamSession.sendBytes(id, data)) {
|
|
||||||
_log.error("STREAM SEND failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (EOFException e) {
|
|
||||||
_log.debug("Too few bytes with RAW SEND message (expected: "
|
|
||||||
+ size);
|
|
||||||
return false;
|
|
||||||
} catch (IOException e) {
|
|
||||||
_log.debug("Caught IOException while parsing RAW SEND message",
|
|
||||||
e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (opcode.equals("CONNECT")) {
|
} else if (opcode.equals("CONNECT")) {
|
||||||
if (props == null) {
|
return execStreamConnect(props);
|
||||||
_log.debug("No parameters specified in STREAM CONNECT message");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int id;
|
|
||||||
{
|
|
||||||
String strid = props.getProperty("ID");
|
|
||||||
if (strid == null) {
|
|
||||||
_log.debug("ID not specified in STREAM SEND message");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
id = Integer.parseInt(strid);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
_log.debug("Invalid STREAM CONNECT ID specified: " +strid);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (id < 1) {
|
|
||||||
_log.debug("Invalid STREAM CONNECT ID specified: " +strid);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
props.remove("ID");
|
|
||||||
}
|
|
||||||
|
|
||||||
String dest = props.getProperty("DESTINATION");
|
|
||||||
if (dest == null) {
|
|
||||||
_log.debug("Destination not specified in RAW SEND message");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
props.remove("DESTINATION");
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!streamSession.connect(id, dest, props)) {
|
|
||||||
_log.debug("STREAM connection failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return writeString("STREAM STATUS RESULT=OK ID=" + id + "\n");
|
|
||||||
} catch (DataFormatException e) {
|
|
||||||
_log.debug("Invalid destination in STREAM CONNECT message");
|
|
||||||
return writeString("STREAM STATUS RESULT=INVALID_KEY ID="
|
|
||||||
+ id + "\n");
|
|
||||||
} catch (SAMInvalidDirectionException e) {
|
|
||||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
|
||||||
return writeString("STREAM STATUS RESULT=INVALID_DIRECTION ID="
|
|
||||||
+ id + "\n");
|
|
||||||
} catch (ConnectException e) {
|
|
||||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
|
||||||
return writeString("STREAM STATUS RESULT=CONNECTION_REFUSED ID="
|
|
||||||
+ id + "\n");
|
|
||||||
} catch (NoRouteToHostException e) {
|
|
||||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
|
||||||
return writeString("STREAM STATUS RESULT=CANT_REACH_PEER ID="
|
|
||||||
+ id + "\n");
|
|
||||||
} catch (InterruptedIOException e) {
|
|
||||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
|
||||||
return writeString("STREAM STATUS RESULT=TIMEOUT ID="
|
|
||||||
+ id + "\n");
|
|
||||||
} catch (I2PException e) {
|
|
||||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
|
||||||
return writeString("STREAM STATUS RESULT=I2P_ERROR ID="
|
|
||||||
+ id + "\n");
|
|
||||||
}
|
|
||||||
} else if (opcode.equals("CLOSE")) {
|
} else if (opcode.equals("CLOSE")) {
|
||||||
if (props == null) {
|
return execStreamClose(props);
|
||||||
_log.debug("No parameters specified in STREAM CLOSE message");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int id;
|
|
||||||
{
|
|
||||||
String strid = props.getProperty("ID");
|
|
||||||
if (strid == null) {
|
|
||||||
_log.debug("ID not specified in STREAM CLOSE message");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
id = Integer.parseInt(strid);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
_log.debug("Invalid STREAM CLOSE ID specified: " +strid);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return streamSession.closeConnection(id);
|
|
||||||
} else {
|
} else {
|
||||||
_log.debug("Unrecognized RAW message opcode: \""
|
_log.debug("Unrecognized RAW message opcode: \""
|
||||||
+ opcode + "\"");
|
+ opcode + "\"");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean execStreamSend(Properties props) {
|
||||||
|
if (props == null) {
|
||||||
|
_log.debug("No parameters specified in STREAM SEND message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int id;
|
||||||
|
{
|
||||||
|
String strid = props.getProperty("ID");
|
||||||
|
if (strid == null) {
|
||||||
|
_log.debug("ID not specified in STREAM SEND message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
id = Integer.parseInt(strid);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
_log.debug("Invalid STREAM SEND ID specified: " + strid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int size;
|
||||||
|
{
|
||||||
|
String strsize = props.getProperty("SIZE");
|
||||||
|
if (strsize == null) {
|
||||||
|
_log.debug("Size not specified in STREAM SEND message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
size = Integer.parseInt(strsize);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
_log.debug("Invalid STREAM SEND size specified: "+strsize);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!checkSize(size)) {
|
||||||
|
_log.debug("Specified size (" + size
|
||||||
|
+ ") is out of protocol limits");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DataInputStream in = new DataInputStream(getClientSocketInputStream());
|
||||||
|
byte[] data = new byte[size];
|
||||||
|
|
||||||
|
in.readFully(data);
|
||||||
|
|
||||||
|
if (!streamSession.sendBytes(id, data)) {
|
||||||
|
_log.error("STREAM SEND failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (EOFException e) {
|
||||||
|
_log.debug("Too few bytes with RAW SEND message (expected: "
|
||||||
|
+ size);
|
||||||
|
return false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
_log.debug("Caught IOException while parsing RAW SEND message",
|
||||||
|
e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean execStreamConnect(Properties props) {
|
||||||
|
if (props == null) {
|
||||||
|
_log.debug("No parameters specified in STREAM CONNECT message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int id;
|
||||||
|
{
|
||||||
|
String strid = props.getProperty("ID");
|
||||||
|
if (strid == null) {
|
||||||
|
_log.debug("ID not specified in STREAM SEND message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
id = Integer.parseInt(strid);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
_log.debug("Invalid STREAM CONNECT ID specified: " +strid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (id < 1) {
|
||||||
|
_log.debug("Invalid STREAM CONNECT ID specified: " +strid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
props.remove("ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
String dest = props.getProperty("DESTINATION");
|
||||||
|
if (dest == null) {
|
||||||
|
_log.debug("Destination not specified in RAW SEND message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
props.remove("DESTINATION");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!streamSession.connect(id, dest, props)) {
|
||||||
|
_log.debug("STREAM connection failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return writeString("STREAM STATUS RESULT=OK ID=" + id + "\n");
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
_log.debug("Invalid destination in STREAM CONNECT message");
|
||||||
|
return writeString("STREAM STATUS RESULT=INVALID_KEY ID="
|
||||||
|
+ id + "\n");
|
||||||
|
} catch (SAMInvalidDirectionException e) {
|
||||||
|
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||||
|
return writeString("STREAM STATUS RESULT=INVALID_DIRECTION ID="
|
||||||
|
+ id + "\n");
|
||||||
|
} catch (ConnectException e) {
|
||||||
|
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||||
|
return writeString("STREAM STATUS RESULT=CONNECTION_REFUSED ID="
|
||||||
|
+ id + "\n");
|
||||||
|
} catch (NoRouteToHostException e) {
|
||||||
|
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||||
|
return writeString("STREAM STATUS RESULT=CANT_REACH_PEER ID="
|
||||||
|
+ id + "\n");
|
||||||
|
} catch (InterruptedIOException e) {
|
||||||
|
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||||
|
return writeString("STREAM STATUS RESULT=TIMEOUT ID="
|
||||||
|
+ id + "\n");
|
||||||
|
} catch (I2PException e) {
|
||||||
|
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||||
|
return writeString("STREAM STATUS RESULT=I2P_ERROR ID="
|
||||||
|
+ id + "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean execStreamClose(Properties props) {
|
||||||
|
if (props == null) {
|
||||||
|
_log.debug("No parameters specified in STREAM CLOSE message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int id;
|
||||||
|
{
|
||||||
|
String strid = props.getProperty("ID");
|
||||||
|
if (strid == null) {
|
||||||
|
_log.debug("ID not specified in STREAM CLOSE message");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
id = Integer.parseInt(strid);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
_log.debug("Invalid STREAM CLOSE ID specified: " +strid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return streamSession.closeConnection(id);
|
||||||
|
}
|
||||||
|
|
||||||
/* Check whether a size is inside the limits allowed by this protocol */
|
/* Check whether a size is inside the limits allowed by this protocol */
|
||||||
private boolean checkSize(int size) {
|
private boolean checkSize(int size) {
|
||||||
|
Reference in New Issue
Block a user