forked from I2P_Developers/i2p.i2p
Added ScalaTest support to router build.xml
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
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.I2PAppContext;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataStructure;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.RouterInfoTest;
|
||||
import net.i2p.data.StructureTest;
|
||||
import net.i2p.util.Clock;
|
||||
|
||||
/**
|
||||
* Test harness for loading / storing I2NP DatabaseStore message objects
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DatabaseStoreMessageTest extends StructureTest {
|
||||
public DataStructure createDataStructure() throws DataFormatException {
|
||||
DatabaseStoreMessage msg = new DatabaseStoreMessage(I2PAppContext.getGlobalContext());
|
||||
RouterInfo info = (RouterInfo)new RouterInfoTest().createDataStructure();
|
||||
msg.setKey(info.getIdentity().getHash());
|
||||
msg.setMessageExpiration(Clock.getInstance().now());
|
||||
msg.setUniqueId(666);
|
||||
msg.setRouterInfo(info);
|
||||
return msg;
|
||||
}
|
||||
|
||||
public DataStructure createStructureToRead() {
|
||||
return new DatabaseStoreMessage(I2PAppContext.getGlobalContext());
|
||||
}
|
||||
}
|
77
router/java/test/junit/net/i2p/data/i2np/DateMessage.java
Normal file
77
router/java/test/junit/net/i2p/data/i2np/DateMessage.java
Normal file
@@ -0,0 +1,77 @@
|
||||
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 net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* Contains the sending router's current time, to sync (and verify sync)
|
||||
*
|
||||
*/
|
||||
public class DateMessage extends I2NPMessageImpl {
|
||||
public final static int MESSAGE_TYPE = 16;
|
||||
private long _now;
|
||||
|
||||
public DateMessage(I2PAppContext context) {
|
||||
super(context);
|
||||
_now = context.clock().now();
|
||||
}
|
||||
|
||||
public long getNow() { return _now; }
|
||||
public void setNow(long now) { _now = now; }
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
int curIndex = offset;
|
||||
|
||||
_now = DataHelper.fromLong(data, curIndex, DataHelper.DATE_LENGTH);
|
||||
}
|
||||
|
||||
/** calculate the message body's length (not including the header and footer */
|
||||
protected int calculateWrittenLength() {
|
||||
return DataHelper.DATE_LENGTH; // now
|
||||
}
|
||||
/** write the message body to the output array, starting at the given index */
|
||||
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
|
||||
if (_now <= 0) throw new I2NPMessageException("Not enough data to write out");
|
||||
|
||||
DataHelper.toLong(out, curIndex, DataHelper.DATE_LENGTH, _now);
|
||||
curIndex += DataHelper.DATE_LENGTH;
|
||||
return curIndex;
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int)getNow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof DateMessage) ) {
|
||||
DateMessage msg = (DateMessage)object;
|
||||
return msg.getNow() == getNow();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[DateMessage: ");
|
||||
buf.append("Now: ").append(_now);
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
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.data.DataFormatException;
|
||||
import net.i2p.data.DataStructure;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.StructureTest;
|
||||
import net.i2p.data.TunnelId;
|
||||
|
||||
/**
|
||||
* Test harness for loading / storing DeliveryInstructions objects
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DeliveryInstructionsTest extends StructureTest {
|
||||
public DataStructure createDataStructure() throws DataFormatException {
|
||||
DeliveryInstructions instructions = new DeliveryInstructions();
|
||||
instructions.setDelayRequested(true);
|
||||
instructions.setDelaySeconds(42);
|
||||
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_TUNNEL);
|
||||
instructions.setEncrypted(true);
|
||||
SessionKey key = new SessionKey();
|
||||
byte keyData[] = new byte[SessionKey.KEYSIZE_BYTES];
|
||||
for (int i = 0; i < keyData.length; i++)
|
||||
keyData[i] = (byte)i;
|
||||
key.setData(keyData);
|
||||
instructions.setEncryptionKey(key);
|
||||
Hash hash = new Hash();
|
||||
byte hashData[] = new byte[32];
|
||||
for (int i = 0; i < hashData.length; i++)
|
||||
hashData[i] = (byte)(i%32);
|
||||
hash.setData(hashData);
|
||||
instructions.setRouter(hash);
|
||||
TunnelId id = new TunnelId();
|
||||
id.setTunnelId(666);
|
||||
instructions.setTunnelId(id);
|
||||
|
||||
return instructions;
|
||||
}
|
||||
public DataStructure createStructureToRead() { return new DeliveryInstructions(); }
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
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 junit.framework.TestCase;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
/**
|
||||
* Test harness for loading / storing I2NP DatabaseStore message objects
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class I2NPMessageReaderTest extends TestCase implements I2NPMessageReader.I2NPMessageEventListener{
|
||||
|
||||
public void setUp(){}
|
||||
|
||||
public void testI2NPMessageReader() throws IOException, DataFormatException{
|
||||
InputStream data = getData();
|
||||
test(data);
|
||||
}
|
||||
|
||||
private InputStream getData() throws IOException, DataFormatException{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DatabaseStoreMessage msg = (DatabaseStoreMessage)new DatabaseStoreMessageTest().createDataStructure();
|
||||
msg.writeBytes(baos);
|
||||
msg.writeBytes(baos);
|
||||
msg.writeBytes(baos);
|
||||
msg.writeBytes(baos);
|
||||
return new ByteArrayInputStream(baos.toByteArray());
|
||||
}
|
||||
|
||||
private void test(InputStream in) {
|
||||
I2NPMessageReader reader = new I2NPMessageReader(new RouterContext(null), in, this);
|
||||
reader.startReading();
|
||||
}
|
||||
|
||||
public void disconnected(I2NPMessageReader reader) {
|
||||
}
|
||||
|
||||
public void messageReceived(I2NPMessageReader reader, I2NPMessage message, long msToRead, int size) {
|
||||
}
|
||||
|
||||
public void readError(I2NPMessageReader reader, Exception error) {
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,277 @@
|
||||
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.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.SessionTag;
|
||||
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.
|
||||
*
|
||||
*/
|
||||
public class TunnelCreateMessage extends I2NPMessageImpl {
|
||||
private Log _log;
|
||||
public final static int MESSAGE_TYPE = 6;
|
||||
private Hash _nextRouter;
|
||||
private TunnelId _nextTunnelId;
|
||||
private int _durationSeconds;
|
||||
private SessionKey _layerKey;
|
||||
private SessionKey _ivKey;
|
||||
private Properties _options;
|
||||
private Hash _replyGateway;
|
||||
private TunnelId _replyTunnel;
|
||||
private SessionTag _replyTag;
|
||||
private SessionKey _replyKey;
|
||||
private boolean _isGateway;
|
||||
private long _nonce;
|
||||
private Certificate _certificate;
|
||||
|
||||
private byte[] _optionsCache;
|
||||
private byte[] _certificateCache;
|
||||
|
||||
public static final long MAX_NONCE_VALUE = ((1l << 32l) - 1l);
|
||||
|
||||
private static final Hash INVALID_HASH = new Hash(new byte[Hash.HASH_LENGTH]); // all 0s
|
||||
private static final TunnelId INVALID_TUNNEL = TunnelId.INVALID;
|
||||
|
||||
public TunnelCreateMessage(I2PAppContext context) {
|
||||
super(context);
|
||||
_log = context.logManager().getLog(TunnelCreateMessage.class);
|
||||
}
|
||||
|
||||
public void setNextRouter(Hash routerIdentityHash) { _nextRouter = routerIdentityHash; }
|
||||
public Hash getNextRouter() { return _nextRouter; }
|
||||
public void setNextTunnelId(TunnelId id) { _nextTunnelId = id; }
|
||||
public TunnelId getNextTunnelId() { return _nextTunnelId; }
|
||||
public void setDurationSeconds(int seconds) { _durationSeconds = seconds; }
|
||||
public int getDurationSeconds() { return _durationSeconds; }
|
||||
public void setLayerKey(SessionKey key) { _layerKey = key; }
|
||||
public SessionKey getLayerKey() { return _layerKey; }
|
||||
public void setIVKey(SessionKey key) { _ivKey = key; }
|
||||
public SessionKey getIVKey() { return _ivKey; }
|
||||
public void setCertificate(Certificate cert) { _certificate = cert; }
|
||||
public Certificate getCertificate() { return _certificate; }
|
||||
public void setReplyTag(SessionTag tag) { _replyTag = tag; }
|
||||
public SessionTag getReplyTag() { return _replyTag; }
|
||||
public void setReplyKey(SessionKey key) { _replyKey = key; }
|
||||
public SessionKey getReplyKey() { return _replyKey; }
|
||||
public void setReplyTunnel(TunnelId id) { _replyTunnel = id; }
|
||||
public TunnelId getReplyTunnel() { return _replyTunnel; }
|
||||
public void setReplyGateway(Hash peer) { _replyGateway = peer; }
|
||||
public Hash getReplyGateway() { return _replyGateway; }
|
||||
public void setNonce(long nonce) { _nonce = nonce; }
|
||||
public long getNonce() { return _nonce; }
|
||||
public void setIsGateway(boolean isGateway) { _isGateway = isGateway; }
|
||||
public boolean getIsGateway() { return _isGateway; }
|
||||
public Properties getOptions() { return _options; }
|
||||
public void setOptions(Properties opts) { _options = opts; }
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
|
||||
if (DataHelper.eq(INVALID_HASH.getData(), 0, data, offset, Hash.HASH_LENGTH)) {
|
||||
_nextRouter = null;
|
||||
} else {
|
||||
_nextRouter = new Hash(new byte[Hash.HASH_LENGTH]);
|
||||
System.arraycopy(data, offset, _nextRouter.getData(), 0, Hash.HASH_LENGTH);
|
||||
}
|
||||
offset += Hash.HASH_LENGTH;
|
||||
|
||||
long id = DataHelper.fromLong(data, offset, 4);
|
||||
if (id > 0)
|
||||
_nextTunnelId = new TunnelId(id);
|
||||
offset += 4;
|
||||
|
||||
_durationSeconds = (int)DataHelper.fromLong(data, offset, 2);
|
||||
offset += 2;
|
||||
|
||||
_layerKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
|
||||
System.arraycopy(data, offset, _layerKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
|
||||
offset += SessionKey.KEYSIZE_BYTES;
|
||||
|
||||
_ivKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
|
||||
System.arraycopy(data, offset, _ivKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
|
||||
offset += SessionKey.KEYSIZE_BYTES;
|
||||
|
||||
try {
|
||||
Properties opts = new Properties();
|
||||
_options = opts;
|
||||
offset = DataHelper.fromProperties(data, offset, opts);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error reading the options", dfe);
|
||||
}
|
||||
|
||||
_replyGateway = new Hash(new byte[Hash.HASH_LENGTH]);
|
||||
System.arraycopy(data, offset, _replyGateway.getData(), 0, Hash.HASH_LENGTH);
|
||||
offset += Hash.HASH_LENGTH;
|
||||
|
||||
_replyTunnel = new TunnelId(DataHelper.fromLong(data, offset, 4));
|
||||
offset += 4;
|
||||
|
||||
_replyTag = new SessionTag(new byte[SessionTag.BYTE_LENGTH]);
|
||||
System.arraycopy(data, offset, _replyTag.getData(), 0, SessionTag.BYTE_LENGTH);
|
||||
offset += SessionTag.BYTE_LENGTH;
|
||||
|
||||
_replyKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
|
||||
System.arraycopy(data, offset, _replyKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
|
||||
offset += SessionKey.KEYSIZE_BYTES;
|
||||
|
||||
_nonce = DataHelper.fromLong(data, offset, 4);
|
||||
offset += 4;
|
||||
|
||||
try {
|
||||
Certificate cert = new Certificate();
|
||||
_certificate = cert;
|
||||
offset += cert.readBytes(data, offset);
|
||||
} catch (DataFormatException dfe) {
|
||||
throw new I2NPMessageException("Error reading the certificate", dfe);
|
||||
}
|
||||
|
||||
Boolean b = DataHelper.fromBoolean(data, offset);
|
||||
if (b == null)
|
||||
throw new I2NPMessageException("isGateway == unknown?!");
|
||||
_isGateway = b.booleanValue();
|
||||
offset += DataHelper.BOOLEAN_LENGTH;
|
||||
}
|
||||
|
||||
|
||||
/** calculate the message body's length (not including the header and footer */
|
||||
protected int calculateWrittenLength() {
|
||||
int length = 0;
|
||||
length += Hash.HASH_LENGTH; // nextRouter
|
||||
length += 4; // nextTunnel
|
||||
length += 2; // duration
|
||||
length += SessionKey.KEYSIZE_BYTES; // layerKey
|
||||
length += SessionKey.KEYSIZE_BYTES; // ivKey
|
||||
|
||||
if (_optionsCache == null)
|
||||
_optionsCache = DataHelper.toProperties(_options);
|
||||
length += _optionsCache.length;
|
||||
|
||||
length += Hash.HASH_LENGTH; // replyGateway
|
||||
length += 4; // replyTunnel
|
||||
length += SessionTag.BYTE_LENGTH; // replyTag
|
||||
length += SessionKey.KEYSIZE_BYTES; // replyKey
|
||||
length += 4; // nonce
|
||||
if (_certificateCache == null)
|
||||
_certificateCache = _certificate.toByteArray();
|
||||
length += _certificateCache.length;
|
||||
length += DataHelper.BOOLEAN_LENGTH;
|
||||
return length;
|
||||
}
|
||||
|
||||
/** write the message body to the output array, starting at the given index */
|
||||
protected int writeMessageBody(byte data[], int offset) throws I2NPMessageException {
|
||||
if (_nextRouter == null)
|
||||
System.arraycopy(INVALID_HASH.getData(), 0, data, offset, Hash.HASH_LENGTH);
|
||||
else
|
||||
System.arraycopy(_nextRouter.getData(), 0, data, offset, Hash.HASH_LENGTH);
|
||||
offset += Hash.HASH_LENGTH;
|
||||
|
||||
if (_nextTunnelId == null)
|
||||
DataHelper.toLong(data, offset, 4, 0);
|
||||
else
|
||||
DataHelper.toLong(data, offset, 4, _nextTunnelId.getTunnelId());
|
||||
offset += 4;
|
||||
|
||||
DataHelper.toLong(data, offset, 2, _durationSeconds);
|
||||
offset += 2;
|
||||
|
||||
System.arraycopy(_layerKey.getData(), 0, data, offset, SessionKey.KEYSIZE_BYTES);
|
||||
offset += SessionKey.KEYSIZE_BYTES;
|
||||
|
||||
System.arraycopy(_ivKey.getData(), 0, data, offset, SessionKey.KEYSIZE_BYTES);
|
||||
offset += SessionKey.KEYSIZE_BYTES;
|
||||
|
||||
if (_optionsCache == null)
|
||||
_optionsCache = DataHelper.toProperties(_options);
|
||||
System.arraycopy(_optionsCache, 0, data, offset, _optionsCache.length);
|
||||
offset += _optionsCache.length;
|
||||
|
||||
System.arraycopy(_replyGateway.getData(), 0, data, offset, Hash.HASH_LENGTH);
|
||||
offset += Hash.HASH_LENGTH;
|
||||
|
||||
DataHelper.toLong(data, offset, 4, _replyTunnel.getTunnelId());
|
||||
offset += 4;
|
||||
|
||||
System.arraycopy(_replyTag.getData(), 0, data, offset, SessionTag.BYTE_LENGTH);
|
||||
offset += SessionTag.BYTE_LENGTH;
|
||||
|
||||
System.arraycopy(_replyKey.getData(), 0, data, offset, SessionKey.KEYSIZE_BYTES);
|
||||
offset += SessionKey.KEYSIZE_BYTES;
|
||||
|
||||
DataHelper.toLong(data, offset, 4, _nonce);
|
||||
offset += 4;
|
||||
|
||||
if (_certificateCache == null)
|
||||
_certificateCache = _certificate.toByteArray();
|
||||
System.arraycopy(_certificateCache, 0, data, offset, _certificateCache.length);
|
||||
offset += _certificateCache.length;
|
||||
|
||||
DataHelper.toBoolean(data, offset, _isGateway);
|
||||
offset += DataHelper.BOOLEAN_LENGTH;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
byte rv[] = super.toByteArray();
|
||||
if (rv == null)
|
||||
throw new RuntimeException("unable to toByteArray(): " + toString());
|
||||
return rv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getNextRouter()) +
|
||||
DataHelper.hashCode(getNextTunnelId()) +
|
||||
DataHelper.hashCode(getReplyGateway()) +
|
||||
DataHelper.hashCode(getReplyTunnel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof TunnelCreateMessage) ) {
|
||||
TunnelCreateMessage msg = (TunnelCreateMessage)object;
|
||||
return DataHelper.eq(getNextRouter(), msg.getNextRouter()) &&
|
||||
DataHelper.eq(getNextTunnelId(), msg.getNextTunnelId()) &&
|
||||
DataHelper.eq(getReplyGateway(), msg.getReplyGateway()) &&
|
||||
DataHelper.eq(getReplyTunnel(), msg.getReplyTunnel());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[TunnelCreateMessage: ");
|
||||
buf.append("\n\tNext Router: ").append(getNextRouter());
|
||||
buf.append("\n\tNext Tunnel: ").append(getNextTunnelId());
|
||||
buf.append("\n\tReply Tunnel: ").append(getReplyTunnel());
|
||||
buf.append("\n\tReply Peer: ").append(getReplyGateway());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
}
|
@@ -0,0 +1,119 @@
|
||||
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 net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
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 _receiveTunnelId;
|
||||
private int _status;
|
||||
private long _nonce;
|
||||
|
||||
public final static int STATUS_SUCCESS = 0;
|
||||
|
||||
public TunnelCreateStatusMessage(I2PAppContext context) {
|
||||
super(context);
|
||||
setReceiveTunnelId(null);
|
||||
setStatus(-1);
|
||||
setNonce(-1);
|
||||
}
|
||||
|
||||
public TunnelId getReceiveTunnelId() { return _receiveTunnelId; }
|
||||
public void setReceiveTunnelId(TunnelId id) {
|
||||
_receiveTunnelId = id;
|
||||
if ( (id != null) && (id.getTunnelId() <= 0) )
|
||||
throw new IllegalArgumentException("wtf, tunnelId " + id);
|
||||
}
|
||||
|
||||
public int getStatus() { return _status; }
|
||||
public void setStatus(int status) { _status = status; }
|
||||
|
||||
public long getNonce() { return _nonce; }
|
||||
public void setNonce(long nonce) { _nonce = nonce; }
|
||||
|
||||
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
|
||||
int curIndex = offset;
|
||||
|
||||
_receiveTunnelId = new TunnelId(DataHelper.fromLong(data, curIndex, 4));
|
||||
curIndex += 4;
|
||||
|
||||
if (_receiveTunnelId.getTunnelId() <= 0)
|
||||
throw new I2NPMessageException("wtf, negative tunnelId? " + _receiveTunnelId);
|
||||
|
||||
_status = (int)DataHelper.fromLong(data, curIndex, 1);
|
||||
curIndex++;
|
||||
|
||||
_nonce = DataHelper.fromLong(data, curIndex, 4);
|
||||
}
|
||||
|
||||
|
||||
/** calculate the message body's length (not including the header and footer */
|
||||
protected int calculateWrittenLength() {
|
||||
return 4 + 1 + 4; // id + status + nonce
|
||||
}
|
||||
/** write the message body to the output array, starting at the given index */
|
||||
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
|
||||
if ( (_receiveTunnelId == null) || (_nonce <= 0) ) throw new I2NPMessageException("Not enough data to write out");
|
||||
if (_receiveTunnelId.getTunnelId() <= 0) throw new I2NPMessageException("Invalid tunnelId!? " + _receiveTunnelId);
|
||||
|
||||
DataHelper.toLong(out, curIndex, 4, _receiveTunnelId.getTunnelId());
|
||||
curIndex += 4;
|
||||
DataHelper.toLong(out, curIndex, 1, _status);
|
||||
curIndex++;
|
||||
DataHelper.toLong(out, curIndex, 4, _nonce);
|
||||
curIndex += 4;
|
||||
return curIndex;
|
||||
}
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(getReceiveTunnelId()) +
|
||||
getStatus() +
|
||||
(int)getNonce();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if ( (object != null) && (object instanceof TunnelCreateStatusMessage) ) {
|
||||
TunnelCreateStatusMessage msg = (TunnelCreateStatusMessage)object;
|
||||
return DataHelper.eq(getReceiveTunnelId(),msg.getReceiveTunnelId()) &&
|
||||
DataHelper.eq(getNonce(),msg.getNonce()) &&
|
||||
(getStatus() == msg.getStatus());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[TunnelCreateStatusMessage: ");
|
||||
buf.append("\n\tTunnel ID: ").append(getReceiveTunnelId());
|
||||
buf.append("\n\tStatus: ").append(getStatus());
|
||||
buf.append("\n\tNonce: ").append(getNonce());
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
281
router/java/test/junit/net/i2p/router/SSUDemo.java
Normal file
281
router/java/test/junit/net/i2p/router/SSUDemo.java
Normal file
@@ -0,0 +1,281 @@
|
||||
package net.i2p.router;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterIdentity;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.i2np.DatabaseStoreMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.data.i2np.I2NPMessageException;
|
||||
import net.i2p.data.i2np.I2NPMessageImpl;
|
||||
|
||||
/**
|
||||
* Demo of a stripped down router - no tunnels, no netDb, no i2cp, no peer profiling,
|
||||
* just the SSU comm layer, crypto, and associated infrastructure, extended to handle
|
||||
* a new type of message ("FooMessage").
|
||||
*
|
||||
*/
|
||||
public class SSUDemo {
|
||||
RouterContext _us;
|
||||
|
||||
public static void main(String args[]) {
|
||||
SSUDemo demo = new SSUDemo();
|
||||
demo.run();
|
||||
}
|
||||
|
||||
public SSUDemo() {}
|
||||
public void run() {
|
||||
String cfgFile = "router.config";
|
||||
Properties envProps = getEnv();
|
||||
Router r = new Router(cfgFile, envProps);
|
||||
r.runRouter();
|
||||
_us = r.getContext();
|
||||
setupHandlers();
|
||||
// wait for it to warm up a bit
|
||||
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {}
|
||||
// now write out our ident and info
|
||||
RouterInfo myInfo = _us.router().getRouterInfo();
|
||||
storeMyInfo(myInfo);
|
||||
// look for any other peers written to the same directory, and send each
|
||||
// a single Foo message (0x0123), unless they've already contacted us first.
|
||||
// this call never returns
|
||||
loadPeers();
|
||||
}
|
||||
|
||||
private Properties getEnv() {
|
||||
Properties envProps = System.getProperties();
|
||||
// disable the TCP transport, as its deprecated
|
||||
envProps.setProperty("i2np.tcp.disable", "true");
|
||||
// we want SNTP synchronization for replay prevention
|
||||
envProps.setProperty("time.disabled", "false");
|
||||
// allow 127.0.0.1/10.0.0.1/etc (useful for testing). If this is false,
|
||||
// peers who say they're on an invalid IP are shitlisted
|
||||
envProps.setProperty("i2np.udp.allowLocal", "true");
|
||||
// explicit IP+port. at least one router on the net has to have their IP+port
|
||||
// set, since there has to be someone to detect one's IP off. most don't need
|
||||
// to set these though
|
||||
envProps.setProperty("i2np.udp.host", "127.0.0.1");
|
||||
envProps.setProperty("i2np.udp.internalPort", "12000");
|
||||
envProps.setProperty("i2np.udp.port", "12000");
|
||||
// disable I2CP, the netDb, peer testing/profile persistence, and tunnel
|
||||
// creation/management
|
||||
envProps.setProperty("i2p.dummyClientFacade", "true");
|
||||
envProps.setProperty("i2p.dummyNetDb", "true");
|
||||
envProps.setProperty("i2p.dummyPeerManager", "true");
|
||||
envProps.setProperty("i2p.dummyTunnelManager", "true");
|
||||
// set to false if you want to use HMAC-SHA256-128 instead of HMAC-MD5-128 as
|
||||
// the SSU MAC
|
||||
envProps.setProperty("i2p.HMACMD5", "true");
|
||||
// if you're using the HMAC MD5, by default it will use a 32 byte MAC field,
|
||||
// which is a bug, as it doesn't generate the same values as a 16 byte MAC field.
|
||||
// set this to false if you don't want the bug
|
||||
envProps.setProperty("i2p.HMACBrokenSize", "false");
|
||||
// no need to include any stats in the routerInfo we send to people on SSU
|
||||
// session establishment
|
||||
envProps.setProperty("router.publishPeerRankings", "false");
|
||||
// write the logs to ./logs/log-router-*.txt (logger configured with the file
|
||||
// ./logger.config, or another config file specified as
|
||||
// -Dlogger.configLocation=blah)
|
||||
envProps.setProperty("loggerFilenameOverride", "logs/log-router-@.txt");
|
||||
return envProps;
|
||||
}
|
||||
|
||||
private void setupHandlers() {
|
||||
// netDb store is sent on connection establishment, which includes contact info
|
||||
// for the peer. the DBStoreJobBuilder builds a new asynchronous Job to process
|
||||
// each one received (storing it in our in-memory, passive netDb)
|
||||
_us.inNetMessagePool().registerHandlerJobBuilder(DatabaseStoreMessage.MESSAGE_TYPE, new DBStoreJobBuilder());
|
||||
// handle any Foo messages by displaying them on stdout
|
||||
_us.inNetMessagePool().registerHandlerJobBuilder(FooMessage.MESSAGE_TYPE, new FooJobBuilder());
|
||||
}
|
||||
|
||||
/** random place for storing router info files - written as $dir/base64(SHA256(info.getIdentity)) */
|
||||
private File getInfoDir() { return new File("/tmp/ssuDemoInfo/"); }
|
||||
|
||||
private void storeMyInfo(RouterInfo info) {
|
||||
File infoDir = getInfoDir();
|
||||
if (!infoDir.exists())
|
||||
infoDir.mkdirs();
|
||||
FileOutputStream fos = null;
|
||||
File infoFile = new File(infoDir, info.getIdentity().calculateHash().toBase64());
|
||||
try {
|
||||
fos = new FileOutputStream(infoFile);
|
||||
info.writeBytes(fos);
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
} catch (DataFormatException dfe) {
|
||||
dfe.printStackTrace();
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
System.out.println("Our info stored at: " + infoFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
private void loadPeers() {
|
||||
File infoDir = getInfoDir();
|
||||
if (!infoDir.exists())
|
||||
infoDir.mkdirs();
|
||||
while (true) {
|
||||
File peerFiles[] = infoDir.listFiles();
|
||||
if ( (peerFiles != null) && (peerFiles.length > 0) ) {
|
||||
for (int i = 0; i < peerFiles.length; i++) {
|
||||
if (peerFiles[i].isFile() && !peerFiles[i].isHidden()) {
|
||||
if (!_us.routerHash().toBase64().equals(peerFiles[i].getName())) {
|
||||
System.out.println("Reading info: " + peerFiles[i].getAbsolutePath());
|
||||
try {
|
||||
FileInputStream in = new FileInputStream(peerFiles[i]);
|
||||
RouterInfo ri = new RouterInfo();
|
||||
ri.readBytes(in);
|
||||
peerRead(ri);
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("Error reading " + peerFiles[i].getAbsolutePath());
|
||||
ioe.printStackTrace();
|
||||
} catch (DataFormatException dfe) {
|
||||
System.err.println("Corrupt " + peerFiles[i].getAbsolutePath());
|
||||
dfe.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
||||
private void peerRead(RouterInfo ri) {
|
||||
RouterInfo old = _us.netDb().store(ri.getIdentity().calculateHash(), ri);
|
||||
if (old == null)
|
||||
newPeerRead(ri);
|
||||
}
|
||||
|
||||
private void newPeerRead(RouterInfo ri) {
|
||||
OutNetMessage out = new OutNetMessage(_us);
|
||||
// _us.clock() is an ntp synchronized clock. give up on sending this message
|
||||
// if it doesn't get ACKed within the next 10 seconds
|
||||
out.setExpiration(_us.clock().now() + 10*1000);
|
||||
out.setPriority(100);
|
||||
out.setTarget(ri);
|
||||
FooMessage data = new FooMessage(_us, new byte[] { 0x0, 0x1, 0x2, 0x3 });
|
||||
System.out.println("SEND: " + Base64.encode(data.getData()));
|
||||
out.setMessage(data);
|
||||
// job fired if we can't contact them, or if it takes too long to get an ACK
|
||||
out.setOnFailedSendJob(null);
|
||||
// job fired once the transport gets a full ACK of the message
|
||||
out.setOnSendJob(new AfterACK());
|
||||
// queue up the message, establishing a new SSU session if necessary, using
|
||||
// their direct SSU address if they have one, or their indirect SSU addresses
|
||||
// if they don't. If we cannot contact them, we will 'shitlist' their address,
|
||||
// during which time we will not even attempt to send messages to them. We also
|
||||
// drop their netDb info when we shitlist them, in case their info is no longer
|
||||
// correct. Since the netDb is disabled for all meaningful purposes, the SSUDemo
|
||||
// will be responsible for fetching such information.
|
||||
_us.outNetMessagePool().add(out);
|
||||
}
|
||||
|
||||
/** fired if and only if the FooMessage is ACKed before we time out */
|
||||
private class AfterACK extends JobImpl {
|
||||
public AfterACK() { super(_us); }
|
||||
public void runJob() { System.out.println("Foo message sent completely"); }
|
||||
public String getName() { return "After Foo message send"; }
|
||||
}
|
||||
|
||||
////
|
||||
// Foo and netDb store handling below
|
||||
|
||||
/**
|
||||
* Deal with an Foo message received
|
||||
*/
|
||||
private class FooJobBuilder implements HandlerJobBuilder {
|
||||
public FooJobBuilder() {
|
||||
I2NPMessageImpl.registerBuilder(new FooBuilder(), FooMessage.MESSAGE_TYPE);
|
||||
}
|
||||
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
|
||||
return new FooHandleJob(_us, receivedMessage, from, fromHash);
|
||||
}
|
||||
}
|
||||
private class FooHandleJob extends JobImpl {
|
||||
private I2NPMessage _msg;
|
||||
public FooHandleJob(RouterContext ctx, I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
|
||||
super(ctx);
|
||||
_msg = receivedMessage;
|
||||
}
|
||||
public void runJob() {
|
||||
// we know its a FooMessage, since thats the type of message that the handler
|
||||
// is registered as
|
||||
FooMessage m = (FooMessage)_msg;
|
||||
System.out.println("RECV: " + Base64.encode(m.getData()));
|
||||
}
|
||||
public String getName() { return "Handle Foo message"; }
|
||||
}
|
||||
private class FooBuilder implements I2NPMessageImpl.Builder {
|
||||
public I2NPMessage build(I2PAppContext ctx) { return new FooMessage(ctx, null); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Just carry some data...
|
||||
*/
|
||||
class FooMessage extends I2NPMessageImpl {
|
||||
private byte[] _data;
|
||||
public static final int MESSAGE_TYPE = 17;
|
||||
public FooMessage(I2PAppContext ctx, byte data[]) {
|
||||
super(ctx);
|
||||
_data = data;
|
||||
}
|
||||
/** pull the read data off */
|
||||
public byte[] getData() { return _data; }
|
||||
/** specify the payload to be sent */
|
||||
public void setData(byte data[]) { _data = data; }
|
||||
|
||||
public int getType() { return MESSAGE_TYPE; }
|
||||
protected int calculateWrittenLength() { return _data.length; }
|
||||
public void readMessage(byte[] data, int offset, int dataSize, int type) throws I2NPMessageException, IOException {
|
||||
_data = new byte[dataSize];
|
||||
System.arraycopy(data, offset, _data, 0, dataSize);
|
||||
}
|
||||
|
||||
protected int writeMessageBody(byte[] out, int curIndex) throws I2NPMessageException {
|
||||
System.arraycopy(_data, 0, out, curIndex, _data.length);
|
||||
return curIndex + _data.length;
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
// netDb store handling below
|
||||
|
||||
/**
|
||||
* Handle any netDb stores from the peer - they send us their netDb as part of
|
||||
* their SSU establishment (and we send them ours).
|
||||
*/
|
||||
private class DBStoreJobBuilder implements HandlerJobBuilder {
|
||||
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
|
||||
return new HandleJob(_us, receivedMessage, from, fromHash);
|
||||
}
|
||||
}
|
||||
private class HandleJob extends JobImpl {
|
||||
private I2NPMessage _msg;
|
||||
public HandleJob(RouterContext ctx, I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
|
||||
super(ctx);
|
||||
_msg = receivedMessage;
|
||||
}
|
||||
public void runJob() {
|
||||
// we know its a DatabaseStoreMessage, since thats the type of message that the handler
|
||||
// is registered as
|
||||
DatabaseStoreMessage m = (DatabaseStoreMessage)_msg;
|
||||
try {
|
||||
_us.netDb().store(m.getKey(), m.getRouterInfo());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
iae.printStackTrace();
|
||||
}
|
||||
}
|
||||
public String getName() { return "Handle netDb store"; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,118 @@
|
||||
package net.i2p.router;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.i2p.router.transport.BandwidthLimitedInputStream;
|
||||
import net.i2p.util.HTTPSendData;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Job that, if its allowed to, will submit the data gathered by the MessageHistory
|
||||
* component to some URL so that the network can be debugged more easily. By default
|
||||
* it does not submit any data or touch the message history file, but if the router
|
||||
* has the line "router.submitHistory=true", it will send the file that the
|
||||
* MessageHistory component is configured to write to once an hour, post it to
|
||||
* http://i2p.net/cgi-bin/submitMessageHistory, and then delete that file
|
||||
* locally. This should only be used if the MessageHistory component is configured to
|
||||
* gather data (via "router.keepHistory=true").
|
||||
*
|
||||
*/
|
||||
public class SubmitMessageHistoryJob extends JobImpl {
|
||||
private Log _log;
|
||||
|
||||
/** default submitting data every hour */
|
||||
private final static long DEFAULT_REQUEUE_DELAY = 60*60*1000;
|
||||
/**
|
||||
* router config param for whether we want to autosubmit (and delete) the
|
||||
* history data managed by MessageHistory
|
||||
*/
|
||||
public final static String PARAM_SUBMIT_DATA = "router.submitHistory";
|
||||
/** default value for whether we autosubmit the data */
|
||||
public final static boolean DEFAULT_SUBMIT_DATA = true;
|
||||
/** where the data should be submitted to (via HTTP POST) */
|
||||
public final static String PARAM_SUBMIT_URL = "router.submitHistoryURL";
|
||||
/** default location */
|
||||
public final static String DEFAULT_SUBMIT_URL = "http://i2p.net/cgi-bin/submitMessageHistory";
|
||||
|
||||
public SubmitMessageHistoryJob(RouterContext context) {
|
||||
super(context);
|
||||
_log = context.logManager().getLog(SubmitMessageHistoryJob.class);
|
||||
}
|
||||
|
||||
public void runJob() {
|
||||
if (shouldSubmit()) {
|
||||
submit();
|
||||
} else {
|
||||
_log.debug("Not submitting data");
|
||||
// if we didn't submit we can just requeue
|
||||
requeue(getRequeueDelay());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We don't want this to be run within the jobqueue itself, so fire off a new thread
|
||||
* to do the actual submission, enqueueing a new submit job when its done
|
||||
*/
|
||||
private void submit() {
|
||||
I2PThread t = new I2PThread(new Runnable() {
|
||||
public void run() {
|
||||
_log.debug("Submitting data");
|
||||
getContext().messageHistory().setPauseFlushes(true);
|
||||
String filename = getContext().messageHistory().getFilename();
|
||||
send(filename);
|
||||
getContext().messageHistory().setPauseFlushes(false);
|
||||
Job job = new SubmitMessageHistoryJob(getContext());
|
||||
job.getTiming().setStartAfter(getContext().clock().now() + getRequeueDelay());
|
||||
getContext().jobQueue().addJob(job);
|
||||
}
|
||||
});
|
||||
t.setName("SubmitData");
|
||||
t.setPriority(I2PThread.MIN_PRIORITY);
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void send(String filename) {
|
||||
String url = getURL();
|
||||
try {
|
||||
File dataFile = new File(filename);
|
||||
if (!dataFile.exists() || !dataFile.canRead()) {
|
||||
_log.warn("Unable to read the message data file [" + dataFile.getAbsolutePath() + "]");
|
||||
return;
|
||||
}
|
||||
long size = dataFile.length();
|
||||
FileInputStream fin = new FileInputStream(dataFile);
|
||||
BandwidthLimitedInputStream in = new BandwidthLimitedInputStream(getContext(), fin, null, true);
|
||||
boolean sent = HTTPSendData.postData(url, size, in);
|
||||
fin.close();
|
||||
boolean deleted = dataFile.delete();
|
||||
_log.debug("Submitted " + size + " bytes? " + sent + " and deleted? " + deleted);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error sending the data", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
private String getURL() {
|
||||
String str = getContext().router().getConfigSetting(PARAM_SUBMIT_URL);
|
||||
if ( (str == null) || (str.trim().length() <= 0) )
|
||||
return DEFAULT_SUBMIT_URL;
|
||||
else
|
||||
return str.trim();
|
||||
}
|
||||
|
||||
private boolean shouldSubmit() {
|
||||
String str = getContext().router().getConfigSetting(PARAM_SUBMIT_DATA);
|
||||
if (str == null) {
|
||||
_log.debug("History submit config not specified [" + PARAM_SUBMIT_DATA + "], default = " + DEFAULT_SUBMIT_DATA);
|
||||
return DEFAULT_SUBMIT_DATA;
|
||||
} else {
|
||||
_log.debug("History submit config specified [" + str + "]");
|
||||
}
|
||||
return Boolean.TRUE.toString().equals(str);
|
||||
}
|
||||
private long getRequeueDelay() { return DEFAULT_REQUEUE_DELAY; }
|
||||
public String getName() { return "Submit Message History"; }
|
||||
}
|
@@ -0,0 +1,212 @@
|
||||
package net.i2p.router.message;
|
||||
/*
|
||||
* 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.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.i2np.DeliveryInstructions;
|
||||
import net.i2p.data.i2np.DeliveryStatusMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.router.Job;
|
||||
import net.i2p.router.JobImpl;
|
||||
import net.i2p.router.MessageSelector;
|
||||
import net.i2p.router.ReplyJob;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.peermanager.PeerProfile;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Build a test message that will be sent to the target to make sure they're alive.
|
||||
* Once that is verified, onSendJob is enqueued. If their reachability isn't
|
||||
* known (or they're unreachable) within timeoutMs, onSendFailedJob is enqueued.
|
||||
* The test message is sent at the specified priority.
|
||||
*
|
||||
*/
|
||||
public class BuildTestMessageJob extends JobImpl {
|
||||
private Log _log;
|
||||
private RouterInfo _target;
|
||||
private Hash _replyTo;
|
||||
private Job _onSend;
|
||||
private Job _onSendFailed;
|
||||
private long _timeoutMs;
|
||||
private int _priority;
|
||||
private long _testMessageKey;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param target router being tested
|
||||
* @param onSendJob after the ping is successful
|
||||
* @param onSendFailedJob after the ping fails or times out
|
||||
* @param timeoutMs how long to wait before timing out
|
||||
* @param priority how high priority to send this test
|
||||
*/
|
||||
public BuildTestMessageJob(RouterContext ctx, RouterInfo target, Hash replyTo,
|
||||
Job onSendJob, Job onSendFailedJob, long timeoutMs, int priority) {
|
||||
super(ctx);
|
||||
_log = ctx.logManager().getLog(BuildTestMessageJob.class);
|
||||
_target = target;
|
||||
_replyTo = replyTo;
|
||||
_onSend = onSendJob;
|
||||
_onSendFailed = onSendFailedJob;
|
||||
_timeoutMs = timeoutMs;
|
||||
_priority = priority;
|
||||
_testMessageKey = -1;
|
||||
}
|
||||
|
||||
public String getName() { return "Build Test Message"; }
|
||||
|
||||
public void runJob() {
|
||||
if (alreadyKnownReachable()) {
|
||||
getContext().jobQueue().addJob(_onSend);
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a test message - build a garlic with a DeliveryStatusMessage that
|
||||
// first goes to the peer then back to us.
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Building garlic message to test " + _target.getIdentity().getHash().toBase64());
|
||||
GarlicConfig config = buildGarlicCloveConfig();
|
||||
// TODO: make the last params on this specify the correct sessionKey and tags used
|
||||
ReplyJob replyJob = new JobReplyJob(getContext(), _onSend, config.getRecipient().getIdentity().getPublicKey(), config.getId(), null, new HashSet());
|
||||
MessageSelector sel = buildMessageSelector();
|
||||
SendGarlicJob job = new SendGarlicJob(getContext(), config, null, _onSendFailed, replyJob, _onSendFailed, _timeoutMs, _priority, sel);
|
||||
getContext().jobQueue().addJob(job);
|
||||
}
|
||||
|
||||
private boolean alreadyKnownReachable() {
|
||||
PeerProfile profile = getContext().profileOrganizer().getProfile(_target.getIdentity().getHash());
|
||||
if ( (profile == null) || (!profile.getIsActive()) )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
private MessageSelector buildMessageSelector() {
|
||||
return new TestMessageSelector(_testMessageKey, _timeoutMs + getContext().clock().now());
|
||||
}
|
||||
|
||||
private GarlicConfig buildGarlicCloveConfig() {
|
||||
_testMessageKey = getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Test message key: " + _testMessageKey);
|
||||
GarlicConfig config = new GarlicConfig();
|
||||
|
||||
PayloadGarlicConfig ackClove = buildAckClove();
|
||||
config.addClove(ackClove);
|
||||
|
||||
DeliveryInstructions instructions = new DeliveryInstructions();
|
||||
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_ROUTER);
|
||||
instructions.setDelayRequested(false);
|
||||
instructions.setDelaySeconds(0);
|
||||
instructions.setEncrypted(false);
|
||||
instructions.setEncryptionKey(null);
|
||||
instructions.setRouter(_target.getIdentity().getHash());
|
||||
instructions.setTunnelId(null);
|
||||
|
||||
config.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
|
||||
config.setDeliveryInstructions(instructions);
|
||||
config.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
config.setExpiration(_timeoutMs+getContext().clock().now()+2*Router.CLOCK_FUDGE_FACTOR);
|
||||
config.setRecipient(_target);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a clove that sends a DeliveryStatusMessage to us
|
||||
*/
|
||||
private PayloadGarlicConfig buildAckClove() {
|
||||
PayloadGarlicConfig ackClove = new PayloadGarlicConfig();
|
||||
|
||||
DeliveryInstructions ackInstructions = new DeliveryInstructions();
|
||||
ackInstructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_ROUTER);
|
||||
ackInstructions.setRouter(_replyTo); // yikes!
|
||||
ackInstructions.setDelayRequested(false);
|
||||
ackInstructions.setDelaySeconds(0);
|
||||
ackInstructions.setEncrypted(false);
|
||||
|
||||
DeliveryStatusMessage msg = new DeliveryStatusMessage(getContext());
|
||||
msg.setArrival(getContext().clock().now());
|
||||
msg.setMessageId(_testMessageKey);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Delivery status message key: " + _testMessageKey + " arrival: " + msg.getArrival());
|
||||
|
||||
ackClove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
|
||||
ackClove.setDeliveryInstructions(ackInstructions);
|
||||
ackClove.setExpiration(_timeoutMs+getContext().clock().now());
|
||||
ackClove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
ackClove.setPayload(msg);
|
||||
ackClove.setRecipient(_target);
|
||||
|
||||
return ackClove;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search inbound messages for delivery status messages with our key
|
||||
*/
|
||||
private final static class TestMessageSelector implements MessageSelector {
|
||||
private long _testMessageKey;
|
||||
private long _timeout;
|
||||
public TestMessageSelector(long key, long timeout) {
|
||||
_testMessageKey = key;
|
||||
_timeout = timeout;
|
||||
}
|
||||
public boolean continueMatching() { return false; }
|
||||
public long getExpiration() { return _timeout; }
|
||||
public boolean isMatch(I2NPMessage inMsg) {
|
||||
if (inMsg.getType() == DeliveryStatusMessage.MESSAGE_TYPE) {
|
||||
return ((DeliveryStatusMessage)inMsg).getMessageId() == _testMessageKey;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On reply, fire off the specified job
|
||||
*
|
||||
*/
|
||||
private static final class JobReplyJob extends JobImpl implements ReplyJob {
|
||||
private Job _job;
|
||||
private PublicKey _target;
|
||||
private long _msgId;
|
||||
private Set _sessionTagsDelivered;
|
||||
private SessionKey _keyDelivered;
|
||||
public JobReplyJob(RouterContext ctx, Job job, PublicKey target, long msgId, SessionKey keyUsed, Set tagsDelivered) {
|
||||
super(ctx);
|
||||
_job = job;
|
||||
_target = target;
|
||||
_msgId = msgId;
|
||||
_keyDelivered = keyUsed;
|
||||
_sessionTagsDelivered = tagsDelivered;
|
||||
}
|
||||
public String getName() { return "Reply To Test Message Received"; }
|
||||
public void runJob() {
|
||||
if ( (_keyDelivered != null) &&
|
||||
(_sessionTagsDelivered != null) &&
|
||||
(_sessionTagsDelivered.size() > 0) )
|
||||
getContext().sessionKeyManager().tagsDelivered(_target, _keyDelivered, _sessionTagsDelivered);
|
||||
|
||||
getContext().jobQueue().addJob(_job);
|
||||
}
|
||||
|
||||
public void setMessage(I2NPMessage message) {
|
||||
// ignored, this is just a ping
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,136 @@
|
||||
package net.i2p.router.message;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.Payload;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.router.ClientMessage;
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
/**
|
||||
* Good ol' fashioned struct with the send status
|
||||
*
|
||||
*/
|
||||
class OutboundClientMessageStatus {
|
||||
private RouterContext _context;
|
||||
private ClientMessage _msg;
|
||||
private PayloadGarlicConfig _clove;
|
||||
private LeaseSet _leaseSet;
|
||||
private final Set _sent;
|
||||
private int _numLookups;
|
||||
private boolean _success;
|
||||
private boolean _failure;
|
||||
private long _start;
|
||||
private int _previousSent;
|
||||
|
||||
public OutboundClientMessageStatus(RouterContext ctx, ClientMessage msg) {
|
||||
_context = ctx;
|
||||
_msg = msg;
|
||||
_clove = null;
|
||||
_leaseSet = null;
|
||||
_sent = new HashSet(4);
|
||||
_success = false;
|
||||
_failure = false;
|
||||
_numLookups = 0;
|
||||
_previousSent = 0;
|
||||
_start = ctx.clock().now();
|
||||
}
|
||||
|
||||
/** raw payload */
|
||||
public Payload getPayload() { return _msg.getPayload(); }
|
||||
/** clove, if we've built it */
|
||||
public PayloadGarlicConfig getClove() { return _clove; }
|
||||
public void setClove(PayloadGarlicConfig clove) { _clove = clove; }
|
||||
public ClientMessage getMessage() { return _msg; }
|
||||
/** date we started the process on */
|
||||
public long getStart() { return _start; }
|
||||
|
||||
public int getNumLookups() { return _numLookups; }
|
||||
public void incrementLookups() { _numLookups++; }
|
||||
public void clearAlreadySent() {
|
||||
synchronized (_sent) {
|
||||
_previousSent += _sent.size();
|
||||
_sent.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/** who sent the message? */
|
||||
public Destination getFrom() { return _msg.getFromDestination(); }
|
||||
/** who is the message going to? */
|
||||
public Destination getTo() { return _msg.getDestination(); }
|
||||
/** what is the target's current leaseSet (or null if we don't know yet) */
|
||||
public LeaseSet getLeaseSet() { return _leaseSet; }
|
||||
public void setLeaseSet(LeaseSet ls) { _leaseSet = ls; }
|
||||
/** have we already sent the message down this tunnel? */
|
||||
public boolean alreadySent(Hash gateway, TunnelId tunnelId) {
|
||||
Tunnel t = new Tunnel(gateway, tunnelId);
|
||||
synchronized (_sent) {
|
||||
return _sent.contains(t);
|
||||
}
|
||||
}
|
||||
public void sent(Hash gateway, TunnelId tunnelId) {
|
||||
Tunnel t = new Tunnel(gateway, tunnelId);
|
||||
synchronized (_sent) {
|
||||
_sent.add(t);
|
||||
}
|
||||
}
|
||||
/** how many messages have we sent through various leases? */
|
||||
public int getNumSent() {
|
||||
synchronized (_sent) {
|
||||
return _sent.size() + _previousSent;
|
||||
}
|
||||
}
|
||||
/** did we totally fail? */
|
||||
public boolean getFailure() { return _failure; }
|
||||
/** we failed. returns true if we had already failed before */
|
||||
public boolean failed() {
|
||||
boolean already = _failure;
|
||||
_failure = true;
|
||||
return already;
|
||||
}
|
||||
/** have we totally succeeded? */
|
||||
public boolean getSuccess() { return _success; }
|
||||
/** we succeeded. returns true if we had already succeeded before */
|
||||
public boolean success() {
|
||||
boolean already = _success;
|
||||
_success = true;
|
||||
return already;
|
||||
}
|
||||
|
||||
/** represent a unique tunnel at any given time */
|
||||
private class Tunnel {
|
||||
private Hash _gateway;
|
||||
private TunnelId _tunnel;
|
||||
|
||||
public Tunnel(Hash tunnelGateway, TunnelId tunnel) {
|
||||
_gateway = tunnelGateway;
|
||||
_tunnel = tunnel;
|
||||
}
|
||||
|
||||
public Hash getGateway() { return _gateway; }
|
||||
public TunnelId getTunnel() { return _tunnel; }
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int rv = 0;
|
||||
if (_gateway != null)
|
||||
rv += _gateway.hashCode();
|
||||
if (_tunnel != null)
|
||||
rv += 7*_tunnel.getTunnelId();
|
||||
return rv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) return false;
|
||||
if (o.getClass() != Tunnel.class) return false;
|
||||
Tunnel t = (Tunnel)o;
|
||||
return (getTunnel() == t.getTunnel()) &&
|
||||
getGateway().equals(t.getGateway());
|
||||
}
|
||||
}
|
||||
}
|
125
router/java/test/junit/net/i2p/router/message/SendGarlicJob.java
Normal file
125
router/java/test/junit/net/i2p/router/message/SendGarlicJob.java
Normal file
@@ -0,0 +1,125 @@
|
||||
package net.i2p.router.message;
|
||||
/*
|
||||
* 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.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.i2np.GarlicMessage;
|
||||
import net.i2p.router.Job;
|
||||
import net.i2p.router.JobImpl;
|
||||
import net.i2p.router.MessageSelector;
|
||||
import net.i2p.router.OutNetMessage;
|
||||
import net.i2p.router.ReplyJob;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Build a garlic message from config, encrypt it, and enqueue it for delivery.
|
||||
*
|
||||
*/
|
||||
public class SendGarlicJob extends JobImpl {
|
||||
private Log _log;
|
||||
//private RouterInfo _target;
|
||||
private GarlicConfig _config;
|
||||
private Job _onSend;
|
||||
private Job _onSendFailed;
|
||||
private ReplyJob _onReply;
|
||||
private Job _onReplyFailed;
|
||||
private long _timeoutMs;
|
||||
private int _priority;
|
||||
private MessageSelector _replySelector;
|
||||
private GarlicMessage _message;
|
||||
private SessionKey _wrappedKey;
|
||||
private Set _wrappedTags;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param config ???
|
||||
* @param onSend after the ping is successful
|
||||
* @param onSendFailed after the ping fails or times out
|
||||
* @param onReply ???
|
||||
* @param onReplyFailed ???
|
||||
* @param timeoutMs how long to wait before timing out
|
||||
* @param priority how high priority to send this test
|
||||
* @param replySelector ???
|
||||
*/
|
||||
public SendGarlicJob(RouterContext ctx, GarlicConfig config, Job onSend, Job onSendFailed, ReplyJob onReply, Job onReplyFailed, long timeoutMs, int priority, MessageSelector replySelector) {
|
||||
this(ctx, config, onSend, onSendFailed, onReply, onReplyFailed, timeoutMs, priority, replySelector, new SessionKey(), new HashSet());
|
||||
}
|
||||
public SendGarlicJob(RouterContext ctx, GarlicConfig config, Job onSend, Job onSendFailed, ReplyJob onReply, Job onReplyFailed, long timeoutMs, int priority, MessageSelector replySelector, SessionKey wrappedKey, Set wrappedTags) {
|
||||
super(ctx);
|
||||
_log = ctx.logManager().getLog(SendGarlicJob.class);
|
||||
if (config == null) throw new IllegalArgumentException("No config specified");
|
||||
if (config.getRecipient() == null) throw new IllegalArgumentException("No recipient in the config");
|
||||
//_target = target;
|
||||
_config = config;
|
||||
_onSend = onSend;
|
||||
_onSendFailed = onSendFailed;
|
||||
_onReply = onReply;
|
||||
_onReplyFailed = onReplyFailed;
|
||||
_timeoutMs = timeoutMs;
|
||||
_priority = priority;
|
||||
_replySelector = replySelector;
|
||||
_message = null;
|
||||
_wrappedKey = wrappedKey;
|
||||
_wrappedTags = wrappedTags;
|
||||
}
|
||||
|
||||
public String getName() { return "Build Garlic Message"; }
|
||||
|
||||
public void runJob() {
|
||||
long before = getContext().clock().now();
|
||||
_message = GarlicMessageBuilder.buildMessage(getContext(), _config, _wrappedKey, _wrappedTags);
|
||||
long after = getContext().clock().now();
|
||||
if ( (after - before) > 1000) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Building the garlic took too long [" + (after-before)+" ms]");
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Building the garlic was fast! " + (after - before) + " ms");
|
||||
}
|
||||
getContext().jobQueue().addJob(new SendJob(getContext()));
|
||||
}
|
||||
|
||||
private class SendJob extends JobImpl {
|
||||
public SendJob(RouterContext enclosingContext) {
|
||||
super(enclosingContext);
|
||||
}
|
||||
public String getName() { return "Send Built Garlic Message"; }
|
||||
public void runJob() {
|
||||
if (_config.getRecipient() != null)
|
||||
_log.info("sending garlic to recipient " + _config.getRecipient().getIdentity().getHash().toBase64());
|
||||
else
|
||||
_log.info("sending garlic to public key " + _config.getRecipientPublicKey());
|
||||
sendGarlic();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendGarlic() {
|
||||
OutNetMessage msg = new OutNetMessage(getContext());
|
||||
long when = _message.getMessageExpiration(); // + Router.CLOCK_FUDGE_FACTOR;
|
||||
msg.setExpiration(when);
|
||||
msg.setMessage(_message);
|
||||
msg.setOnFailedReplyJob(_onReplyFailed);
|
||||
msg.setOnFailedSendJob(_onSendFailed);
|
||||
msg.setOnReplyJob(_onReply);
|
||||
msg.setOnSendJob(_onSend);
|
||||
msg.setPriority(_priority);
|
||||
msg.setReplySelector(_replySelector);
|
||||
msg.setTarget(_config.getRecipient());
|
||||
//_log.info("Sending garlic message to [" + _config.getRecipient() + "] encrypted with " + _config.getRecipientPublicKey() + " or " + _config.getRecipient().getIdentity().getPublicKey());
|
||||
//_log.debug("Garlic config data:\n" + _config);
|
||||
//msg.setTarget(_target);
|
||||
getContext().outNetMessagePool().add(msg);
|
||||
_log.debug("Garlic message added to outbound network message pool");
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,77 @@
|
||||
package net.i2p.router.networkdb.kademlia;
|
||||
/*
|
||||
* 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 junit.framework.TestCase;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.RandomSource;
|
||||
|
||||
/**
|
||||
* Test KBucketImpl
|
||||
*
|
||||
* @author comwiz
|
||||
*/
|
||||
|
||||
public class KBucketImplTest extends TestCase{
|
||||
private I2PAppContext context;
|
||||
|
||||
public void setUp(){
|
||||
context = I2PAppContext.getGlobalContext();
|
||||
}
|
||||
|
||||
public void testLimits() {
|
||||
int low = 0;
|
||||
int high = 4;
|
||||
|
||||
KBucketImpl bucket = new KBucketImpl(I2PAppContext.getGlobalContext(), Hash.FAKE_HASH);
|
||||
bucket.setRange(low, high);
|
||||
Hash lowerBoundKey = bucket.getRangeBeginKey();
|
||||
Hash upperBoundKey = bucket.getRangeEndKey();
|
||||
assertTrue(bucket.shouldContain(lowerBoundKey));//
|
||||
assertTrue(bucket.shouldContain(upperBoundKey));
|
||||
|
||||
}
|
||||
|
||||
public void testRand() {
|
||||
int low = 1;
|
||||
int high = 2000;
|
||||
|
||||
Hash local = Hash.FAKE_HASH;
|
||||
local.prepareCache();
|
||||
KBucketImpl bucket = new KBucketImpl(I2PAppContext.getGlobalContext(), local);
|
||||
bucket.setRange(low, high);
|
||||
Hash lowerBoundKey = bucket.getRangeBeginKey();
|
||||
Hash upperBoundKey = bucket.getRangeEndKey();
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
Hash rnd = bucket.generateRandomKey();
|
||||
assertTrue(bucket.shouldContain(rnd));//
|
||||
}
|
||||
}
|
||||
|
||||
public void testRand2() {
|
||||
int low = 1;
|
||||
int high = 2000;
|
||||
|
||||
byte hash[] = new byte[Hash.HASH_LENGTH];
|
||||
RandomSource.getInstance().nextBytes(hash);
|
||||
Hash local = new Hash(hash);
|
||||
local.prepareCache();
|
||||
KBucketImpl bucket = new KBucketImpl(I2PAppContext.getGlobalContext(), local);
|
||||
bucket.setRange(low, high);
|
||||
Hash lowerBoundKey = bucket.getRangeBeginKey();
|
||||
Hash upperBoundKey = bucket.getRangeEndKey();
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
Hash rnd = bucket.generateRandomKey();
|
||||
assertTrue(bucket.shouldContain(rnd));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package net.i2p.router.networkdb.kademlia;
|
||||
/*
|
||||
* 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 junit.framework.TestCase;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
/**
|
||||
* Test KBucketSet
|
||||
*
|
||||
* @author comwiz
|
||||
*/
|
||||
|
||||
public class KBucketSetTest extends TestCase{
|
||||
private I2PAppContext context;
|
||||
private KBucketSet set;
|
||||
|
||||
public void setUp(){
|
||||
context = I2PAppContext.getGlobalContext();
|
||||
set = new KBucketSet(context, Hash.FAKE_HASH);
|
||||
}
|
||||
|
||||
public void testRandom(){
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
byte val[] = new byte[Hash.HASH_LENGTH];
|
||||
context.random().nextBytes(val);
|
||||
assertTrue(set.add(new Hash(val)));
|
||||
}
|
||||
}
|
||||
|
||||
public void testSelf() {
|
||||
assertTrue(set.add(Hash.FAKE_HASH));
|
||||
}
|
||||
}
|
@@ -0,0 +1,137 @@
|
||||
package net.i2p.router.networkdb.kademlia;
|
||||
/*
|
||||
* 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.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.crypto.KeyGenerator;
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.data.RouterIdentity;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.util.Clock;
|
||||
|
||||
public class RouterGenerator {
|
||||
public static void main(String args[]) {
|
||||
RouterGenerator gen = new RouterGenerator();
|
||||
switch (args.length) {
|
||||
case 0:
|
||||
gen.createRouters(10000, "dummyRouters");
|
||||
break;
|
||||
case 1:
|
||||
gen.createRouters(10000, args[0]);
|
||||
break;
|
||||
case 2:
|
||||
try { gen.createRouters(Integer.parseInt(args[1]), args[0]); } catch (NumberFormatException nfe) { nfe.printStackTrace(); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void createRouters(int numRouters, String outDir) {
|
||||
File dir = new File(outDir);
|
||||
if (!dir.exists())
|
||||
dir.mkdirs();
|
||||
int numSuccess = 0;
|
||||
for (int i = 1; numSuccess < numRouters; i++) {
|
||||
RouterInfo ri = createRouterInfo(i);
|
||||
String hash = ri.getIdentity().getHash().toBase64();
|
||||
if (!hash.startsWith("fwI")) {
|
||||
System.out.print(".");
|
||||
if ( (i % 100) == 0) System.out.println();
|
||||
continue;
|
||||
}
|
||||
|
||||
System.out.println("Router " + i + " created: \t" + hash);
|
||||
numSuccess++;
|
||||
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(new File(dir, "routerInfo-" + hash + ".dat"));
|
||||
ri.writeBytes(fos);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error writing router - " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return;
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static PublicKey pubkey = null;
|
||||
private static PrivateKey privkey = null;
|
||||
private static SigningPublicKey signingPubKey = null;
|
||||
private static SigningPrivateKey signingPrivKey = null;
|
||||
private static Object keypair[] = KeyGenerator.getInstance().generatePKIKeypair();
|
||||
private static Object signingKeypair[] = KeyGenerator.getInstance().generateSigningKeypair();
|
||||
|
||||
static {
|
||||
pubkey = (PublicKey)keypair[0];
|
||||
privkey = (PrivateKey)keypair[1];
|
||||
signingPubKey = (SigningPublicKey)signingKeypair[0];
|
||||
signingPrivKey = (SigningPrivateKey)signingKeypair[1];
|
||||
}
|
||||
|
||||
|
||||
static RouterInfo createRouterInfo(int num) {
|
||||
RouterInfo info = new RouterInfo();
|
||||
try {
|
||||
info.setAddresses(createAddresses(num));
|
||||
// not necessary, in constructor
|
||||
//info.setOptions(new Properties());
|
||||
//info.setPeers(new HashSet());
|
||||
info.setPublished(Clock.getInstance().now());
|
||||
RouterIdentity ident = new RouterIdentity();
|
||||
BigInteger bv = new BigInteger(""+num);
|
||||
Certificate cert = new Certificate(Certificate.CERTIFICATE_TYPE_NULL, bv.toByteArray());
|
||||
ident.setCertificate(cert);
|
||||
ident.setPublicKey(pubkey);
|
||||
ident.setSigningPublicKey(signingPubKey);
|
||||
info.setIdentity(ident);
|
||||
|
||||
info.sign(signingPrivKey);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error building router " + num + ": " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
static Set createAddresses(int num) {
|
||||
Set addresses = new HashSet();
|
||||
RouterAddress addr = createTCPAddress(num);
|
||||
if (addr != null)
|
||||
addresses.add(addr);
|
||||
return addresses;
|
||||
}
|
||||
|
||||
private static RouterAddress createTCPAddress(int num) {
|
||||
RouterAddress addr = new RouterAddress();
|
||||
addr.setCost(10);
|
||||
addr.setExpiration(null);
|
||||
Properties props = new Properties();
|
||||
String name = "blah.random.host.org";
|
||||
String port = "" + (1024+num);
|
||||
props.setProperty("host", name);
|
||||
props.setProperty("port", port);
|
||||
addr.setOptions(props);
|
||||
addr.setTransportStyle("TCP");
|
||||
return addr;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,120 @@
|
||||
package net.i2p.router.transport;
|
||||
/*
|
||||
* 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.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.i2p.data.RouterIdentity;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class BandwidthLimitedInputStream extends FilterInputStream {
|
||||
private Log _log;
|
||||
private RouterIdentity _peer;
|
||||
private String _peerSource;
|
||||
private RouterContext _context;
|
||||
private boolean _pullFromOutbound;
|
||||
private FIFOBandwidthLimiter.Request _currentRequest;
|
||||
|
||||
public BandwidthLimitedInputStream(RouterContext context, InputStream source, RouterIdentity peer) {
|
||||
this(context, source, peer, false);
|
||||
}
|
||||
/**
|
||||
* @param pullFromOutbound even though this is an input stream, if this is true, use the
|
||||
* context's outbound bandwidth limiter queue for delays
|
||||
*/
|
||||
public BandwidthLimitedInputStream(RouterContext context, InputStream source, RouterIdentity peer, boolean pullFromOutbound) {
|
||||
super(source);
|
||||
_context = context;
|
||||
_peer = peer;
|
||||
if (peer != null)
|
||||
_peerSource = peer.getHash().toBase64();
|
||||
_pullFromOutbound = pullFromOutbound;
|
||||
_log = context.logManager().getLog(BandwidthLimitedInputStream.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (_pullFromOutbound)
|
||||
_currentRequest = _context.bandwidthLimiter().requestOutbound(1, _peerSource);
|
||||
else
|
||||
_currentRequest = _context.bandwidthLimiter().requestInbound(1, _peerSource);
|
||||
|
||||
// since its only a single byte, we dont need to loop
|
||||
// or check how much was allocated
|
||||
_currentRequest.waitForNextAllocation();
|
||||
synchronized (this) {
|
||||
_currentRequest = null;
|
||||
}
|
||||
return in.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte dest[]) throws IOException {
|
||||
return read(dest, 0, dest.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte dest[], int off, int len) throws IOException {
|
||||
int read = in.read(dest, off, len);
|
||||
if (read == -1) return -1;
|
||||
|
||||
if (_pullFromOutbound)
|
||||
_currentRequest = _context.bandwidthLimiter().requestOutbound(read, _peerSource);
|
||||
else
|
||||
_currentRequest = _context.bandwidthLimiter().requestInbound(read, _peerSource);
|
||||
|
||||
while ( (_currentRequest.getPendingInboundRequested() > 0) ||
|
||||
(_currentRequest.getPendingOutboundRequested() > 0) ) {
|
||||
// we still haven't been authorized for everything, keep on waiting
|
||||
_currentRequest.waitForNextAllocation();
|
||||
if (_currentRequest.getAborted()) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Request aborted while trying to read " + len + " (actually read " + read + ")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
synchronized (this) {
|
||||
_currentRequest = null;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
@Override
|
||||
public long skip(long numBytes) throws IOException {
|
||||
long skip = in.skip(numBytes);
|
||||
|
||||
if (_pullFromOutbound)
|
||||
_currentRequest = _context.bandwidthLimiter().requestOutbound((int)skip, _peerSource);
|
||||
else
|
||||
_currentRequest = _context.bandwidthLimiter().requestInbound((int)skip, _peerSource);
|
||||
|
||||
while ( (_currentRequest.getPendingInboundRequested() > 0) ||
|
||||
(_currentRequest.getPendingOutboundRequested() > 0) ) {
|
||||
// we still haven't been authorized for everything, keep on waiting
|
||||
_currentRequest.waitForNextAllocation();
|
||||
if (_currentRequest.getAborted()) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Request aborted while trying to skip " + numBytes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return skip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
synchronized (this) {
|
||||
if (_currentRequest != null)
|
||||
_currentRequest.abort();
|
||||
}
|
||||
super.close();
|
||||
}
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
package net.i2p.router.transport;
|
||||
/*
|
||||
* 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.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.i2p.data.RouterIdentity;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class BandwidthLimitedOutputStream extends FilterOutputStream {
|
||||
private RouterIdentity _peer;
|
||||
private String _peerTarget;
|
||||
private RouterContext _context;
|
||||
private Log _log;
|
||||
private FIFOBandwidthLimiter.Request _currentRequest;
|
||||
|
||||
public BandwidthLimitedOutputStream(RouterContext context, OutputStream source, RouterIdentity peer) {
|
||||
super(source);
|
||||
_context = context;
|
||||
_peer = peer;
|
||||
if (peer != null)
|
||||
_peerTarget = peer.getHash().toBase64();
|
||||
else
|
||||
_peerTarget = "unknown";
|
||||
_log = context.logManager().getLog(BandwidthLimitedOutputStream.class);
|
||||
_currentRequest = null;
|
||||
}
|
||||
|
||||
public FIFOBandwidthLimiter.Request getCurrentRequest() { return _currentRequest; }
|
||||
|
||||
@Override
|
||||
public void write(int val) throws IOException {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Writing a single byte!", new Exception("Single byte from..."));
|
||||
long before = _context.clock().now();
|
||||
FIFOBandwidthLimiter.Request req = _context.bandwidthLimiter().requestOutbound(1, _peerTarget);
|
||||
// only a single byte, no need to loop
|
||||
req.waitForNextAllocation();
|
||||
long waited = _context.clock().now() - before;
|
||||
if ( (waited > 1000) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("Waiting to write a byte took too long [" + waited + "ms");
|
||||
out.write(val);
|
||||
}
|
||||
@Override
|
||||
public void write(byte src[]) throws IOException {
|
||||
write(src, 0, src.length);
|
||||
}
|
||||
@Override
|
||||
public void write(byte src[], int off, int len) throws IOException {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Writing " + len + " bytes");
|
||||
if (src == null) return;
|
||||
if (len <= 0) return;
|
||||
if (len + off > src.length)
|
||||
throw new IllegalArgumentException("wtf are you thinking? len=" + len
|
||||
+ ", off=" + off + ", data=" + src.length);
|
||||
_currentRequest = _context.bandwidthLimiter().requestOutbound(len, _peerTarget);
|
||||
|
||||
int written = 0;
|
||||
while (written < len) {
|
||||
int allocated = len - _currentRequest.getPendingOutboundRequested();
|
||||
int toWrite = allocated - written;
|
||||
if (toWrite > 0) {
|
||||
try {
|
||||
out.write(src, off + written, toWrite);
|
||||
} catch (IOException ioe) {
|
||||
_currentRequest.abort();
|
||||
_currentRequest = null;
|
||||
throw ioe;
|
||||
}
|
||||
written += toWrite;
|
||||
}
|
||||
_currentRequest.waitForNextAllocation();
|
||||
}
|
||||
synchronized (this) {
|
||||
_currentRequest = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
synchronized (this) {
|
||||
if (_currentRequest != null)
|
||||
_currentRequest.abort();
|
||||
}
|
||||
super.close();
|
||||
}
|
||||
}
|
@@ -0,0 +1,262 @@
|
||||
package net.i2p.router.transport;
|
||||
/*
|
||||
* 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.Properties;
|
||||
import java.util.Random;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
/**
|
||||
* Stress out the bandwidth limiter by running a series of push and pull tests
|
||||
* through bandwidth limited streams. This includes pushing data through
|
||||
* unthrottled streams, through streams throttled at 4KBps, 32KBps, and 256KBps,
|
||||
* pulling data through those same rates, as well as doing so with 10 concurrent
|
||||
* threads (and, in turn, 10 concurrent streams all using the same BandwidthLimiter).
|
||||
*
|
||||
* Note: this takes a long time to run (~1 hour) since the 4KBps push/pull of 1MB with
|
||||
* 10 concurrent threads is, well, slow.
|
||||
*
|
||||
*/
|
||||
public class BandwidthLimiterTest extends TestCase{
|
||||
private RouterContext _context;
|
||||
private final static int NUM_KB = 256;
|
||||
|
||||
public void setUp() {
|
||||
_context = new RouterContext(null);
|
||||
}
|
||||
|
||||
public void tearDown(){
|
||||
}
|
||||
|
||||
private void prepareLimiter(int inKBps, int outKBps, int inBurst, int outBurst) {
|
||||
Properties props = System.getProperties();
|
||||
props.setProperty(FIFOBandwidthRefiller.PROP_INBOUND_BANDWIDTH, ""+inKBps);
|
||||
props.setProperty(FIFOBandwidthRefiller.PROP_OUTBOUND_BANDWIDTH, ""+outKBps);
|
||||
props.setProperty(FIFOBandwidthRefiller.PROP_INBOUND_BANDWIDTH_PEAK, ""+inBurst);
|
||||
props.setProperty(FIFOBandwidthRefiller.PROP_OUTBOUND_BANDWIDTH_PEAK, ""+outBurst);
|
||||
//props.setProperty(TrivialBandwidthLimiter.PROP_REPLENISH_FREQUENCY, ""+10*1000);
|
||||
System.setProperties(props);
|
||||
_context.bandwidthLimiter().reinitialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the configured limiter, determine how long it takes to shove
|
||||
* numBytes through a BandwidthLimitedOutputStream (broken up into numBytesPerWrite)
|
||||
* chunks.
|
||||
*
|
||||
*/
|
||||
private long testOutboundThrottle(int numBytes, int numBytesPerWrite) {
|
||||
byte source[] = new byte[numBytesPerWrite];
|
||||
new Random().nextBytes(source);
|
||||
NullOutputStream target = new NullOutputStream();
|
||||
BandwidthLimitedOutputStream out = new BandwidthLimitedOutputStream(_context, target, null);
|
||||
long before = System.currentTimeMillis();
|
||||
try {
|
||||
for (int i = 0; i < numBytes; i += numBytesPerWrite) {
|
||||
int num = numBytesPerWrite;
|
||||
if (numBytesPerWrite + i >= numBytes)
|
||||
num = numBytes - i;
|
||||
//_log.info("** Writing " + num + " bytes starting at " + i);
|
||||
out.write(source, 0, num);
|
||||
}
|
||||
} catch (IOException ioe) {}
|
||||
long after = System.currentTimeMillis();
|
||||
return after-before;
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the configured limiter, determine how long it takes to read
|
||||
* numBytes through a BandwidthLimitedInputStream (broken up into numBytesPerRead)
|
||||
* chunks.
|
||||
*
|
||||
*/
|
||||
private long testInboundThrottle(int numBytes, int numBytesPerRead) {
|
||||
FakeInputStream source = new FakeInputStream(numBytes);
|
||||
BandwidthLimitedInputStream in = new BandwidthLimitedInputStream(_context, source, null);
|
||||
long before = System.currentTimeMillis();
|
||||
try {
|
||||
byte buf[] = new byte[numBytesPerRead];
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1) {
|
||||
//_log.info("** Read " + read + " bytes");
|
||||
// gobble the data. who cares
|
||||
}
|
||||
} catch (IOException ioe) {}
|
||||
long after = System.currentTimeMillis();
|
||||
return after-before;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a series of tests on outbound throttling (shoving lots of data through pipes
|
||||
* with various limits) and log the times.
|
||||
*
|
||||
*/
|
||||
public void testOutbound() {
|
||||
double error;
|
||||
double predict;
|
||||
|
||||
prepareLimiter(-1, -1, -1, -1);
|
||||
long ms = testOutboundThrottle(NUM_KB*1024, 1*1024);
|
||||
|
||||
/*prepareLimiter(-1, 4, -1, 4*1024);
|
||||
ms = testOutboundThrottle(NUM_KB*1024, 1*1024);
|
||||
predict = (NUM_KB/4)*1000;
|
||||
error = predict/ms;
|
||||
//assertTrue(error>.89);
|
||||
assertTrue(error<1.05);*/
|
||||
|
||||
prepareLimiter(-1, 32, -1, 32*1024);
|
||||
ms = testOutboundThrottle(NUM_KB*1024, 1*1024);
|
||||
predict = (NUM_KB/32)*1000;
|
||||
error = predict/ms;
|
||||
//assertTrue(error>.89);
|
||||
assertTrue(error<1.05);
|
||||
|
||||
prepareLimiter(-1, 256, -1, 256*1024);
|
||||
ms = testOutboundThrottle(NUM_KB*1024, 1*1024);
|
||||
predict = (NUM_KB/256)*1000;
|
||||
error = predict/ms;
|
||||
//assertTrue(error>.89);
|
||||
assertTrue(error<1.05);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a series of tests on inbound throttling (pulling lots of data through pipes
|
||||
* with various limits) and log the times.
|
||||
*
|
||||
*/
|
||||
public void testInbound() {
|
||||
double predict;
|
||||
double error;
|
||||
|
||||
prepareLimiter(-1, -1, -1, -1);
|
||||
long ms = testInboundThrottle(NUM_KB*1024, 1*1024);
|
||||
|
||||
/*prepareLimiter(4, -1, 4*1024, -1);
|
||||
ms = testInboundThrottle(NUM_KB*1024, 1*1024);
|
||||
predict = (NUM_KB/4)*1000;
|
||||
error = predict/ms;
|
||||
//assertTrue(error>.89);
|
||||
assertTrue(error<1.05);*/
|
||||
|
||||
prepareLimiter(32, -1, 32*1024, -1);
|
||||
ms = testInboundThrottle(NUM_KB*1024, 1*1024);
|
||||
predict = (NUM_KB/32)*1000;
|
||||
error = predict/ms;
|
||||
//assertTrue(error>.89);
|
||||
assertTrue(error<1.05);
|
||||
|
||||
prepareLimiter(256, -1, 256*1024, -1);
|
||||
ms = testInboundThrottle(NUM_KB*1024, 1*1024);
|
||||
predict = (NUM_KB/256)*1000;
|
||||
error = predict/ms;
|
||||
//assertTrue(error>.89);
|
||||
assertTrue(error<1.05);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void testOutboundContention() {
|
||||
double predict;
|
||||
double error;
|
||||
long ms;
|
||||
long end;
|
||||
long start;
|
||||
|
||||
prepareLimiter(-1, -1, -1, -1);
|
||||
start = System.currentTimeMillis();
|
||||
//long runningTimes[] = testOutboundContention(10, NUM_KB*1024);
|
||||
end = System.currentTimeMillis();
|
||||
|
||||
//prepareLimiter(-1, 4, -1, 5*1024*1024);
|
||||
//start = System.currentTimeMillis();
|
||||
//runningTimes = testOutboundContention(10, NUM_KB*1024);
|
||||
//end = System.currentTimeMillis();
|
||||
|
||||
//prepareLimiter(-1, 32, -1, 32*1024);
|
||||
//start = System.currentTimeMillis();
|
||||
//runningTimes = testOutboundContention(10, NUM_KB*1024);
|
||||
//end = System.currentTimeMillis();
|
||||
|
||||
prepareLimiter(-1, 256, -1, 256*1024);
|
||||
start = System.currentTimeMillis();
|
||||
testOutboundContention(10, NUM_KB*1024);
|
||||
end = System.currentTimeMillis();
|
||||
ms = end-start;
|
||||
predict = (NUM_KB/256)*1000*10;
|
||||
error = predict/ms;
|
||||
//assertTrue(error>.89);
|
||||
assertTrue(error<1.05);
|
||||
|
||||
}
|
||||
|
||||
private long[] testOutboundContention(int numConcurrent, int numBytes) {
|
||||
OutboundRunner threads[] = new OutboundRunner[numConcurrent];
|
||||
for (int i = 0; i < numConcurrent; i++) {
|
||||
threads[i] = new OutboundRunner(numBytes);
|
||||
}
|
||||
|
||||
for (int i = 0; i < numConcurrent; i++)
|
||||
threads[i].start();
|
||||
for (int i = 0; i < numConcurrent; i++) {
|
||||
try {
|
||||
threads[i].join();
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
long rv[] = new long[numConcurrent];
|
||||
for (int i = 0; i < numConcurrent; i++)
|
||||
rv[i] = threads[i].getRunningTime();
|
||||
return rv;
|
||||
}
|
||||
|
||||
private static int __runnerNum = 0;
|
||||
private class OutboundRunner extends Thread {
|
||||
private int _numBytes;
|
||||
private int _runnerNum;
|
||||
private long _runningTime;
|
||||
public OutboundRunner(int numBytes) {
|
||||
_numBytes = numBytes;
|
||||
_runnerNum = ++__runnerNum;
|
||||
}
|
||||
public void run() {
|
||||
Thread.currentThread().setName("Out" + _runnerNum);
|
||||
_runningTime = testOutboundThrottle(_numBytes, 8*1024);
|
||||
}
|
||||
public long getRunningTime() { return _runningTime; }
|
||||
}
|
||||
}
|
||||
|
||||
class NullOutputStream extends OutputStream {
|
||||
public void write(int param) {}
|
||||
}
|
||||
|
||||
class FakeInputStream extends InputStream {
|
||||
private volatile int _numRead;
|
||||
private int _size;
|
||||
|
||||
public FakeInputStream(int size) {
|
||||
_size = size;
|
||||
_numRead = 0;
|
||||
}
|
||||
public int read() {
|
||||
int rv = 0;
|
||||
if (_numRead >= _size)
|
||||
rv = -1;
|
||||
else
|
||||
rv = 42;
|
||||
_numRead++;
|
||||
return rv;
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
package net.i2p.router.transport.crypto;
|
||||
|
||||
/*
|
||||
* 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.math.BigInteger;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.RandomSource;
|
||||
|
||||
public class DHSessionKeyBuilderTest extends TestCase {
|
||||
public void testDHSessionKeyBuilder(){
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder();
|
||||
DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder();
|
||||
BigInteger pub1 = builder1.getMyPublicValue();
|
||||
BigInteger pub2 = builder2.getMyPublicValue();
|
||||
try {
|
||||
builder2.setPeerPublicValue(pub1);
|
||||
builder1.setPeerPublicValue(pub2);
|
||||
} catch (DHSessionKeyBuilder.InvalidPublicParameterException ippe) {
|
||||
assertTrue(ippe.getMessage(), true);
|
||||
}
|
||||
SessionKey key1 = builder1.getSessionKey();
|
||||
SessionKey key2 = builder2.getSessionKey();
|
||||
|
||||
assertEquals(key1, key2);
|
||||
|
||||
byte iv[] = new byte[16];
|
||||
RandomSource.getInstance().nextBytes(iv);
|
||||
String origVal = "1234567890123456"; // 16 bytes max using AESEngine
|
||||
byte enc[] = new byte[16];
|
||||
byte dec[] = new byte[16];
|
||||
ctx.aes().encrypt(origVal.getBytes(), 0, enc, 0, key1, iv, 16);
|
||||
ctx.aes().decrypt(enc, 0, dec, 0, key2, iv, 16);
|
||||
String tranVal = new String(dec);
|
||||
assertEquals(origVal, tranVal);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
package net.i2p.router.transport.udp;
|
||||
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class UDPEndpointTest {
|
||||
private RouterContext _context;
|
||||
private Log _log;
|
||||
private UDPEndpoint _endpoints[];
|
||||
private boolean _beginTest;
|
||||
private List _sentNotReceived;
|
||||
|
||||
public UDPEndpointTest(RouterContext ctx) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(UDPEndpointTest.class);
|
||||
_sentNotReceived = Collections.synchronizedList(new ArrayList(1000));
|
||||
}
|
||||
|
||||
public void runTest(int numPeers) {
|
||||
_log.debug("Run test("+numPeers+")");
|
||||
try {
|
||||
_endpoints = new UDPEndpoint[numPeers];
|
||||
int base = 2000 + _context.random().nextInt(10000);
|
||||
for (int i = 0; i < numPeers; i++) {
|
||||
_log.debug("Building " + i);
|
||||
UDPEndpoint endpoint = new UDPEndpoint(_context, null, base + i, null);
|
||||
_endpoints[i] = endpoint;
|
||||
endpoint.startup();
|
||||
I2PThread read = new I2PThread(new TestRead(endpoint), "Test read " + i);
|
||||
I2PThread write = new I2PThread(new TestWrite(endpoint), "Test write " + i);
|
||||
//read.setDaemon(true);
|
||||
read.start();
|
||||
//write.setDaemon(true);
|
||||
write.start();
|
||||
}
|
||||
} catch (SocketException se) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error initializing", se);
|
||||
return;
|
||||
}
|
||||
_beginTest = true;
|
||||
_log.debug("Test begin");
|
||||
}
|
||||
|
||||
private class TestRead implements Runnable {
|
||||
private UDPEndpoint _endpoint;
|
||||
public TestRead(UDPEndpoint peer) {
|
||||
_endpoint = peer;
|
||||
}
|
||||
public void run() {
|
||||
while (!_beginTest) {
|
||||
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
_log.debug("Beginning to read");
|
||||
long start = System.currentTimeMillis();
|
||||
int received = 0;
|
||||
while (true) {
|
||||
UDPPacket packet = _endpoint.receive();
|
||||
ByteArray ba = new ByteArray(packet.getPacket().getData(), 0, packet.getPacket().getLength());
|
||||
boolean removed = _sentNotReceived.remove(ba);
|
||||
int outstanding = _sentNotReceived.size();
|
||||
if (!removed) {
|
||||
_log.error("Received a packet that we weren't expecting: " + packet);
|
||||
} else {
|
||||
_log.debug("Received an expected packet (" + received + ") with outstanding: " + outstanding);
|
||||
received++;
|
||||
}
|
||||
if ((received % 10000) == 0) {
|
||||
long time = System.currentTimeMillis() - start;
|
||||
_log.debug("Received "+received+" in " + time);
|
||||
}
|
||||
packet.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TestWrite implements Runnable {
|
||||
private UDPEndpoint _endpoint;
|
||||
public TestWrite(UDPEndpoint peer) {
|
||||
_endpoint = peer;
|
||||
}
|
||||
public void run() {
|
||||
while (!_beginTest) {
|
||||
try { Thread.sleep(2000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
try { Thread.sleep(2000); } catch (InterruptedException ie) {}
|
||||
_log.debug("Beginning to write");
|
||||
for (int curPacket = 0; curPacket < 10000; curPacket++) {
|
||||
byte data[] = new byte[1024];
|
||||
_context.random().nextBytes(data);
|
||||
int curPeer = (curPacket % _endpoints.length);
|
||||
if (_endpoints[curPeer] == _endpoint)
|
||||
curPeer++;
|
||||
if (curPeer >= _endpoints.length)
|
||||
curPeer = 0;
|
||||
short priority = 1;
|
||||
long expiration = -1;
|
||||
UDPPacket packet = UDPPacket.acquire(_context, true);
|
||||
//try {
|
||||
if (true) throw new RuntimeException("fixme");
|
||||
//packet.initialize(priority, expiration, InetAddress.getLocalHost(), _endpoints[curPeer].getListenPort());
|
||||
packet.writeData(data, 0, 1024);
|
||||
packet.getPacket().setLength(1024);
|
||||
int outstanding = _sentNotReceived.size() + 1;
|
||||
_sentNotReceived.add(new ByteArray(data, 0, 1024));
|
||||
_log.debug("Sending packet " + curPacket + " with outstanding " + outstanding);
|
||||
_endpoint.send(packet);
|
||||
//try { Thread.sleep(10); } catch (InterruptedException ie) {}
|
||||
//} catch (UnknownHostException uhe) {
|
||||
// _log.error("foo!", uhe);
|
||||
//}
|
||||
//if (_log.shouldLog(Log.DEBUG)) {
|
||||
// _log.debug("Sent to " + _endpoints[curPeer].getListenPort() + " from " + _endpoint.getListenPort());
|
||||
//}
|
||||
}
|
||||
try { Thread.sleep(10*1000); } catch (InterruptedException e) {}
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
try { System.out.println("Current dir: " + new java.io.File(".").getCanonicalPath()); } catch (Exception e) {}
|
||||
new java.io.File("udpEndpointTest.stats").delete();
|
||||
Properties props = new Properties();
|
||||
props.setProperty("stat.logFile", "udpEndpointTest.stats");
|
||||
props.setProperty("stat.logFilters", "*");
|
||||
UDPEndpointTest test = new UDPEndpointTest(new RouterContext(null, props));
|
||||
test.runTest(2);
|
||||
}
|
||||
}
|
@@ -0,0 +1,153 @@
|
||||
package net.i2p.router.tunnel;
|
||||
/*
|
||||
* 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.util.ArrayList;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Test the batching behavior of the preprocessor with one, two, or three
|
||||
* messages of various sizes and settings.
|
||||
*
|
||||
*/
|
||||
public class BatchedFragmentTest extends FragmentTest {
|
||||
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
BatchedPreprocessor.DEFAULT_DELAY = 200;
|
||||
}
|
||||
|
||||
protected TunnelGateway.QueuePreprocessor createPreprocessor(I2PAppContext ctx) {
|
||||
return new BatchedPreprocessor(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a small message, wait a second, then send a large message, pushing
|
||||
* the first one through immediately, with the rest of the large one passed
|
||||
* after a brief delay.
|
||||
*
|
||||
*/
|
||||
public void testBatched() {
|
||||
TunnelGateway.Pending pending1 = createPending(10, false, false);
|
||||
ArrayList messages = new ArrayList();
|
||||
messages.add(pending1);
|
||||
|
||||
TunnelGateway.Pending pending2 = createPending(1024, false, false);
|
||||
|
||||
TunnelGateway.QueuePreprocessor pre = createPreprocessor(_context);
|
||||
SenderImpl sender = new SenderImpl();
|
||||
DefragmentedReceiverImpl handleReceiver = new DefragmentedReceiverImpl(pending1.getData(), pending2.getData());
|
||||
FragmentHandler handler = new FragmentHandler(_context, handleReceiver);
|
||||
ReceiverImpl receiver = new ReceiverImpl(handler, 0);
|
||||
byte msg[] = pending1.getData();
|
||||
|
||||
boolean keepGoing = true;
|
||||
boolean alreadyAdded = false;
|
||||
while (keepGoing) {
|
||||
keepGoing = pre.preprocessQueue(messages, new SenderImpl(), receiver);
|
||||
if (keepGoing) {
|
||||
try { Thread.sleep(150); } catch (InterruptedException ie) {}
|
||||
|
||||
if (!alreadyAdded) {
|
||||
messages.add(pending2);
|
||||
alreadyAdded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(handleReceiver.receivedOk());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send a small message, wait a second, then send a large message, pushing
|
||||
* the first one through immediately, with the rest of the large one passed
|
||||
* after a brief delay.
|
||||
*
|
||||
*/
|
||||
public void runBatches() {
|
||||
//success += testBatched(1, false, false, 1024, false, false);
|
||||
// this takes a long fucking time
|
||||
for (int i = 1; i <= 1024; i++) {
|
||||
testBatched(i, false, false, 1024, false, false, 1024, false, false);
|
||||
testBatched(i, true, false, 1024, false, false, 1024, false, false);
|
||||
testBatched(i, true, true, 1024, false, false, 1024, false, false);
|
||||
testBatched(i, false, false, 1024, true, false, 1024, false, false);
|
||||
testBatched(i, true, false, 1024, true, false, 1024, false, false);
|
||||
testBatched(i, true, true, 1024, true, false, 1024, false, false);
|
||||
testBatched(i, false, false, 1024, true, true, 1024, false, false);
|
||||
testBatched(i, true, false, 1024, true, true, 1024, false, false);
|
||||
testBatched(i, true, true, 1024, true, true, 1024, false, false);
|
||||
|
||||
testBatched(i, false, false, 1024, false, false, 1024, true, false);
|
||||
testBatched(i, true, false, 1024, false, false, 1024, true, false);
|
||||
testBatched(i, true, true, 1024, false, false, 1024, true, false);
|
||||
testBatched(i, false, false, 1024, true, false, 1024, true, false);
|
||||
testBatched(i, true, false, 1024, true, false, 1024, true, false);
|
||||
testBatched(i, true, true, 1024, true, false, 1024, true, false);
|
||||
testBatched(i, false, false, 1024, true, true, 1024, true, false);
|
||||
testBatched(i, true, false, 1024, true, true, 1024, true, false);
|
||||
testBatched(i, true, true, 1024, true, true, 1024, true, false);
|
||||
|
||||
testBatched(i, false, false, 1024, false, false, 1024, true, true);
|
||||
testBatched(i, true, false, 1024, false, false, 1024, true, true);
|
||||
testBatched(i, true, true, 1024, false, false, 1024, true, true);
|
||||
testBatched(i, false, false, 1024, true, false, 1024, true, true);
|
||||
testBatched(i, true, false, 1024, true, false, 1024, true, true);
|
||||
testBatched(i, true, true, 1024, true, false, 1024, true, true);
|
||||
testBatched(i, false, false, 1024, true, true, 1024, true, true);
|
||||
testBatched(i, true, false, 1024, true, true, 1024, true, true);
|
||||
testBatched(i, true, true, 1024, true, true, 1024, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void testBatched(int firstSize, boolean firstRouter, boolean firstTunnel,
|
||||
int secondSize, boolean secondRouter, boolean secondTunnel,
|
||||
int thirdSize, boolean thirdRouter, boolean thirdTunnel) {
|
||||
TunnelGateway.Pending pending1 = createPending(firstSize, firstRouter, firstTunnel);
|
||||
TunnelGateway.Pending pending2 = createPending(secondSize, secondRouter, secondTunnel);
|
||||
TunnelGateway.Pending pending3 = createPending(thirdSize, thirdRouter, thirdTunnel);
|
||||
|
||||
runBatch(pending1, pending2, pending3);
|
||||
}
|
||||
|
||||
private void runBatch(TunnelGateway.Pending pending1, TunnelGateway.Pending pending2, TunnelGateway.Pending pending3) {
|
||||
ArrayList messages = new ArrayList();
|
||||
messages.add(pending1);
|
||||
|
||||
TunnelGateway.QueuePreprocessor pre = createPreprocessor(_context);
|
||||
SenderImpl sender = new SenderImpl();
|
||||
DefragmentedReceiverImpl handleReceiver = new DefragmentedReceiverImpl(pending1.getData(), pending2.getData(), pending3.getData());
|
||||
FragmentHandler handler = new FragmentHandler(_context, handleReceiver);
|
||||
ReceiverImpl receiver = new ReceiverImpl(handler, 0);
|
||||
byte msg[] = pending1.getData();
|
||||
|
||||
boolean keepGoing = true;
|
||||
int added = 0;
|
||||
while (keepGoing) {
|
||||
keepGoing = pre.preprocessQueue(messages, new SenderImpl(), receiver);
|
||||
if ( (keepGoing) || ((messages.size() == 0) && (added < 2) ) ) {
|
||||
try { Thread.sleep(150); } catch (InterruptedException ie) {}
|
||||
|
||||
if (added == 0) {
|
||||
messages.add(pending2);
|
||||
added++;
|
||||
keepGoing = true;
|
||||
} else if (added == 1) {
|
||||
messages.add(pending3);
|
||||
added++;
|
||||
keepGoing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(handleReceiver.receivedOk());
|
||||
}
|
||||
}
|
@@ -0,0 +1,183 @@
|
||||
package net.i2p.router.tunnel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.data.i2np.BuildRequestRecord;
|
||||
import net.i2p.data.i2np.BuildResponseRecord;
|
||||
import net.i2p.data.i2np.TunnelBuildMessage;
|
||||
import net.i2p.data.i2np.TunnelBuildReplyMessage;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Simple test to create an encrypted TunnelBuildMessage, decrypt its layers (as it would be
|
||||
* during transmission), inject replies, then handle the TunnelBuildReplyMessage (unwrapping
|
||||
* the reply encryption and reading the replies).
|
||||
*/
|
||||
public class BuildMessageTest {
|
||||
private Hash _peers[];
|
||||
private PrivateKey _privKeys[];
|
||||
private PublicKey _pubKeys[];
|
||||
private Hash _replyRouter;
|
||||
private long _replyTunnel;
|
||||
|
||||
public static void main(String args[]) {
|
||||
BuildMessageTest test = new BuildMessageTest();
|
||||
try {
|
||||
test.runTest();
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
}
|
||||
|
||||
private void runTest() {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
Log log = ctx.logManager().getLog(getClass());
|
||||
|
||||
List order = pickOrder(ctx);
|
||||
|
||||
TunnelCreatorConfig cfg = createConfig(ctx);
|
||||
_replyRouter = new Hash();
|
||||
byte h[] = new byte[Hash.HASH_LENGTH];
|
||||
Arrays.fill(h, (byte)0xFF);
|
||||
_replyRouter.setData(h);
|
||||
_replyTunnel = 42;
|
||||
|
||||
// populate and encrypt the message
|
||||
BuildMessageGenerator gen = new BuildMessageGenerator();
|
||||
TunnelBuildMessage msg = new TunnelBuildMessage(ctx);
|
||||
for (int i = 0; i < BuildMessageGenerator.ORDER.length; i++) {
|
||||
int hop = ((Integer)order.get(i)).intValue();
|
||||
PublicKey key = null;
|
||||
if (hop < _pubKeys.length)
|
||||
key = _pubKeys[hop];
|
||||
gen.createRecord(i, hop, msg, cfg, _replyRouter, _replyTunnel, ctx, key);
|
||||
}
|
||||
gen.layeredEncrypt(ctx, msg, cfg, order);
|
||||
|
||||
log.debug("\n================================================================" +
|
||||
"\nMessage fully encrypted" +
|
||||
"\n================================================================");
|
||||
|
||||
// now msg is fully encrypted, so lets go through the hops, decrypting and replying
|
||||
// as necessary
|
||||
|
||||
BuildMessageProcessor proc = new BuildMessageProcessor(ctx);
|
||||
for (int i = 0; i < cfg.getLength(); i++) {
|
||||
// this not only decrypts the current hop's record, but encrypts the other records
|
||||
// with the reply key
|
||||
BuildRequestRecord req = proc.decrypt(ctx, msg, _peers[i], _privKeys[i]);
|
||||
if (req == null) {
|
||||
// no records matched the _peers[i], or the decryption failed
|
||||
throw new RuntimeException("foo @ " + i);
|
||||
}
|
||||
long ourId = req.readReceiveTunnelId();
|
||||
byte replyIV[] = req.readReplyIV();
|
||||
long nextId = req.readNextTunnelId();
|
||||
Hash nextPeer = req.readNextIdentity();
|
||||
boolean isInGW = req.readIsInboundGateway();
|
||||
boolean isOutEnd = req.readIsOutboundEndpoint();
|
||||
long time = req.readRequestTime();
|
||||
long now = (ctx.clock().now() / (60l*60l*1000l)) * (60*60*1000);
|
||||
int ourSlot = -1;
|
||||
|
||||
BuildResponseRecord resp = new BuildResponseRecord();
|
||||
byte reply[] = resp.create(ctx, 0, req.readReplyKey(), req.readReplyIV(), -1);
|
||||
for (int j = 0; j < TunnelBuildMessage.RECORD_COUNT; j++) {
|
||||
if (msg.getRecord(j) == null) {
|
||||
ourSlot = j;
|
||||
msg.setRecord(j, new ByteArray(reply));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Read slot " + ourSlot + " containing hop " + i + " @ " + _peers[i].toBase64()
|
||||
+ " receives on " + ourId
|
||||
+ " w/ replyIV " + Base64.encode(replyIV) + " sending to " + nextId
|
||||
+ " on " + nextPeer.toBase64()
|
||||
+ " inGW? " + isInGW + " outEnd? " + isOutEnd + " time difference " + (now-time));
|
||||
}
|
||||
|
||||
|
||||
log.debug("\n================================================================" +
|
||||
"\nAll hops traversed and replies gathered" +
|
||||
"\n================================================================");
|
||||
|
||||
// now all of the replies are populated, toss 'em into a reply message and handle it
|
||||
TunnelBuildReplyMessage reply = new TunnelBuildReplyMessage(ctx);
|
||||
for (int i = 0; i < TunnelBuildMessage.RECORD_COUNT; i++)
|
||||
reply.setRecord(i, msg.getRecord(i));
|
||||
|
||||
BuildReplyHandler handler = new BuildReplyHandler();
|
||||
int statuses[] = handler.decrypt(ctx, reply, cfg, order);
|
||||
if (statuses == null) throw new RuntimeException("bar");
|
||||
boolean allAgree = true;
|
||||
for (int i = 0; i < cfg.getLength(); i++) {
|
||||
Hash peer = cfg.getPeer(i);
|
||||
int record = ((Integer)order.get(i)).intValue();
|
||||
if (statuses[record] != 0)
|
||||
allAgree = false;
|
||||
//else
|
||||
// penalize peer according to the rejection cause
|
||||
}
|
||||
|
||||
log.debug("\n================================================================" +
|
||||
"\nAll peers agree? " + allAgree +
|
||||
"\n================================================================");
|
||||
}
|
||||
|
||||
private static final List pickOrder(I2PAppContext ctx) {
|
||||
// pseudorandom, yet consistent (so we can be repeatable)
|
||||
List rv = new ArrayList(8);
|
||||
rv.add(new Integer(2));
|
||||
rv.add(new Integer(4));
|
||||
rv.add(new Integer(6));
|
||||
rv.add(new Integer(0));
|
||||
rv.add(new Integer(1));
|
||||
rv.add(new Integer(3));
|
||||
rv.add(new Integer(5));
|
||||
rv.add(new Integer(7));
|
||||
return rv;
|
||||
}
|
||||
|
||||
private TunnelCreatorConfig createConfig(I2PAppContext ctx) {
|
||||
return configOutbound(ctx);
|
||||
}
|
||||
private TunnelCreatorConfig configOutbound(I2PAppContext ctx) {
|
||||
_peers = new Hash[4];
|
||||
_pubKeys = new PublicKey[_peers.length];
|
||||
_privKeys = new PrivateKey[_peers.length];
|
||||
for (int i = 0; i < _peers.length; i++) {
|
||||
byte buf[] = new byte[Hash.HASH_LENGTH];
|
||||
Arrays.fill(buf, (byte)i); // consistent for repeatability
|
||||
Hash h = new Hash(buf);
|
||||
_peers[i] = h;
|
||||
Object kp[] = ctx.keyGenerator().generatePKIKeypair();
|
||||
_pubKeys[i] = (PublicKey)kp[0];
|
||||
_privKeys[i] = (PrivateKey)kp[1];
|
||||
}
|
||||
|
||||
TunnelCreatorConfig cfg = new TunnelCreatorConfig(null, _peers.length, false);
|
||||
long now = ctx.clock().now();
|
||||
// peers[] is ordered endpoint first, but cfg.getPeer() is ordered gateway first
|
||||
for (int i = 0; i < _peers.length; i++) {
|
||||
cfg.setPeer(i, _peers[i]);
|
||||
HopConfig hop = cfg.getConfig(i);
|
||||
hop.setExpiration(now+10*60*1000);
|
||||
hop.setIVKey(ctx.keyGenerator().generateSessionKey());
|
||||
hop.setLayerKey(ctx.keyGenerator().generateSessionKey());
|
||||
hop.setReplyKey(ctx.keyGenerator().generateSessionKey());
|
||||
byte iv[] = new byte[BuildRequestRecord.IV_SIZE];
|
||||
Arrays.fill(iv, (byte)i); // consistent for repeatability
|
||||
hop.setReplyIV(new ByteArray(iv));
|
||||
hop.setReceiveTunnelId(new TunnelId(i+1));
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
}
|
221
router/java/test/junit/net/i2p/router/tunnel/FragmentTest.java
Normal file
221
router/java/test/junit/net/i2p/router/tunnel/FragmentTest.java
Normal file
@@ -0,0 +1,221 @@
|
||||
package net.i2p.router.tunnel;
|
||||
/*
|
||||
* 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.util.ArrayList;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.data.i2np.DataMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
|
||||
/**
|
||||
* Simple test to see if the fragmentation is working, testing the preprocessor,
|
||||
* FragmentHandler, and FragmentedMessage operation.
|
||||
*
|
||||
*/
|
||||
public class FragmentTest extends TestCase{
|
||||
protected I2PAppContext _context;
|
||||
|
||||
public void setUp() {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_context.random().nextBoolean();
|
||||
FragmentHandler.MAX_DEFRAGMENT_TIME = 10*1000;
|
||||
}
|
||||
|
||||
protected TunnelGateway.QueuePreprocessor createPreprocessor(I2PAppContext ctx) {
|
||||
return new TrivialPreprocessor(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message that fits inside a single fragment through
|
||||
*
|
||||
*/
|
||||
public void testSingle() {
|
||||
TunnelGateway.Pending pending = createPending(949, false, false);
|
||||
ArrayList messages = new ArrayList();
|
||||
messages.add(pending);
|
||||
|
||||
TunnelGateway.QueuePreprocessor pre = createPreprocessor(_context);
|
||||
SenderImpl sender = new SenderImpl();
|
||||
DefragmentedReceiverImpl handleReceiver = new DefragmentedReceiverImpl(pending.getData());
|
||||
FragmentHandler handler = new FragmentHandler(_context, handleReceiver);
|
||||
ReceiverImpl receiver = new ReceiverImpl(handler, 0);
|
||||
byte msg[] = pending.getData();
|
||||
|
||||
boolean keepGoing = true;
|
||||
while (keepGoing) {
|
||||
keepGoing = pre.preprocessQueue(messages, new SenderImpl(), receiver);
|
||||
if (keepGoing)
|
||||
try { Thread.sleep(100); } catch (InterruptedException ie) {}
|
||||
}
|
||||
assertTrue(handleReceiver.receivedOk());
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message with two fragments through with no delay
|
||||
*
|
||||
*/
|
||||
public void testMultiple() {
|
||||
TunnelGateway.Pending pending = createPending(2048, false, false);
|
||||
ArrayList messages = new ArrayList();
|
||||
messages.add(pending);
|
||||
|
||||
TunnelGateway.QueuePreprocessor pre = createPreprocessor(_context);
|
||||
SenderImpl sender = new SenderImpl();
|
||||
DefragmentedReceiverImpl handleReceiver = new DefragmentedReceiverImpl(pending.getData());
|
||||
FragmentHandler handler = new FragmentHandler(_context, handleReceiver);
|
||||
ReceiverImpl receiver = new ReceiverImpl(handler, 0);
|
||||
byte msg[] = pending.getData();
|
||||
|
||||
boolean keepGoing = true;
|
||||
while (keepGoing) {
|
||||
keepGoing = pre.preprocessQueue(messages, new SenderImpl(), receiver);
|
||||
if (keepGoing)
|
||||
try { Thread.sleep(100); } catch (InterruptedException ie) {}
|
||||
}
|
||||
assertTrue(handleReceiver.receivedOk());
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a fragmented message, except wait a while between each fragment, causing
|
||||
* the defragmentation to fail (since the fragments will expire)
|
||||
*
|
||||
*/
|
||||
public void runDelayed() {
|
||||
TunnelGateway.Pending pending = createPending(2048, false, false);
|
||||
ArrayList messages = new ArrayList();
|
||||
messages.add(pending);
|
||||
TunnelGateway.QueuePreprocessor pre = createPreprocessor(_context);
|
||||
SenderImpl sender = new SenderImpl();
|
||||
FragmentHandler handler = new FragmentHandler(_context, new DefragmentedReceiverImpl(pending.getData()));
|
||||
ReceiverImpl receiver = new ReceiverImpl(handler, 11*1000);
|
||||
byte msg[] = pending.getData();
|
||||
|
||||
boolean keepGoing = true;
|
||||
while (keepGoing) {
|
||||
keepGoing = pre.preprocessQueue(messages, new SenderImpl(), receiver);
|
||||
if (keepGoing)
|
||||
try { Thread.sleep(100); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
||||
public void runVaried() {
|
||||
for (int i = 0; i <= 4096; i++) {
|
||||
assertTrue(runVaried(i, false, false));
|
||||
assertTrue(runVaried(i, true, false));
|
||||
assertTrue(runVaried(i, true, true));
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean runVaried(int size, boolean includeRouter, boolean includeTunnel) {
|
||||
TunnelGateway.Pending pending = createPending(size, includeRouter, includeTunnel);
|
||||
ArrayList messages = new ArrayList();
|
||||
messages.add(pending);
|
||||
|
||||
DefragmentedReceiverImpl handleReceiver = new DefragmentedReceiverImpl(pending.getData());
|
||||
TunnelGateway.QueuePreprocessor pre = createPreprocessor(_context);
|
||||
SenderImpl sender = new SenderImpl();
|
||||
FragmentHandler handler = new FragmentHandler(_context, handleReceiver);
|
||||
ReceiverImpl receiver = new ReceiverImpl(handler, 0);
|
||||
byte msg[] = pending.getData();
|
||||
|
||||
boolean keepGoing = true;
|
||||
while (keepGoing) {
|
||||
keepGoing = pre.preprocessQueue(messages, new SenderImpl(), receiver);
|
||||
if (keepGoing)
|
||||
try { Thread.sleep(100); } catch (InterruptedException ie) {}
|
||||
}
|
||||
|
||||
return handleReceiver.receivedOk();
|
||||
}
|
||||
|
||||
protected TunnelGateway.Pending createPending(int size, boolean includeRouter, boolean includeTunnel) {
|
||||
DataMessage m = new DataMessage(_context);
|
||||
byte data[] = new byte[size];
|
||||
_context.random().nextBytes(data);
|
||||
m.setData(data);
|
||||
m.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
m.setMessageExpiration(_context.clock().now() + 60*1000);
|
||||
|
||||
Hash toRouter = null;
|
||||
TunnelId toTunnel = null;
|
||||
if (includeRouter) {
|
||||
toRouter = new Hash(new byte[Hash.HASH_LENGTH]);
|
||||
_context.random().nextBytes(toRouter.getData());
|
||||
}
|
||||
if (includeTunnel)
|
||||
toTunnel = new TunnelId(_context.random().nextLong(TunnelId.MAX_ID_VALUE));
|
||||
return new TunnelGateway.Pending(m, toRouter, toTunnel);
|
||||
}
|
||||
|
||||
protected class SenderImpl implements TunnelGateway.Sender {
|
||||
public void sendPreprocessed(byte[] preprocessed, TunnelGateway.Receiver receiver) {
|
||||
receiver.receiveEncrypted(preprocessed);
|
||||
}
|
||||
}
|
||||
protected class ReceiverImpl implements TunnelGateway.Receiver {
|
||||
private FragmentHandler _handler;
|
||||
private int _delay;
|
||||
public ReceiverImpl(FragmentHandler handler, int delay) {
|
||||
_handler = handler;
|
||||
_delay = delay;
|
||||
}
|
||||
public void receiveEncrypted(byte[] encrypted) {
|
||||
_handler.receiveTunnelMessage(encrypted, 0, encrypted.length);
|
||||
try { Thread.sleep(_delay); } catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
|
||||
protected class DefragmentedReceiverImpl implements FragmentHandler.DefragmentedReceiver {
|
||||
private byte _expected[];
|
||||
private byte _expected2[];
|
||||
private byte _expected3[];
|
||||
private int _received;
|
||||
public DefragmentedReceiverImpl(byte expected[]) {
|
||||
this(expected, null);
|
||||
}
|
||||
public DefragmentedReceiverImpl(byte expected[], byte expected2[]) {
|
||||
this(expected, expected2, null);
|
||||
}
|
||||
public DefragmentedReceiverImpl(byte expected[], byte expected2[], byte expected3[]) {
|
||||
_expected = expected;
|
||||
_expected2 = expected2;
|
||||
_expected3 = expected3;
|
||||
_received = 0;
|
||||
}
|
||||
public void receiveComplete(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) {
|
||||
boolean ok = false;
|
||||
byte m[] = msg.toByteArray();
|
||||
if ( (_expected != null) && (DataHelper.eq(_expected, m)) )
|
||||
ok = true;
|
||||
if (!ok && (_expected2 != null) && (DataHelper.eq(_expected2, m)) )
|
||||
ok = true;
|
||||
if (!ok && (_expected3 != null) && (DataHelper.eq(_expected3, m)) )
|
||||
ok = true;
|
||||
if (ok)
|
||||
_received++;
|
||||
//_log.info("** equal? " + ok);
|
||||
}
|
||||
|
||||
public boolean receivedOk() {
|
||||
if ( (_expected != null) && (_expected2 != null) && (_expected3 != null) )
|
||||
return _received == 3;
|
||||
else if ( (_expected != null) && (_expected2 != null) )
|
||||
return _received == 2;
|
||||
else if ( (_expected != null) || (_expected2 != null) )
|
||||
return _received == 1;
|
||||
else
|
||||
return _received == 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,216 @@
|
||||
package net.i2p.router.tunnel;
|
||||
/*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.data.i2np.DataMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Quick unit test for base functionality of inbound tunnel
|
||||
* operation
|
||||
*/
|
||||
public class InboundGatewayTest extends TestCase{
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private TunnelCreatorConfig _config;
|
||||
private TunnelGateway.QueuePreprocessor _preprocessor;
|
||||
private TunnelGateway.Sender _sender;
|
||||
private TestReceiver _receiver;
|
||||
private TunnelGateway _gw;
|
||||
|
||||
public void setUp() {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_config = prepareConfig(8);
|
||||
_preprocessor = new TrivialPreprocessor(_context);
|
||||
_sender = new InboundSender(_context, _config.getConfig(0));
|
||||
_receiver = new TestReceiver(_config);
|
||||
_gw = new TunnelGateway(_context, _preprocessor, _sender, _receiver);
|
||||
}
|
||||
|
||||
public void testSmall() {
|
||||
int runCount = 1;
|
||||
|
||||
List messages = new ArrayList(runCount);
|
||||
long start = _context.clock().now();
|
||||
|
||||
for (int i = 0; i < runCount; i++) {
|
||||
DataMessage m = new DataMessage(_context);
|
||||
m.setData(new byte[64]);
|
||||
java.util.Arrays.fill(m.getData(), (byte)0xFF);
|
||||
m.setMessageExpiration(_context.clock().now() + 60*1000);
|
||||
m.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
byte data[] = m.toByteArray();
|
||||
messages.add(m);
|
||||
_gw.add(m, null, null);
|
||||
}
|
||||
|
||||
long time = _context.clock().now() - start;
|
||||
|
||||
List received = _receiver.clearReceived();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
assertTrue(received.contains(((I2NPMessage)messages.get(i))));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRouter() {
|
||||
int runCount = 1;
|
||||
|
||||
List messages = new ArrayList(runCount);
|
||||
long start = _context.clock().now();
|
||||
|
||||
for (int i = 0; i < runCount; i++) {
|
||||
DataMessage m = new DataMessage(_context);
|
||||
m.setData(new byte[64]);
|
||||
java.util.Arrays.fill(m.getData(), (byte)0xFF);
|
||||
m.setMessageExpiration(_context.clock().now() + 60*1000);
|
||||
m.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
Hash to = new Hash(new byte[Hash.HASH_LENGTH]);
|
||||
java.util.Arrays.fill(to.getData(), (byte)0xFF);
|
||||
byte data[] = m.toByteArray();
|
||||
messages.add(m);
|
||||
_gw.add(m, to, null);
|
||||
}
|
||||
|
||||
long time = _context.clock().now() - start;
|
||||
|
||||
List received = _receiver.clearReceived();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
assertTrue(received.contains(((I2NPMessage)messages.get(i))));
|
||||
}
|
||||
}
|
||||
|
||||
public void testTunnel() {
|
||||
int runCount = 1;
|
||||
|
||||
List messages = new ArrayList(runCount);
|
||||
long start = _context.clock().now();
|
||||
|
||||
for (int i = 0; i < runCount; i++) {
|
||||
DataMessage m = new DataMessage(_context);
|
||||
m.setData(new byte[64]);
|
||||
java.util.Arrays.fill(m.getData(), (byte)0xFF);
|
||||
m.setMessageExpiration(_context.clock().now() + 60*1000);
|
||||
m.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
Hash to = new Hash(new byte[Hash.HASH_LENGTH]);
|
||||
java.util.Arrays.fill(to.getData(), (byte)0xFF);
|
||||
TunnelId tunnel = new TunnelId(42);
|
||||
byte data[] = m.toByteArray();
|
||||
messages.add(m);
|
||||
_gw.add(m, to, tunnel);
|
||||
}
|
||||
|
||||
long time = _context.clock().now() - start;
|
||||
|
||||
List received = _receiver.clearReceived();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
assertTrue(received.contains(((I2NPMessage)messages.get(i))));
|
||||
}
|
||||
}
|
||||
|
||||
public void testLarge() {
|
||||
int runCount = 1;
|
||||
|
||||
List messages = new ArrayList(runCount);
|
||||
long start = _context.clock().now();
|
||||
|
||||
for (int i = 0; i < runCount; i++) {
|
||||
DataMessage m = new DataMessage(_context);
|
||||
m.setData(new byte[1024]);
|
||||
java.util.Arrays.fill(m.getData(), (byte)0xFF);
|
||||
m.setMessageExpiration(_context.clock().now() + 60*1000);
|
||||
m.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
byte data[] = m.toByteArray();
|
||||
messages.add(m);
|
||||
_gw.add(m, null, null);
|
||||
}
|
||||
|
||||
long time = _context.clock().now() - start;
|
||||
//try { Thread.sleep(60*1000); } catch (Exception e) {}
|
||||
|
||||
List received = _receiver.clearReceived();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
assertTrue(received.contains(((I2NPMessage)messages.get(i))));
|
||||
}
|
||||
}
|
||||
|
||||
private class TestReceiver implements TunnelGateway.Receiver, FragmentHandler.DefragmentedReceiver {
|
||||
private TunnelCreatorConfig _config;
|
||||
private FragmentHandler _handler;
|
||||
private List _received;
|
||||
public TestReceiver(TunnelCreatorConfig config) {
|
||||
_config = config;
|
||||
_handler = new FragmentHandler(_context, TestReceiver.this);
|
||||
_received = new ArrayList(1000);
|
||||
}
|
||||
public void receiveEncrypted(byte[] encrypted) {
|
||||
// fake all the hops...
|
||||
|
||||
for (int i = 1; i <= _config.getLength() - 2; i++) {
|
||||
HopProcessor hop = new HopProcessor(_context, _config.getConfig(i));
|
||||
assertTrue(hop.process(encrypted, 0, encrypted.length, _config.getConfig(i).getReceiveFrom()));
|
||||
}
|
||||
|
||||
// now handle it at the endpoint
|
||||
InboundEndpointProcessor end = new InboundEndpointProcessor(_context, _config);
|
||||
assertTrue(end.retrievePreprocessedData(encrypted, 0, encrypted.length, _config.getPeer(_config.getLength()-2)));
|
||||
|
||||
|
||||
_handler.receiveTunnelMessage(encrypted, 0, encrypted.length);
|
||||
}
|
||||
public void receiveComplete(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) {
|
||||
_received.add(msg);
|
||||
}
|
||||
public List clearReceived() {
|
||||
List rv = _received;
|
||||
_received = new ArrayList();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
private TunnelCreatorConfig prepareConfig(int numHops) {
|
||||
Hash peers[] = new Hash[numHops];
|
||||
byte tunnelIds[][] = new byte[numHops][4];
|
||||
for (int i = 0; i < numHops; i++) {
|
||||
peers[i] = new Hash();
|
||||
peers[i].setData(new byte[Hash.HASH_LENGTH]);
|
||||
_context.random().nextBytes(peers[i].getData());
|
||||
_context.random().nextBytes(tunnelIds[i]);
|
||||
}
|
||||
|
||||
TunnelCreatorConfig config = new TunnelCreatorConfig(numHops, false);
|
||||
for (int i = 0; i < numHops; i++) {
|
||||
config.setPeer(i, peers[i]);
|
||||
HopConfig cfg = config.getConfig(i);
|
||||
cfg.setExpiration(_context.clock().now() + 60000);
|
||||
cfg.setIVKey(_context.keyGenerator().generateSessionKey());
|
||||
cfg.setLayerKey(_context.keyGenerator().generateSessionKey());
|
||||
if (i > 0)
|
||||
cfg.setReceiveFrom(peers[i-1]);
|
||||
else
|
||||
cfg.setReceiveFrom(null);
|
||||
cfg.setReceiveTunnelId(tunnelIds[i]);
|
||||
if (i < numHops - 1) {
|
||||
cfg.setSendTo(peers[i+1]);
|
||||
cfg.setSendTunnelId(tunnelIds[i+1]);
|
||||
} else {
|
||||
cfg.setSendTo(null);
|
||||
cfg.setSendTunnelId(null);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
package net.i2p.router.tunnel;
|
||||
/*
|
||||
* 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 junit.framework.TestCase;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
/**
|
||||
* Quick unit test for base functionality of inbound tunnel
|
||||
* operation
|
||||
*
|
||||
*/
|
||||
public class InboundTest extends TestCase{
|
||||
private I2PAppContext _context;
|
||||
|
||||
public void setUp() {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
}
|
||||
|
||||
public void testInbound() {
|
||||
int numHops = 8;
|
||||
TunnelCreatorConfig config = prepareConfig(numHops);
|
||||
|
||||
byte orig[] = new byte[128];
|
||||
byte message[] = new byte[128];
|
||||
_context.random().nextBytes(orig); // might as well fill the IV
|
||||
System.arraycopy(orig, 0, message, 0, message.length);
|
||||
|
||||
InboundGatewayProcessor p = new InboundGatewayProcessor(_context, config.getConfig(0));
|
||||
p.process(message, 0, message.length, null);
|
||||
|
||||
for (int i = 1; i < numHops-1; i++) {
|
||||
HopProcessor hop = new HopProcessor(_context, config.getConfig(i));
|
||||
Hash prev = config.getConfig(i).getReceiveFrom();
|
||||
assertTrue(hop.process(message, 0, message.length, prev));
|
||||
}
|
||||
|
||||
InboundEndpointProcessor end = new InboundEndpointProcessor(_context, config);
|
||||
assertTrue(end.retrievePreprocessedData(message, 0, message.length, config.getPeer(numHops-2)));
|
||||
|
||||
assertTrue(DataHelper.eq(orig, 16, message, 16, orig.length - 16));
|
||||
}
|
||||
|
||||
private TunnelCreatorConfig prepareConfig(int numHops) {
|
||||
Hash peers[] = new Hash[numHops];
|
||||
byte tunnelIds[][] = new byte[numHops][4];
|
||||
for (int i = 0; i < numHops; i++) {
|
||||
peers[i] = new Hash();
|
||||
peers[i].setData(new byte[Hash.HASH_LENGTH]);
|
||||
_context.random().nextBytes(peers[i].getData());
|
||||
_context.random().nextBytes(tunnelIds[i]);
|
||||
}
|
||||
|
||||
TunnelCreatorConfig config = new TunnelCreatorConfig(numHops, false);
|
||||
for (int i = 0; i < numHops; i++) {
|
||||
config.setPeer(i, peers[i]);
|
||||
HopConfig cfg = config.getConfig(i);
|
||||
cfg.setExpiration(_context.clock().now() + 60000);
|
||||
cfg.setIVKey(_context.keyGenerator().generateSessionKey());
|
||||
cfg.setLayerKey(_context.keyGenerator().generateSessionKey());
|
||||
if (i > 0)
|
||||
cfg.setReceiveFrom(peers[i-1]);
|
||||
else
|
||||
cfg.setReceiveFrom(null);
|
||||
cfg.setReceiveTunnelId(tunnelIds[i]);
|
||||
if (i < numHops - 1) {
|
||||
cfg.setSendTo(peers[i+1]);
|
||||
cfg.setSendTunnelId(tunnelIds[i+1]);
|
||||
} else {
|
||||
cfg.setSendTo(null);
|
||||
cfg.setSendTunnelId(null);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
@@ -0,0 +1,211 @@
|
||||
package net.i2p.router.tunnel;
|
||||
/*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.TunnelId;
|
||||
import net.i2p.data.i2np.DataMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
|
||||
/**
|
||||
* Quick unit test for base functionality of outbound tunnel
|
||||
* operation
|
||||
*/
|
||||
public class OutboundGatewayTest extends TestCase{
|
||||
private I2PAppContext _context;
|
||||
private TunnelCreatorConfig _config;
|
||||
private TunnelGateway.QueuePreprocessor _preprocessor;
|
||||
private TunnelGateway.Sender _sender;
|
||||
private TestReceiver _receiver;
|
||||
private TunnelGateway _gw;
|
||||
|
||||
public void setUp() {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_config = prepareConfig(8);
|
||||
_preprocessor = new TrivialPreprocessor(_context);
|
||||
_sender = new OutboundSender(_context, _config);
|
||||
_receiver = new TestReceiver(_config);
|
||||
_gw = new TunnelGateway(_context, _preprocessor, _sender, _receiver);
|
||||
}
|
||||
|
||||
public void testSmall() {
|
||||
int runCount = 1;
|
||||
|
||||
List messages = new ArrayList(runCount);
|
||||
long start = _context.clock().now();
|
||||
|
||||
for (int i = 0; i < runCount; i++) {
|
||||
DataMessage m = new DataMessage(_context);
|
||||
m.setData(new byte[64]);
|
||||
java.util.Arrays.fill(m.getData(), (byte)0xFF);
|
||||
m.setMessageExpiration(_context.clock().now() + 60*1000);
|
||||
m.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
byte data[] = m.toByteArray();
|
||||
messages.add(m);
|
||||
_gw.add(m, null, null);
|
||||
}
|
||||
|
||||
long time = _context.clock().now() - start;
|
||||
|
||||
List received = _receiver.clearReceived();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
assertTrue(received.contains(((I2NPMessage)messages.get(i))));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRouter() {
|
||||
int runCount = 1;
|
||||
|
||||
List messages = new ArrayList(runCount);
|
||||
long start = _context.clock().now();
|
||||
|
||||
for (int i = 0; i < runCount; i++) {
|
||||
DataMessage m = new DataMessage(_context);
|
||||
m.setData(new byte[64]);
|
||||
java.util.Arrays.fill(m.getData(), (byte)0xFF);
|
||||
m.setMessageExpiration(_context.clock().now() + 60*1000);
|
||||
m.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
Hash to = new Hash(new byte[Hash.HASH_LENGTH]);
|
||||
java.util.Arrays.fill(to.getData(), (byte)0xFF);
|
||||
byte data[] = m.toByteArray();
|
||||
messages.add(m);
|
||||
_gw.add(m, to, null);
|
||||
}
|
||||
|
||||
long time = _context.clock().now() - start;
|
||||
|
||||
List received = _receiver.clearReceived();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
assertTrue(received.contains(((I2NPMessage)messages.get(i))));
|
||||
}
|
||||
}
|
||||
|
||||
public void testTunnel() {
|
||||
int runCount = 1;
|
||||
|
||||
List messages = new ArrayList(runCount);
|
||||
long start = _context.clock().now();
|
||||
|
||||
for (int i = 0; i < runCount; i++) {
|
||||
DataMessage m = new DataMessage(_context);
|
||||
m.setData(new byte[64]);
|
||||
java.util.Arrays.fill(m.getData(), (byte)0xFF);
|
||||
m.setMessageExpiration(_context.clock().now() + 60*1000);
|
||||
m.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
Hash to = new Hash(new byte[Hash.HASH_LENGTH]);
|
||||
java.util.Arrays.fill(to.getData(), (byte)0xFF);
|
||||
TunnelId tunnel = new TunnelId(42);
|
||||
byte data[] = m.toByteArray();
|
||||
messages.add(m);
|
||||
_gw.add(m, to, tunnel);
|
||||
}
|
||||
|
||||
long time = _context.clock().now() - start;
|
||||
|
||||
List received = _receiver.clearReceived();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
assertTrue(received.contains(((I2NPMessage)messages.get(i))));
|
||||
}
|
||||
}
|
||||
|
||||
public void testLarge() {
|
||||
int runCount = 1;
|
||||
|
||||
List messages = new ArrayList(runCount);
|
||||
long start = _context.clock().now();
|
||||
|
||||
for (int i = 0; i < runCount; i++) {
|
||||
DataMessage m = new DataMessage(_context);
|
||||
m.setData(new byte[1024]);
|
||||
java.util.Arrays.fill(m.getData(), (byte)0xFF);
|
||||
m.setMessageExpiration(_context.clock().now() + 60*1000);
|
||||
m.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
byte data[] = m.toByteArray();
|
||||
messages.add(m);
|
||||
_gw.add(m, null, null);
|
||||
}
|
||||
|
||||
long time = _context.clock().now() - start;
|
||||
//try { Thread.sleep(60*1000); } catch (Exception e) {}
|
||||
|
||||
List received = _receiver.clearReceived();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
assertTrue(received.contains(((I2NPMessage)messages.get(i))));
|
||||
}
|
||||
}
|
||||
|
||||
private class TestReceiver implements TunnelGateway.Receiver, FragmentHandler.DefragmentedReceiver {
|
||||
private TunnelCreatorConfig _config;
|
||||
private FragmentHandler _handler;
|
||||
private List _received;
|
||||
public TestReceiver(TunnelCreatorConfig config) {
|
||||
_config = config;
|
||||
_handler = new FragmentHandler(_context, TestReceiver.this);
|
||||
_received = new ArrayList(1000);
|
||||
}
|
||||
public void receiveEncrypted(byte[] encrypted) {
|
||||
// fake all the hops...
|
||||
|
||||
for (int i = 1; i < _config.getLength(); i++) {
|
||||
HopProcessor hop = new HopProcessor(_context, _config.getConfig(i));
|
||||
assertTrue(hop.process(encrypted, 0, encrypted.length, _config.getConfig(i).getReceiveFrom()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
_handler.receiveTunnelMessage(encrypted, 0, encrypted.length);
|
||||
}
|
||||
public void receiveComplete(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) {
|
||||
_received.add(msg);
|
||||
}
|
||||
public List clearReceived() {
|
||||
List rv = _received;
|
||||
_received = new ArrayList();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
private TunnelCreatorConfig prepareConfig(int numHops) {
|
||||
Hash peers[] = new Hash[numHops];
|
||||
byte tunnelIds[][] = new byte[numHops][4];
|
||||
for (int i = 0; i < numHops; i++) {
|
||||
peers[i] = new Hash();
|
||||
peers[i].setData(new byte[Hash.HASH_LENGTH]);
|
||||
_context.random().nextBytes(peers[i].getData());
|
||||
_context.random().nextBytes(tunnelIds[i]);
|
||||
}
|
||||
|
||||
TunnelCreatorConfig config = new TunnelCreatorConfig(numHops, false);
|
||||
for (int i = 0; i < numHops; i++) {
|
||||
config.setPeer(i, peers[i]);
|
||||
HopConfig cfg = config.getConfig(i);
|
||||
cfg.setExpiration(_context.clock().now() + 60000);
|
||||
cfg.setIVKey(_context.keyGenerator().generateSessionKey());
|
||||
cfg.setLayerKey(_context.keyGenerator().generateSessionKey());
|
||||
if (i > 0)
|
||||
cfg.setReceiveFrom(peers[i-1]);
|
||||
else
|
||||
cfg.setReceiveFrom(null);
|
||||
cfg.setReceiveTunnelId(tunnelIds[i]);
|
||||
if (i < numHops - 1) {
|
||||
cfg.setSendTo(peers[i+1]);
|
||||
cfg.setSendTunnelId(tunnelIds[i+1]);
|
||||
} else {
|
||||
cfg.setSendTo(null);
|
||||
cfg.setSendTunnelId(null);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
package net.i2p.router.tunnel;
|
||||
/*
|
||||
* 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 junit.framework.TestCase;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
/**
|
||||
* Quick unit test for base functionality of outbound tunnel
|
||||
* operation
|
||||
*
|
||||
*/
|
||||
public class OutboundTest extends TestCase{
|
||||
private I2PAppContext _context;
|
||||
|
||||
public void setUp() {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
}
|
||||
|
||||
public void testOutbound() {
|
||||
int numHops = 8;
|
||||
TunnelCreatorConfig config = prepareConfig(numHops);
|
||||
|
||||
byte orig[] = new byte[1024];
|
||||
byte message[] = new byte[1024];
|
||||
_context.random().nextBytes(orig); // might as well fill the IV
|
||||
System.arraycopy(orig, 0, message, 0, message.length);
|
||||
|
||||
OutboundGatewayProcessor p = new OutboundGatewayProcessor(_context, config);
|
||||
p.process(message, 0, message.length);
|
||||
|
||||
for (int i = 0; i < numHops; i++) {
|
||||
HopProcessor hop = new HopProcessor(_context, config.getConfig(i));
|
||||
Hash prev = config.getConfig(i).getReceiveFrom();
|
||||
assertTrue(hop.process(message, 0, message.length, prev));
|
||||
}
|
||||
|
||||
boolean eq = DataHelper.eq(orig, 16, message, 16, orig.length - 16);
|
||||
}
|
||||
|
||||
private TunnelCreatorConfig prepareConfig(int numHops) {
|
||||
Hash peers[] = new Hash[numHops];
|
||||
byte tunnelIds[][] = new byte[numHops][4];
|
||||
for (int i = 0; i < numHops; i++) {
|
||||
peers[i] = new Hash();
|
||||
peers[i].setData(new byte[Hash.HASH_LENGTH]);
|
||||
_context.random().nextBytes(peers[i].getData());
|
||||
_context.random().nextBytes(tunnelIds[i]);
|
||||
}
|
||||
|
||||
TunnelCreatorConfig config = new TunnelCreatorConfig(numHops, false);
|
||||
for (int i = 0; i < numHops; i++) {
|
||||
config.setPeer(i, peers[i]);
|
||||
HopConfig cfg = config.getConfig(i);
|
||||
cfg.setExpiration(_context.clock().now() + 60000);
|
||||
cfg.setIVKey(_context.keyGenerator().generateSessionKey());
|
||||
cfg.setLayerKey(_context.keyGenerator().generateSessionKey());
|
||||
if (i > 0)
|
||||
cfg.setReceiveFrom(peers[i-1]);
|
||||
else
|
||||
cfg.setReceiveFrom(null);
|
||||
cfg.setReceiveTunnelId(tunnelIds[i]);
|
||||
if (i < numHops - 1) {
|
||||
cfg.setSendTo(peers[i+1]);
|
||||
cfg.setSendTunnelId(tunnelIds[i+1]);
|
||||
} else {
|
||||
cfg.setSendTo(null);
|
||||
cfg.setSendTunnelId(null);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user