2005-07-19 jrandom

* Further preparation for removing I2CP crypto
    * Added some validation to the DH key agreement (thanks $anon)
    * Validate tunnel data message expirations (though not really a problem,
      since tunnels expire)
    * Minor PRNG threading cleanup
This commit is contained in:
jrandom
2005-07-19 21:00:25 +00:00
committed by zzz
parent 0f8ede85ca
commit 843d5b625a
29 changed files with 355 additions and 174 deletions

View File

@ -121,6 +121,8 @@ public class Connection {
_context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); _context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("stream.chokeSizeBegin", "How many messages were outstanding when we started to choke?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); _context.statManager().createRateStat("stream.chokeSizeBegin", "How many messages were outstanding when we started to choke?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("stream.chokeSizeEnd", "How many messages were outstanding when we stopped being choked?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); _context.statManager().createRateStat("stream.chokeSizeEnd", "How many messages were outstanding when we stopped being choked?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
if (_log.shouldLog(Log.DEBUG))
_log.debug("New connection created with options: " + _options);
} }
public long getNextOutboundPacketNum() { public long getNextOutboundPacketNum() {
@ -805,6 +807,8 @@ public class Connection {
buf.append(" close received"); buf.append(" close received");
buf.append(" acked packets ").append(getAckedPackets()); buf.append(" acked packets ").append(getAckedPackets());
buf.append(" maxWin ").append(getOptions().getMaxWindowSize());
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
@ -885,14 +889,15 @@ public class Connection {
newWindowSize /= 2; newWindowSize /= 2;
if (newWindowSize <= 0) if (newWindowSize <= 0)
newWindowSize = 1; newWindowSize = 1;
if (_log.shouldLog(Log.WARN))
_log.warn("Congestion resending packet " + _packet.getSequenceNum() + ": new windowSize " + newWindowSize
+ ") for " + Connection.this.toString());
// setRTT has its own ceiling // setRTT has its own ceiling
getOptions().setRTT(getOptions().getRTT() + 10*1000); getOptions().setRTT(getOptions().getRTT() + 10*1000);
getOptions().setWindowSize(newWindowSize); getOptions().setWindowSize(newWindowSize);
if (_log.shouldLog(Log.WARN))
_log.warn("Congestion resending packet " + _packet.getSequenceNum() + ": new windowSize " + newWindowSize
+ "/" + getOptions().getWindowSize() + ") for " + Connection.this.toString());
windowAdjusted(); windowAdjusted();
} }
} }

View File

@ -227,15 +227,17 @@ public class ConnectionPacketHandler {
oldSize >>>= 1; oldSize >>>= 1;
if (oldSize <= 0) if (oldSize <= 0)
oldSize = 1; oldSize = 1;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Congestion occurred - new windowSize " + oldSize + " congestionSeenAt: "
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
+ ") for " + con);
// setRTT has its own ceiling // setRTT has its own ceiling
con.getOptions().setRTT(con.getOptions().getRTT() + 10*1000); con.getOptions().setRTT(con.getOptions().getRTT() + 10*1000);
con.getOptions().setWindowSize(oldSize); con.getOptions().setWindowSize(oldSize);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Congestion occurred - new windowSize " + oldSize + " / " + con.getOptions().getWindowSize() + " congestionSeenAt: "
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
+ ") for " + con);
congested = true; congested = true;
} }
@ -266,13 +268,14 @@ public class ConnectionPacketHandler {
if (newWindowSize <= 0) if (newWindowSize <= 0)
newWindowSize = 1; newWindowSize = 1;
if (_log.shouldLog(Log.DEBUG))
_log.debug("New window size " + newWindowSize + "/" + oldWindow + " congestionSeenAt: "
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
+ ") for " + con);
con.getOptions().setWindowSize(newWindowSize); con.getOptions().setWindowSize(newWindowSize);
con.setCongestionWindowEnd(newWindowSize + lowest); con.setCongestionWindowEnd(newWindowSize + lowest);
if (_log.shouldLog(Log.DEBUG))
_log.debug("New window size " + newWindowSize + "/" + oldWindow + "/" + con.getOptions().getWindowSize() + " congestionSeenAt: "
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
+ ") for " + con);
} }
con.windowAdjusted(); con.windowAdjusted();

View File

@ -181,4 +181,4 @@ class I2CPMessageProducer {
msg.setSessionId(session.getSessionId()); msg.setSessionId(session.getSessionId());
session.sendMessage(msg); session.sendMessage(msg);
} }
} }

View File

