Skip to content
Permalink
Browse files

Updates to IPv6 parsing and output

  • Loading branch information
darkfrog26 committed Jan 21, 2020
1 parent 6313dc4 commit b116df0d6dcf48dbd344639bf452204679da188d
@@ -11,14 +11,13 @@ trait IP extends Location {
}

object IP {
lazy val LocalHost = IPv4()
lazy val LocalHost: IP = IPv4()

val IPv4Regex: Regex = """\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b""".r
val IPv6Regex: Regex = """([0-9a-fA-F]*):([0-9a-fA-F]*):([0-9a-fA-F]*):([0-9a-fA-F]*):([0-9a-fA-F]*):([0-9a-fA-F]*):([0-9a-fA-F]*):([0-9a-fA-F]*)%?(\d*)""".r

def get(address: String): Option[IP with Product with Serializable] = address match {
def get(address: String): Option[IP] = address match {
case IPv4Regex(p1, p2, p3, p4) => Some(IPv4(p1.toInt, p2.toInt, p3.toInt, p4.toInt))
case IPv6Regex(p1, p2, p3, p4, p5, p6, p7, p8, scope) => Some(IPv6(Some(p1), Some(p2), Some(p3), Some(p4), Some(p5), Some(p6), Some(p7), Some(p8), Option(scope)))
case _ if address.indexOf(':') != -1 => Some(IPv6(address))
case _ => None
}

@@ -47,7 +46,7 @@ object IP {
}
IP(b.toString()) match {
case IPv4(p1, p2, p3, p4) => c.Expr[IP](q"IPv4($p1, $p2, $p3, $p4)")
case IPv6(p1, p2, p3, p4, p5, p6, p7, p8, scope) => c.Expr[IP](q"IPv6($p1, $p2, $p3, $p4, $p5, $p6, $p7, $p8, $scope)")
case IPv6(p, scope) => c.Expr[IP](q"IPv6($p, $scope)")
}
}
case _ => c.abort(c.enclosingPosition, "Bad usage of IP interpolation.")
@@ -1,23 +1,56 @@
package io.youi.net

case class IPv6(part1: Option[String] = None, part2: Option[String] = None, part3: Option[String] = None, part4: Option[String] = None, part5: Option[String] = None, part6: Option[String] = None, part7: Option[String] = None, part8: Option[String] = Some("1"), scope: Option[String] = None) extends IP {
private def s(part: Option[String]) = part.getOrElse("0")
def p1 = s(part1)
def p2 = s(part2)
def p3 = s(part3)
def p4 = s(part4)
def p5 = s(part5)
def p6 = s(part6)
def p7 = s(part7)
def p8 = s(part8)
case class IPv6(parts: Vector[Int], scope: Option[String]) extends IP {
assert(parts.length == 8, s"IPv6 requires exactly 8 parts, but received: ${parts.mkString("[", ", ", "]")} (${parts.length})")

def toInt(part: Option[String]) = Integer.parseInt(part.getOrElse("0"), 16)
private def s(i: Int, canonical: Boolean): String = if (canonical) {
f"$i%04x"
} else {
i.toHexString
}

lazy val address = Array(toInt(part1), toInt(part2), toInt(part3), toInt(part4), toInt(part5), toInt(part6), toInt(part7), toInt(part8))
lazy val addressString = s"$p1:$p2:$p3:$p4:$p5:$p6:$p7:$p8${scope.map(s => s"%$s").getOrElse("")}"
lazy val address: Array[Int] = parts.toArray
lazy val addressString: String = {
val base = parts.map(i => s(i, canonical = false)).mkString(":")
scope match {
case Some(scp) => s"$base%$scp"
case None => base
}
}
lazy val canonicalString: String = {
val base = parts.map(i => s(i, canonical = true)).mkString(":")
scope match {
case Some(scp) => s"$base%$scp"
case None => base
}
}

override def equals(o: scala.Any) = o match {
override def equals(o: scala.Any): Boolean = o match {
case ip: IPv6 => addressString == ip.addressString
case _ => false
}
}

object IPv6 {
val Empty: IPv6 = IPv6(parts = Vector(0, 0, 0, 0, 0, 0, 0, 1), scope = None)

def apply(address: String): IPv6 = {
val percent = address.indexOf('%')
val (a, scope) = if (percent != -1) {
(address.substring(0, percent), Some(address.substring(percent + 1)))
} else {
(address, None)
}
val separator = a.indexOf("::")
if (separator != -1) {
val left = a.substring(0, separator).split(':').map(Some.apply).toList
val right = a.substring(separator + 2).split(':').map(Some.apply).toList
val middle = (0 until (8 - (left.length + right.length))).map(_ => None).toList
apply((left ::: middle ::: right).map(toInt).toVector, scope)
} else {
apply(a.split(':').map(Some.apply).map(toInt).toVector, scope)
}
}

private def toInt(part: Option[String]): Int = Integer.parseInt(part.getOrElse("0"), 16)
}
@@ -23,9 +23,19 @@ class IPSpec extends WordSpec with Matchers {
}
"parsing IPv6 addresses" should {
"properly parse fe80:0:0:0:0:0:0:1%1" in {
val ip = IP("fe80:0:0:0:0:0:0:1%1")
ip should equal(IPv6(part1 = Some("fe80"), scope = Some("1")))
val ip = IP("fe80:0:0:0:0:0:0:1%1").asInstanceOf[IPv6]
ip.parts should equal(Vector(65152, 0, 0, 0, 0, 0, 0, 1))
ip.scope should be(Some("1"))
ip.addressString should be("fe80:0:0:0:0:0:0:1%1")
ip.canonicalString should be("fe80:0000:0000:0000:0000:0000:0000:0001%1")
}
"properly parse 2604:ca00:129:99af::860:930a" in {
val ip = IP("2604:ca00:129:99af::860:930a").asInstanceOf[IPv6]
ip.parts should equal(Vector(9732, 51712, 297, 39343, 0, 0, 2144, 37642))
ip.scope should be(None)
ip.addressString should be("2604:ca00:129:99af:0:0:860:930a")
ip.canonicalString should be("2604:ca00:0129:99af:0000:0000:0860:930a")
}
}
}
}
}

0 comments on commit b116df0

Please sign in to comment.
You can’t perform that action at this time.