- Deprecate unused stream methods and I2NPMessageReader since
      all transports provide encapsulation.
    - Don't throw IOE from byte array methods
    - Use cached null cert in GarlicClove
    - Add method to limit size of buffer to read
    - Don't check checksum at input, in most cases
    - Reuse checksum at output, for unomodified pass-through messages
      (but recalculating it now and logging on a mismatch for testing)
    - Fix DatabaseLookupMessage to internally store the don't include peers as
      a List, not a Set, so it doesn't get reordered and break the checksum
    - Log cleanup
  * NTCP:
    - Zero-copy and limit size when handing buffer to I2NP
    - Log hex dump message on I2NPMessageException, like in SSU
    - Don't close connection on I2NPMessageException
This commit is contained in:
zzz
2011-12-11 21:04:43 +00:00
parent 032b7d8230
commit 8448001a17
22 changed files with 671 additions and 284 deletions

View File

@@ -30,12 +30,19 @@ import net.i2p.util.SimpleByteCache;
public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPMessage {
protected final Log _log;
protected final I2PAppContext _context;
private long _expiration;
private long _uniqueId;
protected long _expiration;
protected long _uniqueId;
public final static long DEFAULT_EXPIRATION_MS = 1*60*1000; // 1 minute by default
public final static int CHECKSUM_LENGTH = 1; //Hash.HASH_LENGTH;
/** 16 */
public static final int HEADER_LENGTH = 1 // type
+ 4 // uniqueId
+ DataHelper.DATE_LENGTH // expiration
+ 2 // payload length
+ CHECKSUM_LENGTH;
// Whether SSU used the full header or a truncated header.
// We are stuck with the short header, can't change it now.
//private static final boolean RAW_FULL_SIZE = false;
@@ -62,8 +69,10 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
}
/**
* Read the whole message (including the type) and throw it away.
* @deprecated Unused, why would you do this
* Read the whole message but only if it's exactly 1024 bytes.
* Unused - All transports provide encapsulation and so we have byte arrays available.
*
* @deprecated unused
*/
public void readBytes(InputStream in) throws DataFormatException, IOException {
try {
@@ -77,6 +86,9 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
* Read the header, then read the rest into buffer, then call
* readMessage in the implemented message type
*
* This does a copy from the stream to the buffer, so if you already
* have a byte array, use the other readBytes() instead.
*
*<pre>
* Specifically:
* 1 byte type (if caller didn't read already, as specified by the type param
@@ -87,9 +99,12 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
* size bytes of payload (read by readMessage() in implementation)
*</pre>
*
* Unused - All transports provide encapsulation and so we have byte arrays available.
*
* @param type the message type or -1 if we should read it here
* @param buffer temp buffer to use
* @return total length of the message
* @deprecated unused
*/
public int readBytes(InputStream in, int type, byte buffer[]) throws I2NPMessageException, IOException {
try {
@@ -156,7 +171,24 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
* @param type the message type or -1 if we should read it here
* @return total length of the message
*/
public int readBytes(byte data[], int type, int offset) throws I2NPMessageException, IOException {
public int readBytes(byte data[], int type, int offset) throws I2NPMessageException {
return readBytes(data, type, offset, data.length - offset);
}
/**
* Set a limit on the max to read from the data buffer, so that
* we can use a large buffer but prevent the reader from reading off the end.
*
* @param maxLen read no more than this many bytes from data starting at offset, even if it is longer
* This includes the type byte only if type < 0
* @since 0.8.12
*/
public int readBytes(byte data[], int type, int offset, int maxLen) throws I2NPMessageException {
int headerSize = HEADER_LENGTH;
if (type >= 0)
headerSize--;
if (maxLen < headerSize)
throw new I2NPMessageException("Payload is too short " + maxLen);
int cur = offset;
if (type < 0) {
type = (int)DataHelper.fromLong(data, cur, 1);
@@ -174,15 +206,17 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
cur += CHECKSUM_LENGTH;
//h.setData(hdata);
if (cur + size > data.length)
if (cur + size > data.length || headerSize + size > maxLen)
throw new I2NPMessageException("Payload is too short ["
+ "data.len=" + data.length
+ "maxLen=" + maxLen
+ " offset=" + offset
+ " cur=" + cur
+ " wanted=" + size + "]: " + getClass().getName());
+ " wanted=" + size + "]: " + getClass().getSimpleName());
int sz = Math.min(size, maxLen - headerSize);
byte[] calc = SimpleByteCache.acquire(Hash.HASH_LENGTH);
_context.sha().calculateHash(data, cur, size, calc, 0);
_context.sha().calculateHash(data, cur, sz, calc, 0);
boolean eq = DataHelper.eq(hdata, 0, calc, 0, CHECKSUM_LENGTH);
SimpleByteCache.release(calc);
if (!eq)
@@ -191,14 +225,19 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
//long start = _context.clock().now();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Reading bytes: type = " + type + " / uniqueId : " + _uniqueId + " / expiration : " + _expiration);
readMessage(data, cur, size, type);
cur += size;
readMessage(data, cur, sz, type);
cur += sz;
//long time = _context.clock().now() - start;
//if (time > 50)
// _context.statManager().addRateData("i2np.readTime", time, time);
return cur - offset;
}
/**
* Don't do this if you need a byte array - use toByteArray()
*
* @deprecated unused
*/
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
int size = getMessageSize();
if (size < 15 + CHECKSUM_LENGTH) throw new DataFormatException("Unable to build the message");
@@ -242,31 +281,18 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
int written = toByteArray(data);
if (written != data.length) {
_log.log(Log.CRIT, "Error writing out " + data.length + " (written: " + written + ", msgSize: " + getMessageSize() +
", writtenLen: " + calculateWrittenLength() + ") for " + getClass().getName());
", writtenLen: " + calculateWrittenLength() + ") for " + getClass().getSimpleName());
return null;
}
return data;
}
public int toByteArray(byte buffer[]) {
//long start = _context.clock().now();
int prefixLen = 1 // type
+ 4 // uniqueId
+ DataHelper.DATE_LENGTH // expiration
+ 2 // payload length
+ CHECKSUM_LENGTH; // walnuts
//byte prefix[][] = new byte[][] { DataHelper.toLong(1, getType()),
// DataHelper.toLong(4, _uniqueId),
// DataHelper.toLong(DataHelper.DATE_LENGTH, _expiration),
// new byte[2],
// new byte[CHECKSUM_LENGTH]};
//byte suffix[][] = new byte[][] { };
try {
int writtenLen = writeMessageBody(buffer, prefixLen);
int payloadLen = writtenLen - prefixLen;
int writtenLen = writeMessageBody(buffer, HEADER_LENGTH);
int payloadLen = writtenLen - HEADER_LENGTH;
byte[] h = SimpleByteCache.acquire(Hash.HASH_LENGTH);
_context.sha().calculateHash(buffer, prefixLen, payloadLen, h, 0);
_context.sha().calculateHash(buffer, HEADER_LENGTH, payloadLen, h, 0);
int off = 0;
DataHelper.toLong(buffer, off, 1, getType());
@@ -280,25 +306,22 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
System.arraycopy(h, 0, buffer, off, CHECKSUM_LENGTH);
SimpleByteCache.release(h);
//long time = _context.clock().now() - start;
//if (time > 50)
// _context.statManager().addRateData("i2np.writeTime", time, time);
return writtenLen;
} catch (I2NPMessageException ime) {
_context.logManager().getLog(getClass()).log(Log.CRIT, "Error writing", ime);
throw new IllegalStateException("Unable to serialize the message (" + getClass().getName()
+ "): " + ime.getMessage());
throw new IllegalStateException("Unable to serialize the message " + getClass().getSimpleName(), ime);
}
}
/** calculate the message body's length (not including the header and footer */
protected abstract int calculateWrittenLength();
/**
* write the message body to the output array, starting at the given index.
* @return the index into the array after the last byte written
*/
protected abstract int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException;
/*
protected int toByteArray(byte out[], byte[][] prefix, byte[][] suffix) throws I2NPMessageException {
int curIndex = 0;
@@ -338,12 +361,11 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
return writeMessageBody(buffer, off);
} catch (I2NPMessageException ime) {
_context.logManager().getLog(getClass()).log(Log.CRIT, "Error writing", ime);
throw new IllegalStateException("Unable to serialize the message (" + getClass().getName()
+ "): " + ime.getMessage());
throw new IllegalStateException("Unable to serialize the message " + getClass().getSimpleName(), ime);
}
}
public void readMessage(byte data[], int offset, int dataSize, int type, I2NPMessageHandler handler) throws I2NPMessageException, IOException {
public void readMessage(byte data[], int offset, int dataSize, int type, I2NPMessageHandler handler) throws I2NPMessageException {
// ignore the handler (overridden in subclasses if necessary
try {
readMessage(data, offset, dataSize, type);
@@ -388,8 +410,6 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
msg.readMessage(buffer, offset, dataSize, type, handler);
msg.setMessageExpiration(expiration);
return msg;
} catch (IOException ioe) {
throw new I2NPMessageException("IO error reading raw message", ioe);
} catch (IllegalArgumentException iae) {
throw new I2NPMessageException("Corrupt message (negative expiration)", iae);
}