From 4ea99b8a10018970ca9aa4568aacd8e9dc95b547 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sun, 24 May 2015 00:14:32 +0000 Subject: [PATCH] I2CP: Take 2 of fix, so a newly created session isn't destroyed and immediately replaced by i2ptunnel, which caused dup shared clients in a race at startup; Clarify session exception text if not open --- .../i2p/i2ptunnel/I2PTunnelClientBase.java | 21 ++++++++++++++----- core/java/src/net/i2p/client/I2PSession.java | 2 +- .../src/net/i2p/client/I2PSessionImpl.java | 9 ++++++-- .../src/net/i2p/client/I2PSessionImpl2.java | 7 ++++++- .../net/i2p/client/I2PSessionMuxedImpl.java | 7 ++++++- .../src/net/i2p/router/RouterVersion.java | 2 +- 6 files changed, 37 insertions(+), 11 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java index a0a68aa43b..2d4d7e0dd0 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java @@ -77,6 +77,16 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna private volatile ThreadPoolExecutor _executor; + /** this is ONLY for shared clients */ + private static I2PSocketManager socketManager; + + /** + * Only destroy and replace a static shared client socket manager if it's been connected before + * @since 0.9.20 + */ + private enum SocketManagerState { INIT, CONNECTED } + private static SocketManagerState _socketManagerState = SocketManagerState.INIT; + public static final String PROP_USE_SSL = I2PTunnelServer.PROP_USE_SSL; /** @@ -239,10 +249,6 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna connectManager(); } - /** this is ONLY for shared clients */ - private static I2PSocketManager socketManager; - - /** * This is ONLY for shared clients. * As of 0.9.20 this is fast, and does NOT connect the manager to the router. @@ -283,12 +289,13 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna Log _log = tunnel.getContext().logManager().getLog(I2PTunnelClientBase.class); if (socketManager != null && !socketManager.isDestroyed()) { I2PSession s = socketManager.getSession(); - if (s.isClosed()) { + if (s.isClosed() && _socketManagerState != SocketManagerState.INIT) { if (_log.shouldLog(Log.INFO)) _log.info(tunnel.getClientOptions().getProperty("inbound.nickname") + ": Building a new socket manager since the old one closed [s=" + s + "]"); tunnel.removeSession(s); // make sure the old one is closed socketManager.destroySocketManager(); + _socketManagerState = SocketManagerState.INIT; // We could be here a LONG time, holding the lock socketManager = buildSocketManager(tunnel, pkf); } else { @@ -424,6 +431,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna while (sockMgr.getSession().isClosed()) { try { sockMgr.getSession().connect(); + synchronized(I2PTunnelClientBase.class) { + if (sockMgr == socketManager) + _socketManagerState = SocketManagerState.CONNECTED; + } } catch (I2PSessionException ise) { // shadows instance _log Log _log = getTunnel().getContext().logManager().getLog(I2PTunnelClientBase.class); diff --git a/core/java/src/net/i2p/client/I2PSession.java b/core/java/src/net/i2p/client/I2PSession.java index da67e2c02c..a6ce4122ee 100644 --- a/core/java/src/net/i2p/client/I2PSession.java +++ b/core/java/src/net/i2p/client/I2PSession.java @@ -257,7 +257,7 @@ public interface I2PSession { /** * Have we closed the session? * - * @return true if the session is closed + * @return true if the session is closed, OR connect() has not been called yet */ public boolean isClosed(); diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java index cd6154c96a..7527dd8862 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl.java @@ -135,7 +135,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2 CLOSED } - private State _state = State.INIT; + protected State _state = State.INIT; protected final Object _stateLock = new Object(); /** @@ -614,7 +614,12 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2 * Report abuse with regards to the given messageId */ public void reportAbuse(int msgId, int severity) throws I2PSessionException { - if (isClosed()) throw new I2PSessionException(getPrefix() + "Already closed"); + synchronized (_stateLock) { + if (_state == State.CLOSED) + throw new I2PSessionException("Already closed"); + if (_state == State.INIT) + throw new I2PSessionException("Not open, must call connect() first"); + } _producer.reportAbuse(this, msgId, severity); } diff --git a/core/java/src/net/i2p/client/I2PSessionImpl2.java b/core/java/src/net/i2p/client/I2PSessionImpl2.java index 9f8a5465d3..5cd2b930c4 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl2.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl2.java @@ -247,7 +247,12 @@ class I2PSessionImpl2 extends I2PSessionImpl { public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expires) throws I2PSessionException { if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message"); - if (isClosed()) throw new I2PSessionException("Already closed"); + synchronized (_stateLock) { + if (_state == State.CLOSED) + throw new I2PSessionException("Already closed"); + if (_state == State.INIT) + throw new I2PSessionException("Not open, must call connect() first"); + } updateActivity(); // Sadly there is no way to send something completely uncompressed in a backward-compatible way, diff --git a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java index 48ca6016e8..0185496aa3 100644 --- a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java +++ b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java @@ -256,7 +256,12 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 { * @since 0.9.14 */ private byte[] prepPayload(byte[] payload, int offset, int size, int proto, int fromPort, int toPort) throws I2PSessionException { - if (isClosed()) throw new I2PSessionException("Already closed"); + synchronized (_stateLock) { + if (_state == State.CLOSED) + throw new I2PSessionException("Already closed"); + if (_state == State.INIT) + throw new I2PSessionException("Not open, must call connect() first"); + } updateActivity(); if (shouldCompress(size)) diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 0cd02d33d1..5fd579e05b 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 29; + public final static long BUILD = 30; /** for example "-test" */ public final static String EXTRA = "-rc"; -- GitLab