package com.limegroup.gnutella.dht.db; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.Set; import org.limewire.collection.BitNumbers; import org.limewire.io.GGEP; import org.limewire.io.GUID; import org.limewire.io.IpPort; import org.limewire.io.NetworkUtils; import org.limewire.mojito.db.DHTValueType; import org.limewire.mojito.routing.Version; import org.limewire.util.ByteUtils; import com.limegroup.gnutella.uploader.HTTPHeaderUtils; /** * An implementation of <code>DHTValue</code> for Gnutella Push Proxies. */ public abstract class AbstractPushProxiesValue implements PushProxiesValue { /** * DHTValueType for Push Proxies. */ public static final DHTValueType PUSH_PROXIES = DHTValueType.valueOf("Gnutella Push Proxy", "PROX"); /** * Version of PushProxiesDHTValue. */ public static final Version VERSION = Version.valueOf(0); static final String CLIENT_ID = "client-id"; static final String FWT_VERSION = "fwt-version"; static final String FEATURES = "features"; static final String PORT = "port"; static final String PROXIES = "proxies"; static final String TLS = "tls"; private final Version version; public AbstractPushProxiesValue(Version version) { this.version = version; } public DHTValueType getValueType() { return PUSH_PROXIES; } public Version getVersion() { return version; } public int size() { return getValue().length; } /** * Value based comparison with other object. */ @Override public final boolean equals(Object obj) { if (obj instanceof PushProxiesValue) { PushProxiesValue other = (PushProxiesValue)obj; return Arrays.equals(getGUID(), other.getGUID()) && getPort() == other.getPort() && getFeatures() == other.getFeatures() && getFwtVersion() == other.getFwtVersion() && getPushProxies().equals(other.getPushProxies()) && getTLSInfo().equals(other.getTLSInfo()); } return false; } @Override public final int hashCode() { return getPort() + getFeatures() * 31 + getFwtVersion() * 31 * 31 + computeHashCode(getPushProxies()) * 31 * 31 * 31 + getTLSInfo().hashCode() * 31 * 31 * 31 * 31; } private static final int computeHashCode(Set<? extends IpPort> pushProxies) { int hashCode = 1; // in case of connectables: we ignore the tls info for (IpPort ipPort : pushProxies) { hashCode *= 31 * ipPort.getInetSocketAddress().hashCode(); } return hashCode; } @Override public String toString() { StringBuilder buffer = new StringBuilder(); buffer.append("GUID=").append(new GUID(getGUID())).append("\n"); buffer.append("Features=").append(getFeatures()).append("\n"); buffer.append("FWTVersion=").append(getFwtVersion()).append("\n"); buffer.append("PushProxies=").append(getPushProxies()).append("\n"); return buffer.toString(); } /** * A helper method to serialize PushProxiesValues */ protected static byte[] serialize(PushProxiesValue value) { GGEP ggep = new GGEP(); ggep.put(CLIENT_ID, value.getGUID()); // Preserve insertion as an int, not a byte, for backwards compatability. ggep.put(FEATURES, (int)value.getFeatures()); ggep.put(FWT_VERSION, value.getFwtVersion()); byte[] port = new byte[2]; ByteUtils.short2beb((short)value.getPort(), port, 0); ggep.put(PORT, port); try { Set<? extends IpPort> proxies = value.getPushProxies(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); for (IpPort proxy : proxies) { byte[] ipp = NetworkUtils.getBytes(proxy, java.nio.ByteOrder.BIG_ENDIAN); assert (ipp.length == 6 || ipp.length == 18); baos.write(ipp.length); baos.write(ipp); } baos.close(); ggep.put(PROXIES, baos.toByteArray()); if (!value.getTLSInfo().isEmpty()) ggep.put(TLS,value.getTLSInfo().toByteArray()); } catch (IOException err) { // Impossible throw new RuntimeException(err); } return ggep.toByteArray(); } static BitNumbers getNumbersFromProxies(Set<? extends IpPort> proxies) { return BitNumbers.synchronizedBitNumbers(HTTPHeaderUtils.getTLSIndices(proxies)); } }