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:
@ -8,17 +8,17 @@ package net.i2p.data.i2np;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* Defines a message containing arbitrary bytes of data
|
||||
* This is what goes in a GarlicClove.
|
||||
* It was also previously used for generating test messages.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DataMessage extends I2NPMessageImpl {
|
||||
public class DataMessage extends FastI2NPMessageImpl {
|
||||
public final static int MESSAGE_TYPE = 20;
|
||||
private byte _data[];
|
||||
|
||||
@ -29,7 +29,13 @@ public class DataMessage extends I2NPMessageImpl {
|
||||
public byte[] getData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if data previously set, to protect saved checksum
|
||||
*/
|
||||
public void setData(byte[] data) {
|
||||
if (_data != null)
|
||||
throw new IllegalStateException();
|
||||
_data = data;
|
||||
}
|
||||
|
||||
@ -37,7 +43,7 @@ public class DataMessage extends I2NPMessageImpl {
|
||||
return _data.length;
|
||||
}
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
int curIndex = offset;
|
||||
long size = DataHelper.fromLong(data, curIndex, 4);
|
||||
@ -55,6 +61,7 @@ public class DataMessage extends I2NPMessageImpl {
|
||||
else
|
||||
return 4 + _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) {
|
||||
@ -76,14 +83,14 @@ public class DataMessage extends I2NPMessageImpl {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getData());
|
||||
return DataHelper.hashCode(_data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof DataMessage) ) {
|
||||
DataMessage msg = (DataMessage)object;
|
||||
return DataHelper.eq(getData(),msg.getData());
|
||||
return DataHelper.eq(_data, msg._data);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -93,7 +100,7 @@ public class DataMessage extends I2NPMessageImpl {
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[DataMessage: ");
|
||||
buf.append("\n\tData: ").append(DataHelper.toString(getData(), 64));
|
||||
buf.append("\n\tData: ").append(DataHelper.toString(_data, 64));
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
|
@ -8,9 +8,11 @@ package net.i2p.data.i2np;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@ -25,13 +27,14 @@ import net.i2p.data.TunnelId;
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DatabaseLookupMessage extends I2NPMessageImpl {
|
||||
public class DatabaseLookupMessage extends FastI2NPMessageImpl {
|
||||
//private final static Log _log = new Log(DatabaseLookupMessage.class);
|
||||
public final static int MESSAGE_TYPE = 2;
|
||||
private Hash _key;
|
||||
private Hash _fromHash;
|
||||
private TunnelId _replyTunnel;
|
||||
private Set<Hash> _dontIncludePeers;
|
||||
/** this must be kept as a list to preserve the order and not break the checksum */
|
||||
private List<Hash> _dontIncludePeers;
|
||||
|
||||
//private static volatile long _currentLookupPeriod = 0;
|
||||
//private static volatile int _currentLookupCount = 0;
|
||||
@ -102,36 +105,109 @@ public class DatabaseLookupMessage extends I2NPMessageImpl {
|
||||
* Defines the key being searched for
|
||||
*/
|
||||
public Hash getSearchKey() { return _key; }
|
||||
public void setSearchKey(Hash key) { _key = key; }
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if key previously set, to protect saved checksum
|
||||
*/
|
||||
public void setSearchKey(Hash key) {
|
||||
if (_key != null)
|
||||
throw new IllegalStateException();
|
||||
_key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the router who requested this lookup
|
||||
*
|
||||
*/
|
||||
public Hash getFrom() { return _fromHash; }
|
||||
public void setFrom(Hash from) { _fromHash = from; }
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if from previously set, to protect saved checksum
|
||||
*/
|
||||
public void setFrom(Hash from) {
|
||||
if (_fromHash != null)
|
||||
throw new IllegalStateException();
|
||||
_fromHash = from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the tunnel ID a reply should be sent to
|
||||
*
|
||||
*/
|
||||
public TunnelId getReplyTunnel() { return _replyTunnel; }
|
||||
public void setReplyTunnel(TunnelId replyTunnel) { _replyTunnel = replyTunnel; }
|
||||
|
||||
/**
|
||||
* Set of peers that a lookup reply should NOT include
|
||||
*
|
||||
* @return Set of Hash objects, each of which is the H(routerIdentity) to skip
|
||||
* @throws IllegalStateException if tunnel previously set, to protect saved checksum
|
||||
*/
|
||||
public Set<Hash> getDontIncludePeers() { return _dontIncludePeers; }
|
||||
public void setDontIncludePeers(Set peers) {
|
||||
public void setReplyTunnel(TunnelId replyTunnel) {
|
||||
if (_replyTunnel != null)
|
||||
throw new IllegalStateException();
|
||||
_replyTunnel = replyTunnel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of peers that a lookup reply should NOT include.
|
||||
* WARNING - returns a copy.
|
||||
*
|
||||
* @return Set of Hash objects, each of which is the H(routerIdentity) to skip, or null
|
||||
*/
|
||||
public Set<Hash> getDontIncludePeers() {
|
||||
if (_dontIncludePeers == null)
|
||||
return null;
|
||||
return new HashSet(_dontIncludePeers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the dontInclude set with this set.
|
||||
* WARNING - makes a copy.
|
||||
* Invalidates the checksum.
|
||||
*
|
||||
* @param peers may be null
|
||||
*/
|
||||
public void setDontIncludePeers(Collection<Hash> peers) {
|
||||
_hasChecksum = false;
|
||||
if (peers != null)
|
||||
_dontIncludePeers = new HashSet(peers);
|
||||
_dontIncludePeers = new ArrayList(peers);
|
||||
else
|
||||
_dontIncludePeers = null;
|
||||
}
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
/**
|
||||
* Add to the set.
|
||||
* Invalidates the checksum.
|
||||
*
|
||||
* @param peer non-null
|
||||
* @since 0.8.12
|
||||
*/
|
||||
public void addDontIncludePeer(Hash peer) {
|
||||
if (_dontIncludePeers == null)
|
||||
_dontIncludePeers = new ArrayList();
|
||||
else if (_dontIncludePeers.contains(peer))
|
||||
return;
|
||||
_hasChecksum = false;
|
||||
_dontIncludePeers.add(peer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the set.
|
||||
* Invalidates the checksum.
|
||||
*
|
||||
* @param peers non-null
|
||||
* @since 0.8.12
|
||||
*/
|
||||
public void addDontIncludePeers(Collection<Hash> peers) {
|
||||
_hasChecksum = false;
|
||||
if (_dontIncludePeers == null) {
|
||||
_dontIncludePeers = new ArrayList(peers);
|
||||
} else {
|
||||
for (Hash peer : peers) {
|
||||
if (!_dontIncludePeers.contains(peer))
|
||||
_dontIncludePeers.add(peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
int curIndex = offset;
|
||||
|
||||
@ -170,7 +246,7 @@ public class DatabaseLookupMessage extends I2NPMessageImpl {
|
||||
|
||||
if ( (numPeers < 0) || (numPeers > MAX_NUM_PEERS) )
|
||||
throw new I2NPMessageException("Invalid number of peers - " + numPeers);
|
||||
Set<Hash> peers = new HashSet(numPeers);
|
||||
List<Hash> peers = new ArrayList(numPeers);
|
||||
for (int i = 0; i < numPeers; i++) {
|
||||
//byte peer[] = new byte[Hash.HASH_LENGTH];
|
||||
//System.arraycopy(data, curIndex, peer, 0, Hash.HASH_LENGTH);
|
||||
@ -220,8 +296,7 @@ public class DatabaseLookupMessage extends I2NPMessageImpl {
|
||||
byte len[] = DataHelper.toLong(2, size);
|
||||
out[curIndex++] = len[0];
|
||||
out[curIndex++] = len[1];
|
||||
for (Iterator<Hash> iter = _dontIncludePeers.iterator(); iter.hasNext(); ) {
|
||||
Hash peer = iter.next();
|
||||
for (Hash peer : _dontIncludePeers) {
|
||||
System.arraycopy(peer.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
|
||||
curIndex += Hash.HASH_LENGTH;
|
||||
}
|
||||
@ -233,9 +308,9 @@ public class DatabaseLookupMessage extends I2NPMessageImpl {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getSearchKey()) +
|
||||
DataHelper.hashCode(getFrom()) +
|
||||
DataHelper.hashCode(getReplyTunnel()) +
|
||||
return DataHelper.hashCode(_key) +
|
||||
DataHelper.hashCode(_fromHash) +
|
||||
DataHelper.hashCode(_replyTunnel) +
|
||||
DataHelper.hashCode(_dontIncludePeers);
|
||||
}
|
||||
|
||||
@ -243,10 +318,10 @@ public class DatabaseLookupMessage extends I2NPMessageImpl {
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof DatabaseLookupMessage) ) {
|
||||
DatabaseLookupMessage msg = (DatabaseLookupMessage)object;
|
||||
return DataHelper.eq(getSearchKey(),msg.getSearchKey()) &&
|
||||
DataHelper.eq(getFrom(),msg.getFrom()) &&
|
||||
DataHelper.eq(getReplyTunnel(),msg.getReplyTunnel()) &&
|
||||
DataHelper.eq(_dontIncludePeers,msg.getDontIncludePeers());
|
||||
return DataHelper.eq(_key, msg._key) &&
|
||||
DataHelper.eq(_fromHash, msg._fromHash) &&
|
||||
DataHelper.eq(_replyTunnel, msg._replyTunnel) &&
|
||||
DataHelper.eq(_dontIncludePeers, msg._dontIncludePeers);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -256,9 +331,9 @@ public class DatabaseLookupMessage extends I2NPMessageImpl {
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[DatabaseLookupMessage: ");
|
||||
buf.append("\n\tSearch Key: ").append(getSearchKey());
|
||||
buf.append("\n\tFrom: ").append(getFrom());
|
||||
buf.append("\n\tReply Tunnel: ").append(getReplyTunnel());
|
||||
buf.append("\n\tSearch Key: ").append(_key);
|
||||
buf.append("\n\tFrom: ").append(_fromHash);
|
||||
buf.append("\n\tReply Tunnel: ").append(_replyTunnel);
|
||||
buf.append("\n\tDont Include Peers: ");
|
||||
if (_dontIncludePeers != null)
|
||||
buf.append(_dontIncludePeers.size());
|
||||
|
@ -8,7 +8,6 @@ package net.i2p.data.i2np;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -23,7 +22,7 @@ import net.i2p.data.Hash;
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DatabaseSearchReplyMessage extends I2NPMessageImpl {
|
||||
public class DatabaseSearchReplyMessage extends FastI2NPMessageImpl {
|
||||
public final static int MESSAGE_TYPE = 3;
|
||||
private Hash _key;
|
||||
private List<Hash> _peerHashes;
|
||||
@ -41,7 +40,15 @@ public class DatabaseSearchReplyMessage extends I2NPMessageImpl {
|
||||
* Defines the key being searched for
|
||||
*/
|
||||
public Hash getSearchKey() { return _key; }
|
||||
public void setSearchKey(Hash key) { _key = key; }
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if key previously set, to protect saved checksum
|
||||
*/
|
||||
public void setSearchKey(Hash key) {
|
||||
if (_key != null)
|
||||
throw new IllegalStateException();
|
||||
_key = key;
|
||||
}
|
||||
|
||||
public int getNumReplies() { return _peerHashes.size(); }
|
||||
public Hash getReply(int index) { return _peerHashes.get(index); }
|
||||
@ -51,7 +58,7 @@ public class DatabaseSearchReplyMessage extends I2NPMessageImpl {
|
||||
public Hash getFromHash() { return _from; }
|
||||
public void setFromHash(Hash from) { _from = from; }
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
int curIndex = offset;
|
||||
|
||||
@ -114,8 +121,8 @@ public class DatabaseSearchReplyMessage extends I2NPMessageImpl {
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof DatabaseSearchReplyMessage) ) {
|
||||
DatabaseSearchReplyMessage msg = (DatabaseSearchReplyMessage)object;
|
||||
return DataHelper.eq(getSearchKey(),msg.getSearchKey()) &&
|
||||
DataHelper.eq(getFromHash(),msg.getFromHash()) &&
|
||||
return DataHelper.eq(_key,msg._key) &&
|
||||
DataHelper.eq(_from,msg._from) &&
|
||||
DataHelper.eq(_peerHashes,msg._peerHashes);
|
||||
} else {
|
||||
return false;
|
||||
@ -124,8 +131,8 @@ public class DatabaseSearchReplyMessage extends I2NPMessageImpl {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getSearchKey()) +
|
||||
DataHelper.hashCode(getFromHash()) +
|
||||
return DataHelper.hashCode(_key) +
|
||||
DataHelper.hashCode(_from) +
|
||||
DataHelper.hashCode(_peerHashes);
|
||||
}
|
||||
|
||||
@ -133,12 +140,12 @@ public class DatabaseSearchReplyMessage extends I2NPMessageImpl {
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[DatabaseSearchReplyMessage: ");
|
||||
buf.append("\n\tSearch Key: ").append(getSearchKey());
|
||||
buf.append("\n\tSearch Key: ").append(_key);
|
||||
buf.append("\n\tReplies: # = ").append(getNumReplies());
|
||||
for (int i = 0; i < getNumReplies(); i++) {
|
||||
buf.append("\n\t\tReply [").append(i).append("]: ").append(getReply(i));
|
||||
}
|
||||
buf.append("\n\tFrom: ").append(getFromHash());
|
||||
buf.append("\n\tFrom: ").append(_from);
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import net.i2p.data.TunnelId;
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
public class DatabaseStoreMessage extends FastI2NPMessageImpl {
|
||||
public final static int MESSAGE_TYPE = 1;
|
||||
private Hash _key;
|
||||
private DatabaseEntry _dbEntry;
|
||||
@ -61,8 +61,11 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
|
||||
/**
|
||||
* This also sets the key
|
||||
* @throws IllegalStateException if data previously set, to protect saved checksum
|
||||
*/
|
||||
public void setEntry(DatabaseEntry entry) {
|
||||
if (_dbEntry != null)
|
||||
throw new IllegalStateException();
|
||||
_dbEntry = entry;
|
||||
}
|
||||
|
||||
@ -94,7 +97,7 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
public Hash getReplyGateway() { return _replyGateway; }
|
||||
public void setReplyGateway(Hash peer) { _replyGateway = peer; }
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
int curIndex = offset;
|
||||
|
||||
@ -126,14 +129,16 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
_dbEntry.readBytes(new ByteArrayInputStream(data, curIndex, data.length-curIndex));
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error reading the leaseSet", dfe);
|
||||
} catch (IOException ioe) {
|
||||
throw new I2NPMessageException("Error reading the leaseSet", ioe);
|
||||
}
|
||||
} else if (type == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
|
||||
_dbEntry = new RouterInfo();
|
||||
int compressedSize = (int)DataHelper.fromLong(data, curIndex, 2);
|
||||
curIndex += 2;
|
||||
if (compressedSize <= 0 || curIndex + compressedSize > data.length || (curIndex - offset) + compressedSize > dataSize)
|
||||
if (compressedSize <= 0 || curIndex + compressedSize > data.length || curIndex + compressedSize > dataSize + offset)
|
||||
throw new I2NPMessageException("Compressed RI length: " + compressedSize +
|
||||
" but remaining bytes: " + Math.min(data.length - curIndex, dataSize - (curIndex - offset)));
|
||||
" but remaining bytes: " + Math.min(data.length - curIndex, dataSize + offset -curIndex));
|
||||
|
||||
try {
|
||||
// TODO we could delay decompression, just copy to a new byte array and store in _byteCache
|
||||
@ -230,9 +235,9 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getKey()) +
|
||||
DataHelper.hashCode(_dbEntry) +
|
||||
(int)getReplyToken() +
|
||||
DataHelper.hashCode(getReplyTunnel()) +
|
||||
DataHelper.hashCode(getReplyGateway());
|
||||
(int) _replyToken +
|
||||
DataHelper.hashCode(_replyTunnel) +
|
||||
DataHelper.hashCode(_replyGateway);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -241,9 +246,9 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
DatabaseStoreMessage msg = (DatabaseStoreMessage)object;
|
||||
return DataHelper.eq(getKey(),msg.getKey()) &&
|
||||
DataHelper.eq(_dbEntry,msg.getEntry()) &&
|
||||
getReplyToken() == msg.getReplyToken() &&
|
||||
DataHelper.eq(getReplyTunnel(), msg.getReplyTunnel()) &&
|
||||
DataHelper.eq(getReplyGateway(), msg.getReplyGateway());
|
||||
_replyToken == msg._replyToken &&
|
||||
DataHelper.eq(_replyTunnel, msg._replyTunnel) &&
|
||||
DataHelper.eq(_replyGateway, msg._replyGateway);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -253,13 +258,13 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[DatabaseStoreMessage: ");
|
||||
buf.append("\n\tExpiration: ").append(getMessageExpiration());
|
||||
buf.append("\n\tUnique ID: ").append(getUniqueId());
|
||||
buf.append("\n\tExpiration: ").append(_expiration);
|
||||
buf.append("\n\tUnique ID: ").append(_uniqueId);
|
||||
buf.append("\n\tKey: ").append(getKey());
|
||||
buf.append("\n\tEntry: ").append(_dbEntry);
|
||||
buf.append("\n\tReply token: ").append(getReplyToken());
|
||||
buf.append("\n\tReply tunnel: ").append(getReplyTunnel());
|
||||
buf.append("\n\tReply gateway: ").append(getReplyGateway());
|
||||
buf.append("\n\tReply token: ").append(_replyToken);
|
||||
buf.append("\n\tReply tunnel: ").append(_replyTunnel);
|
||||
buf.append("\n\tReply gateway: ").append(_replyGateway);
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.util.Log;
|
||||
//import net.i2p.util.Log;
|
||||
|
||||
|
||||
/**
|
||||
@ -32,7 +32,7 @@ import net.i2p.util.Log;
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DeliveryInstructions extends DataStructureImpl {
|
||||
private final static Log _log = new Log(DeliveryInstructions.class);
|
||||
//private final static Log _log = new Log(DeliveryInstructions.class);
|
||||
private boolean _encrypted;
|
||||
private SessionKey _encryptionKey;
|
||||
private int _deliveryMode;
|
||||
@ -57,7 +57,7 @@ public class DeliveryInstructions extends DataStructureImpl {
|
||||
private final static long FLAG_DELAY = 16;
|
||||
|
||||
public DeliveryInstructions() {
|
||||
setDeliveryMode(-1);
|
||||
_deliveryMode = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -132,10 +132,13 @@ public class DeliveryInstructions extends DataStructureImpl {
|
||||
*/
|
||||
public void setDelaySeconds(long seconds) { _delaySeconds = seconds; }
|
||||
|
||||
/**
|
||||
* @deprecated unused
|
||||
*/
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
long flags = DataHelper.readLong(in, 1);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Read flags: " + flags + " mode: " + flagMode(flags));
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Read flags: " + flags + " mode: " + flagMode(flags));
|
||||
|
||||
/****
|
||||
if (flagEncrypted(flags)) {
|
||||
@ -188,8 +191,8 @@ public class DeliveryInstructions extends DataStructureImpl {
|
||||
int cur = offset;
|
||||
long flags = DataHelper.fromLong(data, cur, 1);
|
||||
cur++;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Read flags: " + flags + " mode: " + flagMode(flags));
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Read flags: " + flags + " mode: " + flagMode(flags));
|
||||
|
||||
/****
|
||||
if (flagEncrypted(flags)) {
|
||||
@ -289,8 +292,8 @@ public class DeliveryInstructions extends DataStructureImpl {
|
||||
val = val | fmode;
|
||||
if (getDelayRequested())
|
||||
val = val | FLAG_DELAY;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("getFlags() = " + val);
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("getFlags() = " + val);
|
||||
return val;
|
||||
}
|
||||
|
||||
@ -304,8 +307,8 @@ public class DeliveryInstructions extends DataStructureImpl {
|
||||
****/
|
||||
switch (getDeliveryMode()) {
|
||||
case FLAG_MODE_LOCAL:
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("mode = local");
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("mode = local");
|
||||
break;
|
||||
case FLAG_MODE_DESTINATION:
|
||||
if (_destinationHash == null) throw new IllegalStateException("Destination hash is not set");
|
||||
@ -334,7 +337,8 @@ public class DeliveryInstructions extends DataStructureImpl {
|
||||
int offset = 0;
|
||||
offset += getAdditionalInfo(rv, offset);
|
||||
if (offset != additionalSize)
|
||||
_log.log(Log.CRIT, "wtf, additionalSize = " + additionalSize + ", offset = " + offset);
|
||||
//_log.log(Log.CRIT, "wtf, additionalSize = " + additionalSize + ", offset = " + offset);
|
||||
throw new IllegalStateException("wtf, additionalSize = " + additionalSize + ", offset = " + offset);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -356,22 +360,22 @@ public class DeliveryInstructions extends DataStructureImpl {
|
||||
|
||||
switch (getDeliveryMode()) {
|
||||
case FLAG_MODE_LOCAL:
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("mode = local");
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("mode = local");
|
||||
break;
|
||||
case FLAG_MODE_DESTINATION:
|
||||
if (_destinationHash == null) throw new IllegalStateException("Destination hash is not set");
|
||||
System.arraycopy(_destinationHash.getData(), 0, rv, offset, Hash.HASH_LENGTH);
|
||||
offset += Hash.HASH_LENGTH;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("mode = destination, hash = " + _destinationHash);
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("mode = destination, hash = " + _destinationHash);
|
||||
break;
|
||||
case FLAG_MODE_ROUTER:
|
||||
if (_routerHash == null) throw new IllegalStateException("Router hash is not set");
|
||||
System.arraycopy(_routerHash.getData(), 0, rv, offset, Hash.HASH_LENGTH);
|
||||
offset += Hash.HASH_LENGTH;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("mode = router, routerHash = " + _routerHash);
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("mode = router, routerHash = " + _routerHash);
|
||||
break;
|
||||
case FLAG_MODE_TUNNEL:
|
||||
if ( (_routerHash == null) || (_tunnelId == null) ) throw new IllegalStateException("Router hash or tunnel ID is not set");
|
||||
@ -379,29 +383,32 @@ public class DeliveryInstructions extends DataStructureImpl {
|
||||
offset += Hash.HASH_LENGTH;
|
||||
DataHelper.toLong(rv, offset, 4, _tunnelId.getTunnelId());
|
||||
offset += 4;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("mode = tunnel, tunnelId = " + _tunnelId.getTunnelId()
|
||||
+ ", routerHash = " + _routerHash);
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("mode = tunnel, tunnelId = " + _tunnelId.getTunnelId()
|
||||
// + ", routerHash = " + _routerHash);
|
||||
break;
|
||||
}
|
||||
if (getDelayRequested()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("delay requested: " + getDelaySeconds());
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("delay requested: " + getDelaySeconds());
|
||||
DataHelper.toLong(rv, offset, 4, getDelaySeconds());
|
||||
offset += 4;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("delay NOT requested");
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("delay NOT requested");
|
||||
}
|
||||
return offset - origOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated unused
|
||||
*/
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
if ( (_deliveryMode < 0) || (_deliveryMode > FLAG_MODE_TUNNEL) ) throw new DataFormatException("Invalid data: mode = " + _deliveryMode);
|
||||
long flags = getFlags();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Write flags: " + flags + " mode: " + getDeliveryMode()
|
||||
+ " =?= " + flagMode(flags));
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Write flags: " + flags + " mode: " + getDeliveryMode()
|
||||
// + " =?= " + flagMode(flags));
|
||||
byte additionalInfo[] = getAdditionalInfo();
|
||||
DataHelper.writeLong(out, 1, flags);
|
||||
if (additionalInfo != null) {
|
||||
@ -416,9 +423,9 @@ public class DeliveryInstructions extends DataStructureImpl {
|
||||
public int writeBytes(byte target[], int offset) {
|
||||
if ( (_deliveryMode < 0) || (_deliveryMode > FLAG_MODE_TUNNEL) ) throw new IllegalStateException("Invalid data: mode = " + _deliveryMode);
|
||||
long flags = getFlags();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Write flags: " + flags + " mode: " + getDeliveryMode()
|
||||
+ " =?= " + flagMode(flags));
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Write flags: " + flags + " mode: " + getDeliveryMode()
|
||||
// + " =?= " + flagMode(flags));
|
||||
int origOffset = offset;
|
||||
DataHelper.toLong(target, offset, 1, flags);
|
||||
offset++;
|
||||
|
@ -8,8 +8,6 @@ package net.i2p.data.i2np;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
@ -19,24 +17,47 @@ import net.i2p.data.DataHelper;
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DeliveryStatusMessage extends I2NPMessageImpl {
|
||||
public class DeliveryStatusMessage extends FastI2NPMessageImpl {
|
||||
public final static int MESSAGE_TYPE = 10;
|
||||
private long _id;
|
||||
private long _arrival;
|
||||
|
||||
public DeliveryStatusMessage(I2PAppContext context) {
|
||||
super(context);
|
||||
setMessageId(-1);
|
||||
setArrival(-1);
|
||||
_id = -1;
|
||||
_arrival = -1;
|
||||
}
|
||||
|
||||
public long getMessageId() { return _id; }
|
||||
public void setMessageId(long id) { _id = id; }
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if id previously set, to protect saved checksum
|
||||
*/
|
||||
public void setMessageId(long id) {
|
||||
if (_id >= 0)
|
||||
throw new IllegalStateException();
|
||||
_id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Misnamed, as it is generally (always?) set by the creator to the current time,
|
||||
* in some future usage it could be set on arrival
|
||||
*/
|
||||
public long getArrival() { return _arrival; }
|
||||
public void setArrival(long arrival) { _arrival = arrival; }
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
/**
|
||||
* Misnamed, as it is generally (always?) set by the creator to the current time,
|
||||
* in some future usage it could be set on arrival
|
||||
*/
|
||||
public void setArrival(long arrival) {
|
||||
// To accomodate setting on arrival,
|
||||
// invalidate the stored checksum instead of throwing ISE
|
||||
if (_arrival >= 0)
|
||||
_hasChecksum = false;
|
||||
_arrival = arrival;
|
||||
}
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
int curIndex = offset;
|
||||
|
||||
|
186
router/java/src/net/i2p/data/i2np/FastI2NPMessageImpl.java
Normal file
186
router/java/src/net/i2p/data/i2np/FastI2NPMessageImpl.java
Normal file
@ -0,0 +1,186 @@
|
||||
package net.i2p.data.i2np;
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2003 and released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* your children, but it might. Use at your own risk.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.HexDump;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleByteCache;
|
||||
|
||||
/**
|
||||
* Ignore, but save, the SHA-256 checksum in the full 16-byte header when read in.
|
||||
* Use the same checksum when writing out.
|
||||
*
|
||||
* This is a savings for NTCP in,
|
||||
* and for NTCP-in to NTCP-out for TunnelDataMessages.
|
||||
* It's also a savings for messages embedded in other messages.
|
||||
* Note that SSU does not use the SHA-256 checksum.
|
||||
*
|
||||
* Subclasses must take care to set _hasChecksum to false to invalidate it
|
||||
* if the message payload changes between reading and writing.
|
||||
*
|
||||
* It isn't clear where, if anywhere, we actually need to send a checksum.
|
||||
* For point-to-point messages over NTCP where we know the router version
|
||||
* of the peer, we could add a method to skip checksum generation.
|
||||
* For end-to-end I2NP messages embedded in a Garlic, TGM, etc...
|
||||
* we would need a flag day.
|
||||
*
|
||||
* @since 0.8.12
|
||||
*/
|
||||
public abstract class FastI2NPMessageImpl extends I2NPMessageImpl {
|
||||
protected byte _checksum;
|
||||
// We skip the fiction that CHECKSUM_LENGTH will ever be anything but 1
|
||||
protected boolean _hasChecksum;
|
||||
|
||||
public FastI2NPMessageImpl(I2PAppContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated unused
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
@Override
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated unused
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
@Override
|
||||
public int readBytes(InputStream in, int type, byte buffer[]) throws I2NPMessageException, IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore, but save, the checksum, to be used later if necessary.
|
||||
*
|
||||
* @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
|
||||
* @throws IllegalStateException if called twice, to protect saved checksum
|
||||
*/
|
||||
@Override
|
||||
public int readBytes(byte data[], int type, int offset, int maxLen) throws I2NPMessageException {
|
||||
if (_hasChecksum)
|
||||
throw new IllegalStateException(getClass().getSimpleName() + " read twice");
|
||||
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);
|
||||
cur++;
|
||||
}
|
||||
_uniqueId = DataHelper.fromLong(data, cur, 4);
|
||||
cur += 4;
|
||||
_expiration = 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 || headerSize + size > maxLen)
|
||||
throw new I2NPMessageException("Payload is too short ["
|
||||
+ "data.len=" + data.length
|
||||
+ "maxLen=" + maxLen
|
||||
+ " offset=" + offset
|
||||
+ " cur=" + cur
|
||||
+ " wanted=" + size + "]: " + getClass().getSimpleName());
|
||||
|
||||
int sz = Math.min(size, maxLen - headerSize);
|
||||
readMessage(data, cur, sz, type);
|
||||
cur += sz;
|
||||
_hasChecksum = true;
|
||||
if (VERIFY_TEST && _log.shouldLog(Log.INFO))
|
||||
_log.info("Ignored c/s " + getClass().getSimpleName());
|
||||
return cur - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated unused
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
@Override
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests the reuse-checksum feature.
|
||||
* The results are that mostly UnknownI2NPMessages (from inside a TGM),
|
||||
* with a lot of DeliveryStatusMessages,
|
||||
* and a few DatabaseLookupMessages that get reused.
|
||||
* The last two are tiny, but the savings at the gateway should help.
|
||||
*/
|
||||
private static final boolean VERIFY_TEST = true;
|
||||
|
||||
/**
|
||||
* If available, use the previously-computed or previously-read checksum for speed
|
||||
*/
|
||||
@Override
|
||||
public int toByteArray(byte buffer[]) {
|
||||
if (_hasChecksum)
|
||||
return toByteArrayWithSavedChecksum(buffer);
|
||||
if (VERIFY_TEST && _log.shouldLog(Log.INFO))
|
||||
_log.info("Generating new c/s " + getClass().getSimpleName());
|
||||
return super.toByteArray(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a previously-computed checksum for speed
|
||||
*/
|
||||
protected int toByteArrayWithSavedChecksum(byte buffer[]) {
|
||||
try {
|
||||
int writtenLen = writeMessageBody(buffer, HEADER_LENGTH);
|
||||
if (VERIFY_TEST) {
|
||||
byte[] h = SimpleByteCache.acquire(32);
|
||||
_context.sha().calculateHash(buffer, HEADER_LENGTH, writtenLen - HEADER_LENGTH, h, 0);
|
||||
if (h[0] != _checksum) {
|
||||
_log.log(Log.CRIT, "Please report " + getClass().getSimpleName() +
|
||||
" size " + writtenLen +
|
||||
" saved c/s " + Integer.toHexString(_checksum & 0xff) +
|
||||
" calc " + Integer.toHexString(h[0] & 0xff), new Exception());
|
||||
_log.log(Log.CRIT, "DUMP:\n" + HexDump.dump(buffer, HEADER_LENGTH, writtenLen - HEADER_LENGTH));
|
||||
_log.log(Log.CRIT, "RAW:\n" + Base64.encode(buffer, HEADER_LENGTH, writtenLen - HEADER_LENGTH));
|
||||
_checksum = h[0];
|
||||
} else if (_log.shouldLog(Log.INFO)) {
|
||||
_log.info("Using saved c/s " + getClass().getSimpleName() + ' ' + _checksum);
|
||||
}
|
||||
SimpleByteCache.release(h);
|
||||
}
|
||||
int payloadLen = writtenLen - HEADER_LENGTH;
|
||||
int off = 0;
|
||||
DataHelper.toLong(buffer, off, 1, getType());
|
||||
off += 1;
|
||||
DataHelper.toLong(buffer, off, 4, _uniqueId);
|
||||
off += 4;
|
||||
DataHelper.toLong(buffer, off, DataHelper.DATE_LENGTH, _expiration);
|
||||
off += DataHelper.DATE_LENGTH;
|
||||
DataHelper.toLong(buffer, off, 2, payloadLen);
|
||||
off += 2;
|
||||
buffer[off] = _checksum;
|
||||
return writtenLen;
|
||||
} catch (I2NPMessageException ime) {
|
||||
_context.logManager().getLog(getClass()).log(Log.CRIT, "Error writing", ime);
|
||||
throw new IllegalStateException("Unable to serialize the message " + getClass().getSimpleName(), ime);
|
||||
}
|
||||
}
|
||||
}
|
@ -24,23 +24,25 @@ import net.i2p.util.Log;
|
||||
* Contains one deliverable message encrypted to a router along with instructions
|
||||
* and a certificate 'paying for' the delivery.
|
||||
*
|
||||
* Note that certificates are always the null certificate at this time, others are unimplemented.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class GarlicClove extends DataStructureImpl {
|
||||
private Log _log;
|
||||
private RouterContext _context;
|
||||
private final Log _log;
|
||||
//private final RouterContext _context;
|
||||
private DeliveryInstructions _instructions;
|
||||
private I2NPMessage _msg;
|
||||
private long _cloveId;
|
||||
private Date _expiration;
|
||||
private Certificate _certificate;
|
||||
private I2NPMessageHandler _handler;
|
||||
private final I2NPMessageHandler _handler;
|
||||
|
||||
public GarlicClove(RouterContext context) {
|
||||
_context = context;
|
||||
//_context = context;
|
||||
_log = context.logManager().getLog(GarlicClove.class);
|
||||
_handler = new I2NPMessageHandler(context);
|
||||
setCloveId(-1);
|
||||
_cloveId = -1;
|
||||
}
|
||||
|
||||
public DeliveryInstructions getInstructions() { return _instructions; }
|
||||
@ -54,6 +56,9 @@ public class GarlicClove extends DataStructureImpl {
|
||||
public Certificate getCertificate() { return _certificate; }
|
||||
public void setCertificate(Certificate cert) { _certificate = cert; }
|
||||
|
||||
/**
|
||||
* @deprecated unused, use byte array method to avoid copying
|
||||
*/
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_instructions = new DeliveryInstructions();
|
||||
_instructions.readBytes(in);
|
||||
@ -86,8 +91,6 @@ public class GarlicClove extends DataStructureImpl {
|
||||
_msg = _handler.lastRead();
|
||||
} catch (I2NPMessageException ime) {
|
||||
throw new DataFormatException("Unable to read the message from a garlic clove", ime);
|
||||
} catch (IOException ioe) {
|
||||
throw new DataFormatException("Not enough data to read the clove", ioe);
|
||||
}
|
||||
_cloveId = DataHelper.fromLong(source, cur, 4);
|
||||
cur += 4;
|
||||
@ -95,14 +98,18 @@ public class GarlicClove extends DataStructureImpl {
|
||||
cur += DataHelper.DATE_LENGTH;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("CloveID read: " + _cloveId + " expiration read: " + _expiration);
|
||||
_certificate = new Certificate();
|
||||
cur += _certificate.readBytes(source, cur);
|
||||
//_certificate = new Certificate();
|
||||
//cur += _certificate.readBytes(source, cur);
|
||||
_certificate = Certificate.create(source, cur);
|
||||
cur += _certificate.size();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Read cert: " + _certificate);
|
||||
return cur - offset;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated unused, use byte array method to avoid copying
|
||||
*/
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
StringBuilder error = null;
|
||||
if (_instructions == null) {
|
||||
|
@ -8,8 +8,6 @@ package net.i2p.data.i2np;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
@ -18,7 +16,7 @@ import net.i2p.data.DataHelper;
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class GarlicMessage extends I2NPMessageImpl {
|
||||
public class GarlicMessage extends FastI2NPMessageImpl {
|
||||
public final static int MESSAGE_TYPE = 11;
|
||||
private byte[] _data;
|
||||
|
||||
@ -29,11 +27,17 @@ public class GarlicMessage extends I2NPMessageImpl {
|
||||
public byte[] getData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if data previously set, to protect saved checksum
|
||||
*/
|
||||
public void setData(byte[] data) {
|
||||
if (_data != null)
|
||||
throw new IllegalStateException();
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
int curIndex = offset;
|
||||
|
||||
|
@ -26,16 +26,53 @@ public interface I2NPMessage extends DataStructure {
|
||||
* Read the body into the data structures, after the initial type byte, using
|
||||
* the current class's format as defined by the I2NP specification
|
||||
*
|
||||
* Unused - All transports provide encapsulation and so we have byte arrays available.
|
||||
*
|
||||
* @param in stream to read from
|
||||
* @param type I2NP message type
|
||||
* starting at type if type is < 0 (16 byte header)
|
||||
* starting at ID if type is >= 0 (15 byte header)
|
||||
* @param type I2NP message type. If less than zero, read the type from data
|
||||
* @param buffer scratch buffer to be used when reading and parsing
|
||||
* @return size of the message read (including headers)
|
||||
* @throws I2NPMessageException if the stream doesn't contain a valid message
|
||||
* that this class can read.
|
||||
* @throws IOException if there is a problem reading from the stream
|
||||
* @deprecated unused
|
||||
*/
|
||||
public int readBytes(InputStream in, int type, byte buffer[]) throws I2NPMessageException, IOException;
|
||||
public int readBytes(byte data[], int type, int offset) throws I2NPMessageException, IOException;
|
||||
|
||||
/**
|
||||
* Read the body into the data structures, after the initial type byte, using
|
||||
* the current class's format as defined by the I2NP specification
|
||||
*
|
||||
* @param data the data
|
||||
* @param type I2NP message type. If less than zero, read the type from data
|
||||
* @param offset where to start
|
||||
* starting at type if type is < 0 (16 byte header)
|
||||
* starting at ID if type is >= 0 (15 byte header)
|
||||
* @return size of the message read (including headers)
|
||||
* @throws I2NPMessageException if there is no valid message
|
||||
* @throws IOException if there is a problem reading from the stream
|
||||
*/
|
||||
public int readBytes(byte data[], int type, int offset) throws I2NPMessageException;
|
||||
|
||||
/**
|
||||
* Read the body into the data structures, after the initial type byte, using
|
||||
* the current class's format as defined by the I2NP specification
|
||||
*
|
||||
* @param data the data, may or may not include the type
|
||||
* @param type I2NP message type. If less than zero, read the type from data
|
||||
* @param offset where to start
|
||||
* starting at type if type is < 0 (16 byte header)
|
||||
* starting at ID if type is >= 0 (15 byte header)
|
||||
* @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
|
||||
* @return size of the message read (including headers)
|
||||
* @throws I2NPMessageException if there is no valid message
|
||||
* @throws IOException if there is a problem reading from the stream
|
||||
* @since 0.8.12
|
||||
*/
|
||||
public int readBytes(byte data[], int type, int offset, int maxLen) throws I2NPMessageException;
|
||||
|
||||
/**
|
||||
* Read the body into the data structures, after the initial type byte and
|
||||
@ -50,8 +87,8 @@ public interface I2NPMessage extends DataStructure {
|
||||
* that this class can read.
|
||||
* @throws IOException if there is a problem reading from the stream
|
||||
*/
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException;
|
||||
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) throws I2NPMessageException;
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type, I2NPMessageHandler handler) throws I2NPMessageException;
|
||||
|
||||
/**
|
||||
* Return the unique identifier for this type of I2NP message, as defined in
|
||||
@ -73,22 +110,25 @@ public interface I2NPMessage extends DataStructure {
|
||||
public void setMessageExpiration(long exp);
|
||||
|
||||
|
||||
/** How large the message is, including any checksums */
|
||||
/** How large the message is, including any checksums, i.e. full 16 byte header */
|
||||
public int getMessageSize();
|
||||
/** How large the raw message is */
|
||||
public int getRawMessageSize();
|
||||
|
||||
/** How large the raw message is with the short 5 byte header */
|
||||
public int getRawMessageSize();
|
||||
|
||||
/**
|
||||
* write the message to the buffer, returning the number of bytes written.
|
||||
* the data is formatted so as to be self contained, with the type, size,
|
||||
* expiration, unique id, as well as a checksum bundled along.
|
||||
* Full 16 byte header.
|
||||
*/
|
||||
public int toByteArray(byte buffer[]);
|
||||
|
||||
/**
|
||||
* write the message to the buffer, returning the number of bytes written.
|
||||
* the data is is not self contained - it does not include the size,
|
||||
* unique id, or any checksum, but does include the type and expiration.
|
||||
* Short 5 byte header.
|
||||
*/
|
||||
public int toRawByteArray(byte buffer[]);
|
||||
}
|
||||
|
@ -39,7 +39,11 @@ public class I2NPMessageHandler {
|
||||
/**
|
||||
* Read an I2NPMessage from the stream and return the fully populated object.
|
||||
*
|
||||
* @throws IOException if there is an IO problem reading from the stream
|
||||
* This is only called by I2NPMessageReader which is unused.
|
||||
* All transports provide encapsulation and so we have byte arrays available.
|
||||
*
|
||||
* @deprecated use the byte array method to avoid an extra copy if you have it
|
||||
*
|
||||
* @throws I2NPMessageException if there is a problem handling the particular
|
||||
* message - if it is an unknown type or has improper formatting, etc.
|
||||
*/
|
||||
@ -54,15 +58,12 @@ public class I2NPMessageHandler {
|
||||
// throw new I2NPMessageException("The type "+ type + " is an unknown I2NP message");
|
||||
try {
|
||||
_lastSize = msg.readBytes(in, type, _messageBuffer);
|
||||
} catch (IOException ioe) {
|
||||
throw ioe;
|
||||
} catch (I2NPMessageException ime) {
|
||||
throw ime;
|
||||
} catch (Exception e) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error reading the stream", e);
|
||||
throw new IOException("Unknown error reading the " + msg.getClass().getName()
|
||||
+ ": " + e.getMessage());
|
||||
throw new I2NPMessageException("Unknown error reading the " + msg.getClass().getSimpleName(), e);
|
||||
}
|
||||
_lastReadEnd = System.currentTimeMillis();
|
||||
return msg;
|
||||
@ -79,19 +80,31 @@ public class I2NPMessageHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an I2NPMessage from the stream and return the fully populated object.
|
||||
* Read an I2NPMessage from the byte array and return the fully populated object.
|
||||
*
|
||||
* @throws IOException if there is an IO problem reading from the stream
|
||||
* @throws I2NPMessageException if there is a problem handling the particular
|
||||
* message - if it is an unknown type or has improper formatting, etc.
|
||||
*/
|
||||
public I2NPMessage readMessage(byte data[]) throws IOException, I2NPMessageException {
|
||||
readMessage(data, 0);
|
||||
public I2NPMessage readMessage(byte data[]) throws I2NPMessageException {
|
||||
readMessage(data, 0, data.length);
|
||||
return lastRead();
|
||||
}
|
||||
|
||||
public int readMessage(byte data[], int offset) throws IOException, I2NPMessageException {
|
||||
public int readMessage(byte data[], int offset) throws I2NPMessageException {
|
||||
return readMessage(data, 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
|
||||
* must be at least 16
|
||||
* @since 0.8.12
|
||||
*/
|
||||
public int readMessage(byte data[], int offset, int maxLen) throws I2NPMessageException {
|
||||
int cur = offset;
|
||||
// we will assume that maxLen is >= 1 here. It's checked to be >= 16 in readBytes()
|
||||
int type = (int)DataHelper.fromLong(data, cur, 1);
|
||||
cur++;
|
||||
_lastReadBegin = System.currentTimeMillis();
|
||||
@ -110,17 +123,14 @@ public class I2NPMessageHandler {
|
||||
// + sz + " all zeros? " + allZero + ")");
|
||||
//}
|
||||
try {
|
||||
_lastSize = msg.readBytes(data, type, cur);
|
||||
_lastSize = msg.readBytes(data, type, cur, maxLen - 1);
|
||||
cur += _lastSize;
|
||||
} catch (IOException ioe) {
|
||||
throw ioe;
|
||||
} catch (I2NPMessageException ime) {
|
||||
throw ime;
|
||||
} catch (Exception e) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error reading the stream", e);
|
||||
throw new IOException("Unknown error reading the " + msg.getClass().getName()
|
||||
+ ": " + e.getMessage());
|
||||
throw new I2NPMessageException("Unknown error reading the " + msg.getClass().getSimpleName(), e);
|
||||
}
|
||||
_lastReadEnd = System.currentTimeMillis();
|
||||
_lastRead = msg;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -23,6 +23,15 @@ import net.i2p.util.Log;
|
||||
* thrown, or the connection being closed. Routers should use this rather
|
||||
* than read from the stream themselves.
|
||||
*
|
||||
* Deprecated - unused.
|
||||
* This was used by the old TCP transport.
|
||||
* Both the NTCP and SSU transports provide encapsulation
|
||||
* of I2NP messages, so they use I2NPMessageHandlers directly.
|
||||
* If we ever add a transport that does not provide encapsulation,
|
||||
* this will be useful again.
|
||||
*
|
||||
* @deprecated unused
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class I2NPMessageReader {
|
||||
|
@ -1,7 +1,5 @@
|
||||
package net.i2p.data.i2np;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,5 @@
|
||||
package net.i2p.data.i2np;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
|
||||
@ -9,6 +7,14 @@ import net.i2p.data.ByteArray;
|
||||
* Base for TBM, TBRM, VTBM, VTBRM
|
||||
* Retrofitted over them.
|
||||
* There's really no difference between the build and build reply.
|
||||
*
|
||||
* TBM and VBTM (but not TBRM and VTBRM?) messages are modified
|
||||
* in-place by doing a single setRecord(), and retransmitted.
|
||||
* Therefore they are NOT good candidates to use FastI2NPMessageImpl;
|
||||
* the checksum would have to be invalidated with every setRecord().
|
||||
* Which we could do in TBM and VTBM but not TBRM and VTBRM,
|
||||
* but keep it simple for now.
|
||||
*
|
||||
* @since 0.8.8
|
||||
*/
|
||||
public abstract class TunnelBuildMessageBase extends I2NPMessageImpl {
|
||||
@ -41,7 +47,7 @@ public abstract class TunnelBuildMessageBase extends I2NPMessageImpl {
|
||||
|
||||
protected int calculateWrittenLength() { return RECORD_SIZE * RECORD_COUNT; }
|
||||
|
||||
public void readMessage(byte[] data, int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
public void readMessage(byte[] data, int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
if (type != getType())
|
||||
throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
if (dataSize != calculateWrittenLength())
|
||||
|
@ -1,7 +1,5 @@
|
||||
package net.i2p.data.i2np;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
|
@ -8,8 +8,6 @@ package net.i2p.data.i2np;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
@ -20,15 +18,20 @@ import net.i2p.util.Log;
|
||||
/**
|
||||
* Defines the message sent between routers as part of the tunnel delivery
|
||||
*
|
||||
* The tunnel ID is changed in-place by TunnelParticipant.send(), so
|
||||
* we can't reuse the checksum on output, but we still subclass
|
||||
* FastI2NPMessageImpl so we don't verify the checksum on input...
|
||||
* because this is a high-usage class.
|
||||
*
|
||||
*/
|
||||
public class TunnelDataMessage extends I2NPMessageImpl {
|
||||
public class TunnelDataMessage extends FastI2NPMessageImpl {
|
||||
private long _tunnelId;
|
||||
private TunnelId _tunnelIdObj;
|
||||
private byte[] _data;
|
||||
private ByteArray _dataBuf;
|
||||
|
||||
public final static int MESSAGE_TYPE = 18;
|
||||
private static final int DATA_SIZE = 1024;
|
||||
public static final int DATA_SIZE = 1024;
|
||||
/** if we can't deliver a tunnel message in 10s, fuck it */
|
||||
private static final int EXPIRATION_PERIOD = 10*1000;
|
||||
|
||||
@ -104,13 +107,26 @@ public class TunnelDataMessage extends I2NPMessageImpl {
|
||||
}
|
||||
|
||||
public long getTunnelId() { return _tunnelId; }
|
||||
public void setTunnelId(long id) { _tunnelId = id; }
|
||||
|
||||
/**
|
||||
* (correctly) Invalidates stored checksum
|
||||
*/
|
||||
public void setTunnelId(long id) {
|
||||
_hasChecksum = false;
|
||||
_tunnelId = id;
|
||||
}
|
||||
|
||||
public TunnelId getTunnelIdObj() {
|
||||
if (_tunnelIdObj == null)
|
||||
_tunnelIdObj = new TunnelId(_tunnelId); // not thread safe, but immutable, so who cares
|
||||
return _tunnelIdObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* (correctly) Invalidates stored checksum
|
||||
*/
|
||||
public void setTunnelId(TunnelId id) {
|
||||
_hasChecksum = false;
|
||||
_tunnelIdObj = id;
|
||||
_tunnelId = id.getTunnelId();
|
||||
}
|
||||
@ -124,13 +140,18 @@ public class TunnelDataMessage extends I2NPMessageImpl {
|
||||
return _data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if data previously set, to protect saved checksum
|
||||
*/
|
||||
public void setData(byte data[]) {
|
||||
if (_data != null)
|
||||
throw new IllegalStateException();
|
||||
if ( (data == null) || (data.length <= 0) )
|
||||
throw new IllegalArgumentException("Empty tunnel payload?");
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
int curIndex = offset;
|
||||
|
||||
@ -214,8 +235,8 @@ public class TunnelDataMessage extends I2NPMessageImpl {
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[TunnelDataMessage:");
|
||||
buf.append(" MessageId: ").append(getUniqueId());
|
||||
buf.append(" Tunnel ID: ").append(getTunnelId());
|
||||
buf.append(" MessageId: ").append(_uniqueId);
|
||||
buf.append(" Tunnel ID: ").append(_tunnelId);
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
|
@ -8,8 +8,6 @@ package net.i2p.data.i2np;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.TunnelId;
|
||||
@ -20,7 +18,7 @@ import net.i2p.util.Log;
|
||||
* format: { tunnelId, sizeof(i2npMessage.toByteArray()), i2npMessage.toByteArray() }
|
||||
*
|
||||
*/
|
||||
public class TunnelGatewayMessage extends I2NPMessageImpl {
|
||||
public class TunnelGatewayMessage extends FastI2NPMessageImpl {
|
||||
private TunnelId _tunnelId;
|
||||
private I2NPMessage _msg;
|
||||
private byte _msgData[];
|
||||
@ -37,16 +35,32 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
|
||||
}
|
||||
|
||||
public TunnelId getTunnelId() { return _tunnelId; }
|
||||
public void setTunnelId(TunnelId id) { _tunnelId = id; }
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if id previously set, to protect saved checksum
|
||||
*/
|
||||
public void setTunnelId(TunnelId id) {
|
||||
if (_tunnelId != null)
|
||||
throw new IllegalStateException();
|
||||
_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().
|
||||
*
|
||||
* Note that if you change the expiration on the embedded message it will
|
||||
* mess up the checksum of this message, so don't do that.
|
||||
*/
|
||||
public I2NPMessage getMessage() { return _msg; }
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if msg previously set, to protect saved checksum
|
||||
*/
|
||||
public void setMessage(I2NPMessage msg) {
|
||||
if (_msg != null)
|
||||
throw new IllegalStateException();
|
||||
if (msg == null)
|
||||
throw new IllegalArgumentException("wtf, dont set me to null");
|
||||
_msg = msg;
|
||||
@ -90,7 +104,7 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
|
||||
}
|
||||
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
//I2NPMessageHandler h = new I2NPMessageHandler(_context);
|
||||
//readMessage(data, offset, dataSize, type, h);
|
||||
readMessage(data, offset, dataSize, type, null);
|
||||
@ -103,7 +117,7 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
|
||||
* @param handler unused, may be null
|
||||
*/
|
||||
@Override
|
||||
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 {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
int curIndex = offset;
|
||||
|
||||
@ -137,7 +151,7 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
|
||||
// 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);
|
||||
umsg.readBytes(data, utype, curIndex);
|
||||
_msg = umsg;
|
||||
}
|
||||
|
||||
|
@ -8,8 +8,6 @@ package net.i2p.data.i2np;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
@ -32,11 +30,9 @@ import net.i2p.util.SimpleByteCache;
|
||||
*
|
||||
* @since 0.7.12 but broken before 0.8.12
|
||||
*/
|
||||
public class UnknownI2NPMessage extends I2NPMessageImpl {
|
||||
public class UnknownI2NPMessage extends FastI2NPMessageImpl {
|
||||
private byte _data[];
|
||||
private final int _type;
|
||||
// we assume CHECKSUM_LENGTH = 1
|
||||
private byte _checksum;
|
||||
|
||||
/** @param type 0-255 */
|
||||
public UnknownI2NPMessage(I2PAppContext context, int type) {
|
||||
@ -44,7 +40,12 @@ public class UnknownI2NPMessage extends I2NPMessageImpl {
|
||||
_type = type;
|
||||
}
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
/**
|
||||
* @throws IllegalStateException if data previously set, to protect saved checksum
|
||||
*/
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
if (_data != null)
|
||||
throw new IllegalStateException();
|
||||
if (type != _type) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
if (dataSize > MAX_SIZE)
|
||||
throw new I2NPMessageException("wtf, size=" + dataSize);
|
||||
@ -77,51 +78,9 @@ public class UnknownI2NPMessage extends I2NPMessageImpl {
|
||||
*/
|
||||
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)
|
||||
* @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.
|
||||
* This does the delayed verification using the saved checksum.
|
||||
*
|
||||
* Used by TunnelGatewayZeroHop.
|
||||
*
|
||||
@ -129,6 +88,8 @@ public class UnknownI2NPMessage extends I2NPMessageImpl {
|
||||
* @since 0.8.12
|
||||
*/
|
||||
public I2NPMessage convert() throws I2NPMessageException {
|
||||
if (_data == null || !_hasChecksum)
|
||||
throw new I2NPMessageException("Illegal state");
|
||||
I2NPMessage msg = I2NPMessageImpl.createMessage(_context, _type);
|
||||
if (msg instanceof UnknownI2NPMessage)
|
||||
throw new I2NPMessageException("Unable to convert unknown type " + _type);
|
||||
@ -138,13 +99,9 @@ public class UnknownI2NPMessage extends I2NPMessageImpl {
|
||||
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());
|
||||
msg.readMessage(_data, 0, _data.length, _type);
|
||||
msg.setUniqueId(_uniqueId);
|
||||
msg.setMessageExpiration(_expiration);
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
package net.i2p.data.i2np;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
@ -30,7 +28,7 @@ public class VariableTunnelBuildMessage extends TunnelBuildMessage {
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
@Override
|
||||
public void readMessage(byte[] data, int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
public void readMessage(byte[] data, int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
// message type will be checked in super()
|
||||
int r = (int)DataHelper.fromLong(data, offset, 1);
|
||||
if (r <= 0 || r > MAX_RECORD_COUNT)
|
||||
|
@ -1,7 +1,5 @@
|
||||
package net.i2p.data.i2np;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
@ -32,7 +30,7 @@ public class VariableTunnelBuildReplyMessage extends TunnelBuildReplyMessage {
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
@Override
|
||||
public void readMessage(byte[] data, int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
public void readMessage(byte[] data, int offset, int dataSize, int type) throws I2NPMessageException {
|
||||
// message type will be checked in super()
|
||||
int r = (int)DataHelper.fromLong(data, offset, 1);
|
||||
if (r <= 0 || r > MAX_RECORD_COUNT)
|
||||
|
@ -28,6 +28,7 @@ import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.transport.FIFOBandwidthLimiter;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.HexDump;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -1226,13 +1227,15 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
||||
|
||||
//public long getReadTime() { return _curReadState.getReadTime(); }
|
||||
|
||||
/**
|
||||
* Just a byte array now (used to have a BAIS in it too,
|
||||
* but that required an extra copy in the message handler)
|
||||
*/
|
||||
private static class DataBuf {
|
||||
final byte data[];
|
||||
final ByteArrayInputStream bais;
|
||||
|
||||
public DataBuf() {
|
||||
data = new byte[BUFFER_SIZE];
|
||||
bais = new ByteArrayInputStream(data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1247,7 +1250,6 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
||||
}
|
||||
|
||||
private static void releaseReadBuf(DataBuf buf) {
|
||||
buf.bais.reset();
|
||||
_dataReadBufs.offer(buf);
|
||||
}
|
||||
|
||||
@ -1387,15 +1389,14 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
||||
if (val == _expectedCrc) {
|
||||
try {
|
||||
I2NPMessageHandler h = acquireHandler(_context);
|
||||
//I2NPMessage read = h.readMessage(new ByteArrayInputStream(_data, 0, _size));
|
||||
// the _bais is mark()ed at 0 on construction, and on init() we
|
||||
// reset() it back to that position, so this read always starts
|
||||
// at the beginning of the _data buffer. the I2NPMessageHandler
|
||||
// also only reads the first I2NP message found, and does not
|
||||
// depend upon EOF to stop reading, so its ok that the _bais could
|
||||
// in theory return more data than _size bytes, since h.readMessage
|
||||
// stops when it should.
|
||||
I2NPMessage read = h.readMessage(_dataBuf.bais);
|
||||
|
||||
// Don't do readMessage(InputStream). I2NPMessageImpl.readBytes() copies the data
|
||||
// from a stream to a temp buffer.
|
||||
// We could extend BAIS to adjust the protected count variable to _size
|
||||
// so that readBytes() doesn't read too far, but it could still read too far.
|
||||
// So use the new handler method that limits the size.
|
||||
h.readMessage(_dataBuf.data, 0, _size);
|
||||
I2NPMessage read = h.lastRead();
|
||||
long timeToRecv = System.currentTimeMillis() - _stateBegin;
|
||||
releaseHandler(h);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
@ -1414,33 +1415,31 @@ class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
||||
_lastReceiveTime = System.currentTimeMillis();
|
||||
_messagesRead++;
|
||||
}
|
||||
// get it ready for the next I2NP message
|
||||
init();
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error parsing I2NP message", ioe);
|
||||
_context.statManager().addRateData("ntcp.corruptI2NPIOE", 1);
|
||||
close();
|
||||
// handler and databuf are lost
|
||||
return;
|
||||
} catch (I2NPMessageException ime) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
_log.warn("Error parsing I2NP message", ime);
|
||||
_log.warn("DUMP:\n" + HexDump.dump(_dataBuf.data, 0, _size));
|
||||
_log.warn("RAW:\n" + Base64.encode(_dataBuf.data, 0, _size));
|
||||
}
|
||||
_context.statManager().addRateData("ntcp.corruptI2NPIME", 1);
|
||||
// FIXME don't close the con, possible attack vector?
|
||||
close();
|
||||
// handler and databuf are lost
|
||||
return;
|
||||
// Don't close the con, possible attack vector, not necessarily the peer's fault,
|
||||
// and should be recoverable
|
||||
// handler and databuf are lost if we do this
|
||||
//close();
|
||||
//return;
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("CRC incorrect for message " + _messagesRead + " (calc=" + val + " expected=" + _expectedCrc + ") size=" + _size + " blocks " + _blocks);
|
||||
_context.statManager().addRateData("ntcp.corruptI2NPCRC", 1);
|
||||
// FIXME don't close the con, possible attack vector?
|
||||
close();
|
||||
// databuf is lost
|
||||
return;
|
||||
// This probably can't be spoofed from somebody else, but do we really need to close it?
|
||||
// This is rare.
|
||||
//close();
|
||||
// databuf is lost if we do this
|
||||
//return;
|
||||
}
|
||||
// get it ready for the next I2NP message
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user