summaryrefslogtreecommitdiff
path: root/src/main/kotlin/IPAddress.kt
blob: 58d0aaed21da83ee8d7bbada29bb79c448d9844d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
 * An IP address (v4 or v6).
 */
class IPAddress {
    private var address: Array<Byte>
    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<Byte>
        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
    }
}