forked from I2P_Developers/i2p.i2p
* I2NP:
- 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:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user