forked from I2P_Developers/i2p.i2p
I2CP Multisession support and multiple destinations in one tunnel pool.
Work in progress. Router-side I2CP mostly done. Client-side I2CP mostly done but undecided on how to handle listeners. Streaming stubbed out but may be wrong, may need multiple socket managers, not clear how to proceed. I2PTunnel not started. Blacklist of DSA-only dests not started. Router leaseset publishing not correct. Not clear whether to have additional tunnel pools with flags, or put the tunnel pools into the client hashmap twice. Client config contains destination, may need to move that to tunnel pool.
This commit is contained in:
@@ -9,6 +9,8 @@ package net.i2p.client;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -21,7 +23,7 @@ import net.i2p.data.SigningPrivateKey;
|
||||
/**
|
||||
* <p>Define the standard means of sending and receiving messages on the
|
||||
* I2P network by using the I2CP (the client protocol). This is done over a
|
||||
* bidirectional TCP socket and never sends any private keys.
|
||||
* bidirectional TCP socket.
|
||||
*
|
||||
* End to end encryption in I2PSession was disabled in release 0.6.
|
||||
*
|
||||
@@ -247,6 +249,27 @@ public interface I2PSession {
|
||||
*
|
||||
*/
|
||||
public void destroySession() throws I2PSessionException;
|
||||
|
||||
/**
|
||||
* @return a new subsession, non-null
|
||||
* @param privateKeyStream null for transient, if non-null must have same encryption keys as primary session
|
||||
* and different signing keys
|
||||
* @param opts subsession options if any, may be null
|
||||
* @since 0.9.19
|
||||
*/
|
||||
public I2PSession addSubsession(InputStream privateKeyStream, Properties opts) throws I2PSessionException;
|
||||
|
||||
/**
|
||||
* @return a list of subsessions, non-null, does not include the primary session
|
||||
* @since 0.9.19
|
||||
*/
|
||||
public void removeSubsession(I2PSession session);
|
||||
|
||||
/**
|
||||
* @return a list of subsessions, non-null, does not include the primary session
|
||||
* @since 0.9.19
|
||||
*/
|
||||
public List<I2PSession> getSubsessions();
|
||||
|
||||
/**
|
||||
* Actually connect the session and start receiving/sending messages
|
||||
|
@@ -23,6 +23,7 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@@ -43,6 +44,7 @@ import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.I2CPMessageReader;
|
||||
import net.i2p.data.i2cp.MessagePayloadMessage;
|
||||
import net.i2p.data.i2cp.SessionId;
|
||||
import net.i2p.data.i2cp.SessionStatusMessage;
|
||||
import net.i2p.internal.I2CPMessageQueue;
|
||||
import net.i2p.internal.InternalClientManager;
|
||||
import net.i2p.internal.QueuedI2CPMessageReader;
|
||||
@@ -76,6 +78,15 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
/** currently granted lease set, or null */
|
||||
private volatile LeaseSet _leaseSet;
|
||||
|
||||
// subsession stuff
|
||||
// registered subsessions
|
||||
private final List<SubSession> _subsessions;
|
||||
// established subsessions
|
||||
private final ConcurrentHashMap<SessionId, SubSession> _subsessionMap;
|
||||
private final Object _subsessionLock = new Object();
|
||||
private static final String MIN_SUBSESSION_VERSION = "0.9.19";
|
||||
private volatile boolean _routerSupportsSubsessions;
|
||||
|
||||
/** hostname of router - will be null if in RouterContext */
|
||||
protected final String _hostname;
|
||||
/** port num to router - will be 0 if in RouterContext */
|
||||
@@ -179,6 +190,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
TEST_LOOKUP ||
|
||||
(routerVersion != null && routerVersion.length() > 0 &&
|
||||
VersionComparator.comp(routerVersion, MIN_HOST_LOOKUP_VERSION) >= 0);
|
||||
_routerSupportsSubsessions = _context.isRouterContext() ||
|
||||
(routerVersion != null && routerVersion.length() > 0 &&
|
||||
VersionComparator.comp(routerVersion, MIN_SUBSESSION_VERSION) >= 0);
|
||||
synchronized (_stateLock) {
|
||||
if (_state == State.OPENING) {
|
||||
_state = State.GOTDATE;
|
||||
@@ -196,18 +210,42 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
*/
|
||||
protected I2PSessionImpl(I2PAppContext context, Properties options,
|
||||
I2PClientMessageHandlerMap handlerMap) {
|
||||
this(context, options, handlerMap, false);
|
||||
this(context, options, handlerMap, null, false);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For extension by SubSession via I2PSessionMuxedImpl and I2PSessionImpl2
|
||||
*
|
||||
* @param destKeyStream stream containing the private key data,
|
||||
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||
* @param options set of options to configure the router with, if null will use System properties
|
||||
* @since 0.9.19
|
||||
*/
|
||||
protected I2PSessionImpl(I2PSessionImpl primary, InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
this(primary.getContext(), options, primary.getHandlerMap(), primary.getProducer(), true);
|
||||
_availabilityNotifier = new AvailabilityNotifier();
|
||||
try {
|
||||
readDestination(destKeyStream);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2PSessionException("Error reading the destination key stream", dfe);
|
||||
} catch (IOException ioe) {
|
||||
throw new I2PSessionException("Error reading the destination key stream", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic setup of finals
|
||||
* @since 0.9.7
|
||||
*/
|
||||
private I2PSessionImpl(I2PAppContext context, Properties options,
|
||||
I2PClientMessageHandlerMap handlerMap, boolean hasDest) {
|
||||
I2PClientMessageHandlerMap handlerMap,
|
||||
I2CPMessageProducer producer,
|
||||
boolean hasDest) {
|
||||
_context = context;
|
||||
_handlerMap = handlerMap;
|
||||
_log = context.logManager().getLog(getClass());
|
||||
_subsessions = new CopyOnWriteArrayList<SubSession>();
|
||||
_subsessionMap = new ConcurrentHashMap<SessionId, SubSession>(4);
|
||||
if (options == null)
|
||||
options = (Properties) System.getProperties().clone();
|
||||
_options = loadConfig(options);
|
||||
@@ -215,7 +253,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
_portNum = getPort();
|
||||
_fastReceive = Boolean.parseBoolean(_options.getProperty(I2PClient.PROP_FAST_RECEIVE));
|
||||
if (hasDest) {
|
||||
_producer = new I2CPMessageProducer(context);
|
||||
_producer = producer;
|
||||
_availableMessages = new ConcurrentHashMap<Long, MessagePayloadMessage>();
|
||||
_myDestination = new Destination();
|
||||
_privateKey = new PrivateKey();
|
||||
@@ -238,10 +276,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
* @param destKeyStream stream containing the private key data,
|
||||
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||
* @param options set of options to configure the router with, if null will use System properties
|
||||
* @throws I2PSessionException if there is a problem loading the private keys or
|
||||
* @throws I2PSessionException if there is a problem loading the private keys
|
||||
*/
|
||||
public I2PSessionImpl(I2PAppContext context, InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
this(context, options, new I2PClientMessageHandlerMap(context), true);
|
||||
this(context, options, new I2PClientMessageHandlerMap(context), new I2CPMessageProducer(context), true);
|
||||
_availabilityNotifier = new AvailabilityNotifier();
|
||||
try {
|
||||
readDestination(destKeyStream);
|
||||
@@ -251,6 +289,66 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
throw new I2PSessionException("Error reading the destination key stream", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Router must be connected or was connected... for now.
|
||||
*
|
||||
* @return a new subsession, non-null
|
||||
* @param privateKeyStream null for transient, if non-null must have same encryption keys as primary session
|
||||
* and different signing keys
|
||||
* @param opts subsession options if any, may be null
|
||||
* @since 0.9.19
|
||||
*/
|
||||
public I2PSession addSubsession(InputStream privateKeyStream, Properties opts) throws I2PSessionException {
|
||||
if (!_routerSupportsSubsessions)
|
||||
throw new I2PSessionException("Router does not support subsessions");
|
||||
SubSession sub;
|
||||
synchronized(_subsessionLock) {
|
||||
if (_subsessions.size() > _subsessionMap.size())
|
||||
throw new I2PSessionException("Subsession request already pending");
|
||||
sub = new SubSession(this, privateKeyStream, opts);
|
||||
for (SubSession ss : _subsessions) {
|
||||
if (ss.getDecryptionKey().equals(sub.getDecryptionKey()) &&
|
||||
ss.getPrivateKey().equals(sub.getPrivateKey())) {
|
||||
throw new I2PSessionException("Dup subsession");
|
||||
}
|
||||
}
|
||||
_subsessions.add(sub);
|
||||
}
|
||||
|
||||
synchronized (_stateLock) {
|
||||
if (_state == State.OPEN) {
|
||||
_producer.connect(sub);
|
||||
} // else will be called in connect()
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.19
|
||||
*/
|
||||
public void removeSubsession(I2PSession session) {
|
||||
if (!(session instanceof SubSession))
|
||||
return;
|
||||
synchronized(_subsessionLock) {
|
||||
_subsessions.remove(session);
|
||||
SessionId id = ((SubSession) session).getSessionId();
|
||||
if (id != null)
|
||||
_subsessionMap.remove(id);
|
||||
/// tell the subsession
|
||||
///....
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list of subsessions, non-null, does not include the primary session
|
||||
* @since 0.9.19
|
||||
*/
|
||||
public List<I2PSession> getSubsessions() {
|
||||
synchronized(_subsessionLock) {
|
||||
return new ArrayList<I2PSession>(_subsessions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the config for anything we know about.
|
||||
@@ -536,6 +634,14 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
startIdleMonitor();
|
||||
startVerifyUsage();
|
||||
success = true;
|
||||
|
||||
// now send CreateSessionMessages for all subsessions, one at a time, must wait for each response
|
||||
synchronized(_subsessionLock) {
|
||||
for (SubSession ss : _subsessions) {
|
||||
_producer.connect(ss);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (InterruptedException ie) {
|
||||
throw new I2PSessionException("Interrupted", ie);
|
||||
} catch (UnknownHostException uhe) {
|
||||
@@ -739,19 +845,80 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
/**
|
||||
* The I2CPMessageEventListener callback.
|
||||
* Recieve notification of some I2CP message and handle it if possible.
|
||||
*
|
||||
* We route the message based on message type AND session ID.
|
||||
*
|
||||
* The following types never contain a session ID and are not routable to
|
||||
* a subsession:
|
||||
* BandwidthLimitsMessage, DestReplyMessage
|
||||
*
|
||||
* The following types may not ontain a valid session ID
|
||||
* even when intended for a subsession, so we must take special care:
|
||||
* SessionStatusMessage
|
||||
*
|
||||
* @param reader unused
|
||||
*/
|
||||
public void messageReceived(I2CPMessageReader reader, I2CPMessage message) {
|
||||
I2CPMessageHandler handler = _handlerMap.getHandler(message.getType());
|
||||
if (handler == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix() + "Unknown message or unhandleable message received: type = "
|
||||
+ message.getType());
|
||||
int type = message.getType();
|
||||
SessionId id = message.sessionId();
|
||||
if (id == null || id.equals(_sessionId) ||
|
||||
(_sessionId == null && id != null && type == SessionStatusMessage.MESSAGE_TYPE)) {
|
||||
// it's for us
|
||||
I2CPMessageHandler handler = _handlerMap.getHandler(type);
|
||||
if (handler != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Message received of type " + type
|
||||
+ " to be handled by " + handler.getClass().getSimpleName());
|
||||
handler.handleMessage(message, this);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix() + "Unknown message or unhandleable message received: type = "
|
||||
+ type);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Message received of type " + message.getType()
|
||||
+ " to be handled by " + handler.getClass().getSimpleName());
|
||||
handler.handleMessage(message, this);
|
||||
SubSession sub = _subsessionMap.get(id);
|
||||
if (sub != null) {
|
||||
// it's for a subsession
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Message received of type " + type
|
||||
+ " to be handled by " + sub);
|
||||
sub.messageReceived(reader, message);
|
||||
} else if (id != null && type == SessionStatusMessage.MESSAGE_TYPE) {
|
||||
// look for a subsession without a session
|
||||
synchronized (_subsessionLock) {
|
||||
for (SubSession sess : _subsessions) {
|
||||
if (sess.getSessionId() == null) {
|
||||
sub.messageReceived(reader, message);
|
||||
id = sess.getSessionId();
|
||||
if (id != null) {
|
||||
if (id.equals(_sessionId)) {
|
||||
// shouldnt happen
|
||||
sess.setSessionId(null);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Dup or our session id " + id);
|
||||
} else {
|
||||
SubSession old = _subsessionMap.putIfAbsent(id, sess);
|
||||
if (old != null) {
|
||||
// shouldnt happen
|
||||
sess.setSessionId(null);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Dup session id " + id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix() + "No session " + id + " to handle message: type = "
|
||||
+ type);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// it's for nobody
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix() + "No session " + id + " to handle message: type = "
|
||||
+ type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -786,6 +953,18 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
*/
|
||||
I2CPMessageProducer getProducer() { return _producer; }
|
||||
|
||||
/**
|
||||
* For Subsessions
|
||||
* @since 0.9.19
|
||||
*/
|
||||
I2PClientMessageHandlerMap getHandlerMap() { return _handlerMap; }
|
||||
|
||||
/**
|
||||
* For Subsessions
|
||||
* @since 0.9.19
|
||||
*/
|
||||
I2PAppContext getContext() { return _context; }
|
||||
|
||||
/**
|
||||
* Retrieve the configuration options
|
||||
* @return non-null, if insantiated with null options, this will be the System properties.
|
||||
@@ -803,7 +982,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
|
||||
/**
|
||||
* Has the session been closed (or not yet connected)?
|
||||
* False when open and during transitions. Unsynchronized.
|
||||
* False when open and during transitions.
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
synchronized (_stateLock) {
|
||||
@@ -892,6 +1071,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
if (_availabilityNotifier != null)
|
||||
_availabilityNotifier.stopNotifying();
|
||||
closeSocket();
|
||||
_subsessionMap.clear();
|
||||
if (_sessionListener != null) _sessionListener.disconnected(this);
|
||||
}
|
||||
|
||||
|
@@ -50,9 +50,9 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
|
||||
private static final long REMOVE_EXPIRED_TIME = 63*1000;
|
||||
|
||||
/**
|
||||
* for extension by SimpleSession (no dest)
|
||||
*/
|
||||
/**
|
||||
* for extension by SimpleSession (no dest)
|
||||
*/
|
||||
protected I2PSessionImpl2(I2PAppContext context, Properties options,
|
||||
I2PClientMessageHandlerMap handlerMap) {
|
||||
super(context, options, handlerMap);
|
||||
@@ -61,15 +61,17 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
}
|
||||
|
||||
/**
|
||||
* for extension by I2PSessionMuxedImpl
|
||||
*
|
||||
* Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey
|
||||
* from the destKeyStream, and using the specified options to connect to the router
|
||||
*
|
||||
* @param destKeyStream stream containing the private key data,
|
||||
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||
* @param options set of options to configure the router with, if null will use System properties
|
||||
* @throws I2PSessionException if there is a problem loading the private keys or
|
||||
* @throws I2PSessionException if there is a problem loading the private keys
|
||||
*/
|
||||
public I2PSessionImpl2(I2PAppContext ctx, InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
protected I2PSessionImpl2(I2PAppContext ctx, InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
super(ctx, destKeyStream, options);
|
||||
_sendingStates = new ConcurrentHashMap<Long, MessageState>(32);
|
||||
_sendMessageNonce = new AtomicLong();
|
||||
@@ -94,6 +96,26 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
_context.statManager().createRateStat("i2cp.tx.msgExpanded", "size before compression", "i2cp", new long[] { 30*60*1000 });
|
||||
}
|
||||
|
||||
/*
|
||||
* For extension by SubSession via I2PSessionMuxedImpl
|
||||
*
|
||||
* @param destKeyStream stream containing the private key data,
|
||||
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||
* @param options set of options to configure the router with, if null will use System properties
|
||||
* @since 0.9.19
|
||||
*/
|
||||
protected I2PSessionImpl2(I2PSessionImpl primary, InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
super(primary, destKeyStream, options);
|
||||
_sendingStates = new ConcurrentHashMap<Long, MessageState>(32);
|
||||
_sendMessageNonce = new AtomicLong();
|
||||
_noEffort = "none".equals(getOptions().getProperty(I2PClient.PROP_RELIABILITY, "").toLowerCase(Locale.US));
|
||||
_context.statManager().createRateStat("i2cp.receiveStatusTime.1", "How long it took to get status=1 back", "i2cp", new long[] { 10*60*1000 });
|
||||
_context.statManager().createRateStat("i2cp.receiveStatusTime.4", "How long it took to get status=4 back", "i2cp", new long[] { 10*60*1000 });
|
||||
_context.statManager().createRateStat("i2cp.receiveStatusTime.5", "How long it took to get status=5 back", "i2cp", new long[] { 10*60*1000 });
|
||||
_context.statManager().createRateStat("i2cp.tx.msgCompressed", "compressed size transferred", "i2cp", new long[] { 30*60*1000 });
|
||||
_context.statManager().createRateStat("i2cp.tx.msgExpanded", "size before compression", "i2cp", new long[] { 30*60*1000 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire up a periodic task to check for unclaimed messages
|
||||
* @since 0.9.14
|
||||
|
@@ -82,6 +82,24 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
|
||||
// discards the one in super(), sorry about that... (no it wasn't started yet)
|
||||
_availabilityNotifier = new MuxedAvailabilityNotifier();
|
||||
}
|
||||
|
||||
/*
|
||||
* For extension by SubSession
|
||||
*
|
||||
* @param destKeyStream stream containing the private key data,
|
||||
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||
* @param options set of options to configure the router with, if null will use System properties
|
||||
* @since 0.9.19
|
||||
*/
|
||||
protected I2PSessionMuxedImpl(I2PSessionImpl primary, InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
super(primary, destKeyStream, options);
|
||||
// also stored in _sessionListener but we keep it in _demultipexer
|
||||
// as well so we don't have to keep casting
|
||||
_demultiplexer = new I2PSessionDemultiplexer(primary.getContext());
|
||||
super.setSessionListener(_demultiplexer);
|
||||
// discards the one in super(), sorry about that... (no it wasn't started yet)
|
||||
_availabilityNotifier = new MuxedAvailabilityNotifier();
|
||||
}
|
||||
|
||||
/** listen on all protocols and ports */
|
||||
@Override
|
||||
|
299
core/java/src/net/i2p/client/SubSession.java
Normal file
299
core/java/src/net/i2p/client/SubSession.java
Normal file
@@ -0,0 +1,299 @@
|
||||
package net.i2p.client;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2003 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.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.SessionId;
|
||||
|
||||
/**
|
||||
* An additional session using another session's connection.
|
||||
*
|
||||
* A subsession uses the same connection to the router as the primary session,
|
||||
* but has a different Destination. It uses the same tunnels as the primary
|
||||
* but has its own leaseset. It must use the same encryption keys as the primary
|
||||
* so that garlic encryption/decryption works.
|
||||
*
|
||||
* The message handler map and message producer are reused from primary.
|
||||
*
|
||||
* Does NOT reuse the session listener ????
|
||||
*
|
||||
* While the I2CP protocol, in theory, allows for fully independent sessions
|
||||
* over the same I2CP connection, this is not currently supported by the router.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
class SubSession extends I2PSessionMuxedImpl {
|
||||
private final I2PSessionMuxedImpl _primary;
|
||||
|
||||
/**
|
||||
* @param primary must be a I2PSessionMuxedImpl
|
||||
*/
|
||||
public SubSession(I2PSession primary, InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
super((I2PSessionMuxedImpl)primary, destKeyStream, options);
|
||||
_primary = (I2PSessionMuxedImpl) primary;
|
||||
if (!getDecryptionKey().equals(_primary.getDecryptionKey()))
|
||||
throw new I2PSessionException("encryption key mismatch");
|
||||
if (getPrivateKey().equals(_primary.getPrivateKey()))
|
||||
throw new I2PSessionException("signing key must differ");
|
||||
// state management
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsupported in a subsession.
|
||||
* @throws UnsupportedOperationException always
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public I2PSession addSubsession(InputStream destKeyStream, Properties opts) throws I2PSessionException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsupported in a subsession.
|
||||
* Does nothing.
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public void removeSubsession(I2PSession session) {}
|
||||
|
||||
/**
|
||||
* Unsupported in a subsession.
|
||||
* @return empty list always
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public List<I2PSession> getSubsessions() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing for now
|
||||
*/
|
||||
@Override
|
||||
public void updateOptions(Properties options) {}
|
||||
|
||||
/**
|
||||
* Connect to the router and establish a session. This call blocks until
|
||||
* a session is granted.
|
||||
*
|
||||
* Should be threadsafe, other threads will block until complete.
|
||||
* Disconnect / destroy from another thread may be called simultaneously and
|
||||
* will (should?) interrupt the connect.
|
||||
*
|
||||
* @throws I2PSessionException if there is a configuration error or the router is
|
||||
* not reachable
|
||||
*/
|
||||
@Override
|
||||
public void connect() throws I2PSessionException {
|
||||
_primary.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the session been closed (or not yet connected)?
|
||||
* False when open and during transitions.
|
||||
*/
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return getSessionId() == null || _primary.isClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deliver an I2CP message to the router
|
||||
* May block for several seconds if the write queue to the router is full
|
||||
*
|
||||
* @throws I2PSessionException if the message is malformed or there is an error writing it out
|
||||
*/
|
||||
@Override
|
||||
void sendMessage(I2CPMessage message) throws I2PSessionException {
|
||||
if (isClosed())
|
||||
throw new I2PSessionException("Already closed");
|
||||
_primary.sendMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass off the error to the listener
|
||||
* Misspelled, oh well.
|
||||
* @param error non-null
|
||||
*/
|
||||
@Override
|
||||
void propogateError(String msg, Throwable error) {
|
||||
_primary.propogateError(msg, error);
|
||||
if (_sessionListener != null) _sessionListener.errorOccurred(this, msg, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tear down the session, and do NOT reconnect.
|
||||
*
|
||||
* Blocks if session has not been fully started.
|
||||
*/
|
||||
@Override
|
||||
public void destroySession() {
|
||||
_primary.destroySession();
|
||||
if (_availabilityNotifier != null)
|
||||
_availabilityNotifier.stopNotifying();
|
||||
if (_sessionListener != null) _sessionListener.disconnected(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will interrupt a connect in progress.
|
||||
*/
|
||||
@Override
|
||||
protected void disconnect() {
|
||||
_primary.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean reconnect() {
|
||||
return _primary.reconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the message handler
|
||||
* on reception of DestReplyMessage
|
||||
*
|
||||
* This will never happen, as the dest reply message does not contain a session ID.
|
||||
*/
|
||||
@Override
|
||||
void destReceived(Destination d) {
|
||||
_primary.destReceived(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the message handler
|
||||
* on reception of DestReplyMessage
|
||||
*
|
||||
* This will never happen, as the dest reply message does not contain a session ID.
|
||||
*
|
||||
* @param h non-null
|
||||
*/
|
||||
@Override
|
||||
void destLookupFailed(Hash h) {
|
||||
_primary.destLookupFailed(h);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the message handler
|
||||
* on reception of HostReplyMessage
|
||||
* @param d non-null
|
||||
*/
|
||||
void destReceived(long nonce, Destination d) {
|
||||
_primary.destReceived(nonce, d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the message handler
|
||||
* on reception of HostReplyMessage
|
||||
*/
|
||||
@Override
|
||||
void destLookupFailed(long nonce) {
|
||||
_primary.destLookupFailed(nonce);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the message handler.
|
||||
* This will never happen, as the bw limits message does not contain a session ID.
|
||||
*/
|
||||
@Override
|
||||
void bwReceived(int[] i) {
|
||||
_primary.bwReceived(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking. Waits a max of 10 seconds by default.
|
||||
* See lookupDest with maxWait parameter to change.
|
||||
* Implemented in 0.8.3 in I2PSessionImpl;
|
||||
* previously was available only in I2PSimpleSession.
|
||||
* Multiple outstanding lookups are now allowed.
|
||||
* @return null on failure
|
||||
*/
|
||||
@Override
|
||||
public Destination lookupDest(Hash h) throws I2PSessionException {
|
||||
return _primary.lookupDest(h);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking.
|
||||
* @param maxWait ms
|
||||
* @return null on failure
|
||||
*/
|
||||
@Override
|
||||
public Destination lookupDest(Hash h, long maxWait) throws I2PSessionException {
|
||||
return _primary.lookupDest(h, maxWait);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the router to lookup a Destination by host name.
|
||||
* Blocking. Waits a max of 10 seconds by default.
|
||||
*
|
||||
* This only makes sense for a b32 hostname, OR outside router context.
|
||||
* Inside router context, just query the naming service.
|
||||
* Outside router context, this does NOT query the context naming service.
|
||||
* Do that first if you expect a local addressbook.
|
||||
*
|
||||
* This will log a warning for non-b32 in router context.
|
||||
*
|
||||
* See interface for suggested implementation.
|
||||
*
|
||||
* Requires router side to be 0.9.11 or higher. If the router is older,
|
||||
* this will return null immediately.
|
||||
*/
|
||||
@Override
|
||||
public Destination lookupDest(String name) throws I2PSessionException {
|
||||
return _primary.lookupDest(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the router to lookup a Destination by host name.
|
||||
* Blocking. See above for details.
|
||||
* @param maxWait ms
|
||||
* @return null on failure
|
||||
*/
|
||||
@Override
|
||||
public Destination lookupDest(String name, long maxWait) throws I2PSessionException {
|
||||
return _primary.lookupDest(name, maxWait);
|
||||
}
|
||||
|
||||
/**
|
||||
* This may not work???????????, as the reply does not contain a session ID, so
|
||||
* it won't be routed back to us?
|
||||
*/
|
||||
@Override
|
||||
public int[] bandwidthLimits() throws I2PSessionException {
|
||||
return _primary.bandwidthLimits();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateActivity() {
|
||||
_primary.updateActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastActivity() {
|
||||
return _primary.lastActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReduced() {
|
||||
_primary.setReduced();
|
||||
}
|
||||
}
|
@@ -38,6 +38,11 @@ public class CreateLeaseSetMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId id) {
|
||||
_sessionId = id;
|
||||
}
|
||||
|
@@ -32,6 +32,16 @@ public class DestroySessionMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId id) {
|
||||
_sessionId = id;
|
||||
}
|
||||
|
@@ -76,6 +76,16 @@ public class HostLookupMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 0 to 2**32 - 1
|
||||
*/
|
||||
|
@@ -73,6 +73,16 @@ public class HostReplyMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 0 to 2**32 - 1
|
||||
*/
|
||||
|
@@ -60,9 +60,20 @@ public interface I2CPMessage extends DataStructure {
|
||||
public void writeMessage(OutputStream out) throws I2CPMessageException, IOException;
|
||||
|
||||
/**
|
||||
* Return the unique identifier for this type of APIMessage, as specified in the
|
||||
* Return the unique identifier for this type of message, as specified in the
|
||||
* network specification document under #ClientAccessLayerMessages
|
||||
* @return unique identifier for this type of APIMessage
|
||||
* @return unique identifier for this type of message
|
||||
*/
|
||||
public int getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this type of message.
|
||||
* Most but not all message types include a SessionId.
|
||||
* The ones that do already define getSessionId(), but some return a SessionId and
|
||||
* some return a long, so we define a new method here.
|
||||
*
|
||||
* @return SessionId or null if this message type does not include a SessionId
|
||||
* @since 0.9.19
|
||||
*/
|
||||
public SessionId sessionId();
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ package net.i2p.data.i2cp;
|
||||
import net.i2p.I2PException;
|
||||
|
||||
/**
|
||||
* Represent an error serializing or deserializing an APIMessage
|
||||
* Represent an error serializing or deserializing a message
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
|
@@ -127,4 +127,15 @@ public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPM
|
||||
throw new DataFormatException("Error writing the message", ime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this type of message.
|
||||
* Most but not all message types include a SessionId.
|
||||
* The ones that do already define getSessionId(), but some return a SessionId and
|
||||
* some return a long, so we define a new method here.
|
||||
*
|
||||
* @return null always. Extending classes with a SessionId must override.
|
||||
* @since 0.9.19
|
||||
*/
|
||||
public SessionId sessionId() { return null; }
|
||||
}
|
||||
|
@@ -37,6 +37,16 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId >= 0 ? new SessionId(_sessionId) : null;
|
||||
}
|
||||
|
||||
/** @param id 0-65535 */
|
||||
public void setSessionId(long id) {
|
||||
_sessionId = (int) id;
|
||||
|
@@ -193,6 +193,16 @@ public class MessageStatusMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId >= 0 ? new SessionId(_sessionId) : null;
|
||||
}
|
||||
|
||||
/** @param id 0-65535 */
|
||||
public void setSessionId(long id) {
|
||||
_sessionId = (int) id;
|
||||
|
@@ -36,6 +36,16 @@ public class ReceiveMessageBeginMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId >= 0 ? new SessionId(_sessionId) : null;
|
||||
}
|
||||
|
||||
/** @param id 0-65535 */
|
||||
public void setSessionId(long id) {
|
||||
_sessionId = (int) id;
|
||||
|
@@ -35,6 +35,16 @@ public class ReceiveMessageEndMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId >= 0 ? new SessionId(_sessionId) : null;
|
||||
}
|
||||
|
||||
/** @param id 0-65535 */
|
||||
public void setSessionId(long id) {
|
||||
_sessionId = (int) id;
|
||||
|
@@ -33,6 +33,16 @@ public class ReconfigureSessionMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId id) {
|
||||
_sessionId = id;
|
||||
}
|
||||
|
@@ -35,6 +35,16 @@ public class ReportAbuseMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId id) {
|
||||
_sessionId = id;
|
||||
}
|
||||
|
@@ -45,6 +45,16 @@ public class RequestLeaseSetMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId id) {
|
||||
_sessionId = id;
|
||||
}
|
||||
|
@@ -55,6 +55,16 @@ public class RequestVariableLeaseSetMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId id) {
|
||||
_sessionId = id;
|
||||
}
|
||||
|
@@ -38,6 +38,16 @@ public class SendMessageMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId id) {
|
||||
_sessionId = id;
|
||||
}
|
||||
|
@@ -42,6 +42,16 @@ public class SessionStatusMessage extends I2CPMessageImpl {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionId for this message.
|
||||
*
|
||||
* @since 0.9.19
|
||||
*/
|
||||
@Override
|
||||
public SessionId sessionId() {
|
||||
return _sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(SessionId id) {
|
||||
_sessionId = id;
|
||||
}
|
||||
|
Reference in New Issue
Block a user