Checking To See If An IP v4 Address Is In A CIDR Range Using ColdFusion And SubnetUtils
At work, I need to build a feature that allows an IP address to be validated against a CIDR (Classless Inter-Domain Routing) range. CIDR - as I just learned - is range notation that defines a High and Low IP address by identifying how many bits in a given IP address are "locked down" and how many are dynamic. You can then take a target IP address and determine if it is in between the High and Low addresses. I know next to nothing about IP addresses or networking; so it took me quite a while to find a working solution (for IPv4) in ColdFusion. As such, I figured I would put a quick demo together.
When approaching this problem, my first thought was that I could just convert the various IP addresses to numeric values. And then, test if one numeric value fell in between the High and Low numeric values. As such, I started to play with converting IP addresses to their integer representation:
<cfscript>
/**
* I convert the given IPv4 or IPv6 address into a numeric INTEGER STRING.
*
* NOTE: I am converting to a NUMERIC STRING since the size of the resultant value may
* not fit nicely into the language data-types.
*
* @ipAddress I am the ip address being converted.
* @output false
*/
public string function ipAddressToInt( required string ipAddress ) {
var bytes = new java( "java.net.InetAddress" )
.getByName( javaCast( "string", ipAddress ) )
.getAddress()
;
// NOTE: The "1" tells BigInteger to use the most significant bit as part of the
// integer value and NOT as the signing bit.
var bigInt = new java( "java.math.BigInteger" )
.init( javaCast( "int", 1 ), bytes )
;
return( bigInt.toString() );
}
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
// IPv4 examples.
writeOutput( "IP to Int: #ipAddressToInt( '0.0.0.1' )# <br />" );
writeOutput( "IP to Int: #ipAddressToInt( '255.255.255.255' )# <br />" );
writeOutput( "IP to Int: #ipAddressToInt( '255.251.3.100' )# <br />" );
// IPv6 examples.
writeOutput( "IP to Int: #ipAddressToInt( '2002:4760:4860::8768' )# <br />" );
writeOutput( "IP to Int: #ipAddressToInt( '2001:0db8:85a3:0000:0000:8a2e:0370:7334' )# <br />" );
writeOutput( "IP to Int: #ipAddressToInt( '::1' )# <br />" );
</cfscript>
Here, I'm using the "java.net.InetAddress" Java class to convert the IP address (v4 or v6) to an array of bytes (that represent the numeric value). Then, I convert that array of bytes into an integer string using the "java.math.BigInteger" Java class. I'm using BigInteger because I don't believe that IPv6 will fit nicely into a ColdFusion integer.
NOTE: I feel like I read that ColdFusion 2018 is now handling large numbers better than previous versions. But, I can't find that documentation at the moment (so it's possible that I am making it up).
When we run the above code, we get the following ColdFusion output:
IP to Int: 1
IP to Int: 4294967295
IP to Int: 4294640484
IP to Int: 42547128138218815811975245505646069608
IP to Int: 42540766452641154071740215577757643572
IP to Int: 1
This worked for parsing a single IP address. But, at this point, I realized that I didn't quite know how to proceed. After much Googling, I came across this StackOverflow answer by Eddie, which mentioned that there is a Jakarta Commons Net project - org.apache.commons.net.util.SubnetUtils - that will handle this check for us. And, as luck would have it, ColdFusion has access to this Java library via the built-in JAR files:
<cfscript>
/**
* I determine if the given IPv4 address range contains the given IP address.
*
* @ipRange I am the CIDR IP v4 address range.
* @ipAddress I am the IP address being tested.
* @output false
*/
public boolean function isIpInRange(
required string ipRange,
required string ipAddress
) {
var utils = createObject( "java", "org.apache.commons.net.util.SubnetUtils" )
.init( javaCast( "string", ipRange ) )
;
// Tell the SubnetUtils to include "network" and "broadcast" addresses when
// checking for the "in range" IP addresses.
utils.setInclusiveHostCount( javaCast( "boolean", true ) );
return( utils.getInfo().isInRange( javaCast( "string", ipAddress ) ) );
}
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
// IPv4 examples.
// --
// CAUTION: SubnetUtils only works with IPv4
writeOutput( "IP in range: #isIpInRange( '192.168.12.0/25', '192.168.11.255' )# <br />" ); // <-- Just outside range.
writeOutput( "IP in range: #isIpInRange( '192.168.12.0/25', '192.168.12.0' )# <br />" );
writeOutput( "IP in range: #isIpInRange( '192.168.12.0/25', '192.168.12.127' )# <br />" );
writeOutput( "IP in range: #isIpInRange( '192.168.12.0/25', '192.168.12.128' )# <br />" ); // <-- Just outside range.
</cfscript>
As you can see, I'm using the SubnetUtils class to parse the IPv4 CIDR range. Then, I simply ask the range if the given IP address is contained within it. And, when we run this page, we get the following ColdFusion output:
IP in range: NO
IP in range: YES
IP in range: YES
IP in range: NO
Works like a charm!
Now, it turns out that the SubnetUtils class only works with IPv4, not IPv6. For my purposes, this is likely fine. But, there appear to be several Java libraries out there that handle both IPv4 and IPv6. I was just looking for (or hoping for) a solution that wouldn't require any additional libraries. That said, loading additional Java libraries with the JavaLoader project is par for the course these days in ColdFusion.
Want to use code from this post? Check out the license.
Reader Comments
@All,
After I got this IPv4 support in place, I went back and tried to figure out how to add IPv6 support. Luckily, I came across the Commons IP Math library, which handles both IPv4 and IPv6 support:
www.bennadel.com/blog/3503-using-commons-ip-math-to-check-if-an-ip-address-exists-in-an-ipv4-or-ipv6-cidr-range-in-coldfusion.htm