Search code examples
scalabit-manipulationipv6bigintegerinetaddress

IPv6ToBigInteger


I have this function which uses InetAddress, but the output is occasionally wrong. (example: "::ffff:49e7:a9b2" will give an incorrect result.)

def IPv6ToBigInteger(ip: String): BigInteger = {
        val i = InetAddress.getByName(ip)
        val a: Array[Byte] = i.getAddress
        new BigInteger(1, a)
    }

And the I also have this function

def IPv6ToBigInteger(ip: String): BigInteger = {
    val fragments = ip.split(":|\\.|::").filter(_.nonEmpty)
    require(fragments.length <= 8, "Bad IPv6")
    var ipNum = new BigInteger("0")
    for (i <-fragments.indices) {
        val frag2Long = new BigInteger(s"${fragments(i)}", 16)
        ipNum = frag2Long.or(ipNum.shiftLeft(16))
    }
    ipNum
}

which appears to have a parsing error because it gives the wrong output unless it is in 0:0:0:0:0:0:0:0 format, but is an based on my IPv4ToLong function:

def IPv4ToLong(ip: String): Long = {
        val fragments = ip.split('.')
        var ipNum = 0L
        for (i <- fragments.indices) {
            val frag2Long = fragments(i).toLong
            ipNum = frag2Long | ipNum << 8L
        }
        ipNum
    }

Solution

  • Interesting challenge: transform IP address strings into BigInt values, allowing for all legal IPv6 address forms.

    Here's my try.

    import scala.util.Try
    
    def iPv62BigInt(ip: String): Try[BigInt] = Try{
      val fill = ":0:" * (8 - ip.split("[:.]").count(_.nonEmpty))
      val fullArr =
        raw"((?<=\.)(\d+)|(\d+)(?=\.))".r
          .replaceAllIn(ip, _.group(1).toInt.toHexString)
          .replace("::", fill)
          .split("[:.]")
          .collect{case s if s.nonEmpty => s"000$s".takeRight(4)}
    
      if (fullArr.length == 8) BigInt(fullArr.mkString, 16)
      else throw new NumberFormatException("wrong number of elements")
    }
    

    This is, admittedly, a bit lenient in that it won't catch all all non-IPv6 forms, but that's not a trivial task using tools like regex.