Skip to content

Code Examples 1: Creating, Parsing, Extracting Address Subnet Info

Sean C Foley edited this page May 27, 2023 · 12 revisions

Parse IP Address or Subnet String

Parses representations of IPv6 and IPv4 addresses and subnets. For a list of supported formats, see javadoc for IPAddressString or the docs and docs examples

Java

You can either validate using exceptions:

try {
	IPAddressString str = new IPAddressString("::1");
	IPAddress addr = str.toAddress();
	// use address
} catch(AddressStringException e) {
	// e.getMessage has validation error
}

... or avoid exceptions, instead checking for null:

IPAddressString str = new IPAddressString("a:b:c:d:e-f:f:1.2-3.3.4/64");
IPAddress addr = str.getAddress();
if(addr != null) {
	// use address
}

Kotlin

Starting with address or subnet strings, use exceptions to handle formats:

try {
	val ipv6AddressStr = IPAddressString("a:b:c:d::a:b/64")
	val ipv6Addr = ipv6AddressStr.toAddress()
	// use address
} catch(e: AddressStringException) {
	// e.message has validation error
}

...or use nullable types and safe calls to handle invalid or unexpected formats:

val ipv6v4Str = "a:b:c:d:e:f:1.2.3.4/112"
val ipv6v4AddressStr = IPAddressString(ipv6v4Str)
val ipAddr: IPAddress? = ipv6v4AddressStr.address

Parse IP Address String with Prefix Length as Subnet or IP Address

In the IPAddress library, an IPAddress instance can be either an address or a subnet, which differs from most other libraries, which typically use separate types. Using a single type simplifies code.

By default, CIDR IP addresses with a host of zero (or IPAddress ranges whose boundaries have hosts of zero) are treated as subnets, while CIDR IP addresses with non-zero hosts are treated as single addresses. The bits beyond the prefix (as determined by the prefix length) form the host. For example:

parse("1.2.0.0/16"); // this is a subnet of 65536 addresses, the 0.0 host makes it a network
parse("1.2.3.4/16"); // this is a single address, since the host is 3.4

static void parse(String addressStr) {
	IPAddress addr = new IPAddressString(addressStr).getAddress();
	System.out.println("count of "+  addressStr + " is " + addr.getCount());
}

Output:

count of 1.2.0.0/16 is 65536
count of 1.2.3.4/16 is 1

This convention follows the same convention in common usage by network engineers.

With this convention, the address 1.2.0.0/16, with a zero host, often called the network address, is the entire network of addresses with the same 16-bit prefix 1.2. Meanwhile, the address 1.2.3.4/16, with a non-zero host, is the single address 1.2.3.4 in that same network. Sometimes it is called an interface address.

The same convention applies to IPv6 in this library. In fact, with IPv6, the address with a zero host is specified to be the subnet router anycast address and is generally not intended to be used for a host, but to be used to communicate with a router for the given subnet.

If you wish to see the network and host bits, use toBinaryString().

To get just the single address

Use getLower()

parseAddress("1.2.0.0/16");
parseAddress("1.2.3.4/16");
		
static void parseAddress(String addressStr) {
	IPAddress addr = new IPAddressString(addressStr).getAddress().getLower();
	System.out.println("count of "+  addr + " is " + addr.getCount());
}

Output:

count of 1.2.0.0/16 is 1
count of 1.2.3.4/16 is 1

Or use toZeroHost()

parseAddressHost("1.2.0.0/16");
parseAddressHost("1.2.3.4/16");
		
static void parseAddressHost(String addressStr) {
	IPAddress addr = new IPAddressString(addressStr).getAddress();
	if(addr.isSinglePrefixBlock()) {
		addr = addr.toZeroHost();
		System.out.print("zero host ");
	}
	System.out.println("count of "+  addr + " is " + addr.getCount());
}

Output:

zero host count of 1.2.0.0/16 is 1
count of 1.2.3.4/16 is 1

Or parse separately from the prefix length using getHostAddress() or toHostAddress():

parseHostAddress("1.2.0.0/16");
parseHostAddress("1.2.3.4/16");
		
static void parseHostAddress(String addressStr) {
	IPAddressString addrStr = new IPAddressString(addressStr);
	IPAddress addr = addrStr.getHostAddress(); // ignores prefix length
	addr = addr.setPrefixLength(addrStr.getNetworkPrefixLength(), false, false); 
	System.out.println("count of "+  addr + " is " + addr.getCount());
}

Output:

count of 1.2.0.0/16 is 1
count of 1.2.3.4/16 is 1

