Created
April 4, 2015 14:00
-
-
Save tbl3rd/a1411fc1ec32c5d5ff05 to your computer and use it in GitHub Desktop.
manage private LAN addresses
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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