package com.limegroup.gnutella.messages;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.InvalidKeyException;
import java.security.InvalidParameterException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.SignatureSpi;
import java.util.*;
import junit.framework.Test;
import com.limegroup.gnutella.ByteOrder;
import com.limegroup.gnutella.CreationTimeCache;
import com.limegroup.gnutella.FileDesc;
import com.limegroup.gnutella.FileManager;
import com.limegroup.gnutella.GUID;
import com.limegroup.gnutella.Response;
import com.limegroup.gnutella.RouterService;
import com.limegroup.gnutella.altlocs.AlternateLocation;
import com.limegroup.gnutella.settings.ConnectionSettings;
import com.limegroup.gnutella.settings.SearchSettings;
import com.limegroup.gnutella.settings.SharingSettings;
import com.limegroup.gnutella.stubs.ActivityCallbackStub;
import com.limegroup.gnutella.stubs.SimpleFileManager;
import com.limegroup.gnutella.util.CommonUtils;
import com.limegroup.gnutella.util.IpPort;
import com.limegroup.gnutella.util.IpPortImpl;
import com.limegroup.gnutella.util.PrivilegedAccessor;
import com.limegroup.gnutella.util.StringUtils;
/**
* This class tests the QueryReply class.
*/
public final class QueryReplyTest extends com.limegroup.gnutella.util.BaseTestCase {
private static final byte[] IP = new byte[] {1, 1, 1, 1};
private static final String EXTENSION = "XYZ";
private static final int MAX_LOCATIONS = 10;
private QueryReply.GGEPUtil _ggepUtil = new QueryReply.GGEPUtil();
private FileManager fman = null;
private Object loaded = new Object();
/**
* Constructs a new test instance for query replies.
*/
public QueryReplyTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(QueryReplyTest.class);
}
/**
* Runs the test individually.
*/
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
public static void globalSetUp() throws Exception {
ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false);
try {
RouterService.getAcceptor().setAddress(InetAddress.getLocalHost());
} catch (UnknownHostException e) {
} catch (SecurityException e) {
}
}
public void setUp() throws Exception {
SharingSettings.EXTENSIONS_TO_SHARE.setValue(EXTENSION);
ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false);
cleanFiles(_sharedDir, false);
fman = new SimpleFileManager();
PrivilegedAccessor.setValue(RouterService.class, "callback", new FManCallback());
}
/**
* Runs the legacy unit test that was formerly in QueryReply.
*/
public void testLegacy() throws Exception {
byte[] ip={(byte)0xFE, (byte)0, (byte)0, (byte)0x1};
long u4=0x00000000FFFFFFFFl;
byte[] guid=new byte[16]; guid[0]=(byte)1; guid[15]=(byte)0xFF;
Response[] responses=new Response[0];
QueryReply qr=new QueryReply(guid, (byte)5,
0xF3F1, ip, 1, responses,
guid, false);
assertEquals(1, qr.getSpeed());
assertEquals(Integer.toHexString(qr.getPort()), 0xF3F1, qr.getPort());
assertEquals(qr.getResults().hasNext(), false);
responses=new Response[2];
responses[0]=new Response(11,22,"Sample.txt");
responses[1]=new Response(0x2FF2,0xF11F,"Another file ");
qr=new QueryReply(guid, (byte)5,
0xFFFF, ip, u4, responses,
guid, false);
assertEquals("254.0.0.1",qr.getIP());
assertEquals(0xFFFF, qr.getPort());
assertEquals(u4, qr.getSpeed());
assertTrue(Arrays.equals(qr.getClientGUID(),guid));
Iterator iter=qr.getResults();
Response r1=(Response)iter.next();
assertEquals(r1, responses[0]);
Response r2=(Response)iter.next();
assertEquals(r2, responses[1]);
assertFalse(iter.hasNext());
//////////////////// Contruct from Raw Bytes /////////////
//Normal case: double null-terminated result
byte[] payload=new byte[11+11+16];
payload[0]=1; //Number of results
payload[1]=1; //non-zero port
payload[3]=1; //non-blank ip
payload[11+8]=(byte)65; //The character 'A'
qr=new QueryReply(new byte[16], (byte)5, (byte)0,
payload);
iter=qr.getResults();
Response response=(Response)iter.next();
assertEquals("A", response.getName());
assertFalse(iter.hasNext());
try {
qr.getVendor(); //undefined => exception
fail("qr should have been invalid");
} catch (BadPacketException e) { }
try {
qr.getNeedsPush(); //undefined => exception
fail("qr should have been invalid");
} catch (BadPacketException e) { }
//Bad case: not enough space for client GUID. We can get
//the client GUID, but not the results.
payload=new byte[11+11+15];
payload[0]=1; //Number of results
payload[1]=1; //non-zero port
payload[3]=1; //non-blank ip
payload[11+8]=(byte)65; //The character 'A'
qr=new QueryReply(new byte[16], (byte)5, (byte)0,
payload);
try {
iter=qr.getResults();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
try {
qr.getVendor();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
//Test case added by Sumeet Thadani to check the metadata part
//Test case modified by Susheel Daswani to check the metadata part
payload=new byte[11+11+(4+1+4+5)+16];
payload[0]=1; //Number of results
payload[1]=1; //non-zero port
payload[3]=1; //non-blank ip
payload[11+8]=(byte)65; //The character 'A'
payload[11+11+0]=(byte)76; //The character 'L'
payload[11+11+1]=(byte)76; //The character 'L'
payload[11+11+2]=(byte)77; //The character 'M'
payload[11+11+3]=(byte)69; //The character 'E'
payload[11+11+4+0]=(byte)QueryReply.COMMON_PAYLOAD_LEN;
payload[11+11+4+1+2]=(byte)5; //size of xml lsb
payload[11+11+4+1+3]=(byte)0; // size of xml msb
payload[11+11+4+1+4]=(byte)'S'; //The character 'L'
payload[11+11+4+1+4+1]=(byte)'U'; //The character 'L'
payload[11+11+4+1+4+2]=(byte)'S'; //The character 'M'
payload[11+11+4+1+4+3]=(byte)'H'; //The character 'E'
payload[11+11+4+1+4+4]=(byte)0; //null terminator
qr=new QueryReply(new byte[16], (byte)5, (byte)0, payload);
try {
iter=qr.getResults();
Response r = (Response)iter.next();
assertEquals("Sumeet test1", "A", r.getName());
assertNull("bad xml", r.getDocument());
}catch(BadPacketException e){
fail("metaResponse not created well!", e);
}
//Normal case: basic metainfo with no vendor data
payload=new byte[11+11+(4+1+4+4)+16];
payload[0]=1; //Number of results
payload[1]=1; //non-zero port
payload[3]=1; //non-blank ip
payload[11+8]=(byte)65; //The character 'A'
payload[11+11+0]=(byte)76; //The character 'L'
payload[11+11+1]=(byte)105; //The character 'i'
payload[11+11+2]=(byte)77; //The character 'M'
payload[11+11+3]=(byte)69; //The character 'E'
payload[11+11+4+0]=(byte)QueryReply.COMMON_PAYLOAD_LEN; //The size of public area
payload[11+11+4+1]=(byte)0xB1; // set push yes/no flag (and other stuff)
payload[11+11+4+1+1]=(byte)0x01; // set the push understood flag
payload[11+11+4+1+2]=(byte)4; // set xml length
payload[11+11+4+1+3]=(byte)0;
qr=new QueryReply(new byte[16], (byte)5, (byte)0, payload);
String vendor=qr.getVendor();
assertEquals(vendor, "LIME", vendor);
vendor=qr.getVendor();
assertEquals(vendor, "LIME", vendor);
assertTrue(qr.getNeedsPush());
//Normal case: basic metainfo with extra vendor data
payload=new byte[11+11+(4+1+4+20000)+16];
payload[0]=1; //Number of results
payload[1]=1; //non-zero port
payload[3]=1; //non-blank ip
payload[11+8]=(byte)65; //The character 'A'
payload[11+11+0]=(byte)76; //The character 'L'
payload[11+11+1]=(byte)76; //The character 'L'
payload[11+11+2]=(byte)77; //The character 'M'
payload[11+11+3]=(byte)69; //The character 'E'
payload[11+11+4+0]=(byte)QueryReply.COMMON_PAYLOAD_LEN;
payload[11+11+4+1]=(byte)0xF0; //no push flag (and other crap)
payload[11+11+4+1+1]=(byte)0x01; // push understood flag
payload[11+11+4+1+2]=(byte)32; //size of xml lsb
payload[11+11+4+1+3]=(byte)78; // size of xml msb
for (int i = 0; i < 20000; i++)
payload[11+11+4+1+4+i] = 'a';
qr=new QueryReply(new byte[16], (byte)5, (byte)0, payload);
vendor=qr.getVendor();
assertEquals(vendor, "LLME", vendor);
vendor=qr.getVendor();
assertEquals(vendor, "LLME", vendor);
assertFalse(qr.getNeedsPush());
assertFalse(qr.getSupportsChat());
//Weird case. No common data. (Don't allow.)
payload=new byte[11+11+(4+1+2)+16];
payload[0]=1; //Number of results
payload[1]=1; //non-zero port
payload[3]=1; //non-blank ip
payload[11+8]=(byte)65; //The character 'A'
payload[11+11+4+1+0]=(byte)1;
qr=new QueryReply(new byte[16], (byte)5, (byte)0, payload);
try {
qr.getNeedsPush();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
try {
qr.getVendor();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
//Bad case. Common payload length lies.
payload=new byte[11+11+(4+2+0)+16];
payload[0]=1; //Number of results
payload[1]=1; //non-zero port
payload[3]=1; //non-blank ip
payload[11+8]=(byte)65; //The character 'A'
payload[11+11+0]=(byte)76; //The character 'L'
payload[11+11+1]=(byte)105; //The character 'i'
payload[11+11+2]=(byte)77; //The character 'M'
payload[11+11+3]=(byte)69; //The character 'E'
payload[11+11+4+0]=(byte)2;
qr=new QueryReply(new byte[16], (byte)5, (byte)0, payload);
qr.getResults();
try {
qr.getVendor();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
///////////// BearShare 2.2.0 QHD (busy bits and friends) ///////////
//Normal case: busy bit undefined and push bits unset.
//(We don't bother testing undefined and set. Who cares?)
payload=new byte[11+11+(4+1+4+1)+16];
payload[0]=1; //Number of results
payload[1]=1; //non-zero port
payload[3]=1; //non-blank ip
payload[11+8]=(byte)65; //The character 'A'
payload[11+11+0]=(byte)76; //The character 'L'
payload[11+11+1]=(byte)105; //The character 'i'
payload[11+11+2]=(byte)77; //The character 'M'
payload[11+11+3]=(byte)69; //The character 'E'
payload[11+11+4+0]=(byte)QueryReply.COMMON_PAYLOAD_LEN;
payload[11+11+4+1]=(byte)0x0; //no data known
payload[11+11+4+1+1]=(byte)0x0;
payload[11+11+4+1+2]=(byte)1;
qr=new QueryReply(new byte[16], (byte)5, (byte)0, payload);
vendor=qr.getVendor();
assertEquals("unexpected vendor", "LIME", vendor);
try {
qr.getNeedsPush();
fail("qr should have been invalid");
} catch(BadPacketException e) {}
try {
qr.getIsBusy();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
try {
qr.getHadSuccessfulUpload();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
try {
qr.getIsMeasuredSpeed();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
//Normal case: busy and push bits defined and set
payload=new byte[11+11+(4+1+4+1+1)+16];
payload[0]=1; //Number of results
payload[1]=1; //non-zero port
payload[3]=1; //non-blank ip
payload[11+8]=(byte)65; //The character 'A'
payload[11+11+0]=(byte)76; //The character 'L'
payload[11+11+1]=(byte)73; //The character 'I'
payload[11+11+2]=(byte)77; //The character 'M'
payload[11+11+3]=(byte)69; //The character 'E'
payload[11+11+4]=(byte)QueryReply.COMMON_PAYLOAD_LEN; //common payload size
payload[11+11+4+1]=(byte)0x1d; // 0001 1101
payload[11+11+4+1+1]=(byte)0x1d; // 0001 1101
payload[11+11+4+1+2]=(byte)1; // no xml, just a null, so 1
payload[11+11+4+1+4]=(byte)0x1; //supports chat
qr=new QueryReply(new byte[16], (byte)5, (byte)0, payload);
vendor=qr.getVendor();
assertEquals(vendor, "LIME", vendor);
assertTrue(qr.getNeedsPush());
assertTrue(qr.getIsBusy());
assertTrue(qr.getIsMeasuredSpeed());
assertTrue(qr.getHadSuccessfulUpload());
assertTrue(qr.getSupportsChat());
//Normal case: busy and push bits defined and unset
payload=new byte[11+11+(4+1+4+1)+16];
payload[0]=1; //Number of results
payload[1]=1; //non-zero port
payload[3]=1; //non-blank ip
payload[11+8]=(byte)65; //The character 'A'
payload[11+11+0]=(byte)76; //The character 'L'
payload[11+11+1]=(byte)105; //The character 'i'
payload[11+11+2]=(byte)77; //The character 'M'
payload[11+11+3]=(byte)69; //The character 'E'
payload[11+11+4]=(byte)QueryReply.COMMON_PAYLOAD_LEN;
payload[11+11+4+1]=(byte)0x1c; // 0001 1100
payload[11+11+4+1+1]=(byte)0x01; // 0000 0001 //push understood
payload[11+11+4+1+2]=(byte)1; // no xml, just a null, so 1
qr=new QueryReply(new byte[16], (byte)5, (byte)0, payload);
vendor=qr.getVendor();
assertEquals(vendor, "LIME", vendor);
assertFalse(qr.getNeedsPush());
assertFalse(qr.getIsBusy());
assertFalse(qr.getIsMeasuredSpeed());
assertFalse(qr.getHadSuccessfulUpload());
assertFalse("LiME!=LIME when looking at private area", qr.getSupportsChat());
//Create extended QHD from scratch
responses=new Response[2];
responses[0]=new Response(11,22,"Sample.txt");
responses[1]=new Response(0x2FF2,0xF11F,"Another file ");
qr=new QueryReply(guid, (byte)5,
0xFFFF, ip, u4, responses,
guid,
false, true, true, false, true, false);
assertEquals("254.0.0.1", qr.getIP());
assertEquals(0xFFFF, qr.getPort());
assertEquals(u4, qr.getSpeed());
assertTrue(Arrays.equals(qr.getClientGUID(),guid));
iter=qr.getResults();
r1=(Response)iter.next();
assertEquals(r1, responses[0]);
r2=(Response)iter.next();
assertEquals(r2, responses[1]);
assertFalse(iter.hasNext());
assertEquals("LIME", qr.getVendor());
assertFalse(qr.getNeedsPush());
assertTrue(qr.getIsBusy());
assertTrue(qr.getHadSuccessfulUpload());
assertFalse(qr.getIsMeasuredSpeed());
assertTrue(qr.getSupportsChat());
assertTrue(qr.getSupportsBrowseHost());
assertTrue(!qr.isReplyToMulticastQuery());
//Create extended QHD from scratch with different bits set
responses=new Response[2];
responses[0]=new Response(11,22,"Sample.txt");
responses[1]=new Response(0x2FF2,0xF11F,"Another file ");
qr=new QueryReply(guid, (byte)5,
0xFFFF, ip, u4, responses,
guid,
true, false, false, true, false, false);
assertEquals("LIME", qr.getVendor());
assertTrue(qr.getNeedsPush());
assertFalse(qr.getIsBusy());
assertFalse(qr.getHadSuccessfulUpload());
assertTrue(qr.getIsMeasuredSpeed());
assertFalse(qr.getSupportsChat());
assertTrue(qr.getSupportsBrowseHost());
assertTrue(!qr.isReplyToMulticastQuery());
//And check raw bytes....
ByteArrayOutputStream out=new ByteArrayOutputStream();
qr.write(out);
byte[] bytes=out.toByteArray();
int ggepLen = _ggepUtil.getQRGGEP(true, false, false,
new HashSet()).length;
//Length includes header, query hit header and footer, responses, and
//QHD (public and private)
assertEquals((23+11+16)+(8+10+2)+(8+14+2)+(4+1+QueryReply.COMMON_PAYLOAD_LEN+1+1)+ggepLen, bytes.length);
assertEquals(0x3d, bytes[bytes.length-16-6-ggepLen]); //11101
assertEquals(0x31, bytes[bytes.length-16-5-ggepLen]); //10001
// check read back....
qr=(QueryReply)Message.read(new ByteArrayInputStream(bytes));
assertEquals("LIME", qr.getVendor());
assertTrue(qr.getNeedsPush());
assertFalse(qr.getIsBusy());
assertFalse(qr.getHadSuccessfulUpload());
assertTrue(qr.getIsMeasuredSpeed());
assertFalse(qr.getSupportsChat());
assertTrue(qr.getSupportsBrowseHost());
assertTrue(!qr.isReplyToMulticastQuery());
//Create extended QHD from scratch with different bits set
// Do not set multicast, as that will unset pushing, busy, etc..
// and generally confuse the test.
responses=new Response[2];
responses[0]=new Response(11,22,"Sample.txt");
responses[1]=new Response(0x2FF2,0xF11F,"Another file ");
qr=new QueryReply(guid, (byte)5,
0xFFFF, ip, u4, responses,
guid,
true, false, false, true, false, false);
assertEquals("LIME", qr.getVendor());
assertTrue(qr.getNeedsPush());
assertFalse(qr.getIsBusy());
assertFalse(qr.getHadSuccessfulUpload());
assertTrue(qr.getIsMeasuredSpeed());
assertFalse(qr.getSupportsChat());
assertTrue(qr.getSupportsBrowseHost());
assertFalse(qr.isReplyToMulticastQuery());
//And check raw bytes....
out=new ByteArrayOutputStream();
qr.write(out);
bytes=out.toByteArray();
ggepLen = _ggepUtil.getQRGGEP(true, false, false,
new HashSet()).length;
//Length includes header, query hit header and footer, responses, and
//QHD (public and private)
assertEquals(
(23+11+16)+(8+10+2)+(8+14+2)+(4+1+QueryReply.COMMON_PAYLOAD_LEN+1+1)+ggepLen,
bytes.length
);
assertEquals(0x3d, bytes[bytes.length-16-6-ggepLen]); //11101
assertEquals(0x31, bytes[bytes.length-16-5-ggepLen]); //10001
// check read back....
qr=(QueryReply)Message.read(new ByteArrayInputStream(bytes));
assertEquals("LIME", qr.getVendor());
assertTrue(qr.getNeedsPush());
assertFalse(qr.getIsBusy());
assertFalse(qr.getHadSuccessfulUpload());
assertTrue(qr.getIsMeasuredSpeed());
assertFalse(qr.getSupportsChat());
assertTrue(qr.getSupportsBrowseHost());
assertFalse(qr.isReplyToMulticastQuery());
//Create extended QHD from scratch with different bits set
// Do not set multicast, as that will unset pushing, busy, etc..
// and generally confuse the test.
responses=new Response[2];
responses[0]=new Response(11,22,"SMDNKD.txt");
responses[1]=new Response(0x2FF2,0xF11F,"OneMore file ");
// first take input of proxies
String[] hosts = {"www.limewire.com", "www.limewire.org",
"www.susheeldaswani.com"};
Set proxies = new TreeSet(IpPort.COMPARATOR);
for (int i = 0; i < hosts.length; i++)
proxies.add(new IpPortImpl(hosts[i], 6346));
qr=new QueryReply(guid, (byte)5,
0xFFFF, ip, u4, responses,
guid, new byte[0],
true, false, false, true, false, false, true,
proxies);
assertEquals("LIME", qr.getVendor());
assertTrue(qr.getNeedsPush());
assertFalse(qr.getIsBusy());
assertFalse(qr.getHadSuccessfulUpload());
assertTrue(qr.getIsMeasuredSpeed());
assertFalse(qr.getSupportsChat());
assertTrue(qr.getSupportsBrowseHost());
assertFalse(qr.isReplyToMulticastQuery());
assertTrue(qr.getSupportsFWTransfer());
//And check raw bytes....
out=new ByteArrayOutputStream();
qr.write(out);
bytes=out.toByteArray();
ggepLen = _ggepUtil.getQRGGEP(true, false, true,
proxies).length;
//Length includes header, query hit header and footer, responses, and
//QHD (public and private)
assertEquals(
(23+11+16)+(8+10+2)+(8+14+2)+(4+1+QueryReply.COMMON_PAYLOAD_LEN+1+1)+ggepLen,
bytes.length
);
assertEquals(0x3d, bytes[bytes.length-16-6-ggepLen]); //11101
assertEquals(0x31, bytes[bytes.length-16-5-ggepLen]); //10001
// check read back....
qr=(QueryReply)Message.read(new ByteArrayInputStream(bytes));
assertEquals("LIME", qr.getVendor());
assertTrue(qr.getNeedsPush());
assertFalse(qr.getIsBusy());
assertFalse(qr.getHadSuccessfulUpload());
assertTrue(qr.getIsMeasuredSpeed());
assertFalse(qr.getSupportsChat());
assertTrue(qr.getSupportsBrowseHost());
assertFalse(qr.isReplyToMulticastQuery());
assertTrue(qr.getSupportsFWTransfer());
//Create from scratch with no bits set
responses=new Response[2];
responses[0]=new Response(11,22,"Sample.txt");
responses[1]=new Response(0x2FF2,0xF11F,"Another file ");
qr=new QueryReply(guid, (byte)5,
0xFFFF, ip, u4, responses,
guid, false);
try {
qr.getVendor();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
try {
qr.getNeedsPush();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
try {
qr.getIsBusy();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
try {
qr.getHadSuccessfulUpload();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
try {
qr.getIsMeasuredSpeed();
fail("qr should have been invalid");
} catch (BadPacketException e) { }
}
public void testCalculateQualityOfService() {
final byte[] addr=new byte[] {(byte)18, (byte)239, (byte)0, (byte)144};
QueryReply reachableNonBusy=new QueryReply(
new byte[16], (byte)5, 6346, addr, 0l, new Response[0], new byte[16],
false, false, //!needsPush, !isBusy
true, false, false, false);
QueryReply reachableBusy=new QueryReply(
new byte[16], (byte)5, 6346, addr, 0l, new Response[0], new byte[16],
false, true, //!needsPush, isBusy
true, false, false, false);
QueryReply unreachableNonBusy=new QueryReply(
new byte[16], (byte)5, 6346, addr, 0l, new Response[0], new byte[16],
true, false, //needsPush, !isBusy
true, false, false, false);
QueryReply unreachableBusy=new QueryReply(
new byte[16], (byte)5, 6346, addr, 0l, new Response[0], new byte[16],
true, true, //needsPush, isBusy
true, false, false, false);
QueryReply oldStyle=new QueryReply(
new byte[16], (byte)5, 6346, addr, 0l, new Response[0],
new byte[16], false);
//Remember that a return value of N corresponds to N+1 stars
assertEquals(3, reachableNonBusy.calculateQualityOfService(false));
assertEquals(3, reachableNonBusy.calculateQualityOfService(true));
assertEquals(1, reachableBusy.calculateQualityOfService(false));
assertEquals(1, reachableBusy.calculateQualityOfService(true));
assertEquals(2, unreachableNonBusy.calculateQualityOfService(false));
assertEquals(-1, unreachableNonBusy.calculateQualityOfService(true));
assertEquals(0, unreachableBusy.calculateQualityOfService(false));
assertEquals(-1, unreachableBusy.calculateQualityOfService(true));
assertEquals(0, oldStyle.calculateQualityOfService(false));
assertEquals(0, oldStyle.calculateQualityOfService(true));
}
public void testGGEPUtil() throws Exception {
GGEP testGGEP = null;
// test standard null GGEP....
try {
// this shouldn't even work....
testGGEP = new GGEP(_ggepUtil.getQRGGEP(false, false, false,
new HashSet()),
0, null);
assertTrue(false);
}
catch (BadGGEPBlockException expected) {}
// test just BH GGEP....
testGGEP = new GGEP(_ggepUtil.getQRGGEP(true, false, false,
new HashSet()),
0, null);
assertEquals(1, testGGEP.getHeaders().size());
assertTrue(testGGEP.hasKey(GGEP.GGEP_HEADER_BROWSE_HOST));
assertTrue(!testGGEP.hasKey(GGEP.GGEP_HEADER_MULTICAST_RESPONSE));
// test just multicast GGEP....
testGGEP = new GGEP(_ggepUtil.getQRGGEP(false, true, false,
new HashSet()),
0, null);
assertEquals(1, testGGEP.getHeaders().size());
assertTrue(!testGGEP.hasKey(GGEP.GGEP_HEADER_BROWSE_HOST));
assertTrue(testGGEP.hasKey(GGEP.GGEP_HEADER_MULTICAST_RESPONSE));
// test combo GGEP....
testGGEP = new GGEP(_ggepUtil.getQRGGEP(true, true, false,
new HashSet()),
0, null);
assertEquals(2, testGGEP.getHeaders().size());
assertTrue(testGGEP.hasKey(GGEP.GGEP_HEADER_BROWSE_HOST));
assertTrue(testGGEP.hasKey(GGEP.GGEP_HEADER_MULTICAST_RESPONSE));
}
public void testBasicPushProxyGGEP() throws Exception {
basicTest(false, false, false);
basicTest(false, true, false);
basicTest(true, false, false);
basicTest(true, true, false);
basicTest(false, false, true);
basicTest(false, true, true);
basicTest(true, false, true);
basicTest(true, true, true);
}
public void basicTest(final boolean browseHost,
final boolean multicast,
final boolean fwTransfer) throws Exception {
int numHeaders = 1;
// first take input of proxies
String[] hosts = {"www.limewire.com", "www.limewire.org",
"www.susheeldaswani.com", "www.stanford.edu"};
Set proxies = new TreeSet(IpPort.COMPARATOR);
for (int i = 0; i < hosts.length; i++)
proxies.add(new IpPortImpl(hosts[i], 6346));
GGEP testGGEP = new GGEP(_ggepUtil.getQRGGEP(browseHost, multicast,
fwTransfer, proxies),
0, null);
if (browseHost) {
numHeaders++;
assertTrue(testGGEP.hasKey(GGEP.GGEP_HEADER_BROWSE_HOST));
}
if (multicast) {
numHeaders++;
assertTrue(testGGEP.hasKey(GGEP.GGEP_HEADER_MULTICAST_RESPONSE));
}
if (fwTransfer) {
numHeaders++;
assertTrue(testGGEP.hasKey(GGEP.GGEP_HEADER_FW_TRANS));
}
assertTrue(testGGEP.hasKey(GGEP.GGEP_HEADER_PUSH_PROXY));
assertEquals(numHeaders, testGGEP.getHeaders().size());
Set retProxies = _ggepUtil.getPushProxies(testGGEP);
assertEquals(4, retProxies.size());
IpPort[] array1 =
(IpPort[])retProxies.toArray(new IpPort[0]);
IpPort[] array2 =
(IpPort[])proxies.toArray(new IpPort[0]);
assertEquals(retProxies, proxies);
//for(int i=0; i<array1.length; i++) {
// assertEquals(array1[i], array2[i]);
//}
//Iterator iter1 = retProxies.iterator();
//Iterator iter2 = proxies.iterator();
//while(iter1.hasNext() && iter2.hasNext()) {
// assertEquals((PushProxyInterface)iter1.next(),
// (PushProxyInterface)iter2.next());
//}
}
public void testBadPushProxyInput() throws Exception {
byte[] badBytes = new byte[6];
GGEP ggep = null;
// test a bad ip
// 0.0.0.0 is a bad address
// trying to input
try {
IpPort proxy =
new IPPortCombo("0.0.0.0", 6346);
fail("allowed bad PPI");
} catch (IllegalArgumentException expected) {}
// from the network
ggep = new GGEP();
badBytes[0] = (byte) 0;
badBytes[1] = (byte) 0;
badBytes[2] = (byte) 0;
badBytes[3] = (byte) 0;
badBytes[4] = (byte) 3;
badBytes[5] = (byte) 4;
ggep.put(GGEP.GGEP_HEADER_PUSH_PROXY, badBytes);
assertEquals(0, _ggepUtil.getPushProxies(ggep).size());
// test a bad port
// trying to input is the only case
try {
IpPort proxy =
new IPPortCombo("0.0.0.0", 634600);
fail("allowed bad PPI");
} catch (IllegalArgumentException expected) {}
// this should work fine...
ggep = new GGEP();
badBytes[0] = (byte) 1;
badBytes[1] = (byte) 1;
badBytes[2] = (byte) 2;
badBytes[3] = (byte) 2;
badBytes[4] = (byte) 0;
badBytes[5] = (byte) 0;
ggep.put(GGEP.GGEP_HEADER_PUSH_PROXY, badBytes);
assertNotNull(_ggepUtil.getPushProxies(ggep));
// try to get proxies when the lengths are wrong
for (int i = 0; i < 100; i++) {
badBytes = new byte[i];
// just put some filler in here....
for (int j = 0; j < badBytes.length; j++) {
// make sure each one is unique, as the returned
// Set from getPushProxies will filter duplicates
if(j%6 == 0) {
badBytes[j] = (byte)j;
}
else{
badBytes[j] = (byte)i;
}
}
ggep = new GGEP();
ggep.put(GGEP.GGEP_HEADER_PUSH_PROXY, badBytes);
if (i == 0)
assertEquals(0, _ggepUtil.getPushProxies(ggep).size());
else if (i < 6)
assertEquals(0, _ggepUtil.getPushProxies(ggep).size());
else {// length is fine
// -1 because the first entry is invalid since it
// begins with 0.
assertEquals((i / 6) - 1,
_ggepUtil.getPushProxies(ggep).size());
}
}
}
public void testManualPushProxyInput() throws Exception {
Random rand = new Random();
GGEP ggep = null;
for (int i = 0; i < 10; i++) {
byte[] bytes = new byte[6*(i+1)];
rand.nextBytes(bytes);
// don't trust zeroes or 255 in IP...
for (int j = 0; j < bytes.length; j++) {
if (bytes[j] == (byte) 0) bytes[j] = (byte) 1;
if (bytes[j] == (byte)255)bytes[j] = (byte)254;
}
// from the network
ggep = new GGEP();
ggep.put(GGEP.GGEP_HEADER_PUSH_PROXY, bytes);
Set proxies = _ggepUtil.getPushProxies(ggep);
assertNotNull(proxies);
Iterator iter = proxies.iterator();
int j = 0;
while(iter.hasNext()) {
final int inIndex = 6*j;
IpPort ppi = (IpPort)iter.next();
InetAddress addr = ppi.getInetAddress();
byte[] tempAddr = new byte[4];
System.arraycopy(bytes, inIndex, tempAddr, 0, 4);
InetAddress addr2 = InetAddress.getByAddress(tempAddr);
String address = addr2.getHostAddress();
int curPort = ByteOrder.leb2int(bytes, inIndex+4, 2);
IpPort ppi2 =
new IpPortImpl(address, curPort);
assertTrue(proxies.contains(ppi2));
//assertEquals(addr, addr2);
//int port = ppi.getPushProxyPort();
/*
final int inIndex = 6*j;
byte[] addrBytes = addr.getAddress();
byte[] portBytes = new byte[2];
ByteOrder.short2leb((short)port, portBytes, 0);
assertEquals(addrBytes[0], bytes[inIndex]);
assertEquals(addrBytes[1], bytes[inIndex+1]);
assertEquals(addrBytes[2], bytes[inIndex+2]);
assertEquals(addrBytes[3], bytes[inIndex+3]);
assertEquals(portBytes[0], bytes[inIndex+4]);
assertEquals(portBytes[1], bytes[inIndex+5]);
*/
j++;
}
}
}
public void testPushProxyQueryReply() throws Exception {
String[] hosts = {"www.limewire.com", "www.limewire.org",
"www.susheeldaswani.com", "www.berkeley.edu"};
for (int outer = 0; outer < hosts.length; outer++) {
//PushProxyInterface[] proxies = new PushProxyInterface[outer+1];
Set proxies = new TreeSet(IpPort.COMPARATOR);
for (int i = 0; i < hosts.length; i++)
proxies.add(
new IpPortImpl(hosts[i], 6346));
QueryReply qr = new QueryReply(GUID.makeGuid(), (byte) 4,
6346, IP, 0, new Response[0],
GUID.makeGuid(), new byte[0],
false, false, true, true, true, false,
proxies);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
qr.write(baos);
ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
QueryReply readQR = (QueryReply) Message.read(bais);
// test read from network
Set retProxies = readQR.getPushProxies();
assertNotNull(retProxies);
assertTrue(retProxies != proxies);
assertEquals(retProxies.size(), proxies.size());
assertEquals(retProxies, proxies);
assertEquals(proxies, retProxies);
// test simple accessor
retProxies = qr.getPushProxies();
assertNotNull(retProxies);
assertTrue(retProxies != proxies);
assertEquals(retProxies.size(), proxies.size());
assertEquals(retProxies, proxies);
assertEquals(proxies, retProxies);
}
}
public void testQueryReplyHasAlternates() throws Exception {
addFilesToLibrary();
addAlternateLocationsToFiles();
boolean checked = false;
for(int i = 0; i < fman.getNumFiles(); i++) {
FileDesc fd = fman.get(i);
Response testResponse = new Response(fd);
String name = fd.getFileName();
char[] illegalChars = SearchSettings.ILLEGAL_CHARS.getValue();
Arrays.sort(illegalChars);
if (name.length() > SearchSettings.MAX_QUERY_LENGTH.getValue()
|| StringUtils.containsCharacters(name, illegalChars)) {
continue;
}
QueryRequest qr = QueryRequest.createQuery(fd.getFileName());
Response[] hits = fman.query(qr);
assertNotNull("didn't get a response for query " + qr, hits);
// we can only do this test on 'unique' names, so if we get more than
// one response, don't test.
if ( hits.length != 1 ) continue;
checked = true;
// first check basic stuff on the response.
assertEquals("responses should be equal", testResponse, hits[0]);
assertEquals("should have 10 other alts",
10, testResponse.getLocations().size());
assertEquals("should have equal alts",
testResponse.getLocations(), hits[0].getLocations());
// then actually create a QueryReply and read it, to make
// sure we can write & read stuff correctly.
QueryReply qReply = new QueryReply(GUID.makeGuid(), (byte) 4,
6346, IP, 0, hits,
GUID.makeGuid(), new byte[0],
false, false, true, true, true, false,
null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
qReply.write(baos);
ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
QueryReply readQR = (QueryReply) Message.read(bais);
List readHits = readQR.getResultsAsList();
assertEquals("wrong # of results", hits.length, readHits.size());
Response hit = (Response)readHits.get(0);
assertEquals("wrong # of alts",
hits[0].getLocations(), hit.getLocations());
}
}
public void testQueryReplyHasCreationTimes() throws Exception {
addFilesToLibrary();
addCreationTimeToFiles();
boolean checked = false;
for(int i = 0; i < fman.getNumFiles(); i++) {
FileDesc fd = fman.get(i);
long expectTime = (fd.getIndex() + 1) * 10013;
Response testResponse = new Response(fd);
assertEquals(expectTime, testResponse.getCreateTime());
String name = fd.getFileName();
char[] illegalChars = SearchSettings.ILLEGAL_CHARS.getValue();
Arrays.sort(illegalChars);
if (name.length() > SearchSettings.MAX_QUERY_LENGTH.getValue()
|| StringUtils.containsCharacters(name, illegalChars)) {
continue;
}
QueryRequest qr = QueryRequest.createQuery(fd.getFileName());
Response[] hits = fman.query(qr);
assertNotNull("didn't get a response for query " + qr, hits);
// we can only do this test on 'unique' names, so if we get more than
// one response, don't test.
if ( hits.length != 1 ) continue;
checked = true;
// first check basic stuff on the response.
assertEquals("responses should be equal", testResponse, hits[0]);
assertEquals("wrong creation time", expectTime,
hits[i].getCreateTime());
// then actually create a QueryReply and read it, to make
// sure we can write & read stuff correctly.
QueryReply qReply = new QueryReply(GUID.makeGuid(), (byte) 4,
6346, IP, 0, hits,
GUID.makeGuid(), new byte[0],
false, false, true, true, true, false,
null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
qReply.write(baos);
ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
QueryReply readQR = (QueryReply) Message.read(bais);
List readHits = readQR.getResultsAsList();
assertEquals("wrong # of results", hits.length, readHits.size());
Response hit = (Response)readHits.get(0);
assertEquals("wrong creation time", expectTime,
hit.getCreateTime());
}
}
/**
* Test to make sure that results that have no name are rejected
*/
public void testThatEmptyResultsAreRejected() throws Exception {
// create a payload that says it has one result, but whose
// result is empty. This should be rejected!
byte[] payload=new byte[11+11+16];
payload[0] = 1; //Number of results
payload[1]=1; //non-zero port
payload[3]=1; //non-blank ip
//payload[11+8]=(byte)65; //The character 'A'
QueryReply qr =
new QueryReply(new byte[16], (byte)5, (byte)0, payload);
try {
List results = qr.getResultsAsList();
fail("should have thrown an exception for empty result");
} catch(BadPacketException e) {
}
}
public void testSecureReplyNoSignature() throws Exception {
int indexes[] = new int[2];
ByteArrayOutputStream payload = new ByteArrayOutputStream();
GGEP ggep = new GGEP(false);
ggep.put("SB");
QueryReply reply = newSecureQueryReply(ggep, indexes, payload);
assertTrue(reply.hasSecureData());
assertNull(reply.getSecureSignature());
runSignatureTest(reply, indexes, payload.toByteArray());
}
public void testSecureReplyWithSignature() throws Exception {
int indexes[] = new int[2];
ByteArrayOutputStream payload = new ByteArrayOutputStream();
GGEP ggep = new GGEP(false);
ggep.put("SB");
byte[] sig = new byte[100];
new Random().nextBytes(sig);
ggep.put("SIG", sig);
QueryReply reply = newSecureQueryReply(ggep, indexes, payload);
assertTrue(reply.hasSecureData());
assertEquals(sig, reply.getSecureSignature());
runSignatureTest(reply, indexes, payload.toByteArray());
}
public void testNoSecureReply() throws Exception {
int indexes[] = new int[2];
ByteArrayOutputStream payload = new ByteArrayOutputStream();
GGEP ggep = new GGEP(false);
ggep.put("SC");
byte[] sig = new byte[100];
new Random().nextBytes(sig);
ggep.put("SIG", sig);
QueryReply reply = newSecureQueryReply(ggep, indexes, payload);
assertFalse(reply.hasSecureData());
assertNull(reply.getSecureSignature());
Provider provider = new TestProvider();
Signature signature = Signature.getInstance("FakeSignature", provider);
signature.initSign(null);
reply.updateSignatureWithSecuredBytes(signature);
assertEquals(-1, FakeSignatureSpi.off1);
assertEquals(-1, FakeSignatureSpi.off2);
assertEquals(-1, FakeSignatureSpi.len1);
assertEquals(-1, FakeSignatureSpi.len2);
assertNull(FakeSignatureSpi.update1);
assertNull(FakeSignatureSpi.update2);
}
public void testSecureStatus() throws Exception {
int indexes[] = new int[2];
ByteArrayOutputStream payload = new ByteArrayOutputStream();
GGEP ggep = new GGEP(false);
QueryReply reply = newSecureQueryReply(ggep, indexes, payload);
assertEquals(SecureMessage.INSECURE, reply.getSecureStatus());
reply.setSecureStatus(SecureMessage.FAILED);
assertEquals(SecureMessage.FAILED, reply.getSecureStatus());
reply.setSecureStatus(SecureMessage.SECURE);
assertEquals(SecureMessage.SECURE, reply.getSecureStatus());
}
private void runSignatureTest(QueryReply reply, int[] indexes, byte[] payload) throws Exception {
Provider provider = new TestProvider();
Signature sig = Signature.getInstance("FakeSignature", provider);
sig.initSign(null);
reply.updateSignatureWithSecuredBytes(sig);
assertNotEquals(-1, FakeSignatureSpi.off1);
assertNotEquals(-1, FakeSignatureSpi.off2);
assertNotEquals(-1, FakeSignatureSpi.len1);
assertNotEquals(-1, FakeSignatureSpi.len2);
assertNotNull(FakeSignatureSpi.update1);
assertNotNull(FakeSignatureSpi.update2);
assertEquals(0, FakeSignatureSpi.off1);
assertEquals(indexes[0], FakeSignatureSpi.len1);
for(int i = 0; i < indexes[0]; i++)
assertEquals("bad match at offset: " + i, payload[i], FakeSignatureSpi.update1[i]);
assertEquals(indexes[1], FakeSignatureSpi.off2);
int len2 = payload.length - 16 - indexes[1];
assertEquals(len2, FakeSignatureSpi.len2);
for(int i = indexes[1]; i < len2 + indexes[1]; i++)
assertEquals("bad match at offset: " + i, payload[i], FakeSignatureSpi.update2[i]);
}
private QueryReply newSecureQueryReply(GGEP secureGGEP, int[] indexes, ByteArrayOutputStream payload) throws Exception {
indexes[0] = -1;
indexes[1] = -1;
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(1); // # of results
ByteOrder.short2leb((short)6346, out); // port
out.write(IP); // ip
ByteOrder.int2leb(1, out);
Response r = new Response(0, 1, "test");
r.writeToStream(out);
out.write(new byte[] { 'L', 'I', 'M', 'E' });
out.write(4); // common payload length
out.write(0x3C); // flags (control no push)
out.write(0x21); // control (yes ggep, flag busy)
ByteOrder.short2leb((short)1, out); // xml size
out.write(0); // no chat
GGEP ggep = new GGEP(false);
ggep.put("test", "data");
ggep.write(out); // normal ggep block.
indexes[0] = out.size();
secureGGEP.write(out);
indexes[1] = out.size();
out.write(0); // null after XML
out.write(new byte[16]); // clientGUID
// copy the payload.
payload.reset();
out.writeTo(payload);
return new QueryReply(new byte[16], (byte)1, (byte)1, out.toByteArray());
}
private void addFilesToLibrary() throws Exception {
String dirString = "com/limegroup/gnutella";
File testDir = CommonUtils.getResourceFile(dirString);
testDir = testDir.getCanonicalFile();
assertTrue("could not find the gnutella directory", testDir.isDirectory());
File[] testFiles = testDir.listFiles(new FileFilter() {
public boolean accept(File file) {
// use files with a $ because they'll generally
// trigger a single-response return, which is
// easier to check
return FileManager.isFilePhysicallyShareable(file) && file.getName().indexOf("$") != -1;
}
});
assertNotNull("no files to test against", testFiles);
assertNotEquals("no files to test against", 0, testFiles.length);
for (int i = 0; i < testFiles.length; i++) {
File shared = new File(_sharedDir, testFiles[i].getName() + "." + EXTENSION);
assertTrue("unable to get file", CommonUtils.copy(testFiles[i], shared));
}
waitForLoad();
assertEquals("unexpected number of shared files", testFiles.length, fman.getNumFiles());
}
private void addAlternateLocationsToFiles() throws Exception {
FileDesc[] fds = fman.getAllSharedFileDescriptors();
for(int i = 0; i < fds.length; i++) {
String urn = fds[i].getSHA1Urn().httpStringValue();
for(int j = 0; j < MAX_LOCATIONS + 5; j++) {
String loc = "http://1.2.3." + j + ":6346/uri-res/N2R?" + urn;
RouterService.getAltlocManager().add(AlternateLocation.create(loc), null);
}
}
}
private void addCreationTimeToFiles() throws Exception {
FileDesc[] fds = fman.getAllSharedFileDescriptors();
for(int i = 0; i < fds.length; i++) {
long time = (fds[i].getIndex() + 1) * 10013;
CreationTimeCache.instance().addTime(fds[i].getSHA1Urn(), time);
CreationTimeCache.instance().commitTime(fds[i].getSHA1Urn());
}
}
private class FManCallback extends ActivityCallbackStub {
public void fileManagerLoaded() {
synchronized(loaded) {
loaded.notify();
}
}
}
private void waitForLoad() {
synchronized(loaded) {
try {
fman.loadSettings();
loaded.wait();
} catch (InterruptedException e) {
//good.
}
}
}
/*
* All the below crap is because we can't subclass Signature and instead need to provide an SPI.
*/
/** Provider that references the fake SPI */
private static final class TestProvider extends Provider {
TestProvider() {
super("LIME", 1.0, "LIME test provider");
put("Signature.FakeSignature", FakeSignatureSpi.class.getName());
}
}
/** SPI that stores things statically because there's no other reference to it anywhere. */
public static class FakeSignatureSpi extends SignatureSpi {
private static byte[] update1;
private static int off1;
private static int len1;
private static byte[] update2;
private static int off2;
private static int len2;
protected Object engineGetParameter(String arg0) throws InvalidParameterException {
return null;
}
protected void engineInitSign(PrivateKey arg0) throws InvalidKeyException {
update1 = null;
off1 = -1;
len1 = -1;
update2 = null;
off2 = -1;
len2 = -1;
}
protected void engineInitVerify(PublicKey arg0) throws InvalidKeyException {
}
protected void engineSetParameter(String arg0, Object arg1) throws InvalidParameterException {
}
protected byte[] engineSign() throws SignatureException {
return null;
}
protected void engineUpdate(byte arg0) throws SignatureException {
}
protected void engineUpdate(byte[] data, int off, int len) throws SignatureException {
if(update1 == null) {
update1 = data;
off1 = off;
len1 = len;
} else if(update2 == null) {
update2 = data;
off2 = off;
len2 = len;
} else {
fail("updating signature more than twice!");
}
}
protected boolean engineVerify(byte[] arg0) throws SignatureException {
return false;
}
}
}