2004-04-08 04:41:54 +00:00
|
|
|
package net.i2p.data.i2np;
|
|
|
|
/*
|
|
|
|
* free (adj.): unencumbered; not under the control of others
|
big ol' update to strip out the singletons, replacing them with
a rooted app context. The core itself has its own I2PAppContext
(see its javadoc for, uh, docs), and the router extends that to
expose the router's singletons. The main point of this is to
make it so that we can run multiple routers in the same JVM, even
to allow different apps in the same JVM to switch singleton
implementations (e.g. run some routers with one set of profile
calculators, and other routers with a different one).
There is still some work to be done regarding the actual boot up
of multiple routers in a JVM, as well as their configuration,
though the plan is to have the RouterContext override the
I2PAppContext's getProperty/getPropertyNames methods to read from
a config file (seperate ones per context) instead of using the
System.getProperty that the base I2PAppContext uses.
Once the multi-router is working, i'll shim in a VMCommSystem
that doesn't depend upon sockets or threads to read/write (and
that uses configurable message send delays / disconnects / etc,
perhaps using data from the routerContext.getProperty to drive it).
I could hold off until the sim is all working, but there's a
truckload of changes in here and I hate dealing with conflicts ;)
Everything works - I've been running 'er for a while and kicked
the tires a bit, but if you see something amiss, please let me
know.
2004-04-24 11:54:35 +00:00
|
|
|
* 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
|
2004-04-08 04:41:54 +00:00
|
|
|
* your children, but it might. Use at your own risk.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2004-09-26 15:16:44 +00:00
|
|
|
import java.io.ByteArrayInputStream;
|
2004-04-08 04:41:54 +00:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.OutputStream;
|
|
|
|
import java.util.Date;
|
|
|
|
|
2004-05-17 03:38:53 +00:00
|
|
|
import net.i2p.I2PAppContext;
|
2004-04-08 04:41:54 +00:00
|
|
|
import net.i2p.data.DataFormatException;
|
|
|
|
import net.i2p.data.DataHelper;
|
|
|
|
import net.i2p.data.DataStructureImpl;
|
2004-09-26 15:16:44 +00:00
|
|
|
import net.i2p.data.Hash;
|
2004-04-10 11:39:00 +00:00
|
|
|
import net.i2p.util.Log;
|
2004-04-08 04:41:54 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Defines the base message implementation.
|
|
|
|
*
|
|
|
|
* @author jrandom
|
|
|
|
*/
|
|
|
|
public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPMessage {
|
big ol' update to strip out the singletons, replacing them with
a rooted app context. The core itself has its own I2PAppContext
(see its javadoc for, uh, docs), and the router extends that to
expose the router's singletons. The main point of this is to
make it so that we can run multiple routers in the same JVM, even
to allow different apps in the same JVM to switch singleton
implementations (e.g. run some routers with one set of profile
calculators, and other routers with a different one).
There is still some work to be done regarding the actual boot up
of multiple routers in a JVM, as well as their configuration,
though the plan is to have the RouterContext override the
I2PAppContext's getProperty/getPropertyNames methods to read from
a config file (seperate ones per context) instead of using the
System.getProperty that the base I2PAppContext uses.
Once the multi-router is working, i'll shim in a VMCommSystem
that doesn't depend upon sockets or threads to read/write (and
that uses configurable message send delays / disconnects / etc,
perhaps using data from the routerContext.getProperty to drive it).
I could hold off until the sim is all working, but there's a
truckload of changes in here and I hate dealing with conflicts ;)
Everything works - I've been running 'er for a while and kicked
the tires a bit, but if you see something amiss, please let me
know.
2004-04-24 11:54:35 +00:00
|
|
|
private Log _log;
|
|
|
|
protected I2PAppContext _context;
|
2004-04-08 04:41:54 +00:00
|
|
|
private Date _expiration;
|
|
|
|
private long _uniqueId;
|
2004-10-07 19:19:51 +00:00
|
|
|
private byte _data[];
|
2004-04-08 04:41:54 +00:00
|
|
|
|
|
|
|
public final static long DEFAULT_EXPIRATION_MS = 1*60*1000; // 1 minute by default
|
|
|
|
|
big ol' update to strip out the singletons, replacing them with
a rooted app context. The core itself has its own I2PAppContext
(see its javadoc for, uh, docs), and the router extends that to
expose the router's singletons. The main point of this is to
make it so that we can run multiple routers in the same JVM, even
to allow different apps in the same JVM to switch singleton
implementations (e.g. run some routers with one set of profile
calculators, and other routers with a different one).
There is still some work to be done regarding the actual boot up
of multiple routers in a JVM, as well as their configuration,
though the plan is to have the RouterContext override the
I2PAppContext's getProperty/getPropertyNames methods to read from
a config file (seperate ones per context) instead of using the
System.getProperty that the base I2PAppContext uses.
Once the multi-router is working, i'll shim in a VMCommSystem
that doesn't depend upon sockets or threads to read/write (and
that uses configurable message send delays / disconnects / etc,
perhaps using data from the routerContext.getProperty to drive it).
I could hold off until the sim is all working, but there's a
truckload of changes in here and I hate dealing with conflicts ;)
Everything works - I've been running 'er for a while and kicked
the tires a bit, but if you see something amiss, please let me
know.
2004-04-24 11:54:35 +00:00
|
|
|
public I2NPMessageImpl(I2PAppContext context) {
|
|
|
|
_context = context;
|
|
|
|
_log = context.logManager().getLog(I2NPMessageImpl.class);
|
|
|
|
_expiration = new Date(_context.clock().now() + DEFAULT_EXPIRATION_MS);
|
2004-06-29 19:28:40 +00:00
|
|
|
_uniqueId = _context.random().nextLong(MAX_ID_VALUE);
|
2004-10-07 19:19:51 +00:00
|
|
|
_context.statManager().createRateStat("i2np.writeTime", "How long it takes to write an I2NP message", "I2NP", new long[] { 10*60*1000, 60*60*1000 });
|
|
|
|
_context.statManager().createRateStat("i2np.readTime", "How long it takes to read an I2NP message", "I2NP", new long[] { 10*60*1000, 60*60*1000 });
|
2004-04-08 04:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
big ol' update to strip out the singletons, replacing them with
a rooted app context. The core itself has its own I2PAppContext
(see its javadoc for, uh, docs), and the router extends that to
expose the router's singletons. The main point of this is to
make it so that we can run multiple routers in the same JVM, even
to allow different apps in the same JVM to switch singleton
implementations (e.g. run some routers with one set of profile
calculators, and other routers with a different one).
There is still some work to be done regarding the actual boot up
of multiple routers in a JVM, as well as their configuration,
though the plan is to have the RouterContext override the
I2PAppContext's getProperty/getPropertyNames methods to read from
a config file (seperate ones per context) instead of using the
System.getProperty that the base I2PAppContext uses.
Once the multi-router is working, i'll shim in a VMCommSystem
that doesn't depend upon sockets or threads to read/write (and
that uses configurable message send delays / disconnects / etc,
perhaps using data from the routerContext.getProperty to drive it).
I could hold off until the sim is all working, but there's a
truckload of changes in here and I hate dealing with conflicts ;)
Everything works - I've been running 'er for a while and kicked
the tires a bit, but if you see something amiss, please let me
know.
2004-04-24 11:54:35 +00:00
|
|
|
* 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
|
2004-04-08 04:41:54 +00:00
|
|
|
* 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 {
|
big ol' update to strip out the singletons, replacing them with
a rooted app context. The core itself has its own I2PAppContext
(see its javadoc for, uh, docs), and the router extends that to
expose the router's singletons. The main point of this is to
make it so that we can run multiple routers in the same JVM, even
to allow different apps in the same JVM to switch singleton
implementations (e.g. run some routers with one set of profile
calculators, and other routers with a different one).
There is still some work to be done regarding the actual boot up
of multiple routers in a JVM, as well as their configuration,
though the plan is to have the RouterContext override the
I2PAppContext's getProperty/getPropertyNames methods to read from
a config file (seperate ones per context) instead of using the
System.getProperty that the base I2PAppContext uses.
Once the multi-router is working, i'll shim in a VMCommSystem
that doesn't depend upon sockets or threads to read/write (and
that uses configurable message send delays / disconnects / etc,
perhaps using data from the routerContext.getProperty to drive it).
I could hold off until the sim is all working, but there's a
truckload of changes in here and I hate dealing with conflicts ;)
Everything works - I've been running 'er for a while and kicked
the tires a bit, but if you see something amiss, please let me
know.
2004-04-24 11:54:35 +00:00
|
|
|
try {
|
|
|
|
readBytes(in, -1);
|
|
|
|
} catch (I2NPMessageException ime) {
|
|
|
|
throw new DataFormatException("Bad bytes", ime);
|
|
|
|
}
|
2004-04-08 04:41:54 +00:00
|
|
|
}
|
|
|
|
public void readBytes(InputStream in, int type) throws I2NPMessageException, IOException {
|
big ol' update to strip out the singletons, replacing them with
a rooted app context. The core itself has its own I2PAppContext
(see its javadoc for, uh, docs), and the router extends that to
expose the router's singletons. The main point of this is to
make it so that we can run multiple routers in the same JVM, even
to allow different apps in the same JVM to switch singleton
implementations (e.g. run some routers with one set of profile
calculators, and other routers with a different one).
There is still some work to be done regarding the actual boot up
of multiple routers in a JVM, as well as their configuration,
though the plan is to have the RouterContext override the
I2PAppContext's getProperty/getPropertyNames methods to read from
a config file (seperate ones per context) instead of using the
System.getProperty that the base I2PAppContext uses.
Once the multi-router is working, i'll shim in a VMCommSystem
that doesn't depend upon sockets or threads to read/write (and
that uses configurable message send delays / disconnects / etc,
perhaps using data from the routerContext.getProperty to drive it).
I could hold off until the sim is all working, but there's a
truckload of changes in here and I hate dealing with conflicts ;)
Everything works - I've been running 'er for a while and kicked
the tires a bit, but if you see something amiss, please let me
know.
2004-04-24 11:54:35 +00:00
|
|
|
try {
|
2004-10-07 19:19:51 +00:00
|
|
|
long start = _context.clock().now();
|
big ol' update to strip out the singletons, replacing them with
a rooted app context. The core itself has its own I2PAppContext
(see its javadoc for, uh, docs), and the router extends that to
expose the router's singletons. The main point of this is to
make it so that we can run multiple routers in the same JVM, even
to allow different apps in the same JVM to switch singleton
implementations (e.g. run some routers with one set of profile
calculators, and other routers with a different one).
There is still some work to be done regarding the actual boot up
of multiple routers in a JVM, as well as their configuration,
though the plan is to have the RouterContext override the
I2PAppContext's getProperty/getPropertyNames methods to read from
a config file (seperate ones per context) instead of using the
System.getProperty that the base I2PAppContext uses.
Once the multi-router is working, i'll shim in a VMCommSystem
that doesn't depend upon sockets or threads to read/write (and
that uses configurable message send delays / disconnects / etc,
perhaps using data from the routerContext.getProperty to drive it).
I could hold off until the sim is all working, but there's a
truckload of changes in here and I hate dealing with conflicts ;)
Everything works - I've been running 'er for a while and kicked
the tires a bit, but if you see something amiss, please let me
know.
2004-04-24 11:54:35 +00:00
|
|
|
if (type < 0)
|
|
|
|
type = (int)DataHelper.readLong(in, 1);
|
|
|
|
_uniqueId = DataHelper.readLong(in, 4);
|
|
|
|
_expiration = DataHelper.readDate(in);
|
2004-09-26 15:16:44 +00:00
|
|
|
int size = (int)DataHelper.readLong(in, 2);
|
|
|
|
Hash h = new Hash();
|
|
|
|
h.readBytes(in);
|
|
|
|
byte data[] = new byte[size];
|
|
|
|
int read = DataHelper.read(in, data);
|
|
|
|
if (read != size)
|
|
|
|
throw new I2NPMessageException("Payload is too short [" + read + ", wanted " + size + "]");
|
|
|
|
Hash calc = _context.sha().calculateHash(data);
|
|
|
|
if (!calc.equals(h))
|
|
|
|
throw new I2NPMessageException("Hash does not match");
|
|
|
|
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("Reading bytes: type = " + type + " / uniqueId : " + _uniqueId + " / expiration : " + _expiration);
|
|
|
|
readMessage(new ByteArrayInputStream(data), type);
|
2004-10-07 19:19:51 +00:00
|
|
|
long time = _context.clock().now() - start;
|
|
|
|
if (time > 50)
|
|
|
|
_context.statManager().addRateData("i2np.readTime", time, time);
|
big ol' update to strip out the singletons, replacing them with
a rooted app context. The core itself has its own I2PAppContext
(see its javadoc for, uh, docs), and the router extends that to
expose the router's singletons. The main point of this is to
make it so that we can run multiple routers in the same JVM, even
to allow different apps in the same JVM to switch singleton
implementations (e.g. run some routers with one set of profile
calculators, and other routers with a different one).
There is still some work to be done regarding the actual boot up
of multiple routers in a JVM, as well as their configuration,
though the plan is to have the RouterContext override the
I2PAppContext's getProperty/getPropertyNames methods to read from
a config file (seperate ones per context) instead of using the
System.getProperty that the base I2PAppContext uses.
Once the multi-router is working, i'll shim in a VMCommSystem
that doesn't depend upon sockets or threads to read/write (and
that uses configurable message send delays / disconnects / etc,
perhaps using data from the routerContext.getProperty to drive it).
I could hold off until the sim is all working, but there's a
truckload of changes in here and I hate dealing with conflicts ;)
Everything works - I've been running 'er for a while and kicked
the tires a bit, but if you see something amiss, please let me
know.
2004-04-24 11:54:35 +00:00
|
|
|
} catch (DataFormatException dfe) {
|
|
|
|
throw new I2NPMessageException("Error reading the message header", dfe);
|
|
|
|
}
|
2004-04-08 04:41:54 +00:00
|
|
|
}
|
|
|
|
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
2004-10-07 19:19:51 +00:00
|
|
|
int size = getMessageSize();
|
|
|
|
if (size < 47) throw new DataFormatException("Unable to build the message");
|
|
|
|
byte buf[] = new byte[size];
|
|
|
|
int read = toByteArray(buf);
|
|
|
|
if (read < 0)
|
|
|
|
out.write(buf, 0, read);
|
2004-04-08 04:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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; }
|
2004-09-26 15:16:44 +00:00
|
|
|
|
2004-10-07 19:19:51 +00:00
|
|
|
public synchronized int getMessageSize() {
|
|
|
|
return calculateWrittenLength()+47; // 47 bytes in the header
|
|
|
|
}
|
|
|
|
|
|
|
|
public byte[] toByteArray() {
|
|
|
|
byte data[] = new byte[getMessageSize()];
|
|
|
|
int written = toByteArray(data);
|
|
|
|
if (written != data.length) {
|
|
|
|
_log.error("Error writing out " + data.length + " for " + getClass().getName());
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int toByteArray(byte buffer[]) {
|
|
|
|
long start = _context.clock().now();
|
|
|
|
|
|
|
|
byte prefix[][] = new byte[][] { DataHelper.toLong(1, getType()),
|
|
|
|
DataHelper.toLong(4, _uniqueId),
|
|
|
|
DataHelper.toDate(_expiration),
|
|
|
|
new byte[2],
|
|
|
|
new byte[Hash.HASH_LENGTH]};
|
|
|
|
byte suffix[][] = new byte[][] { };
|
2004-09-26 15:16:44 +00:00
|
|
|
try {
|
2004-10-07 19:19:51 +00:00
|
|
|
int writtenLen = toByteArray(buffer, prefix, suffix);
|
|
|
|
|
|
|
|
int prefixLen = 1+4+8+2+Hash.HASH_LENGTH;
|
|
|
|
int suffixLen = 0;
|
|
|
|
int payloadLen = writtenLen - prefixLen - suffixLen;
|
|
|
|
Hash h = _context.sha().calculateHash(buffer, prefixLen, payloadLen);
|
|
|
|
|
|
|
|
byte len[] = DataHelper.toLong(2, payloadLen);
|
|
|
|
buffer[1+4+8] = len[0];
|
|
|
|
buffer[1+4+8+1] = len[1];
|
|
|
|
for (int i = 0; i < Hash.HASH_LENGTH; i++)
|
|
|
|
System.arraycopy(h.getData(), 0, buffer, 1+4+8+2, Hash.HASH_LENGTH);
|
|
|
|
|
|
|
|
long time = _context.clock().now() - start;
|
|
|
|
if (time > 50)
|
|
|
|
_context.statManager().addRateData("i2np.writeTime", time, time);
|
|
|
|
|
|
|
|
return writtenLen;
|
2004-09-26 15:16:44 +00:00
|
|
|
} catch (I2NPMessageException ime) {
|
2004-10-07 19:19:51 +00:00
|
|
|
_context.logManager().getLog(getClass()).error("Error writing", ime);
|
|
|
|
throw new IllegalStateException("Unable to serialize the message: " + ime.getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** calculate the message body's length (not including the header and footer */
|
|
|
|
protected abstract int calculateWrittenLength();
|
|
|
|
/**
|
|
|
|
* write the message body to the output array, starting at the given index.
|
|
|
|
* @return the index into the array after the last byte written
|
|
|
|
*/
|
|
|
|
protected abstract int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException;
|
|
|
|
|
|
|
|
protected int toByteArray(byte out[], byte[][] prefix, byte[][] suffix) throws I2NPMessageException {
|
|
|
|
int curIndex = 0;
|
|
|
|
for (int i = 0; i < prefix.length; i++) {
|
|
|
|
System.arraycopy(prefix[i], 0, out, curIndex, prefix[i].length);
|
|
|
|
curIndex += prefix[i].length;
|
|
|
|
}
|
|
|
|
|
|
|
|
curIndex = writeMessageBody(out, curIndex);
|
|
|
|
|
|
|
|
for (int i = 0; i < suffix.length; i++) {
|
|
|
|
System.arraycopy(suffix[i], 0, out, curIndex, suffix[i].length);
|
|
|
|
curIndex += suffix[i].length;
|
2004-09-26 15:16:44 +00:00
|
|
|
}
|
2004-10-07 19:19:51 +00:00
|
|
|
|
|
|
|
return curIndex;
|
2004-09-26 15:16:44 +00:00
|
|
|
}
|
2004-04-08 04:41:54 +00:00
|
|
|
}
|