package com.limegroup.gnutella.messages.vendor; import com.limegroup.gnutella.ByteOrder; import com.limegroup.gnutella.GUID; import com.limegroup.gnutella.messages.BadPacketException; /** * a request for a given ultrapeer's ultrapeer connections. * Useful for crawling. * * Currently it doesn't do any validation of the source, i.e. we * sent our list of ultrapeer to whomever requests it. */ public class UDPCrawlerPing extends VendorMessage { public static final int VERSION = 1; public static final int ALL=-1; /** * the number of requested ultrapeer results. */ private int _numberUP; /** * the number of requested leaf results. */ private int _numberLeaves; /** * a bitmask representing the format of the message. Extensible with up to 8 features. * Add more to the list below, make sure you update the feature mask. */ private byte _format; public static final byte PLAIN = 0x0; public static final byte CONNECTION_TIME = 0x1; public static final byte LOCALE_INFO = 0x2; public static final byte NEW_ONLY = 0x4; public static final byte USER_AGENT = 0x8; //public static final byte SAMPLE_FEATURE = 0x10; //its a bitmask, so the next feature would be 0x16, etc. //all features OR'd. public static final byte FEATURE_MASK = 0xF; /** * constructs a new ultrapeer request message. * @param guid the guid of the message * @param number the number of ultrapeers desired * @param features the features we want to receive in the pong */ public UDPCrawlerPing(GUID guid, int numberUP, int numberLeaves, byte features) { super(F_LIME_VENDOR_ID, F_GIVE_ULTRAPEER, VERSION, derivePayload(numberUP,numberLeaves, features)); setGUID(guid); _numberUP = numberUP; _numberLeaves = numberLeaves; _format = (byte) (features & FEATURE_MASK); } /** * constructs a new ultrapeer request message, asking for * all ultrapeers and leafs of the other guy. * @param guid the guid of the message */ public UDPCrawlerPing(GUID guid) { this(guid,ALL,ALL,PLAIN); } private static byte [] derivePayload(int numberUP, int numberLeaves, byte features) { //we don't expect to have more than 255 connections soon if (numberUP > 255) numberUP=255; if (numberLeaves > 255) numberLeaves=255; //trim the features to the ones we currently support features= (byte) (features & FEATURE_MASK); byte [] temp = new byte [2]; byte [] payload = new byte[3]; ByteOrder.short2leb((short)numberUP,temp,0); payload[0]=temp[0]; ByteOrder.short2leb((short)numberLeaves,temp,0); payload[1] = temp[0]; //the third byte is the requested format payload[2]=features; return payload; } /** * see superclass comment * * note this does not have upper limit to the number of requested results * (other than the 255 byte limit). One day we may have many more connections.. */ protected UDPCrawlerPing(byte[] guid, byte ttl, byte hops, int version, byte[] payload) throws BadPacketException { super(guid, ttl, hops, F_LIME_VENDOR_ID, F_GIVE_ULTRAPEER, version, payload); //see if the payload is valid if (getVersion() == VERSION && (payload == null || payload.length != 3)) throw new BadPacketException(); //a new version would ideally keep the first 3 bytes the same. _numberUP = ByteOrder.ubyte2int(payload[0]); _numberLeaves = ByteOrder.ubyte2int(payload[1]); _format = payload[2]; //trim the features _format =(byte)(_format & FEATURE_MASK); } /** * @return Returns the number of UP neighbor addresses that were requested * with this message. */ public int getNumberUP() { return _numberUP; } /** * @return Returns the number of Leaf neighbor addresses that were requested * with this message. */ public int getNumberLeaves() { return _numberLeaves; } /** * * @return whether the ping is requesting connection uptimes. */ public boolean hasConnectionTime() { return (byte)(CONNECTION_TIME & _format) == CONNECTION_TIME; } /** * * @return whether the ping is requesting locale info */ public boolean hasLocaleInfo() { return (byte)(LOCALE_INFO & _format) == LOCALE_INFO; } /** * * @return whether the ping wants to receive only connections which * support UDP pinging (useful for crawling) */ public boolean hasNewOnly() { return (byte)(NEW_ONLY & _format) == NEW_ONLY; } /** * * @return whether the ping wants to receive information about the * User-Agent strings reported by the connections. */ public boolean hasUserAgent() { return (byte)(USER_AGENT & _format) == USER_AGENT; } /** * checks whether ht ping is requesting a specific feature. * @param featureId the byte id of the feature * @return whether the ping is asking for it */ public boolean hasFeature(byte featureId) { return (byte)(featureId & _format) == featureId; } /** * @return Returns the _format. */ public byte getFormat() { return _format; } }