forked from I2P_Developers/i2p.i2p
beginning of branch i2p.i2p.i2p
This commit is contained in:
84
router/java/src/net/i2p/data/i2np/DataMessage.java
Normal file
84
router/java/src/net/i2p/data/i2np/DataMessage.java
Normal file
@@ -0,0 +1,84 @@
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Defines a message containing arbitrary bytes of data
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DataMessage extends I2NPMessageImpl {
|
||||
private final static Log _log = new Log(DataMessage.class);
|
||||
public final static int MESSAGE_TYPE = 20;
|
||||
private byte _data[];
|
||||
|
||||
public DataMessage() {
|
||||
_data = null;
|
||||
}
|
||||
|
||||
public byte[] getData() { return _data; }
|
||||
public void setData(byte data[]) { _data = data; }
|
||||
|
||||
public int getSize() { return _data.length; }
|
||||
|
||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
try {
|
||||
int size = (int)DataHelper.readLong(in, 4);
|
||||
_data = new byte[size];
|
||||
int read = read(in, _data);
|
||||
if (read != size)
|
||||
throw new DataFormatException("Not enough bytes to read (read = " + read + ", expected = " + size + ")");
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] writeMessage() throws I2NPMessageException, IOException {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream((_data != null ? _data.length + 4 : 4));
|
||||
try {
|
||||
DataHelper.writeLong(os, 4, (_data != null ? _data.length : 0));
|
||||
os.write(_data);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getData());
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof DataMessage) ) {
|
||||
DataMessage msg = (DataMessage)object;
|
||||
return DataHelper.eq(getData(),msg.getData());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("[DataMessage: ");
|
||||
buf.append("\n\tData: ").append(DataHelper.toString(getData(), 64));
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Defines the message a router sends to another router to help integrate into
|
||||
* the network by searching for routers in a particular keyspace.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DatabaseFindNearestMessage extends I2NPMessageImpl {
|
||||
private final static Log _log = new Log(DatabaseFindNearestMessage.class);
|
||||
public final static int MESSAGE_TYPE = 4;
|
||||
private Hash _key;
|
||||
private Hash _from;
|
||||
|
||||
public DatabaseFindNearestMessage() {
|
||||
setSearchKey(null);
|
||||
setFromHash(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the key being searched for
|
||||
*/
|
||||
public Hash getSearchKey() { return _key; }
|
||||
public void setSearchKey(Hash key) { _key = key; }
|
||||
|
||||
/**
|
||||
* Contains the SHA256 Hash of the RouterIdentity sending the message
|
||||
*/
|
||||
public Hash getFromHash() { return _from; }
|
||||
public void setFromHash(Hash from) { _from = from; }
|
||||
|
||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
try {
|
||||
_key = new Hash();
|
||||
_key.readBytes(in);
|
||||
_from = new Hash();
|
||||
_from.readBytes(in);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] writeMessage() throws I2NPMessageException, IOException {
|
||||
if ( (_key == null) || (_from == null) ) throw new I2NPMessageException("Not enough data to write out");
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
|
||||
try {
|
||||
_key.writeBytes(os);
|
||||
_from.writeBytes(os);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getSearchKey()) +
|
||||
DataHelper.hashCode(getFromHash());
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof DatabaseFindNearestMessage) ) {
|
||||
DatabaseFindNearestMessage msg = (DatabaseFindNearestMessage)object;
|
||||
return DataHelper.eq(getSearchKey(),msg.getSearchKey()) &&
|
||||
DataHelper.eq(getFromHash(),msg.getFromHash());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("[DatabaseFindNearestMessage: ");
|
||||
buf.append("\n\tSearch Key: ").append(getSearchKey());
|
||||
buf.append("\n\tFrom: ").append(getFromHash());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
165
router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java
Normal file
165
router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java
Normal file
@@ -0,0 +1,165 @@
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Defines the message a router sends to another router to search for a
|
||||
* key in the network database.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DatabaseLookupMessage extends I2NPMessageImpl {
|
||||
private final static Log _log = new Log(DatabaseLookupMessage.class);
|
||||
public final static int MESSAGE_TYPE = 2;
|
||||
private Hash _key;
|
||||
private RouterInfo _from;
|
||||
private TunnelId _replyTunnel;
|
||||
private Set _dontIncludePeers;
|
||||
|
||||
public DatabaseLookupMessage() {
|
||||
setSearchKey(null);
|
||||
setFrom(null);
|
||||
setDontIncludePeers(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the key being searched for
|
||||
*/
|
||||
public Hash getSearchKey() { return _key; }
|
||||
public void setSearchKey(Hash key) { _key = key; }
|
||||
|
||||
/**
|
||||
* Contains the current router info of the router who requested this lookup
|
||||
*
|
||||
*/
|
||||
public RouterInfo getFrom() { return _from; }
|
||||
public void setFrom(RouterInfo from) { _from = 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
|
||||
*/
|
||||
public Set getDontIncludePeers() { return _dontIncludePeers; }
|
||||
public void setDontIncludePeers(Set peers) {
|
||||
if (peers != null)
|
||||
_dontIncludePeers = new HashSet(peers);
|
||||
else
|
||||
_dontIncludePeers = null;
|
||||
}
|
||||
|
||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
try {
|
||||
_key = new Hash();
|
||||
_key.readBytes(in);
|
||||
_from = new RouterInfo();
|
||||
_from.readBytes(in);
|
||||
boolean tunnelSpecified = DataHelper.readBoolean(in).booleanValue();
|
||||
if (tunnelSpecified) {
|
||||
_replyTunnel = new TunnelId();
|
||||
_replyTunnel.readBytes(in);
|
||||
}
|
||||
int numPeers = (int)DataHelper.readLong(in, 2);
|
||||
if ( (numPeers < 0) || (numPeers >= (1<<16) ) )
|
||||
throw new DataFormatException("Invalid number of peers - " + numPeers);
|
||||
Set peers = new HashSet(numPeers);
|
||||
for (int i = 0; i < numPeers; i++) {
|
||||
Hash peer = new Hash();
|
||||
peer.readBytes(in);
|
||||
peers.add(peer);
|
||||
}
|
||||
_dontIncludePeers = peers;
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] writeMessage() throws I2NPMessageException, IOException {
|
||||
if (_key == null) throw new I2NPMessageException("Key being searched for not specified");
|
||||
if (_from == null) throw new I2NPMessageException("From address not specified");
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
|
||||
try {
|
||||
_key.writeBytes(os);
|
||||
_from.writeBytes(os);
|
||||
if (_replyTunnel != null) {
|
||||
DataHelper.writeBoolean(os, Boolean.TRUE);
|
||||
_replyTunnel.writeBytes(os);
|
||||
} else {
|
||||
DataHelper.writeBoolean(os, Boolean.FALSE);
|
||||
}
|
||||
if ( (_dontIncludePeers == null) || (_dontIncludePeers.size() <= 0) ) {
|
||||
DataHelper.writeLong(os, 2, 0);
|
||||
} else {
|
||||
DataHelper.writeLong(os, 2, _dontIncludePeers.size());
|
||||
for (Iterator iter = _dontIncludePeers.iterator(); iter.hasNext(); ) {
|
||||
Hash peer = (Hash)iter.next();
|
||||
peer.writeBytes(os);
|
||||
}
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getSearchKey()) +
|
||||
DataHelper.hashCode(getFrom()) +
|
||||
DataHelper.hashCode(getReplyTunnel()) +
|
||||
DataHelper.hashCode(_dontIncludePeers);
|
||||
}
|
||||
|
||||
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());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
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\tDont Include Peers: ").append(getDontIncludePeers());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Defines the message a router sends to another router in response to a
|
||||
* search (DatabaseFindNearest or DatabaseLookup) when it doesn't have the value,
|
||||
* specifying what routers it would search.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DatabaseSearchReplyMessage extends I2NPMessageImpl {
|
||||
private final static Log _log = new Log(DatabaseSearchReplyMessage.class);
|
||||
public final static int MESSAGE_TYPE = 3;
|
||||
private Hash _key;
|
||||
private List _routerInfoStructures;
|
||||
private Hash _from;
|
||||
|
||||
public DatabaseSearchReplyMessage() {
|
||||
setSearchKey(null);
|
||||
_routerInfoStructures = new ArrayList();
|
||||
setFromHash(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the key being searched for
|
||||
*/
|
||||
public Hash getSearchKey() { return _key; }
|
||||
public void setSearchKey(Hash key) { _key = key; }
|
||||
|
||||
public int getNumReplies() { return _routerInfoStructures.size(); }
|
||||
public RouterInfo getReply(int index) { return (RouterInfo)_routerInfoStructures.get(index); }
|
||||
public void addReply(RouterInfo info) { _routerInfoStructures.add(info); }
|
||||
public void addReplies(Collection replies) { _routerInfoStructures.addAll(replies); }
|
||||
|
||||
public Hash getFromHash() { return _from; }
|
||||
public void setFromHash(Hash from) { _from = from; }
|
||||
|
||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
try {
|
||||
_key = new Hash();
|
||||
_key.readBytes(in);
|
||||
|
||||
int compressedLength = (int)DataHelper.readLong(in, 2);
|
||||
byte compressedData[] = new byte[compressedLength];
|
||||
int read = DataHelper.read(in, compressedData);
|
||||
if (read != compressedLength)
|
||||
throw new IOException("Not enough data to decompress");
|
||||
byte decompressedData[] = DataHelper.decompress(compressedData);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(decompressedData);
|
||||
int num = (int)DataHelper.readLong(bais, 1);
|
||||
_routerInfoStructures.clear();
|
||||
for (int i = 0; i < num; i++) {
|
||||
RouterInfo info = new RouterInfo();
|
||||
info.readBytes(bais);
|
||||
addReply(info);
|
||||
}
|
||||
|
||||
_from = new Hash();
|
||||
_from.readBytes(in);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] writeMessage() throws I2NPMessageException, IOException {
|
||||
if (_key == null)
|
||||
throw new I2NPMessageException("Key in reply to not specified");
|
||||
if (_routerInfoStructures == null)
|
||||
throw new I2NPMessageException("RouterInfo replies are null");
|
||||
if (_routerInfoStructures.size() <= 0)
|
||||
throw new I2NPMessageException("No replies specified in SearchReply! Always include oneself!");
|
||||
if (_from == null)
|
||||
throw new I2NPMessageException("No 'from' address specified!");
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
|
||||
try {
|
||||
_key.writeBytes(os);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
|
||||
DataHelper.writeLong(baos, 1, _routerInfoStructures.size());
|
||||
for (int i = 0; i < getNumReplies(); i++) {
|
||||
RouterInfo info = getReply(i);
|
||||
info.writeBytes(baos);
|
||||
}
|
||||
|
||||
byte compressed[] = DataHelper.compress(baos.toByteArray());
|
||||
DataHelper.writeLong(os, 2, compressed.length);
|
||||
os.write(compressed);
|
||||
_from.writeBytes(os);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
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()) &&
|
||||
DataHelper.eq(_routerInfoStructures,msg._routerInfoStructures);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getSearchKey()) +
|
||||
DataHelper.hashCode(getFromHash()) +
|
||||
DataHelper.hashCode(_routerInfoStructures);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("[DatabaseSearchReplyMessage: ");
|
||||
buf.append("\n\tSearch Key: ").append(getSearchKey());
|
||||
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("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
170
router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java
Normal file
170
router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java
Normal file
@@ -0,0 +1,170 @@
|
||||
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.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Defines the message a router sends to another router to test the network
|
||||
* database reachability, as well as the reply message sent back.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
private final static Log _log = new Log(DatabaseStoreMessage.class);
|
||||
public final static int MESSAGE_TYPE = 1;
|
||||
private Hash _key;
|
||||
private int _type;
|
||||
private LeaseSet _leaseSet;
|
||||
private RouterInfo _info;
|
||||
|
||||
public final static int KEY_TYPE_ROUTERINFO = 0;
|
||||
public final static int KEY_TYPE_LEASESET = 1;
|
||||
|
||||
public DatabaseStoreMessage() {
|
||||
setValueType(-1);
|
||||
setKey(null);
|
||||
setLeaseSet(null);
|
||||
setRouterInfo(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the key in the network database being stored
|
||||
*
|
||||
*/
|
||||
public Hash getKey() { return _key; }
|
||||
public void setKey(Hash key) { _key = key; }
|
||||
|
||||
/**
|
||||
* Defines the router info value in the network database being stored
|
||||
*
|
||||
*/
|
||||
public RouterInfo getRouterInfo() { return _info; }
|
||||
public void setRouterInfo(RouterInfo routerInfo) {
|
||||
_info = routerInfo;
|
||||
if (_info != null)
|
||||
setValueType(KEY_TYPE_ROUTERINFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the lease set value in the network database being stored
|
||||
*
|
||||
*/
|
||||
public LeaseSet getLeaseSet() { return _leaseSet; }
|
||||
public void setLeaseSet(LeaseSet leaseSet) {
|
||||
_leaseSet = leaseSet;
|
||||
if (_leaseSet != null)
|
||||
setValueType(KEY_TYPE_LEASESET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines type of key being stored in the network database -
|
||||
* either KEY_TYPE_ROUTERINFO or KEY_TYPE_LEASESET
|
||||
*
|
||||
*/
|
||||
public int getValueType() { return _type; }
|
||||
public void setValueType(int type) { _type = type; }
|
||||
|
||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
try {
|
||||
_key = new Hash();
|
||||
_key.readBytes(in);
|
||||
_log.debug("Hash read: " + _key.toBase64());
|
||||
_type = (int)DataHelper.readLong(in, 1);
|
||||
if (_type == KEY_TYPE_LEASESET) {
|
||||
_leaseSet = new LeaseSet();
|
||||
_leaseSet.readBytes(in);
|
||||
} else if (_type == KEY_TYPE_ROUTERINFO) {
|
||||
_info = new RouterInfo();
|
||||
int compressedSize = (int)DataHelper.readLong(in, 2);
|
||||
byte compressed[] = new byte[compressedSize];
|
||||
int read = DataHelper.read(in, compressed);
|
||||
if (read != compressedSize)
|
||||
throw new I2NPMessageException("Invalid compressed data size");
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(DataHelper.decompress(compressed));
|
||||
_info.readBytes(bais);
|
||||
} else {
|
||||
throw new I2NPMessageException("Invalid type of key read from the structure - " + _type);
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] writeMessage() throws I2NPMessageException, IOException {
|
||||
if (_key == null) throw new I2NPMessageException("Invalid key");
|
||||
if ( (_type != KEY_TYPE_LEASESET) && (_type != KEY_TYPE_ROUTERINFO) ) throw new I2NPMessageException("Invalid key type");
|
||||
if ( (_type == KEY_TYPE_LEASESET) && (_leaseSet == null) ) throw new I2NPMessageException("Missing lease set");
|
||||
if ( (_type == KEY_TYPE_ROUTERINFO) && (_info == null) ) throw new I2NPMessageException("Missing router info");
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(256);
|
||||
try {
|
||||
_key.writeBytes(os);
|
||||
DataHelper.writeLong(os, 1, _type);
|
||||
if (_type == KEY_TYPE_LEASESET) {
|
||||
_leaseSet.writeBytes(os);
|
||||
} else if (_type == KEY_TYPE_ROUTERINFO) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
|
||||
_info.writeBytes(baos);
|
||||
byte uncompressed[] = baos.toByteArray();
|
||||
byte compressed[] = DataHelper.compress(uncompressed);
|
||||
DataHelper.writeLong(os, 2, compressed.length);
|
||||
os.write(compressed);
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getKey()) +
|
||||
DataHelper.hashCode(getLeaseSet()) +
|
||||
DataHelper.hashCode(getRouterInfo()) +
|
||||
getValueType();
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof DatabaseStoreMessage) ) {
|
||||
DatabaseStoreMessage msg = (DatabaseStoreMessage)object;
|
||||
return DataHelper.eq(getKey(),msg.getKey()) &&
|
||||
DataHelper.eq(getLeaseSet(),msg.getLeaseSet()) &&
|
||||
DataHelper.eq(getRouterInfo(),msg.getRouterInfo()) &&
|
||||
DataHelper.eq(getValueType(),msg.getValueType());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("[DatabaseStoreMessage: ");
|
||||
buf.append("\n\tExpiration: ").append(getMessageExpiration());
|
||||
buf.append("\n\tUnique ID: ").append(getUniqueId());
|
||||
buf.append("\n\tKey: ").append(getKey());
|
||||
buf.append("\n\tValue Type: ").append(getValueType());
|
||||
buf.append("\n\tRouter Info: ").append(getRouterInfo());
|
||||
buf.append("\n\tLease Set: ").append(getLeaseSet());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
274
router/java/src/net/i2p/data/i2np/DeliveryInstructions.java
Normal file
274
router/java/src/net/i2p/data/i2np/DeliveryInstructions.java
Normal file
@@ -0,0 +1,274 @@
|
||||
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.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.TunnelId;
|
||||
|
||||
|
||||
/**
|
||||
* Contains the delivery instructions
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DeliveryInstructions extends DataStructureImpl {
|
||||
private final static Log _log = new Log(DeliveryInstructions.class);
|
||||
private boolean _encrypted;
|
||||
private SessionKey _encryptionKey;
|
||||
private int _deliveryMode;
|
||||
public final static int DELIVERY_MODE_LOCAL = 0;
|
||||
public final static int DELIVERY_MODE_DESTINATION = 1;
|
||||
public final static int DELIVERY_MODE_ROUTER = 2;
|
||||
public final static int DELIVERY_MODE_TUNNEL = 3;
|
||||
private Hash _destinationHash;
|
||||
private Hash _routerHash;
|
||||
private TunnelId _tunnelId;
|
||||
private boolean _delayRequested;
|
||||
private long _delaySeconds;
|
||||
|
||||
private final static int FLAG_MODE_LOCAL = 0;
|
||||
private final static int FLAG_MODE_DESTINATION = 1;
|
||||
private final static int FLAG_MODE_ROUTER = 2;
|
||||
private final static int FLAG_MODE_TUNNEL = 3;
|
||||
|
||||
private final static long FLAG_ENCRYPTED = 128;
|
||||
private final static long FLAG_MODE = 96;
|
||||
private final static long FLAG_DELAY = 16;
|
||||
|
||||
public DeliveryInstructions() {
|
||||
setEncrypted(false);
|
||||
setEncryptionKey(null);
|
||||
setDeliveryMode(-1);
|
||||
setDestination(null);
|
||||
setRouter(null);
|
||||
setTunnelId(null);
|
||||
setDelayRequested(false);
|
||||
setDelaySeconds(0);
|
||||
}
|
||||
|
||||
public boolean getEncrypted() { return _encrypted; }
|
||||
public void setEncrypted(boolean encrypted) { _encrypted = encrypted; }
|
||||
public SessionKey getEncryptionKey() { return _encryptionKey; }
|
||||
public void setEncryptionKey(SessionKey key) { _encryptionKey = key; }
|
||||
public int getDeliveryMode() { return _deliveryMode; }
|
||||
public void setDeliveryMode(int mode) { _deliveryMode = mode; }
|
||||
public Hash getDestination() { return _destinationHash; }
|
||||
public void setDestination(Hash dest) { _destinationHash = dest; }
|
||||
public Hash getRouter() { return _routerHash; }
|
||||
public void setRouter(Hash router) { _routerHash = router; }
|
||||
public TunnelId getTunnelId() { return _tunnelId; }
|
||||
public void setTunnelId(TunnelId id) { _tunnelId = id; }
|
||||
public boolean getDelayRequested() { return _delayRequested; }
|
||||
public void setDelayRequested(boolean req) { _delayRequested = req; }
|
||||
public long getDelaySeconds() { return _delaySeconds; }
|
||||
public void setDelaySeconds(long seconds) { _delaySeconds = seconds; }
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
long flags = DataHelper.readLong(in, 1);
|
||||
_log.debug("Read flags: " + flags + " mode: " + flagMode(flags));
|
||||
|
||||
if (flagEncrypted(flags)) {
|
||||
SessionKey k = new SessionKey();
|
||||
k.readBytes(in);
|
||||
setEncryptionKey(k);
|
||||
setEncrypted(true);
|
||||
} else {
|
||||
setEncrypted(false);
|
||||
}
|
||||
|
||||
setDeliveryMode(flagMode(flags));
|
||||
switch (flagMode(flags)) {
|
||||
case FLAG_MODE_LOCAL:
|
||||
break;
|
||||
case FLAG_MODE_DESTINATION:
|
||||
Hash destHash = new Hash();
|
||||
destHash.readBytes(in);
|
||||
setDestination(destHash);
|
||||
break;
|
||||
case FLAG_MODE_ROUTER:
|
||||
Hash routerHash = new Hash();
|
||||
routerHash.readBytes(in);
|
||||
setRouter(routerHash);
|
||||
break;
|
||||
case FLAG_MODE_TUNNEL:
|
||||
Hash tunnelRouterHash = new Hash();
|
||||
tunnelRouterHash.readBytes(in);
|
||||
setRouter(tunnelRouterHash);
|
||||
TunnelId id = new TunnelId();
|
||||
id.readBytes(in);
|
||||
setTunnelId(id);
|
||||
break;
|
||||
}
|
||||
|
||||
if (flagDelay(flags)) {
|
||||
long delay = DataHelper.readLong(in, 4);
|
||||
setDelayRequested(true);
|
||||
setDelaySeconds(delay);
|
||||
} else {
|
||||
setDelayRequested(false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean flagEncrypted(long flags) {
|
||||
return (0 != (flags & FLAG_ENCRYPTED));
|
||||
}
|
||||
|
||||
private int flagMode(long flags) {
|
||||
long v = flags & FLAG_MODE;
|
||||
v >>>= 5;
|
||||
return (int)v;
|
||||
}
|
||||
|
||||
private boolean flagDelay(long flags) {
|
||||
return (0 != (flags & FLAG_DELAY));
|
||||
}
|
||||
|
||||
private long getFlags() {
|
||||
long val = 0L;
|
||||
if (getEncrypted())
|
||||
val = val | FLAG_ENCRYPTED;
|
||||
long fmode = 0;
|
||||
switch (getDeliveryMode()) {
|
||||
case FLAG_MODE_LOCAL:
|
||||
break;
|
||||
case FLAG_MODE_DESTINATION:
|
||||
fmode = FLAG_MODE_DESTINATION << 5;
|
||||
break;
|
||||
case FLAG_MODE_ROUTER:
|
||||
fmode = FLAG_MODE_ROUTER << 5;
|
||||
break;
|
||||
case FLAG_MODE_TUNNEL:
|
||||
fmode = FLAG_MODE_TUNNEL << 5;
|
||||
break;
|
||||
}
|
||||
val = val | fmode;
|
||||
if (getDelayRequested())
|
||||
val = val | FLAG_DELAY;
|
||||
_log.debug("getFlags() = " + val);
|
||||
return val;
|
||||
}
|
||||
|
||||
private byte[] getAdditionalInfo() throws DataFormatException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(64);
|
||||
try {
|
||||
if (getEncrypted()) {
|
||||
if (_encryptionKey == null) throw new DataFormatException("Encryption key is not set");
|
||||
_encryptionKey.writeBytes(baos);
|
||||
_log.debug("IsEncrypted");
|
||||
} else {
|
||||
_log.debug("Is NOT Encrypted");
|
||||
}
|
||||
switch (getDeliveryMode()) {
|
||||
case FLAG_MODE_LOCAL:
|
||||
_log.debug("mode = local");
|
||||
break;
|
||||
case FLAG_MODE_DESTINATION:
|
||||
if (_destinationHash == null) throw new DataFormatException("Destination hash is not set");
|
||||
_destinationHash.writeBytes(baos);
|
||||
_log.debug("mode = destination, hash = " + _destinationHash);
|
||||
break;
|
||||
case FLAG_MODE_ROUTER:
|
||||
if (_routerHash == null) throw new DataFormatException("Router hash is not set");
|
||||
_routerHash.writeBytes(baos);
|
||||
_log.debug("mode = router, routerHash = " + _routerHash);
|
||||
break;
|
||||
case FLAG_MODE_TUNNEL:
|
||||
if ( (_routerHash == null) || (_tunnelId == null) ) throw new DataFormatException("Router hash or tunnel ID is not set");
|
||||
_routerHash.writeBytes(baos);
|
||||
_tunnelId.writeBytes(baos);
|
||||
_log.debug("mode = tunnel, tunnelId = " + _tunnelId.getTunnelId() + ", routerHash = " + _routerHash);
|
||||
break;
|
||||
}
|
||||
if (getDelayRequested()) {
|
||||
_log.debug("delay requested: " + getDelaySeconds());
|
||||
DataHelper.writeLong(baos, 4, getDelaySeconds());
|
||||
} else {
|
||||
_log.debug("delay NOT requested");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new DataFormatException("Unable to write out additional info", ioe);
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
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();
|
||||
_log.debug("Write flags: " + flags + " mode: " + getDeliveryMode() + " =?= " + flagMode(flags));
|
||||
byte additionalInfo[] = getAdditionalInfo();
|
||||
DataHelper.writeLong(out, 1, flags);
|
||||
if (additionalInfo != null) {
|
||||
out.write(additionalInfo);
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ( (obj == null) || !(obj instanceof DeliveryInstructions))
|
||||
return false;
|
||||
DeliveryInstructions instr = (DeliveryInstructions)obj;
|
||||
return (getDelayRequested() == instr.getDelayRequested()) &&
|
||||
(getDelaySeconds() == instr.getDelaySeconds()) &&
|
||||
(getDeliveryMode() == instr.getDeliveryMode()) &&
|
||||
(getEncrypted() == instr.getEncrypted()) &&
|
||||
DataHelper.eq(getDestination(), instr.getDestination()) &&
|
||||
DataHelper.eq(getEncryptionKey(), instr.getEncryptionKey()) &&
|
||||
DataHelper.eq(getRouter(), instr.getRouter()) &&
|
||||
DataHelper.eq(getTunnelId(), instr.getTunnelId());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return (int)getDelaySeconds() +
|
||||
getDeliveryMode() +
|
||||
DataHelper.hashCode(getDestination()) +
|
||||
DataHelper.hashCode(getEncryptionKey()) +
|
||||
DataHelper.hashCode(getRouter()) +
|
||||
DataHelper.hashCode(getTunnelId());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
buf.append("[DeliveryInstructions: ");
|
||||
buf.append("\n\tDelivery mode: ");
|
||||
switch (getDeliveryMode()) {
|
||||
case DELIVERY_MODE_LOCAL:
|
||||
buf.append("local");
|
||||
break;
|
||||
case DELIVERY_MODE_DESTINATION:
|
||||
buf.append("destination");
|
||||
break;
|
||||
case DELIVERY_MODE_ROUTER:
|
||||
buf.append("router");
|
||||
break;
|
||||
case DELIVERY_MODE_TUNNEL:
|
||||
buf.append("tunnel");
|
||||
break;
|
||||
}
|
||||
buf.append("\n\tDelay requested: ").append(getDelayRequested());
|
||||
buf.append("\n\tDelay seconds: ").append(getDelaySeconds());
|
||||
buf.append("\n\tDestination: ").append(getDestination());
|
||||
buf.append("\n\tEncrypted: ").append(getEncrypted());
|
||||
buf.append("\n\tEncryption key: ").append(getEncryptionKey());
|
||||
buf.append("\n\tRouter: ").append(getRouter());
|
||||
buf.append("\n\tTunnelId: ").append(getTunnelId());
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
91
router/java/src/net/i2p/data/i2np/DeliveryStatusMessage.java
Normal file
91
router/java/src/net/i2p/data/i2np/DeliveryStatusMessage.java
Normal file
@@ -0,0 +1,91 @@
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Defines the message sent back in reply to a message when requested, containing
|
||||
* the private ack id.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DeliveryStatusMessage extends I2NPMessageImpl {
|
||||
private final static Log _log = new Log(DeliveryStatusMessage.class);
|
||||
public final static int MESSAGE_TYPE = 10;
|
||||
private long _id;
|
||||
private Date _arrival;
|
||||
|
||||
public DeliveryStatusMessage() {
|
||||
setMessageId(-1);
|
||||
setArrival(null);
|
||||
}
|
||||
|
||||
public long getMessageId() { return _id; }
|
||||
public void setMessageId(long id) { _id = id; }
|
||||
|
||||
public Date getArrival() { return _arrival; }
|
||||
public void setArrival(Date arrival) { _arrival = arrival; }
|
||||
|
||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
try {
|
||||
_id = DataHelper.readLong(in, 4);
|
||||
_arrival = DataHelper.readDate(in);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] writeMessage() throws I2NPMessageException, IOException {
|
||||
if ( (_id < 0) || (_arrival == null) ) throw new I2NPMessageException("Not enough data to write out");
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
|
||||
try {
|
||||
DataHelper.writeLong(os, 4, _id);
|
||||
DataHelper.writeDate(os, _arrival);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
public int hashCode() {
|
||||
return (int)getMessageId() +
|
||||
DataHelper.hashCode(getArrival());
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof DeliveryStatusMessage) ) {
|
||||
DeliveryStatusMessage msg = (DeliveryStatusMessage)object;
|
||||
return DataHelper.eq(getMessageId(),msg.getMessageId()) &&
|
||||
DataHelper.eq(getArrival(),msg.getArrival());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("[DeliveryStatusMessage: ");
|
||||
buf.append("\n\tMessage ID: ").append(getMessageId());
|
||||
buf.append("\n\tArrival: ").append(getArrival());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
62
router/java/src/net/i2p/data/i2np/EndPointPrivateKey.java
Normal file
62
router/java/src/net/i2p/data/i2np/EndPointPrivateKey.java
Normal file
@@ -0,0 +1,62 @@
|
||||
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.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.PrivateKey;
|
||||
|
||||
/**
|
||||
* Contains the private key which matches the EndPointPublicKey which, in turn,
|
||||
* is published on the LeaseSet and used to encrypt messages to the router to
|
||||
* which a Destination is currently connected.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class EndPointPrivateKey extends DataStructureImpl {
|
||||
private final static Log _log = new Log(EndPointPrivateKey.class);
|
||||
private PrivateKey _key;
|
||||
|
||||
public EndPointPrivateKey() { setKey(null); }
|
||||
|
||||
public PrivateKey getKey() { return _key; }
|
||||
public void setKey(PrivateKey key) { _key= key; }
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_key = new PrivateKey();
|
||||
_key.readBytes(in);
|
||||
}
|
||||
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
if (_key == null) throw new DataFormatException("Invalid key");
|
||||
_key.writeBytes(out);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ( (obj == null) || !(obj instanceof EndPointPublicKey))
|
||||
return false;
|
||||
return DataHelper.eq(getKey(), ((EndPointPublicKey)obj).getKey());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
if (_key == null) return 0;
|
||||
return getKey().hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[EndPointPrivateKey: " + getKey() + "]";
|
||||
}
|
||||
}
|
||||
62
router/java/src/net/i2p/data/i2np/EndPointPublicKey.java
Normal file
62
router/java/src/net/i2p/data/i2np/EndPointPublicKey.java
Normal file
@@ -0,0 +1,62 @@
|
||||
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.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.PublicKey;
|
||||
|
||||
/**
|
||||
* Contains the public key which matches the EndPointPrivateKey. This is
|
||||
* published on the LeaseSet and used to encrypt messages to the router to
|
||||
* which a Destination is currently connected.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class EndPointPublicKey extends DataStructureImpl {
|
||||
private final static Log _log = new Log(EndPointPublicKey.class);
|
||||
private PublicKey _key;
|
||||
|
||||
public EndPointPublicKey() { setKey(null); }
|
||||
|
||||
public PublicKey getKey() { return _key; }
|
||||
public void setKey(PublicKey key) { _key= key; }
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_key = new PublicKey();
|
||||
_key.readBytes(in);
|
||||
}
|
||||
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
if (_key == null) throw new DataFormatException("Invalid key");
|
||||
_key.writeBytes(out);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ( (obj == null) || !(obj instanceof EndPointPublicKey))
|
||||
return false;
|
||||
return DataHelper.eq(getKey(), ((EndPointPublicKey)obj).getKey());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
if (_key == null) return 0;
|
||||
return getKey().hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[EndPointPublicKey: " + getKey() + "]";
|
||||
}
|
||||
}
|
||||
171
router/java/src/net/i2p/data/i2np/GarlicClove.java
Normal file
171
router/java/src/net/i2p/data/i2np/GarlicClove.java
Normal file
@@ -0,0 +1,171 @@
|
||||
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.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Certificate;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Contains one deliverable message encrypted to a router along with instructions
|
||||
* and a certificate 'paying for' the delivery.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class GarlicClove extends DataStructureImpl {
|
||||
private final static Log _log = new Log(GarlicClove.class);
|
||||
private DeliveryInstructions _instructions;
|
||||
private I2NPMessage _msg;
|
||||
private long _cloveId;
|
||||
private Date _expiration;
|
||||
private Certificate _certificate;
|
||||
private int _replyAction;
|
||||
private SourceRouteBlock _sourceRouteBlock;
|
||||
|
||||
/** No action requested with the source route block */
|
||||
public final static int ACTION_NONE = 0;
|
||||
/**
|
||||
* A DeliveryStatusMessage is requested with the source route block using
|
||||
* the cloveId as the id received
|
||||
*
|
||||
*/
|
||||
public final static int ACTION_STATUS = 1;
|
||||
/**
|
||||
* No DeliveryStatusMessage is requested, but the source route block is
|
||||
* included for message specific replies
|
||||
*
|
||||
*/
|
||||
public final static int ACTION_MESSAGE_SPECIFIC = 2;
|
||||
|
||||
public GarlicClove() {
|
||||
setInstructions(null);
|
||||
setData(null);
|
||||
setCloveId(-1);
|
||||
setExpiration(null);
|
||||
setCertificate(null);
|
||||
setSourceRouteBlockAction(ACTION_NONE);
|
||||
setSourceRouteBlock(null);
|
||||
}
|
||||
|
||||
public DeliveryInstructions getInstructions() { return _instructions; }
|
||||
public void setInstructions(DeliveryInstructions instr) { _instructions = instr; }
|
||||
public I2NPMessage getData() { return _msg; }
|
||||
public void setData(I2NPMessage msg) { _msg = msg; }
|
||||
public long getCloveId() { return _cloveId; }
|
||||
public void setCloveId(long id) { _cloveId = id; }
|
||||
public Date getExpiration() { return _expiration; }
|
||||
public void setExpiration(Date exp) { _expiration = exp; }
|
||||
public Certificate getCertificate() { return _certificate; }
|
||||
public void setCertificate(Certificate cert) { _certificate = cert; }
|
||||
public int getSourceRouteBlockAction() { return _replyAction; }
|
||||
public void setSourceRouteBlockAction(int action) { _replyAction = action; }
|
||||
public SourceRouteBlock getSourceRouteBlock() { return _sourceRouteBlock; }
|
||||
public void setSourceRouteBlock(SourceRouteBlock block) { _sourceRouteBlock = block; }
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_instructions = new DeliveryInstructions();
|
||||
_instructions.readBytes(in);
|
||||
_log.debug("Read instructions: " + _instructions);
|
||||
try {
|
||||
_msg = new I2NPMessageHandler().readMessage(in);
|
||||
} catch (I2NPMessageException ime) {
|
||||
throw new DataFormatException("Unable to read the message from a garlic clove", ime);
|
||||
}
|
||||
_cloveId = DataHelper.readLong(in, 4);
|
||||
_expiration = DataHelper.readDate(in);
|
||||
_log.debug("CloveID read: " + _cloveId + " expiration read: " + _expiration);
|
||||
_certificate = new Certificate();
|
||||
_certificate.readBytes(in);
|
||||
_log.debug("Read cert: " + _certificate);
|
||||
int replyStyle = (int)DataHelper.readLong(in, 1);
|
||||
setSourceRouteBlockAction(replyStyle);
|
||||
if (replyStyle != ACTION_NONE) {
|
||||
_sourceRouteBlock = new SourceRouteBlock();
|
||||
_sourceRouteBlock.readBytes(in);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
StringBuffer error = new StringBuffer();
|
||||
if (_instructions == null)
|
||||
error.append("No instructions ");
|
||||
if (_msg == null)
|
||||
error.append("No message ");
|
||||
if (_cloveId < 0)
|
||||
error.append("CloveID < 0 [").append(_cloveId).append("] ");
|
||||
if (_expiration == null)
|
||||
error.append("Expiration is null ");
|
||||
if (_certificate == null)
|
||||
error.append("Certificate is null ");
|
||||
if (_replyAction < 0)
|
||||
error.append("Reply action is < 0 [").append(_replyAction).append("] ");;
|
||||
if (error.length() > 0)
|
||||
throw new DataFormatException(error.toString());
|
||||
if ( (_replyAction != 0) && (_sourceRouteBlock == null) )
|
||||
throw new DataFormatException("Source route block must be specified for non-null action");
|
||||
_instructions.writeBytes(out);
|
||||
|
||||
_log.debug("Wrote instructions: " + _instructions);
|
||||
_msg.writeBytes(out);
|
||||
DataHelper.writeLong(out, 4, _cloveId);
|
||||
DataHelper.writeDate(out, _expiration);
|
||||
_log.debug("CloveID written: " + _cloveId + " expiration written: " + _expiration);
|
||||
_certificate.writeBytes(out);
|
||||
_log.debug("Written cert: " + _certificate);
|
||||
DataHelper.writeLong(out, 1, _replyAction);
|
||||
if ( (_replyAction != 0) && (_sourceRouteBlock != null) )
|
||||
_sourceRouteBlock.writeBytes(out);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ( (obj == null) || !(obj instanceof GarlicClove))
|
||||
return false;
|
||||
GarlicClove clove = (GarlicClove)obj;
|
||||
return DataHelper.eq(getCertificate(), clove.getCertificate()) &&
|
||||
DataHelper.eq(getCloveId(), clove.getCloveId()) &&
|
||||
DataHelper.eq(getData(), clove.getData()) &&
|
||||
DataHelper.eq(getExpiration(), clove.getExpiration()) &&
|
||||
DataHelper.eq(getInstructions(), clove.getInstructions()) &&
|
||||
DataHelper.eq(getSourceRouteBlock(), clove.getSourceRouteBlock()) &&
|
||||
(getSourceRouteBlockAction() == clove.getSourceRouteBlockAction());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getCertificate()) +
|
||||
(int)getCloveId() +
|
||||
DataHelper.hashCode(getData()) +
|
||||
DataHelper.hashCode(getExpiration()) +
|
||||
DataHelper.hashCode(getInstructions()) +
|
||||
DataHelper.hashCode(getSourceRouteBlock()) +
|
||||
getSourceRouteBlockAction();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
buf.append("[GarlicClove: ");
|
||||
buf.append("\n\tInstructions: ").append(getInstructions());
|
||||
buf.append("\n\tCertificate: ").append(getCertificate());
|
||||
buf.append("\n\tClove ID: ").append(getCloveId());
|
||||
buf.append("\n\tExpiration: ").append(getExpiration());
|
||||
buf.append("\n\tSource route style: ").append(getSourceRouteBlockAction());
|
||||
buf.append("\n\tSource route block: ").append(getSourceRouteBlock());
|
||||
buf.append("\n\tData: ").append(getData());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
84
router/java/src/net/i2p/data/i2np/GarlicMessage.java
Normal file
84
router/java/src/net/i2p/data/i2np/GarlicMessage.java
Normal file
@@ -0,0 +1,84 @@
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Defines the wrapped garlic message
|
||||
*
|
||||
* @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;
|
||||
|
||||
public GarlicMessage() {
|
||||
setData(null);
|
||||
}
|
||||
|
||||
public byte[] getData() { return _data; }
|
||||
public void setData(byte[] data) { _data = data; }
|
||||
|
||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
try {
|
||||
long len = DataHelper.readLong(in, 4);
|
||||
_data = new byte[(int)len];
|
||||
int read = read(in, _data);
|
||||
if (read != len)
|
||||
throw new I2NPMessageException("Incorrect size read");
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] writeMessage() throws I2NPMessageException, IOException {
|
||||
if ( (_data == null) || (_data.length <= 0) ) throw new I2NPMessageException("Not enough data to write out");
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
|
||||
try {
|
||||
DataHelper.writeLong(os, 4, _data.length);
|
||||
os.write(_data);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getData());
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof GarlicMessage) ) {
|
||||
GarlicMessage msg = (GarlicMessage)object;
|
||||
return DataHelper.eq(getData(),msg.getData());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("[GarlicMessage: ");
|
||||
buf.append("\n\tData length: ").append(getData().length).append(" bytes");
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
51
router/java/src/net/i2p/data/i2np/I2NPMessage.java
Normal file
51
router/java/src/net/i2p/data/i2np/I2NPMessage.java
Normal file
@@ -0,0 +1,51 @@
|
||||
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.util.Date;
|
||||
|
||||
import net.i2p.data.DataStructure;
|
||||
|
||||
/**
|
||||
* Base interface for all I2NP messages
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @param in stream to read from
|
||||
* @param type I2NP message type
|
||||
* @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
|
||||
*/
|
||||
public void readBytes(InputStream in, int type) throws I2NPMessageException, IOException;
|
||||
|
||||
/**
|
||||
* Return the unique identifier for this type of I2NP message, as defined in
|
||||
* the I2NP spec
|
||||
*/
|
||||
public int getType();
|
||||
|
||||
/**
|
||||
* Replay resistent message Id
|
||||
*/
|
||||
public long getUniqueId();
|
||||
|
||||
/**
|
||||
* Date after which the message should be dropped (and the associated uniqueId forgotten)
|
||||
*
|
||||
*/
|
||||
public Date getMessageExpiration();
|
||||
}
|
||||
28
router/java/src/net/i2p/data/i2np/I2NPMessageException.java
Normal file
28
router/java/src/net/i2p/data/i2np/I2NPMessageException.java
Normal file
@@ -0,0 +1,28 @@
|
||||
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 net.i2p.I2PException;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Represent an error serializing or deserializing an APIMessage
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class I2NPMessageException extends I2PException {
|
||||
private final static Log _log = new Log(I2NPMessageException.class);
|
||||
|
||||
public I2NPMessageException(String message, Throwable parent) {
|
||||
super(message, parent);
|
||||
}
|
||||
public I2NPMessageException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
92
router/java/src/net/i2p/data/i2np/I2NPMessageHandler.java
Normal file
92
router/java/src/net/i2p/data/i2np/I2NPMessageHandler.java
Normal file
@@ -0,0 +1,92 @@
|
||||
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.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.FileInputStream;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataFormatException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.Clock;
|
||||
|
||||
/**
|
||||
* Handle messages from router to router
|
||||
*
|
||||
*/
|
||||
public class I2NPMessageHandler {
|
||||
private final static Log _log = new Log(I2NPMessageHandler.class);
|
||||
private long _lastReadBegin;
|
||||
private long _lastReadEnd;
|
||||
public 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
|
||||
* @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(InputStream in) throws IOException, I2NPMessageException {
|
||||
try {
|
||||
int type = (int)DataHelper.readLong(in, 1);
|
||||
_lastReadBegin = Clock.getInstance().now();
|
||||
I2NPMessage msg = createMessage(in, type);
|
||||
msg.readBytes(in, type);
|
||||
_lastReadEnd = Clock.getInstance().now();
|
||||
return msg;
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error reading the message", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
public long getLastReadTime() { return _lastReadEnd - _lastReadBegin; }
|
||||
|
||||
/**
|
||||
* Yes, this is fairly ugly, but its the only place it ever happens.
|
||||
*
|
||||
*/
|
||||
private static I2NPMessage createMessage(InputStream in, int type) throws IOException, I2NPMessageException {
|
||||
switch (type) {
|
||||
case DatabaseStoreMessage.MESSAGE_TYPE:
|
||||
return new DatabaseStoreMessage();
|
||||
case DatabaseLookupMessage.MESSAGE_TYPE:
|
||||
return new DatabaseLookupMessage();
|
||||
case DatabaseSearchReplyMessage.MESSAGE_TYPE:
|
||||
return new DatabaseSearchReplyMessage();
|
||||
case DeliveryStatusMessage.MESSAGE_TYPE:
|
||||
return new DeliveryStatusMessage();
|
||||
case GarlicMessage.MESSAGE_TYPE:
|
||||
return new GarlicMessage();
|
||||
case TunnelMessage.MESSAGE_TYPE:
|
||||
return new TunnelMessage();
|
||||
case DataMessage.MESSAGE_TYPE:
|
||||
return new DataMessage();
|
||||
case SourceRouteReplyMessage.MESSAGE_TYPE:
|
||||
return new SourceRouteReplyMessage();
|
||||
case TunnelCreateMessage.MESSAGE_TYPE:
|
||||
return new TunnelCreateMessage();
|
||||
case TunnelCreateStatusMessage.MESSAGE_TYPE:
|
||||
return new TunnelCreateStatusMessage();
|
||||
default:
|
||||
throw new I2NPMessageException("The type "+ type + " is an unknown I2NP message");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
try {
|
||||
I2NPMessage msg = new I2NPMessageHandler().readMessage(new FileInputStream(args[0]));
|
||||
System.out.println(msg);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
104
router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java
Normal file
104
router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java
Normal file
@@ -0,0 +1,104 @@
|
||||
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 java.util.Date;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.RandomSource;
|
||||
|
||||
/**
|
||||
* Defines the base message implementation.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPMessage {
|
||||
private final static Log _log = new Log(I2NPMessageImpl.class);
|
||||
private Date _expiration;
|
||||
private long _uniqueId;
|
||||
|
||||
public final static long DEFAULT_EXPIRATION_MS = 1*60*1000; // 1 minute by default
|
||||
|
||||
public I2NPMessageImpl() {
|
||||
_expiration = new Date(Clock.getInstance().now() + DEFAULT_EXPIRATION_MS);
|
||||
_uniqueId = RandomSource.getInstance().nextInt(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out the payload part of the message (not including the initial
|
||||
* 1 byte type)
|
||||
*
|
||||
*/
|
||||
protected abstract byte[] writeMessage() throws I2NPMessageException, IOException;
|
||||
|
||||
/**
|
||||
* Read the body into the data structures, after the initial type byte and
|
||||
* the uniqueId / expiration, using the current class's format as defined by
|
||||
* the I2NP specification
|
||||
*
|
||||
* @param in stream to read from
|
||||
* @param type I2NP message type
|
||||
* @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
|
||||
*/
|
||||
protected abstract void readMessage(InputStream in, int type) throws I2NPMessageException, IOException;
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
try {
|
||||
readBytes(in, -1);
|
||||
} catch (I2NPMessageException ime) {
|
||||
throw new DataFormatException("Bad bytes", ime);
|
||||
}
|
||||
}
|
||||
public void readBytes(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
try {
|
||||
if (type < 0)
|
||||
type = (int)DataHelper.readLong(in, 1);
|
||||
_uniqueId = DataHelper.readLong(in, 4);
|
||||
_expiration = DataHelper.readDate(in);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error reading the message header", dfe);
|
||||
}
|
||||
_log.debug("Reading bytes: type = " + type + " / uniqueId : " + _uniqueId + " / expiration : " + _expiration);
|
||||
readMessage(in, type);
|
||||
}
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
try {
|
||||
DataHelper.writeLong(out, 1, getType());
|
||||
DataHelper.writeLong(out, 4, _uniqueId);
|
||||
DataHelper.writeDate(out, _expiration);
|
||||
_log.debug("Writing bytes: type = " + getType() + " / uniqueId : " + _uniqueId + " / expiration : " + _expiration);
|
||||
byte[] data = writeMessage();
|
||||
out.write(data);
|
||||
} catch (I2NPMessageException ime) {
|
||||
throw new DataFormatException("Error writing out the I2NP message data", ime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replay resistent message Id
|
||||
*/
|
||||
public long getUniqueId() { return _uniqueId; }
|
||||
public void setUniqueId(long id) { _uniqueId = id; }
|
||||
|
||||
/**
|
||||
* Date after which the message should be dropped (and the associated uniqueId forgotten)
|
||||
*
|
||||
*/
|
||||
public Date getMessageExpiration() { return _expiration; }
|
||||
public void setMessageExpiration(Date exp) { _expiration = exp; }
|
||||
}
|
||||
139
router/java/src/net/i2p/data/i2np/I2NPMessageReader.java
Normal file
139
router/java/src/net/i2p/data/i2np/I2NPMessageReader.java
Normal file
@@ -0,0 +1,139 @@
|
||||
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 net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* The I2NPMessageReader reads an InputStream (using
|
||||
* {@link I2NPMessageHandler I2NPMessageHandler}) and passes out events to a registered
|
||||
* listener, where events are either messages being received, exceptions being
|
||||
* thrown, or the connection being closed. Routers should use this rather
|
||||
* than read from the stream themselves.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class I2NPMessageReader {
|
||||
private final static Log _log = new Log(I2NPMessageReader.class);
|
||||
private InputStream _stream;
|
||||
private I2NPMessageEventListener _listener;
|
||||
private I2NPMessageReaderRunner _reader;
|
||||
private Thread _readerThread;
|
||||
|
||||
public I2NPMessageReader(InputStream stream, I2NPMessageEventListener lsnr) {
|
||||
this(stream, lsnr, "I2NP Reader");
|
||||
}
|
||||
|
||||
public I2NPMessageReader(InputStream stream, I2NPMessageEventListener lsnr, String name) {
|
||||
_stream = stream;
|
||||
setListener(lsnr);
|
||||
_reader = new I2NPMessageReaderRunner();
|
||||
_readerThread = new I2PThread(_reader);
|
||||
_readerThread.setName(name);
|
||||
_readerThread.setDaemon(true);
|
||||
}
|
||||
|
||||
public void setListener(I2NPMessageEventListener lsnr) { _listener = lsnr; }
|
||||
public I2NPMessageEventListener getListener() { return _listener; }
|
||||
|
||||
/**
|
||||
* Instruct the reader to begin reading messages off the stream
|
||||
*
|
||||
*/
|
||||
public void startReading() { _readerThread.start(); }
|
||||
/**
|
||||
* Have the already started reader pause its reading indefinitely
|
||||
*
|
||||
*/
|
||||
public void pauseReading() { _reader.pauseRunner(); }
|
||||
/**
|
||||
* Resume reading after a pause
|
||||
*
|
||||
*/
|
||||
public void resumeReading() { _reader.resumeRunner(); }
|
||||
/**
|
||||
* Cancel reading.
|
||||
*
|
||||
*/
|
||||
public void stopReading() { _reader.cancelRunner(); }
|
||||
|
||||
/**
|
||||
* Defines the different events the reader produces while reading the stream
|
||||
*
|
||||
*/
|
||||
public static interface I2NPMessageEventListener {
|
||||
/**
|
||||
* Notify the listener that a message has been received from the given
|
||||
* reader
|
||||
*
|
||||
*/
|
||||
public void messageReceived(I2NPMessageReader reader, I2NPMessage message, long msToRead);
|
||||
/**
|
||||
* Notify the listener that an exception was thrown while reading from the given
|
||||
* reader
|
||||
*
|
||||
*/
|
||||
public void readError(I2NPMessageReader reader, Exception error);
|
||||
/**
|
||||
* Notify the listener that the stream the given reader was running off
|
||||
* closed
|
||||
*
|
||||
*/
|
||||
public void disconnected(I2NPMessageReader reader);
|
||||
}
|
||||
|
||||
private class I2NPMessageReaderRunner implements Runnable {
|
||||
private boolean _doRun;
|
||||
private boolean _stayAlive;
|
||||
private I2NPMessageHandler _handler;
|
||||
public I2NPMessageReaderRunner() {
|
||||
_doRun = true;
|
||||
_stayAlive = true;
|
||||
_handler = new I2NPMessageHandler();
|
||||
}
|
||||
public void pauseRunner() { _doRun = false; }
|
||||
public void resumeRunner() { _doRun = true; }
|
||||
public void cancelRunner() {
|
||||
_doRun = false;
|
||||
_stayAlive = false;
|
||||
}
|
||||
public void run() {
|
||||
while (_stayAlive) {
|
||||
while (_doRun) {
|
||||
// do read
|
||||
try {
|
||||
I2NPMessage msg = _handler.readMessage(_stream);
|
||||
if (msg != null) {
|
||||
long msToRead = _handler.getLastReadTime();
|
||||
_listener.messageReceived(I2NPMessageReader.this, msg, msToRead);
|
||||
}
|
||||
} catch (I2NPMessageException ime) {
|
||||
//_log.warn("Error handling message", ime);
|
||||
_listener.readError(I2NPMessageReader.this, ime);
|
||||
_listener.disconnected(I2NPMessageReader.this);
|
||||
cancelRunner();
|
||||
} catch (IOException ioe) {
|
||||
_log.warn("IO Error handling message", ioe);
|
||||
_listener.disconnected(I2NPMessageReader.this);
|
||||
cancelRunner();
|
||||
}
|
||||
}
|
||||
if (!_doRun) {
|
||||
// pause .5 secs when we're paused
|
||||
try { Thread.sleep(500); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
// boom bye bye bad bwoy
|
||||
}
|
||||
}
|
||||
}
|
||||
225
router/java/src/net/i2p/data/i2np/SourceRouteBlock.java
Normal file
225
router/java/src/net/i2p/data/i2np/SourceRouteBlock.java
Normal file
@@ -0,0 +1,225 @@
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Date;
|
||||
|
||||
import net.i2p.crypto.ElGamalAESEngine;
|
||||
import net.i2p.crypto.KeyGenerator;
|
||||
import net.i2p.crypto.SessionKeyManager;
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.SessionTag;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
|
||||
/**
|
||||
* Defines a single hop of a source routed message, as usable for building a
|
||||
* SourceRouteReplyMessage
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class SourceRouteBlock extends DataStructureImpl {
|
||||
private final static Log _log = new Log(SourceRouteBlock.class);
|
||||
private Hash _router;
|
||||
private byte[] _data;
|
||||
private SessionKey _key;
|
||||
private byte[] _tag;
|
||||
private DeliveryInstructions _decryptedInstructions;
|
||||
private long _decryptedMessageId;
|
||||
private Certificate _decryptedCertificate;
|
||||
private long _decryptedExpiration;
|
||||
|
||||
public SourceRouteBlock() {
|
||||
setRouter(null);
|
||||
setData(null);
|
||||
setKey(null);
|
||||
setTag((byte[])null);
|
||||
_decryptedInstructions = null;
|
||||
_decryptedMessageId = -1;
|
||||
_decryptedCertificate = null;
|
||||
_decryptedExpiration = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the router through which replies using this source route block must
|
||||
* be sent (as the getData() is encrypted for their eyes only)
|
||||
*
|
||||
*/
|
||||
public Hash getRouter() { return _router; }
|
||||
public void setRouter(Hash router) { _router= router; }
|
||||
/**
|
||||
* Get the encrypted header. After decryption (via ElGamal+AES as defined
|
||||
* in the data structures spec), this array contains:
|
||||
* DeliveryInstructions
|
||||
* 4 byte Integer for a message ID
|
||||
* Certificate
|
||||
* Date of expiration for replies
|
||||
*
|
||||
*/
|
||||
public byte[] getData() { return _data; }
|
||||
private void setData(byte data[]) { _data = data; }
|
||||
/**
|
||||
* Retrieve the session key which may be used in conjunction with the tag
|
||||
* to encrypt a garlic message and send it as a reply to this message.
|
||||
* The encryption would follow scenario 2 of the ElGamal+AES encryption method
|
||||
* defined in the data structures spec.
|
||||
*
|
||||
*/
|
||||
public SessionKey getKey() { return _key; }
|
||||
public void setKey(SessionKey key) { _key = key; }
|
||||
/**
|
||||
* Get the tag made available for use in conjunction with the getKey() to
|
||||
* ElGamal+AES encrypt a garlic message without knowing the public key to
|
||||
* which the message is destined
|
||||
*
|
||||
*/
|
||||
public byte[] getTag() { return _tag; }
|
||||
public void setTag(SessionTag tag) { setTag(tag.getData()); }
|
||||
public void setTag(byte tag[]) {
|
||||
if ( (tag != null) && (tag.length != SessionTag.BYTE_LENGTH) )
|
||||
throw new IllegalArgumentException("Tag must be either null or 32 bytes");
|
||||
_tag = tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* After decryptData, this contains the delivery instructions for this block
|
||||
*/
|
||||
public DeliveryInstructions getDecryptedInstructions() { return _decryptedInstructions; }
|
||||
/**
|
||||
* After decryptData, this contains the message ID to be used with this block
|
||||
*/
|
||||
public long getDecryptedMessageId() { return _decryptedMessageId; }
|
||||
/**
|
||||
* After decryptData, this contains the Certificate 'paying' for the forwarding according to
|
||||
* this block
|
||||
*/
|
||||
public Certificate getDecryptedCertificate() { return _decryptedCertificate; }
|
||||
/**
|
||||
* After decryptData, this contains the date after which this block should not be forwarded
|
||||
*/
|
||||
public long getDecryptedExpiration() { return _decryptedExpiration; }
|
||||
|
||||
/**
|
||||
* Set the raw data with the formatted and encrypted options specified
|
||||
*
|
||||
* @param instructions Where a message bearing this block should be sent
|
||||
* @param messageId ID of the message for this block (not repeatable)
|
||||
* @param expiration date after which this block expires
|
||||
* @param replyThrough Encryption key of the router to whom this block is specified (not
|
||||
* the router specified in the delivery instructions!)
|
||||
*
|
||||
* @throws DataFormatException if the data is invalid or could not be encrypted
|
||||
*/
|
||||
public void setData(DeliveryInstructions instructions, long messageId, Certificate cert, long expiration, PublicKey replyThrough) throws DataFormatException {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(64);
|
||||
|
||||
_decryptedInstructions = instructions;
|
||||
_decryptedMessageId = messageId;
|
||||
_decryptedCertificate = cert;
|
||||
_decryptedExpiration = expiration;
|
||||
|
||||
instructions.writeBytes(baos);
|
||||
DataHelper.writeLong(baos, 4, messageId);
|
||||
cert.writeBytes(baos);
|
||||
DataHelper.writeDate(baos, new Date(expiration));
|
||||
|
||||
int paddedSize = 256;
|
||||
SessionKey sessKey = null;
|
||||
SessionTag tag = null;
|
||||
if (instructions.getDelayRequested()) {
|
||||
// always use a new key if we're delaying, since the reply block may not be used within the
|
||||
// window of a session
|
||||
sessKey = KeyGenerator.getInstance().generateSessionKey();
|
||||
tag = null;
|
||||
_log.debug("Delay requested - creating a new session key");
|
||||
} else {
|
||||
sessKey = SessionKeyManager.getInstance().getCurrentKey(replyThrough);
|
||||
if (sessKey == null) {
|
||||
sessKey = KeyGenerator.getInstance().generateSessionKey();
|
||||
tag = null;
|
||||
_log.debug("No delay requested, but no session key is known");
|
||||
} else {
|
||||
tag = SessionKeyManager.getInstance().consumeNextAvailableTag(replyThrough, sessKey);
|
||||
}
|
||||
}
|
||||
byte encData[] = ElGamalAESEngine.encrypt(baos.toByteArray(), replyThrough, sessKey, null, tag, paddedSize);
|
||||
setData(encData);
|
||||
} catch (IOException ioe) {
|
||||
throw new DataFormatException("Error writing out the source route block data", ioe);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new DataFormatException("Error writing out the source route block data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_router = new Hash();
|
||||
_router.readBytes(in);
|
||||
int size = (int)DataHelper.readLong(in, 2);
|
||||
_data = new byte[size];
|
||||
int read = read(in, _data);
|
||||
if (read != _data.length)
|
||||
throw new DataFormatException("Incorrect # of bytes read for source route block: " + read);
|
||||
_key = new SessionKey();
|
||||
_key.readBytes(in);
|
||||
_tag = new byte[32];
|
||||
read = read(in, _tag);
|
||||
if (read != _tag.length)
|
||||
throw new DataFormatException("Incorrect # of bytes read for session tag: " + read);
|
||||
}
|
||||
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
if ( (_router == null) || (_data == null) || (_key == null) || (_tag == null) || (_tag.length != 32) )
|
||||
throw new DataFormatException("Insufficient data to write");
|
||||
_router.writeBytes(out);
|
||||
DataHelper.writeLong(out, 2, _data.length);
|
||||
out.write(_data);
|
||||
_key.writeBytes(out);
|
||||
out.write(_tag);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ( (obj == null) || !(obj instanceof SourceRouteBlock))
|
||||
return false;
|
||||
SourceRouteBlock block = (SourceRouteBlock)obj;
|
||||
return DataHelper.eq(getRouter(), block.getRouter()) &&
|
||||
DataHelper.eq(getData(), block.getData()) &&
|
||||
DataHelper.eq(getKey(), block.getKey()) &&
|
||||
DataHelper.eq(getTag(), block.getTag());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getRouter()) +
|
||||
DataHelper.hashCode(getData()) +
|
||||
DataHelper.hashCode(getKey()) +
|
||||
DataHelper.hashCode(getTag());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
buf.append("[SourceRouteBlock: ");
|
||||
buf.append("\n\tRouter: ").append(getRouter());
|
||||
buf.append("\n\tData: ").append(DataHelper.toString(getData(), getData().length));
|
||||
buf.append("\n\tTag: ").append(DataHelper.toString(getTag(), (getTag() != null ? getTag().length : 0)));
|
||||
buf.append("\n\tKey: ").append(getKey());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
159
router/java/src/net/i2p/data/i2np/SourceRouteReplyMessage.java
Normal file
159
router/java/src/net/i2p/data/i2np/SourceRouteReplyMessage.java
Normal file
@@ -0,0 +1,159 @@
|
||||
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.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.i2p.crypto.ElGamalAESEngine;
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Defines a message directed by a source route block to deliver a message to an
|
||||
* unknown location.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class SourceRouteReplyMessage extends I2NPMessageImpl {
|
||||
private final static Log _log = new Log(SourceRouteReplyMessage.class);
|
||||
public final static int MESSAGE_TYPE = 13;
|
||||
private byte _encryptedHeader[];
|
||||
private I2NPMessage _message;
|
||||
private DeliveryInstructions _decryptedInstructions;
|
||||
private long _decryptedMessageId;
|
||||
private Certificate _decryptedCertificate;
|
||||
private long _decryptedExpiration;
|
||||
|
||||
public SourceRouteReplyMessage() {
|
||||
_encryptedHeader = null;
|
||||
_message = null;
|
||||
_decryptedInstructions = null;
|
||||
_decryptedMessageId = -1;
|
||||
_decryptedCertificate = null;
|
||||
_decryptedExpiration = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the message being sent as a reply
|
||||
*/
|
||||
public I2NPMessage getMessage() { return _message; }
|
||||
public void setMessage(I2NPMessage message) { _message = message; }
|
||||
|
||||
public void setEncryptedHeader(byte header[]) { _encryptedHeader = header; }
|
||||
|
||||
/**
|
||||
* After decryptHeader, this contains the delivery instructions for this block
|
||||
*/
|
||||
public DeliveryInstructions getDecryptedInstructions() { return _decryptedInstructions; }
|
||||
/**
|
||||
* After decryptHeader, this contains the message ID to be used with this block
|
||||
*/
|
||||
public long getDecryptedMessageId() { return _decryptedMessageId; }
|
||||
/**
|
||||
* After decryptHeader, this contains the Certificate 'paying' for the forwarding according to
|
||||
* this block
|
||||
*/
|
||||
public Certificate getDecryptedCertificate() { return _decryptedCertificate; }
|
||||
/**
|
||||
* After decryptHeader, this contains the date after which this block should not be forwarded
|
||||
*/
|
||||
public long getDecryptedExpiration() { return _decryptedExpiration; }
|
||||
|
||||
/**
|
||||
* Decrypt the header and store it in the various getDecryptedXYZ() properties
|
||||
*
|
||||
* @throws DataFormatException if the decryption fails or if the data is somehow malformed
|
||||
*/
|
||||
public void decryptHeader(PrivateKey key) throws DataFormatException {
|
||||
if ( (_encryptedHeader == null) || (_encryptedHeader.length <= 0) )
|
||||
throw new DataFormatException("No header to decrypt");
|
||||
|
||||
byte decr[] = ElGamalAESEngine.decrypt(_encryptedHeader, key);
|
||||
|
||||
if (decr == null)
|
||||
throw new DataFormatException("Decrypted data is null");
|
||||
|
||||
try {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(decr);
|
||||
|
||||
_decryptedInstructions = new DeliveryInstructions();
|
||||
_decryptedInstructions.readBytes(bais);
|
||||
_decryptedMessageId = DataHelper.readLong(bais, 4);
|
||||
_decryptedCertificate = new Certificate();
|
||||
_decryptedCertificate.readBytes(bais);
|
||||
_decryptedExpiration = DataHelper.readDate(bais).getTime();
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new DataFormatException("Error reading the source route reply header", ioe);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new DataFormatException("Error reading the source route reply header", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
try {
|
||||
int headerSize = (int)DataHelper.readLong(in, 2);
|
||||
_encryptedHeader = new byte[headerSize];
|
||||
int read = read(in, _encryptedHeader);
|
||||
if (read != headerSize)
|
||||
throw new DataFormatException("Not enough bytes to read the header (read = " + read + ", required = " + headerSize + ")");
|
||||
_message = new I2NPMessageHandler().readMessage(in);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] writeMessage() throws I2NPMessageException, IOException {
|
||||
if ( (_encryptedHeader == null) || (_message == null) )
|
||||
throw new I2NPMessageException("Not enough data to write out");
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
|
||||
try {
|
||||
DataHelper.writeLong(os, 2, _encryptedHeader.length);
|
||||
os.write(_encryptedHeader);
|
||||
_message.writeBytes(os);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(_encryptedHeader) +
|
||||
DataHelper.hashCode(_message);
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof SourceRouteReplyMessage) ) {
|
||||
SourceRouteReplyMessage msg = (SourceRouteReplyMessage)object;
|
||||
return DataHelper.eq(_message,msg._message) &&
|
||||
DataHelper.eq(_encryptedHeader,msg._encryptedHeader);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("[SourceRouteReplyMessage: ");
|
||||
buf.append("\n\tHeader: ").append(DataHelper.toString(_encryptedHeader, 64));
|
||||
buf.append("\n\tMessage: ").append(_message);
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
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.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.SessionKey;
|
||||
|
||||
/**
|
||||
* Contains the session key used by the owner/creator of the tunnel to modify
|
||||
* its operational settings.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class TunnelConfigurationSessionKey extends DataStructureImpl {
|
||||
private final static Log _log = new Log(TunnelConfigurationSessionKey.class);
|
||||
private SessionKey _key;
|
||||
|
||||
public TunnelConfigurationSessionKey() { setKey(null); }
|
||||
|
||||
public SessionKey getKey() { return _key; }
|
||||
public void setKey(SessionKey key) { _key= key; }
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_key = new SessionKey();
|
||||
_key.readBytes(in);
|
||||
}
|
||||
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
if (_key == null) throw new DataFormatException("Invalid key");
|
||||
_key.writeBytes(out);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ( (obj == null) || !(obj instanceof TunnelConfigurationSessionKey))
|
||||
return false;
|
||||
return DataHelper.eq(getKey(), ((TunnelConfigurationSessionKey)obj).getKey());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
if (_key == null) return 0;
|
||||
return getKey().hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[TunnelConfigurationSessionKey: " + getKey() + "]";
|
||||
}
|
||||
}
|
||||
312
router/java/src/net/i2p/data/i2np/TunnelCreateMessage.java
Normal file
312
router/java/src/net/i2p/data/i2np/TunnelCreateMessage.java
Normal file
@@ -0,0 +1,312 @@
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Defines the message sent to a router to request that it participate in a
|
||||
* tunnel using the included configuration settings.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
private final static Log _log = new Log(TunnelCreateMessage.class);
|
||||
public final static int MESSAGE_TYPE = 6;
|
||||
private int _participantType;
|
||||
private Hash _nextRouter;
|
||||
private TunnelId _tunnelId;
|
||||
private long _tunnelDuration;
|
||||
private TunnelConfigurationSessionKey _configKey;
|
||||
private long _maxPeakMessagesPerMin;
|
||||
private long _maxAvgMessagesPerMin;
|
||||
private long _maxPeakBytesPerMin;
|
||||
private long _maxAvgBytesPerMin;
|
||||
private boolean _includeDummyTraffic;
|
||||
private boolean _reorderMessages;
|
||||
private TunnelSigningPublicKey _verificationPubKey;
|
||||
private TunnelSigningPrivateKey _verificationPrivKey;
|
||||
private TunnelSessionKey _tunnelKey;
|
||||
private Certificate _certificate;
|
||||
private SourceRouteBlock _replyBlock;
|
||||
|
||||
public static final int PARTICIPANT_TYPE_GATEWAY = 1;
|
||||
public static final int PARTICIPANT_TYPE_ENDPOINT = 2;
|
||||
public static final int PARTICIPANT_TYPE_OTHER = 3;
|
||||
|
||||
private final static long FLAG_DUMMY = 1 << 7;
|
||||
private final static long FLAG_REORDER = 1 << 6;
|
||||
|
||||
public TunnelCreateMessage() {
|
||||
setParticipantType(-1);
|
||||
setNextRouter(null);
|
||||
setTunnelId(null);
|
||||
setTunnelDurationSeconds(-1);
|
||||
setConfigurationKey(null);
|
||||
setMaxPeakMessagesPerMin(-1);
|
||||
setMaxAvgMessagesPerMin(-1);
|
||||
setMaxPeakBytesPerMin(-1);
|
||||
setMaxAvgBytesPerMin(-1);
|
||||
setIncludeDummyTraffic(false);
|
||||
setReorderMessages(false);
|
||||
setVerificationPublicKey(null);
|
||||
setVerificationPrivateKey(null);
|
||||
setTunnelKey(null);
|
||||
setCertificate(null);
|
||||
setReplyBlock(null);
|
||||
}
|
||||
|
||||
public void setParticipantType(int type) { _participantType = type; }
|
||||
public int getParticipantType() { return _participantType; }
|
||||
public void setNextRouter(Hash routerIdentityHash) { _nextRouter = routerIdentityHash; }
|
||||
public Hash getNextRouter() { return _nextRouter; }
|
||||
public void setTunnelId(TunnelId id) { _tunnelId = id; }
|
||||
public TunnelId getTunnelId() { return _tunnelId; }
|
||||
public void setTunnelDurationSeconds(long durationSeconds) { _tunnelDuration = durationSeconds; }
|
||||
public long getTunnelDurationSeconds() { return _tunnelDuration; }
|
||||
public void setConfigurationKey(TunnelConfigurationSessionKey key) { _configKey = key; }
|
||||
public TunnelConfigurationSessionKey getConfigurationKey() { return _configKey; }
|
||||
public void setMaxPeakMessagesPerMin(long msgs) { _maxPeakMessagesPerMin = msgs; }
|
||||
public long getMaxPeakMessagesPerMin() { return _maxPeakMessagesPerMin; }
|
||||
public void setMaxAvgMessagesPerMin(long msgs) { _maxAvgMessagesPerMin = msgs; }
|
||||
public long getMaxAvgMessagesPerMin() { return _maxAvgMessagesPerMin; }
|
||||
public void setMaxPeakBytesPerMin(long bytes) { _maxPeakBytesPerMin = bytes; }
|
||||
public long getMaxPeakBytesPerMin() { return _maxPeakBytesPerMin; }
|
||||
public void setMaxAvgBytesPerMin(long bytes) { _maxAvgBytesPerMin = bytes; }
|
||||
public long getMaxAvgBytesPerMin() { return _maxAvgBytesPerMin; }
|
||||
public void setIncludeDummyTraffic(boolean include) { _includeDummyTraffic = include; }
|
||||
public boolean getIncludeDummyTraffic() { return _includeDummyTraffic; }
|
||||
public void setReorderMessages(boolean reorder) { _reorderMessages = reorder; }
|
||||
public boolean getReorderMessages() { return _reorderMessages; }
|
||||
public void setVerificationPublicKey(TunnelSigningPublicKey key) { _verificationPubKey = key; }
|
||||
public TunnelSigningPublicKey getVerificationPublicKey() { return _verificationPubKey; }
|
||||
public void setVerificationPrivateKey(TunnelSigningPrivateKey key) { _verificationPrivKey = key; }
|
||||
public TunnelSigningPrivateKey getVerificationPrivateKey() { return _verificationPrivKey; }
|
||||
public void setTunnelKey(TunnelSessionKey key) { _tunnelKey = key; }
|
||||
public TunnelSessionKey getTunnelKey() { return _tunnelKey; }
|
||||
public void setCertificate(Certificate cert) { _certificate = cert; }
|
||||
public Certificate getCertificate() { return _certificate; }
|
||||
public void setReplyBlock(SourceRouteBlock block) { _replyBlock = block; }
|
||||
public SourceRouteBlock getReplyBlock() { return _replyBlock; }
|
||||
|
||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
try {
|
||||
_participantType = (int)DataHelper.readLong(in, 1);
|
||||
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
|
||||
_nextRouter = new Hash();
|
||||
_nextRouter.readBytes(in);
|
||||
}
|
||||
_tunnelId = new TunnelId();
|
||||
_tunnelId.readBytes(in);
|
||||
_tunnelDuration = DataHelper.readLong(in, 4);
|
||||
_configKey = new TunnelConfigurationSessionKey();
|
||||
_configKey.readBytes(in);
|
||||
_maxPeakMessagesPerMin = DataHelper.readLong(in, 4);
|
||||
_maxAvgMessagesPerMin = DataHelper.readLong(in, 4);
|
||||
_maxPeakBytesPerMin = DataHelper.readLong(in, 4);
|
||||
_maxAvgBytesPerMin = DataHelper.readLong(in, 4);
|
||||
|
||||
int flags = (int)DataHelper.readLong(in, 1);
|
||||
_includeDummyTraffic = flagsIncludeDummy(flags);
|
||||
_reorderMessages = flagsReorder(flags);
|
||||
|
||||
_verificationPubKey = new TunnelSigningPublicKey();
|
||||
_verificationPubKey.readBytes(in);
|
||||
if (_participantType == PARTICIPANT_TYPE_GATEWAY) {
|
||||
_verificationPrivKey = new TunnelSigningPrivateKey();
|
||||
_verificationPrivKey.readBytes(in);
|
||||
}
|
||||
if ( (_participantType == PARTICIPANT_TYPE_ENDPOINT) || (_participantType == PARTICIPANT_TYPE_GATEWAY) ) {
|
||||
_tunnelKey = new TunnelSessionKey();
|
||||
_tunnelKey.readBytes(in);
|
||||
}
|
||||
_certificate = new Certificate();
|
||||
_certificate.readBytes(in);
|
||||
_replyBlock = new SourceRouteBlock();
|
||||
_replyBlock.readBytes(in);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] writeMessage() throws I2NPMessageException, IOException {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
|
||||
try {
|
||||
DataHelper.writeLong(os, 1, _participantType);
|
||||
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
|
||||
_nextRouter.writeBytes(os);
|
||||
}
|
||||
_tunnelId.writeBytes(os);
|
||||
DataHelper.writeLong(os, 4, _tunnelDuration);
|
||||
_configKey.writeBytes(os);
|
||||
|
||||
DataHelper.writeLong(os, 4, _maxPeakMessagesPerMin);
|
||||
DataHelper.writeLong(os, 4, _maxAvgMessagesPerMin);
|
||||
DataHelper.writeLong(os, 4, _maxPeakBytesPerMin);
|
||||
DataHelper.writeLong(os, 4, _maxAvgBytesPerMin);
|
||||
|
||||
long flags = getFlags();
|
||||
DataHelper.writeLong(os, 1, flags);
|
||||
|
||||
_verificationPubKey.writeBytes(os);
|
||||
if (_participantType == PARTICIPANT_TYPE_GATEWAY) {
|
||||
_verificationPrivKey.writeBytes(os);
|
||||
}
|
||||
if ( (_participantType == PARTICIPANT_TYPE_ENDPOINT) || (_participantType == PARTICIPANT_TYPE_GATEWAY) ) {
|
||||
_tunnelKey.writeBytes(os);
|
||||
}
|
||||
_certificate.writeBytes(os);
|
||||
_replyBlock.writeBytes(os);
|
||||
} catch (Throwable t) {
|
||||
throw new I2NPMessageException("Error writing out the message data", t);
|
||||
}
|
||||
/*
|
||||
try {
|
||||
DataHelper.writeLong(os, 1, _participantType);
|
||||
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
|
||||
if (_nextRouter == null)
|
||||
throw new I2NPMessageException("Next router is not defined");
|
||||
_nextRouter.writeBytes(os);
|
||||
}
|
||||
if (_tunnelId == null)
|
||||
throw new I2NPMessageException("Tunnel ID is not defined");
|
||||
_tunnelId.writeBytes(os);
|
||||
if (_tunnelDuration < 0)
|
||||
throw new I2NPMessageException("Tunnel duration is negative");
|
||||
DataHelper.writeLong(os, 4, _tunnelDuration);
|
||||
if (_configKey == null)
|
||||
throw new I2NPMessageException("Configuration key is not defined");
|
||||
_configKey.writeBytes(os);
|
||||
if ( (_maxPeakMessagesPerMin < 0) || (_maxAvgMessagesPerMin < 0) ||
|
||||
(_maxAvgMessagesPerMin < 0) || (_maxAvgBytesPerMin < 0) )
|
||||
throw new I2NPMessageException("Negative limits defined");
|
||||
|
||||
long flags = getFlags();
|
||||
DataHelper.writeLong(os, 1, flags);
|
||||
|
||||
if (_verificationPubKey == null)
|
||||
throw new I2NPMessageException("Verification public key is not defined");
|
||||
_verificationPubKey.writeBytes(os);
|
||||
if (_participantType == PARTICIPANT_TYPE_GATEWAY) {
|
||||
if (_verificationPrivKey == null)
|
||||
throw new I2NPMessageException("Verification private key is needed and not defined");
|
||||
_verificationPrivKey.writeBytes(os);
|
||||
}
|
||||
if ( (_participantType == PARTICIPANT_TYPE_ENDPOINT) || (_participantType == PARTICIPANT_TYPE_GATEWAY) ) {
|
||||
if (_tunnelKey == null)
|
||||
throw new I2NPMessageException("Tunnel key is needed and not defined");
|
||||
_tunnelKey.writeBytes(os);
|
||||
}
|
||||
if (_certificate == null)
|
||||
throw new I2NPMessageException("Certificate is not defined");
|
||||
_certificate.writeBytes(os);
|
||||
if (_replyBlock == null)
|
||||
throw new I2NPMessageException("Reply block not defined");
|
||||
_replyBlock.writeBytes(os);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
*/
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
private boolean flagsIncludeDummy(long flags) {
|
||||
return (0 != (flags & FLAG_DUMMY));
|
||||
}
|
||||
private boolean flagsReorder(long flags) {
|
||||
return (0 != (flags & FLAG_REORDER));
|
||||
}
|
||||
|
||||
private long getFlags() {
|
||||
long val = 0L;
|
||||
if (getIncludeDummyTraffic())
|
||||
val = val | FLAG_DUMMY;
|
||||
if (getReorderMessages())
|
||||
val = val | FLAG_REORDER;
|
||||
return val;
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
public int hashCode() {
|
||||
return (int)(DataHelper.hashCode(getCertificate()) +
|
||||
DataHelper.hashCode(getConfigurationKey()) +
|
||||
DataHelper.hashCode(getNextRouter()) +
|
||||
DataHelper.hashCode(getReplyBlock()) +
|
||||
DataHelper.hashCode(getTunnelId()) +
|
||||
DataHelper.hashCode(getTunnelKey()) +
|
||||
DataHelper.hashCode(getVerificationPrivateKey()) +
|
||||
DataHelper.hashCode(getVerificationPublicKey()) +
|
||||
(getIncludeDummyTraffic() ? 1 : 0) +
|
||||
getMaxAvgBytesPerMin() +
|
||||
getMaxAvgMessagesPerMin() +
|
||||
getMaxPeakBytesPerMin() +
|
||||
getMaxPeakMessagesPerMin() +
|
||||
getParticipantType() +
|
||||
(getReorderMessages() ? 1 : 0) +
|
||||
getTunnelDurationSeconds());
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof TunnelCreateMessage) ) {
|
||||
TunnelCreateMessage msg = (TunnelCreateMessage)object;
|
||||
return DataHelper.eq(getCertificate(), msg.getCertificate()) &&
|
||||
DataHelper.eq(getConfigurationKey(), msg.getConfigurationKey()) &&
|
||||
DataHelper.eq(getNextRouter(), msg.getNextRouter()) &&
|
||||
DataHelper.eq(getReplyBlock(), msg.getReplyBlock()) &&
|
||||
DataHelper.eq(getTunnelId(), msg.getTunnelId()) &&
|
||||
DataHelper.eq(getTunnelKey(), msg.getTunnelKey()) &&
|
||||
DataHelper.eq(getVerificationPrivateKey(), msg.getVerificationPrivateKey()) &&
|
||||
DataHelper.eq(getVerificationPublicKey(), msg.getVerificationPublicKey()) &&
|
||||
(getIncludeDummyTraffic() == msg.getIncludeDummyTraffic()) &&
|
||||
(getMaxAvgBytesPerMin() == msg.getMaxAvgBytesPerMin()) &&
|
||||
(getMaxAvgMessagesPerMin() == msg.getMaxAvgMessagesPerMin()) &&
|
||||
(getMaxPeakBytesPerMin() == msg.getMaxPeakBytesPerMin()) &&
|
||||
(getMaxPeakMessagesPerMin() == msg.getMaxPeakMessagesPerMin()) &&
|
||||
(getParticipantType() == msg.getParticipantType()) &&
|
||||
(getReorderMessages() == msg.getReorderMessages()) &&
|
||||
(getTunnelDurationSeconds() == msg.getTunnelDurationSeconds());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("[TunnelCreateMessage: ");
|
||||
buf.append("\n\tParticipant Type: ").append(getParticipantType());
|
||||
buf.append("\n\tCertificate: ").append(getCertificate());
|
||||
buf.append("\n\tConfiguration Key: ").append(getConfigurationKey());
|
||||
buf.append("\n\tNext Router: ").append(getNextRouter());
|
||||
buf.append("\n\tReply Block: ").append(getReplyBlock());
|
||||
buf.append("\n\tTunnel ID: ").append(getTunnelId());
|
||||
buf.append("\n\tTunnel Key: ").append(getTunnelKey());
|
||||
buf.append("\n\tVerification Private Key: ").append(getVerificationPrivateKey());
|
||||
buf.append("\n\tVerification Public Key: ").append(getVerificationPublicKey());
|
||||
buf.append("\n\tInclude Dummy Traffic: ").append(getIncludeDummyTraffic());
|
||||
buf.append("\n\tMax Avg Bytes / Minute: ").append(getMaxAvgBytesPerMin());
|
||||
buf.append("\n\tMax Peak Bytes / Minute: ").append(getMaxPeakBytesPerMin());
|
||||
buf.append("\n\tMax Avg Messages / Minute: ").append(getMaxAvgMessagesPerMin());
|
||||
buf.append("\n\tMax Peak Messages / Minute: ").append(getMaxPeakMessagesPerMin());
|
||||
buf.append("\n\tReorder Messages: ").append(getReorderMessages());
|
||||
buf.append("\n\tTunnel Duration (seconds): ").append(getTunnelDurationSeconds());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
113
router/java/src/net/i2p/data/i2np/TunnelCreateStatusMessage.java
Normal file
113
router/java/src/net/i2p/data/i2np/TunnelCreateStatusMessage.java
Normal file
@@ -0,0 +1,113 @@
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Defines the message a router sends to another router in reply to a
|
||||
* TunnelCreateMessage
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class TunnelCreateStatusMessage extends I2NPMessageImpl {
|
||||
private final static Log _log = new Log(TunnelCreateStatusMessage.class);
|
||||
public final static int MESSAGE_TYPE = 7;
|
||||
private TunnelId _tunnelId;
|
||||
private int _status;
|
||||
private Hash _from;
|
||||
|
||||
public final static int STATUS_SUCCESS = 0;
|
||||
public final static int STATUS_FAILED_DUPLICATE_ID = 1;
|
||||
public final static int STATUS_FAILED_OVERLOADED = 2;
|
||||
public final static int STATUS_FAILED_CERTIFICATE = 3;
|
||||
public final static int STATUS_FAILED_DELETED = 100;
|
||||
|
||||
public TunnelCreateStatusMessage() {
|
||||
setTunnelId(null);
|
||||
setStatus(-1);
|
||||
setFromHash(null);
|
||||
}
|
||||
|
||||
public TunnelId getTunnelId() { return _tunnelId; }
|
||||
public void setTunnelId(TunnelId id) { _tunnelId = id; }
|
||||
|
||||
public int getStatus() { return _status; }
|
||||
public void setStatus(int status) { _status = status; }
|
||||
|
||||
/**
|
||||
* Contains the SHA256 Hash of the RouterIdentity sending the message
|
||||
*/
|
||||
public Hash getFromHash() { return _from; }
|
||||
public void setFromHash(Hash from) { _from = from; }
|
||||
|
||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
try {
|
||||
_tunnelId = new TunnelId();
|
||||
_tunnelId.readBytes(in);
|
||||
_status = (int)DataHelper.readLong(in, 1);
|
||||
_from = new Hash();
|
||||
_from.readBytes(in);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] writeMessage() throws I2NPMessageException, IOException {
|
||||
if ( (_tunnelId == null) || (_from == null) ) throw new I2NPMessageException("Not enough data to write out");
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
|
||||
try {
|
||||
_tunnelId.writeBytes(os);
|
||||
DataHelper.writeLong(os, 1, (_status < 0 ? 255 : _status));
|
||||
_from.writeBytes(os);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getTunnelId()) +
|
||||
getStatus() +
|
||||
DataHelper.hashCode(getFromHash());
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof TunnelCreateStatusMessage) ) {
|
||||
TunnelCreateStatusMessage msg = (TunnelCreateStatusMessage)object;
|
||||
return DataHelper.eq(getTunnelId(),msg.getTunnelId()) &&
|
||||
DataHelper.eq(getFromHash(),msg.getFromHash()) &&
|
||||
(getStatus() == msg.getStatus());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("[TunnelCreateStatusMessage: ");
|
||||
buf.append("\n\tTunnel ID: ").append(getTunnelId());
|
||||
buf.append("\n\tStatus: ").append(getStatus());
|
||||
buf.append("\n\tFrom: ").append(getFromHash());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
145
router/java/src/net/i2p/data/i2np/TunnelMessage.java
Normal file
145
router/java/src/net/i2p/data/i2np/TunnelMessage.java
Normal file
@@ -0,0 +1,145 @@
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Defines the message sent between routers for tunnel delivery
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class TunnelMessage extends I2NPMessageImpl {
|
||||
private final static Log _log = new Log(TunnelMessage.class);
|
||||
public final static int MESSAGE_TYPE = 8;
|
||||
private TunnelId _tunnelId;
|
||||
private long _size;
|
||||
private byte[] _data;
|
||||
private TunnelVerificationStructure _verification;
|
||||
private byte[] _encryptedInstructions;
|
||||
|
||||
private final static int FLAG_INCLUDESTRUCTURE = 0;
|
||||
private final static int FLAG_DONT_INCLUDESTRUCTURE = 1;
|
||||
|
||||
public TunnelMessage() {
|
||||
setTunnelId(null);
|
||||
setData(null);
|
||||
setVerificationStructure(null);
|
||||
setEncryptedDeliveryInstructions(null);
|
||||
}
|
||||
|
||||
public TunnelId getTunnelId() { return _tunnelId; }
|
||||
public void setTunnelId(TunnelId id) { _tunnelId = id; }
|
||||
|
||||
public byte[] getData() { return _data; }
|
||||
public void setData(byte data[]) { _data = data; }
|
||||
|
||||
public TunnelVerificationStructure getVerificationStructure() { return _verification; }
|
||||
public void setVerificationStructure(TunnelVerificationStructure verification) { _verification = verification; }
|
||||
|
||||
public byte[] getEncryptedDeliveryInstructions() { return _encryptedInstructions; }
|
||||
public void setEncryptedDeliveryInstructions(byte instructions[]) { _encryptedInstructions = instructions; }
|
||||
|
||||
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
try {
|
||||
_tunnelId = new TunnelId();
|
||||
_tunnelId.readBytes(in);
|
||||
_log.debug("Read tunnel message for tunnel " + _tunnelId);
|
||||
_size = DataHelper.readLong(in, 4);
|
||||
_log.debug("Read tunnel message size: " + _size);
|
||||
if (_size < 0) throw new I2NPMessageException("Invalid size in the structure: " + _size);
|
||||
_data = new byte[(int)_size];
|
||||
int read = read(in, _data);
|
||||
if (read != _size)
|
||||
throw new I2NPMessageException("Incorrect number of bytes read (" + read + ", expected " + _size);
|
||||
int includeVerification = (int)DataHelper.readLong(in, 1);
|
||||
if (includeVerification == FLAG_INCLUDESTRUCTURE) {
|
||||
_verification = new TunnelVerificationStructure();
|
||||
_verification.readBytes(in);
|
||||
int len = (int)DataHelper.readLong(in, 2);
|
||||
_encryptedInstructions = new byte[len];
|
||||
read = read(in, _encryptedInstructions);
|
||||
if (read != len)
|
||||
throw new I2NPMessageException("Incorrect number of bytes read for instructions (" + read + ", expected " + len + ")");
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Unable to load the message data", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] writeMessage() throws I2NPMessageException, IOException {
|
||||
if ( (_tunnelId == null) || (_data == null) || (_data.length <= 0) )
|
||||
throw new I2NPMessageException("Not enough data to write out");
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
|
||||
try {
|
||||
_tunnelId.writeBytes(os);
|
||||
_log.debug("Writing tunnel message for tunnel " + _tunnelId);
|
||||
DataHelper.writeLong(os, 4, _data.length);
|
||||
_log.debug("Writing tunnel message length: " + _data.length);
|
||||
os.write(_data);
|
||||
_log.debug("Writing tunnel message data");
|
||||
if ( (_verification == null) || (_encryptedInstructions == null) ) {
|
||||
DataHelper.writeLong(os, 1, FLAG_DONT_INCLUDESTRUCTURE);
|
||||
_log.debug("Writing DontIncludeStructure flag");
|
||||
} else {
|
||||
DataHelper.writeLong(os, 1, FLAG_INCLUDESTRUCTURE);
|
||||
_log.debug("Writing IncludeStructure flag, then the verification structure, then the E(instr).length [" + _encryptedInstructions.length + "], then the E(instr)");
|
||||
_verification.writeBytes(os);
|
||||
DataHelper.writeLong(os, 2, _encryptedInstructions.length);
|
||||
os.write(_encryptedInstructions);
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error writing out the message data", dfe);
|
||||
}
|
||||
byte rv[] = os.toByteArray();
|
||||
_log.debug("Overall data being written: " + rv.length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getTunnelId()) +
|
||||
DataHelper.hashCode(_data) +
|
||||
DataHelper.hashCode(getVerificationStructure()) +
|
||||
DataHelper.hashCode(getEncryptedDeliveryInstructions());
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof TunnelMessage) ) {
|
||||
TunnelMessage msg = (TunnelMessage)object;
|
||||
return DataHelper.eq(getTunnelId(),msg.getTunnelId()) &&
|
||||
DataHelper.eq(getVerificationStructure(),msg.getVerificationStructure()) &&
|
||||
DataHelper.eq(getData(),msg.getData()) &&
|
||||
DataHelper.eq(getEncryptedDeliveryInstructions(), msg.getEncryptedDeliveryInstructions());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("[TunnelMessage: ");
|
||||
buf.append("\n\tTunnel ID: ").append(getTunnelId());
|
||||
buf.append("\n\tVerification Structure: ").append(getVerificationStructure());
|
||||
buf.append("\n\tEncrypted Instructions: ").append(getEncryptedDeliveryInstructions());
|
||||
buf.append("\n\tData size: ").append(getData().length);
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
61
router/java/src/net/i2p/data/i2np/TunnelSessionKey.java
Normal file
61
router/java/src/net/i2p/data/i2np/TunnelSessionKey.java
Normal file
@@ -0,0 +1,61 @@
|
||||
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.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.SessionKey;
|
||||
|
||||
/**
|
||||
* Contains the session key used by the tunnel gateway to encrypt the DeliveryInstructions
|
||||
* and used by the tunnel end point to decrypt those instructions.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class TunnelSessionKey extends DataStructureImpl {
|
||||
private final static Log _log = new Log(TunnelSessionKey.class);
|
||||
private SessionKey _key;
|
||||
|
||||
public TunnelSessionKey() { setKey(null); }
|
||||
|
||||
public SessionKey getKey() { return _key; }
|
||||
public void setKey(SessionKey key) { _key= key; }
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_key = new SessionKey();
|
||||
_key.readBytes(in);
|
||||
}
|
||||
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
if (_key == null) throw new DataFormatException("Invalid key");
|
||||
_key.writeBytes(out);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ( (obj == null) || !(obj instanceof TunnelSessionKey))
|
||||
return false;
|
||||
return DataHelper.eq(getKey(), ((TunnelSessionKey)obj).getKey());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
if (_key == null) return 0;
|
||||
return getKey().hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[TunnelSessionKey: " + getKey() + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
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.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
|
||||
/**
|
||||
* Contains the private key which constructs a signature for the TunnelMessage
|
||||
* which every participant in a tunnel uses to verify the
|
||||
* TunnelVerificationStructure with.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class TunnelSigningPrivateKey extends DataStructureImpl {
|
||||
private final static Log _log = new Log(EndPointPrivateKey.class);
|
||||
private SigningPrivateKey _key;
|
||||
|
||||
public TunnelSigningPrivateKey() { setKey(null); }
|
||||
|
||||
public SigningPrivateKey getKey() { return _key; }
|
||||
public void setKey(SigningPrivateKey key) { _key= key; }
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_key = new SigningPrivateKey();
|
||||
_key.readBytes(in);
|
||||
}
|
||||
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
if (_key == null) throw new DataFormatException("Invalid key");
|
||||
_key.writeBytes(out);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ( (obj == null) || !(obj instanceof TunnelSigningPrivateKey))
|
||||
return false;
|
||||
return DataHelper.eq(getKey(), ((TunnelSigningPrivateKey)obj).getKey());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
if (_key == null) return 0;
|
||||
return getKey().hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[EndPointPrivateKey: " + getKey() + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
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.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
|
||||
/**
|
||||
* Contains the public key which every participant in a tunnel uses to verify
|
||||
* the TunnelVerificationStructure for TunnelMessages that pass by.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class TunnelSigningPublicKey extends DataStructureImpl {
|
||||
private final static Log _log = new Log(TunnelSigningPublicKey.class);
|
||||
private SigningPublicKey _key;
|
||||
|
||||
public TunnelSigningPublicKey() { setKey(null); }
|
||||
|
||||
public SigningPublicKey getKey() { return _key; }
|
||||
public void setKey(SigningPublicKey key) { _key= key; }
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_key = new SigningPublicKey();
|
||||
_key.readBytes(in);
|
||||
}
|
||||
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
if (_key == null) throw new DataFormatException("Invalid key");
|
||||
_key.writeBytes(out);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ( (obj == null) || !(obj instanceof TunnelSigningPublicKey))
|
||||
return false;
|
||||
return DataHelper.eq(getKey(), ((TunnelSigningPublicKey)obj).getKey());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
if (_key == null) return 0;
|
||||
return getKey().hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[TunnelSigningPublicKey: " + getKey() + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
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.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.crypto.DSAEngine;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class TunnelVerificationStructure extends DataStructureImpl {
|
||||
private final static Log _log = new Log(TunnelVerificationStructure.class);
|
||||
private Hash _msgHash;
|
||||
private Signature _authSignature;
|
||||
|
||||
public TunnelVerificationStructure() {
|
||||
setMessageHash(null);
|
||||
setAuthorizationSignature(null);
|
||||
}
|
||||
|
||||
public Hash getMessageHash() { return _msgHash; }
|
||||
public void setMessageHash(Hash hash) { _msgHash = hash; }
|
||||
|
||||
public Signature getAuthorizationSignature() { return _authSignature; }
|
||||
public void setAuthorizationSignature(Signature sig) { _authSignature = sig; }
|
||||
|
||||
public void sign(SigningPrivateKey key) {
|
||||
if (_msgHash != null) {
|
||||
Signature sig = DSAEngine.getInstance().sign(_msgHash.getData(), key);
|
||||
setAuthorizationSignature(sig);
|
||||
}
|
||||
}
|
||||
public boolean verifySignature(SigningPublicKey key) {
|
||||
if (_msgHash == null) return false;
|
||||
return DSAEngine.getInstance().verifySignature(_authSignature, _msgHash.getData(), key);
|
||||
}
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
_msgHash = new Hash();
|
||||
_msgHash.readBytes(in);
|
||||
_authSignature = new Signature();
|
||||
_authSignature.readBytes(in);
|
||||
}
|
||||
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
if (_authSignature == null) {
|
||||
_authSignature = new Signature();
|
||||
_authSignature.setData(Signature.FAKE_SIGNATURE);
|
||||
}
|
||||
if ( (_msgHash == null) || (_authSignature == null) ) throw new DataFormatException("Invalid data");
|
||||
_msgHash.writeBytes(out);
|
||||
_authSignature.writeBytes(out);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ( (obj == null) || !(obj instanceof TunnelVerificationStructure))
|
||||
return false;
|
||||
TunnelVerificationStructure str = (TunnelVerificationStructure)obj;
|
||||
return DataHelper.eq(getMessageHash(), str.getMessageHash()) &&
|
||||
DataHelper.eq(getAuthorizationSignature(), str.getAuthorizationSignature());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
if ( (_msgHash == null) || (_authSignature == null) ) return 0;
|
||||
return getMessageHash().hashCode() + getAuthorizationSignature().hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[TunnelVerificationStructure: " + getMessageHash() + " " + getAuthorizationSignature() + "]";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user