package com.limegroup.gnutella; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileWriter; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import junit.framework.Test; import org.limewire.collection.FixedsizePriorityQueue; import org.limewire.core.settings.ConnectionSettings; import org.limewire.core.settings.FilterSettings; import org.limewire.io.GGEP; import org.limewire.io.GUID; import org.limewire.io.IpPortImpl; import org.limewire.io.LocalSocketAddressProvider; import org.limewire.io.NetworkInstanceUtils; import org.limewire.util.ByteUtils; import org.limewire.util.CommonUtils; import org.limewire.util.PrivilegedAccessor; import com.google.inject.AbstractModule; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Provider; import com.google.inject.Singleton; import com.limegroup.gnutella.bootstrap.UDPHostCache; import com.limegroup.gnutella.bootstrap.UDPHostCacheFactory; import com.limegroup.gnutella.dht.DHTManager.DHTMode; import com.limegroup.gnutella.messages.GGEPKeys; import com.limegroup.gnutella.messages.Message; import com.limegroup.gnutella.messages.MessageFactory; import com.limegroup.gnutella.messages.PingReply; import com.limegroup.gnutella.messages.PingReplyFactory; import com.limegroup.gnutella.messages.PingRequest; import com.limegroup.gnutella.messages.PingRequestFactory; import com.limegroup.gnutella.messages.Message.Network; import com.limegroup.gnutella.stubs.ConnectionManagerStub; import com.limegroup.gnutella.util.LimeTestCase; public class HostCatcherTest extends LimeTestCase { private HostCatcher hostCatcher; private Injector injector; public HostCatcherTest(String name) { super(name); } public static Test suite() { return buildTestSuite(HostCatcherTest.class); } public static void main(String argv[]) { junit.textui.TestRunner.run(suite()); } /** * Returns a new HostCatcher connected to stubs. YOU MAY WANT TO CALL EXPIRE to force bootstrap pongs. */ public void setUp() { // explicitly allow all ips to test. FilterSettings.BLACK_LISTED_IP_ADDRESSES.setValue(new String[] {}); FilterSettings.WHITE_LISTED_IP_ADDRESSES.setValue(new String[] { "*.*" }); HostCatcher.DEBUG = true; injector = LimeTestUtils.createInjector(); hostCatcher = injector.getInstance(HostCatcher.class); hostCatcher.start(); } /** * Test the method for putting hosts on probation. * * @throws Exception if an error occurs */ public void testPutHostOnProbation() throws Exception { String ipStart = "34.56."; int penultimatetByte; for(int i=0; i<HostCatcher.PROBATION_HOSTS_SIZE; i++) { // Add a bunch of unique endpoints. if(i >= 512) { penultimatetByte = 2; } else if(i >= 255) { penultimatetByte = 1; } else { penultimatetByte = 0; } int lastByte = i%256; Endpoint curHost = new Endpoint(ipStart+penultimatetByte+"."+lastByte, 6346); hostCatcher.putHostOnProbation(curHost); } Set probatedHosts = (Set)PrivilegedAccessor.getValue(hostCatcher, "PROBATION_HOSTS"); assertEquals("unexpected size", HostCatcher.PROBATION_HOSTS_SIZE, probatedHosts.size()); // Start adding slightly different IPs ipStart = "35.56.5."; for(int i=0; i<10; i++) { Endpoint curHost = new Endpoint(ipStart+i, 6346); hostCatcher.putHostOnProbation(curHost); assertEquals("unexpected size", HostCatcher.PROBATION_HOSTS_SIZE, probatedHosts.size()); } } /** * Test the method for expiring hosts * * @throws Exception if an error occurs */ public void testExpireHosts() throws Exception { String ipStart = "34.56."; int penultimatetByte; for(int i=0; i<HostCatcher.EXPIRED_HOSTS_SIZE; i++) { // Add a bunch of unique endpoints. if(i >= 512) { penultimatetByte = 2; } else if(i >= 255) { penultimatetByte = 1; } else { penultimatetByte = 0; } int lastByte = i%256; Endpoint curHost = new Endpoint(ipStart+penultimatetByte+"."+lastByte, 6346); hostCatcher.expireHost(curHost); } Set expiredHosts = (Set)PrivilegedAccessor.getValue(hostCatcher, "EXPIRED_HOSTS"); assertEquals("unexpected size", HostCatcher.EXPIRED_HOSTS_SIZE, expiredHosts.size()); // Start adding slightly different IPs ipStart = "35.56.5."; for(int i=0; i<10; i++) { Endpoint curHost = new Endpoint(ipStart+i, 6346); hostCatcher.putHostOnProbation(curHost); assertEquals("unexpected size", HostCatcher.EXPIRED_HOSTS_SIZE, expiredHosts.size()); } } /** * Tests to make sure that the UDP Host Cache is used * if we know of any host caches. */ public void testUDPCachesUsed() throws Exception { // Use a different setup... injector = LimeTestUtils.createInjector(new AbstractModule() { @Override protected void configure() { bind(UDPHostCacheFactory.class).to(UDPHostCacheFactoryStub.class); } }); hostCatcher = injector.getInstance(HostCatcher.class); hostCatcher.start(); assertEquals(0, hostCatcher.getNumHosts()); StubUDPBootstrapper udp = (StubUDPBootstrapper)injector.getInstance(HostCatcher.class).getUdpHostCache(); Endpoint firstHost = hostCatcher.getAnEndpoint(); assertTrue(udp.fetched); assertEquals(udp.host, firstHost.getAddress()); udp.fetched = false; // Since udp was done quickly and only gave us one host (and we // just used it), the next request will spark a GW request. Endpoint second = hostCatcher.getAnEndpointImmediate(null); assertNull(second); Thread.sleep(5000); // just to make sure it doesn't trigger a fetch later assertFalse(udp.fetched); udp.expired = false; // Now another fetch will wait until time passes enough to retry // udp (too long before retrying a GW) Endpoint thirdHost = hostCatcher.getAnEndpoint(); assertTrue(udp.fetched); assertEquals(udp.host, thirdHost.getAddress()); } /** * Tests to make sure that we ignore hosts that have expired. * * @throws Exception if any error occurs */ public void testIgnoreExpiredHosts() throws Exception { Endpoint expiredHost = new Endpoint("20.4.5.7", 6346); hostCatcher.start(); hostCatcher.add(expiredHost,true); assertEquals("unexpected number of hosts", 1, hostCatcher.getNumHosts()); Endpoint accessedHost = hostCatcher.getAnEndpoint(); assertNotNull("host should not be null", accessedHost); assertEquals("unexpected number of hosts", 0, hostCatcher.getNumHosts()); hostCatcher.expireHost(expiredHost); hostCatcher.add(expiredHost, true); assertEquals("unexpected number of hosts", 0, hostCatcher.getNumHosts()); } /** * Tests to make sure that we ignore hosts that have been put on probation. * * @throws Exception if any error occurs */ public void testIgnoreProbatedHosts() throws Exception { Endpoint probatedHost = new Endpoint("20.4.5.7", 6346); hostCatcher.start(); hostCatcher.add(probatedHost,true); assertEquals("unexpected number of hosts", 1, hostCatcher.getNumHosts()); Endpoint accessedHost = hostCatcher.getAnEndpoint(); assertNotNull("host should not be null", accessedHost); assertEquals("unexpected number of hosts", 0, hostCatcher.getNumHosts()); hostCatcher.putHostOnProbation(probatedHost); hostCatcher.add(probatedHost, true); assertEquals("unexpected number of hosts", 0, hostCatcher.getNumHosts()); } /** * Tests to make sure that hosts that are put on probation are properly * recovered. * * @throws Exception if any error occurs */ public void testRecoveryOfHostsOnProbation() throws Exception { long waitTime = 100; PrivilegedAccessor.setValue(HostCatcher.class, "PROBATION_RECOVERY_WAIT_TIME", new Long(waitTime)); long interval = 20000; PrivilegedAccessor.setValue(HostCatcher.class, "PROBATION_RECOVERY_TIME", new Long(interval)); Endpoint probatedHost = new Endpoint("20.4.5.7", 6346); // Put the host on probation. hostCatcher.putHostOnProbation(probatedHost); hostCatcher.add(probatedHost, true); // And make sure that it did not get added assertEquals("unexpected number of hosts", 0, hostCatcher.getNumHosts()); // Start the probation recovery sequence... hostCatcher.start(); // Sleep until the recovery operation takes place... Thread.sleep(waitTime+200); // Finally, make sure we are then able to add the host that was // formerly on probation. hostCatcher.add(probatedHost, true); assertEquals("unexpected number of hosts", 1, hostCatcher.getNumHosts()); } /** * Tests to make sure that recovering used hosts works as expected. This * method is used when the user's network connection goes down. * * @throws Exception if any error occurs */ public void testRecoversUsedHosts() throws Exception { // write data to gnutella.net hostCatcher.add(new Endpoint("18.239.0.1"), false); hostCatcher.add(new Endpoint("18.239.0.2"), false); hostCatcher.add(new Endpoint("18.239.0.3"), false); hostCatcher.add(new Endpoint("18.239.0.4"), false); hostCatcher.add(new Endpoint("18.239.0.5"), false); hostCatcher.add(new Endpoint("18.239.0.6"), false); hostCatcher.add(new Endpoint("18.239.0.7"), false); hostCatcher.add(new Endpoint("18.239.0.8"), false); hostCatcher.add(new Endpoint("18.239.0.9"), false); hostCatcher.add(new Endpoint("18.239.0.10"), false); hostCatcher.write(); int numHosts = hostCatcher.getNumHosts(); for(int i=0; i<10; i++) { hostCatcher.getAnEndpoint(); } assertEquals("hosts should be 0", 0, hostCatcher.getNumHosts()); hostCatcher.recoverHosts(); assertEquals("hosts should have been recovered", numHosts, hostCatcher.getNumHosts()); } /** Tests that FixedsizePriorityQueue can hold two endpoints with same * priority but different ip's. This was a problem at one point. */ public void testEndpointPriorities() { ExtendedEndpoint e1=new ExtendedEndpoint("18.239.0.146", 6346); ExtendedEndpoint e2=new ExtendedEndpoint("18.239.0.147", 6347); assertNotEquals("e1 vs e2", e1, e2); assertNotEquals("e2 vs e1", e2, e1); FixedsizePriorityQueue<ExtendedEndpoint> queue=new FixedsizePriorityQueue<ExtendedEndpoint>( ExtendedEndpoint.priorityComparator(), 10); assertNull(queue.insert(e1)); assertNull(queue.insert(e2)); assertEquals(2, queue.size()); } public void testAddPriorities() { PingReplyFactory pingReplyFactory = injector.getInstance(PingReplyFactory.class); // HostCatcher should ignore attempts to add hosts with private // addresses. hostCatcher.add(new Endpoint("192.168.0.1"), false); assertEquals("private endpoint added as ultrapeer", 0, hostCatcher.getNumUltrapeerHosts()); assertEquals("private endpoint added at all", 0, hostCatcher.getNumHosts()); setUp(); // Adding a normal host should add 1 more to numNormalHosts hostCatcher.add(new Endpoint("18.239.0.1"), false); assertEquals("normal endpoint added as ultrapeer", 0, hostCatcher.getNumUltrapeerHosts()); setUp(); // Adding a ultrapeer should add 1 more to numUltrapeerHosts hostCatcher.add(new Endpoint("18.239.0.1"), true); assertEquals("ultrapeer endpoint not added as ultrapeer", 1, hostCatcher.getNumUltrapeerHosts()); //PingReply's. setUp(); // Adding a private should add 1 more to numPrivateHosts hostCatcher.add(pingReplyFactory.createExternal(new byte[16], (byte)3, 6346, new byte[] {(byte)192,(byte)168,(byte)0,(byte)1}, false)); assertEquals("private PingReply added as ultrapeer", 0 ,hostCatcher.getNumUltrapeerHosts()); setUp(); hostCatcher.add(pingReplyFactory.createExternal(new byte[16], (byte)3, 6346, new byte[] {(byte)18,(byte)239,(byte)0,(byte)1}, false)); assertEquals("normal PingReply added as ultrapeer", 0, hostCatcher.getNumUltrapeerHosts()); setUp(); hostCatcher.add(pingReplyFactory.createExternal(new byte[16], (byte)3, 6346, new byte[] {(byte)18,(byte)239,(byte)0,(byte)1}, true)); assertEquals("ultrapeer PingReply not added as ultrapeer", 1, hostCatcher.getNumUltrapeerHosts()); } public void testPermanent() throws Exception { PingReplyFactory pingReplyFactory = injector.getInstance(PingReplyFactory.class); //Systm.out.println("-Testing write of permanent nodes to Gnutella.net"); //1. Create HC, add entries, write to disk. hostCatcher.add(new Endpoint("18.239.0.141", 6341), false);//default time=345 hostCatcher.add(pingReplyFactory.createExternal(GUID.makeGuid(), (byte)7, 6342, new byte[] {(byte)18, (byte)239, (byte)0, (byte)142}, 1000, false)); // duplicate hostCatcher.add(pingReplyFactory.createExternal(GUID.makeGuid(), (byte)7, 6342, new byte[] {(byte)18, (byte)239, (byte)0, (byte)142}, 1000, false)); hostCatcher.add(pingReplyFactory.createExternal(GUID.makeGuid(), (byte)7, 6343, new byte[] {(byte)18, (byte)239, (byte)0, (byte)143}, 30, false)); // duplicate (well, with lower uptime) hostCatcher.add(pingReplyFactory.createExternal(GUID.makeGuid(), (byte)7, 6343, new byte[] {(byte)18, (byte)239, (byte)0, (byte)143}, 30, false)); // private address (ignored) hostCatcher.add(pingReplyFactory.createExternal(GUID.makeGuid(), (byte)7, 6343, new byte[] {(byte)192, (byte)168, (byte)0, (byte)1}, 3000, false)); // udp host caches .. hostCatcher.add(new ExtendedEndpoint("1.2.3.4", 6346).setUDPHostCache(true), false); hostCatcher.add(new ExtendedEndpoint("1.2.3.5", 6341).setUDPHostCache(true), false); // dht capable node ExtendedEndpoint ep = new ExtendedEndpoint("18.239.0.100", 6323, 3); ep.setDHTVersion(2); ep.setDHTMode(DHTMode.INACTIVE); hostCatcher.add(ep, false); // dht active node ep = new ExtendedEndpoint("18.239.0.101", 6322, 2); ep.setDHTVersion(1); ep.setDHTMode(DHTMode.ACTIVE); hostCatcher.add(ep, false); File tmp=File.createTempFile("hc_test", ".net" ); hostCatcher.write(tmp); //2. read HC from file. setUp(); // make sure we clear from memory the stuff we just added. UDPHostCache uhc = (UDPHostCache)PrivilegedAccessor.getValue(hostCatcher, "udpHostCache"); assertEquals(0, uhc.getSize()); assertEquals(0, hostCatcher.getNumHosts()); hostCatcher.read(tmp); assertEquals(2, uhc.getSize()); assertEquals(5, hostCatcher.getNumHosts()); // the highest uptime hosts should be given out first, // but not in order Set<Endpoint> s = new HashSet<Endpoint>(); s.add(hostCatcher.getAnEndpoint()); s.add(hostCatcher.getAnEndpoint()); s.add(hostCatcher.getAnEndpoint()); assertTrue(s.contains(new Endpoint("18.239.0.142", 6342))); assertTrue(s.contains(new Endpoint("18.239.0.141", 6341))); assertTrue(s.contains(new Endpoint("18.239.0.143", 6343))); ExtendedEndpoint xep = (ExtendedEndpoint) hostCatcher.getAnEndpoint(); assertTrue(xep.supportsDHT()); assertEquals(xep.getDHTVersion(), 2); xep = (ExtendedEndpoint) hostCatcher.getAnEndpoint(); assertTrue(xep.supportsDHT()); assertEquals(xep.getDHTVersion(), 1); assertEquals(DHTMode.ACTIVE, xep.getDHTMode()); assertEquals(0, hostCatcher.getNumHosts()); //Cleanup. tmp.delete(); } /** Tests that only the best hosts are remembered. */ public void testBestPermanent() throws Exception { PingReplyFactory pingReplyFactory = injector.getInstance(PingReplyFactory.class); HostCatcher.DEBUG=false; //Too darn slow //1. Fill up host catcher with PERMANENT_SIZE+1 mid-level pongs //(various uptimes). final int N=HostCatcher.PERMANENT_SIZE; for (int i=0; i<=N; i++) { hostCatcher.add(pingReplyFactory.createExternal(GUID.makeGuid(), (byte)7, i+1, new byte[] {(byte)18, (byte)239, (byte)0, (byte)142}, i+10, false)); } //Now add bad pong--which isn't really added hostCatcher.add(pingReplyFactory.createExternal(GUID.makeGuid(), (byte)7, N+2, new byte[] {(byte)18, (byte)239, (byte)0, (byte)142}, 0, false)); //Now re-add port 1 (which was kicked out earlier). hostCatcher.add(pingReplyFactory.createExternal(GUID.makeGuid(), (byte)7, 1, new byte[] {(byte)18, (byte)239, (byte)0, (byte)142}, N+101,false)); File tmp=File.createTempFile("hc_test", ".net" ); hostCatcher.write(tmp); //2. Read setUp(); HostCatcher.DEBUG=false; //Too darn slow hostCatcher.read(tmp); assertEquals(0, hostCatcher.getNumUltrapeerHosts()); Set<Endpoint> s = new HashSet<Endpoint>(); // Note that we only go to 1 (not 0) because we already extracted // a host in the line before this. for (int i=N; i > 0; i--) s.add(hostCatcher.getAnEndpoint()); // the bad host should not be in there assertFalse(s.contains(new Endpoint("18.239.0.142",N+2))); // the good one should assertTrue(s.contains(new Endpoint("18.239.0.142",1))); assertEquals("some hosts leftover", 0, hostCatcher.getNumHosts()); //Cleanup. tmp.delete(); } /** * tests that when reading from disk, the best third of the hosts * will be given out first */ public void testBestRestored() throws Exception { // add a bunch of bad hosts for (int i = 0; i < 10; i++) hostCatcher.add(new ExtendedEndpoint("1.1.1.1", 100+i, i), false); // a bunch of average hosts for (int i = 0; i < 10; i++) hostCatcher.add(new ExtendedEndpoint("2.2.2.2", 200+i, 200+i), false); // a bunch of good hosts for (int i = 0; i < 20; i++) hostCatcher.add(new ExtendedEndpoint("3.3.3.3", 300+i, 300+i), false); // reload File tmp=File.createTempFile("hc_test", ".net" ); hostCatcher.write(tmp); setUp(); hostCatcher.read(tmp); tmp.delete(); // the first 10 hosts should all be good for (int i = 0; i < 10; i++) assertEquals("3.3.3.3",hostCatcher.getAnEndpoint().getAddress()); // the next 10 should not have any bad ones Set<Endpoint> s = new HashSet<Endpoint>(); for (int i = 0; i < 10; i++) s.add(hostCatcher.getAnEndpoint()); for (int i = 0; i < 10; i++) assertFalse(s.contains(new Endpoint("1.1.1.1", 100+i))); } /** Test that connection history is recorded. */ public void testDoneWithConnect() throws Exception { hostCatcher.add(new Endpoint("18.239.0.1"), true); hostCatcher.add(new Endpoint("18.239.0.2"), true); //will succeed hostCatcher.add(new Endpoint("18.239.0.3"), true); //will fail ExtendedEndpoint e3=(ExtendedEndpoint)hostCatcher.getAnEndpoint(); assertEquals(new Endpoint("18.239.0.3"), e3); ExtendedEndpoint e2=(ExtendedEndpoint)hostCatcher.getAnEndpoint(); assertEquals(new Endpoint("18.239.0.2"), e2); //record success (increases priority) hostCatcher.doneWithConnect(e2, true); //record failure (lowers priority) with alternate form of method hostCatcher.doneWithConnect(e3, false); //Garbage (ignored) hostCatcher.doneWithConnect(new Endpoint("1.2.3.4", 6346), false); hostCatcher.doneWithConnect(new Endpoint("18.239.0.3", 6349), true); //port //Check that permanent hosts are re-arranged. //Note that iterator yields worst to best. Iterator iter=hostCatcher.getPermanentHosts(); ExtendedEndpoint e=(ExtendedEndpoint)iter.next(); assertEquals(new Endpoint("18.239.0.3"), e); assertTrue(!e.getConnectionSuccesses().hasNext()); assertTrue(e.getConnectionFailures().hasNext()); e=(ExtendedEndpoint)iter.next(); assertEquals(new Endpoint("18.239.0.1"), e); assertTrue(!e.getConnectionSuccesses().hasNext()); assertTrue(!e.getConnectionFailures().hasNext()); e=(ExtendedEndpoint)iter.next(); assertEquals(new Endpoint("18.239.0.2"), e); assertTrue(e.getConnectionSuccesses().hasNext()); assertTrue(!e.getConnectionFailures().hasNext()); } public void testBadGnutellaDotNet() throws Exception { //System.out.println("-Testing bad Gnutella.net"); //1. Write (mostly) corrupt file File tmp=File.createTempFile("hc_test", ".net" ); FileWriter out=new FileWriter(tmp); out.write("18.239.0.141\n"); //GOOD: port optional out.write("\n"); //blank line out.write("18.239.0.144:A\n"); //bad port out.write("18.239.0.141:6347 A\n"); //bad uptime out.write("<html>total crap\n"); //not even close! out.write(" some garbage,1000,a,b,c,d,e,f,g\n"); //bad address out.write("18.239.0.142:6342,1000,a,b,c,d,e,f,g\n");//GOOD: ignore extra out.flush(); out.close(); //2. Read and verify setUp(); hostCatcher.read(tmp); Set<Endpoint> s = new HashSet<Endpoint>(); s.add(hostCatcher.getAnEndpoint()); s.add(hostCatcher.getAnEndpoint()); assertTrue(s.contains(new Endpoint("18.239.0.142", 6342))); assertTrue(s.contains(new Endpoint("18.239.0.141", 6346))); assertEquals("unexpected number of hosts", 0, hostCatcher.getNumHosts()); assertEquals("unexpected number of ultrapeers", 0, hostCatcher.getNumUltrapeerHosts()); //Clean up tmp.delete(); } public void testUDPHostCacheAdded() throws Exception { PingReplyFactory pingReplyFactory = injector.getInstance(PingReplyFactory.class); UDPHostCache uhc = (UDPHostCache)PrivilegedAccessor.getValue(hostCatcher, "udpHostCache"); assertEquals(0, hostCatcher.getNumHosts()); assertEquals(0, uhc.getSize()); // Test with UDPHC pongs. GGEP ggep = new GGEP(); ggep.put(GGEPKeys.GGEP_HEADER_UDP_HOST_CACHE); PingReply pr = pingReplyFactory.create(GUID.makeGuid(), (byte)1, 1, new byte[] { 1, 1, 1, 1 }, 0, 0, false, ggep); hostCatcher.add(pr); assertEquals(0, hostCatcher.getNumHosts()); assertEquals(1, uhc.getSize()); // Test with an endpoint. ExtendedEndpoint ep = new ExtendedEndpoint("3.2.3.4", 6346); ep.setUDPHostCache(true); hostCatcher.add(ep, false); assertEquals(0, hostCatcher.getNumHosts()); assertEquals(2, uhc.getSize()); // Test with a name in the cache. ggep = new GGEP(); ggep.put(GGEPKeys.GGEP_HEADER_UDP_HOST_CACHE, "www.limewire.org"); pr = pingReplyFactory.create(GUID.makeGuid(), (byte)1, 1, new byte[] { 5, 4, 3, 2 }, 0, 0, false, ggep); hostCatcher.add(pr); assertEquals(0, hostCatcher.getNumHosts()); assertEquals(3, uhc.getSize()); Set s = (Set)PrivilegedAccessor.getValue(uhc, "udpHostsSet"); // assert that it had all our endpoints. assertContains(s, new ExtendedEndpoint("1.1.1.1", 1)); assertContains(s, new ExtendedEndpoint("3.2.3.4", 6346)); assertContains(s, new ExtendedEndpoint("www.limewire.org", 1)); } public void testPackedIPPongsAreUsed() throws Exception { PingReplyFactory pingReplyFactory = injector.getInstance(PingReplyFactory.class); ConnectionSettings.FILTER_CLASS_C.setValue(true); assertEquals(0, hostCatcher.getNumHosts()); GGEP ggep = new GGEP(); ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(new byte[] { 1, 1, 1, 1, 1, 0 } ); out.write(new byte[] { 1, 1, 1, 2, 1, 0 } ); // same class C - filtered out.write(new byte[] { 1, 2, 3, 4, 2, 0 } ); out.write(new byte[] { 3, 4, 2, 3, 3, 0 } ); out.write(new byte[] { (byte)0xFE, 0, 0, 3, 4, 0 } ); ggep.put(GGEPKeys.GGEP_HEADER_PACKED_IPPORTS, out.toByteArray()); PingReply pr = pingReplyFactory.create( GUID.makeGuid(), (byte)1, 1, new byte[] { 4, 3, 2, 1 }, 0, 0, false, ggep); hostCatcher.add(pr); assertEquals(5, hostCatcher.getNumHosts()); } public void testPackedIPsWithUHC() throws Exception { PingReplyFactory pingReplyFactory = injector.getInstance(PingReplyFactory.class); UDPHostCache uhc = (UDPHostCache)PrivilegedAccessor.getValue(hostCatcher, "udpHostCache"); assertEquals(0, hostCatcher.getNumHosts()); assertEquals(0, uhc.getSize()); GGEP ggep = new GGEP(); ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(new byte[] { 1, 1, 1, 1, 1, 0 } ); out.write(new byte[] { 1, 2, 3, 4, 2, 0 } ); out.write(new byte[] { 3, 4, 2, 3, 3, 0 } ); out.write(new byte[] { (byte)0xFE, 0, 0, 3, 4, 0 } ); ggep.put(GGEPKeys.GGEP_HEADER_PACKED_IPPORTS, out.toByteArray()); ggep.put(GGEPKeys.GGEP_HEADER_UDP_HOST_CACHE); PingReply pr = pingReplyFactory.create( GUID.makeGuid(), (byte)1, 1, new byte[] { 4, 3, 2, 1 }, 0, 0, false, ggep); hostCatcher.add(pr); assertEquals(4, hostCatcher.getNumHosts()); assertEquals(1, uhc.getSize()); } public void testPackedHostCachesAreStored() throws Exception { PingReplyFactory pingReplyFactory = injector.getInstance(PingReplyFactory.class); UDPHostCache uhc = (UDPHostCache)PrivilegedAccessor.getValue(hostCatcher, "udpHostCache"); assertEquals(0, hostCatcher.getNumHosts()); assertEquals(0, uhc.getSize()); GGEP ggep = new GGEP(); String addrs ="1.2.3.4:81\n" + "www.limewire.com:6379\n"+ "www.eff.org\n"+ "www.test.org:1"; ggep.putCompressed(GGEPKeys.GGEP_HEADER_PACKED_HOSTCACHES, addrs.getBytes()); PingReply pr = pingReplyFactory.create( GUID.makeGuid(), (byte)1, 1, new byte[] { 4, 3, 2, 1 }, 0, 0, false, ggep); hostCatcher.add(pr); assertEquals(1, hostCatcher.getNumHosts()); assertEquals(4, uhc.getSize()); } public void testImmediateEndpointObserverNoHosts() throws Exception { StubEndpointObserver observer = new StubEndpointObserver(); assertEquals(0, hostCatcher.getNumHosts()); assertNull(observer.getEndpoint()); assertNull(hostCatcher.getAnEndpointImmediate(observer)); assertNull(observer.getEndpoint()); Endpoint p = new Endpoint("231.123.254.1", 1); hostCatcher.add(p, true); assertEquals(p, observer.getEndpoint()); } public void testImmediateEndpointObserverWithHosts() throws Exception { Endpoint p = new Endpoint("231.123.254.1", 1); hostCatcher.add(p, true); StubEndpointObserver observer = new StubEndpointObserver(); assertEquals(1, hostCatcher.getNumHosts()); assertNull(observer.getEndpoint()); assertEquals(p, hostCatcher.getAnEndpointImmediate(observer)); assertNull(observer.getEndpoint()); } public void testAsyncEndpointObserver() throws Exception { StubEndpointObserver observer = new StubEndpointObserver(); assertEquals(0, hostCatcher.getNumHosts()); assertNull(observer.getEndpoint()); hostCatcher.getAnEndpoint(observer); assertNull(observer.getEndpoint()); Endpoint p = new Endpoint("231.123.254.1", 1); hostCatcher.add(p, true); assertEquals(p, observer.getEndpoint()); } public void testMultipleAsyncEndpointObservers() throws Exception { StubEndpointObserver o1 = new StubEndpointObserver(); StubEndpointObserver o2 = new StubEndpointObserver(); assertEquals(0, hostCatcher.getNumHosts()); hostCatcher.getAnEndpoint(o1); hostCatcher.getAnEndpoint(o2); assertNull(o1.getEndpoint()); assertNull(o2.getEndpoint()); Endpoint p1 = new Endpoint("231.123.254.1", 1); Endpoint p2 = new Endpoint("231.123.254.1", 2); hostCatcher.add(p1, true); hostCatcher.add(p2, true); assertEquals(p1, o1.getEndpoint()); assertEquals(p2, o2.getEndpoint()); } public void testAsyncEndpointObserverEndpointAlreadyAdded() throws Exception { StubEndpointObserver observer = new StubEndpointObserver(); assertEquals(0, hostCatcher.getNumHosts()); assertNull(observer.getEndpoint()); Endpoint p = new Endpoint("231.123.254.1", 1); hostCatcher.add(p, true); hostCatcher.getAnEndpoint(observer); assertEquals(p, observer.getEndpoint()); } public void testGetDHTSupportEndpoint() throws Exception { ConnectionSettings.FILTER_CLASS_C.setValue(true); assertEquals(0, hostCatcher.getDHTSupportEndpoint(0).size()); // add a bunch of nodes from the same class C network ExtendedEndpoint ep; //dht nodes for(int i=6300; i < 6309 ; i++) { ep = new ExtendedEndpoint("18.239.0.100", i); ep.setDHTVersion(2); if((i % 2) == 0) { ep.setDHTMode(DHTMode.INACTIVE); } else { ep.setDHTMode(DHTMode.PASSIVE); } hostCatcher.add(ep, false); } List<ExtendedEndpoint> hostList = hostCatcher.getDHTSupportEndpoint(0); assertEquals(1, hostList.size()); // add nodes from different class C networks for(int i=1; i < 9 ; i++) { ep = new ExtendedEndpoint("18.239."+i+".100", 6000); ep.setDHTVersion(2); if((i % 2) == 0) { ep.setDHTMode(DHTMode.INACTIVE); } else { ep.setDHTMode(DHTMode.PASSIVE); } hostCatcher.add(ep, false); } //dht active node ep = new ExtendedEndpoint("18.239.10.101", 6322); ep.setDHTVersion(1); ep.setDHTMode(DHTMode.ACTIVE); hostCatcher.add(ep, false); hostList = hostCatcher.getDHTSupportEndpoint(0); ep = hostList.get(0); assertEquals("18.239.10.101", ep.getAddress()); assertTrue(ep.getDHTMode().equals(DHTMode.ACTIVE)); hostList.remove(0); ep = hostList.iterator().next(); assertTrue(ep.getDHTMode().equals(DHTMode.PASSIVE)); ep = hostList.get(hostList.size()-1); assertFalse(ep.getDHTMode().equals(DHTMode.PASSIVE)); assertTrue(ep.getDHTMode().equals(DHTMode.INACTIVE)); //try excluding version hostList = hostCatcher.getDHTSupportEndpoint(2); assertEquals(9, hostList.size()); assertTrue(hostList.get(0).getAddress().endsWith(".100")); } @SuppressWarnings("unchecked") public void testPingTagging() throws Exception { injector = LimeTestUtils.createInjector(new AbstractModule() { @Override protected void configure() { bind(ConnectionManager.class).to(ConnectionManagerStub.class); bind(LocalSocketAddressProvider.class).toInstance(new LocalSocketAddressProvider() { public byte[] getLocalAddress() { return null; } public int getLocalPort() { return 0; } public boolean isLocalAddressPrivate() { return false; }; public boolean isTLSCapable() { return false; } }); } }); hostCatcher = injector.getInstance(HostCatcher.class); hostCatcher.start(); PingReplyFactory pingReplyFactory = injector.getInstance(PingReplyFactory.class); Acceptor acceptor = injector.getInstance(Acceptor.class); MessageRouter messageRouter = injector.getInstance(MessageRouter.class); MessageFactory messageFactory = injector.getInstance(MessageFactory.class); UDPService udpService = injector.getInstance(UDPService.class); ConnectionManagerStub connectionManager = (ConnectionManagerStub)injector.getInstance(ConnectionManager.class); connectionManager.setFullyConnected(false); connectionManager.setInitializedConnections(Collections.EMPTY_LIST); connectionManager.setPreferredConnectionCount(1); ConnectionSettings.FILTER_CLASS_C.setValue(true); ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false); // tell hostcatcher about one node File tmp=new File(CommonUtils.getUserSettingsDir(), "gnutella.net" ); FileWriter out=new FileWriter(tmp); out.write("127.0.0.1:6000,26694,1176133322250,1176133334390,,en,,,"); out.flush(); out.close(); DatagramSocket s = new DatagramSocket(6000); s.setSoTimeout(3000); // make it send udp pings acceptor.bindAndStartUpnp(); acceptor.start(); messageRouter.start(); hostCatcher.expire(); hostCatcher.sendUDPPings(); // empty the hostcatcher StubEndpointObserver seo = new StubEndpointObserver(); assertNotNull(hostCatcher.getAnEndpointImmediate(seo)); assertNull(hostCatcher.getAnEndpointImmediate(seo)); hostCatcher.removeEndpointObserver(seo); // receive the ping DatagramPacket p = new DatagramPacket(new byte[1000], 1000); s.receive(p); PingRequest ping = (PingRequest)messageFactory.read(new ByteArrayInputStream(p.getData()), Network.TCP); assertNotNull(ping); // this ping should be tagged byte [] expectedGUID = udpService.getSolicitedGUID().bytes(); assertNotEquals(expectedGUID, ping.getGUID()); // if a pong is sent from the same host, it will be processed byte [] payload = new byte[14]; ByteUtils.short2leb((short)6000, payload, 0); System.arraycopy(InetAddress.getByName("127.0.0.1").getAddress(),0,payload,2,4); PingReply pong = pingReplyFactory.createFromNetwork(ping.getGUID().clone(), (byte)1, (byte)1, payload, Message.Network.UDP); udpService.processMessage(pong, new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 6000)); // the pong guid should now be restored to the solicited guid assertEquals(expectedGUID,pong.getGUID()); Thread.sleep(1000); Endpoint e = hostCatcher.getAnEndpointImmediate(new StubEndpointObserver()); assertNotNull(e); assertEquals("127.0.0.1",e.getAddress()); assertEquals(6000, e.getPort()); // however if a pong with the same guid arrives from // another address, it will be ignored System.arraycopy(InetAddress.getByName("127.0.0.2").getAddress(),0,payload,2,4); pong = pingReplyFactory.createFromNetwork(ping.getGUID().clone(), (byte)1, (byte)1, payload, Message.Network.UDP); udpService.processMessage(pong, new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 6000)); // the guid of this pong will not be restored correctly assertNotEquals(expectedGUID,pong.getGUID()); Thread.sleep(1000); assertEquals(0,hostCatcher.getNumHosts()); tmp.delete(); } public void testIsTLSCapable() throws Exception { PingReplyFactory pingReplyFactory = injector.getInstance(PingReplyFactory.class); assertEquals(0, hostCatcher.getNumHosts()); ExtendedEndpoint p = new ExtendedEndpoint("231.123.254.1", 1); hostCatcher.add(p, true); assertFalse(hostCatcher.isHostTLSCapable(new IpPortImpl("231.123.254.1", 1))); assertFalse(hostCatcher.isHostTLSCapable(p)); p = new ExtendedEndpoint("21.81.1.1", 1); p.setTLSCapable(true); hostCatcher.add(p, true); assertTrue(hostCatcher.isHostTLSCapable(new IpPortImpl("21.81.1.1", 1))); assertTrue(hostCatcher.isHostTLSCapable(p)); p = new ExtendedEndpoint("1.2.3.101", 1); p.setTLSCapable(true); assertTrue(hostCatcher.isHostTLSCapable(p)); // Hand-craft a PingReply w/ TLS IPPs to see if they're added as // TLS capable hosts. assertEquals(2, hostCatcher.getNumHosts()); GGEP ggep = new GGEP(); ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(new byte[] { 1, 1, 1, 1, 1, 0 } ); out.write(new byte[] { 1, 2, 3, 4, 2, 0 } ); out.write(new byte[] { 3, 4, 2, 3, 3, 0 } ); out.write(new byte[] { (byte)0xFE, 0, 0, 3, 4, 0 } ); ggep.put(GGEPKeys.GGEP_HEADER_PACKED_IPPORTS, out.toByteArray()); // mark the second & third items as TLS ggep.put(GGEPKeys.GGEP_HEADER_PACKED_IPPORTS_TLS, (0x40 | 0x20)); ggep.put(GGEPKeys.GGEP_HEADER_TLS_CAPABLE); // mark this guy as TLS capable. PingReply pr = pingReplyFactory.create( GUID.makeGuid(), (byte)1, 1, new byte[] { 1, 0, 1, 0 }, 0, 0, false, ggep); hostCatcher.add(pr); assertEquals(7, hostCatcher.getNumHosts()); assertFalse(hostCatcher.isHostTLSCapable(new IpPortImpl("1.1.1.1:1"))); assertTrue(hostCatcher.isHostTLSCapable(new IpPortImpl("1.2.3.4:2"))); assertTrue(hostCatcher.isHostTLSCapable(new IpPortImpl("3.4.2.3:3"))); assertFalse(hostCatcher.isHostTLSCapable(new IpPortImpl("254.0.0.3:4"))); assertTrue(hostCatcher.isHostTLSCapable(new IpPortImpl("1.0.1.0:1"))); // The order of these checks are a little stricter than necessary Endpoint ep = hostCatcher.getAnEndpointImmediate(null); assertEquals("254.0.0.3", ep.getAddress()); assertFalse(ep.isTLSCapable()); ep = hostCatcher.getAnEndpointImmediate(null); assertEquals("3.4.2.3", ep.getAddress()); assertTrue(ep.isTLSCapable()); ep = hostCatcher.getAnEndpointImmediate(null); assertEquals("1.2.3.4", ep.getAddress()); assertTrue(ep.isTLSCapable()); ep = hostCatcher.getAnEndpointImmediate(null); assertEquals("1.1.1.1", ep.getAddress()); assertFalse(ep.isTLSCapable()); ep = hostCatcher.getAnEndpointImmediate(null); assertEquals("21.81.1.1", ep.getAddress()); assertTrue(ep.isTLSCapable()); ep = hostCatcher.getAnEndpointImmediate(null); assertEquals("231.123.254.1", ep.getAddress()); assertFalse(ep.isTLSCapable()); ep = hostCatcher.getAnEndpointImmediate(null); assertEquals("1.0.1.0", ep.getAddress()); assertTrue(ep.isTLSCapable()); assertNull(hostCatcher.getAnEndpointImmediate(null)); } @Singleton private static class UDPHostCacheFactoryStub implements UDPHostCacheFactory { private final Provider<MessageRouter> messageRouter; private final PingRequestFactory pingRequestFactory; private final ConnectionServices connectionServices; private final Provider<HostCatcher> hostCatcher; private final NetworkInstanceUtils networkInstanceUtils; @Inject public UDPHostCacheFactoryStub(Provider<MessageRouter> messageRouter, PingRequestFactory pingRequestFactory, ConnectionServices connectionServices, Provider<HostCatcher> hostCatcher, NetworkInstanceUtils networkInstanceUtils) { this.messageRouter = messageRouter; this.pingRequestFactory = pingRequestFactory; this.connectionServices = connectionServices; this.hostCatcher = hostCatcher; this.networkInstanceUtils = networkInstanceUtils; } public UDPHostCache createUDPHostCache(UDPPinger pinger) { return new StubUDPBootstrapper(pinger, messageRouter, pingRequestFactory, connectionServices, hostCatcher, networkInstanceUtils); } public UDPHostCache createUDPHostCache(long expiryTime, UDPPinger pinger) { throw new UnsupportedOperationException(); } } private static class StubUDPBootstrapper extends UDPHostCache { private final Provider<HostCatcher> hostCatcher; public StubUDPBootstrapper(UDPPinger pinger, Provider<MessageRouter> messageRouter, PingRequestFactory pingRequestFactory, ConnectionServices connectionServices, Provider<HostCatcher> hostCatcher, NetworkInstanceUtils networkInstanceUtils) { super(pinger, messageRouter, pingRequestFactory, connectionServices, networkInstanceUtils); this.hostCatcher = hostCatcher; } private boolean fetched = false; private String host = "143.123.234.132"; private boolean expired = false; public boolean fetchHosts() { if(expired) return false; expired = true; fetched = true; Endpoint ep = new Endpoint(host, 6346); hostCatcher.get().add(ep, false); return true; } public int getSize() { if(expired) return 0; else return 1; } } }