/* * TrackedPeerInfo - All public information concerning a peer. Copyright (C) 2003 Mark J. * Wielaard * * This file is part of Snark. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307, USA. * * Revised by Stephen L. Reed, Dec 22, 2009. * Reformatted, fixed Checkstyle, Findbugs and PMD violations, and substituted Log4J logger * for consistency with the Texai project. */ package org.texai.torrent; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Map; import org.texai.torrent.bencode.BDecoder; import org.texai.torrent.bencode.BEValue; import org.texai.torrent.bencode.InvalidBEncodingException; import org.texai.util.TexaiException; /** All public information concerning a peer. */ public final class TrackedPeerInfo implements Comparable<TrackedPeerInfo> { /** the peer id bytes */ private byte[] peerIdBytes; /** the internet address */ private final InetAddress inetAddress; /** the connection port */ private final int port; /** the hash */ private final int hash; /** Constructs a new TrackedPeerInfo instance. * * @param peerIdBytes the peer id bytes * @param inetAddress the internet address * @param port the connection port */ public TrackedPeerInfo( final byte[] peerIdBytes, final InetAddress inetAddress, final int port) { this.peerIdBytes = peerIdBytes; this.inetAddress = inetAddress; this.port = port; hash = calculateHash(); } /** Creates a TrackedPeerInfo from a BDecoder. * * @param bDecoder the BDecoder * @throws IOException when an input/output error occurs */ public TrackedPeerInfo(final BDecoder bDecoder) throws IOException { this(bDecoder.bdecodeMap().getMap()); } /** Creates a TrackedPeerInfo from a Map containing BEncoded peer id, ip and port. * * @param map the map containing BEncoded peer id, ip and port * @throws InvalidBEncodingException when the map is missing the internet address or connection port * @throws UnknownHostException when the host cannot be found */ public TrackedPeerInfo(final Map<String, BEValue> map) throws InvalidBEncodingException, UnknownHostException { BEValue bevalue = map.get("peer id"); if (bevalue == null) { throw new InvalidBEncodingException("peer id missing"); } peerIdBytes = bevalue.getBytes(); bevalue = map.get("ip"); if (bevalue == null) { throw new InvalidBEncodingException("ip missing"); } inetAddress = InetAddress.getByName(bevalue.getString()); bevalue = map.get("port"); if (bevalue == null) { throw new InvalidBEncodingException("port missing"); } port = bevalue.getInt(); hash = calculateHash(); } /** Gets the peer id bytes. * * @return the peer id bytes */ public byte[] getPeerIdBytes() { return peerIdBytes; } /** Sets the peer id bytes * * @param peerIdBytes the peer id bytes */ public void setID(final byte[] peerIdBytes) { this.peerIdBytes = peerIdBytes; } /** Gets the internet address. * * @return the internet address */ public InetAddress getInetAddress() { return inetAddress; } /** Gets the connection port. * * @return the connection port */ public int getPort() { return port; } /** Calculates the hash for this peer. * * @return the hash for this peer */ private int calculateHash() { return (inetAddress.hashCode()) ^ port; } /** Returns a hash code for this object. * * @return a hash code for this object */ @Override public int hashCode() { return hash; } /** Returns true if and only if this peerID and the given peerID have the * same 20 bytes as their IDs. * * @param pid the other peer id * @return whether peerID and the given peerID have the * same 20 bytes as their IDs */ public boolean samePeerIdBytes(final TrackedPeerInfo pid) { boolean equal = true; for (int i = 0; equal && i < peerIdBytes.length; i++) { equal = peerIdBytes[i] == pid.peerIdBytes[i]; } return equal; } /** Returns whether some other peed id is equal to this one. If both peer id byte arrays are non-zero, then * they are used for the equality test, otherwise the internet address and port numbers are used. * * @param obj the other object * @return whether some other peed id is equal to this one */ @Override public boolean equals(final Object obj) { if (obj instanceof TrackedPeerInfo) { final TrackedPeerInfo pid = (TrackedPeerInfo) obj; boolean isEitherAllZero = false; final int peerIdBytes_len = peerIdBytes.length; boolean isAllZero = true; for (int i = 0; i < peerIdBytes_len; i++) { if (peerIdBytes[i] != 0) { isAllZero = false; break; } } if (isAllZero) { isEitherAllZero = true; } else { final byte[] pidPeerIdBytes = pid.peerIdBytes; final int pidPeerIdBytes_len = pidPeerIdBytes.length; isAllZero = true; for (int i = 0; i < pidPeerIdBytes_len; i++) { if (pidPeerIdBytes[i] != 0) { isAllZero = false; break; } } if (isAllZero) { isEitherAllZero = true; } } if (isEitherAllZero) { return port == pid.port && inetAddress.equals(pid.inetAddress); } else { return Arrays.equals(peerIdBytes, pid.peerIdBytes); } } else { return false; } } /** Compares port, address and id. * * @param pid the other peer id. * @return -1 if this peer id is less than the other one, 0 if they are equal, otherwise +1 */ @Override public int compareTo(final TrackedPeerInfo pid) { int result = port - pid.port; if (result != 0) { return result; } result = inetAddress.hashCode() - pid.inetAddress.hashCode(); if (result != 0) { return result; } for (int i = 0; i < peerIdBytes.length; i++) { result = peerIdBytes[i] - pid.peerIdBytes[i]; if (result != 0) { return result; } } return 0; } /** Returns a string representation for this object. * * @return the String "address:port" */ @Override public String toString() { return inetAddress.getHostAddress() + ":" + port; } /** Returns an ID string representation for this object. * * @return an ID string representation for this object */ public String toIDString() { try { return new String(peerIdBytes, "US-ASCII"); } catch (UnsupportedEncodingException ex) { throw new TexaiException(ex); } } /** Encodes an id as a hex encoded string. * * @param idBytes the id bytes * @return a hex encoded string */ public static String hexEncode(final byte[] idBytes) { boolean leading_zeros = true; final StringBuffer stringBuffer = new StringBuffer(idBytes.length * 2); for (byte element : idBytes) { final int hexDigit = element & 0xFF; if (leading_zeros && hexDigit == 0) { continue; } else { leading_zeros = false; } if (hexDigit < 16) { stringBuffer.append('0'); } stringBuffer.append(Integer.toHexString(hexDigit)); } return stringBuffer.toString(); } }