diff --git a/router/java/src/net/i2p/router/tunnel/OutboundGatewayTest.java b/router/java/src/net/i2p/router/tunnel/OutboundGatewayTest.java new file mode 100644 index 000000000..39c1085b0 --- /dev/null +++ b/router/java/src/net/i2p/router/tunnel/OutboundGatewayTest.java @@ -0,0 +1,250 @@ +package net.i2p.router.tunnel; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import net.i2p.I2PAppContext; +import net.i2p.data.Base64; +import net.i2p.data.ByteArray; +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; +import net.i2p.util.Log; + +/** + * Quick unit test for base functionality of outbound tunnel + * operation + */ +public class OutboundGatewayTest { + private I2PAppContext _context; + private Log _log; + private TunnelCreatorConfig _config; + private TunnelGateway.QueuePreprocessor _preprocessor; + private TunnelGateway.Sender _sender; + private TestReceiver _receiver; + private TunnelGateway _gw; + + public OutboundGatewayTest() { + _context = I2PAppContext.getGlobalContext(); + _log = _context.logManager().getLog(OutboundGatewayTest.class); + } + + public void runTest() { + int numHops = 8; + int runCount = 1; + _config = prepareConfig(numHops); + _preprocessor = new TrivialPreprocessor(_context); + _sender = new OutboundSender(_context, _config); + _receiver = new TestReceiver(_config); + _gw = new TunnelGateway(_context, _preprocessor, _sender, _receiver); + + // single fragment + testSmall(runCount); + // includes target router instructions + testRouter(runCount); + // includes target router & tunnel instructions + testTunnel(runCount); + // multiple fragments + testLarge(runCount); + + try { Thread.sleep(5*1000); } catch (Exception e) {} + } + + private void testSmall(int runCount) { + 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(new Date(_context.clock().now() + 60*1000)); + m.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE)); + _log.debug("Sending " + m.getUniqueId()); + byte data[] = m.toByteArray(); + _log.debug("SEND(" + data.length + "): " + Base64.encode(data) + " " + _context.sha().calculateHash(data).toBase64()); + messages.add(m); + _gw.add(m, null, null); + } + + long time = _context.clock().now() - start; + _log.debug("Time for " + runCount + " messages: " + time); + + List received = _receiver.clearReceived(); + for (int i = 0; i < messages.size(); i++) { + if (!received.contains(((I2NPMessage)messages.get(i)))) { + _log.error("Message " + i + " not received"); + } + } + } + + private void testRouter(int runCount) { + 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(new Date(_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); + _log.debug("Sending " + m.getUniqueId() + " to " + to); + byte data[] = m.toByteArray(); + _log.debug("SEND(" + data.length + "): " + Base64.encode(data) + " " + _context.sha().calculateHash(data).toBase64()); + messages.add(m); + _gw.add(m, to, null); + } + + long time = _context.clock().now() - start; + _log.debug("Time for " + runCount + " messages: " + time); + + List received = _receiver.clearReceived(); + for (int i = 0; i < messages.size(); i++) { + if (!received.contains(((I2NPMessage)messages.get(i)))) { + _log.error("Message " + i + " not received"); + } + } + } + + private void testTunnel(int runCount) { + 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(new Date(_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); + _log.debug("Sending " + m.getUniqueId() + " to " + to + "/" + tunnel); + byte data[] = m.toByteArray(); + _log.debug("SEND(" + data.length + "): " + Base64.encode(data) + " " + _context.sha().calculateHash(data).toBase64()); + messages.add(m); + _gw.add(m, to, tunnel); + } + + long time = _context.clock().now() - start; + _log.debug("Time for " + runCount + " messages: " + time); + + List received = _receiver.clearReceived(); + for (int i = 0; i < messages.size(); i++) { + if (!received.contains(((I2NPMessage)messages.get(i)))) { + _log.error("Message " + i + " not received"); + } + } + } + + private void testLarge(int runCount) { + 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(new Date(_context.clock().now() + 60*1000)); + m.setUniqueId(_context.random().nextLong(I2NPMessage.MAX_ID_VALUE)); + _log.debug("Sending " + m.getUniqueId()); + byte data[] = m.toByteArray(); + _log.debug("SEND(" + data.length + "): " + Base64.encode(data) + " " + _context.sha().calculateHash(data).toBase64()); + messages.add(m); + _gw.add(m, null, null); + } + + long time = _context.clock().now() - start; + try { Thread.sleep(60*1000); } catch (Exception e) {} + _log.debug("Time for " + runCount + " messages: " + time); + + List received = _receiver.clearReceived(); + for (int i = 0; i < messages.size(); i++) { + if (!received.contains(((I2NPMessage)messages.get(i)))) { + _log.error("Message " + i + " not received"); + } + } + } + + 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 = 0; i < _config.getLength(); i++) { + HopProcessor hop = new HopProcessor(_context, _config.getConfig(i)); + boolean ok = hop.process(encrypted, 0, encrypted.length, _config.getConfig(i).getReceiveFrom()); + if (!ok) + _log.error("Error processing at hop " + i); + //else + // _log.info("Processing OK at hop " + i); + } + + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Received " + Base64.encode(encrypted)); + + _handler.receiveTunnelMessage(encrypted, 0, encrypted.length); + _log.debug("\n\ndone receiving message\n\n"); + } + public void receiveComplete(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Completed " + msg.getUniqueId() + " to " + toRouter + "/" + 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; + } + + public static void main(String args[]) { + OutboundGatewayTest test = new OutboundGatewayTest(); + test.runTest(); + } +} diff --git a/router/java/src/net/i2p/router/tunnel/OutboundSender.java b/router/java/src/net/i2p/router/tunnel/OutboundSender.java new file mode 100644 index 000000000..5aca8933c --- /dev/null +++ b/router/java/src/net/i2p/router/tunnel/OutboundSender.java @@ -0,0 +1,30 @@ +package net.i2p.router.tunnel; + +import net.i2p.I2PAppContext; +import net.i2p.data.Base64; +import net.i2p.util.Log; + +/** + * Receive the preprocessed data for an outbound gateway, encrypt all of the + * layers, and forward it on to the first hop. + * + */ +public class OutboundSender implements TunnelGateway.Sender { + private I2PAppContext _context; + private Log _log; + private OutboundGatewayProcessor _processor; + + static final boolean USE_ENCRYPTION = HopProcessor.USE_ENCRYPTION; + + public OutboundSender(I2PAppContext ctx, TunnelCreatorConfig config) { + _context = ctx; + _log = ctx.logManager().getLog(OutboundSender.class); + _processor = new OutboundGatewayProcessor(_context, config); + } + + public void sendPreprocessed(byte[] preprocessed, TunnelGateway.Receiver receiver) { + if (USE_ENCRYPTION) + _processor.process(preprocessed, 0, preprocessed.length); + receiver.receiveEncrypted(preprocessed); + } +}