2004-10-08 jrandom

* Don't kill the establisher threads during a soft restart.
    * Attempt to validate the peer's routerInfo earlier during handshaking.
    * Revamp the AESOutputStream so it doesn't allocate any temporary objects
      during its operation.
This commit is contained in:
jrandom
2004-10-08 18:38:48 +00:00
committed by zzz
parent ff8674bca9
commit 730da3aa27
7 changed files with 161 additions and 58 deletions

View File

@ -226,8 +226,8 @@ public class AESInputStream extends FilterInputStream {
_log.warn("Decrypt got odd segment - " + trailing _log.warn("Decrypt got odd segment - " + trailing
+ " bytes pushed back for later decryption - corrupted or slow data stream perhaps?"); + " bytes pushed back for later decryption - corrupted or slow data stream perhaps?");
} else { } else {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.DEBUG))
_log.info(encrypted.length + " bytes makes up " + numBlocks + " blocks to decrypt normally"); _log.debug(encrypted.length + " bytes makes up " + numBlocks + " blocks to decrypt normally");
} }
for (int i = 0; i < numBlocks; i++) { for (int i = 0; i < numBlocks; i++) {
@ -313,9 +313,22 @@ public class AESInputStream extends FilterInputStream {
/** /**
* Test AESOutputStream/AESInputStream * Test AESOutputStream/AESInputStream
*/ */
public static void main(String args[]) { public static void main(String args[]) {
I2PAppContext ctx = new I2PAppContext(); I2PAppContext ctx = new I2PAppContext();
try {
System.out.println("pwd=" + new java.io.File(".").getAbsolutePath());
System.out.println("Beginning");
runTest(ctx);
} catch (Throwable e) {
ctx.logManager().getLog(AESInputStream.class).error("Fail", e);
}
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {}
System.out.println("Done");
}
private static void runTest(I2PAppContext ctx) {
Log log = ctx.logManager().getLog(AESInputStream.class); Log log = ctx.logManager().getLog(AESInputStream.class);
log.setMinimumPriority(Log.DEBUG);
byte orig[] = new byte[1024 * 32]; byte orig[] = new byte[1024 * 32];
RandomSource.getInstance().nextBytes(orig); RandomSource.getInstance().nextBytes(orig);
//byte orig[] = "you are my sunshine, my only sunshine".getBytes(); //byte orig[] = "you are my sunshine, my only sunshine".getBytes();
@ -351,10 +364,51 @@ public class AESInputStream extends FilterInputStream {
} }
log.info("Done testing 0 byte data"); log.info("Done testing 0 byte data");
for (int i = 0; i <= 32768; i++) {
orig = new byte[i];
ctx.random().nextBytes(orig);
try {
log.info("Testing " + orig.length);
runTest(ctx, orig, key, iv);
} catch (RuntimeException re) {
log.error("Error testing " + orig.length);
throw re;
}
}
/*
orig = new byte[615280];
RandomSource.getInstance().nextBytes(orig);
for (int i = 0; i < 20; i++) {
runTest(ctx, orig, key, iv);
}
log.info("Done testing 615280 byte data");
*/
/*
for (int i = 0; i < 100; i++) {
orig = new byte[ctx.random().nextInt(1024*1024)];
ctx.random().nextBytes(orig);
try {
runTest(ctx, orig, key, iv);
} catch (RuntimeException re) {
log.error("Error testing " + orig.length);
throw re;
}
}
log.info("Done testing 100 random lengths");
*/
orig = new byte[32]; orig = new byte[32];
RandomSource.getInstance().nextBytes(orig); RandomSource.getInstance().nextBytes(orig);
runOffsetTest(ctx, orig, key, iv); try {
runOffsetTest(ctx, orig, key, iv);
} catch (Exception e) {
log.info("Error running offset test", e);
}
log.info("Done testing offset test (it should have come back with a statement NOT EQUAL!)"); log.info("Done testing offset test (it should have come back with a statement NOT EQUAL!)");
@ -389,18 +443,19 @@ public class AESInputStream extends FilterInputStream {
Hash newHash = SHA256Generator.getInstance().calculateHash(fin); Hash newHash = SHA256Generator.getInstance().calculateHash(fin);
boolean eq = origHash.equals(newHash); boolean eq = origHash.equals(newHash);
if (eq) if (eq) {
log.info("Equal hashes. hash: " + origHash); //log.info("Equal hashes. hash: " + origHash);
else } else {
log.error("NOT EQUAL! \norig: \t" + Base64.encode(orig) + "\nnew : \t" + Base64.encode(fin)); throw new RuntimeException("NOT EQUAL! len=" + orig.length + "\norig: \t" + Base64.encode(orig) + "\nnew : \t" + Base64.encode(fin));
}
boolean ok = DataHelper.eq(orig, fin); boolean ok = DataHelper.eq(orig, fin);
log.debug("EQ data? " + ok + " origLen: " + orig.length + " fin.length: " + fin.length); log.debug("EQ data? " + ok + " origLen: " + orig.length + " fin.length: " + fin.length);
log.debug("Time to D(E(" + orig.length + ")): " + (end - start) + "ms"); log.debug("Time to D(E(" + orig.length + ")): " + (end - start) + "ms");
log.debug("Time to E(" + orig.length + "): " + (endE - start) + "ms"); log.debug("Time to E(" + orig.length + "): " + (endE - start) + "ms");
log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms"); log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms");
} catch (Throwable t) { } catch (IOException ioe) {
log.error("ERROR transferring", t); log.error("ERROR transferring", ioe);
} }
//try { Thread.sleep(5000); } catch (Throwable t) {} //try { Thread.sleep(5000); } catch (Throwable t) {}
} }
@ -441,15 +496,16 @@ public class AESInputStream extends FilterInputStream {
if (eq) if (eq)
log.info("Equal hashes. hash: " + origHash); log.info("Equal hashes. hash: " + origHash);
else else
log.error("NOT EQUAL! \norig: \t" + Base64.encode(orig) + "\nnew : \t" + Base64.encode(fin)); throw new RuntimeException("NOT EQUAL! len=" + orig.length + "\norig: \t" + Base64.encode(orig) + "\nnew : \t" + Base64.encode(fin));
boolean ok = DataHelper.eq(orig, fin); boolean ok = DataHelper.eq(orig, fin);
log.debug("EQ data? " + ok + " origLen: " + orig.length + " fin.length: " + fin.length); log.debug("EQ data? " + ok + " origLen: " + orig.length + " fin.length: " + fin.length);
log.debug("Time to D(E(" + orig.length + ")): " + (end - start) + "ms"); log.debug("Time to D(E(" + orig.length + ")): " + (end - start) + "ms");
log.debug("Time to E(" + orig.length + "): " + (endE - start) + "ms"); log.debug("Time to E(" + orig.length + "): " + (endE - start) + "ms");
log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms"); log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms");
} catch (RuntimeException re) {
} catch (Throwable t) { throw re;
log.error("ERROR transferring", t); } catch (IOException ioe) {
log.error("ERROR transferring", ioe);
} }
//try { Thread.sleep(5000); } catch (Throwable t) {} //try { Thread.sleep(5000); } catch (Throwable t) {}
} }

