package com.limegroup.gnutella.dht; import java.io.IOException; import java.io.InterruptedIOException; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; import junit.framework.Test; import org.limewire.core.settings.ConnectionSettings; import org.limewire.core.settings.DHTSettings; import org.limewire.core.settings.FilterSettings; import org.limewire.core.settings.PingPongSettings; import org.limewire.core.settings.UltrapeerSettings; import org.limewire.gnutella.tests.LimeTestCase; import org.limewire.gnutella.tests.LimeTestUtils; import org.limewire.io.GUID; import org.limewire.mojito.MojitoDHT; import org.limewire.mojito.routing.Contact; import org.limewire.mojito.settings.ContextSettings; import org.limewire.mojito.settings.NetworkSettings; import org.limewire.mojito.util.MojitoUtils; import com.google.inject.AbstractModule; import com.google.inject.Injector; import com.limegroup.gnutella.BlockingConnectionUtils; import com.limegroup.gnutella.ConnectionManager; import com.limegroup.gnutella.ConnectionServices; import com.limegroup.gnutella.LifecycleManager; import com.limegroup.gnutella.NodeAssigner; import com.limegroup.gnutella.connection.BlockingConnection; import com.limegroup.gnutella.connection.BlockingConnectionFactory; import com.limegroup.gnutella.connection.RoutedConnection; import com.limegroup.gnutella.dht.DHTManager.DHTMode; import com.limegroup.gnutella.handshaking.HeadersFactory; import com.limegroup.gnutella.messages.Message; import com.limegroup.gnutella.messages.vendor.CapabilitiesVM; import com.limegroup.gnutella.messages.vendor.CapabilitiesVMFactory; import com.limegroup.gnutella.messages.vendor.DHTContactsMessage; import com.limegroup.gnutella.messages.vendor.PushProxyRequest; import com.limegroup.gnutella.stubs.CapabilitiesVMFactoryImplStub; import com.limegroup.gnutella.util.EmptyResponder; /* * DHT <----> Ultrapeer <----> Leaf * * Everytime an Ultrapeer learns of a new Contact or updates an * existing Contact it will forward it to its leafs */ public class PassiveLeafForwardContactsTest extends LimeTestCase { private static final int PORT = 6667; private List<MojitoDHT> dhts; private Injector injector; private ConnectionManager connectionManager; private ConnectionServices connectionServices; private DHTManager dhtManager; public PassiveLeafForwardContactsTest(String name) { super(name); } public static Test suite() { return buildTestSuite(PassiveLeafForwardContactsTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } @Override protected void setUp() throws Exception { doSettings(); final NodeAssigner na = new NodeAssignerStub(); injector = LimeTestUtils.createInjector(new AbstractModule() { @Override protected void configure() { bind(CapabilitiesVMFactory.class).to(CapabilitiesVMFactoryImplStub.class); bind(NodeAssigner.class).toInstance(na); } }); // start an instance of LimeWire in Ultrapeer mode injector.getInstance(LifecycleManager.class).start(); connectionServices = injector.getInstance(ConnectionServices.class); connectionServices.connect(); // make sure LimeWire is running as an Ultrapeer assertTrue(connectionServices.isSupernode()); assertFalse(connectionServices.isActiveSuperNode()); assertFalse(connectionServices.isShieldedLeaf()); connectionManager = injector.getInstance(ConnectionManager.class); dhtManager = injector.getInstance(DHTManager.class); // Start and bootstrap a bunch of DHT Nodes dhts = Collections.emptyList(); dhts = MojitoUtils.createBootStrappedDHTs(2); } private void doSettings() { ConnectionSettings.CONNECT_ON_STARTUP.setValue(false); UltrapeerSettings.EVER_ULTRAPEER_CAPABLE.setValue(true); UltrapeerSettings.DISABLE_ULTRAPEER_MODE.setValue(false); UltrapeerSettings.FORCE_ULTRAPEER_MODE.setValue(true); UltrapeerSettings.MAX_LEAVES.setValue(33); ConnectionSettings.NUM_CONNECTIONS.setValue(33); ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false); ConnectionSettings.WATCHDOG_ACTIVE.setValue(false); UltrapeerSettings.NEED_MIN_CONNECT_TIME.setValue(false); ConnectionSettings.CONNECT_ON_STARTUP.setValue(false); ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false); ConnectionSettings.WATCHDOG_ACTIVE.setValue(false); PingPongSettings.PINGS_ACTIVE.setValue(false); ConnectionSettings.EVER_ACCEPTED_INCOMING.setValue(false); FilterSettings.BLACK_LISTED_IP_ADDRESSES.set( new String[] {"*.*.*.*"}); FilterSettings.WHITE_LISTED_IP_ADDRESSES.set( new String[] {"127.*.*.*"}); org.limewire.core.settings.NetworkSettings.PORT.setValue(PORT); ConnectionSettings.FORCED_PORT.setValue(PORT); assertEquals("unexpected port", PORT, org.limewire.core.settings.NetworkSettings.PORT.getValue()); DHTSettings.DISABLE_DHT_USER.setValue(false); DHTSettings.DISABLE_DHT_NETWORK.setValue(false); DHTSettings.ENABLE_PASSIVE_DHT_MODE.setValue(true); DHTSettings.ENABLE_PASSIVE_LEAF_DHT_MODE.setValue(true); DHTSettings.PERSIST_ACTIVE_DHT_ROUTETABLE.setValue(false); DHTSettings.PERSIST_DHT_DATABASE.setValue(false); ContextSettings.SHUTDOWN_MESSAGES_MULTIPLIER.setValue(0); NetworkSettings.FILTER_CLASS_C.setValue(false); NetworkSettings.LOCAL_IS_PRIVATE.setValue(false); // We're working on the loopback. Everything should be done // in less than 500ms NetworkSettings.DEFAULT_TIMEOUT.setValue(500); // Nothing should take longer than 1.5 seconds. If we start seeing // LockTimeoutExceptions on the loopback then check this Setting! ContextSettings.WAIT_ON_LOCK.setValue(1500); } @Override protected void tearDown() throws Exception { for (MojitoDHT dht : dhts) { dht.close(); } dhts = null; injector.getInstance(LifecycleManager.class).shutdown(); } public void testForwardContacts() throws Exception { // There should be no connections assertEquals(0, connectionManager.getNumConnections()); // Connect a leaf Node to the Ultrapeer BlockingConnection out = createLeafConnection(); try { assertTrue(out.isOpen()); assertTrue(out.isOutgoing()); BlockingConnectionUtils.drain(out); // There should be one connection now assertEquals(1, connectionManager.getNumConnections()); assertTrue(connectionServices.isActiveSuperNode()); // Check a few more things RoutedConnection in = connectionManager.getConnections().get(0); assertFalse(in.isOutgoing()); assertTrue(in.isSupernodeClientConnection()); assertEquals(-1, in.getConnectionCapabilities().remoteHostIsPassiveLeafNode()); assertFalse(in.isPushProxyFor()); // Pretend the leaf is running in PASSIVE_LEAF mode. addPassiveLeafCapability(); // Tell our Ultrapeer that we've PASSIVE_LEAF mode enabled CapabilitiesVM vm = injector.getInstance(CapabilitiesVMFactory.class).getCapabilitiesVM(); assertEquals(0, vm.isPassiveLeafNode()); out.send(vm); out.flush(); // A second requirement is that the given Ultrapeer is our // Push Proxy out.send(new PushProxyRequest(new GUID(GUID.makeGuid()))); out.flush(); Thread.sleep(250); // And did it work? assertNotEquals(-1, in.getConnectionCapabilities().remoteHostIsPassiveLeafNode()); assertTrue(in.isPushProxyFor()); // we expect to get the first dht contacts msg about a minute from now long firstMsg = System.currentTimeMillis() + 58000; dhtManager.start(DHTMode.PASSIVE); Thread.sleep(250); assertEquals(DHTMode.PASSIVE, dhtManager.getDHTMode()); // Bootstrap the Ultrapeer dhtManager.getMojitoDHT().bootstrap(dhts.get(0).getContactAddress()).get(); // ------------------------------------- // the leaf should not receive a dht contacts msg for about a minute try { long now = System.currentTimeMillis(); while( now < firstMsg) { assertNotInstanceof(DHTContactsMessage.class, out.receive((int)(firstMsg - now))); now = System.currentTimeMillis(); } } catch (IOException expected){} // And check what the leaf is receiving... Set<Contact> nodes = new HashSet<Contact>(); while(true) { Message msg = out.receive(10000); if (msg instanceof DHTContactsMessage) { DHTContactsMessage message = (DHTContactsMessage)msg; assertEquals(10,message.getContacts().size()); nodes.addAll(message.getContacts()); break; } } // the ultrapeer id should not be sent as ups are passive. for (Contact c : nodes) assertFalse(dhtManager.getMojitoDHT().getLocalNodeID().equals(c.getNodeID())); } finally { out.close(); } } public void testDoNotSendCapabilitiesVM() throws Exception { // There should be no connections assertEquals(0, connectionManager.getNumConnections()); // Connect a leaf Node to the Ultrapeer BlockingConnection out = createLeafConnection(); try { assertTrue(out.isOpen()); assertTrue(out.isOutgoing()); BlockingConnectionUtils.drain(out); // There should be one connection now assertEquals(1, connectionManager.getNumConnections()); assertTrue(connectionServices.isActiveSuperNode()); // Check a few more things RoutedConnection in = connectionManager.getConnections().get(0); assertFalse(in.isOutgoing()); assertTrue(in.isSupernodeClientConnection()); assertEquals(-1, in.getConnectionCapabilities().remoteHostIsPassiveLeafNode()); assertFalse(in.isPushProxyFor()); // --- WE DO NOT PRETEND TO BE IN PASSIVE_LEAF MODE !!! --- // A second requirement is that the given Ultrapeer is our // Push Proxy out.send(new PushProxyRequest(new GUID(GUID.makeGuid()))); out.flush(); Thread.sleep(2000); // And did it work? assertEquals(-1, in.getConnectionCapabilities().remoteHostIsPassiveLeafNode()); assertTrue(in.isPushProxyFor()); dhtManager.start(DHTMode.PASSIVE); Thread.sleep(350); assertEquals(DHTMode.PASSIVE, dhtManager.getDHTMode()); // Bootstrap the Ultrapeer dhtManager.getMojitoDHT().bootstrap(dhts.get(0).getContactAddress()).get(); // ------------------------------------- // And check what the leaf is receiving try { while(true) { Message msg = out.receive(2000); if (msg instanceof DHTContactsMessage) { fail("Should not have received a DHTContactsMessage: " + msg); } } } catch (InterruptedIOException expected) { } } finally { out.close(); } } public void testDoNotSendPushProxyRequest() throws Exception { // There should be no connections assertEquals(0, connectionManager.getNumConnections()); // Connect a leaf Node to the Ultrapeer BlockingConnection out = createLeafConnection(); try { assertTrue(out.isOpen()); assertTrue(out.isOutgoing()); BlockingConnectionUtils.drain(out); // There should be one connection now assertEquals(1, connectionManager.getNumConnections()); assertTrue(connectionServices.isActiveSuperNode()); // Check a few more things RoutedConnection in = connectionManager.getConnections().get(0); assertFalse(in.isOutgoing()); assertTrue(in.isSupernodeClientConnection()); assertEquals(-1, in.getConnectionCapabilities().remoteHostIsPassiveLeafNode()); assertFalse(in.isPushProxyFor()); // pretend the leaf is running in PASSIVE_LEAF mode. addPassiveLeafCapability(); // Tell our Ultrapeer that we've PASSIVE_LEAF mode enabled CapabilitiesVM vm = injector.getInstance(CapabilitiesVMFactory.class).getCapabilitiesVM(); assertEquals(0, vm.isPassiveLeafNode()); out.send(vm); out.flush(); // --- But don't tell the Ultrapeer its our Push Proxy! --- Thread.sleep(2000); // And did it work? assertNotEquals(-1, in.getConnectionCapabilities().remoteHostIsPassiveLeafNode()); assertFalse(in.isPushProxyFor()); dhtManager.start(DHTMode.PASSIVE); Thread.sleep(250); assertEquals(DHTMode.PASSIVE, dhtManager.getDHTMode()); // Bootstrap the Ultrapeer dhtManager.getMojitoDHT().bootstrap(dhts.get(0).getContactAddress()).get(); // ------------------------------------- // And check what the leaf is receiving try { while(true) { Message msg = out.receive(2000); if (msg instanceof DHTContactsMessage) { fail("Should not have received a DHTContactsMessage: " + msg); } } } catch (InterruptedIOException expected) { } } finally { out.close(); } } private void addPassiveLeafCapability() { CapabilitiesVMFactoryImplStub factory = (CapabilitiesVMFactoryImplStub) injector.getInstance(CapabilitiesVMFactory.class); factory.addMessageBlock(DHTMode.PASSIVE_LEAF.getCapabilityName(), 0); } protected BlockingConnection createLeafConnection() throws Exception { return createConnection(injector.getInstance(HeadersFactory.class).createLeafHeaders("localhost")); } /** Builds a single connection with the given headers. */ protected BlockingConnection createConnection(Properties headers) throws Exception { BlockingConnection c = injector.getInstance(BlockingConnectionFactory.class).createConnection("localhost", PORT); c.initialize(headers, new EmptyResponder(), 1000); return c; } private class NodeAssignerStub implements NodeAssigner { public boolean isTooGoodUltrapeerToPassUp() { return false; } public void start() { } public void stop() { } } }