package com.limegroup.gnutella.messages.vendor;
import org.limewire.io.GUID;
import org.limewire.util.ByteUtils;
import com.limegroup.gnutella.messages.BadPacketException;
/**
* A request for a given ultrapeer's ultrapeer connections. Useful for crawling.
* <p>
* 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 AbstractVendorMessage {
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 << 0;
public static final byte LOCALE_INFO = 0x1 << 1;
public static final byte NEW_ONLY = 0x1 << 2;
public static final byte USER_AGENT = 0x1 << 3;
public static final byte NODE_UPTIME = 0x1 << 4;
public static final byte REPLIES = 0x1 << 5;
public static final byte DHT_STATUS = 0x1 << 6;
// all features OR'd.
public static final byte FEATURE_MASK = CONNECTION_TIME | LOCALE_INFO | NEW_ONLY | USER_AGENT
| NODE_UPTIME | REPLIES | DHT_STATUS;
/**
* 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_CRAWLER_PING, 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];
ByteUtils.short2leb((short) numberUP, temp, 0);
payload[0] = temp[0];
ByteUtils.short2leb((short) numberLeaves, temp, 0);
payload[1] = temp[0];
// the third byte is the requested format
payload[2] = features;
return payload;
}
/**
* See superclass comment.
* <p>
* 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,
Network network) throws BadPacketException {
super(guid, ttl, hops, F_LIME_VENDOR_ID, F_CRAWLER_PING, version, payload, network);
// 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 = ByteUtils.ubyte2int(payload[0]);
_numberLeaves = ByteUtils.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 this node's connection uptime
*/
public boolean hasNodeUptime() {
return (byte) (NODE_UPTIME & _format) == NODE_UPTIME;
}
/**
*
* @return whether the ping is requesting this node DHT status
*/
public boolean hasDHTStatus() {
return (byte) (DHT_STATUS & _format) == DHT_STATUS;
}
/**
*
* @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;
}
/**
* @return whether the ping wants the number of replies received over each
* connection
*/
public boolean hasReplies() {
return hasFeature(REPLIES);
}
/**
* Checks whether the 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;
}
}