View File

@ -34,7 +34,15 @@ public class AESOutputStream extends FilterOutputStream {
private I2PAppContext _context; private I2PAppContext _context;
private SessionKey _key; private SessionKey _key;
private byte[] _lastBlock; private byte[] _lastBlock;
private ByteArrayOutputStream _inBuf; /**
* buffer containing the unwritten bytes. The first unwritten
* byte is _lastCommitted+1, and the last unwritten byte is _nextWrite-1
* (aka the next byte to be written on the array is _nextWrite)
*/
private byte[] _unencryptedBuf;
private byte _writeBlock[];
/** how many bytes have we been given since we flushed it to the stream? */
private int _writesSinceCommit;
private long _cumulativeProvided; // how many bytes provided to this stream private long _cumulativeProvided; // how many bytes provided to this stream
private long _cumulativeWritten; // how many bytes written to the underlying stream private long _cumulativeWritten; // how many bytes written to the underlying stream
private long _cumulativePadding; // how many bytes of padding written private long _cumulativePadding; // how many bytes of padding written
@ -51,31 +59,32 @@ public class AESOutputStream extends FilterOutputStream {
_key = key; _key = key;
_lastBlock = new byte[BLOCK_SIZE]; _lastBlock = new byte[BLOCK_SIZE];
System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE); System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE);
_inBuf = new ByteArrayOutputStream(MAX_BUF); _unencryptedBuf = new byte[MAX_BUF];
_writeBlock = new byte[BLOCK_SIZE];
_writesSinceCommit = 0;
} }
public void write(int val) throws IOException { public void write(int val) throws IOException {
_cumulativeProvided++; _cumulativeProvided++;
_inBuf.write(val); _unencryptedBuf[_writesSinceCommit++] = (byte)(val & 0xFF);
if (_inBuf.size() > MAX_BUF) doFlush(); if (_writesSinceCommit == _unencryptedBuf.length)
doFlush();
} }
public void write(byte src[]) throws IOException { public void write(byte src[]) throws IOException {
_cumulativeProvided += src.length; write(src, 0, src.length);
_inBuf.write(src);
if (_inBuf.size() > MAX_BUF) doFlush();
} }
public void write(byte src[], int off, int len) throws IOException { public void write(byte src[], int off, int len) throws IOException {
_cumulativeProvided += len; // i'm too lazy to unroll this into the partial writes (dealing with
_inBuf.write(src, off, len); // wrapping around the buffer size)
if (_inBuf.size() > MAX_BUF) doFlush(); for (int i = 0; i < len; i++)
write(src[i+off]);
} }
public void close() throws IOException { public void close() throws IOException {
flush(); flush();
out.close(); out.close();
_inBuf.reset();
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Cumulative bytes provided to this stream / written out / padded: " _log.debug("Cumulative bytes provided to this stream / written out / padded: "
+ _cumulativeProvided + "/" + _cumulativeWritten + "/" + _cumulativePadding); + _cumulativeProvided + "/" + _cumulativeWritten + "/" + _cumulativePadding);
@ -87,8 +96,10 @@ public class AESOutputStream extends FilterOutputStream {
} }
private void doFlush() throws IOException { private void doFlush() throws IOException {
writeEncrypted(_inBuf.toByteArray()); if (_log.shouldLog(Log.INFO))
_inBuf.reset(); _log.info("doFlush(): writesSinceCommit=" + _writesSinceCommit);
writeEncrypted();
_writesSinceCommit = 0;
} }
/** /**
@ -101,39 +112,37 @@ public class AESOutputStream extends FilterOutputStream {
* times). * times).
* *
*/ */
private void writeEncrypted(byte src[]) throws IOException { private void writeEncrypted() throws IOException {
if ((src == null) || (src.length == 0)) return; int numBlocks = _writesSinceCommit / (BLOCK_SIZE - 1);
int numBlocks = src.length / (BLOCK_SIZE - 1);
byte block[] = new byte[BLOCK_SIZE]; if (_log.shouldLog(Log.INFO))
_log.info("writeE(): #=" + _writesSinceCommit + " blocks=" + numBlocks);
for (int i = 0; i < numBlocks; i++) { for (int i = 0; i < numBlocks; i++) {
DataHelper.xor(src, i * 15, _lastBlock, 0, block, 0, 15); DataHelper.xor(_unencryptedBuf, i * 15, _lastBlock, 0, _writeBlock, 0, 15);
// the padding byte for "full" blocks // the padding byte for "full" blocks
block[BLOCK_SIZE - 1] = (byte)(_lastBlock[BLOCK_SIZE - 1] ^ 0x01); _writeBlock[BLOCK_SIZE - 1] = (byte)(_lastBlock[BLOCK_SIZE - 1] ^ 0x01);
_context.aes().encrypt(block, 0, block, 0, _key, _lastBlock, BLOCK_SIZE); _context.aes().encrypt(_writeBlock, 0, _writeBlock, 0, _key, _lastBlock, BLOCK_SIZE);
if (_log.shouldLog(Log.DEBUG)) out.write(_writeBlock);
_log.debug("Padding block " + i + " of " + numBlocks + " with 1 byte"); System.arraycopy(_writeBlock, 0, _lastBlock, 0, BLOCK_SIZE);
out.write(block);
System.arraycopy(block, 0, _lastBlock, 0, BLOCK_SIZE);
_cumulativeWritten += BLOCK_SIZE; _cumulativeWritten += BLOCK_SIZE;
_cumulativePadding++; _cumulativePadding++;
} }
if (src.length % 15 != 0) { if (_writesSinceCommit % 15 != 0) {
// we need to do non trivial padding // we need to do non trivial padding
int remainingBytes = src.length - numBlocks * 15; int remainingBytes = _writesSinceCommit - numBlocks * 15;
int paddingBytes = BLOCK_SIZE - remainingBytes; int paddingBytes = BLOCK_SIZE - remainingBytes;
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Padding " + src.length + " with " + paddingBytes + " bytes in " + numBlocks + " blocks"); _log.debug("Padding " + _writesSinceCommit + " with " + paddingBytes + " bytes in " + (numBlocks+1) + " blocks");
System.arraycopy(src, numBlocks * 15, block, 0, remainingBytes); System.arraycopy(_unencryptedBuf, numBlocks * 15, _writeBlock, 0, remainingBytes);
Arrays.fill(block, remainingBytes, BLOCK_SIZE, (byte) paddingBytes); Arrays.fill(_writeBlock, remainingBytes, BLOCK_SIZE, (byte) paddingBytes);
DataHelper.xor(block, 0, _lastBlock, 0, block, 0, BLOCK_SIZE); DataHelper.xor(_writeBlock, 0, _lastBlock, 0, _writeBlock, 0, BLOCK_SIZE);
_context.aes().encrypt(block, 0, block, 0, _key, _lastBlock, BLOCK_SIZE); _context.aes().encrypt(_writeBlock, 0, _writeBlock, 0, _key, _lastBlock, BLOCK_SIZE);
out.write(block); out.write(_writeBlock);
System.arraycopy(block, 0, _lastBlock, 0, BLOCK_SIZE); System.arraycopy(_writeBlock, 0, _lastBlock, 0, BLOCK_SIZE);
_cumulativePadding += paddingBytes; _cumulativePadding += paddingBytes;
_cumulativeWritten += BLOCK_SIZE; _cumulativeWritten += BLOCK_SIZE;
} }
} }
} }