To get just the prefix-block subnet

Use toPrefixBlock()

parseSubnet("1.2.0.0/16");
parseSubnet("1.2.3.4/16");

static void parseSubnet(String str) {
	IPAddress addr = new IPAddressString(str).getAddress().toPrefixBlock();
	System.out.println("count of "+  addr + " is " + addr.getCount());
}

Output:

count of 1.2.0.0/16 is 65536
count of 1.2.0.0/16 is 65536

The code above works the same way for IPv6 address strings with prefix lengths.

Check if String is an IP Address or Host Name

Unlike java.net.InetAddress.getByName(), parsing an invalid address or host will not trigger a DNS lookup.

check("1.2.3.4");
check("1.2.a.4");
check("::1");
check("[::1]");
check("1.2.?.4"); 

static void check(String hostStr) {
	HostName host = new HostName(hostStr);
	try {
		host.validate();
		if(host.isAddress()) {
			System.out.println("address: " + host.asAddress());
		} else {
			System.out.println("host name: " + host);
		}
	} catch(HostNameException e) {
		System.out.println(e.getMessage());
	}
}

Output:

address: 1.2.3.4
host name: 1.2.a.4
address: ::1
address: ::1
1.2.?.4 Host error: invalid character at index 4

Parse Socket Address

A socket address, or TCP address, consists of address and port, or possibly a host name and port. Use HostName to parse.

String hostName = "[a:b:c:d:e:f:a:b]:8080";
String hostName2 = "1.2.3.4:8080";
String hostName3 = "a.com:8080";
try {
	HostName host = new HostName(hostName);
	host.validate(); // to throw parse exception, rather than check for null
	InetSocketAddress address = host.asInetSocketAddress();
	HostName host2 = new HostName(hostName2);
	host2.validate();
	InetSocketAddress address2 = host2.asInetSocketAddress();
	HostName host3 = new HostName(hostName3);
	host3.validate();
	InetSocketAddress address3 = host3.asInetSocketAddress();
	// use socket addresses
} catch (HostNameException e) {
	String msg = e.getMessage();
	// handle improperly formatted host name or address string
}

Convert to/from bytes, int, BigInteger or InetAddress

IP-version polymorphic to byte[] and back

IPAddress loopback = new IPAddressString("::1").getAddress();
byte bytes[] = loopback.getBytes(); 
System.out.println(Arrays.toString(bytes));
IPAddressGenerator generator = new IPAddressGenerator();
IPAddress backAgain = generator.from(bytes);
System.out.println(backAgain);

Output:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
::1

IP-version polymorphic to InetAddress and back

IPAddress loopback = new IPAddressString("::1").getAddress();
InetAddress inetAddress = loopback.toInetAddress();
System.out.println(inetAddress);
IPAddressGenerator generator = new IPAddressGenerator();
IPAddress andBackAgain = generator.from(inetAddress);
System.out.println(andBackAgain);

Output:

/0:0:0:0:0:0:0:1
::1

IPv6 to BigInteger and back

IPAddress loopbackv6 = new IPAddressString("::1").getAddress();
BigInteger bigVal = loopbackv6.getValue();
System.out.println(bigVal);
IPv6Address backAgainv6 = new IPv6Address(bigVal);
System.out.println(backAgainv6);

Output:

1
::1

IPv4 to int and back

IPv4Address loopbackv4 = new IPAddressString("127.0.0.1").
	getAddress().toIPv4();
int val = loopbackv4.intValue();
System.out.println(val);
IPv4Address backAgainv4 = new IPv4Address(val);
System.out.println(backAgainv4);

Output:

2130706433
127.0.0.1

IPv4/v6 to BigInteger and back

IPAddress loopback = new IPAddressString("::1").getAddress();
BigInteger value = loopback.getValue();
System.out.println(value);
IPAddress andAgain = loopback.isIPv4() ?
	new IPv4Address(value.intValue()) : new IPv6Address(value);
System.out.println(andAgain);

Output:

1
::1

IP-version polymorphic to BigInteger and back

static byte[] extendArray(byte bytes[], int len) {
	if(bytes.length < len) { 
		byte newBytes[] = new byte[len];
		System.arraycopy(bytes, 0, newBytes, 
			newBytes.length - bytes.length, bytes.length);
		return newBytes;
	}
	return bytes;
}

IPAddress loopback = new IPAddressString("::1").getAddress();
BigInteger value = loopback.getValue();
System.out.println(value);
byte bigIntBytes[] = extendArray(value.toByteArray(), loopback.getByteCount());
IPAddressGenerator generator = new IPAddressGenerator();
IPAddress andAgain = generator.from(bigIntBytes); // keys on byte length for version
System.out.println(andAgain);

