forked from I2P_Developers/i2p.i2p
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:
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -181,4 +181,4 @@ class I2CPMessageProducer {
|
|||||||
msg.setSessionId(session.getSessionId());
|
msg.setSessionId(session.getSessionId());
|
||||||
session.sendMessage(msg);
|
session.sendMessage(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -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 + "]");
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
17
history.txt
17
history.txt
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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?");
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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; }
|
||||||
|
@ -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();
|
||||||
|
@ -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) {}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
Reference in New Issue
Block a user