Added ScalaTest support to router build.xml

This commit is contained in:
str4d
2012-07-31 00:06:28 +00:00
parent 5b6e7ba91d
commit 3253f82900
27 changed files with 74 additions and 8 deletions

View File

@@ -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());
}
}

View 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();
}
}

View File

@@ -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(); }
}

View File

@@ -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) {
}
}

View File

@@ -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; }
}

View File

@@ -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();
}
}

View 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"; }
}
}

View File

@@ -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"; }
}

View File

@@ -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
}
}
}

View File

@@ -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());
}
}
}

View 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");
}
}

View File

@@ -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));
}
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View 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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}