forked from I2P_Developers/i2p.i2p
* I2NP:
- Earlier detection and better logging of truncated TunnelGatewayMessage and DatabaseStoreMessage - Fix and enhance UnknownI2NPMessage implementation - Don't deserialize or verify the checksum of the embeddedI2NP message in the TunnelGatewayMessage at the IBGW, just use UnknownI2NPMessage and pass it along, except if zero hop; Still to do: similar thing at OBEP - Round expiration times when converting to/from seconds for SSU - Cleanups and javadoc
This commit is contained in:
@@ -24,6 +24,9 @@ import net.i2p.data.TunnelId;
|
||||
* Defines the message a router sends to another router to test the network
|
||||
* database reachability, as well as the reply message sent back.
|
||||
*
|
||||
* TODO: Don't decompress and recompress RouterInfos at the OBEP and IBGW.
|
||||
* Could this even change the message length or corrupt things?
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
@@ -128,14 +131,22 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
_dbEntry = new RouterInfo();
|
||||
int compressedSize = (int)DataHelper.fromLong(data, curIndex, 2);
|
||||
curIndex += 2;
|
||||
if (compressedSize <= 0 || curIndex + compressedSize > data.length || (curIndex - offset) + compressedSize > dataSize)
|
||||
throw new I2NPMessageException("Compressed RI length: " + compressedSize +
|
||||
" but remaining bytes: " + Math.min(data.length - curIndex, dataSize - (curIndex - offset)));
|
||||
|
||||
try {
|
||||
// TODO we could delay decompression, just copy to a new byte array and store in _byteCache
|
||||
// May not be necessary since the IBGW now uses UnknownI2NPMessage.
|
||||
// DSMs at the OBEP are generally garlic wrapped, so the OBEP won't see it.
|
||||
// If we do delay it, getEntry() will have to check if _dbEntry is null and _byteCache
|
||||
// is non-null, and then decompress.
|
||||
byte decompressed[] = DataHelper.decompress(data, curIndex, compressedSize);
|
||||
_dbEntry.readBytes(new ByteArrayInputStream(decompressed));
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error reading the routerInfo", dfe);
|
||||
} catch (IOException ioe) {
|
||||
throw new I2NPMessageException("Compressed routerInfo was corrupt", ioe);
|
||||
throw new I2NPMessageException("Corrupt compressed routerInfo size = " + compressedSize, ioe);
|
||||
}
|
||||
} else {
|
||||
throw new I2NPMessageException("Invalid type of key read from the structure - " + type);
|
||||
@@ -145,17 +156,29 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
}
|
||||
|
||||
|
||||
/** calculate the message body's length (not including the header and footer */
|
||||
/**
|
||||
* calculate the message body's length (not including the header and footer)
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
protected int calculateWrittenLength() {
|
||||
// TODO if _byteCache is non-null, don't check _dbEntry
|
||||
if (_dbEntry == null)
|
||||
throw new IllegalStateException("Missing entry");
|
||||
int len = Hash.HASH_LENGTH + 1 + 4; // key+type+replyToken
|
||||
if (_replyToken > 0)
|
||||
len += 4 + Hash.HASH_LENGTH; // replyTunnel+replyGateway
|
||||
int type = _dbEntry.getType();
|
||||
if (type == DatabaseEntry.KEY_TYPE_LEASESET) {
|
||||
_byteCache = _dbEntry.toByteArray();
|
||||
if (_byteCache == null) {
|
||||
_byteCache = _dbEntry.toByteArray();
|
||||
}
|
||||
} else if (type == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
|
||||
byte uncompressed[] = _dbEntry.toByteArray();
|
||||
_byteCache = DataHelper.compress(uncompressed);
|
||||
// only decompress once
|
||||
if (_byteCache == null) {
|
||||
byte uncompressed[] = _dbEntry.toByteArray();
|
||||
_byteCache = DataHelper.compress(uncompressed);
|
||||
}
|
||||
len += 2;
|
||||
} else {
|
||||
throw new IllegalStateException("Invalid key type " + type);
|
||||
|
@@ -12,7 +12,6 @@ import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Defines the wrapped garlic message
|
||||
@@ -20,7 +19,6 @@ import net.i2p.util.Log;
|
||||
* @author jrandom
|
||||
*/
|
||||
public class GarlicMessage extends I2NPMessageImpl {
|
||||
private final static Log _log = new Log(GarlicMessage.class);
|
||||
public final static int MESSAGE_TYPE = 11;
|
||||
private byte[] _data;
|
||||
|
||||
|
@@ -49,8 +49,9 @@ public class I2NPMessageHandler {
|
||||
int type = (int)DataHelper.readLong(in, 1);
|
||||
_lastReadBegin = System.currentTimeMillis();
|
||||
I2NPMessage msg = I2NPMessageImpl.createMessage(_context, type);
|
||||
if (msg == null)
|
||||
throw new I2NPMessageException("The type "+ type + " is an unknown I2NP message");
|
||||
// can't be null
|
||||
//if (msg == null)
|
||||
// throw new I2NPMessageException("The type "+ type + " is an unknown I2NP message");
|
||||
try {
|
||||
_lastSize = msg.readBytes(in, type, _messageBuffer);
|
||||
} catch (IOException ioe) {
|
||||
@@ -88,24 +89,26 @@ public class I2NPMessageHandler {
|
||||
readMessage(data, 0);
|
||||
return lastRead();
|
||||
}
|
||||
|
||||
public int readMessage(byte data[], int offset) throws IOException, I2NPMessageException {
|
||||
int cur = offset;
|
||||
int type = (int)DataHelper.fromLong(data, cur, 1);
|
||||
cur++;
|
||||
_lastReadBegin = System.currentTimeMillis();
|
||||
I2NPMessage msg = I2NPMessageImpl.createMessage(_context, type);
|
||||
if (msg == null) {
|
||||
int sz = data.length-offset;
|
||||
boolean allZero = false;
|
||||
for (int i = offset; i < data.length; i++) {
|
||||
if (data[i] != 0) {
|
||||
allZero = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new I2NPMessageException("The type "+ type + " is an unknown I2NP message (remaining sz="
|
||||
+ sz + " all zeros? " + allZero + ")");
|
||||
}
|
||||
// can't be null
|
||||
//if (msg == null) {
|
||||
// int sz = data.length-offset;
|
||||
// boolean allZero = false;
|
||||
// for (int i = offset; i < data.length; i++) {
|
||||
// if (data[i] != 0) {
|
||||
// allZero = false;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// throw new I2NPMessageException("The type "+ type + " is an unknown I2NP message (remaining sz="
|
||||
// + sz + " all zeros? " + allZero + ")");
|
||||
//}
|
||||
try {
|
||||
_lastSize = msg.readBytes(data, type, cur);
|
||||
cur += _lastSize;
|
||||
@@ -127,6 +130,7 @@ public class I2NPMessageHandler {
|
||||
public long getLastReadTime() { return _lastReadEnd - _lastReadBegin; }
|
||||
public int getLastSize() { return _lastSize; }
|
||||
|
||||
/****
|
||||
public static void main(String args[]) {
|
||||
try {
|
||||
I2NPMessage msg = new I2NPMessageHandler(I2PAppContext.getGlobalContext()).readMessage(new FileInputStream(args[0]));
|
||||
@@ -135,4 +139,5 @@ public class I2NPMessageHandler {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ import net.i2p.util.SimpleByteCache;
|
||||
* @author jrandom
|
||||
*/
|
||||
public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPMessage {
|
||||
private final Log _log;
|
||||
protected final Log _log;
|
||||
protected final I2PAppContext _context;
|
||||
private long _expiration;
|
||||
private long _uniqueId;
|
||||
@@ -36,12 +36,16 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
public final static long DEFAULT_EXPIRATION_MS = 1*60*1000; // 1 minute by default
|
||||
public final static int CHECKSUM_LENGTH = 1; //Hash.HASH_LENGTH;
|
||||
|
||||
private static final boolean RAW_FULL_SIZE = false;
|
||||
// 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;
|
||||
|
||||
/** unused */
|
||||
private static final Map<Integer, Builder> _builders = new ConcurrentHashMap(1);
|
||||
|
||||
/** @deprecated unused */
|
||||
public static final void registerBuilder(Builder builder, int type) { _builders.put(Integer.valueOf(type), builder); }
|
||||
|
||||
/** interface for extending the types of messages handled - unused */
|
||||
public interface Builder {
|
||||
/** instantiate a new I2NPMessage to be populated shortly */
|
||||
@@ -120,7 +124,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
boolean eq = DataHelper.eq(checksum, 0, calc, 0, CHECKSUM_LENGTH);
|
||||
SimpleByteCache.release(calc);
|
||||
if (!eq)
|
||||
throw new I2NPMessageException("Hash does not match for " + getClass().getName());
|
||||
throw new I2NPMessageException("Bad checksum on " + size + " byte I2NP " + getClass().getSimpleName());
|
||||
|
||||
//long start = _context.clock().now();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -182,7 +186,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
boolean eq = DataHelper.eq(hdata, 0, calc, 0, CHECKSUM_LENGTH);
|
||||
SimpleByteCache.release(calc);
|
||||
if (!eq)
|
||||
throw new I2NPMessageException("Hash does not match for " + getClass().getName());
|
||||
throw new I2NPMessageException("Bad checksum on " + size + " byte I2NP " + getClass().getSimpleName());
|
||||
|
||||
//long start = _context.clock().now();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -220,10 +224,15 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
public synchronized int getMessageSize() {
|
||||
return calculateWrittenLength()+15 + CHECKSUM_LENGTH; // 16 bytes in the header
|
||||
}
|
||||
|
||||
/**
|
||||
* The raw header consists of a one-byte type and a 4-byte expiration in seconds only.
|
||||
* Used by SSU only!
|
||||
*/
|
||||
public synchronized int getRawMessageSize() {
|
||||
if (RAW_FULL_SIZE)
|
||||
return getMessageSize();
|
||||
else
|
||||
//if (RAW_FULL_SIZE)
|
||||
// return getMessageSize();
|
||||
//else
|
||||
return calculateWrittenLength()+5;
|
||||
}
|
||||
|
||||
@@ -310,15 +319,21 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
*/
|
||||
|
||||
|
||||
/** used by SSU only */
|
||||
/**
|
||||
* Write the message with a short 5-byte header.
|
||||
* THe header consists of a one-byte type and a 4-byte expiration in seconds only.
|
||||
* Used by SSU only!
|
||||
*/
|
||||
public int toRawByteArray(byte buffer[]) {
|
||||
if (RAW_FULL_SIZE)
|
||||
return toByteArray(buffer);
|
||||
//if (RAW_FULL_SIZE)
|
||||
// return toByteArray(buffer);
|
||||
try {
|
||||
int off = 0;
|
||||
DataHelper.toLong(buffer, off, 1, getType());
|
||||
off += 1;
|
||||
DataHelper.toLong(buffer, off, 4, _expiration/1000); // seconds
|
||||
// January 19 2038? No, unsigned, good until Feb. 7 2106
|
||||
// in seconds, round up so we don't lose time every hop
|
||||
DataHelper.toLong(buffer, off, 4, (_expiration + 500) / 1000);
|
||||
off += 4;
|
||||
return writeMessageBody(buffer, off);
|
||||
} catch (I2NPMessageException ime) {
|
||||
@@ -344,24 +359,30 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
}
|
||||
*****/
|
||||
|
||||
/** used by SSU only */
|
||||
/**
|
||||
* Read the message with a short 5-byte header.
|
||||
* THe header consists of a one-byte type and a 4-byte expiration in seconds only.
|
||||
* Used by SSU only!
|
||||
*/
|
||||
public static I2NPMessage fromRawByteArray(I2PAppContext ctx, byte buffer[], int offset, int len, I2NPMessageHandler handler) throws I2NPMessageException {
|
||||
int type = (int)DataHelper.fromLong(buffer, offset, 1);
|
||||
offset++;
|
||||
I2NPMessageImpl msg = (I2NPMessageImpl)createMessage(ctx, type);
|
||||
if (msg == null)
|
||||
throw new I2NPMessageException("Unknown message type: " + type);
|
||||
if (RAW_FULL_SIZE) {
|
||||
try {
|
||||
msg.readBytes(buffer, type, offset);
|
||||
} catch (IOException ioe) {
|
||||
throw new I2NPMessageException("Error reading the " + msg, ioe);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
//if (RAW_FULL_SIZE) {
|
||||
// try {
|
||||
// msg.readBytes(buffer, type, offset);
|
||||
// } catch (IOException ioe) {
|
||||
// throw new I2NPMessageException("Error reading the " + msg, ioe);
|
||||
// }
|
||||
// return msg;
|
||||
//}
|
||||
|
||||
try {
|
||||
long expiration = DataHelper.fromLong(buffer, offset, 4) * 1000; // seconds
|
||||
// January 19 2038? No, unsigned, good until Feb. 7 2106
|
||||
// in seconds, round up so we don't lose time every hop
|
||||
long expiration = (DataHelper.fromLong(buffer, offset, 4) * 1000) + 500;
|
||||
offset += 4;
|
||||
int dataSize = len - 1 - 4;
|
||||
msg.readMessage(buffer, offset, dataSize, type, handler);
|
||||
@@ -377,6 +398,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
||||
/**
|
||||
* Yes, this is fairly ugly, but its the only place it ever happens.
|
||||
*
|
||||
* @return non-null, returns an UnknownI2NPMessage if unknown type
|
||||
*/
|
||||
public static I2NPMessage createMessage(I2PAppContext context, int type) throws I2NPMessageException {
|
||||
switch (type) {
|
||||
|
@@ -22,7 +22,6 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
public class TunnelDataMessage extends I2NPMessageImpl {
|
||||
private Log _log;
|
||||
private long _tunnelId;
|
||||
private TunnelId _tunnelIdObj;
|
||||
private byte[] _data;
|
||||
@@ -101,7 +100,6 @@ public class TunnelDataMessage extends I2NPMessageImpl {
|
||||
|
||||
public TunnelDataMessage(I2PAppContext context) {
|
||||
super(context);
|
||||
_log = context.logManager().getLog(TunnelDataMessage.class);
|
||||
setMessageExpiration(context.clock().now() + EXPIRATION_PERIOD);
|
||||
}
|
||||
|
||||
|
@@ -21,11 +21,10 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
public class TunnelGatewayMessage extends I2NPMessageImpl {
|
||||
private Log _log;
|
||||
private TunnelId _tunnelId;
|
||||
private I2NPMessage _msg;
|
||||
private byte _msgData[];
|
||||
private Exception _creator;
|
||||
//private Exception _creator;
|
||||
|
||||
public final static int MESSAGE_TYPE = 19;
|
||||
/** if we can't deliver a tunnel message in 10s, fuck it */
|
||||
@@ -33,7 +32,6 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
|
||||
|
||||
public TunnelGatewayMessage(I2PAppContext context) {
|
||||
super(context);
|
||||
_log = context.logManager().getLog(TunnelGatewayMessage.class);
|
||||
setMessageExpiration(context.clock().now() + EXPIRATION_PERIOD);
|
||||
//_creator = new Exception("i made this");
|
||||
}
|
||||
@@ -41,7 +39,13 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
|
||||
public TunnelId getTunnelId() { return _tunnelId; }
|
||||
public void setTunnelId(TunnelId id) { _tunnelId = id; }
|
||||
|
||||
/**
|
||||
* Warning, at the IBGW, where the message was read in,
|
||||
* this will be an UnknownI2NPMessage.
|
||||
* If you need a real message class, use UnknownI2NPMessage.convert().
|
||||
*/
|
||||
public I2NPMessage getMessage() { return _msg; }
|
||||
|
||||
public void setMessage(I2NPMessage msg) {
|
||||
if (msg == null)
|
||||
throw new IllegalArgumentException("wtf, dont set me to null");
|
||||
@@ -61,7 +65,7 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
|
||||
/** write the message body to the output array, starting at the given index */
|
||||
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
|
||||
if ( (_tunnelId == null) || ( (_msg == null) && (_msgData == null) ) ) {
|
||||
_log.log(Log.CRIT, "failing to write out gateway message, created by: ", _creator);
|
||||
_log.log(Log.CRIT, "failing to write out gateway message");
|
||||
throw new I2NPMessageException("Not enough data to write out (id=" + _tunnelId + " data=" + _msg + ")");
|
||||
}
|
||||
|
||||
@@ -87,9 +91,17 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
|
||||
|
||||
|
||||
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);
|
||||
//I2NPMessageHandler h = new I2NPMessageHandler(_context);
|
||||
//readMessage(data, offset, dataSize, type, h);
|
||||
readMessage(data, offset, dataSize, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that for efficiency at the IBGW, this does not fully deserialize the included
|
||||
* I2NP Message. It just puts it in an UnknownI2NPMessage.
|
||||
*
|
||||
* @param handler unused, may be null
|
||||
*/
|
||||
@Override
|
||||
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");
|
||||
@@ -101,12 +113,32 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
|
||||
if (_tunnelId.getTunnelId() <= 0)
|
||||
throw new I2NPMessageException("Invalid tunnel Id " + _tunnelId);
|
||||
|
||||
DataHelper.fromLong(data, curIndex, 2);
|
||||
int len = (int) DataHelper.fromLong(data, curIndex, 2);
|
||||
curIndex += 2;
|
||||
curIndex = handler.readMessage(data, curIndex);
|
||||
_msg = handler.lastRead();
|
||||
if (_msg == null)
|
||||
throw new I2NPMessageException("wtf, message read has no payload?");
|
||||
if (len <= 1 || curIndex + len > data.length || len > dataSize - 6)
|
||||
throw new I2NPMessageException("I2NP length in TGM: " + len +
|
||||
" but remaining bytes: " + Math.min(data.length - curIndex, dataSize - 6));
|
||||
|
||||
// OLD WAY full message parsing and instantiation
|
||||
//handler.readMessage(data, curIndex);
|
||||
//_msg = handler.lastRead();
|
||||
//if (_msg == null)
|
||||
// throw new I2NPMessageException("wtf, message read has no payload?");
|
||||
|
||||
// NEW WAY save lots of effort at the IBGW by reading as an UnknownI2NPMessage instead
|
||||
// This will save a lot of object churn and processing,
|
||||
// primarily for unencrypted msgs (V)TBRM, DatabaseStoreMessage, and DSRMs.
|
||||
// DatabaseStoreMessages in particluar are intensive for readBytes()
|
||||
// since the RI is decompressed.
|
||||
// For a zero-hop IB tunnel, where we do need the real thing,
|
||||
// it is converted to a real message class in TunnelGatewayZeroHop
|
||||
// using UnknownI2NPMessage.convert() in TunnelGatewayZeroHop.
|
||||
// We also skip processing the checksum as it's covered by the TGM checksum.
|
||||
// If a zero-hop, the checksum will be verified in convert().
|
||||
int utype = data[curIndex++] & 0xff;
|
||||
UnknownI2NPMessage umsg = new UnknownI2NPMessage(_context, utype);
|
||||
umsg.readBytesIgnoreChecksum(data, curIndex);
|
||||
_msg = umsg;
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
@@ -12,6 +12,8 @@ import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.SimpleByteCache;
|
||||
|
||||
/**
|
||||
* This is the same as DataMessage but with a variable message type.
|
||||
@@ -23,11 +25,13 @@ import net.i2p.data.DataHelper;
|
||||
* There is no setData() method, the only way to create one of these is to
|
||||
* read it with readMessage() (i.e., it came from some other router)
|
||||
*
|
||||
* @since 0.7.12
|
||||
* @since 0.7.12 but broken before 0.8.12
|
||||
*/
|
||||
public class UnknownI2NPMessage extends I2NPMessageImpl {
|
||||
private byte _data[];
|
||||
private int _type;
|
||||
private final int _type;
|
||||
// we assume CHECKSUM_LENGTH = 1
|
||||
private byte _checksum;
|
||||
|
||||
/** @param type 0-255 */
|
||||
public UnknownI2NPMessage(I2PAppContext context, int type) {
|
||||
@@ -35,41 +39,25 @@ public class UnknownI2NPMessage extends I2NPMessageImpl {
|
||||
_type = type;
|
||||
}
|
||||
|
||||
/** warning - only public for equals() */
|
||||
public byte[] getData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
if (type != _type) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
int curIndex = offset;
|
||||
long size = DataHelper.fromLong(data, curIndex, 4);
|
||||
curIndex += 4;
|
||||
if (size > MAX_SIZE)
|
||||
throw new I2NPMessageException("wtf, size=" + size);
|
||||
_data = new byte[(int)size];
|
||||
System.arraycopy(data, curIndex, _data, 0, (int)size);
|
||||
if (dataSize > MAX_SIZE)
|
||||
throw new I2NPMessageException("wtf, size=" + dataSize);
|
||||
_data = new byte[dataSize];
|
||||
System.arraycopy(data, offset, _data, 0, dataSize);
|
||||
}
|
||||
|
||||
/** calculate the message body's length (not including the header and footer */
|
||||
protected int calculateWrittenLength() {
|
||||
if (_data == null)
|
||||
return 4;
|
||||
return 0;
|
||||
else
|
||||
return 4 + _data.length;
|
||||
return _data.length;
|
||||
}
|
||||
|
||||
/** write the message body to the output array, starting at the given index */
|
||||
protected int writeMessageBody(byte out[], int curIndex) {
|
||||
if (_data == null) {
|
||||
out[curIndex++] = 0x0;
|
||||
out[curIndex++] = 0x0;
|
||||
out[curIndex++] = 0x0;
|
||||
out[curIndex++] = 0x0;
|
||||
} else {
|
||||
byte len[] = DataHelper.toLong(4, _data.length);
|
||||
System.arraycopy(len, 0, out, curIndex, 4);
|
||||
curIndex += 4;
|
||||
if (_data != null) {
|
||||
System.arraycopy(_data, 0, out, curIndex, _data.length);
|
||||
curIndex += _data.length;
|
||||
}
|
||||
@@ -79,16 +67,88 @@ public class UnknownI2NPMessage extends I2NPMessageImpl {
|
||||
/** @return 0-255 */
|
||||
public int getType() { return _type; }
|
||||
|
||||
|
||||
/**
|
||||
* Read the full message including the header.
|
||||
* This is the same as I2NPMessageImpl.readBytes(), except
|
||||
* start after the type field, and
|
||||
* do NOT verify the checksum, but simply save it for later
|
||||
* so it can be verified in convert() if required.
|
||||
*
|
||||
*<pre>
|
||||
* Standard message format AFTER the type field
|
||||
* 4 byte ID
|
||||
* 8 byte expiration
|
||||
* 2 byte size
|
||||
* 1 byte checksum (saved in case we need to check later)
|
||||
* size bytes of payload, read by readMessage()
|
||||
*</pre>
|
||||
*
|
||||
* @param offset starting at the ID (must skip the type)
|
||||
* @return total length of the message
|
||||
* @since 0.8.12
|
||||
*/
|
||||
public void readBytesIgnoreChecksum(byte data[], int offset) throws I2NPMessageException, IOException {
|
||||
int cur = offset;
|
||||
setUniqueId(DataHelper.fromLong(data, cur, 4));
|
||||
cur += 4;
|
||||
setMessageExpiration(DataHelper.fromLong(data, cur, DataHelper.DATE_LENGTH));
|
||||
cur += DataHelper.DATE_LENGTH;
|
||||
int size = (int)DataHelper.fromLong(data, cur, 2);
|
||||
cur += 2;
|
||||
_checksum = data[cur];
|
||||
cur++;
|
||||
|
||||
if (cur + size > data.length)
|
||||
throw new I2NPMessageException("Payload is too short ["
|
||||
+ "data.len=" + data.length
|
||||
+ " offset=" + offset
|
||||
+ " cur=" + cur
|
||||
+ " wanted=" + size + ']');
|
||||
|
||||
readMessage(data, cur, size, _type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to convert this message to a known message class.
|
||||
* Must have been created with readBytesIgnoreChecksum previously,
|
||||
* as this does the delayed verification using the saved checksum.
|
||||
*
|
||||
* Used by TunnelGatewayZeroHop.
|
||||
*
|
||||
* @throws I2NPMessageException if the conversion fails
|
||||
* @since 0.8.12
|
||||
*/
|
||||
public I2NPMessage convert() throws I2NPMessageException {
|
||||
I2NPMessage msg = I2NPMessageImpl.createMessage(_context, _type);
|
||||
if (msg instanceof UnknownI2NPMessage)
|
||||
throw new I2NPMessageException("Unable to convert unknown type " + _type);
|
||||
byte[] calc = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
||||
_context.sha().calculateHash(_data, 0, _data.length, calc, 0);
|
||||
boolean eq = _checksum == calc[0];
|
||||
SimpleByteCache.release(calc);
|
||||
if (!eq)
|
||||
throw new I2NPMessageException("Bad checksum on " + _data.length + " byte msg type " + _type);
|
||||
try {
|
||||
msg.readMessage(_data, 0, _data.length, _type);
|
||||
} catch (IOException ioe) {
|
||||
throw new I2NPMessageException("Unable to convert type " + _type, ioe);
|
||||
}
|
||||
msg.setUniqueId(getUniqueId());
|
||||
msg.setMessageExpiration(getMessageExpiration());
|
||||
return msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return _type + DataHelper.hashCode(getData());
|
||||
return _type + DataHelper.hashCode(_data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof UnknownI2NPMessage) ) {
|
||||
UnknownI2NPMessage msg = (UnknownI2NPMessage)object;
|
||||
return _type == msg.getType() && DataHelper.eq(getData(), msg.getData());
|
||||
return _type == msg.getType() && DataHelper.eq(_data, msg._data);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -99,7 +159,7 @@ public class UnknownI2NPMessage extends I2NPMessageImpl {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[UnknownI2NPMessage: ");
|
||||
buf.append("\n\tType: ").append(_type);
|
||||
buf.append("\n\tLength: ").append(calculateWrittenLength() - 4);
|
||||
buf.append("\n\tLength: ").append(calculateWrittenLength());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 17;
|
||||
public final static long BUILD = 18;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
@@ -477,6 +477,13 @@ class FragmentHandler {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("RECV(" + data.length + "): "); // + Base64.encode(data)
|
||||
//+ " " + _context.sha().calculateHash(data).toBase64());
|
||||
|
||||
// TODO read in as unknown message for outbound tunnels,
|
||||
// since this will just be packaged in a TunnelGatewayMessage.
|
||||
// Not a big savings since most everything is a GarlicMessage
|
||||
// and so the readMessage() call is fast.
|
||||
// The unencrypted messages at the OBEP are (V)TBMs
|
||||
// and perhaps an occasional DatabaseLookupMessage
|
||||
I2NPMessage m = new I2NPMessageHandler(_context).readMessage(data);
|
||||
noteReception(m.getUniqueId(), fragmentCount-1, "complete: ");// + msg.toString());
|
||||
noteCompletion(m.getUniqueId());
|
||||
|
@@ -61,6 +61,8 @@ class OutboundMessageDistributor {
|
||||
if (_context.routerHash().equals(target.getIdentity().calculateHash())) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("queueing inbound message to ourselves: " + m);
|
||||
// TODO if UnknownI2NPMessage, convert it.
|
||||
// See FragmentHandler.receiveComplete()
|
||||
_context.inNetMessagePool().add(m, null, null);
|
||||
return;
|
||||
} else {
|
||||
|
@@ -452,7 +452,7 @@ public class TunnelDispatcher implements Service {
|
||||
+ " as the wrapper's expiration is in " + DataHelper.formatDuration(msg.getMessageExpiration()-before)
|
||||
+ " and/or the content's expiration is in " + DataHelper.formatDuration(msg.getMessage().getMessageExpiration()-before)
|
||||
+ " with messageId " + msg.getUniqueId() + "/" + msg.getMessage().getUniqueId() + " and message type "
|
||||
+ msg.getMessage().getClass().getName());
|
||||
+ msg.getMessage().getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
//_context.messageHistory().tunnelDispatched("message " + msg.getUniqueId() + "/" + msg.getMessage().getUniqueId() + " on tunnel "
|
||||
@@ -471,7 +471,7 @@ public class TunnelDispatcher implements Service {
|
||||
+ DataHelper.formatDuration(msg.getMessage().getMessageExpiration()-_context.clock().now())
|
||||
+ " messageId " + msg.getUniqueId()
|
||||
+ "/" + msg.getMessage().getUniqueId()
|
||||
+ " messageType: " + msg.getMessage().getClass().getName()
|
||||
+ " messageType: " + msg.getMessage().getClass().getSimpleName()
|
||||
+ " existing = " + _inboundGateways.size(), new Exception("source"));
|
||||
}
|
||||
|
||||
|
@@ -3,7 +3,9 @@ package net.i2p.router.tunnel;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.data.i2np.I2NPMessageException;
|
||||
import net.i2p.data.i2np.TunnelGatewayMessage;
|
||||
import net.i2p.data.i2np.UnknownI2NPMessage;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@@ -30,12 +32,29 @@ class TunnelGatewayZeroHop extends TunnelGateway {
|
||||
|
||||
/**
|
||||
* Add a message to be sent down the tunnel, where we are the inbound gateway.
|
||||
* This requires converting the message included in the TGM from an
|
||||
* UnknownI2NPMessage to the correct message class.
|
||||
* See TunnelGatewayMessage for details.
|
||||
*
|
||||
* @param msg message received to be sent through the tunnel
|
||||
*/
|
||||
@Override
|
||||
public void add(TunnelGatewayMessage msg) {
|
||||
add(msg.getMessage(), null, null);
|
||||
I2NPMessage imsg = msg.getMessage();
|
||||
if (_config.isInbound()) {
|
||||
if (imsg instanceof UnknownI2NPMessage) {
|
||||
// Do the delayed deserializing - convert to a standard message class
|
||||
try {
|
||||
UnknownI2NPMessage umsg = (UnknownI2NPMessage) imsg;
|
||||
imsg = umsg.convert();
|
||||
} catch (I2NPMessageException ime) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to convert to std. msg. class at zero-hop IBGW", ime);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
add(imsg, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +69,7 @@ class TunnelGatewayZeroHop extends TunnelGateway {
|
||||
@Override
|
||||
public void add(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("zero hop gateway: distribute " + (_config.isInbound() ? "inbound " : " outbound ")
|
||||
_log.debug("zero hop gateway: distribute " + (_config.isInbound() ? "inbound" : " outbound")
|
||||
+ " to " + (toRouter != null ? toRouter.toBase64().substring(0,4) : "" )
|
||||
+ "." + (toTunnel != null ? toTunnel.getTunnelId() + "" : "")
|
||||
+ ": " + msg);
|
||||
|
Reference in New Issue
Block a user