forked from I2P_Developers/i2p.i2p
* NTCP:
- More optimizations in recvEncrypted() - More efficient XOR - Reduce bandwidth stat update frequency - Check for repeated zero-length reads
This commit is contained in:
10
history.txt
10
history.txt
@ -1,3 +1,13 @@
|
|||||||
|
2011-11-23 zzz
|
||||||
|
* CryptixAESEngine: Fix bogus bounds checks
|
||||||
|
* NTCP:
|
||||||
|
- More optimizations in recvEncrypted()
|
||||||
|
- More efficient XOR
|
||||||
|
- Reduce bandwidth stat update frequency
|
||||||
|
- Check for repeated zero-length reads
|
||||||
|
* RandomSource: Add new method getBytes(buf, offset, length)
|
||||||
|
* Tunnel encryption: More efficient XOR
|
||||||
|
|
||||||
2011-11-21 zzz
|
2011-11-21 zzz
|
||||||
* NTCP Pumper:
|
* NTCP Pumper:
|
||||||
- Ensure failsafe pumper code gets run on schedule
|
- Ensure failsafe pumper code gets run on schedule
|
||||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 6;
|
public final static long BUILD = 7;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
@ -101,6 +101,8 @@ class EventPumper implements Runnable {
|
|||||||
_context.statManager().createRateStat("ntcp.pumperKeySetSize", "", "ntcp", new long[] {10*60*1000} );
|
_context.statManager().createRateStat("ntcp.pumperKeySetSize", "", "ntcp", new long[] {10*60*1000} );
|
||||||
_context.statManager().createRateStat("ntcp.pumperKeysPerLoop", "", "ntcp", new long[] {10*60*1000} );
|
_context.statManager().createRateStat("ntcp.pumperKeysPerLoop", "", "ntcp", new long[] {10*60*1000} );
|
||||||
_context.statManager().createRateStat("ntcp.pumperLoopsPerSecond", "", "ntcp", new long[] {10*60*1000} );
|
_context.statManager().createRateStat("ntcp.pumperLoopsPerSecond", "", "ntcp", new long[] {10*60*1000} );
|
||||||
|
_context.statManager().createRateStat("ntcp.zeroRead", "", "ntcp", new long[] {10*60*1000} );
|
||||||
|
_context.statManager().createRateStat("ntcp.zeroReadDrop", "", "ntcp", new long[] {10*60*1000} );
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void startPumping() {
|
public synchronized void startPumping() {
|
||||||
@ -561,7 +563,21 @@ class EventPumper implements Runnable {
|
|||||||
// stay interested
|
// stay interested
|
||||||
//key.interestOps(key.interestOps() | SelectionKey.OP_READ);
|
//key.interestOps(key.interestOps() | SelectionKey.OP_READ);
|
||||||
releaseBuf(buf);
|
releaseBuf(buf);
|
||||||
|
// workaround for channel stuck returning 0 all the time, causing 100% CPU
|
||||||
|
int consec = con.gotZeroRead();
|
||||||
|
if (consec >= 5) {
|
||||||
|
_context.statManager().addRateData("ntcp.zeroReadDrop", 1);
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Fail safe zero read close " + con);
|
||||||
|
con.close();
|
||||||
|
} else {
|
||||||
|
_context.statManager().addRateData("ntcp.zeroRead", consec);
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("nothing to read for " + con + ", but stay interested");
|
||||||
|
}
|
||||||
} else if (read > 0) {
|
} else if (read > 0) {
|
||||||
|
// clear counter for workaround above
|
||||||
|
con.clearZeroRead();
|
||||||
// ZERO COPY. The buffer will be returned in Reader.processRead()
|
// ZERO COPY. The buffer will be returned in Reader.processRead()
|
||||||
buf.flip();
|
buf.flip();
|
||||||
FIFOBandwidthLimiter.Request req = _context.bandwidthLimiter().requestInbound(read, "NTCP read"); //con, buf);
|
FIFOBandwidthLimiter.Request req = _context.bandwidthLimiter().requestInbound(read, "NTCP read"); //con, buf);
|
||||||
|
@ -106,15 +106,26 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
private long _messagesWritten;
|
private long _messagesWritten;
|
||||||
private long _lastSendTime;
|
private long _lastSendTime;
|
||||||
private long _lastReceiveTime;
|
private long _lastReceiveTime;
|
||||||
|
private long _lastRateUpdated;
|
||||||
private final long _created;
|
private final long _created;
|
||||||
private long _nextMetaTime;
|
private long _nextMetaTime;
|
||||||
|
private int _consecutiveZeroReads;
|
||||||
|
|
||||||
|
private static final int BLOCK_SIZE = 16;
|
||||||
|
private static final int META_SIZE = BLOCK_SIZE;
|
||||||
|
|
||||||
/** unencrypted outbound metadata buffer */
|
/** unencrypted outbound metadata buffer */
|
||||||
private final byte _meta[] = new byte[16];
|
private final byte _meta[] = new byte[META_SIZE];
|
||||||
private boolean _sendingMeta;
|
private boolean _sendingMeta;
|
||||||
/** how many consecutive sends were failed due to (estimated) send queue time */
|
/** how many consecutive sends were failed due to (estimated) send queue time */
|
||||||
private int _consecutiveBacklog;
|
private int _consecutiveBacklog;
|
||||||
private long _nextInfoTime;
|
private long _nextInfoTime;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update frequency for send/recv rates in console peers page
|
||||||
|
*/
|
||||||
|
private static final long STAT_UPDATE_TIME_MS = 30*1000;
|
||||||
|
|
||||||
private static final int META_FREQUENCY = 10*60*1000;
|
private static final int META_FREQUENCY = 10*60*1000;
|
||||||
/** how often we send our routerinfo unsolicited */
|
/** how often we send our routerinfo unsolicited */
|
||||||
private static final int INFO_FREQUENCY = 90*60*1000;
|
private static final int INFO_FREQUENCY = 90*60*1000;
|
||||||
@ -144,7 +155,7 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
// TODO possible switch to CLQ but beware non-constant size() - see below
|
// TODO possible switch to CLQ but beware non-constant size() - see below
|
||||||
_outbound = new LinkedBlockingQueue();
|
_outbound = new LinkedBlockingQueue();
|
||||||
_isInbound = true;
|
_isInbound = true;
|
||||||
_decryptBlockBuf = new byte[16];
|
_decryptBlockBuf = new byte[BLOCK_SIZE];
|
||||||
_curReadState = new ReadState();
|
_curReadState = new ReadState();
|
||||||
_establishState = new EstablishState(ctx, transport, this);
|
_establishState = new EstablishState(ctx, transport, this);
|
||||||
_conKey = key;
|
_conKey = key;
|
||||||
@ -169,7 +180,7 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
// TODO possible switch to CLQ but beware non-constant size() - see below
|
// TODO possible switch to CLQ but beware non-constant size() - see below
|
||||||
_outbound = new LinkedBlockingQueue();
|
_outbound = new LinkedBlockingQueue();
|
||||||
_isInbound = false;
|
_isInbound = false;
|
||||||
_decryptBlockBuf = new byte[16];
|
_decryptBlockBuf = new byte[BLOCK_SIZE];
|
||||||
_curReadState = new ReadState();
|
_curReadState = new ReadState();
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
@ -177,8 +188,9 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
private void initialize() {
|
private void initialize() {
|
||||||
_lastSendTime = _created;
|
_lastSendTime = _created;
|
||||||
_lastReceiveTime = _created;
|
_lastReceiveTime = _created;
|
||||||
_curReadBlock = new byte[16];
|
_lastRateUpdated = _created;
|
||||||
_prevReadBlock = new byte[16];
|
_curReadBlock = new byte[BLOCK_SIZE];
|
||||||
|
_prevReadBlock = new byte[BLOCK_SIZE];
|
||||||
_transport.establishing(this);
|
_transport.establishing(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,9 +212,9 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
_sessionKey = key;
|
_sessionKey = key;
|
||||||
_clockSkew = clockSkew;
|
_clockSkew = clockSkew;
|
||||||
_prevWriteEnd = prevWriteEnd;
|
_prevWriteEnd = prevWriteEnd;
|
||||||
System.arraycopy(prevReadEnd, prevReadEnd.length-16, _prevReadBlock, 0, _prevReadBlock.length);
|
System.arraycopy(prevReadEnd, prevReadEnd.length - BLOCK_SIZE, _prevReadBlock, 0, BLOCK_SIZE);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Inbound established, prevWriteEnd: " + Base64.encode(prevWriteEnd) + " prevReadEnd: " + Base64.encode(prevReadEnd));
|
// _log.debug("Inbound established, prevWriteEnd: " + Base64.encode(prevWriteEnd) + " prevReadEnd: " + Base64.encode(prevReadEnd));
|
||||||
_established = true;
|
_established = true;
|
||||||
_establishedOn = System.currentTimeMillis();
|
_establishedOn = System.currentTimeMillis();
|
||||||
_transport.inboundEstablished(this);
|
_transport.inboundEstablished(this);
|
||||||
@ -241,7 +253,24 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
public long getTimeSinceCreated() { return System.currentTimeMillis()-_created; }
|
public long getTimeSinceCreated() { return System.currentTimeMillis()-_created; }
|
||||||
|
|
||||||
public int getConsecutiveBacklog() { return _consecutiveBacklog; }
|
public int getConsecutiveBacklog() { return _consecutiveBacklog; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* workaround for EventPumper
|
||||||
|
* @since 0.8.12
|
||||||
|
*/
|
||||||
|
public void clearZeroRead() {
|
||||||
|
_consecutiveZeroReads = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* workaround for EventPumper
|
||||||
|
* @return value after incrementing
|
||||||
|
* @since 0.8.12
|
||||||
|
*/
|
||||||
|
public int gotZeroRead() {
|
||||||
|
return ++_consecutiveZeroReads;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isClosed() { return _closed; }
|
public boolean isClosed() { return _closed; }
|
||||||
public void close() { close(false); }
|
public void close() { close(false); }
|
||||||
public void close(boolean allowRequeue) {
|
public void close(boolean allowRequeue) {
|
||||||
@ -441,7 +470,7 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
_sessionKey = key;
|
_sessionKey = key;
|
||||||
_clockSkew = clockSkew;
|
_clockSkew = clockSkew;
|
||||||
_prevWriteEnd = prevWriteEnd;
|
_prevWriteEnd = prevWriteEnd;
|
||||||
System.arraycopy(prevReadEnd, prevReadEnd.length-16, _prevReadBlock, 0, _prevReadBlock.length);
|
System.arraycopy(prevReadEnd, prevReadEnd.length - BLOCK_SIZE, _prevReadBlock, 0, BLOCK_SIZE);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Outbound established, prevWriteEnd: " + Base64.encode(prevWriteEnd) + " prevReadEnd: " + Base64.encode(prevReadEnd));
|
_log.debug("Outbound established, prevWriteEnd: " + Base64.encode(prevWriteEnd) + " prevReadEnd: " + Base64.encode(prevReadEnd));
|
||||||
|
|
||||||
@ -595,8 +624,8 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
synchronized void prepareNextWriteFast() {
|
synchronized void prepareNextWriteFast() {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("prepare next write w/ isInbound? " + _isInbound + " established? " + _established);
|
// _log.debug("prepare next write w/ isInbound? " + _isInbound + " established? " + _established);
|
||||||
if (!_isInbound && !_established) {
|
if (!_isInbound && !_established) {
|
||||||
if (_establishState == null) {
|
if (_establishState == null) {
|
||||||
_establishState = new EstablishState(_context, _transport, this);
|
_establishState = new EstablishState(_context, _transport, this);
|
||||||
@ -715,13 +744,12 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
if (rem > 0)
|
if (rem > 0)
|
||||||
padding = 16 - rem;
|
padding = 16 - rem;
|
||||||
|
|
||||||
buf.padLength = padding;
|
|
||||||
buf.unencryptedLength = min+padding;
|
buf.unencryptedLength = min+padding;
|
||||||
DataHelper.toLong(buf.unencrypted, 0, 2, sz);
|
DataHelper.toLong(buf.unencrypted, 0, 2, sz);
|
||||||
System.arraycopy(buf.base, 0, buf.unencrypted, 2, buf.baseLength);
|
System.arraycopy(buf.base, 0, buf.unencrypted, 2, buf.baseLength);
|
||||||
if (padding > 0)
|
if (padding > 0) {
|
||||||
_context.random().nextBytes(buf.pad); // maybe more than necessary, but its only the prng
|
_context.random().nextBytes(buf.unencrypted, 2+sz, padding);
|
||||||
System.arraycopy(buf.pad, 0, buf.unencrypted, 2+sz, buf.padLength);
|
}
|
||||||
|
|
||||||
//long serialized = System.currentTimeMillis();
|
//long serialized = System.currentTimeMillis();
|
||||||
buf.crc.update(buf.unencrypted, 0, buf.unencryptedLength-4);
|
buf.crc.update(buf.unencrypted, 0, buf.unencryptedLength-4);
|
||||||
@ -765,22 +793,18 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
int unencryptedLength;
|
int unencryptedLength;
|
||||||
final byte base[];
|
final byte base[];
|
||||||
int baseLength;
|
int baseLength;
|
||||||
final byte pad[];
|
|
||||||
int padLength;
|
|
||||||
final Adler32 crc;
|
final Adler32 crc;
|
||||||
byte encrypted[];
|
byte encrypted[];
|
||||||
|
|
||||||
PrepBuffer() {
|
PrepBuffer() {
|
||||||
unencrypted = new byte[BUFFER_SIZE];
|
unencrypted = new byte[BUFFER_SIZE];
|
||||||
base = new byte[BUFFER_SIZE];
|
base = new byte[BUFFER_SIZE];
|
||||||
pad = new byte[16];
|
|
||||||
crc = new Adler32();
|
crc = new Adler32();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
unencryptedLength = 0;
|
unencryptedLength = 0;
|
||||||
baseLength = 0;
|
baseLength = 0;
|
||||||
padLength = 0;
|
|
||||||
encrypted = null;
|
encrypted = null;
|
||||||
crc.reset();
|
crc.reset();
|
||||||
}
|
}
|
||||||
@ -948,20 +972,24 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
private long _lastBytesReceived;
|
private long _lastBytesReceived;
|
||||||
/** _bytesSent when we last updated the rate */
|
/** _bytesSent when we last updated the rate */
|
||||||
private long _lastBytesSent;
|
private long _lastBytesSent;
|
||||||
private long _lastRateUpdated;
|
|
||||||
private float _sendBps;
|
private float _sendBps;
|
||||||
private float _recvBps;
|
private float _recvBps;
|
||||||
private float _sendBps15s;
|
//private float _sendBps15s;
|
||||||
private float _recvBps15s;
|
//private float _recvBps15s;
|
||||||
|
|
||||||
public float getSendRate() { return _sendBps15s; }
|
public float getSendRate() { return _sendBps; }
|
||||||
public float getRecvRate() { return _recvBps15s; }
|
public float getRecvRate() { return _recvBps; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stats only for console
|
||||||
|
*/
|
||||||
private void updateStats() {
|
private void updateStats() {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
long time = now - _lastRateUpdated;
|
long time = now - _lastRateUpdated;
|
||||||
// If at least one second has passed
|
// If enough time has passed...
|
||||||
if (time >= 1000) {
|
// Perhaps should synchronize, but if so do the time check before synching...
|
||||||
|
// only for console so don't bother....
|
||||||
|
if (time >= STAT_UPDATE_TIME_MS) {
|
||||||
long totS = _bytesSent;
|
long totS = _bytesSent;
|
||||||
long totR = _bytesReceived;
|
long totR = _bytesReceived;
|
||||||
long sent = totS - _lastBytesSent; // How much we sent meanwhile
|
long sent = totS - _lastBytesSent; // How much we sent meanwhile
|
||||||
@ -976,14 +1004,14 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
// Maintain an approximate average with a 15-second halflife
|
// Maintain an approximate average with a 15-second halflife
|
||||||
// Weights (0.955 and 0.045) are tuned so that transition between two values (e.g. 0..10)
|
// Weights (0.955 and 0.045) are tuned so that transition between two values (e.g. 0..10)
|
||||||
// would reach their midpoint (e.g. 5) in 15s
|
// would reach their midpoint (e.g. 5) in 15s
|
||||||
_sendBps15s = (0.955f)*_sendBps15s + (0.045f)*((float)sent*1000f)/(float)time;
|
//_sendBps15s = (0.955f)*_sendBps15s + (0.045f)*((float)sent*1000f)/(float)time;
|
||||||
_recvBps15s = (0.955f)*_recvBps15s + (0.045f)*((float)recv*1000)/(float)time;
|
//_recvBps15s = (0.955f)*_recvBps15s + (0.045f)*((float)recv*1000)/(float)time;
|
||||||
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Rates updated to "
|
_log.debug("Rates updated to "
|
||||||
+ _sendBps + "/" + _recvBps + "Bps in/out ("
|
+ _sendBps + '/' + _recvBps + "Bps in/out "
|
||||||
+ _sendBps15s + "/" + _recvBps15s + "Bps in/out 15s) after "
|
//+ _sendBps15s + "/" + _recvBps15s + "Bps in/out 15s after "
|
||||||
+ sent + "/" + recv + " in " + time);
|
+ sent + '/' + recv + " in " + DataHelper.formatDuration(time));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1003,17 +1031,30 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
synchronized void recvEncryptedI2NP(ByteBuffer buf) {
|
synchronized void recvEncryptedI2NP(ByteBuffer buf) {
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
// _log.debug("receive encrypted i2np: " + buf.remaining());
|
// _log.debug("receive encrypted i2np: " + buf.remaining());
|
||||||
|
// hasArray() is false for direct buffers, at least on my system...
|
||||||
|
if (_curReadBlockIndex == 0 && buf.hasArray()) {
|
||||||
|
// fast way
|
||||||
|
int tot = buf.remaining();
|
||||||
|
if (tot >= 32 && tot % 16 == 0) {
|
||||||
|
recvEncryptedFast(buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (buf.hasRemaining() && !_closed) {
|
while (buf.hasRemaining() && !_closed) {
|
||||||
int want = Math.min(buf.remaining(), _curReadBlock.length-_curReadBlockIndex);
|
int want = Math.min(buf.remaining(), BLOCK_SIZE - _curReadBlockIndex);
|
||||||
if (want > 0) {
|
if (want > 0) {
|
||||||
buf.get(_curReadBlock, _curReadBlockIndex, want);
|
buf.get(_curReadBlock, _curReadBlockIndex, want);
|
||||||
_curReadBlockIndex += want;
|
_curReadBlockIndex += want;
|
||||||
}
|
}
|
||||||
//_curReadBlock[_curReadBlockIndex++] = buf.get();
|
//_curReadBlock[_curReadBlockIndex++] = buf.get();
|
||||||
if (_curReadBlockIndex >= _curReadBlock.length) {
|
if (_curReadBlockIndex >= BLOCK_SIZE) {
|
||||||
// cbc
|
// cbc
|
||||||
_context.aes().decryptBlock(_curReadBlock, 0, _sessionKey, _decryptBlockBuf, 0);
|
_context.aes().decryptBlock(_curReadBlock, 0, _sessionKey, _decryptBlockBuf, 0);
|
||||||
DataHelper.xor(_decryptBlockBuf, 0, _prevReadBlock, 0, _decryptBlockBuf, 0, _decryptBlockBuf.length);
|
//DataHelper.xor(_decryptBlockBuf, 0, _prevReadBlock, 0, _decryptBlockBuf, 0, BLOCK_SIZE);
|
||||||
|
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||||
|
_decryptBlockBuf[i] ^= _prevReadBlock[i];
|
||||||
|
}
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
// _log.debug("parse decrypted i2np block (remaining: " + buf.remaining() + ")");
|
// _log.debug("parse decrypted i2np block (remaining: " + buf.remaining() + ")");
|
||||||
boolean ok = recvUnencryptedI2NP();
|
boolean ok = recvUnencryptedI2NP();
|
||||||
@ -1029,6 +1070,51 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt directly out of the ByteBuffer instead of copying the bytes
|
||||||
|
* 16 at a time to the _curReadBlock / _prevReadBlock flip buffers.
|
||||||
|
*
|
||||||
|
* More efficient but can only be used if buf.hasArray == true AND
|
||||||
|
* _curReadBlockIndex must be 0 and buf.getRemaining() % 16 must be 0
|
||||||
|
* and buf.getRemaining() must be >= 16.
|
||||||
|
* All this is true for most buffers.
|
||||||
|
* In theory this could be fixed up to handle the other cases too but that's hard.
|
||||||
|
* Caller must synchronize!
|
||||||
|
* @since 0.8.12
|
||||||
|
*/
|
||||||
|
private void recvEncryptedFast(ByteBuffer buf) {
|
||||||
|
byte[] array = buf.array();
|
||||||
|
int pos = buf.arrayOffset();
|
||||||
|
int end = pos + buf.remaining();
|
||||||
|
boolean first = true;
|
||||||
|
|
||||||
|
for ( ; pos < end && !_closed; pos += BLOCK_SIZE) {
|
||||||
|
_context.aes().decryptBlock(array, pos, _sessionKey, _decryptBlockBuf, 0);
|
||||||
|
if (first) {
|
||||||
|
// XOR with _prevReadBlock the first time...
|
||||||
|
//DataHelper.xor(_decryptBlockBuf, 0, _prevReadBlock, 0, _decryptBlockBuf, 0, BLOCK_SIZE);
|
||||||
|
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||||
|
_decryptBlockBuf[i] ^= _prevReadBlock[i];
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
//DataHelper.xor(_decryptBlockBuf, 0, array, pos - BLOCK_SIZE, _decryptBlockBuf, 0, BLOCK_SIZE);
|
||||||
|
int start = pos - BLOCK_SIZE;
|
||||||
|
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||||
|
_decryptBlockBuf[i] ^= array[start + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean ok = recvUnencryptedI2NP();
|
||||||
|
if (!ok) {
|
||||||
|
_log.error("Read buffer " + System.identityHashCode(buf) + " contained corrupt data");
|
||||||
|
_context.statManager().addRateData("ntcp.corruptDecryptedI2NP", 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ...and copy to _prevReadBlock the last time
|
||||||
|
System.arraycopy(array, end - BLOCK_SIZE, _prevReadBlock, 0, BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append the next 16 bytes of cleartext to the read state.
|
* Append the next 16 bytes of cleartext to the read state.
|
||||||
@ -1038,6 +1124,7 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
*/
|
*/
|
||||||
private boolean recvUnencryptedI2NP() {
|
private boolean recvUnencryptedI2NP() {
|
||||||
_curReadState.receiveBlock(_decryptBlockBuf);
|
_curReadState.receiveBlock(_decryptBlockBuf);
|
||||||
|
// FIXME move check to ReadState; must we close? possible attack vector?
|
||||||
if (_curReadState.getSize() > BUFFER_SIZE) {
|
if (_curReadState.getSize() > BUFFER_SIZE) {
|
||||||
_log.error("I2NP message too big - size: " + _curReadState.getSize() + " Dropping " + toString());
|
_log.error("I2NP message too big - size: " + _curReadState.getSize() + " Dropping " + toString());
|
||||||
_context.statManager().addRateData("ntcp.corruptTooLargeI2NP", _curReadState.getSize());
|
_context.statManager().addRateData("ntcp.corruptTooLargeI2NP", _curReadState.getSize());
|
||||||
@ -1087,12 +1174,23 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One special case is a metadata message where the sizeof(data) is 0. In
|
||||||
|
* that case, the unencrypted message is encoded as:
|
||||||
|
*<pre>
|
||||||
|
* +-------+-------+-------+-------+-------+-------+-------+-------+
|
||||||
|
* | 0 | timestamp in seconds | uninterpreted
|
||||||
|
* +-------+-------+-------+-------+-------+-------+-------+-------+
|
||||||
|
* uninterpreted | adler checksum of sz+data+pad |
|
||||||
|
* +-------+-------+-------+-------+-------+-------+-------+-------+
|
||||||
|
*</pre>
|
||||||
|
*/
|
||||||
private void sendMeta() {
|
private void sendMeta() {
|
||||||
byte encrypted[] = new byte[_meta.length];
|
byte encrypted[] = new byte[_meta.length];
|
||||||
synchronized (_meta) {
|
synchronized (_meta) {
|
||||||
_context.random().nextBytes(_meta); // randomize the uninterpreted, then overwrite w/ data
|
|
||||||
DataHelper.toLong(_meta, 0, 2, 0);
|
DataHelper.toLong(_meta, 0, 2, 0);
|
||||||
DataHelper.toLong(_meta, 2, 4, (_context.clock().now() + 500) / 1000);
|
DataHelper.toLong(_meta, 2, 4, (_context.clock().now() + 500) / 1000);
|
||||||
|
_context.random().nextBytes(_meta, 6, 6);
|
||||||
Adler32 crc = new Adler32();
|
Adler32 crc = new Adler32();
|
||||||
crc.update(_meta, 0, _meta.length-4);
|
crc.update(_meta, 0, _meta.length-4);
|
||||||
DataHelper.toLong(_meta, _meta.length-4, 4, crc.getValue());
|
DataHelper.toLong(_meta, _meta.length-4, 4, crc.getValue());
|
||||||
@ -1234,13 +1332,12 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
|
|
||||||
/** @param buf 16 bytes */
|
/** @param buf 16 bytes */
|
||||||
private void receiveInitial(byte buf[]) {
|
private void receiveInitial(byte buf[]) {
|
||||||
_stateBegin = System.currentTimeMillis();
|
|
||||||
_size = (int)DataHelper.fromLong(buf, 0, 2);
|
_size = (int)DataHelper.fromLong(buf, 0, 2);
|
||||||
if (_size == 0) {
|
if (_size == 0) {
|
||||||
readMeta(buf);
|
readMeta(buf);
|
||||||
init();
|
init();
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
|
_stateBegin = System.currentTimeMillis();
|
||||||
_dataBuf = acquireReadBuf();
|
_dataBuf = acquireReadBuf();
|
||||||
System.arraycopy(buf, 2, _dataBuf.data, 0, buf.length-2);
|
System.arraycopy(buf, 2, _dataBuf.data, 0, buf.length-2);
|
||||||
_nextWrite += buf.length-2;
|
_nextWrite += buf.length-2;
|
||||||
@ -1262,6 +1359,7 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
remaining -= blockUsed;
|
remaining -= blockUsed;
|
||||||
}
|
}
|
||||||
if ( (remaining <= 0) && (buf.length-blockUsed < 4) ) {
|
if ( (remaining <= 0) && (buf.length-blockUsed < 4) ) {
|
||||||
|
// we've received all the data but not the 4-byte checksum
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("crc wraparound required on block " + _blocks + " in message " + _messagesRead);
|
_log.debug("crc wraparound required on block " + _blocks + " in message " + _messagesRead);
|
||||||
_crc.update(buf);
|
_crc.update(buf);
|
||||||
@ -1284,8 +1382,8 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
_expectedCrc = DataHelper.fromLong(buf, buf.length-4, 4);
|
_expectedCrc = DataHelper.fromLong(buf, buf.length-4, 4);
|
||||||
_crc.update(buf, 0, buf.length-4);
|
_crc.update(buf, 0, buf.length-4);
|
||||||
long val = _crc.getValue();
|
long val = _crc.getValue();
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("CRC value computed: " + val + " expected: " + _expectedCrc + " size: " + _size);
|
// _log.debug("CRC value computed: " + val + " expected: " + _expectedCrc + " size: " + _size);
|
||||||
if (val == _expectedCrc) {
|
if (val == _expectedCrc) {
|
||||||
try {
|
try {
|
||||||
I2NPMessageHandler h = acquireHandler(_context);
|
I2NPMessageHandler h = acquireHandler(_context);
|
||||||
@ -1329,6 +1427,7 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Error parsing I2NP message", ime);
|
_log.warn("Error parsing I2NP message", ime);
|
||||||
_context.statManager().addRateData("ntcp.corruptI2NPIME", 1);
|
_context.statManager().addRateData("ntcp.corruptI2NPIME", 1);
|
||||||
|
// FIXME don't close the con, possible attack vector?
|
||||||
close();
|
close();
|
||||||
// handler and databuf are lost
|
// handler and databuf are lost
|
||||||
return;
|
return;
|
||||||
@ -1337,7 +1436,7 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
|||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("CRC incorrect for message " + _messagesRead + " (calc=" + val + " expected=" + _expectedCrc + ") size=" + _size + " blocks " + _blocks);
|
_log.warn("CRC incorrect for message " + _messagesRead + " (calc=" + val + " expected=" + _expectedCrc + ") size=" + _size + " blocks " + _blocks);
|
||||||
_context.statManager().addRateData("ntcp.corruptI2NPCRC", 1);
|
_context.statManager().addRateData("ntcp.corruptI2NPCRC", 1);
|
||||||
// FIXME should we try to read in the message and keep going?
|
// FIXME don't close the con, possible attack vector?
|
||||||
close();
|
close();
|
||||||
// databuf is lost
|
// databuf is lost
|
||||||
return;
|
return;
|
||||||
|
@ -756,16 +756,18 @@ public class NTCPTransport extends TransportImpl {
|
|||||||
buf.append(DataHelper.formatDuration2(con.getTimeSinceReceive()));
|
buf.append(DataHelper.formatDuration2(con.getTimeSinceReceive()));
|
||||||
buf.append(THINSP).append(DataHelper.formatDuration2(con.getTimeSinceSend()));
|
buf.append(THINSP).append(DataHelper.formatDuration2(con.getTimeSinceSend()));
|
||||||
buf.append("</td><td class=\"cells\" align=\"right\">");
|
buf.append("</td><td class=\"cells\" align=\"right\">");
|
||||||
if (con.getTimeSinceReceive() < 10*1000) {
|
if (con.getTimeSinceReceive() < 2*60*1000) {
|
||||||
buf.append(formatRate(con.getRecvRate()/1024));
|
float r = con.getRecvRate();
|
||||||
bpsRecv += con.getRecvRate();
|
buf.append(formatRate(r / 1024));
|
||||||
|
bpsRecv += r;
|
||||||
} else {
|
} else {
|
||||||
buf.append(formatRate(0));
|
buf.append(formatRate(0));
|
||||||
}
|
}
|
||||||
buf.append(THINSP);
|
buf.append(THINSP);
|
||||||
if (con.getTimeSinceSend() < 10*1000) {
|
if (con.getTimeSinceSend() < 2*60*1000) {
|
||||||
buf.append(formatRate(con.getSendRate()/1024));
|
float r = con.getSendRate();
|
||||||
bpsSend += con.getSendRate();
|
buf.append(formatRate(r / 1024));
|
||||||
|
bpsSend += r;
|
||||||
} else {
|
} else {
|
||||||
buf.append(formatRate(0));
|
buf.append(formatRate(0));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user