package org.limewire.io; import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; import org.limewire.util.ByteUtils; import org.limewire.util.Decorator; /** * Provides methods for network programming. * <code>NetworkUtils</code>' methods check the validity of IP addresses, ports * and socket addresses. <code>NetworkUtils</code> includes both * IPv4 and * <a href="http://en.wikipedia.org/wiki/IPv6">IPv6</a> compliant methods. */ //2345678|012345678|012345678|012345678|012345678|012345678|012345678|012345678| public final class NetworkUtils { /** * Netmask for Class C Networks. */ public static final int CLASS_C_NETMASK = 0xFFFFFF00; /** * Ensure that this class cannot be constructed. */ private NetworkUtils() {} /** * Determines if the given addr or port is valid. * Both must be valid for this to return true. */ public static boolean isValidAddressAndPort(byte[] addr, int port) { return isValidAddress(addr) && isValidPort(port); } /** * Determines if the given addr or port is valid. * Both must be valid for this to return true. */ public static boolean isValidAddressAndPort(String addr, int port) { return isValidAddress(addr) && isValidPort(port); } /** * Determines if the given IpPort is valid. Does resolve address again * by name to do so. */ public static boolean isValidIpPort(IpPort ipport) { return isValidAddress(ipport.getAddress()) && isValidPort(ipport.getPort()); } /** * Returns whether or not the specified port is within the valid range of * ports. * * @param port * the port number to check */ public static boolean isValidPort(int port) { return (port > 0 && port <= 0xFFFF); } /** * Returns whether or not the specified address is valid. * <p> * This method is IPv6 compliant */ public static boolean isValidAddress(byte[] address) { return !isAnyLocalAddress(address) && !isInvalidAddress(address) && !isBroadcastAddress(address) && !isDocumentationAddress(address); } /** * Returns whether or not the specified IP is valid. */ public static boolean isValidAddress(IP ip) { int msb = (ip.addr >> 24) & 0xFF; return (msb != 0x00 && msb != 0xFF); } /** * Returns whether or not the specified InetAddress is valid. */ public static boolean isValidAddress(InetAddress address) { return !address.isAnyLocalAddress() && !isInvalidAddress(address) && !isBroadcastAddress(address) && !isDocumentationAddress(address); } /** * Returns whether or not the specified host is a valid address. */ public static boolean isValidAddress(String host) { try { return isValidAddress(InetAddress.getByName(host)); } catch(UnknownHostException uhe) { return false; } } /** * @return true if the provided string is a dotted ipv4 address * of the format "a.b.c.d" */ public static boolean isDottedIPV4(String s) { int octets = 0; while(octets < 3) { int dot = s.indexOf("."); if (dot == -1) return false; String octet = s.substring(0,dot); try { int parsed = Integer.parseInt(octet); if (parsed < 0 || parsed > 255) return false; } catch (NumberFormatException bad) { return false; } octets++; s = s.substring(Math.min(dot+1,s.length()),s.length()); } if (s.indexOf(".") != -1) return false; try { int parsed = Integer.parseInt(s); if (parsed < 0 || parsed > 255) return false; } catch (NumberFormatException bad) { return false; } return true; } /** * Determines if <code>hostAndPort</code> is either "host" or in * "host:port" format in which case the port is checked if it is within a * valid range. */ public static boolean isAddress(String hostAndPort) { hostAndPort = hostAndPort.trim(); int i = hostAndPort.indexOf(":"); if (i == -1) { return hostAndPort.length() > 0; } else if (i > 0){ try { final int port = Integer.parseInt(hostAndPort.substring(i + 1)); return isValidPort(port); } catch(NumberFormatException e) { } } return false; } /** * @return whether the IpPort is a valid external address. */ static boolean isValidExternalIpPort(IpPort addr) { InetAddress address = addr.getInetAddress(); return address != null && isValidAddress(address) && isValidPort(addr.getPort()); } /** * Returns whether or not the specified InetAddress and Port is valid. */ public static boolean isValidSocketAddress(SocketAddress address) { InetSocketAddress iaddr = (InetSocketAddress)address; return !iaddr.isUnresolved() && isValidAddress(iaddr.getAddress()) && isValidPort(iaddr.getPort()); } /** * Returns true if the InetAddress is any of our local machine addresses * * This method is IPv6 compliant */ public static boolean isLocalAddress(InetAddress addr) { // There are cases where InetAddress.getLocalHost() returns addresses // such as 127.0.1.1 (note the two 1) but if you iterate through all // NetworkInterfaces and look at every InetAddress then it's not there // and NetworkInterface.getByInetAddress(...) returns null 'cause it // cannot find an Interface for it. The following checks take care // of this case. if (addr.isAnyLocalAddress() || addr.isLoopbackAddress()) { return true; } // Note: The ideal way of doing this would be to return: // NetworkInterface.getByInetAddress(addr) != null; // However, that call crashes the JVM on a lot of machines. // So, we have a crappy & long-winded workaround... try { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while(interfaces.hasMoreElements()) { NetworkInterface ni = interfaces.nextElement(); Enumeration<InetAddress> addresses = ni.getInetAddresses(); while(addresses.hasMoreElements()) { InetAddress address = addresses.nextElement(); if(Arrays.equals(addr.getAddress(), address.getAddress())) { return true; } } } } catch(SocketException err) { return false; } return false; } /** * Returns true if the SocketAddress is any of our local machine addresses */ public static boolean isLocalAddress(SocketAddress addr) { InetSocketAddress iaddr = (InetSocketAddress)addr; return !iaddr.isUnresolved() && isLocalAddress(iaddr.getAddress()); } /** * Returns whether or not the two IP addresses share the same * first octet in their address. * <p> * This method is IPv6 compliant but returns always false if * any of the two addresses in an IPv6 address. * * @param addr0 the first address to compare * @param addr1 the second address to compare */ public static boolean isCloseIP(InetAddress addr0, InetAddress addr1) { return isCloseIP(addr0.getAddress(), addr1.getAddress()); } /** * Returns whether or not the two IP addresses share the same * first octet in their address. * <p> * This method is IPv6 compliant but returns always false if * any of the two addresses in an IPv6 address. * * @param addr0 the first address to compare * @param addr1 the second address to compare */ public static boolean isCloseIP(byte[] addr0, byte[] addr1) { if ((isIPv4Address(addr0) && isIPv4Address(addr1)) || (isIPv4MappedAddress(addr0) && isIPv4MappedAddress(addr1))) { return addr0[/* 0 */ addr0.length - 4] == addr1[/* 0 */ addr1.length - 4]; } return false; } /** * Returns whether or not the two IP addresses share the same * first two octets in their address -- the most common * indication that they may be on the same network. * <p> * Private networks are NOT CONSIDERED CLOSE. * <p> * This method is IPv6 compliant but returns always false if * any of the two addresses in a true IPv6 address. * * @param addr0 the first address to compare * @param addr1 the second address to compare */ public static boolean isVeryCloseIP(byte[] addr0, byte[] addr1) { if ((isIPv4Address(addr0) && isIPv4Address(addr1)) || (isIPv4MappedAddress(addr0) && isIPv4MappedAddress(addr1))) { return addr0[/* 0 */ addr0.length - 4] == addr1[/* 0 */ addr1.length - 4] && addr0[/* 1 */ addr0.length - 3] == addr1[/* 1 */ addr1.length - 3]; } return false; } /** * Utility method for determining whether or not the given * address is private taking an InetAddress object as argument * like the isLocalAddress(InetAddress) method. * <p> * This method is IPv6 compliant. * <p> * Don't make this method public please. * * @return <tt>true</tt> if the specified address is private, * otherwise <tt>false</tt> */ static boolean isPrivateAddress(InetAddress address) { if (address.isAnyLocalAddress() || address.isLoopbackAddress() || address.isLinkLocalAddress() || address.isSiteLocalAddress() || isUniqueLocalUnicastAddress(address) || isBroadcastAddress(address) || isInvalidAddress(address) || isDocumentationAddress(address)) { return true; } return false; } /** * Returns true if both addresses belong to the same site local network. * This method is IPV6 safe. */ public static boolean areInSameSiteLocalNetwork(InetAddress address1, InetAddress address2) { return areInSameSiteLocalNetwork(address1.getAddress(), address2.getAddress()); } /** * Returns true if both addresses belong to the same site local network. * This method is IPV6 safe. */ public static boolean areInSameSiteLocalNetwork(byte[] address1, byte[] address2) { if (address1.length != address2.length) { return false; } if (address1.length == 4) { if (address1[0] == 10) { return address2[0] == 10; } else if (address1[0] == (byte)172 && (address1[1] & 0xF0) == 16) { return address2[0] == (byte)172 && (address2[1] & 0xF0) == 16; } else if (address1[0] == (byte)192 && address1[1] == (byte)168) { return address2[0] == (byte)192 && address2[1] == (byte)168; } else { return false; } } else if (address1.length == 16) { try { InetAddress a1 = InetAddress.getByAddress(address1); InetAddress a2 = InetAddress.getByAddress(address2); // it looks like ipv6 only has one type of site local address, so // just check if both addresses are site local return a1.isSiteLocalAddress() && a2.isSiteLocalAddress(); } catch (UnknownHostException e) { // impossible since addresses are of correct length throw new RuntimeException(e); } } throw new IllegalArgumentException("addresses of illegal length: " + address1.length); } /** * Checks if the given address is a private address. * <p> * This method is IPv6 compliant * * @param address the address to check */ public static boolean isPrivateAddress(byte[] address) { if (isAnyLocalAddress(address) || isInvalidAddress(address) || isLoopbackAddress(address) || isLinkLocalAddress(address) || isSiteLocalAddress(address) || isUniqueLocalUnicastAddress(address) || isBroadcastAddress(address) || isDocumentationAddress(address)) { return true; } return false; } /** * Returns the IP (given in BIG-endian) format as standard * dotted-decimal, e.g., 192.168.0.1<p> * * @param ip the IP address in BIG-endian format * @return the IP address as a dotted-quad string */ public static final String ip2string(byte[] ip) { return ip2string(ip, 0); } /** * Returns the IP (given in BIG-endian) format of * buf[offset]...buf[offset+3] as standard dotted-decimal, e.g., * 192.168.0.1<p> * * @param ip the IP address to convert * @param offset the offset into the IP array to convert * @return the IP address as a dotted-quad string */ public static final String ip2string(byte[] ip, int offset) { // xxx.xxx.xxx.xxx => 15 chars StringBuilder sbuf = new StringBuilder(16); sbuf.append(ByteUtils.ubyte2int(ip[offset])); sbuf.append('.'); sbuf.append(ByteUtils.ubyte2int(ip[offset+1])); sbuf.append('.'); sbuf.append(ByteUtils.ubyte2int(ip[offset+2])); sbuf.append('.'); sbuf.append(ByteUtils.ubyte2int(ip[offset+3])); return sbuf.toString(); } /** * Determines if the given socket is from a local host. * <p> * This method is IPv6 compliant. */ public static boolean isLocalHost(Socket socket) { return isLocalAddress(socket.getInetAddress()); } /** * Packs a Collection of IpPorts into a byte array. */ public static byte[] packIpPorts(Collection<? extends IpPort> ipPorts) { byte[] data = new byte[ipPorts.size() * 6]; int offset = 0; for(IpPort next : ipPorts) { byte[] addr = next.getInetAddress().getAddress(); int port = next.getPort(); System.arraycopy(addr, 0, data, offset, 4); offset += 4; ByteUtils.short2leb((short)port, data, offset); offset += 2; } return data; } /** * Parses an ip:port byte-packed values. * * @return a collection of <tt>IpPort</tt> objects. * @throws InvalidDataException if an invalid IP is found or the size * is not divisible by six */ public static List<IpPort> unpackIps(byte [] data) throws InvalidDataException { return unpackIps(data, null); } /** * Parses an ip:port byte-packed values. * The decorator is consulted for each IpPort prior to inserted it into the list. * * @param data the packed IpPorts. * @param decorator A decorator that can optionally change the IpPort that is added into the returned list. * @return a collection of <tt>IpPort</tt> objects. * @throws InvalidDataException if an invalid IP is found or the size * is not divisible by six */ public static List<IpPort> unpackIps(byte [] data, Decorator<IpPort, ? extends IpPort> decorator) throws InvalidDataException { if (data.length % 6 != 0) throw new InvalidDataException("invalid size"); int size = data.length/6; List<IpPort> ret = new ArrayList<IpPort>(size); byte [] current = new byte[6]; for (int i=0;i<size;i++) { System.arraycopy(data,i*6,current,0,6); IpPort ipp = NetworkUtils.getIpPort(current, java.nio.ByteOrder.LITTLE_ENDIAN); if(decorator != null) { ipp = decorator.decorate(ipp); if(ipp == null) throw new InvalidDataException("decorator returned null"); } ret.add(ipp); } return Collections.unmodifiableList(ret); } /** * Filters unique IPs based on a Class C Netmask. */ public static <T extends IpPort> Collection<T> filterOnePerClassC(Collection<T> c) { return filterUnique(c, CLASS_C_NETMASK); } /** * Filters unique IPs based on a netmask. */ public static <T extends IpPort> Collection<T> filterUnique(Collection<T> c, int netmask) { ArrayList<T> ret = new ArrayList<T>(c.size()); Set<Integer> ips = new HashSet<Integer>(); for (T ip : c) { if (ips.add( getMaskedIP(ip.getInetAddress(), netmask) )) ret.add(ip); } ret.trimToSize(); return ret; } /** * Returns the Class C Network part of the given InetAddress. * See {@link #getMaskedIP(InetAddress, int)} for more info. */ public static int getClassC(InetAddress addr) { return getMaskedIP(addr, CLASS_C_NETMASK); } /** * Applies the netmask on the lower four bytes of the given * InetAddress and returns it as an Integer. * <p> * This method is IPv6 compliant but shouldn't be called if * the InetAddress is neither IPv4 compatible nor mapped! */ public static int getMaskedIP(InetAddress addr, int netmask) { byte[] address = addr.getAddress(); return ByteUtils.beb2int(address, /* 0 */ address.length - 4) & netmask; } /** * Converts integer <code>ip</code> into byte array. * <p> * This method is not IPv6 compliant. */ public static byte[] toByteAddress(int ip) { byte[] address = new byte[4]; ByteUtils.int2beb(ip, address, 0); return address; } /** * @param decMask a netmask in decimal, like /24 * @return an integer that can be and-ed for masking */ public static int getHexMask(int decMask) { if (decMask < 0 || decMask > 32) throw new IllegalArgumentException("bad mask "+decMask); if (decMask == 0) return 0; return 0xFFFFFFFF << (32 - decMask); } /** * @return A non-loopback IPv4 address of a network interface on the local * host. * @throws UnknownHostException */ public static InetAddress getLocalAddress() throws UnknownHostException { InetAddress addr = InetAddress.getLocalHost(); if (addr instanceof Inet4Address && !addr.isLoopbackAddress()) { return addr; } try { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); if (interfaces != null) { while (interfaces.hasMoreElements()) { Enumeration<InetAddress> addresses = interfaces.nextElement().getInetAddresses(); while (addresses.hasMoreElements()) { addr = addresses.nextElement(); if (addr instanceof Inet4Address && !addr.isLoopbackAddress()) { return addr; } } } } } catch (SocketException se) { } throw new UnknownHostException( "localhost has no interface with a non-loopback IPv4 address"); } /** * Returns the IP:Port as byte array. * <p> * This method is IPv6 compliant */ public static byte[] getBytes(SocketAddress addr, java.nio.ByteOrder order) throws UnknownHostException { InetSocketAddress iaddr = (InetSocketAddress)addr; if (iaddr.isUnresolved()) { throw new UnknownHostException(iaddr.toString()); } return getBytes(iaddr.getAddress(), iaddr.getPort(), order); } /** * Returns the IP:Port as byte array. * <p> * This method is IPv6 compliant */ public static byte[] getBytes(IpPort ipp, java.nio.ByteOrder order) { return getBytes(ipp.getInetAddress(), ipp.getPort(), order); } /** * Returns the IP:Port as byte array. * <p> * This method is IPv6 compliant */ public static byte[] getBytes(InetAddress addr, int port, java.nio.ByteOrder order) { if (!isValidPort(port)) throw new IllegalArgumentException("Port out of range: " + port); if(!isValidAddress(addr)) throw new IllegalArgumentException("invalid addr: " + addr); byte[] address = addr.getAddress(); byte[] dst = new byte[address.length + 2]; System.arraycopy(address, 0, dst, 0, address.length); if(order == java.nio.ByteOrder.BIG_ENDIAN) ByteUtils.short2beb((short)port, dst, dst.length-2); else // if order == LITTLE_ENDIAN ByteUtils.short2leb((short)port, dst, dst.length-2); return dst; } /** * Creates an IpPort out of network data. The ByteOrder is used to determine * the order of the port. Throws <code>InvalidDataException</code> if the * data is invalid. * <p> * This method is IPv6 compliant. */ public static IpPort getIpPort(byte[] ipport, java.nio.ByteOrder order) throws InvalidDataException { if(ipport.length < 6) throw new InvalidDataException("length must be >= 6, is: " + ipport.length); short shortport; if(order == java.nio.ByteOrder.BIG_ENDIAN) shortport = ByteUtils.beb2short(ipport, ipport.length - 2); else // if order == LITTLE_ENDIAN shortport = ByteUtils.leb2short(ipport, ipport.length - 2); int port = ByteUtils.ushort2int(shortport); if (!NetworkUtils.isValidPort(port)) throw new InvalidDataException("Bad Port: " + port); // Optimized for IPv4 as far as memory goes.. if(ipport.length == 6) { IP ip = new IP(ipport, 0); if(!NetworkUtils.isValidAddress(ip)) { throw new InvalidDataException("invalid addr: " + ip); } return new MemoryOptimizedIpPortImpl(ip, shortport); } else { // any other length, go w/ IPv6. InetAddress host; try { byte[] ip = new byte[ipport.length - 2]; System.arraycopy(ipport, 0, ip, 0, ip.length); host = InetAddress.getByAddress(ip); } catch(UnknownHostException uhe) { throw new InvalidDataException(uhe); } if(!NetworkUtils.isValidAddress(host)) { throw new InvalidDataException("invalid addr: " + host); } return new IpPortImpl(host, port); } } /** * Parses IP port in the same fashion as {@link #getIpPort(byte[], java.nio.ByteOrder)}. * <p> * Creates Connectable using <code>tlsCapable</code>. */ public static Connectable getConnectable(byte[] ipport, java.nio.ByteOrder order, boolean tlsCapable) throws InvalidDataException { return new ConnectableImpl(getIpPort(ipport, order), tlsCapable); } /** * Returns true if both SocketAddresses are either IPv4 or IPv6 addresses * <p> * This method is IPv6 compliant */ public static boolean isSameAddressSpace(SocketAddress a, SocketAddress b) { return isSameAddressSpace( ((InetSocketAddress)a).getAddress(), ((InetSocketAddress)b).getAddress()); } /** * Returns true if both InetAddresses are compatible (IPv4 and IPv4 * or IPv6 and IPv6). * <p> * This method is IPv6 compliant */ public static boolean isSameAddressSpace(InetAddress a, InetAddress b) { if (a == null || b == null) { return false; } // Both are either IPv4 or IPv6 if ((a instanceof Inet4Address && b instanceof Inet4Address) || (a instanceof Inet6Address && b instanceof Inet6Address)) { return true; } return false; } /** * Returns the IPv6 address bytes of IPv6 and IPv4 IP addresses. * @throws IllegalArgumentException if given a different address type */ public static byte[] getIPv6AddressBytes(InetAddress address) { byte[] bytes = address.getAddress(); switch (bytes.length) { case 16: // Return the IPv6 address return bytes; case 4: // Turn the IPv4 address into a IPv4 mapped IPv6 // address byte[] result = new byte[16]; result[10] = (byte) 0xff; result[11] = (byte) 0xff; System.arraycopy(bytes, 0, result, 12, bytes.length); return result; default: throw new IllegalArgumentException("unhandled address length"); } } /** * Returns true if an IPv6 representation of <code>address</code> exists. */ public static boolean isIPv6Compatible(InetAddress address) { int length = address.getAddress().length; return length == 4 || length == 16; } /** * Returns true if the given byte-array is an IPv4 address */ private static boolean isIPv4Address(byte[] address) { return address.length == 4; } /** * Returns true if the given byte-array is an IPv4 compatible address. * They're used when IPv6 systems need to communicate with each other, * but are separated by an IPv4 network. */ static boolean isIPv4CompatibleAddress(byte[] address) { // Is it a IPv4 compatible IPv6 address? // (copied from Inet6Address) if (address.length == 16 && (address[ 0] == 0x00) && (address[ 1] == 0x00) && (address[ 2] == 0x00) && (address[ 3] == 0x00) && (address[ 4] == 0x00) && (address[ 5] == 0x00) && (address[ 6] == 0x00) && (address[ 7] == 0x00) && (address[ 8] == 0x00) && (address[ 9] == 0x00) && (address[10] == 0x00) && (address[11] == 0x00)) { return true; } return false; } /** * Returns true if the given byte-array is an IPv4 mapped address. * IPv4 mapped addresses indicate systems that do not support IPv6. * They are limited to IPv4. An IPv6 host can communicate with an * IPv4 only host using the IPv4 mapped IPv6 address. */ static boolean isIPv4MappedAddress(byte[] address) { if (address.length == 16 && (address[ 0] == 0x00) && (address[ 1] == 0x00) && (address[ 2] == 0x00) && (address[ 3] == 0x00) && (address[ 4] == 0x00) && (address[ 5] == 0x00) && (address[ 6] == 0x00) && (address[ 7] == 0x00) && (address[ 8] == 0x00) && (address[ 9] == 0x00) && (address[10] == (byte)0xFF) && (address[11] == (byte)0xFF)) { return true; } return false; } /** * Returns true if it's an IPv4 InetAddress and the first octed is 0x00. */ private static boolean isInvalidAddress(InetAddress address) { return isInvalidAddress(address.getAddress()); } /** * Returns true if it's an IPv4 InetAddress and the first octet is 0x00. */ private static boolean isInvalidAddress(byte[] address) { if (isIPv4Address(address) || isIPv4MappedAddress(address)) { return address[/* 0 */ address.length - 4] == 0x00; } return false; } /** * Returns true if the given byte-array is an any local address. */ static boolean isAnyLocalAddress(byte[] address) { if (address.length == 4 || address.length == 16) { byte test = 0; for (int i = 0; i < address.length; i++) { test |= address[i]; } return (test == 0x00); } return false; } /** * Returns true if the given byte-array is a loopback address. */ static boolean isLoopbackAddress(byte[] address) { if (isIPv4Address(address) || isIPv4MappedAddress(address)) { return (address[/* 0 */ address.length - 4] & 0xFF) == 127; } else if (address.length == 16) { byte test = 0x00; for (int i = 0; i < 15; i++) { test |= address[i]; } return (test == 0x00) && (address[15] == 0x01); } return false; } /** * Returns true if the given byte-array is a link-local address. */ static boolean isLinkLocalAddress(byte[] address) { if (isIPv4Address(address) || isIPv4MappedAddress(address)) { return (address[/* 0 */ address.length - 4] & 0xFF) == 169 && (address[/* 1 */ address.length - 3] & 0xFF) == 254; // FE80::/64 } else if (address.length == 16) { return (address[0] & 0xFF) == 0xFE && (address[1] & 0xC0) == 0x80; } return false; } /** * Returns true if the given byte-array is a site-local address. * IPv6 site-local addresses were deprecated in September 2004 * by RFC 3879 and replaced by RFC 4193 (Unique Local IPv6 Unicast * Addresses). */ static boolean isSiteLocalAddress(byte[] address) { if (isIPv4Address(address) || isIPv4MappedAddress(address)) { return (address[/* 0 */ address.length - 4] & 0xFF) == 10 || ((address[/* 0 */ address.length - 4] & 0xFF) == 172 && (address[/* 1 */ address.length - 3] & 0xF0) == 16) || ((address[/* 0 */ address.length - 4] & 0xFF) == 192 && (address[/* 1 */ address.length - 3] & 0xFF) == 168); // Has been deprecated in September 2004 by RFC 3879 // FEC0::/10 } else if (address.length == 16) { return (address[0] & 0xFF) == 0xFE && (address[1] & 0xC0) == 0xC0; } return false; } /** * Returns true if the given InetAddress is an Unique Local IPv6 * Unicast Address. See RFC 4193 for more info. */ public static boolean isUniqueLocalUnicastAddress(InetAddress address) { if (address instanceof Inet6Address) { return isUniqueLocalUnicastAddress(address.getAddress()); } return false; } /** * Returns true if the given byte-array is an Unique Local IPv6 * Unicast Address. See RFC 4193 for more info. */ private static boolean isUniqueLocalUnicastAddress(byte[] address) { // FC00::/7 if (address.length == 16) { return (address[0] & 0xFE) == 0xFC; } return false; } /** * Returns true if the given InetAddress is a broadcast address. */ public static boolean isBroadcastAddress(InetAddress address) { return isBroadcastAddress(address.getAddress()); } /** * Returns true if the given byte-array is a broadcast address * <p> * This method is IPv6 compliant but returns always false if * the given address is neither a true IPv4, nor an IPv4-mapped * address. */ private static boolean isBroadcastAddress(byte[] address) { if (isIPv4Address(address) || isIPv4MappedAddress(address)) { return (address[/* 0 */ address.length - 4] & 0xFF) == 0xFF; } return false; } /** * Returns true if the given InetAddress is a private IPv4-compatible * address. * <p> * It checks for a somewhat tricky and undefined case. An address such * as ::0000:192.168.0.1 is an IPv6 address, it's an IPv4-compatible * address but it's by IPv6 definition not a site-local (private) address. * On the other hand it's a private IPv4 address. */ public static boolean isPrivateIPv4CompatibleAddress(InetAddress address) { if (address instanceof Inet6Address) { return isPrivateIPv4CompatibleAddress(address.getAddress()); } return false; } /** * Returns true if the given byte-array is a private IPv4-compatible * address. */ private static boolean isPrivateIPv4CompatibleAddress(byte[] address) { if (isIPv4CompatibleAddress(address)) { // Copy the lower four bytes and perform the // checks on it to determinate whether or not // it's a private IPv4 address byte[] ipv4 = new byte[4]; System.arraycopy(address, 12, ipv4, 0, ipv4.length); return isPrivateAddress(ipv4); } return false; } /** * Returns true if the given InetAddress has a prefix that's used in * Documentation. It's a non-routeable IPv6 address. See <a href= * "http://www.ietf.org/rfc/rfc3849.txt">RFC 3849</a> * for more information. */ public static boolean isDocumentationAddress(InetAddress address) { if (address instanceof Inet6Address) { return isDocumentationAddress(address.getAddress()); } return false; } /** * Returns true if the given byte-array has a prefix that's used in * Documentation. It's a non-routeable IPv6 address. See <a href= * "http://www.ietf.org/rfc/rfc3849.txt">RFC 3849</a> * for more information. */ private static boolean isDocumentationAddress(byte[] address) { // 2001:0DB8::/32 if (address.length == 16) { return (address[0] & 0xFF) == 0x20 && (address[1] & 0xFF) == 0x01 && (address[2] & 0xFF) == 0x0D && (address[3] & 0xFF) == 0xB8; } return false; } /** * Parses port from string and checks if it's valid. * * @throws IOException if the port could not be parsed or is invalid */ static int parsePort(String portString) throws IOException { try { int port = Integer.parseInt(portString); if(!isValidPort(port)) { throw new IOException("invalid port: " + port); } return port; } catch(NumberFormatException invalid) { throw (IOException)new IOException().initCause(invalid); } } /** * Gets {@link InetAddress} and checks if it's valid. * @throws IOException if the address is not valid address */ static InetAddress getAndCheckAddress(String addressString) throws IOException { InetAddress address = InetAddress.getByName(addressString); if (!isValidAddress(address)) throw new IOException("invalid addr: " + address); return address; } /** * Returns the index of the ':' separator between ip and port and checks * if it's at a valid position. * @throws IOException if separator is not found or not at a valid position */ static int getAndCheckIpPortSeparator(String ipPort) throws IOException { int separator = ipPort.indexOf(":"); //see if this is a valid ip:port address; if (separator <= 0 || separator!= ipPort.lastIndexOf(":") || separator == ipPort.length() - 1) throw new IOException("invalid separator in http: " + ipPort); return separator; } /** * Returns a Connectable of the ipPort as described by "a.b.c.d:port". * * @param ipPort a string representing an IP and port * @throws IOException parsing failed. */ public static Connectable parseIpPort(String ipPort, boolean tlsCapable) throws IOException { int separator = getAndCheckIpPortSeparator(ipPort); InetAddress address = getAndCheckAddress(ipPort.substring(0, separator)); int port = parsePort(ipPort.substring(separator+1)); return new ConnectableImpl(new InetSocketAddress(address, port), tlsCapable); } /** * @param http a string representing a port and an ip * @return an object implementing IpPort * @throws IOException parsing failed. */ public static IpPort parsePortIp(String http) throws IOException{ int separator = getAndCheckIpPortSeparator(http); int port = parsePort(http.substring(0, separator)); InetAddress address = getAndCheckAddress(http.substring(separator+1)); return new IpPortImpl(address, port); } }