package com.limegroup.gnutella; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.Socket; import java.util.Iterator; import junit.framework.Test; import com.limegroup.gnutella.guess.QueryKey; import com.limegroup.gnutella.messages.Message; import com.limegroup.gnutella.messages.PingReply; import com.limegroup.gnutella.messages.PingRequest; import com.limegroup.gnutella.messages.QueryReply; import com.limegroup.gnutella.messages.QueryRequest; import com.limegroup.gnutella.messages.vendor.MessagesSupportedVendorMessage; import com.limegroup.gnutella.stubs.ActivityCallbackStub; import com.limegroup.gnutella.util.CommonUtils; import com.limegroup.gnutella.util.PrivilegedAccessor; import com.limegroup.gnutella.util.Sockets; /** * Checks whether a leaf node handles GUESS queries correctly. Tests the * Server side of GUESS. */ public class ServerSideLeafGuessTest extends ClientSideTestCase { /** * Constant for the size of UDP messages to accept -- dependent upon * IP-layer fragmentation. */ private final int BUFFER_SIZE = 8192; /** * Ultrapeer 1 UDP connection. */ private static DatagramSocket UDP_ACCESS; public ServerSideLeafGuessTest(String name) { super(name); } public static Test suite() { return buildTestSuite(ServerSideLeafGuessTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } public void setUp() { try { // Wait between each test so dup GUIDs don't trigger msg drops Thread.sleep(1000*10); } catch (Exception ignored) {} } ///////////////////////// Actual Tests //////////////////////////// // MUST RUN THIS TEST FIRST public void testBasicProtocol() throws Exception { UDP_ACCESS = new DatagramSocket(); UDP_ACCESS.setSoTimeout(1000 * 20); for (int i = 0; i < testUP.length; i++) { drain(testUP[i]); // OOB client side needs server side leaf guidance testUP[i].send(MessagesSupportedVendorMessage.instance()); testUP[i].flush(); } // first we need to set up GUESS capability // ---------------------------------------- // set up solicited UDP support PrivilegedAccessor.setValue( rs.getUdpService(), "_acceptedSolicitedIncoming", Boolean.TRUE ); // set up unsolicited UDP support PrivilegedAccessor.setValue( rs.getUdpService(), "_acceptedUnsolicitedIncoming", Boolean.TRUE ); // you also have to set up TCP incoming.... { Socket sock = null; OutputStream os = null; try { sock = Sockets.connect(InetAddress.getLocalHost().getHostAddress(), SERVER_PORT, 1200); os = sock.getOutputStream(); os.write("CONNECT BACK\r\n\r\n".getBytes()); os.flush(); } catch (IOException ignored) { } finally { if(sock != null) try { sock.close(); } catch(IOException ignored) {} if(os != null) try { os.close(); } catch(IOException ignored) {} } } InetAddress localHost = InetAddress.getLocalHost(); // first send a QueryKey request.... send(PingRequest.createQueryKeyRequest(), localHost, SERVER_PORT); // we should get a QueryKey.... Message m = null; QueryKey qkToUse = null; while (true) { m = receive(); if (m instanceof PingReply) { PingReply rep = (PingReply) m; qkToUse = rep.getQueryKey(); if (rep.getQueryKey() != null) break; } } assertNotNull(qkToUse); // we should be able to send a query QueryRequest goodQuery = QueryRequest.createQueryKeyQuery("susheel", qkToUse); byte[] guid = goodQuery.getGUID(); send(goodQuery, localHost, SERVER_PORT); // now we should get an ack m = receive(); assertTrue(m instanceof PingReply); PingReply pRep = (PingReply) m; assertEquals(new GUID(guid), new GUID(pRep.getGUID())); // followed by a query hit m = receive(); assertTrue(m instanceof QueryReply); QueryReply qRep = (QueryReply) m; assertEquals(new GUID(guid), new GUID(qRep.getGUID())); } public void testGoodURNQuery() throws Exception { InetAddress localHost = InetAddress.getLocalHost(); // first send a QueryKey request.... send(PingRequest.createQueryKeyRequest(), localHost, SERVER_PORT); // we should get a QueryKey.... Message m = null; QueryKey qkToUse = null; while (true) { m = receive(); if (m instanceof PingReply) { PingReply rep = (PingReply) m; qkToUse = rep.getQueryKey(); if (rep.getQueryKey() != null) break; } } assertNotNull(qkToUse); // now send a URN query, make sure that works.... File berkeley = CommonUtils.getResourceFile("com/limegroup/gnutella/berkeley.txt"); Iterator iter = calculateAndCacheURN(berkeley).iterator(); URN berkeleyURN = (URN) iter.next(); // we should be able to send a URN query QueryRequest goodQuery = QueryRequest.createQueryKeyQuery(berkeleyURN, qkToUse); byte[] guid = goodQuery.getGUID(); send(goodQuery, localHost, SERVER_PORT); // now we should get an ack m = receive(); assertTrue(m instanceof PingReply); PingReply pRep = (PingReply) m; assertEquals(new GUID(guid), new GUID(pRep.getGUID())); // followed by a query hit with a URN m = receive(); assertTrue(m instanceof QueryReply); QueryReply qRep = (QueryReply) m; assertEquals(new GUID(guid), new GUID(qRep.getGUID())); iter = qRep.getResults(); Response first = (Response) iter.next(); assertEquals(first.getUrns(), calculateAndCacheURN(berkeley)); } public void testQueryWithNoHit() throws Exception { InetAddress localHost = InetAddress.getLocalHost(); // first send a QueryKey request.... send(PingRequest.createQueryKeyRequest(), localHost, SERVER_PORT); // we should get a QueryKey.... Message m = null; QueryKey qkToUse = null; while (true) { m = receive(); if (m instanceof PingReply) { PingReply rep = (PingReply) m; qkToUse = rep.getQueryKey(); if (rep.getQueryKey() != null) break; } } assertNotNull(qkToUse); // send a query that shouldn't get results.... QueryRequest goodQuery = QueryRequest.createQueryKeyQuery("anita", qkToUse); byte[] guid = goodQuery.getGUID(); send(goodQuery, localHost, SERVER_PORT); // now we should get an ack m = receive(); assertTrue(m instanceof PingReply); PingReply pRep = (PingReply) m; assertEquals(new GUID(guid), new GUID(pRep.getGUID())); // but not a query hit try { m = receive(); assertTrue(false); } catch (InterruptedIOException expected) {}; } public void testBadQueryKey() throws Exception { InetAddress localHost = InetAddress.getLocalHost(); Message m = null; QueryKey qkToUse = QueryKey.getQueryKey(localHost, 0); assertNotNull(qkToUse); { // we shouldn't get any response to our query... QueryRequest goodQuery = QueryRequest.createQueryKeyQuery("susheel", qkToUse); byte[] guid = goodQuery.getGUID(); send(goodQuery, localHost, SERVER_PORT); try { // now we should NOT get an ack m = receive(); assertTrue(false); } catch (InterruptedIOException expected) {} } } ////////////////////////////////////////////////////////////////// private Message receive() throws Exception { byte[] datagramBytes = new byte[BUFFER_SIZE]; DatagramPacket datagram = new DatagramPacket(datagramBytes, BUFFER_SIZE); UDP_ACCESS.receive(datagram); byte[] data = datagram.getData(); // construct a message out of it... InputStream in = new ByteArrayInputStream(data); Message message = Message.read(in); return message; } private void send(Message msg, InetAddress ip, int port) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); msg.write(baos); byte[] data = baos.toByteArray(); DatagramPacket dg = new DatagramPacket(data, data.length, ip, port); UDP_ACCESS.send(dg); } private static byte[] myIP() { return new byte[] { (byte)127, (byte)0, 0, 1 }; } public static Integer numUPs() { return new Integer(3); } public static ActivityCallback getActivityCallback() { return new ActivityCallbackStub(); } }