forked from I2P_Developers/i2p.i2p
SAM:
- Don't spawn a thread for each transmitted datagram - Set protocol field for raw and signed datagrams - Enforce a 60s timeout for HELLO - Use naming service cache to reduce Destination object churn - Get Log object from the log manager - Log spelling fixes
This commit is contained in:
@@ -161,6 +161,7 @@ public class SAMBridge implements Runnable, ClientApp {
|
||||
*
|
||||
* @param name name of the destination
|
||||
* @return null if the name does not exist, or if it is improperly formatted
|
||||
* @deprecated unused
|
||||
*/
|
||||
public Destination getDestination(String name) {
|
||||
synchronized (nameToPrivKeys) {
|
||||
|
@@ -12,6 +12,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.client.datagram.I2PDatagramDissector;
|
||||
import net.i2p.client.datagram.I2PDatagramMaker;
|
||||
@@ -88,7 +89,9 @@ class SAMDatagramSession extends SAMMessageSession {
|
||||
synchronized (dgramMaker) {
|
||||
dgram = dgramMaker.makeI2PDatagram(data);
|
||||
}
|
||||
return sendBytesThroughMessageSession(dest, dgram);
|
||||
// TODO pass ports through
|
||||
return sendBytesThroughMessageSession(dest, dgram, I2PSession.PROTO_DATAGRAM,
|
||||
I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
|
||||
}
|
||||
|
||||
protected void messageReceived(byte[] msg) {
|
||||
|
@@ -13,6 +13,7 @@ import java.nio.channels.SocketChannel;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
@@ -54,7 +55,7 @@ abstract class SAMHandler implements Runnable {
|
||||
*/
|
||||
protected SAMHandler(SocketChannel s,
|
||||
int verMajor, int verMinor, Properties i2cpProps) throws IOException {
|
||||
_log = new Log(getClass());
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
|
||||
socket = s;
|
||||
|
||||
this.verMajor = verMajor;
|
||||
|
@@ -9,12 +9,13 @@ package net.i2p.sam;
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.VersionComparator;
|
||||
@@ -26,6 +27,8 @@ class SAMHandlerFactory {
|
||||
|
||||
private static final String VERSION = "3.1";
|
||||
|
||||
private static final int HELLO_TIMEOUT = 60*1000;
|
||||
|
||||
/**
|
||||
* Return the right SAM handler depending on the protocol version
|
||||
* required by the client.
|
||||
@@ -36,17 +39,21 @@ class SAMHandlerFactory {
|
||||
* @return A SAM protocol handler, or null if the client closed before the handshake
|
||||
*/
|
||||
public static SAMHandler createSAMHandler(SocketChannel s, Properties i2cpProps) throws SAMException {
|
||||
String line;
|
||||
StringTokenizer tok;
|
||||
Log log = new Log(SAMHandlerFactory.class);
|
||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(SAMHandlerFactory.class);
|
||||
|
||||
try {
|
||||
line = DataHelper.readLine(s.socket().getInputStream());
|
||||
Socket sock = s.socket();
|
||||
sock.setSoTimeout(HELLO_TIMEOUT);
|
||||
String line = DataHelper.readLine(sock.getInputStream());
|
||||
sock.setSoTimeout(0);
|
||||
if (line == null) {
|
||||
log.debug("Connection closed by client");
|
||||
return null;
|
||||
}
|
||||
tok = new StringTokenizer(line.trim(), " ");
|
||||
} catch (SocketTimeoutException e) {
|
||||
throw new SAMException("Timeout waiting for HELLO VERSION", e);
|
||||
} catch (IOException e) {
|
||||
throw new SAMException("Error reading from socket", e);
|
||||
} catch (Exception e) {
|
||||
|
@@ -13,6 +13,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.client.I2PSession;
|
||||
@@ -33,10 +34,8 @@ import net.i2p.util.Log;
|
||||
abstract class SAMMessageSession {
|
||||
|
||||
protected final Log _log;
|
||||
|
||||
private I2PSession session = null;
|
||||
|
||||
private SAMMessageSessionHandler handler = null;
|
||||
private I2PSession session;
|
||||
private SAMMessageSessionHandler handler;
|
||||
|
||||
/**
|
||||
* Initialize a new SAM message-based session.
|
||||
@@ -48,7 +47,7 @@ abstract class SAMMessageSession {
|
||||
* @throws I2PSessionException
|
||||
*/
|
||||
protected SAMMessageSession(String dest, Properties props) throws IOException, DataFormatException, I2PSessionException {
|
||||
_log = new Log(getClass());
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(dest));
|
||||
initSAMMessageSession(bais, props);
|
||||
}
|
||||
@@ -103,11 +102,15 @@ abstract class SAMMessageSession {
|
||||
*
|
||||
* @param dest Destination
|
||||
* @param data Bytes to be sent
|
||||
* @param proto I2CP protocol
|
||||
* @param fromPort I2CP from port
|
||||
* @param toPort I2CP to port
|
||||
*
|
||||
* @return True if the data was sent, false otherwise
|
||||
* @throws DataFormatException
|
||||
* @throws DataFormatException on unknown / bad dest
|
||||
*/
|
||||
protected boolean sendBytesThroughMessageSession(String dest, byte[] data) throws DataFormatException {
|
||||
protected boolean sendBytesThroughMessageSession(String dest, byte[] data,
|
||||
int proto, int fromPort, int toPort) throws DataFormatException {
|
||||
Destination d = SAMUtils.getDest(dest);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
@@ -115,7 +118,7 @@ abstract class SAMMessageSession {
|
||||
}
|
||||
|
||||
try {
|
||||
return session.sendMessage(d, data);
|
||||
return session.sendMessage(d, data, proto, fromPort, toPort);
|
||||
} catch (I2PSessionException e) {
|
||||
_log.error("I2PSessionException while sending data", e);
|
||||
return false;
|
||||
|
@@ -12,6 +12,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.util.Log;
|
||||
@@ -73,7 +74,9 @@ class SAMRawSession extends SAMMessageSession {
|
||||
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);
|
||||
// TODO pass ports through
|
||||
return sendBytesThroughMessageSession(dest, data, I2PSession.PROTO_DATAGRAM_RAW,
|
||||
I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
|
||||
}
|
||||
|
||||
protected void messageReceived(byte[] msg) {
|
||||
|
@@ -25,6 +25,7 @@ import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
@@ -107,9 +108,9 @@ class SAMStreamSession {
|
||||
public SAMStreamSession(InputStream destStream, String dir,
|
||||
Properties props, SAMStreamReceiver recv) throws IOException, DataFormatException, SAMException {
|
||||
this.recv = recv;
|
||||
_log = new Log(getClass());
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("SAM STREAM session instantiated");
|
||||
_log.debug("SAM STREAM session instantiated");
|
||||
|
||||
Properties allprops = (Properties) System.getProperties().clone();
|
||||
allprops.putAll(props);
|
||||
|
@@ -123,26 +123,12 @@ class SAMUtils {
|
||||
* Resolved the specified hostname.
|
||||
*
|
||||
* @param name Hostname to be resolved
|
||||
* @param pubKey A stream to write the Destination public key (may be null)
|
||||
*
|
||||
* @return the Destination for the specified hostname, or null if not found
|
||||
*/
|
||||
public static Destination lookupHost(String name, OutputStream pubKey) {
|
||||
private static Destination lookupHost(String name) {
|
||||
NamingService ns = I2PAppContext.getGlobalContext().namingService();
|
||||
Destination dest = ns.lookup(name);
|
||||
|
||||
if ((pubKey != null) && (dest != null)) {
|
||||
try {
|
||||
dest.writeBytes(pubKey);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} catch (DataFormatException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
@@ -151,20 +137,26 @@ class SAMUtils {
|
||||
*
|
||||
* @param s Hostname or key to be resolved
|
||||
*
|
||||
* @return the Destination for the specified hostname, or null if not found
|
||||
* @return the Destination for the specified hostname, non-null
|
||||
* @throws DataFormatException on bad Base 64 or name 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 ;
|
||||
// NamingService caches b64 so just use it for everything
|
||||
// TODO: Add a static local cache here so SAM doesn't flush the
|
||||
// NamingService cache
|
||||
Destination d = lookupHost(s);
|
||||
if (d == null) {
|
||||
String msg;
|
||||
if (s.length() >= 516)
|
||||
msg = "Bad Base64 dest: ";
|
||||
else if (s.length() == 60 && s.endsWith(".b32.i2p"))
|
||||
msg = "Lease set not found: ";
|
||||
else
|
||||
msg = "Host name not found: ";
|
||||
throw new DataFormatException(msg + s);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -246,7 +246,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
||||
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");
|
||||
_log.debug("Custom destination specified [" + dest + "] but it isn't known, creating a new one");
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(640);
|
||||
SAMUtils.genRandomKey(baos, null);
|
||||
destKeystream = Base64.encode(baos.toByteArray());
|
||||
@@ -284,7 +284,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
||||
if (!dir.equals("CREATE") && !dir.equals("RECEIVE")
|
||||
&& !dir.equals("BOTH")) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Unknow DIRECTION parameter value: [" + dir + "]");
|
||||
_log.debug("Unknown DIRECTION parameter value: [" + dir + "]");
|
||||
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unknown DIRECTION parameter\"\n");
|
||||
}
|
||||
props.remove("DIRECTION");
|
||||
|
@@ -25,6 +25,7 @@ import java.util.Properties;
|
||||
import java.util.HashMap;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
@@ -159,7 +160,13 @@ class SAMv3Handler extends SAMv1Handler
|
||||
ByteBuffer outBuf = ByteBuffer.wrap(new byte[inBuf.remaining()]);
|
||||
outBuf.put(inBuf);
|
||||
outBuf.flip();
|
||||
new I2PAppThread(new MessageDispatcher(outBuf.array()), "MessageDispatcher").start();
|
||||
// A new thread for every message is wildly inefficient...
|
||||
//new I2PAppThread(new MessageDispatcher(outBuf.array()), "MessageDispatcher").start();
|
||||
// inline
|
||||
// Even though we could be sending messages through multiple sessions,
|
||||
// that isn't a common use case, and blocking should be rare.
|
||||
// Inside router context, I2CP drops on overflow.
|
||||
(new MessageDispatcher(outBuf.array())).run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,9 +201,15 @@ class SAMv3Handler extends SAMv1Handler
|
||||
SessionRecord rec = sSessionsHash.get(nick);
|
||||
if (rec!=null) {
|
||||
rec.getHandler().session.sendBytes(dest,data);
|
||||
} else {
|
||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(SAMv3Handler.class);
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Dropping datagram, no session for " + nick);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// FIXME log? throw?
|
||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(SAMv3Handler.class);
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn("Error handling datagram", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user