* 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:
jrandom
2004-05-03 11:34:38 +00:00
committed by zzz
parent 2585460286
commit 2156f4c2f3
8 changed files with 281 additions and 194 deletions

View File

@ -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();
} }
} }

View File

@ -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);

View File

@ -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;
} }
/** /**

View File

@ -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;
} }

View File

@ -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);
} }

View File

@ -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,

View File

@ -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) {

View File

@ -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) {