@ -119,49 +119,57 @@ class I2PSessionImpl2 extends I2PSessionImpl {
private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent) private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent)
throws I2PSessionException { throws I2PSessionException {
long begin = _context.clock().now(); SessionKey key = null;
if (_log.shouldLog(Log.DEBUG)) _log.debug("begin sendBestEffort"); SessionKey newKey = null;
SessionKey key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey()); SessionTag tag = null;
if (_log.shouldLog(Log.DEBUG)) _log.debug("key fetched");
if (key == null) key = _context.sessionKeyManager().createSession(dest.getPublicKey());
SessionTag tag = _context.sessionKeyManager().consumeNextAvailableTag(dest.getPublicKey(), key);
if (_log.shouldLog(Log.DEBUG)) _log.debug("tag consumed");
Set sentTags = null; Set sentTags = null;
int oldTags = _context.sessionKeyManager().getAvailableTags(dest.getPublicKey(), key); int oldTags = 0;
long availTimeLeft = _context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key); long begin = _context.clock().now();
if (I2CPMessageProducer.END_TO_END_CRYPTO) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("begin sendBestEffort");
key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey());
if (_log.shouldLog(Log.DEBUG)) _log.debug("key fetched");
if (key == null) key = _context.sessionKeyManager().createSession(dest.getPublicKey());
tag = _context.sessionKeyManager().consumeNextAvailableTag(dest.getPublicKey(), key);
if (_log.shouldLog(Log.DEBUG)) _log.debug("tag consumed");
sentTags = null;
oldTags = _context.sessionKeyManager().getAvailableTags(dest.getPublicKey(), key);
long availTimeLeft = _context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key);
if ( (tagsSent == null) || (tagsSent.size() <= 0) ) { if ( (tagsSent == null) || (tagsSent.size() <= 0) ) {
if (oldTags < NUM_TAGS) { if (oldTags < NUM_TAGS) {
sentTags = createNewTags(NUM_TAGS); sentTags = createNewTags(NUM_TAGS);
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("** sendBestEffort only had " + oldTags + " with " + availTimeLeft + ", adding " + NUM_TAGS + ": " + sentTags); _log.debug("** sendBestEffort only had " + oldTags + " with " + availTimeLeft + ", adding " + NUM_TAGS + ": " + sentTags);
} else if (availTimeLeft < 2 * 60 * 1000) { } else if (availTimeLeft < 2 * 60 * 1000) {
// if we have > 50 tags, but they expire in under 2 minutes, we want more // if we have > 50 tags, but they expire in under 2 minutes, we want more
sentTags = createNewTags(NUM_TAGS); sentTags = createNewTags(NUM_TAGS);
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Tags expiring in " + availTimeLeft + ", adding " + NUM_TAGS + " new ones: " + sentTags); _log.debug(getPrefix() + "Tags expiring in " + availTimeLeft + ", adding " + NUM_TAGS + " new ones: " + sentTags);
//_log.error("** sendBestEffort available time left " + availTimeLeft); //_log.error("** sendBestEffort available time left " + availTimeLeft);
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("sendBestEffort old tags: " + oldTags + " available time left: " + availTimeLeft);
}
} else { } else {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("sendBestEffort old tags: " + oldTags + " available time left: " + availTimeLeft); _log.debug("sendBestEffort is sending " + tagsSent.size() + " with " + availTimeLeft
+ "ms left, " + oldTags + " tags known and "
+ (tag == null ? "no tag" : " a valid tag"));
}
if (false) // rekey
newKey = _context.keyGenerator().generateSessionKey();
if ( (tagsSent != null) && (tagsSent.size() > 0) ) {
if (sentTags == null)
sentTags = new HashSet();
sentTags.addAll(tagsSent);
} }
} else { } else {
if (_log.shouldLog(Log.DEBUG)) // not using end to end crypto, so don't ever bundle any tags
_log.debug("sendBestEffort is sending " + tagsSent.size() + " with " + availTimeLeft
+ "ms left, " + oldTags + " tags known and "
+ (tag == null ? "no tag" : " a valid tag"));
} }
SessionKey newKey = null;
if (false) // rekey
newKey = _context.keyGenerator().generateSessionKey();
if ( (tagsSent != null) && (tagsSent.size() > 0) ) {
if (sentTags == null)
sentTags = new HashSet();
sentTags.addAll(tagsSent);
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("before creating nonce"); if (_log.shouldLog(Log.DEBUG)) _log.debug("before creating nonce");
long nonce = _context.random().nextInt(Integer.MAX_VALUE); long nonce = _context.random().nextInt(Integer.MAX_VALUE);
@ -174,10 +182,14 @@ class I2PSessionImpl2 extends I2PSessionImpl {
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key); if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key);
if (keyUsed != null) { if (keyUsed != null) {
if (newKey != null) if (I2CPMessageProducer.END_TO_END_CRYPTO) {
keyUsed.setData(newKey.getData()); if (newKey != null)
else keyUsed.setData(newKey.getData());
keyUsed.setData(key.getData()); else
keyUsed.setData(key.getData());
} else {
keyUsed.setData(SessionKey.INVALID_KEY.getData());
}
} }
if (tagsSent != null) { if (tagsSent != null) {
if (sentTags != null) { if (sentTags != null) {

View File

@ -18,6 +18,7 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.data.ByteArray; import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
@ -157,8 +158,14 @@ public class DHSessionKeyBuilder {
// read: Y // read: Y
BigInteger Y = readBigI(in); BigInteger Y = readBigI(in);
if (Y == null) return null; if (Y == null) return null;
builder.setPeerPublicValue(Y); try {
return builder; builder.setPeerPublicValue(Y);
return builder;
} catch (InvalidPublicParameterException ippe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Key exchange failed (hostile peer?)", ippe);
return null;
}
} }
static BigInteger readBigI(InputStream in) throws IOException { static BigInteger readBigI(InputStream in) throws IOException {
@ -175,7 +182,7 @@ public class DHSessionKeyBuilder {
System.arraycopy(Y, 0, Y2, 1, 256); System.arraycopy(Y, 0, Y2, 1, 256);
Y = Y2; Y = Y2;
} }
return new NativeBigInteger(Y); return new NativeBigInteger(1, Y);
} }
/** /**
@ -269,10 +276,11 @@ public class DHSessionKeyBuilder {
* Specify the value given by the peer for use in the session key negotiation * Specify the value given by the peer for use in the session key negotiation
* *
*/ */
public void setPeerPublicValue(BigInteger peerVal) { public void setPeerPublicValue(BigInteger peerVal) throws InvalidPublicParameterException {
validatePublic(peerVal);
_peerValue = peerVal; _peerValue = peerVal;
} }
public void setPeerPublicValue(byte val[]) { public void setPeerPublicValue(byte val[]) throws InvalidPublicParameterException {
if (val.length != 256) if (val.length != 256)
throw new IllegalArgumentException("Peer public value must be exactly 256 bytes"); throw new IllegalArgumentException("Peer public value must be exactly 256 bytes");
@ -284,7 +292,8 @@ public class DHSessionKeyBuilder {
System.arraycopy(val, 0, val2, 1, 256); System.arraycopy(val, 0, val2, 1, 256);
val = val2; val = val2;
} }
_peerValue = new NativeBigInteger(val); setPeerPublicValue(new NativeBigInteger(1, val));
//_peerValue = new NativeBigInteger(val);
} }
public BigInteger getPeerPublicValue() { public BigInteger getPeerPublicValue() {
@ -355,8 +364,58 @@ public class DHSessionKeyBuilder {
} }
return key; return key;
} }
/**
* rfc2631:
* The following algorithm MAY be used to validate a received public key y.
*
* 1. Verify that y lies within the interval [2,p-1]. If it does not,
* the key is invalid.
* 2. Compute y^q mod p. If the result == 1, the key is valid.
* Otherwise the key is invalid.
*/
private static final void validatePublic(BigInteger publicValue) throws InvalidPublicParameterException {
int cmp = publicValue.compareTo(NativeBigInteger.ONE);
if (cmp <= 0)
throw new InvalidPublicParameterException("Public value is below two: " + publicValue.toString());
cmp = publicValue.compareTo(CryptoConstants.elgp);
if (cmp >= 0)
throw new InvalidPublicParameterException("Public value is above p-1: " + publicValue.toString());
// todo:
// whatever validation needs to be done to mirror the rfc's part 2 (we don't have a q, so can't do
// if (NativeBigInteger.ONE.compareTo(publicValue.modPow(q, CryptoConstants.elgp)) != 0)
// throw new InvalidPublicParameterException("Invalid public value with y^q mod p != 1");
//
}
/*
private static void testValidation() {
NativeBigInteger bi = new NativeBigInteger("-3416069082912684797963255430346582466254460710249795973742848334283491150671563023437888953432878859472362439146158925287289114133666004165938814597775594104058593692562989626922979416277152479694258099203456493995467386903611666213773085025718340335205240293383622352894862685806192183268523899615405287022135356656720938278415659792084974076416864813957028335830794117802560169423133816961503981757298122040391506600117301607823659479051969827845787626261515313227076880722069706394405554113103165334903531980102626092646197079218895216346725765704256096661045699444128316078549709132753443706200863682650825635513");
try {
validatePublic(bi);
System.err.println("valid?!");
} catch (InvalidPublicParameterException ippe) {
System.err.println("Ok, invalid. cool");
}
byte val[] = bi.toByteArray();
System.out.println("Len: " + val.length + " first is ok? " + ( (val[0] & 0x80) == 1)
+ "\n" + DataHelper.toString(val, 64));
NativeBigInteger bi2 = new NativeBigInteger(1, val);
try {
validatePublic(bi2);
System.out.println("valid");
} catch (InvalidPublicParameterException ippe) {
System.out.println("invalid");
}
}
*/
public static void main(String args[]) { public static void main(String args[]) {
//if (true) { testValidation(); return; }
RandomSource.getInstance().nextBoolean(); // warm it up RandomSource.getInstance().nextBoolean(); // warm it up
try { try {
Thread.sleep(20 * 1000); Thread.sleep(20 * 1000);
@ -365,36 +424,40 @@ public class DHSessionKeyBuilder {
I2PAppContext ctx = new I2PAppContext(); I2PAppContext ctx = new I2PAppContext();
_log.debug("\n\n\n\nBegin test\n"); _log.debug("\n\n\n\nBegin test\n");
long negTime = 0; long negTime = 0;
for (int i = 0; i < 5; i++) { try {
long startNeg = Clock.getInstance().now(); for (int i = 0; i < 5; i++) {
DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder(); long startNeg = Clock.getInstance().now();
DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder(); DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder();
BigInteger pub1 = builder1.getMyPublicValue(); DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder();
builder2.setPeerPublicValue(pub1); BigInteger pub1 = builder1.getMyPublicValue();
BigInteger pub2 = builder2.getMyPublicValue(); builder2.setPeerPublicValue(pub1);
builder1.setPeerPublicValue(pub2); BigInteger pub2 = builder2.getMyPublicValue();
SessionKey key1 = builder1.getSessionKey(); builder1.setPeerPublicValue(pub2);
SessionKey key2 = builder2.getSessionKey(); SessionKey key1 = builder1.getSessionKey();
long endNeg = Clock.getInstance().now(); SessionKey key2 = builder2.getSessionKey();
negTime += endNeg - startNeg; long endNeg = Clock.getInstance().now();
negTime += endNeg - startNeg;
if (!key1.equals(key2)) if (!key1.equals(key2))
_log.error("**ERROR: Keys do not match"); _log.error("**ERROR: Keys do not match");
else else
_log.debug("**Success: Keys match"); _log.debug("**Success: Keys match");
byte iv[] = new byte[16]; byte iv[] = new byte[16];
RandomSource.getInstance().nextBytes(iv); RandomSource.getInstance().nextBytes(iv);
String origVal = "1234567890123456"; // 16 bytes max using AESEngine String origVal = "1234567890123456"; // 16 bytes max using AESEngine
byte enc[] = new byte[16]; byte enc[] = new byte[16];
byte dec[] = new byte[16]; byte dec[] = new byte[16];
ctx.aes().encrypt(origVal.getBytes(), 0, enc, 0, key1, iv, 16); ctx.aes().encrypt(origVal.getBytes(), 0, enc, 0, key1, iv, 16);
ctx.aes().decrypt(enc, 0, dec, 0, key2, iv, 16); ctx.aes().decrypt(enc, 0, dec, 0, key2, iv, 16);
String tranVal = new String(dec); String tranVal = new String(dec);
if (origVal.equals(tranVal)) if (origVal.equals(tranVal))
_log.debug("**Success: D(E(val)) == val"); _log.debug("**Success: D(E(val)) == val");
else else
_log.error("**ERROR: D(E(val)) != val [val=(" + tranVal + "), origVal=(" + origVal + ")"); _log.error("**ERROR: D(E(val)) != val [val=(" + tranVal + "), origVal=(" + origVal + ")");
}
} catch (InvalidPublicParameterException ippe) {
_log.error("Invalid dh", ippe);
} }
_log.debug("Negotiation time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each"); _log.debug("Negotiation time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each");
try { try {
@ -451,4 +514,13 @@ public class DHSessionKeyBuilder {
return builder; return builder;
} }
} }
public static class InvalidPublicParameterException extends I2PException {
public InvalidPublicParameterException() {
super();
}
public InvalidPublicParameterException(String msg) {
super(msg);
}
}
} }

View File

@ -220,10 +220,10 @@ class TransientSessionKeyManager extends SessionKeyManager {
* *
*/ */
public void tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) { public void tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) {
if (_log.shouldLog(Log.WARN)) { if (_log.shouldLog(Log.DEBUG)) {
//_log.debug("Tags delivered to set " + set + " on session " + sess); //_log.debug("Tags delivered to set " + set + " on session " + sess);
if (sessionTags.size() > 0) if (sessionTags.size() > 0)
_log.warn("Tags delivered: " + sessionTags.size() + " for key: " + key.toBase64() + ": " + sessionTags); _log.debug("Tags delivered: " + sessionTags.size() + " for key: " + key.toBase64() + ": " + sessionTags);
} }
OutboundSession sess = getSession(target); OutboundSession sess = getSession(target);
if (sess == null) { if (sess == null) {
@ -286,10 +286,10 @@ class TransientSessionKeyManager extends SessionKeyManager {
} }
} }
if (_log.shouldLog(Log.ERROR)) { if (_log.shouldLog(Log.WARN)) {
_log.error("Multiple tags matching! tagSet: " + tagSet + " and old tagSet: " + old + " tag: " + dupTag); _log.warn("Multiple tags matching! tagSet: " + tagSet + " and old tagSet: " + old + " tag: " + dupTag + "/" + dupTag.toBase64());
_log.error("Earlier tag set creation: " + old + ": key=" + old.getAssociatedKey().toBase64(), old.getCreatedBy()); _log.warn("Earlier tag set creation: " + old + ": key=" + old.getAssociatedKey().toBase64(), old.getCreatedBy());
_log.error("Current tag set creation: " + tagSet + ": key=" + tagSet.getAssociatedKey().toBase64(), tagSet.getCreatedBy()); _log.warn("Current tag set creation: " + tagSet + ": key=" + tagSet.getAssociatedKey().toBase64(), tagSet.getCreatedBy());
} }
} }
@ -413,7 +413,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
long now = _context.clock().now(); long now = _context.clock().now();
StringBuffer buf = null; StringBuffer buf = null;
StringBuffer bufSummary = null; StringBuffer bufSummary = null;
if (_log.shouldLog(Log.WARN)) { if (_log.shouldLog(Log.DEBUG)) {
buf = new StringBuffer(128); buf = new StringBuffer(128);
buf.append("Expiring inbound: "); buf.append("Expiring inbound: ");
bufSummary = new StringBuffer(1024); bufSummary = new StringBuffer(1024);
@ -438,9 +438,9 @@ class TransientSessionKeyManager extends SessionKeyManager {
} }
_context.statManager().addRateData("crypto.sessionTagsRemaining", remaining, 0); _context.statManager().addRateData("crypto.sessionTagsRemaining", remaining, 0);
if ( (buf != null) && (removed > 0) ) if ( (buf != null) && (removed > 0) )
_log.warn(buf.toString()); _log.debug(buf.toString());
if (bufSummary != null) if (bufSummary != null)
_log.warn("Cleaning up with remaining: " + bufSummary.toString()); _log.debug("Cleaning up with remaining: " + bufSummary.toString());
//_log.warn("Expiring tags: [" + tagsToDrop + "]"); //_log.warn("Expiring tags: [" + tagsToDrop + "]");

View File

@ -10,6 +10,7 @@ package net.i2p.data;
*/ */
import java.io.Serializable; import java.io.Serializable;
import net.i2p.data.Base64;
/** /**
* Wrap up an array of bytes so that they can be compared and placed in hashes, * Wrap up an array of bytes so that they can be compared and placed in hashes,
@ -82,6 +83,10 @@ public class ByteArray implements Serializable, Comparable {
} }
public final String toString() { public final String toString() {
return DataHelper.toString(getData(), 32); return super.toString() + "/" + DataHelper.toString(getData(), 32) + "." + _valid;
}
public final String toBase64() {
return Base64.encode(_data, _offset, _valid);
} }
} }

View File

@ -27,6 +27,7 @@ public class SessionKey extends DataStructureImpl {
private Object _preparedKey; private Object _preparedKey;
public final static int KEYSIZE_BYTES = 32; public final static int KEYSIZE_BYTES = 32;
public static final SessionKey INVALID_KEY = new SessionKey(new byte[KEYSIZE_BYTES]);
public SessionKey() { public SessionKey() {
this(null); this(null);

View File

@ -31,11 +31,13 @@ public class BufferedStatLog implements StatLog {
private String _outFile; private String _outFile;
private static final int BUFFER_SIZE = 1024; private static final int BUFFER_SIZE = 1024;
private static final boolean DISABLE_LOGGING = false;
public BufferedStatLog(I2PAppContext ctx) { public BufferedStatLog(I2PAppContext ctx) {
_context = ctx; _context = ctx;
_log = ctx.logManager().getLog(BufferedStatLog.class); _log = ctx.logManager().getLog(BufferedStatLog.class);
_events = new StatEvent[BUFFER_SIZE]; _events = new StatEvent[BUFFER_SIZE];
if (DISABLE_LOGGING) return;
for (int i = 0; i < BUFFER_SIZE; i++) for (int i = 0; i < BUFFER_SIZE; i++)
_events[i] = new StatEvent(); _events[i] = new StatEvent();
_eventNext = 0; _eventNext = 0;
@ -48,6 +50,7 @@ public class BufferedStatLog implements StatLog {
} }
public void addData(String scope, String stat, long value, long duration) { public void addData(String scope, String stat, long value, long duration) {
if (DISABLE_LOGGING) return;
synchronized (_events) { synchronized (_events) {
_events[_eventNext].init(scope, stat, value, duration); _events[_eventNext].init(scope, stat, value, duration);
_eventNext = (_eventNext + 1) % _events.length; _eventNext = (_eventNext + 1) % _events.length;

View File

@ -85,11 +85,15 @@ public final class ByteCache {
* *
*/ */
public final void release(ByteArray entry) { public final void release(ByteArray entry) {
release(entry, true);
}
public final void release(ByteArray entry, boolean shouldZero) {
if (_cache) { if (_cache) {
if ( (entry == null) || (entry.getData() == null) ) if ( (entry == null) || (entry.getData() == null) )
return; return;
Arrays.fill(entry.getData(), (byte)0x0); if (shouldZero)
Arrays.fill(entry.getData(), (byte)0x0);
synchronized (_available) { synchronized (_available) {
if (_available.size() < _maxCached) if (_available.size() < _maxCached)
_available.add(entry); _available.add(entry);

View File

@ -61,9 +61,14 @@ public class PooledRandomSource extends RandomSource {
} }
private final RandomSource pickPRNG() { private final RandomSource pickPRNG() {
int i = _nextPool % POOL_SIZE; // how much more explicit can we get?
_nextPool = (++_nextPool) % POOL_SIZE; int cur = _nextPool;
return _pool[i]; cur = cur % POOL_SIZE;
RandomSource rv = _pool[cur];
cur++;
cur = cur % POOL_SIZE;
_nextPool = cur;
return rv;
} }
/** /**

View File

@ -1,12 +1,19 @@
$Id: history.txt,v 1.212 2005/07/13 15:07:32 jrandom Exp $ $Id: history.txt,v 1.213 2005/07/16 07:52:36 cervantes Exp $
2005-07-19 jrandom
* Further preparation for removing I2CP crypto
* Added some validation to the DH key agreement (thanks $anon)
* Validate tunnel data message expirations (though not really a problem,
since tunnels expire)
* Minor PRNG threading cleanup
2005-07-15 cervantes 2005-07-15 cervantes
* Added workaround for an odd win32 bug in the stats configuration * Added workaround for an odd win32 bug in the stats configuration console
console page which meant only the first checkbox selection was saved. page which meant only the first checkbox selection was saved.
2005-07-15 Romster 2005-07-15 Romster
* Added per group selection toggles in the stats configuration console * Added per group selection toggles in the stats configuration console
page. page.
2005-07-13 jrandom 2005-07-13 jrandom
* Fixed a recently injected bug in the multitransport bidding which had * Fixed a recently injected bug in the multitransport bidding which had

View File

@ -54,8 +54,7 @@ public class DeliveryStatusMessage extends I2NPMessageImpl {
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException { protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
if ( (_id < 0) || (_arrival <= 0) ) throw new I2NPMessageException("Not enough data to write out"); if ( (_id < 0) || (_arrival <= 0) ) throw new I2NPMessageException("Not enough data to write out");
byte id[] = DataHelper.toLong(4, _id); DataHelper.toLong(out, curIndex, 4, _id);
System.arraycopy(id, 0, out, curIndex, 4);
curIndex += 4; curIndex += 4;
DataHelper.toLong(out, curIndex, DataHelper.DATE_LENGTH, _arrival); DataHelper.toLong(out, curIndex, DataHelper.DATE_LENGTH, _arrival);
curIndex += DataHelper.DATE_LENGTH; curIndex += DataHelper.DATE_LENGTH;

View File

@ -51,6 +51,7 @@ public interface I2NPMessage extends DataStructure {
* @throws IOException if there is a problem reading from the stream * @throws IOException if there is a problem reading from the stream
*/ */
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException; public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException;
public void readMessage(byte data[], int offset, int dataSize, int type, I2NPMessageHandler handler) throws I2NPMessageException, IOException;
/** /**
* Return the unique identifier for this type of I2NP message, as defined in * Return the unique identifier for this type of I2NP message, as defined in

View File

@ -268,7 +268,16 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
} }
} }
public void readMessage(byte data[], int offset, int dataSize, int type, I2NPMessageHandler handler) throws I2NPMessageException, IOException {
// ignore the handler (overridden in subclasses if necessary
readMessage(data, offset, dataSize, type);
}
public static I2NPMessage fromRawByteArray(I2PAppContext ctx, byte buffer[], int offset, int len) throws I2NPMessageException { public static I2NPMessage fromRawByteArray(I2PAppContext ctx, byte buffer[], int offset, int len) throws I2NPMessageException {
return fromRawByteArray(ctx, buffer, offset, len, new I2NPMessageHandler(ctx));
}
public static I2NPMessage fromRawByteArray(I2PAppContext ctx, byte buffer[], int offset, int len, I2NPMessageHandler handler) throws I2NPMessageException {
int type = (int)DataHelper.fromLong(buffer, offset, 1); int type = (int)DataHelper.fromLong(buffer, offset, 1);
offset++; offset++;
I2NPMessage msg = createMessage(ctx, type); I2NPMessage msg = createMessage(ctx, type);
@ -287,7 +296,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
offset += 4; offset += 4;
int dataSize = len - 1 - 4; int dataSize = len - 1 - 4;
try { try {
msg.readMessage(buffer, offset, dataSize, type); msg.readMessage(buffer, offset, dataSize, type, handler);
msg.setMessageExpiration(expiration); msg.setMessageExpiration(expiration);
return msg; return msg;
} catch (IOException ioe) { } catch (IOException ioe) {

View File

@ -81,6 +81,10 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException { public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
I2NPMessageHandler h = new I2NPMessageHandler(_context);
readMessage(data, offset, dataSize, type, h);
}
public void readMessage(byte data[], int offset, int dataSize, int type, I2NPMessageHandler handler) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message"); if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
int curIndex = offset; int curIndex = offset;
@ -92,9 +96,8 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
int size = (int)DataHelper.fromLong(data, curIndex, 2); int size = (int)DataHelper.fromLong(data, curIndex, 2);
curIndex += 2; curIndex += 2;
I2NPMessageHandler h = new I2NPMessageHandler(_context); curIndex = handler.readMessage(data, curIndex);
curIndex = h.readMessage(data, curIndex); _msg = handler.lastRead();
_msg = h.lastRead();
if (_msg == null) if (_msg == null)
throw new I2NPMessageException("wtf, message read has no payload?"); throw new I2NPMessageException("wtf, message read has no payload?");
} }

View File

@ -113,30 +113,35 @@ public class InNetMessagePool implements Service {
// _context.statManager().getStatLog().addData(fromRouterHash.toBase64().substring(0,6), "udp.floodDataReceived", 1, 0); // _context.statManager().getStatLog().addData(fromRouterHash.toBase64().substring(0,6), "udp.floodDataReceived", 1, 0);
// return 0; // return 0;
//} //}
String invalidReason = null;
if (messageBody instanceof TunnelDataMessage) { if (messageBody instanceof TunnelDataMessage) {
// do not validate the message with the validator - the IV validator is sufficient // the IV validator is sufficient for dup detection on tunnel messages, so
} else { // just validate the expiration
String invalidReason = _context.messageValidator().validateMessage(messageBody.getUniqueId(), exp); invalidReason = _context.messageValidator().validateMessage(exp);
if (invalidReason != null) { } else {
int level = Log.WARN; invalidReason = _context.messageValidator().validateMessage(messageBody.getUniqueId(), exp);
if (messageBody instanceof TunnelCreateMessage) }
level = Log.INFO;
if (_log.shouldLog(level)) if (invalidReason != null) {
_log.log(level, "Duplicate message received [" + messageBody.getUniqueId() int level = Log.WARN;
+ " expiring on " + exp + "]: " + messageBody.getClass().getName() + ": " + invalidReason if (messageBody instanceof TunnelCreateMessage)
+ ": " + messageBody); level = Log.INFO;
_context.statManager().addRateData("inNetPool.dropped", 1, 0); if (_log.shouldLog(level))
_context.statManager().addRateData("inNetPool.duplicate", 1, 0); _log.log(level, "Duplicate message received [" + messageBody.getUniqueId()
_context.messageHistory().droppedOtherMessage(messageBody); + " expiring on " + exp + "]: " + messageBody.getClass().getName() + ": " + invalidReason
_context.messageHistory().messageProcessingError(messageBody.getUniqueId(), + ": " + messageBody);
messageBody.getClass().getName(), _context.statManager().addRateData("inNetPool.dropped", 1, 0);
"Duplicate/expired"); _context.statManager().addRateData("inNetPool.duplicate", 1, 0);
return -1; _context.messageHistory().droppedOtherMessage(messageBody);
} else { _context.messageHistory().messageProcessingError(messageBody.getUniqueId(),
if (_log.shouldLog(Log.DEBUG)) messageBody.getClass().getName(),
_log.debug("Message received [" + messageBody.getUniqueId() "Duplicate/expired");
+ " expiring on " + exp + "] is NOT a duplicate or exipired"); return -1;
} } else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Message received [" + messageBody.getUniqueId()
+ " expiring on " + exp + "] is NOT a duplicate or exipired");
} }
boolean jobFound = false; boolean jobFound = false;

View File

@ -443,13 +443,13 @@ public class MessageHistory {
addEntry(buf.toString()); addEntry(buf.toString());
} }
public void receiveTunnelFragment(long messageId, int fragmentId, String status) { public void receiveTunnelFragment(long messageId, int fragmentId, Object status) {
if (!_doLog) return; if (!_doLog) return;
if (messageId == -1) throw new IllegalArgumentException("why are you -1?"); if (messageId == -1) throw new IllegalArgumentException("why are you -1?");
StringBuffer buf = new StringBuffer(48); StringBuffer buf = new StringBuffer(48);
buf.append(getPrefix()); buf.append(getPrefix());
buf.append("Receive fragment ").append(fragmentId).append(" in ").append(messageId); buf.append("Receive fragment ").append(fragmentId).append(" in ").append(messageId);
buf.append(" status: ").append(status); buf.append(" status: ").append(status.toString());
addEntry(buf.toString()); addEntry(buf.toString());
} }
public void receiveTunnelFragmentComplete(long messageId) { public void receiveTunnelFragmentComplete(long messageId) {

View File

@ -34,18 +34,9 @@ public class MessageValidator {
* @return reason why the message is invalid (or null if the message is valid) * @return reason why the message is invalid (or null if the message is valid)
*/ */
public String validateMessage(long messageId, long expiration) { public String validateMessage(long messageId, long expiration) {
long now = _context.clock().now(); String msg = validateMessage(expiration);
if (now - Router.CLOCK_FUDGE_FACTOR >= expiration) { if (msg != null)
if (_log.shouldLog(Log.WARN)) return msg;
_log.warn("Rejecting message " + messageId + " because it expired " + (now-expiration) + "ms ago");
_context.statManager().addRateData("router.invalidMessageTime", (now-expiration), 0);
return "expired " + (now-expiration) + "ms ago";
} else if (now + 4*Router.CLOCK_FUDGE_FACTOR < expiration) {
if (_log.shouldLog(Log.WARN))
_log.warn("Rejecting message " + messageId + " because it will expire too far in the future (" + (expiration-now) + "ms)");
_context.statManager().addRateData("router.invalidMessageTime", (now-expiration), 0);
return "expire too far in the future (" + (expiration-now) + "ms)";
}
boolean isDuplicate = noteReception(messageId, expiration); boolean isDuplicate = noteReception(messageId, expiration);
if (isDuplicate) { if (isDuplicate) {
@ -59,6 +50,24 @@ public class MessageValidator {
return null; return null;
} }
} }
/**
* Only check the expiration for the message
*/
public String validateMessage(long expiration) {
long now = _context.clock().now();
if (now - Router.CLOCK_FUDGE_FACTOR >= expiration) {
if (_log.shouldLog(Log.WARN))
_log.warn("Rejecting message because it expired " + (now-expiration) + "ms ago");
_context.statManager().addRateData("router.invalidMessageTime", (now-expiration), 0);
return "expired " + (now-expiration) + "ms ago";
} else if (now + 4*Router.CLOCK_FUDGE_FACTOR < expiration) {
if (_log.shouldLog(Log.WARN))
_log.warn("Rejecting message because it will expire too far in the future (" + (expiration-now) + "ms)");
_context.statManager().addRateData("router.invalidMessageTime", (now-expiration), 0);
return "expire too far in the future (" + (expiration-now) + "ms)";
}
return null;
}
private static final long TIME_MASK = 0xFFFFFC00; private static final long TIME_MASK = 0xFFFFFC00;

View File

@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
* *
*/ */
public class RouterVersion { public class RouterVersion {
public final static String ID = "$Revision: 1.203 $ $Date: 2005/07/13 15:07:32 $"; public final static String ID = "$Revision: 1.204 $ $Date: 2005/07/16 07:52:36 $";
public final static String VERSION = "0.5.0.7"; public final static String VERSION = "0.5.0.7";
public final static long BUILD = 15; public final static long BUILD = 16;
public static void main(String args[]) { public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION); System.out.println("I2P Router version: " + VERSION);
System.out.println("Router ID: " + RouterVersion.ID); System.out.println("Router ID: " + RouterVersion.ID);

View File

@ -100,6 +100,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
private final static String PROP_I2NP_TCP_HOSTNAME = "i2np.tcp.hostname"; private final static String PROP_I2NP_TCP_HOSTNAME = "i2np.tcp.hostname";
private final static String PROP_I2NP_TCP_PORT = "i2np.tcp.port"; private final static String PROP_I2NP_TCP_PORT = "i2np.tcp.port";
private final static String PROP_I2NP_TCP_DISABLED = "i2np.tcp.disable";
private RouterAddress createTCPAddress() { private RouterAddress createTCPAddress() {
RouterAddress addr = new RouterAddress(); RouterAddress addr = new RouterAddress();
@ -108,6 +109,10 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
Properties props = new Properties(); Properties props = new Properties();
String name = _context.router().getConfigSetting(PROP_I2NP_TCP_HOSTNAME); String name = _context.router().getConfigSetting(PROP_I2NP_TCP_HOSTNAME);
String port = _context.router().getConfigSetting(PROP_I2NP_TCP_PORT); String port = _context.router().getConfigSetting(PROP_I2NP_TCP_PORT);
String disabledStr = _context.router().getConfigSetting(PROP_I2NP_TCP_DISABLED);
boolean disabled = false;
if ( (disabledStr != null) && ("true".equalsIgnoreCase(disabledStr)) )
return null;
if ( (name == null) || (port == null) ) { if ( (name == null) || (port == null) ) {
//_log.info("TCP Host/Port not specified in config file - skipping TCP transport"); //_log.info("TCP Host/Port not specified in config file - skipping TCP transport");
return null; return null;

View File

@ -5,6 +5,7 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import net.i2p.crypto.DHSessionKeyBuilder;
import net.i2p.data.RouterAddress; import net.i2p.data.RouterAddress;
import net.i2p.data.RouterIdentity; import net.i2p.data.RouterIdentity;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
@ -286,7 +287,16 @@ public class EstablishmentManager {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Send created to: " + state.getRemoteHostId().toString()); _log.debug("Send created to: " + state.getRemoteHostId().toString());
state.generateSessionKey(); try {
state.generateSessionKey();
} catch (DHSessionKeyBuilder.InvalidPublicParameterException ippe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Peer " + state.getRemoteHostId() + " sent us an invalid DH parameter (or were spoofed)", ippe);
synchronized (_inboundStates) {
_inboundStates.remove(state.getRemoteHostId());
}
return;
}
_transport.send(_builder.buildSessionCreatedPacket(state, _transport.getExternalPort(), _transport.getIntroKey())); _transport.send(_builder.buildSessionCreatedPacket(state, _transport.getExternalPort(), _transport.getIntroKey()));
// if they haven't advanced to sending us confirmed packets in 5s, // if they haven't advanced to sending us confirmed packets in 5s,
// repeat // repeat

View File

@ -99,7 +99,7 @@ public class InboundEstablishState {
public synchronized byte[] getReceivedX() { return _receivedX; } public synchronized byte[] getReceivedX() { return _receivedX; }
public synchronized byte[] getReceivedOurIP() { return _bobIP; } public synchronized byte[] getReceivedOurIP() { return _bobIP; }
public synchronized void generateSessionKey() { public synchronized void generateSessionKey() throws DHSessionKeyBuilder.InvalidPublicParameterException {
if (_sessionKey != null) return; if (_sessionKey != null) return;
_keyBuilder = new DHSessionKeyBuilder(); _keyBuilder = new DHSessionKeyBuilder();
_keyBuilder.setPeerPublicValue(_receivedX); _keyBuilder.setPeerPublicValue(_receivedX);

View File

@ -9,6 +9,7 @@ import net.i2p.data.DataFormatException;
import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.I2NPMessageImpl; import net.i2p.data.i2np.I2NPMessageImpl;
import net.i2p.data.i2np.I2NPMessageException; import net.i2p.data.i2np.I2NPMessageException;
import net.i2p.data.i2np.I2NPMessageHandler;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
import net.i2p.util.ByteCache; import net.i2p.util.ByteCache;
import net.i2p.util.I2PThread; import net.i2p.util.I2PThread;
@ -27,6 +28,7 @@ public class MessageReceiver implements Runnable {
private List _completeMessages; private List _completeMessages;
private boolean _alive; private boolean _alive;
private ByteCache _cache; private ByteCache _cache;
private I2NPMessageHandler _handler;
public MessageReceiver(RouterContext ctx, UDPTransport transport) { public MessageReceiver(RouterContext ctx, UDPTransport transport) {
_context = ctx; _context = ctx;
@ -34,6 +36,7 @@ public class MessageReceiver implements Runnable {
_transport = transport; _transport = transport;
_completeMessages = new ArrayList(16); _completeMessages = new ArrayList(16);
_cache = ByteCache.getInstance(64, I2NPMessage.MAX_SIZE); _cache = ByteCache.getInstance(64, I2NPMessage.MAX_SIZE);
_handler = new I2NPMessageHandler(ctx);
_alive = true; _alive = true;
} }
@ -60,6 +63,8 @@ public class MessageReceiver implements Runnable {
public void run() { public void run() {
InboundMessageState message = null; InboundMessageState message = null;
ByteArray buf = _cache.acquire();
while (_alive) { while (_alive) {
try { try {
synchronized (_completeMessages) { synchronized (_completeMessages) {
@ -74,16 +79,18 @@ public class MessageReceiver implements Runnable {
int size = message.getCompleteSize(); int size = message.getCompleteSize();
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info("Full message received (" + message.getMessageId() + ") after " + message.getLifetime()); _log.info("Full message received (" + message.getMessageId() + ") after " + message.getLifetime());
I2NPMessage msg = readMessage(message); I2NPMessage msg = readMessage(buf, message);
if (msg != null) if (msg != null)
_transport.messageReceived(msg, null, message.getFrom(), message.getLifetime(), size); _transport.messageReceived(msg, null, message.getFrom(), message.getLifetime(), size);
message = null; message = null;
} }
} }
// no need to zero it out, as these buffers are only used with an explicit getCompleteSize
_cache.release(buf, false);
} }
private I2NPMessage readMessage(InboundMessageState state) { private I2NPMessage readMessage(ByteArray buf, InboundMessageState state) {
ByteArray buf = _cache.acquire();
try { try {
//byte buf[] = new byte[state.getCompleteSize()]; //byte buf[] = new byte[state.getCompleteSize()];
ByteArray fragments[] = state.getFragments(); ByteArray fragments[] = state.getFragments();
@ -102,7 +109,7 @@ public class MessageReceiver implements Runnable {
_log.error("Hmm, offset of the fragments = " + off + " while the state says " + state.getCompleteSize()); _log.error("Hmm, offset of the fragments = " + off + " while the state says " + state.getCompleteSize());
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Raw byte array for " + state.getMessageId() + ": " + Base64.encode(buf.getData(), 0, state.getCompleteSize())); _log.debug("Raw byte array for " + state.getMessageId() + ": " + Base64.encode(buf.getData(), 0, state.getCompleteSize()));
I2NPMessage m = I2NPMessageImpl.fromRawByteArray(_context, buf.getData(), 0, state.getCompleteSize()); I2NPMessage m = I2NPMessageImpl.fromRawByteArray(_context, buf.getData(), 0, state.getCompleteSize(), _handler);
m.setUniqueId(state.getMessageId()); m.setUniqueId(state.getMessageId());
return m; return m;
} catch (I2NPMessageException ime) { } catch (I2NPMessageException ime) {
@ -114,7 +121,6 @@ public class MessageReceiver implements Runnable {
return null; return null;
} finally { } finally {
state.releaseResources(); state.releaseResources();
_cache.release(buf);
} }
} }
} }

View File

@ -163,10 +163,18 @@ public class OutboundEstablishState {
return true; return true;
} }
generateSessionKey(); boolean valid = true;
decryptSignature(); try {
generateSessionKey();
} catch (DHSessionKeyBuilder.InvalidPublicParameterException ippe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Peer " + getRemoteHostId() + " sent us an invalid DH parameter (or were spoofed)", ippe);
valid = false;
}
if (valid)
decryptSignature();
if (verifySessionCreated()) { if (valid && verifySessionCreated()) {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Session created passed validation"); _log.debug("Session created passed validation");
return true; return true;
@ -191,7 +199,7 @@ public class OutboundEstablishState {
} }
} }
private void generateSessionKey() { private void generateSessionKey() throws DHSessionKeyBuilder.InvalidPublicParameterException {
if (_sessionKey != null) return; if (_sessionKey != null) return;
_keyBuilder.setPeerPublicValue(_receivedY); _keyBuilder.setPeerPublicValue(_receivedY);
_sessionKey = _keyBuilder.getSessionKey(); _sessionKey = _keyBuilder.getSessionKey();

View File

@ -509,7 +509,8 @@ public class PeerState {
synchronized (_currentACKs) { synchronized (_currentACKs) {
rv = new ArrayList(_currentACKs.size()); rv = new ArrayList(_currentACKs.size());
while ( (bytesRemaining >= 4) && (_currentACKs.size() > 0) ) { while ( (bytesRemaining >= 4) && (_currentACKs.size() > 0) ) {
rv.add(new FullACKBitfield((Long)_currentACKs.remove(0))); long id = ((Long)_currentACKs.remove(0)).longValue();
rv.add(new FullACKBitfield(id));
bytesRemaining -= 4; bytesRemaining -= 4;
} }
if (_currentACKs.size() <= 0) if (_currentACKs.size() <= 0)
@ -576,9 +577,9 @@ public class PeerState {
} }
/** represent a full ACK of a message */ /** represent a full ACK of a message */
private class FullACKBitfield implements ACKBitfield { private static class FullACKBitfield implements ACKBitfield {
private long _msgId; private long _msgId;
public FullACKBitfield(Long id) { _msgId = id.longValue(); } public FullACKBitfield(long id) { _msgId = id; }
public int fragmentCount() { return 0; } public int fragmentCount() { return 0; }
public long getMessageId() { return _msgId; } public long getMessageId() { return _msgId; }
public boolean received(int fragmentNum) { return true; } public boolean received(int fragmentNum) { return true; }

View File

@ -310,9 +310,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
boolean addRemotePeerState(PeerState peer) { boolean addRemotePeerState(PeerState peer) {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info("Add remote peer state: " + peer); _log.info("Add remote peer state: " + peer);
PeerState oldPeer = null;
if (peer.getRemotePeer() != null) { if (peer.getRemotePeer() != null) {
synchronized (_peersByIdent) { synchronized (_peersByIdent) {
PeerState oldPeer = (PeerState)_peersByIdent.put(peer.getRemotePeer(), peer); oldPeer = (PeerState)_peersByIdent.put(peer.getRemotePeer(), peer);
if ( (oldPeer != null) && (oldPeer != peer) ) { if ( (oldPeer != null) && (oldPeer != peer) ) {
// should we transfer the oldPeer's RTT/RTO/etc? nah // should we transfer the oldPeer's RTT/RTO/etc? nah
// or perhaps reject the new session? nah, // or perhaps reject the new session? nah,
@ -321,17 +322,24 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
} }
} }
if ( (oldPeer != null) && (_log.shouldLog(Log.WARN)) )
_log.warn("Peer already connected: old=" + oldPeer + " new=" + peer, new Exception("dup"));
oldPeer = null;
RemoteHostId remoteId = peer.getRemoteHostId(); RemoteHostId remoteId = peer.getRemoteHostId();
if (remoteId == null) return false; if (remoteId == null) return false;
synchronized (_peersByRemoteHost) { synchronized (_peersByRemoteHost) {
PeerState oldPeer = (PeerState)_peersByRemoteHost.put(remoteId, peer); oldPeer = (PeerState)_peersByRemoteHost.put(remoteId, peer);
if ( (oldPeer != null) && (oldPeer != peer) ) { if ( (oldPeer != null) && (oldPeer != peer) ) {
//_peersByRemoteHost.put(remoteString, oldPeer); //_peersByRemoteHost.put(remoteString, oldPeer);
//return false; //return false;
} }
} }
if ( (oldPeer != null) && (_log.shouldLog(Log.WARN)) )
_log.warn("Peer already connected: old=" + oldPeer + " new=" + peer, new Exception("dup"));
_activeThrottle.unchoke(peer.getRemotePeer()); _activeThrottle.unchoke(peer.getRemotePeer());
_context.shitlist().unshitlistRouter(peer.getRemotePeer()); _context.shitlist().unshitlistRouter(peer.getRemotePeer());
@ -348,7 +356,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
} }
private void dropPeer(PeerState peer, boolean shouldShitlist) { private void dropPeer(PeerState peer, boolean shouldShitlist) {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info("Dropping remote peer: " + peer); _log.info("Dropping remote peer: " + peer + " shitlist? " + shouldShitlist, new Exception("Dropped by"));
if (peer.getRemotePeer() != null) { if (peer.getRemotePeer() != null) {
if (shouldShitlist) { if (shouldShitlist) {
long now = _context.clock().now(); long now = _context.clock().now();

View File

@ -264,7 +264,7 @@ public class FragmentHandler {
SimpleTimer.getInstance().removeEvent(msg.getExpireEvent()); SimpleTimer.getInstance().removeEvent(msg.getExpireEvent());
receiveComplete(msg); receiveComplete(msg);
} else { } else {
noteReception(msg.getMessageId(), 0, msg.toString()); noteReception(msg.getMessageId(), 0, msg);
} }
if (isNew && fragmented && !msg.isComplete()) { if (isNew && fragmented && !msg.isComplete()) {
@ -326,7 +326,7 @@ public class FragmentHandler {
_context.statManager().addRateData("tunnel.fragmentedComplete", msg.getFragmentCount(), msg.getLifetime()); _context.statManager().addRateData("tunnel.fragmentedComplete", msg.getFragmentCount(), msg.getLifetime());
receiveComplete(msg); receiveComplete(msg);
} else { } else {
noteReception(msg.getMessageId(), fragmentNum, msg.toString()); noteReception(msg.getMessageId(), fragmentNum, msg);
} }
if (isNew && !msg.isComplete()) { if (isNew && !msg.isComplete()) {
@ -360,7 +360,7 @@ public class FragmentHandler {
} }
} }
protected void noteReception(long messageId, int fragmentId, String status) {} protected void noteReception(long messageId, int fragmentId, Object status) {}
protected void noteCompletion(long messageId) {} protected void noteCompletion(long messageId) {}
protected void noteFailure(long messageId, String status) {} protected void noteFailure(long messageId, String status) {}

View File

@ -16,7 +16,7 @@ public class RouterFragmentHandler extends FragmentHandler {
_log = context.logManager().getLog(RouterFragmentHandler.class); _log = context.logManager().getLog(RouterFragmentHandler.class);
} }
protected void noteReception(long messageId, int fragmentId, String status) { protected void noteReception(long messageId, int fragmentId, Object status) {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info("Received fragment " + fragmentId + " for message " + messageId + ": " + status); _log.info("Received fragment " + fragmentId + " for message " + messageId + ": " + status);
_routerContext.messageHistory().receiveTunnelFragment(messageId, fragmentId, status); _routerContext.messageHistory().receiveTunnelFragment(messageId, fragmentId, status);