/** * */ package com.limegroup.gnutella.dht.db; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Set; import org.limewire.collection.BitNumbers; import org.limewire.io.BadGGEPBlockException; import org.limewire.io.BadGGEPPropertyException; import org.limewire.io.ConnectableImpl; import org.limewire.io.GGEP; import org.limewire.io.IpPort; import org.limewire.io.IpPortImpl; import org.limewire.io.IpPortSet; import org.limewire.io.NetworkUtils; import org.limewire.mojito.exceptions.DHTValueException; import org.limewire.mojito.routing.Version; import org.limewire.util.ByteUtils; public class PushProxiesValueImpl extends AbstractPushProxiesValue { private static final long serialVersionUID = -2912251955825278890L; /** * The GUID of the Gnutella Node. */ private final byte[] guid; /** * Gnutella features bit-field. */ private final byte features; /** * Gnutella Firewall-2-Firewall Transfer Protocol version. */ private final int fwtVersion; /** * The port number which may differ from the Contact addresse's * port number. */ private final int port; /** * A Set of PushProxy IpPorts. */ private final Set<? extends IpPort> proxies; /** * The raw bytes of the value. */ private final byte[] data; /** * TLS info for the push proxies. */ private final BitNumbers tlsInfo; /** * Constructor for testing purposes. */ public PushProxiesValueImpl(Version version, byte[] guid, byte features, int fwtVersion, int port, Set<? extends IpPort> proxies) { super(version); this.guid = guid; this.features = features; this.fwtVersion = fwtVersion; this.port = port; this.proxies = new IpPortSet(proxies); this.tlsInfo = AbstractPushProxiesValue.getNumbersFromProxies(proxies); this.data = AbstractPushProxiesValue.serialize(this); } public PushProxiesValueImpl(Version version, byte[] data) throws DHTValueException { super(version); if (version == null) { throw new DHTValueException("Version is null"); } if (data == null) { throw new DHTValueException("Data is null"); } this.data = data; try { GGEP ggep = new GGEP(data, 0); this.guid = ggep.getBytes(AbstractPushProxiesValue.CLIENT_ID); if (guid.length != 16) { throw new DHTValueException("Illegal GUID length: " + guid.length); } // Ideally this would be changed to getByte and getByte would be added, // but since clients in the field are inserting features as an int, // we need to preserve the functionality. this.features = (byte)ggep.getInt(AbstractPushProxiesValue.FEATURES); this.fwtVersion = ggep.getInt(AbstractPushProxiesValue.FWT_VERSION); byte[] portBytes = ggep.getBytes(AbstractPushProxiesValue.PORT); this.port = ByteUtils.beb2short(portBytes, 0) & 0xFFFF; if (!NetworkUtils.isValidPort(port)) { throw new DHTValueException("Illegal port: " + port); } BitNumbers tlsInfo = BitNumbers.EMPTY_BN; try { tlsInfo = new BitNumbers(ggep.getBytes(AbstractPushProxiesValue.TLS)); } catch (BadGGEPPropertyException notThere){} byte[] proxiesBytes = ggep.getBytes(AbstractPushProxiesValue.PROXIES); ByteArrayInputStream bais = new ByteArrayInputStream(proxiesBytes); DataInputStream in = new DataInputStream(bais); Set<IpPort> proxies = new IpPortSet(); int id = 0; while(in.available() > 0) { int length = in.readUnsignedByte(); if (length != 6 && length != 18) { throw new IOException("Illegal IP:Port length: " + length); } byte[] addr = new byte[length-2]; in.readFully(addr); int port = in.readUnsignedShort(); if (!NetworkUtils.isValidPort(port)) { throw new DHTValueException("Illegal port: " + port); } IpPort proxy = new IpPortImpl(InetAddress.getByAddress(addr), port); if (tlsInfo.isSet(id++)) proxy = new ConnectableImpl(proxy, true); proxies.add(proxy); } this.proxies = proxies; this.tlsInfo = tlsInfo; } catch (BadGGEPPropertyException err) { throw new DHTValueException(err); } catch (BadGGEPBlockException err) { throw new DHTValueException(err); } catch (UnknownHostException err) { throw new DHTValueException(err); } catch (IOException err) { throw new DHTValueException(err); } } /* * (non-Javadoc) * @see org.limewire.mojito.db.DHTValue#getValue() */ public byte[] getValue() { byte[] copy = new byte[data.length]; System.arraycopy(data, 0, copy, 0, data.length); return copy; } /* * (non-Javadoc) * @see org.limewire.mojito.db.DHTValue#write(java.io.OutputStream) */ public void write(OutputStream out) throws IOException { out.write(data); } /* * (non-Javadoc) * @see com.limegroup.gnutella.dht.db.PushProxiesValue#getGUID() */ public byte[] getGUID() { return guid; } /* * (non-Javadoc) * @see com.limegroup.gnutella.dht.db.PushProxiesValue#getFeatures() */ public byte getFeatures() { return features; } /* * (non-Javadoc) * @see com.limegroup.gnutella.dht.db.PushProxiesValue#getFwtVersion() */ public int getFwtVersion() { return fwtVersion; } /* * (non-Javadoc) * @see com.limegroup.gnutella.dht.db.PushProxiesValue#getPort() */ public int getPort() { return port; } /* * (non-Javadoc) * @see com.limegroup.gnutella.dht.db.PushProxiesValue#getPushProxies() */ public Set<? extends IpPort> getPushProxies() { return proxies; } public BitNumbers getTLSInfo() { return tlsInfo; } }