Skip to content

Instantly share code, notes, and snippets.

@Stanback
Last active June 10, 2022 10:46
Show Gist options
  • Save Stanback/1d96985ebfd61e9e66ef55cd46b114fc to your computer and use it in GitHub Desktop.
Save Stanback/1d96985ebfd61e9e66ef55cd46b114fc to your computer and use it in GitHub Desktop.
Scala example for converting an IPv4 or IPv6 address to a base 10 decimal/long/BigInt w/ netmask range calculation
import java.net.{Inet4Address, Inet6Address, InetAddress}
import scala.util.Try
sealed trait IPAddressException {
self: Throwable => val message: String
}
case class IPAddressParseException(message: String) extends Exception(message) with IPAddressException
trait IPAddress {
val ip: String
val netmask: Int
def asBase10: String
def asRange: (String, String)
}
case class IPV4Address(
ip: String,
netmask: Int,
bytes: Array[Byte],
base10: Long,
range: (Long, Long)
) extends IPAddress {
def asBase10: String = base10.toString
def asRange: (String, String) = (range._1.toString -> range._2.toString)
}
object IPV4Address {
val MAX_SIZE = 32
def bytes2Long(bytes: Array[Byte]): Long = bytes.foldLeft(0L)((acc, b) => (acc << 8) + (b & 0xff))
def long2Bytes(base10: Long): Array[Byte] = (for (i <- 0 to 3) yield (base10 >> (i * 8)).toByte).reverse.toArray
def getRange(base10: Long, netmask: Int): (Long, Long) = {
val mask = 0xffffffff
val nm = if (netmask == 0) 0 else (mask << (MAX_SIZE - netmask))
val min = base10 & mask
val max = base10 | (~nm & mask)
(min -> max)
}
def apply(ia: Inet4Address, netmask: Option[Int]): IPV4Address = {
val bytes = ia.getAddress
val base10 = bytes2Long(bytes)
val mask = netmask.getOrElse(MAX_SIZE)
new IPV4Address(
ip = ia.getHostAddress,
netmask = mask,
bytes = bytes,
base10 = base10,
range = getRange(base10, mask)
)
}
@throws(classOf[IPAddressParseException])
def apply(base10: Long, netmask: Option[Int]): IPV4Address = {
val bytes = long2Bytes(base10)
Try(InetAddress.getByAddress(bytes)).toOption match {
case Some(ia: Inet4Address) => apply(ia, netmask)
case _ => throw IPAddressParseException(s"Unable to convert base10 to Inet4Address: $base10")
}
}
}
case class IPV6Address(
ip: String,
netmask: Int,
bytes: Array[Byte],
base10: BigInt,
range: (BigInt, BigInt)
) extends IPAddress {
def asBase10: String = base10.toString
def asRange: (String, String) = (range._1.toString -> range._2.toString)
}
object IPV6Address {
val MAX_SIZE = 128
def bytes2BigInt(bytes: Array[Byte]): BigInt = BigInt(1, bytes)
def bigInt2Bytes(base10: BigInt): Array[Byte] = {
val bytes = base10.toByteArray
if (bytes(0) == 0) bytes.slice(1, bytes.length) else bytes
}
def getRange(base10: BigInt, netmask: Int): (BigInt, BigInt) = {
val mask = BigInt("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)
val nm = mask << (MAX_SIZE - netmask)
val min = base10 & nm
val max = base10 | (~nm & mask)
(min -> max)
}
def apply(ia: Inet6Address, netmask: Option[Int]): IPV6Address = {
val bytes = ia.getAddress
val base10 = bytes2BigInt(bytes)
val mask = netmask.getOrElse(MAX_SIZE)
new IPV6Address(
ip = ia.getHostAddress,
netmask = mask,
bytes = bytes,
base10 = base10,
range = getRange(base10, mask)
)
}
@throws(classOf[IPAddressParseException])
def apply(base10: BigInt, netmask: Option[Int]): IPV6Address = {
val bytes = bigInt2Bytes(base10)
Try(InetAddress.getByAddress(bytes)).toOption match {
case Some(ia: Inet6Address) => apply(ia, netmask)
case _ => throw IPAddressParseException(s"Unable to convert base10 to Inet6Address: $base10")
}
}
}
object IPAddress {
@throws(classOf[IPAddressParseException])
def apply(ip: String, netmask: Option[Int] = None): IPAddress = {
Try(InetAddress.getByName(ip)).toOption match {
case Some(ia: Inet4Address) => IPV4Address(ia, netmask)
case Some(ia: Inet6Address) => IPV6Address(ia, netmask)
case _ => throw IPAddressParseException(s"Unknown IP format: $ip")
}
}
}
/*
* Example usage:
*/
val myIPV4 = IPAddress("127.0.0.1")
println(s"IPv4 ${myIPV4.ip} to base 10 is: ${myIPV4.asBase10}")
val myIPV6 = IPAddress("2001:4860:4001:803::1011")
println(s"IPv6 ${myIPV6.ip} to base 10 is: ${myIPV6.asBase10}")
val myIPV4Network = IPAddress("192.168.1.1", Some(24))
println(s"Decimal range of ${myIPV4Network.ip}/${myIPV4Network.netmask} is: ${myIPV4Network.asRange._1} through ${myIPV4Network.asRange._2}")
val myIPV6Network = IPAddress("fd86:ae29:179b:9ef5::", Some(64)).asInstanceOf[IPV6Address]
val ip6Start = IPV6Address(myIPV6Network.range._1, None)
val ip6End = IPV6Address(myIPV6Network.range._2, None)
println(s"Range of ${myIPV6Network.ip}/${myIPV6Network.netmask} spans from ${ip6Start.ip} to ${ip6End.ip}")
@Stanback
Copy link
Author

Stanback commented Sep 4, 2019

Example output:

IPv4 127.0.0.1 to base 10 is: 2130706433
IPv6 2001:4860:4001:803:0:0:0:1011 to base 10 is: 42541956121179194438855083813463134225
Decimal range of 192.168.1.1/24 is: 3232235777 through 3232236031
Range of fd86:ae29:179b:9ef5:0:0:0:0/64 spans from fd86:ae29:179b:9ef5:0:0:0:0 to fd86:ae29:179b:9ef5:ffff:ffff:ffff:ffff

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment