package com.netifera.platform.util.addresses.inet; import java.io.Serializable; import java.util.Iterator; import com.netifera.platform.api.iterables.IndexedIterable; import com.netifera.platform.api.iterables.SequentialIterator; import com.netifera.platform.util.addresses.AddressFormatException; import com.netifera.platform.util.addresses.INetblock; /** * CIDR blocks * * @see com.netifera.platform.util.addresses.inet.IPv4Netblock * @see com.netifera.platform.util.addresses.inet.IPv6Netblock */ public abstract class InternetNetblock implements INetblock<InternetAddress>, Comparable<InternetNetblock>, IndexedIterable<InternetAddress>, Serializable { private static final long serialVersionUID = 8850929139528536587L; protected final InternetAddress network; /** * The prefix length, the number of shared initial bits, counting from the * left-hand side of the address. */ protected final int maskBitCount; /** * The 'base' address of that network block. */ public abstract InternetAddress getNetworkAddress(); /** * A broadcast address is an IP address that allows information to be sent * to all machines on a given subnet rather than a specific machine. */ public abstract InternetAddress getBroadcastAddress(); /** * A subnet mask is a bitmask that encodes the prefix length in a form * similar to an IP address */ public abstract InternetAddress getNetmaskAddress(); protected InternetNetblock(int size, byte[] bytes, int maskBitCount) { if (size != bytes.length) { throw new AddressFormatException("Bad address size: " + size); } InternetAddress addr = InternetAddress.fromBytes(bytes); if (!addr.isValidMaskBit(maskBitCount)) { throw new IllegalArgumentException("Invalid maskBit: " + maskBitCount); } this.maskBitCount = maskBitCount; this.network = netblockStartAddress(addr, maskBitCount); } protected InternetNetblock(InternetAddress network, int maskBitCount) { if (!network.isValidMaskBit(maskBitCount)) { throw new IllegalArgumentException("Invalid maskBit: " + maskBitCount); } this.maskBitCount = maskBitCount; this.network = netblockStartAddress(network, maskBitCount); } protected abstract InternetAddress netblockStartAddress( InternetAddress network, int maskBitCount); /** * Creates a new instance of this object. * * @return A new InternetAddress instance */ public InternetNetblock newInstance() { return network.createNetblock(maskBitCount); } /** * The number of shared initial bits, counting from the left-hand side of * the address. * * @return the network Mask (in bits) of this network block */ public int getCIDR() { return maskBitCount; } /** * @param address An IP address * @param maskBitCount bit mask * * @exception IllegalArgumentException if the prefix is invalid. */ public static InternetNetblock fromAddress(InternetAddress address, int maskBitCount) { return address.createNetblock(maskBitCount); } /** * @param netblock An network block in CIDR notation (address '/' mask) * * @exception IllegalArgumentException * @exception AddressFormatException */ public static InternetNetblock fromString(String netblock) { String[] arg = netblock.split("/"); // TODO '-' separator if (arg.length != 2) { throw new IllegalArgumentException( "Invalid Netblock (Missing '/' separator)"); } // TODO short notation: 193/8, 193.165.64/19 try { return fromString(arg[0], Integer.parseInt(arg[1])); } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid Netblock (wrong CIDR)", e); } } /** * @param address An IP address * @param maskBitCount bit mask * * @exception IllegalArgumentException if the prefix is invalid. * @exception AddressFormatException */ public static InternetNetblock fromString(String address, int maskBitCount) { return InternetAddress.fromString(address).createNetblock(maskBitCount); } /** * @param addr IP address in network byte order * @param maskBitCount bit mask * * @exception IllegalArgumentException if the prefix is invalid. * @exception AddressFormatException if the array length is not valid */ public static InternetNetblock fromData(byte[] addr, int maskBitCount) { return InternetAddress.fromBytes(addr).createNetblock(maskBitCount); } public static InternetNetblock fromRange(String fromIP, String toIP) { InternetAddress from = InternetAddress.fromString(fromIP); InternetAddress to = InternetAddress.fromString(toIP); if (from.getNetworkFamily().compareTo(to.getNetworkFamily()) != 0) { // FIXME encapsulated? throw new IllegalArgumentException("Different families: from/to"); } if (from instanceof IPv6Address) { // TODO throw new UnsupportedOperationException("IPv6 from-to netblock"); } int maskBitCount = IPv4Netblock.getMaskBits(((IPv4Address)to) .addressData - ((IPv4Address)from).addressData); return fromAddress(from, maskBitCount); } /** * @param arpa network address ARPA notation. * * @see InternetAddress#fromARPA(String) */ public static InternetNetblock fromARPA(String arpa) { String[] members = arpa.split("\\."); if (members.length < 3) { throw new AddressFormatException("Bad address format: " + arpa); } StringBuffer sb = new StringBuffer(IPv6Address.MAX_TEXTUAL_LENGTH); int maskBits; if (arpa.endsWith(".in-addr.arpa.")) { // TODO handles RFC2317 (Classless delegation) ? for (int i = members.length - 3, j = 0; j < 4; i--, j++) { if (i >= 0) { sb.append(members[i]); } else { sb.append('0'); } if (j + 1 != 4) { sb.append('.'); } } maskBits = (members.length - 2) * 8; } else if (arpa.endsWith(".ip6.arpa.")) { for (int i = members.length - 3, j = 0; j < 32; i--, j++) { if (i >= 0) { sb.append(members[i]); } else { sb.append('0'); } if (j % 4 == 3 && j + 1 != 32) { sb.append(':'); } } maskBits = (members.length - 2) * 4; } else { throw new AddressFormatException("Bad address format: " + arpa); } return fromString(sb.toString(), maskBits); } @Override public String toString() { return network.toString() + "/" + maskBitCount; } @Override public int hashCode() { return network == null ? 0 : network.hashCode() >>> maskBitCount; } @Override public boolean equals(final Object obj) { if(!(obj instanceof InternetNetblock)){ return false; } InternetNetblock other = (InternetNetblock) obj; if (maskBitCount != other.maskBitCount){ return false; } return network.equals(other.network); } public abstract boolean isLoopback(); // TODO //public abstract boolean isLocal(); //public abstract boolean isPrivate(); //public abstract boolean isMulticast(); public IndexedIterable<InternetAddress> getIndexedIterable() { if (!isIndexedIterable()) { return null; } return this; } public Iterator<InternetAddress> iterator() { return new SequentialIterator<InternetAddress>(this); } }