View File

@ -1,4 +1,10 @@
$Id: history.txt,v 1.38 2004/10/07 14:19:52 jrandom Exp $ $Id: history.txt,v 1.39 2004/10/07 21:08:11 jrandom Exp $
2004-10-08 jrandom
* Don't kill the establisher threads during a soft restart.
* Attempt to validate the peer's routerInfo earlier during handshaking.
* Revamp the AESOutputStream so it doesn't allocate any temporary objects
during its operation.
2004-10-07 jrandom 2004-10-07 jrandom
* Reimplement the I2NP reading with less temporary memory allocation. * Reimplement the I2NP reading with less temporary memory allocation.

View File

@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
* *
*/ */
public class RouterVersion { public class RouterVersion {
public final static String ID = "$Revision: 1.46 $ $Date: 2004/10/07 14:19:52 $"; public final static String ID = "$Revision: 1.47 $ $Date: 2004/10/07 21:08:11 $";
public final static String VERSION = "0.4.1.1"; public final static String VERSION = "0.4.1.1";
public final static long BUILD = 12; public final static long BUILD = 13;
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

@ -437,7 +437,15 @@ public class ConnectionBuilder {
} }
_actualPeer = peer; _actualPeer = peer;
return true;
try {
_context.netDb().store(peer.getIdentity().getHash(), peer);
return true;
} catch (IllegalArgumentException iae) {
fail("Peer sent us bad info - " + _target.getIdentity().getHash().toBase64().substring(0,6)
+ ": " + iae.getMessage());
return false;
}
} catch (IOException ioe) { } catch (IOException ioe) {
fail("Error reading the verified info from " fail("Error reading the verified info from "
+ _target.getIdentity().calculateHash().toBase64().substring(0,6) + _target.getIdentity().calculateHash().toBase64().substring(0,6)
@ -584,7 +592,15 @@ public class ConnectionBuilder {
} }
_actualPeer = peer; _actualPeer = peer;
return true;
try {
_context.netDb().store(peer.getIdentity().getHash(), peer);
return true;
} catch (IllegalArgumentException iae) {
fail("Peer sent us bad info - " + _target.getIdentity().getHash().toBase64().substring(0,6)
+ ": " + iae.getMessage());
return false;
}
} catch (IOException ioe) { } catch (IOException ioe) {
fail("Error reading the verified info from " fail("Error reading the verified info from "
+ _target.getIdentity().calculateHash().toBase64().substring(0,6) + _target.getIdentity().calculateHash().toBase64().substring(0,6)
@ -645,7 +661,6 @@ public class ConnectionBuilder {
//_connectionOut = _rawOut; //_connectionOut = _rawOut;
Hash peer = _actualPeer.getIdentity().getHash(); Hash peer = _actualPeer.getIdentity().getHash();
_context.netDb().store(peer, _actualPeer);
_transport.getTagManager().replaceTag(peer, _nextConnectionTag, _key); _transport.getTagManager().replaceTag(peer, _nextConnectionTag, _key);
} }

View File

@ -443,7 +443,14 @@ public class ConnectionHandler {
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddhhmmssSSS"); SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddhhmmssSSS");
props.setProperty("SKEW", fmt.format(new Date(_context.clock().now()))); props.setProperty("SKEW", fmt.format(new Date(_context.clock().now())));
} else { } else {
status = STATUS_OK; try {
_context.netDb().store(_actualPeer.getIdentity().getHash(), _actualPeer);
status = STATUS_OK;
} catch (IllegalArgumentException iae) {
// bad peer info
status = STATUS_UNKNOWN;
props.setProperty("REASON", "RouterInfoFailed");
}
} }
baos.write(status); baos.write(status);
@ -460,7 +467,7 @@ public class ConnectionHandler {
verification.writeBytes(_rawOut); verification.writeBytes(_rawOut);
_rawOut.flush(); _rawOut.flush();
return handleStatus(status, clockSkew); return handleStatus(status, clockSkew);
} catch (IOException ioe) { } catch (IOException ioe) {
fail("Error writing the peer info to " + _from fail("Error writing the peer info to " + _from
+ ": " + ioe.getMessage(), ioe); + ": " + ioe.getMessage(), ioe);
@ -601,7 +608,14 @@ public class ConnectionHandler {
} else if (!sigOk) { } else if (!sigOk) {
status = STATUS_SIGNATURE_FAILED; status = STATUS_SIGNATURE_FAILED;
} else { } else {
status = STATUS_OK; try {
_context.netDb().store(_actualPeer.getIdentity().getHash(), _actualPeer);
status = STATUS_OK;
} catch (IllegalArgumentException iae) {
// bad peer info
status = STATUS_UNKNOWN;
props.setProperty("REASON", "RouterInfoFailed");
}
} }
if (_actualPeer.getIdentity().getHash().equals(_context.routerHash())) { if (_actualPeer.getIdentity().getHash().equals(_context.routerHash())) {
@ -827,7 +841,6 @@ public class ConnectionHandler {
//_connectionOut = _rawOut; //_connectionOut = _rawOut;
Hash peer = _actualPeer.getIdentity().getHash(); Hash peer = _actualPeer.getIdentity().getHash();
_context.netDb().store(peer, _actualPeer);
_transport.getTagManager().replaceTag(peer, _nextConnectionTag, _key); _transport.getTagManager().replaceTag(peer, _nextConnectionTag, _key);
} }

View File

@ -24,6 +24,10 @@ public class TCPConnectionEstablisher implements Runnable {
public void run() { public void run() {
while (true) { while (true) {
RouterInfo info = _transport.getNextPeer(); RouterInfo info = _transport.getNextPeer();
if (info == null) {
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
continue;
}
ConnectionBuilder cb = new ConnectionBuilder(_context, _transport, info); ConnectionBuilder cb = new ConnectionBuilder(_context, _transport, info);
TCPConnection con = null; TCPConnection con = null;