forked from I2P_Developers/i2p.i2p
* SessionKeyManager:
- Don't use unacked tagsets after consecutive ack failures and revert to full ElGamal if necessary (ticket #574) - Synchronize creation of new sessions to prevent dups - Don't remove an unacked session until it's really out of tags - Failsafe removal of old unacked tagsets - Cleanups, final, comments, log tweaks, debug.jsp tweaks, synchronization tweaks
This commit is contained in:
@ -38,15 +38,28 @@ public class SessionKeyManager {
|
|||||||
* Retrieve the session key currently associated with encryption to the target,
|
* Retrieve the session key currently associated with encryption to the target,
|
||||||
* or null if a new session key should be generated.
|
* or null if a new session key should be generated.
|
||||||
*
|
*
|
||||||
|
* Warning - don't generate a new session if this returns null, it's racy, use getCurrentOrNewKey()
|
||||||
*/
|
*/
|
||||||
public SessionKey getCurrentKey(PublicKey target) {
|
public SessionKey getCurrentKey(PublicKey target) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the session key currently associated with encryption to the target.
|
||||||
|
* Generates a new session and session key if not previously exising.
|
||||||
|
*
|
||||||
|
* @return non-null
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
public SessionKey getCurrentOrNewKey(PublicKey target) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a new session key with the specified target. Metrics to determine
|
* Associate a new session key with the specified target. Metrics to determine
|
||||||
* when to expire that key begin with this call.
|
* when to expire that key begin with this call.
|
||||||
*
|
*
|
||||||
|
* @deprecated racy
|
||||||
*/
|
*/
|
||||||
public void createSession(PublicKey target, SessionKey key) { // nop
|
public void createSession(PublicKey target, SessionKey key) { // nop
|
||||||
}
|
}
|
||||||
@ -54,6 +67,7 @@ public class SessionKeyManager {
|
|||||||
/**
|
/**
|
||||||
* Generate a new session key and associate it with the specified target.
|
* Generate a new session key and associate it with the specified target.
|
||||||
*
|
*
|
||||||
|
* @deprecated racy
|
||||||
*/
|
*/
|
||||||
public SessionKey createSession(PublicKey target) {
|
public SessionKey createSession(PublicKey target) {
|
||||||
SessionKey key = KeyGenerator.getInstance().generateSessionKey();
|
SessionKey key = KeyGenerator.getInstance().generateSessionKey();
|
||||||
|
@ -22,6 +22,7 @@ import java.util.Map;
|
|||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
@ -74,13 +75,16 @@ import net.i2p.util.SimpleTimer;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class TransientSessionKeyManager extends SessionKeyManager {
|
public class TransientSessionKeyManager extends SessionKeyManager {
|
||||||
private Log _log;
|
private final Log _log;
|
||||||
/** Map allowing us to go from the targeted PublicKey to the OutboundSession used */
|
/** Map allowing us to go from the targeted PublicKey to the OutboundSession used */
|
||||||
private final Map<PublicKey, OutboundSession> _outboundSessions;
|
private final Map<PublicKey, OutboundSession> _outboundSessions;
|
||||||
/** Map allowing us to go from a SessionTag to the containing TagSet */
|
/** Map allowing us to go from a SessionTag to the containing TagSet */
|
||||||
private final Map<SessionTag, TagSet> _inboundTagSets;
|
private final Map<SessionTag, TagSet> _inboundTagSets;
|
||||||
protected I2PAppContext _context;
|
protected final I2PAppContext _context;
|
||||||
private volatile boolean _alive;
|
private volatile boolean _alive;
|
||||||
|
/** for debugging */
|
||||||
|
private final AtomicInteger _rcvTagSetID = new AtomicInteger();
|
||||||
|
private final AtomicInteger _sentTagSetID = new AtomicInteger();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Let session tags sit around for 10 minutes before expiring them. We can now have such a large
|
* Let session tags sit around for 10 minutes before expiring them. We can now have such a large
|
||||||
@ -119,7 +123,6 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
_alive = true;
|
_alive = true;
|
||||||
SimpleScheduler.getInstance().addEvent(new CleanupEvent(), 60*1000);
|
SimpleScheduler.getInstance().addEvent(new CleanupEvent(), 60*1000);
|
||||||
}
|
}
|
||||||
private TransientSessionKeyManager() { this(null); }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
@ -192,6 +195,7 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
* Retrieve the session key currently associated with encryption to the target,
|
* Retrieve the session key currently associated with encryption to the target,
|
||||||
* or null if a new session key should be generated.
|
* or null if a new session key should be generated.
|
||||||
*
|
*
|
||||||
|
* Warning - don't generate a new session if this returns null, it's racy, use getCurrentOrNewKey()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public SessionKey getCurrentKey(PublicKey target) {
|
public SessionKey getCurrentKey(PublicKey target) {
|
||||||
@ -204,16 +208,42 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
+ new Date(sess.getEstablishedDate())
|
+ new Date(sess.getEstablishedDate())
|
||||||
+ " but not used for "
|
+ " but not used for "
|
||||||
+ (now-sess.getLastUsedDate())
|
+ (now-sess.getLastUsedDate())
|
||||||
+ "ms with target " + target);
|
+ "ms with target " + toString(target));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return sess.getCurrentKey();
|
return sess.getCurrentKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the session key currently associated with encryption to the target.
|
||||||
|
* Generates a new session and session key if not previously exising.
|
||||||
|
*
|
||||||
|
* @return non-null
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public SessionKey getCurrentOrNewKey(PublicKey target) {
|
||||||
|
synchronized (_outboundSessions) {
|
||||||
|
OutboundSession sess = _outboundSessions.get(target);
|
||||||
|
if (sess != null) {
|
||||||
|
long now = _context.clock().now();
|
||||||
|
if (sess.getLastUsedDate() < now - SESSION_LIFETIME_MAX_MS)
|
||||||
|
sess = null;
|
||||||
|
}
|
||||||
|
if (sess == null) {
|
||||||
|
SessionKey key = _context.keyGenerator().generateSessionKey();
|
||||||
|
sess = createAndReturnSession(target, key);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
return sess.getCurrentKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a new session key with the specified target. Metrics to determine
|
* Associate a new session key with the specified target. Metrics to determine
|
||||||
* when to expire that key begin with this call.
|
* when to expire that key begin with this call.
|
||||||
*
|
*
|
||||||
|
* @deprecated racy
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void createSession(PublicKey target, SessionKey key) {
|
public void createSession(PublicKey target, SessionKey key) {
|
||||||
@ -226,7 +256,9 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private OutboundSession createAndReturnSession(PublicKey target, SessionKey key) {
|
private OutboundSession createAndReturnSession(PublicKey target, SessionKey key) {
|
||||||
OutboundSession sess = new OutboundSession(target);
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("New OB session, sesskey: " + key + " target: " + toString(target));
|
||||||
|
OutboundSession sess = new OutboundSession(_context, _log, target);
|
||||||
sess.setCurrentKey(key);
|
sess.setCurrentKey(key);
|
||||||
addSession(sess);
|
addSession(sess);
|
||||||
return sess;
|
return sess;
|
||||||
@ -243,18 +275,19 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
public SessionTag consumeNextAvailableTag(PublicKey target, SessionKey key) {
|
public SessionTag consumeNextAvailableTag(PublicKey target, SessionKey key) {
|
||||||
OutboundSession sess = getSession(target);
|
OutboundSession sess = getSession(target);
|
||||||
if (sess == null) {
|
if (sess == null) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.debug("No session for " + target);
|
_log.warn("No session for " + toString(target));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (sess.getCurrentKey().equals(key)) {
|
if (sess.getCurrentKey().equals(key)) {
|
||||||
SessionTag nxt = sess.consumeNext();
|
SessionTag nxt = sess.consumeNext();
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
// logged in OutboundSession
|
||||||
_log.debug("OB Tag consumed: " + nxt + " with: " + key);
|
//if (nxt != null && _log.shouldLog(Log.DEBUG))
|
||||||
|
// _log.debug("OB Tag consumed: " + nxt + " with: " + key);
|
||||||
return nxt;
|
return nxt;
|
||||||
}
|
}
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.debug("Key does not match existing key, no tag");
|
_log.warn("Key does not match existing key, no tag");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,18 +338,20 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TagSetHandle tagsDelivered(PublicKey target, SessionKey key, Set<SessionTag> sessionTags) {
|
public TagSetHandle tagsDelivered(PublicKey target, SessionKey key, Set<SessionTag> sessionTags) {
|
||||||
if (_log.shouldLog(Log.DEBUG)) {
|
// if this is ever null, this is racy and needs synch
|
||||||
//_log.debug("Tags delivered to set " + set + " on session " + sess);
|
|
||||||
if (!sessionTags.isEmpty())
|
|
||||||
_log.debug("Tags delivered: " + sessionTags.size() + " for key: " + key + ": " + sessionTags);
|
|
||||||
}
|
|
||||||
OutboundSession sess = getSession(target);
|
OutboundSession sess = getSession(target);
|
||||||
if (sess == null)
|
if (sess == null) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("No session for delivered TagSet to target: " + toString(target));
|
||||||
sess = createAndReturnSession(target, key);
|
sess = createAndReturnSession(target, key);
|
||||||
else
|
} else {
|
||||||
sess.setCurrentKey(key);
|
sess.setCurrentKey(key);
|
||||||
TagSet set = new TagSet(sessionTags, key, _context.clock().now());
|
}
|
||||||
|
TagSet set = new TagSet(sessionTags, key, _context.clock().now(), _sentTagSetID.incrementAndGet());
|
||||||
sess.addTags(set);
|
sess.addTags(set);
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Tags delivered: " + set +
|
||||||
|
" target: " + toString(target) /** + ": " + sessionTags */ );
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,13 +374,19 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
@Override
|
@Override
|
||||||
public void failTags(PublicKey target, SessionKey key, TagSetHandle ts) {
|
public void failTags(PublicKey target, SessionKey key, TagSetHandle ts) {
|
||||||
OutboundSession sess = getSession(target);
|
OutboundSession sess = getSession(target);
|
||||||
if (sess == null)
|
if (sess == null) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("No session for failed TagSet: " + ts);
|
||||||
return;
|
return;
|
||||||
if(!key.equals(sess.getCurrentKey()))
|
}
|
||||||
|
if(!key.equals(sess.getCurrentKey())) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Wrong session key (wanted " + sess.getCurrentKey() + ") for failed TagSet: " + ts);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("TagSet failed: " + ts);
|
||||||
sess.failTags((TagSet)ts);
|
sess.failTags((TagSet)ts);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("TagSet failed: " + ts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -354,13 +395,19 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
@Override
|
@Override
|
||||||
public void tagsAcked(PublicKey target, SessionKey key, TagSetHandle ts) {
|
public void tagsAcked(PublicKey target, SessionKey key, TagSetHandle ts) {
|
||||||
OutboundSession sess = getSession(target);
|
OutboundSession sess = getSession(target);
|
||||||
if (sess == null)
|
if (sess == null) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("No session for acked TagSet: " + ts);
|
||||||
return;
|
return;
|
||||||
if(!key.equals(sess.getCurrentKey()))
|
}
|
||||||
|
if(!key.equals(sess.getCurrentKey())) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Wrong session key (wanted " + sess.getCurrentKey() + ") for acked TagSet: " + ts);
|
||||||
return;
|
return;
|
||||||
sess.ackTags((TagSet)ts);
|
}
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("TagSet acked: " + ts);
|
_log.debug("TagSet acked: " + ts);
|
||||||
|
sess.ackTags((TagSet)ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -370,13 +417,15 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
@Override
|
@Override
|
||||||
public void tagsReceived(SessionKey key, Set<SessionTag> sessionTags) {
|
public void tagsReceived(SessionKey key, Set<SessionTag> sessionTags) {
|
||||||
int overage = 0;
|
int overage = 0;
|
||||||
TagSet tagSet = new TagSet(sessionTags, key, _context.clock().now());
|
TagSet tagSet = new TagSet(sessionTags, key, _context.clock().now(), _rcvTagSetID.incrementAndGet());
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Received " + tagSet);
|
||||||
TagSet old = null;
|
TagSet old = null;
|
||||||
SessionTag dupTag = null;
|
SessionTag dupTag = null;
|
||||||
for (Iterator<SessionTag> iter = sessionTags.iterator(); iter.hasNext();) {
|
for (Iterator<SessionTag> iter = sessionTags.iterator(); iter.hasNext();) {
|
||||||
SessionTag tag = iter.next();
|
SessionTag tag = iter.next();
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Receiving tag " + tag + " for key " + key + ": tagSet: " + tagSet);
|
_log.debug("Receiving tag " + tag + " in tagSet: " + tagSet);
|
||||||
synchronized (_inboundTagSets) {
|
synchronized (_inboundTagSets) {
|
||||||
old = _inboundTagSets.put(tag, tagSet);
|
old = _inboundTagSets.put(tag, tagSet);
|
||||||
overage = _inboundTagSets.size() - MAX_INBOUND_SESSION_TAGS;
|
overage = _inboundTagSets.size() - MAX_INBOUND_SESSION_TAGS;
|
||||||
@ -415,8 +464,8 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
if (overage > 0)
|
if (overage > 0)
|
||||||
clearExcess(overage);
|
clearExcess(overage);
|
||||||
|
|
||||||
if ( (sessionTags.isEmpty()) && (_log.shouldLog(Log.DEBUG)) )
|
//if ( (sessionTags.isEmpty()) && (_log.shouldLog(Log.DEBUG)) )
|
||||||
_log.debug("Received 0 tags for key " + key);
|
// _log.debug("Received 0 tags for key " + key);
|
||||||
//if (false) aggressiveExpire();
|
//if (false) aggressiveExpire();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,7 +543,7 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
|
|
||||||
SessionKey key = tagSet.getAssociatedKey();
|
SessionKey key = tagSet.getAssociatedKey();
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Consuming IB " + tag + " for " + key + " on: " + tagSet);
|
_log.debug("IB Tag consumed: " + tag + " from: " + tagSet);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -607,7 +656,8 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
TagSet ts = siter.next();
|
TagSet ts = siter.next();
|
||||||
int size = ts.getTags().size();
|
int size = ts.getTags().size();
|
||||||
total += size;
|
total += size;
|
||||||
buf.append("<li><b>Received:</b> ").append(DataHelper.formatDuration(now - ts.getDate())).append(" ago with ");
|
buf.append("<li><b>ID: ").append(ts.getID())
|
||||||
|
.append(" Received:</b> ").append(DataHelper.formatDuration2(now - ts.getDate())).append(" ago with ");
|
||||||
buf.append(size).append(" tags remaining</li>");
|
buf.append(size).append(" tags remaining</li>");
|
||||||
}
|
}
|
||||||
buf.append("</ul></td></tr>\n");
|
buf.append("</ul></td></tr>\n");
|
||||||
@ -615,7 +665,7 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
buf.setLength(0);
|
buf.setLength(0);
|
||||||
}
|
}
|
||||||
buf.append("<tr><th colspan=\"2\">Total tags: ").append(total).append(" (");
|
buf.append("<tr><th colspan=\"2\">Total tags: ").append(total).append(" (");
|
||||||
buf.append(DataHelper.formatSize(32*total)).append("B)</th></tr>\n" +
|
buf.append(DataHelper.formatSize2(32*total)).append("B)</th></tr>\n" +
|
||||||
"</table>" +
|
"</table>" +
|
||||||
"<h2><b>Outbound sessions</b></h2>" +
|
"<h2><b>Outbound sessions</b></h2>" +
|
||||||
"<table>");
|
"<table>");
|
||||||
@ -625,9 +675,10 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
OutboundSession sess = iter.next();
|
OutboundSession sess = iter.next();
|
||||||
Set<TagSet> sets = new TreeSet(new TagSetComparator());
|
Set<TagSet> sets = new TreeSet(new TagSetComparator());
|
||||||
sets.addAll(sess.getTagSets());
|
sets.addAll(sess.getTagSets());
|
||||||
buf.append("<tr><td><b>Target public key:</b> ").append(sess.getTarget().toBase64().substring(0, 20)).append("...<br>" +
|
buf.append("<tr><td><b>Target public key:</b> ").append(toString(sess.getTarget())).append("<br>" +
|
||||||
"<b>Established:</b> ").append(DataHelper.formatDuration(now - sess.getEstablishedDate())).append(" ago<br>" +
|
"<b>Established:</b> ").append(DataHelper.formatDuration2(now - sess.getEstablishedDate())).append(" ago<br>" +
|
||||||
"<b>Last Used:</b> ").append(DataHelper.formatDuration(now - sess.getLastUsedDate())).append(" ago<br>" +
|
"<b>Ack Received?</b> ").append(sess.getAckReceived()).append("<br>" +
|
||||||
|
"<b>Last Used:</b> ").append(DataHelper.formatDuration2(now - sess.getLastUsedDate())).append(" ago<br>" +
|
||||||
"<b>Session key:</b> ").append(sess.getCurrentKey().toBase64()).append("</td>" +
|
"<b>Session key:</b> ").append(sess.getCurrentKey().toBase64()).append("</td>" +
|
||||||
"<td><b># Sets:</b> ").append(sess.getTagSets().size()).append("</td></tr>" +
|
"<td><b># Sets:</b> ").append(sess.getTagSets().size()).append("</td></tr>" +
|
||||||
"<tr><td colspan=\"2\"><ul>");
|
"<tr><td colspan=\"2\"><ul>");
|
||||||
@ -635,7 +686,8 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
TagSet ts = siter.next();
|
TagSet ts = siter.next();
|
||||||
int size = ts.getTags().size();
|
int size = ts.getTags().size();
|
||||||
total += size;
|
total += size;
|
||||||
buf.append("<li><b>Sent:</b> ").append(DataHelper.formatDuration(now - ts.getDate())).append(" ago with ");
|
buf.append("<li><b>ID: ").append(ts.getID())
|
||||||
|
.append(" Sent:</b> ").append(DataHelper.formatDuration2(now - ts.getDate())).append(" ago with ");
|
||||||
buf.append(size).append(" tags remaining; acked? ").append(ts.getAcked()).append("</li>");
|
buf.append(size).append(" tags remaining; acked? ").append(ts.getAcked()).append("</li>");
|
||||||
}
|
}
|
||||||
buf.append("</ul></td></tr>\n");
|
buf.append("</ul></td></tr>\n");
|
||||||
@ -643,12 +695,22 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
buf.setLength(0);
|
buf.setLength(0);
|
||||||
}
|
}
|
||||||
buf.append("<tr><th colspan=\"2\">Total tags: ").append(total).append(" (");
|
buf.append("<tr><th colspan=\"2\">Total tags: ").append(total).append(" (");
|
||||||
buf.append(DataHelper.formatSize(32*total)).append("B)</th></tr>\n" +
|
buf.append(DataHelper.formatSize2(32*total)).append("B)</th></tr>\n" +
|
||||||
"</table>");
|
"</table>");
|
||||||
|
|
||||||
out.write(buf.toString());
|
out.write(buf.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For debugging
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
private static String toString(PublicKey target) {
|
||||||
|
if (target == null)
|
||||||
|
return "null";
|
||||||
|
return target.toBase64().substring(0, 20) + "...";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Just for the HTML method above so we can see what's going on easier
|
* Just for the HTML method above so we can see what's going on easier
|
||||||
* Earliest first
|
* Earliest first
|
||||||
@ -659,28 +721,51 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** fixme pass in context and change to static */
|
/**
|
||||||
private class OutboundSession {
|
* The state for a crypto session to a single public key
|
||||||
private PublicKey _target;
|
*/
|
||||||
|
private static class OutboundSession {
|
||||||
|
private final I2PAppContext _context;
|
||||||
|
private final Log _log;
|
||||||
|
private final PublicKey _target;
|
||||||
private SessionKey _currentKey;
|
private SessionKey _currentKey;
|
||||||
private long _established;
|
private final long _established;
|
||||||
private long _lastUsed;
|
private long _lastUsed;
|
||||||
/** before the first ack, all tagsets go here. These are never expired, we rely
|
/**
|
||||||
on the callers to call failTags() or ackTags() to remove them from this list. */
|
* Before the first ack, all tagsets go here. These are never expired, we rely
|
||||||
private /* FIXME final FIXME */ List<TagSet> _unackedTagSets;
|
* on the callers to call failTags() or ackTags() to remove them from this list.
|
||||||
|
* Actually we now do a failsafe expire.
|
||||||
|
* Synch on _tagSets to access this.
|
||||||
|
*/
|
||||||
|
private final List<TagSet> _unackedTagSets;
|
||||||
/**
|
/**
|
||||||
* As tagsets are acked, they go here.
|
* As tagsets are acked, they go here.
|
||||||
* After the first ack, new tagsets go here (i.e. presumed acked)
|
* After the first ack, new tagsets go here (i.e. presumed acked)
|
||||||
*/
|
*/
|
||||||
private /* FIXME final FIXME */ List<TagSet> _tagSets;
|
private final List<TagSet> _tagSets;
|
||||||
/** set to true after first tagset is acked */
|
/**
|
||||||
private boolean _acked;
|
* Set to true after first tagset is acked.
|
||||||
|
* Upon repeated failures, we may revert back to false.
|
||||||
|
* This prevents us getting "stuck" forever, using tags that weren't acked
|
||||||
|
* to deliver the next set of tags.
|
||||||
|
*/
|
||||||
|
private volatile boolean _acked;
|
||||||
|
/**
|
||||||
|
* Fail count
|
||||||
|
* Synch on _tagSets to access this.
|
||||||
|
*/
|
||||||
|
private int _consecutiveFailures;
|
||||||
|
|
||||||
public OutboundSession(PublicKey target) {
|
private static final int MAX_FAILS = 2;
|
||||||
this(target, null, _context.clock().now(), _context.clock().now(), new ArrayList());
|
|
||||||
|
public OutboundSession(I2PAppContext ctx, Log log, PublicKey target) {
|
||||||
|
this(ctx, log, target, null, ctx.clock().now(), ctx.clock().now(), new ArrayList());
|
||||||
}
|
}
|
||||||
|
|
||||||
OutboundSession(PublicKey target, SessionKey curKey, long established, long lastUsed, List<TagSet> tagSets) {
|
OutboundSession(I2PAppContext ctx, Log log, PublicKey target, SessionKey curKey,
|
||||||
|
long established, long lastUsed, List<TagSet> tagSets) {
|
||||||
|
_context = ctx;
|
||||||
|
_log = log;
|
||||||
_target = target;
|
_target = target;
|
||||||
_currentKey = curKey;
|
_currentKey = curKey;
|
||||||
_established = established;
|
_established = established;
|
||||||
@ -711,9 +796,17 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
void ackTags(TagSet set) {
|
void ackTags(TagSet set) {
|
||||||
synchronized (_tagSets) {
|
synchronized (_tagSets) {
|
||||||
if (_unackedTagSets.remove(set)) {
|
if (_unackedTagSets.remove(set)) {
|
||||||
|
// we could perhaps use it even if not previuosly in unacked,
|
||||||
|
// i.e. it was expired already, but _tagSets is a list not a set...
|
||||||
_tagSets.add(set);
|
_tagSets.add(set);
|
||||||
_acked = true;
|
} else if (_log.shouldLog(Log.WARN)) {
|
||||||
|
if(!_tagSets.contains(set))
|
||||||
|
_log.warn("Ack of unknown tagset: " + set);
|
||||||
|
else if (set.getAcked())
|
||||||
|
_log.warn("Dup ack of tagset: " + set);
|
||||||
}
|
}
|
||||||
|
_acked = true;
|
||||||
|
_consecutiveFailures = 0;
|
||||||
}
|
}
|
||||||
set.setAcked();
|
set.setAcked();
|
||||||
}
|
}
|
||||||
@ -722,7 +815,29 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
void failTags(TagSet set) {
|
void failTags(TagSet set) {
|
||||||
synchronized (_tagSets) {
|
synchronized (_tagSets) {
|
||||||
_unackedTagSets.remove(set);
|
_unackedTagSets.remove(set);
|
||||||
_tagSets.remove(set);
|
if (_tagSets.remove(set)) {
|
||||||
|
if (++_consecutiveFailures >= MAX_FAILS) {
|
||||||
|
// revert back to non-speculative ack mode,
|
||||||
|
// and force full ElG next time by reclassifying all tagsets that weren't really acked
|
||||||
|
_acked = false;
|
||||||
|
int acked = 0;
|
||||||
|
int unacked = 0;
|
||||||
|
for (Iterator<TagSet> iter = _tagSets.iterator(); iter.hasNext(); ) {
|
||||||
|
TagSet ts = iter.next();
|
||||||
|
if (!ts.getAcked()) {
|
||||||
|
iter.remove();
|
||||||
|
_unackedTagSets.add(ts);
|
||||||
|
unacked++;
|
||||||
|
} else {
|
||||||
|
acked++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn(_consecutiveFailures + " consecutive failed tagset deliveries to " + _currentKey
|
||||||
|
+ ": reverting to full ElG and un-acking " + unacked + " unacked tag sets, with "
|
||||||
|
+ acked + " remaining acked tag sets");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -738,16 +853,18 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
_lastUsed = _context.clock().now();
|
_lastUsed = _context.clock().now();
|
||||||
if (_currentKey != null) {
|
if (_currentKey != null) {
|
||||||
if (!_currentKey.equals(key)) {
|
if (!_currentKey.equals(key)) {
|
||||||
int dropped = 0;
|
synchronized (_tagSets) {
|
||||||
List<TagSet> sets = _tagSets;
|
if (_log.shouldLog(Log.WARN)) {
|
||||||
_tagSets = new ArrayList();
|
int dropped = 0;
|
||||||
for (int i = 0; i < sets.size(); i++) {
|
for (TagSet set : _tagSets) {
|
||||||
TagSet set = (TagSet) sets.get(i);
|
dropped += set.getTags().size();
|
||||||
dropped += set.getTags().size();
|
}
|
||||||
|
_log.warn("Rekeyed from " + _currentKey + " to " + key
|
||||||
|
+ ": dropping " + dropped + " session tags", new Exception());
|
||||||
|
}
|
||||||
|
_acked = false;
|
||||||
|
_tagSets.clear();
|
||||||
}
|
}
|
||||||
if (_log.shouldLog(Log.INFO))
|
|
||||||
_log.info("Rekeyed from " + _currentKey + " to " + key
|
|
||||||
+ ": dropping " + dropped + " session tags");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_currentKey = key;
|
_currentKey = key;
|
||||||
@ -769,14 +886,23 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
long now = _context.clock().now();
|
long now = _context.clock().now();
|
||||||
int removed = 0;
|
int removed = 0;
|
||||||
synchronized (_tagSets) {
|
synchronized (_tagSets) {
|
||||||
for (int i = 0; i < _tagSets.size(); i++) {
|
for (Iterator<TagSet> iter = _tagSets.iterator(); iter.hasNext(); ) {
|
||||||
TagSet set = _tagSets.get(i);
|
TagSet set = iter.next();
|
||||||
if (set.getDate() + SESSION_TAG_DURATION_MS <= now) {
|
if (set.getDate() + SESSION_TAG_DURATION_MS <= now) {
|
||||||
_tagSets.remove(i);
|
iter.remove();
|
||||||
i--;
|
|
||||||
removed++;
|
removed++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// failsafe, sometimes these are sticking around, not sure why, so clean them periodically
|
||||||
|
if ((now & 0x0f) == 0) {
|
||||||
|
for (Iterator<TagSet> iter = _unackedTagSets.iterator(); iter.hasNext(); ) {
|
||||||
|
TagSet set = iter.next();
|
||||||
|
if (set.getDate() + SESSION_TAG_DURATION_MS <= now) {
|
||||||
|
iter.remove();
|
||||||
|
removed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
@ -789,10 +915,16 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
TagSet set = _tagSets.get(0);
|
TagSet set = _tagSets.get(0);
|
||||||
if (set.getDate() + SESSION_TAG_DURATION_MS > now) {
|
if (set.getDate() + SESSION_TAG_DURATION_MS > now) {
|
||||||
SessionTag tag = set.consumeNext();
|
SessionTag tag = set.consumeNext();
|
||||||
if (tag != null) return tag;
|
if (tag != null) {
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("OB Tag consumed: " + tag + " from: " + set);
|
||||||
|
return tag;
|
||||||
|
} else if (_log.shouldLog(Log.INFO)) {
|
||||||
|
_log.info("Removing empty " + set);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("TagSet from " + new Date(set.getDate()) + " expired");
|
_log.info("Expired " + set);
|
||||||
}
|
}
|
||||||
_tagSets.remove(0);
|
_tagSets.remove(0);
|
||||||
}
|
}
|
||||||
@ -812,7 +944,8 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
// so tags are sent when the acked tags are below
|
// so tags are sent when the acked tags are below
|
||||||
// 30, 17, and 4.
|
// 30, 17, and 4.
|
||||||
if (!set.getAcked())
|
if (!set.getAcked())
|
||||||
sz /= 3;
|
// round up so we don't report 0 when we have 1 or 2 remaining and get the session removed
|
||||||
|
sz = (sz + 2) / 3;
|
||||||
tags += sz;
|
tags += sz;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -846,32 +979,36 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
*/
|
*/
|
||||||
public void addTags(TagSet set) {
|
public void addTags(TagSet set) {
|
||||||
_lastUsed = _context.clock().now();
|
_lastUsed = _context.clock().now();
|
||||||
if (_acked) {
|
synchronized (_tagSets) {
|
||||||
synchronized (_tagSets) {
|
if (_acked)
|
||||||
_tagSets.add(set);
|
_tagSets.add(set);
|
||||||
}
|
else
|
||||||
} else {
|
|
||||||
synchronized (_unackedTagSets) {
|
|
||||||
_unackedTagSets.add(set);
|
_unackedTagSets.add(set);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 0.9 for debugging */
|
||||||
|
public boolean getAckReceived() {
|
||||||
|
return _acked;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TagSet implements TagSetHandle {
|
private static class TagSet implements TagSetHandle {
|
||||||
private Set<SessionTag> _sessionTags;
|
private final Set<SessionTag> _sessionTags;
|
||||||
private SessionKey _key;
|
private final SessionKey _key;
|
||||||
private long _date;
|
private final long _date;
|
||||||
|
private final int _id;
|
||||||
//private Exception _createdBy;
|
//private Exception _createdBy;
|
||||||
/** did we get an ack for this tagset? */
|
/** did we get an ack for this tagset? Only for outbound tagsets */
|
||||||
private boolean _acked;
|
private boolean _acked;
|
||||||
|
|
||||||
public TagSet(Set<SessionTag> tags, SessionKey key, long date) {
|
public TagSet(Set<SessionTag> tags, SessionKey key, long date, int id) {
|
||||||
if (key == null) throw new IllegalArgumentException("Missing key");
|
if (key == null) throw new IllegalArgumentException("Missing key");
|
||||||
if (tags == null) throw new IllegalArgumentException("Missing tags");
|
if (tags == null) throw new IllegalArgumentException("Missing tags");
|
||||||
_sessionTags = tags;
|
_sessionTags = tags;
|
||||||
_key = key;
|
_key = key;
|
||||||
_date = date;
|
_date = date;
|
||||||
|
_id = id;
|
||||||
//if (true) {
|
//if (true) {
|
||||||
// long now = I2PAppContext.getGlobalContext().clock().now();
|
// long now = I2PAppContext.getGlobalContext().clock().now();
|
||||||
// _createdBy = new Exception("Created by: key=" + _key.toBase64() + " on "
|
// _createdBy = new Exception("Created by: key=" + _key.toBase64() + " on "
|
||||||
@ -885,9 +1022,9 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
return _date;
|
return _date;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDate(long when) {
|
//void setDate(long when) {
|
||||||
_date = when;
|
// _date = when;
|
||||||
}
|
//}
|
||||||
|
|
||||||
/** tags still available */
|
/** tags still available */
|
||||||
public Set<SessionTag> getTags() {
|
public Set<SessionTag> getTags() {
|
||||||
@ -898,15 +1035,24 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
return _key;
|
return _key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caller must synch.
|
||||||
|
*/
|
||||||
public boolean contains(SessionTag tag) {
|
public boolean contains(SessionTag tag) {
|
||||||
return _sessionTags.contains(tag);
|
return _sessionTags.contains(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caller must synch.
|
||||||
|
*/
|
||||||
public void consume(SessionTag tag) {
|
public void consume(SessionTag tag) {
|
||||||
_sessionTags.remove(tag);
|
_sessionTags.remove(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** let's do this without counting the elements first */
|
/**
|
||||||
|
* Let's do this without counting the elements first.
|
||||||
|
* Caller must synch.
|
||||||
|
*/
|
||||||
public SessionTag consumeNext() {
|
public SessionTag consumeNext() {
|
||||||
SessionTag first;
|
SessionTag first;
|
||||||
try {
|
try {
|
||||||
@ -943,11 +1089,16 @@ public class TransientSessionKeyManager extends SessionKeyManager {
|
|||||||
}
|
}
|
||||||
******/
|
******/
|
||||||
|
|
||||||
|
/** @since 0.9 for debugging */
|
||||||
|
public int getID() {
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder buf = new StringBuilder(256);
|
StringBuilder buf = new StringBuilder(256);
|
||||||
buf.append("TagSet established: ").append(new Date(_date));
|
buf.append("TagSet #").append(_id).append(" created: ").append(new Date(_date));
|
||||||
buf.append(" Session key: ").append(_key.toBase64());
|
buf.append(" Session key: ").append(_key);
|
||||||
buf.append(" Size: ").append(_sessionTags.size());
|
buf.append(" Size: ").append(_sessionTags.size());
|
||||||
buf.append(" Acked? ").append(_acked);
|
buf.append(" Acked? ").append(_acked);
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
|
10
history.txt
10
history.txt
@ -1,3 +1,13 @@
|
|||||||
|
2012-03-09 zzz
|
||||||
|
* GarlicConfig: Remove unused reply block methods
|
||||||
|
* SessionKeyManager:
|
||||||
|
- Don't use unacked tagsets after consecutive ack failures
|
||||||
|
and revert to full ElGamal if necessary (ticket #574)
|
||||||
|
- Synchronize creation of new sessions to prevent dups
|
||||||
|
- Don't remove an unacked session until it's really out of tags
|
||||||
|
- Failsafe removal of old unacked tagsets
|
||||||
|
- Cleanups, final, comments, log tweaks, debug.jsp tweaks, synchronization tweaks
|
||||||
|
|
||||||
2012-03-06 kytv
|
2012-03-06 kytv
|
||||||
* German and Spanish translation updates from Transifex
|
* German and Spanish translation updates from Transifex
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 6;
|
public final static long BUILD = 7;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
@ -165,10 +165,8 @@ public class GarlicMessageBuilder {
|
|||||||
if (log.shouldLog(Log.INFO))
|
if (log.shouldLog(Log.INFO))
|
||||||
log.info("Encrypted with public key " + key + " to expire on " + new Date(config.getExpiration()));
|
log.info("Encrypted with public key " + key + " to expire on " + new Date(config.getExpiration()));
|
||||||
|
|
||||||
SessionKey curKey = skm.getCurrentKey(key);
|
SessionKey curKey = skm.getCurrentOrNewKey(key);
|
||||||
SessionTag curTag = null;
|
SessionTag curTag = null;
|
||||||
if (curKey == null)
|
|
||||||
curKey = skm.createSession(key);
|
|
||||||
if (!forceElGamal) {
|
if (!forceElGamal) {
|
||||||
curTag = skm.consumeNextAvailableTag(key, curKey);
|
curTag = skm.consumeNextAvailableTag(key, curKey);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user