Output:

1
::1

Get Prefix Length, Network Mask, Network Address, Version, Subnet Size and Segments from CIDR Address or Subnet

The code is entirely polymorphic. Note the addresses are subnets since hosts are zero.

cidr("192.168.10.0/24");
cidr("2001:db8:abcd:0012::/64");

static void cidr(String str) {
	IPAddressString addrString = new IPAddressString(str);
	IPAddress addr = addrString.getAddress();
	IPVersion version = addrString.getIPVersion();
	IPAddressSegment[] segments = addr.getSegments();
	int bitLength = addr.getBitCount();
	BigInteger size = addr.getCount();
	Integer prefixLen = addr.getNetworkPrefixLength();
	IPAddress mask = addr.getNetworkMask();
	IPAddress maskNoPrefix = mask.withoutPrefixLength();

	// three different ways to get the network address
	IPAddress networkAddr = addr.mask(mask);
	IPAddress networkAddrAnotherWay = addr.getLower().withoutPrefixLength();
	IPAddress networkAddrOneMoreWay = addr.toZeroHost().withoutPrefixLength();
		
	PrintStream out = System.out;
	out.println(version + " address: " + addr);
	out.println("prefix length: " + prefixLen);
	out.println("bit length: " + bitLength);
	out.println("segments: " + Arrays.toString(segments));
	out.println("size: " + size);
	out.println("mask with prefix: " + mask);
	out.println("mask: " + maskNoPrefix);
	out.println("network address: " + networkAddr + "\n");
}

Output:

IPv4 address: 192.168.10.0/24
prefix length: 24
bit length: 32
segments: [192, 168, 10, 0-255]
size: 256
mask with prefix: 255.255.255.0/24
mask: 255.255.255.0
network address: 192.168.10.0

IPv6 address: 2001:db8:abcd:12::/64
prefix length: 64
bit length: 128
segments: [0x2001, 0xdb8, 0xabcd, 0x12, 0x0-0xffff, 0x0-0xffff, 0x0-0xffff, 0x0-0xffff]
size: 18446744073709551616
mask with prefix: ffff:ffff:ffff:ffff::/64
mask: ffff:ffff:ffff:ffff::
network address: 2001:db8:abcd:12::

Apply Masks and Prefix Lengths

Starting with a mask string, we convert directly to a prefix length:

String maskStr = "255.255.255.0";
IPAddress mask = new IPAddressString(maskStr).getAddress();
Integer prefixLen =  mask.getBlockMaskPrefixLength(true);
System.out.println(maskStr + " is the network mask for prefix length " + prefixLen);

Output:

255.255.255.0 is the network mask for prefix length 24

Or we can parse addresses paired with masks, which converts the mask to a prefix length and pairs it with the address:

String addrStr = "1.2.3.4";
String fullAddrStr = addrStr + IPAddress.PREFIX_LEN_SEPARATOR + maskStr;
IPAddress addr = new IPAddressString(fullAddrStr).getAddress();
prefixLen = addr.getPrefixLength();
System.out.println(fullAddrStr + " parsed as " + addr + " has prefix length " + prefixLen);

Output:

1.2.3.4/255.255.255.0 parsed as 1.2.3.4/24 has prefix length 24

We obtain the enclosing block, the network for that mask:

IPAddress block = addr.toPrefixBlock();
IPAddress blockNoPrefix = block.withoutPrefixLength();
System.out.println("Enclosing block is " + block + ", without prefix length it is " + 
	blockNoPrefix);

Output:

Enclosing block is 1.2.3.0/24, without prefix length it is 1.2.3.*

In the reverse direction, we can obtain a mask corresponding to the prefix length:

IPAddress networkMaskAgain = IPv4Address.defaultIpv4Network().
	getNetworkMask(prefixLen).withoutPrefixLength();
System.out.println("Mask for prefix length " + prefixLen + " is " + networkMaskAgain);

Output:

Mask for prefix length 24 is 255.255.255.0

And we can apply that network mask to an address:

IPAddress ipAddr = new IPAddressString(addrStr).getAddress();
IPAddress masked = ipAddr.mask(mask);
System.out.println(ipAddr + " masked by " + mask + " is " + masked);

Output:

1.2.3.4 masked by 255.255.255.0 is 1.2.3.0

Equivalently, we can zero-out the host with the prefix length:

