Skip to content

Instantly share code, notes, and snippets.

@tbl3rd
Created April 4, 2015 14:00
Show Gist options
  • Save tbl3rd/a1411fc1ec32c5d5ff05 to your computer and use it in GitHub Desktop.
Save tbl3rd/a1411fc1ec32c5d5ff05 to your computer and use it in GitHub Desktop.
manage private LAN addresses
package tbl3rd.provider;
import tbl3rd.provider.Api_2015_04_01;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* An IPv4 Classless Inter-Domain Routing subnet address block.
*
* Bad CIDR notation will likely throw an UnknownHostException.
*/
class Inet4CidrBlock
{
/**
* The number of bits in the CIDR subnet prefix. The number of
* address bits used to identify the network. The number of bits
* available for host addresses is (32 - this.prefix).
*/
final int prefix;
/**
* The CIDR block's net mask, obtained by setting all the
* (this.prefix) network bits to 1 and all the (remaining) host
* bits to 0.
*/
final Inet4Address netmask;
/**
* The CIDR block's network identifier. This is masked by the
* block's netmask, so may not be the same address passed to the
* constructor.
*/
final Inet4Address network;
/**
* The CIDR block's broadcast address, obtained by bitwise ORing
* this.network with the (1's) complement of this.netmask.
*/
final Inet4Address broadcast;
private static final String addressRe = "^\\d+\\.\\d+\\.\\d+\\.\\d+$";
private static final Pattern addressPattern = Pattern.compile(addressRe);
/**
* Return true if s is an IP address. Otherwise return false.
*/
static boolean isIp(String s)
{
final Matcher m = addressPattern.matcher(s);
return m.matches();
}
/**
* Return the MAC address for the local interface named ifName.
*/
static String getHardwareAddress(String ifName)
{
try {
final Enumeration<NetworkInterface> interfaces =
NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
final NetworkInterface nextIf = interfaces.nextElement();
final boolean ifOk = ifName.equals(nextIf.getDisplayName());
if (ifOk) {
final byte[] b = nextIf.getHardwareAddress();
if (b.length == 6) {
return String.format(
"%02x:%02x:%02x:%02x:%02x:%02x",
b[0], b[1], b[2], b[3], b[4], b[5]);
}
}
}
} catch (IOException x) {
throw new AssertionError(x);
}
throw new AssertionError("No MAC address for interface " + ifName);
}
/**
* Return a routable IPv4 address for the caller.
*/
static Inet4Address getRoutableLocalIp()
{
try {
final Enumeration<NetworkInterface> interfaces =
NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
final NetworkInterface nextIf = interfaces.nextElement();
final boolean nextIfOk =
nextIf.isUp() &&
!nextIf.isLoopback() &&
!nextIf.isVirtual();
if (nextIfOk) {
final Enumeration<InetAddress> as =
nextIf.getInetAddresses();
while (as.hasMoreElements()) {
final InetAddress nextAddress = as.nextElement();
if (nextAddress.isLoopbackAddress()) continue;
if (nextAddress instanceof Inet4Address) {
return (Inet4Address)nextAddress;
}
}
}
}
} catch (IOException x) {
throw new AssertionError(x);
}
throw new AssertionError("No routable IPv4 address for local host");
}
/**
* Return the integer representation of a in native byte-order.
*/
static int toInt(Inet4Address a)
{
return ByteBuffer.wrap(a.getAddress()).getInt();
}
/**
* Return the address that i represents, where:
* (a == Inet4CidrBlock.fromInt(Inet4CidrBlock.toInt(a)))
* is true.
*/
static Inet4Address fromInt(int i)
{
try {
final byte[] bytes = ByteBuffer.allocate(4).putInt(i).array();
return (Inet4Address)Inet4Address.getByAddress(bytes);
} catch (UnknownHostException x) {
throw new AssertionError(x);
}
}
/**
* Throw a bad address syntax error.
*/
private static void throwBadAddress(String a, Exception x)
throws ApiException
{
final String message = (x == null)?
String.format("%s is a bad address", a):
String.format("%s is a bad address: %s", a, x);
throw new ApiException(
Api_2015_04_01.ResultCode.BAD_REQUEST,
message);
}
/**
* Return the address named by host or throw.
*/
static Inet4Address getByName(String host)
throws ApiException
{
try {
return (Inet4Address)Inet4Address.getByName(host);
} catch (UnknownHostException x) {
throwBadAddress(host, x);
}
return null;
}
/**
* Return the addresses described by the addressPool strings.
*/
static ArrayList<Inet4Address> parseAddressPool(List<String> addressPool)
throws ApiException
{
final HashSet<Integer> addresses = new HashSet<Integer>();
if (addressPool != null) {
for (String s : addressPool) {
final String[] parts = s.split("\\s*-\\s*");
if (parts.length == 2) {
final int first = toInt(getByName(parts[0]));
final int last = toInt(getByName(parts[1]));
for (int n = first; n <= last; ++n) addresses.add(n);
} else if (parts.length == 1) {
addresses.add(toInt(getByName(parts[0])));
} else {
throwBadAddress(s, null);
}
}
}
final Integer[] integers = addresses.toArray(new Integer[0]);
Arrays.sort(integers);
final ArrayList<Inet4Address> result = new ArrayList<Inet4Address>();
for (Integer i : integers) result.add(fromInt(i.intValue()));
return result;
}
/**
* Return the number of host addresses available on this subnet,
* excluding the network identifier and broadcast addresses.
*
* Return long because result exceeds INT_MAX when prefix is 0.
*/
final long hostCount()
{
final long count = (1L << (32 - this.prefix)) - 2;
return Math.max(0, count);
}
/**
* Return the host addresses available on this CIDR subnet.
*
* The resulting list excludes the network identifer (host bits
* all 0) and broadcast (host bits all 1) addresses. This can't
* return an Inet4Address[] because hostCount() is long and Java
* can't index an array with a long.
*/
final ArrayList<Inet4Address> getHosts()
throws UnknownHostException
{
final ArrayList<Inet4Address> result = new ArrayList<Inet4Address>();
final int begin = Inet4CidrBlock.toInt(network);
final int end = Inet4CidrBlock.toInt(broadcast);
for (int h = begin + 1; h < end; ++h) {
result.add(Inet4CidrBlock.fromInt(h));
}
return result;
}
/**
* The canonicalized string representation of this CIDR block.
* The result may not be the string passed to the constructor.
*/
@Override
public final String toString()
{
return this.network.getHostAddress() + "/" + this.prefix;
}
/**
* Interpret an IPv4 CIDR subnet block such as "192.168.5.192/26"
* where "192.168.5.192" is the "network identifier" and "/26" is
* the "prefix" or number of bits used to identify the network.
*/
Inet4CidrBlock(String subnet)
throws ApiException
{
final String[] part = subnet.split("/");
final String addressString = part[0];
this.prefix = (part.length == 1)? 0: (Integer.parseInt(part[1]));
final int shiftBits = 32 - this.prefix;
final int maskInt = (shiftBits < 32)? (-1 << shiftBits): 0;
final Inet4Address networkAddress = getByName(part[0]);
final int networkInt = maskInt & Inet4CidrBlock.toInt(networkAddress);
final int broadcastInt = ~maskInt | networkInt;
this.netmask = Inet4CidrBlock.fromInt(maskInt);
this.network = Inet4CidrBlock.fromInt(networkInt);
this.broadcast = Inet4CidrBlock.fromInt(broadcastInt);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment