/** * An IP address (v4 or v6). */ class IPAddress { private var address: Array private var is4: Boolean /** * @throws NumberFormatException if the IP address follows neither the IPv4 syntax, nor the IPv6 syntax. */ constructor(address: String) { if (isAddressV4(address)) { this.is4 = true } else if (isAddressV6(address)) { this.is4 = false } else { throw NumberFormatException() } val split4 = address.split('.') val split6 = address.split(':') val bytes: List if (this.is4) { bytes = split4.map { it.toByte(10) } } else { val shorts = split6.map { it.toShort(16) } bytes = shorts.flatMap { listOf( it.toInt().shr(8).toByte(), it.toInt().and(0x00FF).toByte() ) } } this.address = bytes.toTypedArray() } private fun isAddressV4(address: String): Boolean { val split4 = address.split('.') var isOK = split4.size == 4 isOK = isOK && split4.size == split4.filter { it.matches(Regex("^[0-9]+$")) }.size return isOK } private fun isAddressV6(address: String): Boolean { val split6 = address.split(':') var isOK = split6.size == 8 isOK = isOK && split6.size == split6.filter { it.contains(Regex("^[0-9][a-f]+$")) }.size if (isOK) { return true } // try with "::" val split6Double = address.split("::") if (split6Double.size != 2) { return false } val omitted = 8 - split6Double[0].split(':').size - split6Double[1].split(':').size if (omitted < 0) { return false } return true } override fun toString(): String { val str: String if (this.is4) { str = this.address.joinToString(separator = ".") { it.toString() } } else { str = this.address.joinToString(separator = ":") { it.toString() } } return str } }