IPAddress zeroHost = ipAddr.toZeroHost(prefixLen);
System.out.println(ipAddr + " with zero host for prefix length " + prefixLen +
	" is " + zeroHost);

Output:

1.2.3.4 with zero host for prefix length 24 is 1.2.3.0

Or we can simply pair the prefix length with the address:

addr = ipAddr.setPrefixLength(prefixLen, false /* no zeroing bits */, 
	false /* no zero-host block conversion */);
System.out.println(ipAddr + " with prefix length " + prefixLen + " is " + addr);

Output:

1.2.3.4 with prefix length 24 is 1.2.3.4/24

Check if Multicast

static void isMulticast(String addressStr) {
	IPAddressString addrString = new IPAddressString(addressStr);
	IPAddress addr = addrString.getAddress();
	boolean result = addr.isMulticast();
	System.out.println(addr +  " is multicast " + result);
}

isMulticast("224.0.0.0/4");
isMulticast("ff00::/64");
isMulticast("224-239.0.0.1-5");
isMulticast("1.0.0.0/4");
isMulticast("fe::/64");

Output:

224.0.0.0/4 is multicast true
ff00::/64 is multicast true
224-239.0.0.1-5 is multicast true
1.0.0.0/4 is multicast false
fe::/64 is multicast false

Control Parsing of IP Addresses with Fewer Segments than Standard

If you type 23.23.43 into the address bar of some browsers, you will see it is interpreted as 23.23.0.43. If you do the same with http://2222, you will see it interpreted as the IPv4 address 0.0.8.174. Those strings are IPv4 string formats originally introduced by the inet_aton utility in BSD Unix. Those formats have persisted with inet_aton in the various Unix and Linux flavours, while spreading elsewhere into browsers and other software.

If you wish, you can treat such addresses as invalid, using validation parameters. You can either do it on a case-by-case basis or make it the default behaviour.

IP address strings:

IPAddressStringParameters strParams = new IPAddressStringParameters.Builder().
	allow_inet_aton(false).toParams();
String str = "23.23.43";
	
// do not allow it this time
IPAddressString addrStr = new IPAddressString(str, strParams);
check(addrStr);
			
// otherwise allowed, since the default validation options are permissive
addrStr = new IPAddressString(str);
check(addrStr);
			
// or make it default behaviour using fixed params with your own class
class MyStringClass extends IPAddressString {
	MyStringClass(String name) {
		super(name, strParams);
	}
}
addrStr = new MyStringClass(str);
check(addrStr);
	
static void check(HostIdentifierString addrStr) {
	try {
		System.out.println("address: " + addrStr.toAddress());
	} catch (HostIdentifierException | IOException e) {
		System.out.println(e.getMessage());
	}
}

Output:

23.23.43 IP Address error: options do not allow IPv4 address with less than four segments
address: 23.23.0.43
23.23.43 IP Address error: options do not allow IPv4 address with less than four segments

Host names:

HostNameParameters hostParams = new HostNameParameters.Builder().
	getAddressOptionsBuilder().allow_inet_aton(false).
	getParentBuilder().toParams();
String str = "23.23.43";

// do not allow it this time
HostName hostName = new HostName(str, hostParams);
check(hostName);
				
// otherwise allowed
hostName = new HostName(str);
check(hostName);
				
// or make it default behaviour using fixed params with your own class
class MyHostNameClass extends HostName {
	MyHostNameClass(String name) {
		super(name, hostParams);
	}
}
hostName = new MyHostNameClass(str);
check(hostName);

Output:

23.23.43 Host error: invalid host
address: 23.23.0.43
23.23.43 Host error: invalid host

You can also be more specific, using allow_inet_aton_joined_segments, allow_inet_aton_hex, allow_inet_aton_octal, or allowSingleSegment.

The allowSingleSegment parameter also applies to IPv6, for which 32 hex characters or 20 base 85 chars can be parsed an an IPv6 address.

IPAddressStringParameters strParams = new IPAddressStringParameters.Builder().
	allowSingleSegment(false).toParams();
String str = "aaaabbbbccccddddeeeeffffaaaabbbb";
				
// do not allow it this time
IPAddressString addrStr = new IPAddressString(str, strParams);
check(addrStr);
			
// otherwise allowed
addrStr = new IPAddressString(str);
check(addrStr);

Output:

aaaabbbbccccddddeeeeffffaaaabbbb IP Address error: validation options do not allow you to specify a non-segmented single value
address: aaaa:bbbb:cccc:dddd:eeee:ffff:aaaa:bbbb