///|
/// IP parsing is strict: no DNS, zones, CIDR suffixes, brackets or octal IPv4.
priv struct Address {
bytes : Array[Int]
version : Int
}
///|
fn ipv4(text : String) -> Array[Int] raise MmdbError {
let parts = text.split(".").to_array()
if parts.length() != 4 {
raise MmdbError("invalid-ip", -1, "Expected four IPv4 octets")
}
let result : Array[Int] = []
for part in parts {
if part.length() == 0 ||
part.length() > 3 ||
(part.length() > 1 && part[0] == '0') {
raise MmdbError("invalid-ip", -1, "Invalid IPv4 octet")
}
let mut number = 0
for ch in part {
if ch < '0' || ch > '9' {
raise MmdbError("invalid-ip", -1, "Non-decimal IPv4 octet")
}
number = number * 10 + ch.to_int() - 48
}
if number > 255 {
raise MmdbError("invalid-ip", -1, "IPv4 octet exceeds 255")
}
result.push(number)
}
result
}
///|
fn hex_group(text : StringView) -> Int raise MmdbError {
if text.length() == 0 || text.length() > 4 {
raise MmdbError("invalid-ip", -1, "Invalid IPv6 group")
}
let mut value = 0
for ch in text {
let digit = if ch >= '0' && ch <= '9' {
ch.to_int() - 48
} else if ch >= 'a' && ch <= 'f' {
ch.to_int() - 87
} else if ch >= 'A' && ch <= 'F' {
ch.to_int() - 55
} else {
raise MmdbError("invalid-ip", -1, "Invalid IPv6 digit")
}
value = value * 16 + digit
}
value
}
///|
fn ipv6_groups(
text : StringView,
allow_v4 : Bool,
) -> Array[Int] raise MmdbError {
if text.is_empty() {
return []
}
let parts = text.split(":").to_array()
let result : Array[Int] = []
for i in 0.. Address raise MmdbError {
if text.length() == 0 || text.length() > 45 {
raise MmdbError("invalid-ip", -1, "Invalid IP length")
}
if !text.contains(":") {
return { bytes: ipv4(text), version: 4, }
}
let halves = text.split("::").to_array()
let groups : Array[Int] = []
if halves.length() == 1 {
let whole = ipv6_groups(text[:], true)
if whole.length() != 8 {
raise MmdbError("invalid-ip", -1, "Expected eight IPv6 groups")
}
for n in whole {
groups.push(n)
}
} else if halves.length() == 2 {
let left = ipv6_groups(halves[0], false)
let right = ipv6_groups(halves[1], true)
if left.length() + right.length() >= 8 {
raise MmdbError("invalid-ip", -1, "Compression must omit a group")
}
for n in left {
groups.push(n)
}
for _ in 0..<(8 - left.length() - right.length()) {
groups.push(0)
}
for n in right {
groups.push(n)
}
} else {
raise MmdbError("invalid-ip", -1, "Multiple IPv6 compressions")
}
let bytes : Array[Int] = []
for n in groups {
bytes.push(n >> 8)
bytes.push(n & 255)
}
{ bytes, version: 6, }
}