package org.ripple.power.txns.btc; import java.io.EOFException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.Arrays; /** * A PeerAddress holds an IP address and port number representing the network location of * a peer in the Bitcoin Peer-to-Peer network. */ public class PeerAddress implements ByteSerializable { /** Length of an encoded peer address */ public static final int PEER_ADDRESS_SIZE = 30; /** IPv6-encoded IPv4 address prefix */ public static final byte[] IPV6_PREFIX = new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xff, (byte)0xff }; /** The IP address */ private InetAddress address; /** The IP port */ private int port; /** Time seen */ private long timeSeen; /** Peer services */ private long services; /** Peer connected */ private boolean connected; /** Time peer connected */ private long timeConnected; /** Outbound connection */ private boolean outboundConnection; /** Static address */ private boolean staticAddress; /** Address has been broadcast */ private boolean wasBroadcast; /** * Constructs a peer address from the given IP address and port * * @param address IP address * @param port IP port */ public PeerAddress(InetAddress address, int port) { this.address = address; this.port = port; timeSeen = System.currentTimeMillis()/1000; } /** * Constructs a peer address from the given IP address and port * * @param address IP address * @param port IP port * @param timeSeen Latest time peer was seen */ public PeerAddress(InetAddress address, int port, long timeSeen) { this.address = address; this.port = port; this.timeSeen = timeSeen; } /** * Constructs a peer address from a network socket * * @param socket Network socket */ public PeerAddress(InetSocketAddress socket) { this(socket.getAddress(), socket.getPort()); } /** * Constructs a peer address from a string in the format "[address]:port" where * address can be "nnn.nnn.nnn.nnn" for IPv4 or "xxxx:xxxx:xxxx;xxxx:xxxx:xxxx:xxxx:xxxx" * for IPv6. * * @param peerString Address string * @throws UnknownHostException Incorrect address format */ public PeerAddress(String peerString) throws UnknownHostException { // // Separate the address and the port // int addrSep = peerString.lastIndexOf(']'); int portSep = peerString.lastIndexOf(':'); if (peerString.charAt(0) != '[' || addrSep < 0 || portSep < addrSep || portSep == peerString.length()-1) throw new UnknownHostException("Incorrect [address]:port format"); String addrString = peerString.substring(1, addrSep); String portString = peerString.substring(portSep+1); // // Create the address and port values // address = InetAddress.getByName(addrString); port = Integer.parseInt(portString); timeSeen = System.currentTimeMillis()/1000; } /** * Constructs a peer address from the serialized data * * @param inBuffer Serialized buffer * @throws EOFException End-of-data while processing serialized data */ public PeerAddress(SerializedBuffer inBuffer) throws EOFException { // // Get the address values // timeSeen = inBuffer.getInt(); services = inBuffer.getLong(); byte[] addrBytes = inBuffer.getBytes(16); port = (inBuffer.getUnsignedByte()<<8) | inBuffer.getUnsignedByte(); // // Generate the IPv4 or IPv6 address // try { boolean ipv4 = true; for (int j=0; j<12; j++) { if (addrBytes[j] != IPV6_PREFIX[j]) { ipv4 = false; break; } } if (ipv4) address = InetAddress.getByAddress(Arrays.copyOfRange(addrBytes, 12, 16)); else address = InetAddress.getByAddress(addrBytes); } catch (UnknownHostException exc) { throw new RuntimeException("Unexpected exception thrown by InetAddress.getByAddress: "+exc.getMessage()); } } /** * Writes the serialized address to a buffer * * @param buffer Serialized buffer * @return Serialized buffer */ @Override public SerializedBuffer getBytes(SerializedBuffer buffer) { buffer.putInt((int)timeSeen) .putLong(services); byte[] addrBytes = address.getAddress(); if (addrBytes.length == 16) buffer.putBytes(addrBytes); else buffer.putBytes(IPV6_PREFIX) .putBytes(addrBytes); buffer.putUnsignedByte(port>>>8) .putUnsignedByte(port); return buffer; } /** * Returns the serialized address * * @return Serialized address */ @Override public byte[] getBytes() { SerializedBuffer buffer = new SerializedBuffer(PEER_ADDRESS_SIZE); return getBytes(buffer).toByteArray(); } /** * Returns the IP address * * @return IP address */ public InetAddress getAddress() { return address; } /** * Sets the IP address * * @param address IP address */ public void setAddress(InetAddress address) { this.address = address; } /** * Returns the IP port * * @return IP port */ public int getPort() { return port; } /** * Sets the IP port * * @param port IP port */ public void setPort(int port) { this.port = port; } /** * Returns the timestamp for this peer * * @return Timestamp in seconds since the epoch */ public long getTimeStamp() { return timeSeen; } /** * Sets the timestamp for this peer * * @param timeSeen Time peer was seen in seconds since the epoch */ public void setTimeStamp(long timeSeen) { this.timeSeen = timeSeen; } /** * Check if address has been broadcast * * @return True if address has been broadcast */ public boolean wasBroadcast() { return wasBroadcast; } /** * Set address broadcast status * * @param wasBroadcast True if the address has been broadcast */ public void setBroadcast(boolean wasBroadcast) { this.wasBroadcast = wasBroadcast; } /** * Sets the peer services * * @param services Peer services */ public void setServices(long services) { this.services = services; } /** * Returns the peer services * * @return Peer services */ public long getServices() { return services; } /** * Checks if this peer is connected * * @return TRUE if the peer is connected */ public boolean isConnected() { return connected; } /** * Sets the peer connection status * * @param isConnected TRUE if the peer is connected */ public void setConnected(boolean isConnected) { connected = isConnected; } /** * Returns the time the peer connected to us * * @return Time connected */ public long getTimeConnected() { return timeConnected; } /** * Sets the time the peer connected to us * * @param timeConnected Time the peer connected (seconds since the epoch) */ public void setTimeConnected(long timeConnected) { this.timeConnected = timeConnected; } /** * Checks if this is an outbound connection * * @return TRUE if this is an outbound connection */ public boolean isOutbound() { return outboundConnection; } /** * Set the peer connection type * * @param isOutbound TRUE if this is an outbound connection */ public void setOutbound(boolean isOutbound) { outboundConnection = isOutbound; } /** * Check if this is a static address * * @return TRUE if this is a static address */ public boolean isStatic() { return staticAddress; } /** * Set the address type * * @param isStatic TRUE if this is a static address */ public void setStatic(boolean isStatic) { staticAddress = isStatic; } /** * Return a socket address for our IP address and port * * @return Socket address */ public InetSocketAddress toSocketAddress() { return new InetSocketAddress(address, port); } /** * Returns a string representation of the IP address and port * * @return String representation */ @Override public String toString() { return String.format("[%s]:%d", address.getHostAddress(), port); } /** * Checks if the supplied address is equal to this address * * @param obj Address to check * @return TRUE if the addresses are equal */ @Override public boolean equals(Object obj) { return (obj!=null && (obj instanceof PeerAddress) && address.equals(((PeerAddress)obj).address) && port == ((PeerAddress)obj).port); } /** * Returns the hash code for this object * * @return The hash code */ @Override public int hashCode() { return (address.hashCode()^port); } }