- 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:
zzz
2014-07-22 14:52:08 +00:00
parent cca5bef8c1
commit 328d7d0008
10 changed files with 72 additions and 48 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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